summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2021-11-17 09:22:37 +0100
committerGitHub <noreply@github.com>2021-11-17 09:22:37 +0100
commit19339fff868707dbcd08efa910d67ba20f7154d1 (patch)
treed98abb474b85f9a6267156b1ce621cb7e352be67 /controller-server
parentac75c95519127d3bcc56e8ae30c8b429e0ef51b5 (diff)
parent803d44ee818d1ebeaf8e7aa16a0630c0a040a60c (diff)
Merge pull request #20038 from vespa-engine/mpolden/add-names-to-cert
Include declared application endpoints in certificate
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java24
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java58
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java25
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java66
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java65
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java6
7 files changed, 174 insertions, 72 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index 2d7f9b27334..01b12a5136d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -376,7 +376,7 @@ public class ApplicationController {
&& run.testerCertificate().isPresent())
applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get());
- endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec().instance(instance.name()));
+ endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec());
containerEndpoints = controller.routing().containerEndpointsOf(application.get(), job.application().instance(), zone);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index 16f12b3ac07..37266dd6dac 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -173,27 +173,41 @@ public class RoutingController {
}
/** Returns certificate DNS names (CN and SAN values) for given deployment */
- public List<String> certificateDnsNames(DeploymentId deployment) {
+ public List<String> certificateDnsNames(DeploymentId deployment, DeploymentSpec deploymentSpec) {
List<String> endpointDnsNames = new ArrayList<>();
// We add first an endpoint name based on a hash of the application ID,
// as the certificate provider requires the first CN to be < 64 characters long.
endpointDnsNames.add(commonNameHashOf(deployment.applicationId(), controller.system()));
- // Add wildcard names for global endpoints when deploying to production
List<Endpoint.EndpointBuilder> builders = new ArrayList<>();
if (deployment.zoneId().environment().isProduction()) {
+ // Add default and wildcard names for global endpoints
builders.add(Endpoint.of(deployment.applicationId()).target(EndpointId.defaultId()));
builders.add(Endpoint.of(deployment.applicationId()).wildcard());
+
+ // Add default and wildcard names for each region targeted by application endpoints
+ List<DeploymentId> deploymentTargets = deploymentSpec.endpoints().stream()
+ .map(com.yahoo.config.application.api.Endpoint::targets)
+ .flatMap(Collection::stream)
+ .map(com.yahoo.config.application.api.Endpoint.Target::region)
+ .distinct()
+ .map(region -> new DeploymentId(deployment.applicationId(), ZoneId.from(Environment.prod, region)))
+ .collect(Collectors.toUnmodifiableList());
+ for (var targetDeployment : deploymentTargets) {
+ builders.add(Endpoint.of(targetDeployment.applicationId()).targetApplication(EndpointId.defaultId(), targetDeployment));
+ builders.add(Endpoint.of(targetDeployment.applicationId()).wildcardApplication(targetDeployment));
+ }
}
- // Add wildcard names for zone endpoints
+ // Add default and wildcard names for zone endpoints
builders.add(Endpoint.of(deployment.applicationId()).target(ClusterSpec.Id.from("default"), deployment));
builders.add(Endpoint.of(deployment.applicationId()).wildcard(deployment));
- // Build all endpoints
+ // Build all certificate names
for (var builder : builders) {
- Endpoint endpoint = builder.routingMethod(RoutingMethod.exclusive)
+ Endpoint endpoint = builder.certificateName()
+ .routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(controller.system());
endpointDnsNames.add(endpoint.dnsName());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index 35601dd94dd..1e917dd1376 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -46,7 +46,7 @@ public class Endpoint {
private Endpoint(TenantAndApplicationId application, Optional<InstanceName> instanceName, EndpointId id,
ClusterSpec.Id cluster, URI url, List<Target> targets, Scope scope, Port port, boolean legacy,
- RoutingMethod routingMethod) {
+ RoutingMethod routingMethod, boolean certificateName) {
Objects.requireNonNull(application, "application must be non-null");
Objects.requireNonNull(instanceName, "instanceName must be non-null");
Objects.requireNonNull(cluster, "cluster must be non-null");
@@ -56,6 +56,9 @@ public class Endpoint {
Objects.requireNonNull(routingMethod, "routingMethod must be non-null");
if (scope.multiDeployment()) {
if (id == null) throw new IllegalArgumentException("Endpoint ID must be set for multi-deployment endpoints");
+ if (!certificateName && targets.isEmpty()) {
+ throw new IllegalArgumentException("At least one target must be given for " + scope + " endpoints");
+ }
} else {
if (scope == Scope.zone && id != null) throw new IllegalArgumentException("Endpoint ID cannot be set for " + scope + " endpoints");
if (targets.size() != 1) throw new IllegalArgumentException("A single target must be given for " + scope + " endpoints");
@@ -89,11 +92,14 @@ public class Endpoint {
this.legacy = legacy;
this.routingMethod = routingMethod;
this.tls = port.tls;
+ if (!certificateName && "*".equals(name())) {
+ throw new IllegalArgumentException("Wildcard found in endpoint that is not intended as a certificate name");
+ }
}
private Endpoint(EndpointId id, ClusterSpec.Id cluster, TenantAndApplicationId application,
Optional<InstanceName> instance, List<Target> targets, Scope scope, SystemName system, Port port,
- boolean legacy, RoutingMethod routingMethod) {
+ boolean legacy, RoutingMethod routingMethod, boolean certificateName) {
this(application,
instance,
id,
@@ -107,7 +113,7 @@ public class Endpoint {
Objects.requireNonNull(port, "port must be non-null"),
legacy,
routingMethod),
- targets, scope, port, legacy, routingMethod);
+ targets, scope, port, legacy, routingMethod, certificateName);
}
/**
@@ -446,7 +452,7 @@ public class Endpoint {
ClusterSpec.Id.from("admin"),
url,
List.of(new Target(new DeploymentId(systemApplication.id(), zone))),
- Scope.zone, port, false, routingMethod);
+ Scope.zone, port, false, routingMethod, false);
}
/** A target of an endpoint */
@@ -491,6 +497,7 @@ public class Endpoint {
private Port port;
private RoutingMethod routingMethod = RoutingMethod.shared;
private boolean legacy = false;
+ private boolean certificateName = false;
private EndpointBuilder(TenantAndApplicationId application, Optional<InstanceName> instance) {
this.application = Objects.requireNonNull(application);
@@ -499,33 +506,41 @@ public class Endpoint {
/** Sets the deployment target for this */
public EndpointBuilder target(ClusterSpec.Id cluster, DeploymentId deployment) {
- checkScope();
this.cluster = cluster;
- this.scope = Scope.zone;
+ this.scope = requireUnset(Scope.zone);
this.targets = List.of(new Target(deployment));
return this;
}
- /** Sets the global target with given ID and pointing to the default cluster */
- public EndpointBuilder target(EndpointId endpointId) {
- return target(endpointId, ClusterSpec.Id.from("default"), List.of());
- }
-
/** Sets the global target with given ID, deployments and cluster (as defined in deployments.xml) */
public EndpointBuilder target(EndpointId endpointId, ClusterSpec.Id cluster, List<DeploymentId> deployments) {
- checkScope();
this.endpointId = endpointId;
this.cluster = cluster;
this.targets = deployments.stream().map(Target::new).collect(Collectors.toUnmodifiableList());
- this.scope = Scope.global;
+ this.scope = requireUnset(Scope.global);
return this;
}
+ /** Sets the global target with given ID and pointing to the default cluster */
+ public EndpointBuilder target(EndpointId endpointId) {
+ return target(endpointId, ClusterSpec.Id.from("default"), List.of());
+ }
+
+ /** Sets the application target with given ID and pointing to the default cluster */
+ public EndpointBuilder targetApplication(EndpointId endpointId, DeploymentId deployment) {
+ return targetApplication(endpointId, ClusterSpec.Id.from("default"), Map.of(deployment, 1));
+ }
+
/** Sets the global wildcard target for this */
public EndpointBuilder wildcard() {
return target(EndpointId.of("*"), ClusterSpec.Id.from("*"), List.of());
}
+ /** Sets the application wildcard target for this */
+ public EndpointBuilder wildcardApplication(DeploymentId deployment) {
+ return targetApplication(EndpointId.of("*"), ClusterSpec.Id.from("*"), Map.of(deployment, 1));
+ }
+
/** Sets the zone wildcard target for this */
public EndpointBuilder wildcard(DeploymentId deployment) {
return target(ClusterSpec.Id.from("*"), deployment);
@@ -533,7 +548,6 @@ public class Endpoint {
/** Sets the application target with given ID, cluster, deployments and their weights */
public EndpointBuilder targetApplication(EndpointId endpointId, ClusterSpec.Id cluster, Map<DeploymentId, Integer> deployments) {
- checkScope();
this.endpointId = endpointId;
this.cluster = cluster;
this.targets = deployments.entrySet().stream()
@@ -545,9 +559,8 @@ public class Endpoint {
/** Sets the region target for this, deduced from given zone */
public EndpointBuilder targetRegion(ClusterSpec.Id cluster, ZoneId zone) {
- checkScope();
this.cluster = cluster;
- this.scope = Scope.weighted;
+ this.scope = requireUnset(Scope.weighted);
this.targets = List.of(new Target(new DeploymentId(application.instance(instance.get()), effectiveZone(zone))));
return this;
}
@@ -570,6 +583,12 @@ public class Endpoint {
return this;
}
+ /** Sets whether we're building a name for inclusion in a certificate */
+ public EndpointBuilder certificateName() {
+ this.certificateName = true;
+ return this;
+ }
+
/** Sets the system that owns this */
public Endpoint in(SystemName system) {
if (system.isPublic() && routingMethod != RoutingMethod.exclusive) {
@@ -578,13 +597,14 @@ public class Endpoint {
if (routingMethod.isDirect() && !port.isDefault()) {
throw new IllegalArgumentException("Routing method " + routingMethod + " can only use default port");
}
- return new Endpoint(endpointId, cluster, application, instance, targets, scope, system, port, legacy, routingMethod);
+ return new Endpoint(endpointId, cluster, application, instance, targets, scope, system, port, legacy, routingMethod, certificateName);
}
- private void checkScope() {
- if (scope != null) {
+ private Scope requireUnset(Scope scope) {
+ if (this.scope != null) {
throw new IllegalArgumentException("Cannot change endpoint scope. Already set to " + scope);
}
+ return scope;
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
index 684648ed70a..e3091b704e4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificates.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.certificate;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
+import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.text.Text;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -52,9 +53,9 @@ public class EndpointCertificates {
}
/** Returns certificate metadata for endpoints of given instance and zone */
- public Optional<EndpointCertificateMetadata> getMetadata(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
+ public Optional<EndpointCertificateMetadata> getMetadata(Instance instance, ZoneId zone, DeploymentSpec deploymentSpec) {
Instant start = clock.instant();
- Optional<EndpointCertificateMetadata> metadata = getOrProvision(instance, zone, instanceSpec);
+ Optional<EndpointCertificateMetadata> metadata = getOrProvision(instance, zone, deploymentSpec);
metadata.ifPresent(m -> curator.writeEndpointCertificateMetadata(instance.id(), m.withLastRequested(clock.instant().getEpochSecond())));
Duration duration = Duration.between(start, clock.instant());
if (duration.toSeconds() > 30)
@@ -62,13 +63,12 @@ public class EndpointCertificates {
return metadata;
}
- private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, Optional<DeploymentInstanceSpec> instanceSpec) {
- final var currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
-
+ private Optional<EndpointCertificateMetadata> getOrProvision(Instance instance, ZoneId zone, DeploymentSpec deploymentSpec) {
+ Optional<EndpointCertificateMetadata> currentCertificateMetadata = curator.readEndpointCertificateMetadata(instance.id());
DeploymentId deployment = new DeploymentId(instance.id(), zone);
if (currentCertificateMetadata.isEmpty()) {
- var provisionedCertificateMetadata = provisionEndpointCertificate(deployment, Optional.empty(), instanceSpec);
+ var provisionedCertificateMetadata = provisionEndpointCertificate(deployment, Optional.empty(), deploymentSpec);
// We do not verify the certificate if one has never existed before - because we do not want to
// wait for it to be available before we deploy. This allows the config server to start
// provisioning nodes ASAP, and the risk is small for a new deployment.
@@ -77,10 +77,10 @@ public class EndpointCertificates {
}
// Re-provision certificate if it is missing SANs for the zone we are deploying to
- var requiredSansForZone = controller.routing().certificateDnsNames(deployment);
+ var requiredSansForZone = controller.routing().certificateDnsNames(deployment, deploymentSpec);
if (!currentCertificateMetadata.get().requestedDnsSans().containsAll(requiredSansForZone)) {
var reprovisionedCertificateMetadata =
- provisionEndpointCertificate(deployment, currentCertificateMetadata, instanceSpec)
+ provisionEndpointCertificate(deployment, currentCertificateMetadata, deploymentSpec)
.withRequestId(currentCertificateMetadata.get().requestId()); // We're required to keep the original request ID
curator.writeEndpointCertificateMetadata(instance.id(), reprovisionedCertificateMetadata);
// Verification is unlikely to succeed in this case, as certificate must be available first - controller will retry
@@ -94,12 +94,13 @@ public class EndpointCertificates {
private EndpointCertificateMetadata provisionEndpointCertificate(DeploymentId deployment,
Optional<EndpointCertificateMetadata> currentMetadata,
- Optional<DeploymentInstanceSpec> instanceSpec) {
+ DeploymentSpec deploymentSpec) {
List<ZoneId> zonesInSystem = controller.zoneRegistry().zones().controllerUpgraded().ids();
Set<ZoneId> requiredZones = new LinkedHashSet<>();
requiredZones.add(deployment.zoneId());
if (!deployment.zoneId().environment().isManuallyDeployed()) {
// If not deploying to a dev or perf zone, require all prod zones in deployment spec + test and staging
+ Optional<DeploymentInstanceSpec> instanceSpec = deploymentSpec.instance(deployment.applicationId().instance());
zonesInSystem.stream()
.filter(zone -> zone.environment().isTest() ||
(instanceSpec.isPresent() &&
@@ -107,14 +108,16 @@ public class EndpointCertificates {
.forEach(requiredZones::add);
}
Set<String> requiredNames = requiredZones.stream()
- .flatMap(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone)).stream())
+ .flatMap(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone),
+ deploymentSpec)
+ .stream())
.collect(Collectors.toCollection(LinkedHashSet::new));
// Preserve any currently present names that are still valid
List<String> currentNames = currentMetadata.map(EndpointCertificateMetadata::requestedDnsSans)
.orElseGet(List::of);
zonesInSystem.stream()
- .map(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone)))
+ .map(zone -> controller.routing().certificateDnsNames(new DeploymentId(deployment.applicationId(), zone), deploymentSpec))
.filter(currentNames::containsAll)
.forEach(requiredNames::addAll);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index c1f9137ecfa..fa4a871d423 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -27,122 +27,128 @@ public class EndpointTest {
@Test
public void global_endpoints() {
+ DeploymentId deployment1 = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ DeploymentId deployment2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1"));
+ ClusterSpec.Id cluster = ClusterSpec.Id.from("default");
EndpointId endpointId = EndpointId.defaultId();
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint
"http://a1.t1.global.vespa.yahooapis.com:4080/",
- Endpoint.of(instance1).target(endpointId).on(Port.plain(4080)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.plain(4080)).legacy().in(SystemName.main),
// Legacy endpoint with TLS
"https://a1--t1.global.vespa.yahooapis.com:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).legacy().in(SystemName.main),
// Main endpoint
"https://a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.main),
// Main endpoint in CD
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint in CD
"https://cd--i2--a2--t2.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint with direct routing and default TLS port
"https://a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint with custom rotation name
"https://r1.a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(EndpointId.of("r1")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(EndpointId.of("r1"), cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance in default rotation
"https://i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
"https://r2.i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("r2")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(EndpointId.of("r2"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint in public system
"https://a1.t1.g.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
Map<String, Endpoint> tests2 = Map.of(
// Default endpoint in public system
"https://a1.t1.g.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public),
// Default endpoint in public CD system
"https://a1.t1.g.cd.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
// Custom instance in public system
"https://i2.a2.t2.g.vespa-app.cloud/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
}
@Test
public void global_endpoints_with_endpoint_id() {
- var endpointId = EndpointId.defaultId();
+ DeploymentId deployment1 = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ DeploymentId deployment2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1"));
+ ClusterSpec.Id cluster = ClusterSpec.Id.from("default");
+ EndpointId endpointId = EndpointId.defaultId();
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint
"http://a1.t1.global.vespa.yahooapis.com:4080/",
- Endpoint.of(instance1).target(endpointId).on(Port.plain(4080)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.plain(4080)).legacy().in(SystemName.main),
// Legacy endpoint with TLS
"https://a1--t1.global.vespa.yahooapis.com:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).legacy().in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).legacy().in(SystemName.main),
// Main endpoint
"https://a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.main),
// Main endpoint in CD
"https://cd--i2--a2--t2.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint in CD
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls(4443)).in(SystemName.cd),
// Main endpoint with direct routing and default TLS port
"https://a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint with custom rotation name
"https://r1.a1.t1.global.vespa.oath.cloud/",
- Endpoint.of(instance1).target(EndpointId.of("r1")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance1).target(EndpointId.of("r1"), cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance in default rotation
"https://i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(endpointId, cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
"https://r2.i2.a2.t2.global.vespa.oath.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("r2")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
+ Endpoint.of(instance2).target(EndpointId.of("r2"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main),
// Main endpoint in public system
"https://a1.t1.g.vespa-app.cloud/",
- Endpoint.of(instance1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance1).target(endpointId, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
Map<String, Endpoint> tests2 = Map.of(
// Custom endpoint and instance in public CD system)
"https://foo.i2.a2.t2.g.cd.vespa-app.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("foo")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
+ Endpoint.of(instance2).target(EndpointId.of("foo"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
// Custom endpoint and instance in public system
"https://foo.i2.a2.t2.g.vespa-app.cloud/",
- Endpoint.of(instance2).target(EndpointId.of("foo")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
+ Endpoint.of(instance2).target(EndpointId.of("foo"), cluster, List.of(deployment2)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public)
);
tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
}
@@ -228,6 +234,7 @@ public class EndpointTest {
"https://a1.t1.g.vespa-app.cloud/",
Endpoint.of(instance1)
.target(EndpointId.defaultId())
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -236,6 +243,7 @@ public class EndpointTest {
"https://*.a1.t1.g.vespa-app.cloud/",
Endpoint.of(instance1)
.wildcard()
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -244,6 +252,7 @@ public class EndpointTest {
"https://a1.t1.us-north-1.z.vespa-app.cloud/",
Endpoint.of(instance1)
.target(defaultCluster, prodZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -252,6 +261,7 @@ public class EndpointTest {
"https://a1.t1.us-north-2.test.z.vespa-app.cloud/",
Endpoint.of(instance1)
.target(defaultCluster, testZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -260,6 +270,7 @@ public class EndpointTest {
"https://*.a1.t1.us-north-2.test.z.vespa-app.cloud/",
Endpoint.of(instance1)
.wildcard(testZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
@@ -268,6 +279,7 @@ public class EndpointTest {
"https://*.a1.t1.us-north-1.z.vespa-app.cloud/",
Endpoint.of(instance1)
.wildcard(prodZone)
+ .certificateName()
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public)
@@ -353,7 +365,7 @@ public class EndpointTest {
var tests1 = Map.of(
// With default cluster
"a1.t1.us-north-1.prod",
- Endpoint.of(instance1).target(EndpointId.defaultId()).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance1).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main),
// With non-default cluster
"c1.a1.t1.us-north-1.prod",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
index c6a9c12027f..0a93c936b41 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java
@@ -5,6 +5,8 @@ import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
@@ -19,6 +21,8 @@ import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidatorImpl;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
+import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import org.junit.Before;
@@ -31,6 +35,7 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -111,7 +116,7 @@ public class EndpointCertificatesTest {
@Test
public void provisions_new_certificate_in_dev() {
ZoneId testZone = tester.zoneRegistry().zones().routingMethod(RoutingMethod.exclusive).in(Environment.dev).zones().stream().findFirst().orElseThrow().getId();
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -121,7 +126,7 @@ public class EndpointCertificatesTest {
@Test
public void provisions_new_certificate_in_prod() {
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -145,7 +150,7 @@ public class EndpointCertificatesTest {
"default.default.aws-us-east-1c.staging.z.vespa-app.cloud",
"*.default.default.aws-us-east-1c.staging.z.vespa-app.cloud"
);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -164,7 +169,7 @@ public class EndpointCertificatesTest {
"", Optional.empty(), Optional.empty()));
secretStore.setSecret(testKeyName, KeyUtils.toPem(testKeyPair.getPrivate()), 7);
secretStore.setSecret(testCertName, X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 7);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(testKeyName, endpointCertificateMetadata.get().keyName());
assertEquals(testCertName, endpointCertificateMetadata.get().certName());
@@ -176,7 +181,7 @@ public class EndpointCertificatesTest {
mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "uuid", List.of(), "issuer", Optional.empty(), Optional.empty()));
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 0);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(endpointCertificateMetadata, mockCuratorDb.readEndpointCertificateMetadata(testInstance.id()));
@@ -193,7 +198,7 @@ public class EndpointCertificatesTest {
secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0);
secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate2) + X509CertificateUtils.toPem(testCertificate2), 0);
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.empty());
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty);
assertTrue(endpointCertificateMetadata.isPresent());
assertEquals(0, endpointCertificateMetadata.get().version());
assertEquals(endpointCertificateMetadata, mockCuratorDb.readEndpointCertificateMetadata(testInstance.id()));
@@ -203,7 +208,6 @@ public class EndpointCertificatesTest {
@Test
public void includes_zones_in_deployment_spec_when_deploying_to_staging() {
-
DeploymentSpec deploymentSpec = new DeploymentSpecXmlReader(true).read(
"<deployment version=\"1.0\">\n" +
" <instance id=\"default\">\n" +
@@ -215,7 +219,7 @@ public class EndpointCertificatesTest {
"</deployment>\n");
ZoneId testZone = tester.zoneRegistry().zones().all().in(Environment.staging).zones().stream().findFirst().orElseThrow().getId();
- Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, Optional.of(deploymentSpec.requireInstance("default")));
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, deploymentSpec);
assertTrue(endpointCertificateMetadata.isPresent());
assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key"));
assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.default.default.*-cert"));
@@ -223,4 +227,49 @@ public class EndpointCertificatesTest {
assertEquals(Set.copyOf(expectedCombinedSans), Set.copyOf(endpointCertificateMetadata.get().requestedDnsSans()));
}
+ @Test
+ public void includes_application_endpoint_when_declared() {
+ Instance instance = new Instance(ApplicationId.from("t1", "a1", "default"));
+ ZoneId zone1 = ZoneId.from(Environment.prod, RegionName.from("aws-us-east-1c"));
+ ZoneId zone2 = ZoneId.from(Environment.prod, RegionName.from("aws-us-west-2a"));
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .instances("beta,main")
+ .region(zone1.region())
+ .region(zone2.region())
+ .applicationEndpoint("a", "qrs", zone2.region().value(),
+ Map.of(InstanceName.from("beta"), 2,
+ InstanceName.from("main"), 8))
+ .applicationEndpoint("b", "qrs", zone2.region().value(),
+ Map.of(InstanceName.from("beta"), 1,
+ InstanceName.from("main"), 1))
+ .applicationEndpoint("c", "qrs", zone1.region().value(),
+ Map.of(InstanceName.from("beta"), 4,
+ InstanceName.from("main"), 6))
+ .build();
+ ControllerTester tester = new ControllerTester(SystemName.Public);
+ EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock);
+ EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator);
+ List<String> expectedSans = List.of(
+ "vlfms2wpoa4nyrka2s5lktucypjtxkqhv.internal.vespa-app.cloud",
+ "a1.t1.g.vespa-app.cloud",
+ "*.a1.t1.g.vespa-app.cloud",
+ "a1.t1.aws-us-west-2a.r.vespa-app.cloud",
+ "*.a1.t1.aws-us-west-2a.r.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.r.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.r.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.z.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.z.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.test.z.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.test.z.vespa-app.cloud",
+ "a1.t1.aws-us-east-1c.staging.z.vespa-app.cloud",
+ "*.a1.t1.aws-us-east-1c.staging.z.vespa-app.cloud"
+ );
+ Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone1, applicationPackage.deploymentSpec());
+ assertTrue(endpointCertificateMetadata.isPresent());
+ assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.t1.a1.*-key"));
+ assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.t1.a1.*-cert"));
+ assertEquals(0, endpointCertificateMetadata.get().version());
+ assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans());
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
index 4ad70c4e9dd..61eca05cc67 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java
@@ -2,9 +2,11 @@
package com.yahoo.vespa.hosted.controller.deployment;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.SlimeUtils;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
@@ -31,7 +33,9 @@ public class TestConfigSerializerTest {
JobType.systemTest,
true,
Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId())
- .target(EndpointId.of("ai"))
+ .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"),
+ List.of(new DeploymentId(ApplicationId.defaultId(),
+ ZoneId.defaultId())))
.on(Endpoint.Port.tls())
.in(SystemName.main))),
Map.of(zone, List.of("facts")));