summaryrefslogtreecommitdiffstats
path: root/controller-api
diff options
context:
space:
mode:
authorMorten Tokle <morten.tokle@gmail.com>2017-12-06 12:51:14 +0100
committerGitHub <noreply@github.com>2017-12-06 12:51:14 +0100
commite4975438ac6ff14d01c944e64dc1178e96cb19f4 (patch)
treebb2d7b7ea8274f7ef238e03fe5dc7c4b22ffd13f /controller-api
parent4008bac83f5a6a8c34850d1032728e70eafa851f (diff)
parenta0fd0b19c985b7d6547bd5d86c851261d14f92b5 (diff)
Merge pull request #4365 from vespa-engine/bjorncs/move-athenz-api-to-controller-api
Bjorncs/move athenz api to controller api
Diffstat (limited to 'controller-api')
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java17
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java15
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzIdentity.java16
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPrincipal.java59
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPublicKey.java49
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzService.java58
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUser.java56
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUtils.java26
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java11
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/NToken.java36
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZToken.java36
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java35
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java24
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java16
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java15
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java19
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/package-info.java8
17 files changed, 496 insertions, 0 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java
new file mode 100644
index 00000000000..3323cda89b3
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java
@@ -0,0 +1,17 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+/**
+ * @author bjorncs
+ */
+public enum ApplicationAction {
+ deploy("deployer"),
+ read("reader"),
+ write("writer");
+
+ public final String roleName;
+
+ ApplicationAction(String roleName) {
+ this.roleName = roleName;
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java
new file mode 100644
index 00000000000..a2a16d10cdb
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java
@@ -0,0 +1,15 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+/**
+ * @author bjorncs
+ */
+public interface AthenzClientFactory {
+
+ ZmsClient createZmsClientWithServicePrincipal();
+
+ ZtsClient createZtsClientWithServicePrincipal();
+
+ ZmsClient createZmsClientWithAuthorizedServiceToken(NToken authorizedServiceToken);
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzIdentity.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzIdentity.java
new file mode 100644
index 00000000000..ef63ef2581f
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzIdentity.java
@@ -0,0 +1,16 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+
+/**
+ * @author bjorncs
+ */
+public interface AthenzIdentity {
+ AthenzDomain getDomain();
+ String getName();
+ default String getFullName() {
+ return getDomain().id() + "." + getName();
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPrincipal.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPrincipal.java
new file mode 100644
index 00000000000..8279edcd8e6
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPrincipal.java
@@ -0,0 +1,59 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+
+import java.security.Principal;
+import java.util.Objects;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzPrincipal implements Principal {
+
+ private final AthenzIdentity athenzIdentity;
+ private final NToken nToken;
+
+ public AthenzPrincipal(AthenzIdentity athenzIdentity,
+ NToken nToken) {
+ this.athenzIdentity = athenzIdentity;
+ this.nToken = nToken;
+ }
+
+ public AthenzIdentity getIdentity() {
+ return athenzIdentity;
+ }
+
+ @Override
+ public String getName() {
+ return athenzIdentity.getFullName();
+ }
+
+ public AthenzDomain getDomain() {
+ return athenzIdentity.getDomain();
+ }
+
+ public NToken getNToken() {
+ return nToken;
+ }
+
+ @Override
+ public String toString() {
+ return "AthenzPrincipal{" +
+ "athenzIdentity=" + athenzIdentity +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AthenzPrincipal principal = (AthenzPrincipal) o;
+ return Objects.equals(athenzIdentity, principal.athenzIdentity);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(athenzIdentity);
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPublicKey.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPublicKey.java
new file mode 100644
index 00000000000..c7f370dd4e3
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzPublicKey.java
@@ -0,0 +1,49 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import java.security.PublicKey;
+import java.util.Objects;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzPublicKey {
+
+ private final PublicKey publicKey;
+ private final String keyId;
+
+ public AthenzPublicKey(PublicKey publicKey, String keyId) {
+ this.publicKey = publicKey;
+ this.keyId = keyId;
+ }
+
+ public PublicKey getPublicKey() {
+ return publicKey;
+ }
+
+ public String getKeyId() {
+ return keyId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AthenzPublicKey that = (AthenzPublicKey) o;
+ return Objects.equals(publicKey, that.publicKey) &&
+ Objects.equals(keyId, that.keyId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(publicKey, keyId);
+ }
+
+ @Override
+ public String toString() {
+ return "AthenzPublicKey{" +
+ "publicKey=" + publicKey +
+ ", keyId='" + keyId + '\'' +
+ '}';
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzService.java
new file mode 100644
index 00000000000..24cd7671d96
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzService.java
@@ -0,0 +1,58 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId;
+
+import java.util.Objects;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzService implements AthenzIdentity {
+
+ private final AthenzDomain domain;
+ private final String serviceName;
+
+ public AthenzService(AthenzDomain domain, String serviceName) {
+ this.domain = domain;
+ this.serviceName = serviceName;
+ }
+
+ public AthenzService(String domain, String serviceName) {
+ this(new AthenzDomain(domain), serviceName);
+ }
+
+ public static AthenzService fromScrewdriverId(ScrewdriverId screwdriverId) {
+ return new AthenzService(AthenzUtils.SCREWDRIVER_DOMAIN, "sd" + screwdriverId.id());
+ }
+
+ @Override
+ public AthenzDomain getDomain() {
+ return domain;
+ }
+
+ @Override
+ public String getName() {
+ return serviceName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AthenzService that = (AthenzService) o;
+ return Objects.equals(domain, that.domain) &&
+ Objects.equals(serviceName, that.serviceName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(domain, serviceName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("AthenzService(%s)", getFullName());
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUser.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUser.java
new file mode 100644
index 00000000000..782876f21f1
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUser.java
@@ -0,0 +1,56 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
+
+import java.util.Objects;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzUser implements AthenzIdentity {
+ private final UserId userId;
+
+ public AthenzUser(UserId userId) {
+ this.userId = userId;
+ }
+
+ public static AthenzUser fromUserId(UserId userId) {
+ return new AthenzUser(userId);
+ }
+
+ @Override
+ public AthenzDomain getDomain() {
+ return AthenzUtils.USER_PRINCIPAL_DOMAIN;
+ }
+
+ @Override
+ public String getName() {
+ return userId.id();
+ }
+
+ public UserId getUserId() {
+ return userId;
+ }
+
+ @Override
+ public String toString() {
+ return "AthenzUser{" +
+ "userId=" + userId +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AthenzUser that = (AthenzUser) o;
+ return Objects.equals(userId, that.userId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userId);
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUtils.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUtils.java
new file mode 100644
index 00000000000..0ed5d86dd7e
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzUtils.java
@@ -0,0 +1,26 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzUtils {
+
+ private AthenzUtils() {}
+
+ public static final AthenzDomain USER_PRINCIPAL_DOMAIN = new AthenzDomain("user");
+ public static final AthenzDomain SCREWDRIVER_DOMAIN = new AthenzDomain("cd.screwdriver.project");
+ public static final AthenzService ZMS_ATHENZ_SERVICE = new AthenzService("sys.auth", "zms");
+
+ public static AthenzIdentity createAthenzIdentity(AthenzDomain domain, String identityName) {
+ if (domain.equals(USER_PRINCIPAL_DOMAIN)) {
+ return AthenzUser.fromUserId(new UserId(identityName));
+ } else {
+ return new AthenzService(domain, identityName);
+ }
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java
new file mode 100644
index 00000000000..1df1746b02e
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/InvalidTokenException.java
@@ -0,0 +1,11 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+/**
+ * @author bjorncs
+ */
+public class InvalidTokenException extends Exception {
+ public InvalidTokenException(String message) {
+ super(message);
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/NToken.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/NToken.java
new file mode 100644
index 00000000000..c2796befdc8
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/NToken.java
@@ -0,0 +1,36 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import java.util.Objects;
+
+/**
+ * Represents an Athenz NToken (principal token)
+ *
+ * @author bjorncs
+ */
+public class NToken {
+
+ private final String rawToken;
+
+ public NToken(String rawToken) {
+ this.rawToken = rawToken;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NToken nToken = (NToken) o;
+ return Objects.equals(rawToken, nToken.rawToken);
+ }
+
+ public String getRawToken() {
+ return rawToken;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rawToken);
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZToken.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZToken.java
new file mode 100644
index 00000000000..cfa63b04197
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZToken.java
@@ -0,0 +1,36 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import java.util.Objects;
+
+/**
+ * Represents an Athenz ZToken (role token)
+ *
+ * @author bjorncs
+ */
+public class ZToken {
+
+ private final String rawToken;
+
+ public ZToken(String rawToken) {
+ this.rawToken = rawToken;
+ }
+
+ public String getRawToken() {
+ return rawToken;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ZToken zToken = (ZToken) o;
+ return Objects.equals(rawToken, zToken.rawToken);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rawToken);
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java
new file mode 100644
index 00000000000..d72b8960427
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java
@@ -0,0 +1,35 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+
+import java.util.List;
+
+/**
+ * @author bjorncs
+ */
+public interface ZmsClient {
+
+ void createTenant(AthenzDomain tenantDomain);
+
+ void deleteTenant(AthenzDomain tenantDomain);
+
+ void addApplication(AthenzDomain tenantDomain, ApplicationId applicationName);
+
+ void deleteApplication(AthenzDomain tenantDomain, ApplicationId applicationName);
+
+ boolean hasApplicationAccess(AthenzIdentity athenzIdentity, ApplicationAction action, AthenzDomain tenantDomain, ApplicationId applicationName);
+
+ boolean hasTenantAdminAccess(AthenzIdentity athenzIdentity, AthenzDomain tenantDomain);
+
+ // Used before vespa tenancy is established for the domain.
+ boolean isDomainAdmin(AthenzIdentity athenzIdentity, AthenzDomain domain);
+
+ List<AthenzDomain> getDomainList(String prefix);
+
+ AthenzPublicKey getPublicKey(AthenzService service, String keyId);
+
+ List<AthenzPublicKey> getPublicKeys(AthenzService service);
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java
new file mode 100644
index 00000000000..31e9e549c08
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java
@@ -0,0 +1,24 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+/**
+ * @author bjorncs
+ */
+public class ZmsException extends RuntimeException {
+
+ private final int code;
+
+ public ZmsException(int code, Throwable cause) {
+ super(cause.getMessage(), cause);
+ this.code = code;
+ }
+
+ public ZmsException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java
new file mode 100644
index 00000000000..e2cb38a8466
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsKeystore.java
@@ -0,0 +1,16 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import java.security.PublicKey;
+import java.util.Optional;
+
+/**
+ * @author bjorncs
+ */
+public interface ZmsKeystore {
+
+ Optional<PublicKey> getPublicKey(AthenzService service, String keyId);
+
+ default void preloadKeys(AthenzService service) { /* Default implementation is noop */ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java
new file mode 100644
index 00000000000..c7a2adfb17e
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsClient.java
@@ -0,0 +1,15 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.vespa.hosted.controller.api.identifiers.AthenzDomain;
+
+import java.util.List;
+
+/**
+ * @author bjorncs
+ */
+public interface ZtsClient {
+
+ List<AthenzDomain> getTenantDomainsForUser(AthenzIdentity principal);
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java
new file mode 100644
index 00000000000..2be998e1544
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZtsException.java
@@ -0,0 +1,19 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+/**
+ * @author bjorncs
+ */
+public class ZtsException extends RuntimeException {
+
+ private final int code;
+
+ public ZtsException(int code, Throwable cause) {
+ super(cause.getMessage(), cause);
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/package-info.java
new file mode 100644
index 00000000000..d66525275bc
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.hosted.controller.api.integration.athenz;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file