summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-09-23 10:55:56 +0200
committerGitHub <noreply@github.com>2019-09-23 10:55:56 +0200
commitf07176a7d3df4cc3bd9087e86b195eac0d7b6f3d (patch)
treef4f828b7a3789464826348d0f52a8ea5cf8fac8e
parentcfe167148c1c19a4ceb6175268d10ac01982f7d3 (diff)
parent00c7e655dfb56f00e8451b2aaaee44189077b433 (diff)
Merge pull request #10753 from vespa-engine/mpolden/ca-api
Initial CA API
-rw-r--r--athenz-identity-provider-service/pom.xml18
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java57
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java64
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java81
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java109
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java54
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java60
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java33
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java101
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java72
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java66
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java34
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java1
-rw-r--r--configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java38
-rw-r--r--container-core/abi-spec.json110
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/ErrorResponse.java (renamed from configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java)2
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/MessageResponse.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/MessageResponse.java)2
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/ResourceResponse.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ResourceResponse.java)2
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/SlimeJsonResponse.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/SlimeJsonResponse.java)2
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/StringResponse.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/StringResponse.java)5
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/Uri.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Uri.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ErrorResponse.java82
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java23
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/AuditLogResponse.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/JobsResponse.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/UpgraderResponse.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java2
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java2
-rw-r--r--security-utils/src/main/java/com/yahoo/security/Pkcs10Csr.java14
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java10
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java2
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java2
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java2
45 files changed, 938 insertions, 176 deletions
diff --git a/athenz-identity-provider-service/pom.xml b/athenz-identity-provider-service/pom.xml
index 501904a1d62..a8de9b58269 100644
--- a/athenz-identity-provider-service/pom.xml
+++ b/athenz-identity-provider-service/pom.xml
@@ -94,6 +94,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-dev</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
<!-- COMPILE -->
<dependency>
@@ -134,6 +140,18 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>application</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>testutil</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java
new file mode 100644
index 00000000000..6d121657a40
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java
@@ -0,0 +1,57 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca;
+
+import com.yahoo.security.Pkcs10Csr;
+import com.yahoo.security.SubjectAlternativeName;
+import com.yahoo.security.X509CertificateBuilder;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.time.Clock;
+import java.time.Duration;
+import java.util.Objects;
+
+import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
+import static com.yahoo.security.SubjectAlternativeName.Type.DNS_NAME;
+
+/**
+ * Helper class for creating {@link X509Certificate}s.
+ *
+ * @author mpolden
+ */
+public class Certificates {
+
+ private static final Duration CERTIFICATE_TTL = Duration.ofDays(30);
+
+ private final Clock clock;
+
+ public Certificates(Clock clock) {
+ this.clock = Objects.requireNonNull(clock, "clock must be non-null");
+ }
+
+ /** Create a new certificate from csr signed by the given CA private key */
+ public X509Certificate create(Pkcs10Csr csr, X509Certificate caCertificate, PrivateKey caPrivateKey) {
+ var x500principal = caCertificate.getSubjectX500Principal();
+ var now = clock.instant();
+ var notBefore = now.minus(Duration.ofHours(1));
+ var notAfter = now.plus(CERTIFICATE_TTL);
+ return X509CertificateBuilder.fromCsr(csr,
+ x500principal,
+ notBefore,
+ notAfter,
+ caPrivateKey,
+ SHA256_WITH_ECDSA,
+ X509CertificateBuilder.generateRandomSerialNumber())
+ .build();
+ }
+
+ /** Returns the DNS name field from Subject Alternative Names in given csr */
+ public static String extractDnsName(Pkcs10Csr csr) {
+ return csr.getSubjectAlternativeNames().stream()
+ .filter(san -> san.getType() == DNS_NAME)
+ .map(SubjectAlternativeName::getValue)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("DNS name not found in CSR"));
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java
new file mode 100644
index 00000000000..b499debcc47
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java
@@ -0,0 +1,64 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.instance;
+
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A signed instance identity object that includes a client certificate. This is the result of a successful
+ * {@link InstanceRegistration}.
+ *
+ * @author mpolden
+ */
+public class InstanceIdentity {
+
+ private final String provider;
+ private final String service;
+ private final String instanceId;
+ private final Optional<X509Certificate> x509Certificate;
+
+ public InstanceIdentity(String provider, String service, String instanceId, Optional<X509Certificate> x509Certificate) {
+ this.provider = Objects.requireNonNull(provider, "provider must be non-null");
+ this.service = Objects.requireNonNull(service, "service must be non-null");
+ this.instanceId = Objects.requireNonNull(instanceId, "instanceId must be non-null");
+ this.x509Certificate = Objects.requireNonNull(x509Certificate, "x509Certificate must be non-null");
+ }
+
+ /** Same as {@link InstanceRegistration#domain()} */
+ public String provider() {
+ return provider;
+ }
+
+ /** Same as {@link InstanceRegistration#service()} ()} */
+ public String service() {
+ return service;
+ }
+
+ /** A unique identifier of the instance to which the certificate is issued */
+ public String instanceId() {
+ return instanceId;
+ }
+
+ /** The issued certificate */
+ public Optional<X509Certificate> x509Certificate() {
+ return x509Certificate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ InstanceIdentity that = (InstanceIdentity) o;
+ return provider.equals(that.provider) &&
+ service.equals(that.service) &&
+ instanceId.equals(that.instanceId) &&
+ x509Certificate.equals(that.x509Certificate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(provider, service, instanceId, x509Certificate);
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java
new file mode 100644
index 00000000000..7a9ec74e075
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java
@@ -0,0 +1,81 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.instance;
+
+import com.yahoo.security.Pkcs10Csr;
+
+import java.util.Objects;
+
+/**
+ * Information for registering a new instance in the system. This is similar to the InstanceRegisterInformation type in
+ * ZTS.
+ *
+ * @author mpolden
+ */
+public class InstanceRegistration {
+
+ private final String provider;
+ private final String domain;
+ private final String service;
+ private final String attestationData;
+ private final Pkcs10Csr csr;
+
+ public InstanceRegistration(String provider, String domain, String service, String attestationData, Pkcs10Csr csr) {
+ this.provider = Objects.requireNonNull(provider, "provider must be non-null");
+ this.domain = Objects.requireNonNull(domain, "domain must be non-null");
+ this.service = Objects.requireNonNull(service, "service must be non-null");
+ this.attestationData = Objects.requireNonNull(attestationData, "attestationData must be non-null");
+ this.csr = Objects.requireNonNull(csr, "csr must be non-null");
+ }
+
+ /** The provider which issued the attestation data contained in this */
+ public String provider() {
+ return provider;
+ }
+
+ /** Athenz domain of the instance */
+ public String domain() {
+ return domain;
+ }
+
+ /** Athenz service of the instance */
+ public String service() {
+ return service;
+ }
+
+ /** Host document describing this instance (received from config server) */
+ public String attestationData() {
+ return attestationData;
+ }
+
+ public Pkcs10Csr csr() {
+ return csr;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ InstanceRegistration that = (InstanceRegistration) o;
+ return provider.equals(that.provider) &&
+ domain.equals(that.domain) &&
+ service.equals(that.service) &&
+ attestationData.equals(that.attestationData) &&
+ csr.equals(that.csr);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(provider, domain, service, attestationData, csr);
+ }
+
+ @Override
+ public String toString() {
+ return "InstanceRegistration{" +
+ "provider='" + provider + '\'' +
+ ", domain='" + domain + '\'' +
+ ", service='" + service + '\'' +
+ ", attestationData='" + attestationData + '\'' +
+ ", csr=" + csr +
+ '}';
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java
new file mode 100644
index 00000000000..ba4f0ce932c
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java
@@ -0,0 +1,109 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi;
+
+import com.google.inject.Inject;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.LoggingRequestHandler;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
+import com.yahoo.restapi.Path;
+import com.yahoo.restapi.SlimeJsonResponse;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.hosted.ca.Certificates;
+import com.yahoo.vespa.hosted.ca.instance.InstanceIdentity;
+import com.yahoo.vespa.hosted.provision.restapi.v2.ErrorResponse;
+import com.yahoo.yolean.Exceptions;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.time.Clock;
+import java.util.Optional;
+import java.util.logging.Level;
+
+/**
+ * REST API for issuing and refreshing node certificates in a hosted Vespa system.
+ *
+ * The API implements the following subset of methods from the Athenz ZTS REST API:
+ * - Instance registration
+ * - Instance refresh
+ *
+ * @author mpolden
+ */
+public class CertificateAuthorityApiHandler extends LoggingRequestHandler {
+
+ private final SecretStore secretStore;
+ private final Certificates certificates;
+ private final SystemName system;
+
+ @Inject
+ public CertificateAuthorityApiHandler(Context ctx, SecretStore secretStore, Zone zone) {
+ this(ctx, secretStore, new Certificates(Clock.systemUTC()), zone.system());
+ }
+
+ CertificateAuthorityApiHandler(Context ctx, SecretStore secretStore, Certificates certificates, SystemName system) {
+ super(ctx);
+ this.secretStore = secretStore;
+ this.certificates = certificates;
+ this.system = system;
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ try {
+ switch (request.getMethod()) {
+ case POST: return handlePost(request);
+ default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is unsupported");
+ }
+ } catch (IllegalArgumentException e) {
+ return ErrorResponse.badRequest(Exceptions.toMessageString(e));
+ } catch (RuntimeException e) {
+ log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
+ return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
+ }
+ }
+
+ private HttpResponse handlePost(HttpRequest request) {
+ Path path = new Path(request.getUri());
+ if (path.matches("/ca/v1/instance/")) return registerInstance(request);
+ // TODO: Implement refresh
+ return ErrorResponse.notFoundError("Nothing at " + path);
+ }
+
+ private HttpResponse registerInstance(HttpRequest request) {
+ var body = slimeFromRequest(request);
+ var instanceRegistration = InstanceSerializer.registrationFromSlime(body);
+ var certificate = certificates.create(instanceRegistration.csr(), caCertificate(), caPrivateKey());
+ var instanceId = Certificates.extractDnsName(instanceRegistration.csr());
+ var identity = new InstanceIdentity(instanceRegistration.provider(), instanceRegistration.service(), instanceId,
+ Optional.of(certificate));
+ return new SlimeJsonResponse(InstanceSerializer.identityToSlime(identity));
+ }
+
+ /** Returns CA certificate from secret store */
+ private X509Certificate caCertificate() {
+ var keyName = String.format("vespa.external.%s.configserver.ca.cert.cert", system.value().toLowerCase());
+ return X509CertificateUtils.fromPem(secretStore.getSecret(keyName));
+ }
+
+ /** Returns CA private key from secret store */
+ private PrivateKey caPrivateKey() {
+ var keyName = String.format("vespa.external.%s.configserver.ca.key.key", system.value().toLowerCase());
+ return KeyUtils.fromPemEncodedPrivateKey(secretStore.getSecret(keyName));
+ }
+
+ private static Slime slimeFromRequest(HttpRequest request) {
+ try {
+ return SlimeUtils.jsonToSlime(request.getData().readAllBytes());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java
new file mode 100644
index 00000000000..46a09e9c6f2
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java
@@ -0,0 +1,54 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi;
+
+import com.yahoo.security.Pkcs10CsrUtils;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.ca.instance.InstanceIdentity;
+import com.yahoo.vespa.hosted.ca.instance.InstanceRegistration;
+
+/**
+ * @author mpolden
+ */
+public class InstanceSerializer {
+
+ private static final String PROVIDER_FIELD = "provider";
+ private static final String DOMAIN_FIELD = "domain";
+ private static final String SERVICE_FIELD = "service";
+ private static final String ATTESTATION_DATA_FIELD = "attestationData";
+ private static final String CSR_FIELD = "csr";
+ private static final String NAME_FIELD = "service";
+ private static final String INSTANCE_ID_FIELD = "instanceId";
+ private static final String X509_CERTIFICATE_FIELD = "x509Certificate";
+
+ private InstanceSerializer() {}
+
+ public static InstanceRegistration registrationFromSlime(Slime slime) {
+ Cursor root = slime.get();
+ return new InstanceRegistration(requireField(PROVIDER_FIELD, root).asString(),
+ requireField(DOMAIN_FIELD, root).asString(),
+ requireField(SERVICE_FIELD, root).asString(),
+ requireField(ATTESTATION_DATA_FIELD, root).asString(),
+ Pkcs10CsrUtils.fromPem(requireField(CSR_FIELD, root).asString()));
+ }
+
+ public static Slime identityToSlime(InstanceIdentity identity) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ root.setString(PROVIDER_FIELD, identity.provider());
+ root.setString(NAME_FIELD, identity.service());
+ root.setString(INSTANCE_ID_FIELD, identity.instanceId());
+ identity.x509Certificate()
+ .map(X509CertificateUtils::toPem)
+ .ifPresent(pem -> root.setString(X509_CERTIFICATE_FIELD, pem));
+ return slime;
+ }
+
+ private static Cursor requireField(String fieldName, Cursor root) {
+ var field = root.field(fieldName);
+ if (!field.valid()) throw new IllegalArgumentException("Missing required field '" + fieldName + "'");
+ return field;
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
new file mode 100644
index 00000000000..4946de93f6d
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
@@ -0,0 +1,60 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca;
+
+import com.yahoo.security.KeyAlgorithm;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.Pkcs10Csr;
+import com.yahoo.security.Pkcs10CsrBuilder;
+import com.yahoo.security.SignatureAlgorithm;
+import com.yahoo.security.SubjectAlternativeName;
+import com.yahoo.security.X509CertificateBuilder;
+
+import javax.security.auth.x500.X500Principal;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+
+import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
+
+/**
+ * Helper class for creating certificates, CSRs etc. for testing purposes.
+ *
+ * @author mpolden
+ */
+public class CertificateTester {
+
+ private CertificateTester() {}
+
+ public static X509Certificate createCertificate() {
+ var keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ return createCertificate("subject", keyPair);
+ }
+
+ public static X509Certificate createCertificate(String cn, KeyPair keyPair) {
+ var subject = new X500Principal("CN=" + cn);
+ return X509CertificateBuilder.fromKeypair(keyPair,
+ subject,
+ Instant.EPOCH,
+ Instant.EPOCH.plus(Duration.ofMinutes(1)),
+ SHA256_WITH_ECDSA,
+ BigInteger.ONE)
+ .build();
+ }
+
+ public static Pkcs10Csr createCsr() {
+ return createCsr(null);
+ }
+
+ public static Pkcs10Csr createCsr(String dnsName) {
+ X500Principal subject = new X500Principal("CN=subject");
+ KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ var builder = Pkcs10CsrBuilder.fromKeypair(subject, keyPair, SignatureAlgorithm.SHA512_WITH_ECDSA);
+ if (dnsName != null) {
+ builder = builder.addSubjectAlternativeName(SubjectAlternativeName.Type.DNS_NAME, dnsName);
+ }
+ return builder.build();
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java
new file mode 100644
index 00000000000..4e306d9a70e
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java
@@ -0,0 +1,33 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca;
+
+import com.yahoo.security.KeyAlgorithm;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.test.ManualClock;
+import org.junit.Test;
+
+import java.time.Duration;
+
+import static java.time.temporal.ChronoUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class CertificatesTest {
+
+ @Test
+ public void expiry() {
+ var clock = new ManualClock();
+ var certificates = new Certificates(clock);
+ var csr = CertificateTester.createCsr();
+ var keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ var caCertificate = CertificateTester.createCertificate("CA", keyPair);
+ var certificate = certificates.create(csr, caCertificate, keyPair.getPrivate());
+ var now = clock.instant();
+
+ assertEquals(now.minus(Duration.ofHours(1)).truncatedTo(SECONDS), certificate.getNotBefore().toInstant());
+ assertEquals(now.plus(Duration.ofDays(30)).truncatedTo(SECONDS), certificate.getNotAfter().toInstant());
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
new file mode 100644
index 00000000000..4393c3a25b9
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
@@ -0,0 +1,101 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi;
+
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.security.KeyAlgorithm;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.Pkcs10Csr;
+import com.yahoo.security.Pkcs10CsrUtils;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.hosted.ca.CertificateTester;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.net.ssl.SSLContext;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author mpolden
+ */
+public class CertificateAuthorityApiTest extends ContainerTester {
+
+ @Before
+ public void before() {
+ setCaCertificateAndKey();
+ }
+
+ @Test
+ public void register_instance() throws Exception {
+ // POST instance registration
+ var csr = CertificateTester.createCsr("node1.example.com");
+ assertRegistration(new Request("http://localhost:12345/ca/v1/instance/",
+ instanceRegistrationJson(csr),
+ Request.Method.POST));
+
+ // POST instance registration with ZTS client
+ var ztsClient = new DefaultZtsClient(URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
+ new AthenzService("vespa.external", "tenant"),
+ "identity document generated by config server",
+ csr);
+ assertEquals("CN=Vespa CA", instanceIdentity.certificate().getIssuerX500Principal().getName());
+ }
+
+ @Test
+ public void invalid_register_instance() {
+ // POST instance registration with missing fields
+ assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Missing required field 'provider'\"}",
+ new Request("http://localhost:12345/ca/v1/instance/",
+ new byte[0],
+ Request.Method.POST));
+
+ // POST instance registration without DNS name in CSR
+ var csr = CertificateTester.createCsr();
+ var request = new Request("http://localhost:12345/ca/v1/instance/",
+ instanceRegistrationJson(csr),
+ Request.Method.POST);
+ assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"DNS name not found in CSR\"}", request);
+ }
+
+ private void setCaCertificateAndKey() {
+ var keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ var caCertificatePem = X509CertificateUtils.toPem(CertificateTester.createCertificate("Vespa CA", keyPair));
+ var privateKeyPem = KeyUtils.toPem(keyPair.getPrivate());
+ secretStore().setSecret("vespa.external.main.configserver.ca.cert.cert", caCertificatePem)
+ .setSecret("vespa.external.main.configserver.ca.key.key", privateKeyPem);
+ }
+
+ private void assertRegistration(Request request) {
+ assertResponse(200, (body) -> {
+ var slime = SlimeUtils.jsonToSlime(body);
+ var root = slime.get();
+ assertEquals("provider_prod_us-north-1", root.field("provider").asString());
+ assertEquals("tenant", root.field("service").asString());
+ assertEquals("node1.example.com", root.field("instanceId").asString());
+ var pemEncodedCertificate = root.field("x509Certificate").asString();
+ assertTrue("Response contains PEM certificate",
+ pemEncodedCertificate.startsWith("-----BEGIN CERTIFICATE-----") &&
+ pemEncodedCertificate.endsWith("-----END CERTIFICATE-----\n"));
+ }, request);
+ }
+
+ private static byte[] instanceRegistrationJson(Pkcs10Csr csr) {
+ var csrPem = Pkcs10CsrUtils.toPem(csr);
+ var json = "{\n" +
+ " \"provider\": \"provider_prod_us-north-1\",\n" +
+ " \"domain\": \"vespa.external\",\n" +
+ " \"service\": \"tenant\",\n" +
+ " \"attestationData\": \"identity document generated by config server\",\n" +
+ " \"csr\": \"" + csrPem + "\"\n" +
+ "}";
+ return json.getBytes(StandardCharsets.UTF_8);
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
new file mode 100644
index 00000000000..2ca45cf7e56
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
@@ -0,0 +1,72 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi;
+
+import com.yahoo.application.Networking;
+import com.yahoo.application.container.JDisc;
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock;
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.UncheckedIOException;
+import java.nio.charset.CharacterCodingException;
+import java.util.function.Consumer;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * The superclass of REST API tests which require a functional container instance.
+ *
+ * @author mpolden
+ */
+public class ContainerTester {
+
+ private JDisc container;
+
+ @Before
+ public void startContainer() {
+ container = JDisc.fromServicesXml(servicesXml(), Networking.enable);
+ }
+
+ @After
+ public void stopContainer() {
+ container.close();
+ }
+
+ public SecretStoreMock secretStore() {
+ return (SecretStoreMock) container.components().getComponent(SecretStoreMock.class.getName());
+ }
+
+ public void assertResponse(int expectedStatus, String expectedBody, Request request) {
+ assertResponse(expectedStatus, (body) -> assertEquals(expectedBody, body), request);
+ }
+
+ public void assertResponse(int expectedStatus, Consumer<String> bodyAsserter, Request request) {
+ var response = container.handleRequest(request);
+ try {
+ bodyAsserter.accept(response.getBodyAsString());
+ } catch (CharacterCodingException e) {
+ throw new UncheckedIOException(e);
+ }
+ assertEquals(expectedStatus, response.getStatus());
+ assertEquals("application/json; charset=UTF-8", response.getHeaders().getFirst("Content-Type"));
+ }
+
+ private static String servicesXml() {
+ return "<container version='1.0'>\n" +
+ " <config name=\"container.handler.threadpool\">\n" +
+ " <maxthreads>10</maxthreads>\n" +
+ " </config> \n" +
+ " <component id='com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors'/>\n" +
+ " <component id='com.yahoo.config.provision.Zone'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock'/>\n" +
+ " <handler id='com.yahoo.vespa.hosted.ca.restapi.CertificateAuthorityApiHandler'>\n" +
+ " <binding>http://*/ca/v1/*</binding>\n" +
+ " </handler>\n" +
+ " <http>\n" +
+ " <server id='default' port='12345'/>\n" +
+ " </http>\n" +
+ "</container>";
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
new file mode 100644
index 00000000000..51010422b6d
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
@@ -0,0 +1,66 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi;
+
+import com.yahoo.security.Pkcs10CsrUtils;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.hosted.ca.CertificateTester;
+import com.yahoo.vespa.hosted.ca.instance.InstanceIdentity;
+import com.yahoo.vespa.hosted.ca.instance.InstanceRegistration;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class InstanceSerializerTest {
+
+ @Test
+ public void deserialize_instance_registration() {
+ var csr = CertificateTester.createCsr();
+ var csrPem = Pkcs10CsrUtils.toPem(csr);
+ var json = "{\n" +
+ " \"provider\": \"provider_prod_us-north-1\",\n" +
+ " \"domain\": \"vespa.external\",\n" +
+ " \"service\": \"tenant\",\n" +
+ " \"attestationData\": \"identity document from configserevr\",\n" +
+ " \"csr\": \"" + csrPem + "\"\n" +
+ "}";
+ var instanceRegistration = new InstanceRegistration("provider_prod_us-north-1", "vespa.external",
+ "tenant", "identity document from configserevr",
+ csr);
+ var deserialized = InstanceSerializer.registrationFromSlime(SlimeUtils.jsonToSlime(json));
+ assertEquals(instanceRegistration, deserialized);
+ }
+
+ @Test
+ public void serialize_instance_identity() {
+ var certificate = CertificateTester.createCertificate();
+ var pem = X509CertificateUtils.toPem(certificate);
+ var identity = new InstanceIdentity("provider_prod_us-north-1", "tenant", "node1.example.com",
+ Optional.of(certificate));
+ var json = "{" +
+ "\"provider\":\"provider_prod_us-north-1\"," +
+ "\"service\":\"tenant\"," +
+ "\"instanceId\":\"node1.example.com\"," +
+ "\"x509Certificate\":\"" + pem.replace("\n", "\\n") + "\"" +
+ "}";
+ assertEquals(json, asJsonString(InstanceSerializer.identityToSlime(identity)));
+ }
+
+ private static String asJsonString(Slime slime) {
+ try {
+ return new String(SlimeUtils.toJsonBytes(slime), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java
new file mode 100644
index 00000000000..a53bf9d9fd3
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java
@@ -0,0 +1,34 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi.mock;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author mpolden
+ */
+public class SecretStoreMock extends AbstractComponent implements SecretStore {
+
+ private final Map<String, String> secrets = new HashMap<>();
+
+ public SecretStoreMock setSecret(String key, String value) {
+ secrets.put(key, value);
+ return this;
+ }
+
+ @Override
+ public String getSecret(String key) {
+ if (!secrets.containsKey(key)) throw new RuntimeException("No such key '" + key + "'");
+ return secrets.get(key);
+ }
+
+ @Override
+ public String getSecret(String key, int version) {
+ if (!secrets.containsKey(key)) throw new RuntimeException("No such key '" + key + "'");
+ return secrets.get(key);
+ }
+
+}
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java
index 40bb69111e0..8810a13f909 100644
--- a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java
+++ b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/FlagsHandler.java
@@ -6,6 +6,7 @@ import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
import com.yahoo.log.LogLevel;
+import com.yahoo.restapi.ErrorResponse;
import com.yahoo.restapi.Path;
import com.yahoo.vespa.configserver.flags.FlagsDb;
import com.yahoo.vespa.flags.FlagDefinition;
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java b/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java
deleted file mode 100644
index e5568514894..00000000000
--- a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/SlimeJsonResponse.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.configserver.flags.http;
-
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.slime.JsonFormat;
-import com.yahoo.slime.Slime;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A generic Json response using Slime for JSON encoding
- *
- * @author bratseth
- */
-public class SlimeJsonResponse extends HttpResponse {
-
- private final Slime slime;
-
- public SlimeJsonResponse(Slime slime) {
- super(200);
- this.slime = slime;
- }
-
- public SlimeJsonResponse(int statusCode, Slime slime) {
- super(statusCode);
- this.slime = slime;
- }
-
- @Override
- public void render(OutputStream stream) throws IOException {
- new JsonFormat(true).encode(stream, slime);
- }
-
- @Override
- public String getContentType() { return "application/json"; }
-
-}
diff --git a/container-core/abi-spec.json b/container-core/abi-spec.json
index 81af58b6681..9292db89eb5 100644
--- a/container-core/abi-spec.json
+++ b/container-core/abi-spec.json
@@ -854,6 +854,57 @@
],
"fields": []
},
+ "com.yahoo.restapi.ErrorResponse$errorCodes": {
+ "superClass": "java.lang.Enum",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "final",
+ "enum"
+ ],
+ "methods": [
+ "public static com.yahoo.restapi.ErrorResponse$errorCodes[] values()",
+ "public static com.yahoo.restapi.ErrorResponse$errorCodes valueOf(java.lang.String)"
+ ],
+ "fields": [
+ "public static final enum com.yahoo.restapi.ErrorResponse$errorCodes NOT_FOUND",
+ "public static final enum com.yahoo.restapi.ErrorResponse$errorCodes BAD_REQUEST",
+ "public static final enum com.yahoo.restapi.ErrorResponse$errorCodes FORBIDDEN",
+ "public static final enum com.yahoo.restapi.ErrorResponse$errorCodes METHOD_NOT_ALLOWED",
+ "public static final enum com.yahoo.restapi.ErrorResponse$errorCodes INTERNAL_SERVER_ERROR",
+ "public static final enum com.yahoo.restapi.ErrorResponse$errorCodes UNAUTHORIZED"
+ ]
+ },
+ "com.yahoo.restapi.ErrorResponse": {
+ "superClass": "com.yahoo.restapi.SlimeJsonResponse",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public void <init>(int, java.lang.String, java.lang.String)",
+ "public static com.yahoo.restapi.ErrorResponse notFoundError(java.lang.String)",
+ "public static com.yahoo.restapi.ErrorResponse internalServerError(java.lang.String)",
+ "public static com.yahoo.restapi.ErrorResponse badRequest(java.lang.String)",
+ "public static com.yahoo.restapi.ErrorResponse forbidden(java.lang.String)",
+ "public static com.yahoo.restapi.ErrorResponse unauthorized(java.lang.String)",
+ "public static com.yahoo.restapi.ErrorResponse methodNotAllowed(java.lang.String)"
+ ],
+ "fields": []
+ },
+ "com.yahoo.restapi.MessageResponse": {
+ "superClass": "com.yahoo.container.jdisc.HttpResponse",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public void <init>(java.lang.String)",
+ "public void render(java.io.OutputStream)",
+ "public java.lang.String getContentType()"
+ ],
+ "fields": []
+ },
"com.yahoo.restapi.Path": {
"superClass": "java.lang.Object",
"interfaces": [],
@@ -872,5 +923,62 @@
"public java.lang.String toString()"
],
"fields": []
+ },
+ "com.yahoo.restapi.ResourceResponse": {
+ "superClass": "com.yahoo.container.jdisc.HttpResponse",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public varargs void <init>(com.yahoo.container.jdisc.HttpRequest, java.lang.String[])",
+ "public void render(java.io.OutputStream)",
+ "public java.lang.String getContentType()"
+ ],
+ "fields": []
+ },
+ "com.yahoo.restapi.SlimeJsonResponse": {
+ "superClass": "com.yahoo.container.jdisc.HttpResponse",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public void <init>(com.yahoo.slime.Slime)",
+ "public void <init>(int, com.yahoo.slime.Slime)",
+ "public void render(java.io.OutputStream)",
+ "public java.lang.String getContentType()"
+ ],
+ "fields": []
+ },
+ "com.yahoo.restapi.StringResponse": {
+ "superClass": "com.yahoo.container.jdisc.HttpResponse",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public void <init>(java.lang.String)",
+ "public void render(java.io.OutputStream)"
+ ],
+ "fields": []
+ },
+ "com.yahoo.restapi.Uri": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public"
+ ],
+ "methods": [
+ "public void <init>(java.net.URI)",
+ "public void <init>(java.lang.String)",
+ "public com.yahoo.restapi.Uri append(java.lang.String)",
+ "public com.yahoo.restapi.Uri withoutParameters()",
+ "public com.yahoo.restapi.Uri withPath(java.lang.String)",
+ "public com.yahoo.restapi.Uri withTrailingSlash()",
+ "public java.net.URI toURI()",
+ "public java.lang.String toString()"
+ ],
+ "fields": []
}
-}
+} \ No newline at end of file
diff --git a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java b/container-core/src/main/java/com/yahoo/restapi/ErrorResponse.java
index b9e5c75fe22..d3e81a10720 100644
--- a/configserver-flags/src/main/java/com/yahoo/vespa/configserver/flags/http/ErrorResponse.java
+++ b/container-core/src/main/java/com/yahoo/restapi/ErrorResponse.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.configserver.flags.http;
+package com.yahoo.restapi;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/MessageResponse.java b/container-core/src/main/java/com/yahoo/restapi/MessageResponse.java
index 8b2f0e9f09d..8669d4f9b0c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/MessageResponse.java
+++ b/container-core/src/main/java/com/yahoo/restapi/MessageResponse.java
@@ -1,5 +1,5 @@
// 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.restapi;
+package com.yahoo.restapi;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.slime.JsonFormat;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ResourceResponse.java b/container-core/src/main/java/com/yahoo/restapi/ResourceResponse.java
index 550b47d8280..4852bfafa60 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ResourceResponse.java
+++ b/container-core/src/main/java/com/yahoo/restapi/ResourceResponse.java
@@ -1,5 +1,5 @@
// 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.restapi;
+package com.yahoo.restapi;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/SlimeJsonResponse.java b/container-core/src/main/java/com/yahoo/restapi/SlimeJsonResponse.java
index 81b07b81efb..2473da3578d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/SlimeJsonResponse.java
+++ b/container-core/src/main/java/com/yahoo/restapi/SlimeJsonResponse.java
@@ -1,5 +1,5 @@
// 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.restapi;
+package com.yahoo.restapi;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.slime.JsonFormat;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/StringResponse.java b/container-core/src/main/java/com/yahoo/restapi/StringResponse.java
index 1fc30b7d880..55ea22880de 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/StringResponse.java
+++ b/container-core/src/main/java/com/yahoo/restapi/StringResponse.java
@@ -1,10 +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.restapi;
+package com.yahoo.restapi;
import com.yahoo.container.jdisc.HttpResponse;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
/**
* @author bratseth
@@ -20,7 +21,7 @@ public class StringResponse extends HttpResponse {
@Override
public void render(OutputStream stream) throws IOException {
- stream.write(message.getBytes("utf-8"));
+ stream.write(message.getBytes(StandardCharsets.UTF_8));
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Uri.java b/container-core/src/main/java/com/yahoo/restapi/Uri.java
index 479e7434f9b..c1b0d19eb3e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Uri.java
+++ b/container-core/src/main/java/com/yahoo/restapi/Uri.java
@@ -1,5 +1,5 @@
// 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.restapi;
+package com.yahoo.restapi;
import java.net.URI;
import java.net.URISyntaxException;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ErrorResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ErrorResponse.java
deleted file mode 100644
index deee3357771..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/ErrorResponse.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.restapi;
-
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
-import com.yahoo.yolean.Exceptions;
-
-import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
-import static com.yahoo.jdisc.Response.Status.CONFLICT;
-import static com.yahoo.jdisc.Response.Status.FORBIDDEN;
-import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR;
-import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED;
-import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
-import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED;
-
-/**
- * A HTTP JSON response containing an error code and a message
- *
- * @author bratseth
- */
-public class ErrorResponse extends SlimeJsonResponse {
-
- public enum errorCodes {
- NOT_FOUND,
- BAD_REQUEST,
- FORBIDDEN,
- METHOD_NOT_ALLOWED,
- INTERNAL_SERVER_ERROR,
- UNAUTHORIZED
- }
-
- public ErrorResponse(int statusCode, String errorType, String message) {
- super(statusCode, asSlimeMessage(errorType, message));
- }
-
- private static Slime asSlimeMessage(String errorType, String message) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- root.setString("error-code", errorType);
- root.setString("message", message);
- return slime;
- }
-
- public static ErrorResponse notFoundError(String message) {
- return new ErrorResponse(NOT_FOUND, errorCodes.NOT_FOUND.name(), message);
- }
-
- public static ErrorResponse internalServerError(String message) {
- return new ErrorResponse(INTERNAL_SERVER_ERROR, errorCodes.INTERNAL_SERVER_ERROR.name(), message);
- }
-
- public static ErrorResponse badRequest(String message) {
- return new ErrorResponse(BAD_REQUEST, errorCodes.BAD_REQUEST.name(), message);
- }
-
- public static ErrorResponse forbidden(String message) {
- return new ErrorResponse(FORBIDDEN, errorCodes.FORBIDDEN.name(), message);
- }
-
- public static ErrorResponse unauthorized(String message) {
- return new ErrorResponse(UNAUTHORIZED, errorCodes.UNAUTHORIZED.name(), message);
- }
-
- public static ErrorResponse methodNotAllowed(String message) {
- return new ErrorResponse(METHOD_NOT_ALLOWED, errorCodes.METHOD_NOT_ALLOWED.name(), message);
- }
-
- public static ErrorResponse from(ConfigServerException e) {
- switch (e.getErrorCode()) {
- case NOT_FOUND:
- return new ErrorResponse(NOT_FOUND, e.getErrorCode().name(), Exceptions.toMessageString(e));
- case ACTIVATION_CONFLICT:
- return new ErrorResponse(CONFLICT, e.getErrorCode().name(), Exceptions.toMessageString(e));
- case INTERNAL_SERVER_ERROR:
- return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getErrorCode().name(), Exceptions.toMessageString(e));
- default:
- return new ErrorResponse(BAD_REQUEST, e.getErrorCode().name(), Exceptions.toMessageString(e));
- }
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index c7b8e148e31..123da447757 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -66,10 +66,10 @@ import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel;
import com.yahoo.vespa.hosted.controller.deployment.TestConfigSerializer;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.MessageResponse;
+import com.yahoo.restapi.ResourceResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import com.yahoo.vespa.hosted.controller.rotation.RotationState;
import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
@@ -108,6 +108,10 @@ import java.util.StringJoiner;
import java.util.logging.Level;
import java.util.stream.Collectors;
+import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
+import static com.yahoo.jdisc.Response.Status.CONFLICT;
+import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR;
+import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
import static java.util.stream.Collectors.joining;
/**
@@ -166,7 +170,16 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return ErrorResponse.badRequest(Exceptions.toMessageString(e));
}
catch (ConfigServerException e) {
- return ErrorResponse.from(e);
+ switch (e.getErrorCode()) {
+ case NOT_FOUND:
+ return new ErrorResponse(NOT_FOUND, e.getErrorCode().name(), Exceptions.toMessageString(e));
+ case ACTIVATION_CONFLICT:
+ return new ErrorResponse(CONFLICT, e.getErrorCode().name(), Exceptions.toMessageString(e));
+ case INTERNAL_SERVER_ERROR:
+ return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getErrorCode().name(), Exceptions.toMessageString(e));
+ default:
+ return new ErrorResponse(BAD_REQUEST, e.getErrorCode().name(), Exceptions.toMessageString(e));
+ }
}
catch (RuntimeException e) {
log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
index 5f7a3e1dcc8..697e95e75b3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
@@ -29,8 +29,8 @@ import com.yahoo.vespa.hosted.controller.deployment.RunLog;
import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
import com.yahoo.vespa.hosted.controller.deployment.Step;
import com.yahoo.vespa.hosted.controller.deployment.Versions;
-import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.MessageResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import java.net.URI;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java
index 976919f34be..3166a380f47 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java
@@ -7,7 +7,7 @@ import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
-import com.yahoo.vespa.hosted.controller.restapi.Uri;
+import com.yahoo.restapi.Uri;
import com.yahoo.vespa.serviceview.bindings.ApplicationView;
import com.yahoo.vespa.serviceview.bindings.ClusterView;
import com.yahoo.vespa.serviceview.bindings.ServiceView;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiHandler.java
index 61e5dc359d2..d36a7487e59 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiHandler.java
@@ -14,9 +14,9 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.integration.entity.EntityService;
import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.ResourceResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.yolean.Exceptions;
import java.util.Map;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/AuditLogResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/AuditLogResponse.java
index 01959104491..f2c1d2917dc 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/AuditLogResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/AuditLogResponse.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.restapi.controller;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLog;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
/**
* @author mpolden
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
index dde79e78850..2cadd864df1 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
@@ -13,9 +13,9 @@ import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler;
import com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance;
import com.yahoo.vespa.hosted.controller.maintenance.Upgrader;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.MessageResponse;
+import com.yahoo.restapi.ResourceResponse;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion.Confidence;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/JobsResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/JobsResponse.java
index 6e872038a24..82e76ddeda4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/JobsResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/JobsResponse.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.restapi.controller;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
/**
* A response containing maintenance job status
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/UpgraderResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/UpgraderResponse.java
index fd8eb85a0ec..8968b5ecbe0 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/UpgraderResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/UpgraderResponse.java
@@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.restapi.controller;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.maintenance.Upgrader;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
/**
* @author mpolden
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java
index 6ed9db7455c..8b300c10d1d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java
@@ -8,8 +8,8 @@ import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository;
import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumer;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.StringResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.StringResponse;
import java.time.Clock;
import java.util.Optional;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java
index 0c5cfc539f1..f3c45ca7221 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiHandler.java
@@ -10,7 +10,7 @@ import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
+import com.yahoo.restapi.ErrorResponse;
import com.yahoo.yolean.Exceptions;
import java.io.OutputStream;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
index 3eb57875dad..b2bc6d72044 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
@@ -15,9 +15,9 @@ import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.application.JobList;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
-import com.yahoo.vespa.hosted.controller.restapi.Uri;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.Uri;
import com.yahoo.vespa.hosted.controller.restapi.application.EmptyResponse;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java
index fde013b223c..450f4481c5f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java
@@ -18,9 +18,9 @@ import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.config.provision.zone.ZoneList;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.MessageResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.versions.OsVersion;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java
index 1aa883359ee..8c445fe3a0a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java
@@ -7,9 +7,9 @@ import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.slime.Slime;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
+import com.yahoo.restapi.ErrorResponse;
import com.yahoo.restapi.Path;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.statuspage.config.StatuspageConfig;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
index c32ab6726e9..410194a8ef7 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
@@ -19,9 +19,9 @@ import com.yahoo.vespa.hosted.controller.api.integration.user.UserId;
import com.yahoo.vespa.hosted.controller.api.integration.user.UserManagement;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.RoleDefinition;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.MessageResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.vespa.hosted.controller.restapi.application.EmptyResponse;
import com.yahoo.yolean.Exceptions;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java
index 6cfaed93fa9..53373bb228a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiHandler.java
@@ -3,18 +3,16 @@ package com.yahoo.vespa.hosted.controller.restapi.zone.v1;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.Zone;
import com.yahoo.config.provision.zone.ZoneApi;
-import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
+import com.yahoo.restapi.ErrorResponse;
import com.yahoo.restapi.Path;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.yolean.Exceptions;
import java.util.Comparator;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
index f0259fc4d51..5ce679276f7 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
@@ -15,8 +15,8 @@ import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler;
import com.yahoo.vespa.hosted.controller.proxy.ConfigServerRestExecutor;
import com.yahoo.vespa.hosted.controller.proxy.ProxyException;
import com.yahoo.vespa.hosted.controller.proxy.ProxyRequest;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.yolean.Exceptions;
import java.io.IOException;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java
index 4d70987ff28..d6e1af07938 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerProxyMock.java
@@ -5,7 +5,7 @@ import com.yahoo.component.AbstractComponent;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.vespa.hosted.controller.proxy.ConfigServerRestExecutor;
import com.yahoo.vespa.hosted.controller.proxy.ProxyRequest;
-import com.yahoo.vespa.hosted.controller.restapi.StringResponse;
+import com.yahoo.restapi.StringResponse;
import java.io.InputStream;
import java.util.Optional;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
index b952ae096b0..865bcc61837 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java
@@ -182,7 +182,7 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer {
.withTrustStore(trustStorePath, KeyStoreType.JKS)
.build();
try {
- try (ZtsClient ztsClient = new DefaultZtsClient(ztsEndpoint, context.identity(), containerIdentitySslContext)) {
+ try (ZtsClient ztsClient = new DefaultZtsClient(ztsEndpoint, containerIdentitySslContext)) {
InstanceIdentity instanceIdentity =
ztsClient.refreshInstance(
configserverIdentity,
diff --git a/security-utils/src/main/java/com/yahoo/security/Pkcs10Csr.java b/security-utils/src/main/java/com/yahoo/security/Pkcs10Csr.java
index e08ee117fcd..7bcf67b64b6 100644
--- a/security-utils/src/main/java/com/yahoo/security/Pkcs10Csr.java
+++ b/security-utils/src/main/java/com/yahoo/security/Pkcs10Csr.java
@@ -12,6 +12,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import javax.security.auth.x500.X500Principal;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import static java.util.Collections.emptyList;
@@ -68,4 +69,17 @@ public class Pkcs10Csr {
.map(attribute -> Extensions.getInstance(attribute.getAttrValues().getObjectAt(0)));
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Pkcs10Csr pkcs10Csr = (Pkcs10Csr) o;
+ return Objects.equals(csr, pkcs10Csr.csr);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(csr);
+ }
+
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
index 7116bf72ec4..8bd0d0b50d4 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
@@ -47,20 +47,18 @@ import static java.util.stream.Collectors.toList;
public class DefaultZtsClient extends ClientBase implements ZtsClient {
private final URI ztsUrl;
- private final AthenzIdentity identity;
- public DefaultZtsClient(URI ztsUrl, AthenzIdentity identity, SSLContext sslContext) {
- this(ztsUrl, identity, () -> sslContext);
+ public DefaultZtsClient(URI ztsUrl, SSLContext sslContext) {
+ this(ztsUrl, () -> sslContext);
}
public DefaultZtsClient(URI ztsUrl, ServiceIdentityProvider identityProvider) {
- this(ztsUrl, identityProvider.identity(), identityProvider::getIdentitySslContext);
+ this(ztsUrl, identityProvider::getIdentitySslContext);
}
- private DefaultZtsClient(URI ztsUrl, AthenzIdentity identity, Supplier<SSLContext> sslContextSupplier) {
+ private DefaultZtsClient(URI ztsUrl, Supplier<SSLContext> sslContextSupplier) {
super("vespa-zts-client", sslContextSupplier, ZtsClientException::new);
this.ztsUrl = addTrailingSlash(ztsUrl);
- this.identity = identity;
}
@Override
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
index 52d33f79c1d..e6b74d9df4c 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java
@@ -79,7 +79,7 @@ public class SignedIdentityDocumentEntity {
Objects.equals(instanceHostname, that.instanceHostname) &&
Objects.equals(createdAt, that.createdAt) &&
Objects.equals(ipAddresses, that.ipAddresses) &&
- Objects.equals(identityType, identityType);
+ Objects.equals(identityType, that.identityType);
}
@Override
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
index eccf1088cce..3b733e05708 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
@@ -99,7 +99,7 @@ class AthenzCredentialsService {
document.ipAddresses(),
newKeyPair);
- try (ZtsClient ztsClient = new DefaultZtsClient(ztsEndpoint, tenantIdentity, sslContext)) {
+ try (ZtsClient ztsClient = new DefaultZtsClient(ztsEndpoint, sslContext)) {
InstanceIdentity instanceIdentity =
ztsClient.refreshInstance(
configserverIdentity,
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
index bff40d67fa6..d3be7829927 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
@@ -245,7 +245,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
}
private DefaultZtsClient createZtsClient() {
- return new DefaultZtsClient(ztsEndpoint, identity(), getIdentitySslContext());
+ return new DefaultZtsClient(ztsEndpoint, getIdentitySslContext());
}
@Override