diff options
author | Martin Polden <mpolden@mpolden.no> | 2023-07-04 11:57:49 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2023-07-05 12:52:00 +0200 |
commit | d56134d93b7d62f5e96b688185d3d5b64d0bf942 (patch) | |
tree | cfb60fe3ef1a99288be9974d0eb65984a6f5d777 /controller-server/src/test | |
parent | 8fc733d22134a4645e6b3bb4cdde524cb251f5ae (diff) |
Support anonymized endpoints
Diffstat (limited to 'controller-server/src/test')
6 files changed, 176 insertions, 87 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index eabbdd76d5a..d9b95a53a0e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -66,6 +66,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.OptionalLong; +import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -307,11 +308,11 @@ public final class ControllerTester { } public TenantName createTenant(String tenantName, Tenant.Type type) { - switch (type) { - case athenz: return createTenant(tenantName, "domain" + nextDomainId.getAndIncrement(), nextPropertyId.getAndIncrement()); - case cloud: return createCloudTenant(tenantName); - default: throw new UnsupportedOperationException(); - } + return switch (type) { + case athenz -> createTenant(tenantName, "domain" + nextDomainId.getAndIncrement(), nextPropertyId.getAndIncrement()); + case cloud -> createCloudTenant(tenantName); + default -> throw new UnsupportedOperationException(); + }; } public TenantName createTenant(String tenantName, String domainName, Long propertyId) { @@ -347,17 +348,13 @@ public final class ControllerTester { public Credentials credentialsFor(TenantName tenantName) { Tenant tenant = controller().tenants().require(tenantName); - switch (tenant.type()) { - case athenz: - return new AthenzCredentials(new AthenzPrincipal(new AthenzUser("user")), - ((AthenzTenant) tenant).domain(), - OAuthCredentials.createForTesting("okta-access-token", "okta-identity-token")); - case cloud: - return new Credentials(new SimplePrincipal("dev")); - - default: - throw new IllegalArgumentException("Unexpected tenant type '" + tenant.type() + "'"); - } + return switch (tenant.type()) { + case athenz -> new AthenzCredentials(new AthenzPrincipal(new AthenzUser("user")), + ((AthenzTenant) tenant).domain(), + OAuthCredentials.createForTesting("okta-access-token", "okta-identity-token")); + case cloud -> new Credentials(new SimplePrincipal("dev")); + default -> throw new IllegalArgumentException("Unexpected tenant type '" + tenant.type() + "'"); + }; } public Application createApplication(ApplicationId id) { @@ -385,6 +382,7 @@ public final class ControllerTester { AthenzDbMock athensDb, ServiceRegistryMock serviceRegistry, FlagSource flagSource) { + Random random = new Random(serviceRegistry.clock().instant().toEpochMilli()); // Seed with clock for test determinism Controller controller = new Controller(curator, rotationsConfig, serviceRegistry.zoneRegistry().system().isPublic() ? @@ -395,7 +393,9 @@ public final class ControllerTester { serviceRegistry, new MetricsMock(), new SecretStoreMock(), new ControllerConfig.Builder().build(), - Sleeper.NOOP); + Sleeper.NOOP, + random, + random); // Calculate initial versions controller.updateVersionStatus(VersionStatus.compute(controller)); return controller; 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 dc96aa6c62c..23c029845bb 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 @@ -344,4 +344,36 @@ public class EndpointTest { tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamName(zone2))); } + @Test + public void generated_id() { + GeneratedEndpoint ge = new GeneratedEndpoint("cafed00d", "deadbeef", Endpoint.AuthMethod.mtls); + var deployment = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); + var tests = Map.of( + // Zone endpoint in main, unlike named endpoints, this includes the scope symbol 'z' + "cafed00d.deadbeef.z.vespa.oath.cloud", + Endpoint.of(instance1).target(ClusterSpec.Id.from("c1"), deployment).generatedEndpoint(ge) + .routingMethod(RoutingMethod.sharedLayer4).on(Port.tls()).in(SystemName.main), + // Zone endpoint in public + "cafed00d.deadbeef.z.vespa-app.cloud", + Endpoint.of(instance1).target(ClusterSpec.Id.from("c1"), deployment).generatedEndpoint(ge) + .routingMethod(RoutingMethod.exclusive).on(Port.tls()).in(SystemName.Public), + // Global endpoint in public + "foo.deadbeef.g.vespa-app.cloud", + Endpoint.of(instance1).target(EndpointId.of("foo"), ClusterSpec.Id.from("c1"), List.of(deployment)) + .generatedEndpoint(ge) + .routingMethod(RoutingMethod.exclusive).on(Port.tls()).in(SystemName.Public), + // Global endpoint in public, with default ID + "deadbeef.g.vespa-app.cloud", + Endpoint.of(instance1).target(EndpointId.defaultId(), ClusterSpec.Id.from("c1"), List.of(deployment)) + .generatedEndpoint(ge) + .routingMethod(RoutingMethod.exclusive).on(Port.tls()).in(SystemName.Public), + // Application endpoint in public + "bar.deadbeef.a.vespa-app.cloud", + Endpoint.of(TenantAndApplicationId.from(instance1)).targetApplication(EndpointId.of("bar"), deployment) + .generatedEndpoint(ge) + .routingMethod(RoutingMethod.exclusive).on(Port.tls()).in(SystemName.Public) + ); + tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.dnsName())); + } + } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CertificatePoolMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CertificatePoolMaintainerTest.java index f94120241e7..a371677b82b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CertificatePoolMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CertificatePoolMaintainerTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; -import java.util.Random; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -20,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class CertificatePoolMaintainerTest { private final ControllerTester tester = new ControllerTester(); - private final CertificatePoolMaintainer maintainer = new CertificatePoolMaintainer(tester.controller(), new MockMetric(), Duration.ofHours(1), new Random(4)); + private final CertificatePoolMaintainer maintainer = new CertificatePoolMaintainer(tester.controller(), new MockMetric(), Duration.ofHours(1)); @Test void new_certs_are_requested_until_limit() { @@ -41,17 +40,18 @@ public class CertificatePoolMaintainerTest { assertEquals( List.of( - new DnsNameStatus("*.c8868d4e.z.vespa.oath.cloud", "done"), - new DnsNameStatus("*.c8868d4e.g.vespa.oath.cloud", "done"), - new DnsNameStatus("*.c8868d4e.a.vespa.oath.cloud", "done") + new DnsNameStatus("*.f5549014.z.vespa.oath.cloud", "done"), + new DnsNameStatus("*.f5549014.g.vespa.oath.cloud", "done"), + new DnsNameStatus("*.f5549014.a.vespa.oath.cloud", "done") ), metadata.dnsNames()); - assertEquals("vespa.tls.preprovisioned.c8868d4e-cert", endpointCertificateProvider.certificateDetails(metadata.requestId()).cert_key_keyname()); - assertEquals("vespa.tls.preprovisioned.c8868d4e-key", endpointCertificateProvider.certificateDetails(metadata.requestId()).private_key_keyname()); + assertEquals("vespa.tls.preprovisioned.f5549014-cert", endpointCertificateProvider.certificateDetails(metadata.requestId()).cert_key_keyname()); + assertEquals("vespa.tls.preprovisioned.f5549014-key", endpointCertificateProvider.certificateDetails(metadata.requestId()).private_key_keyname()); } private void assertNumCerts(int n) { assertEquals(0.0, maintainer.maintain(), 0.0000001); assertEquals(n, tester.curator().readUnassignedCertificates().size()); } + } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java index 24eb9f33d33..247ffe1de00 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java @@ -31,7 +31,6 @@ import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; import java.util.OptionalDouble; -import java.util.Random; import java.util.stream.Stream; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.productionUsWest1; @@ -49,7 +48,7 @@ public class EndpointCertificateMaintainerTest { private final ControllerTester tester = new ControllerTester(); private final SecretStoreMock secretStore = (SecretStoreMock) tester.controller().secretStore(); private final EndpointCertificateMaintainer maintainer = new EndpointCertificateMaintainer(tester.controller(), Duration.ofHours(1)); - private final CertificatePoolMaintainer certificatePoolMaintainer = new CertificatePoolMaintainer(tester.controller(), new MockMetric(), Duration.ofHours(1), new Random(4)); + private final CertificatePoolMaintainer certificatePoolMaintainer = new CertificatePoolMaintainer(tester.controller(), new MockMetric(), Duration.ofHours(1)); private final EndpointCertificateMetadata exampleMetadata = new EndpointCertificateMetadata("keyName", "certName", 0, 0, "root-request-uuid", Optional.of("leaf-request-uuid"), List.of(), "issuer", Optional.empty(), Optional.empty(), Optional.empty()); @Test diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java index c1267ad5edf..f685c75bbe3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java @@ -5,7 +5,9 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.EndpointId; +import com.yahoo.vespa.hosted.controller.application.GeneratedEndpoint; import com.yahoo.vespa.hosted.controller.routing.RoutingPolicy; import com.yahoo.vespa.hosted.controller.routing.RoutingPolicyId; import com.yahoo.vespa.hosted.controller.routing.RoutingStatus; @@ -44,7 +46,8 @@ public class RoutingPolicySerializerTest { Set.of(), Set.of(), RoutingStatus.DEFAULT, - false), + false, + List.of(new GeneratedEndpoint("deadbeef", "cafed00d", Endpoint.AuthMethod.mtls))), new RoutingPolicy(id2, Optional.of(HostName.of("long-and-ugly-name-2")), Optional.empty(), @@ -54,7 +57,8 @@ public class RoutingPolicySerializerTest { new RoutingStatus(RoutingStatus.Value.out, RoutingStatus.Agent.tenant, Instant.ofEpochSecond(123)), - true), + true, + List.of(new GeneratedEndpoint("cafed00d", "deadbeef", Endpoint.AuthMethod.token))), new RoutingPolicy(id1, Optional.empty(), Optional.of("127.0.0.1"), @@ -62,7 +66,8 @@ public class RoutingPolicySerializerTest { instanceEndpoints, applicationEndpoints, RoutingStatus.DEFAULT, - true)); + true, + List.of())); var serialized = serializer.fromSlime(owner, serializer.toSlime(policies)); assertEquals(policies.size(), serialized.size()); for (Iterator<RoutingPolicy> it1 = policies.iterator(), it2 = serialized.iterator(); it1.hasNext(); ) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java index 0233db50ac6..783629c8f4a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java @@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.controller.routing; import ai.vespa.http.DomainName; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.ValidationId; import com.yahoo.config.provision.ApplicationId; @@ -20,6 +19,7 @@ import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; +import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata; import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record.Type; @@ -32,6 +32,7 @@ import com.yahoo.vespa.hosted.controller.application.EndpointList; import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; +import com.yahoo.vespa.hosted.controller.certificate.UnassignedCertificate; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; @@ -53,7 +54,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -262,11 +262,11 @@ public class RoutingPoliciesTest { context1.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); // Deployment creates records and policies for all clusters in all zones - Set<String> expectedRecords = Set.of( - "c0.app1.tenant1.us-west-1.vespa.oath.cloud", - "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + List<String> expectedRecords = List.of( "c0.app1.tenant1.us-central-1.vespa.oath.cloud", - "c1.app1.tenant1.us-central-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); assertEquals(4, tester.policiesOf(context1.instanceId()).size()); @@ -279,13 +279,13 @@ public class RoutingPoliciesTest { // Add 1 cluster in each zone and deploy tester.provisionLoadBalancers(clustersPerZone + 1, context1.instanceId(), sharedRoutingLayer, zone1, zone2); context1.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); - expectedRecords = Set.of( - "c0.app1.tenant1.us-west-1.vespa.oath.cloud", - "c1.app1.tenant1.us-west-1.vespa.oath.cloud", - "c2.app1.tenant1.us-west-1.vespa.oath.cloud", + expectedRecords = List.of( "c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", "c1.app1.tenant1.us-central-1.vespa.oath.cloud", - "c2.app1.tenant1.us-central-1.vespa.oath.cloud" + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c2.app1.tenant1.us-central-1.vespa.oath.cloud", + "c2.app1.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); assertEquals(6, tester.policiesOf(context1.instanceId()).size()); @@ -293,17 +293,17 @@ public class RoutingPoliciesTest { // Deploy another application tester.provisionLoadBalancers(clustersPerZone, context2.instanceId(), sharedRoutingLayer, zone1, zone2); context2.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); - expectedRecords = Set.of( - "c0.app1.tenant1.us-west-1.vespa.oath.cloud", - "c1.app1.tenant1.us-west-1.vespa.oath.cloud", - "c2.app1.tenant1.us-west-1.vespa.oath.cloud", + expectedRecords = List.of( "c0.app1.tenant1.us-central-1.vespa.oath.cloud", - "c1.app1.tenant1.us-central-1.vespa.oath.cloud", - "c2.app1.tenant1.us-central-1.vespa.oath.cloud", + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", "c0.app2.tenant1.us-central-1.vespa.oath.cloud", - "c1.app2.tenant1.us-central-1.vespa.oath.cloud", "c0.app2.tenant1.us-west-1.vespa.oath.cloud", - "c1.app2.tenant1.us-west-1.vespa.oath.cloud" + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app2.tenant1.us-central-1.vespa.oath.cloud", + "c1.app2.tenant1.us-west-1.vespa.oath.cloud", + "c2.app1.tenant1.us-central-1.vespa.oath.cloud", + "c2.app1.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords.stream().sorted().toList(), tester.recordNames().stream().sorted().toList()); assertEquals(4, tester.policiesOf(context2.instanceId()).size()); @@ -311,14 +311,14 @@ public class RoutingPoliciesTest { // Deploy removes cluster from app1 tester.provisionLoadBalancers(clustersPerZone, context1.instanceId(), sharedRoutingLayer, zone1, zone2); context1.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); - expectedRecords = Set.of( - "c0.app1.tenant1.us-west-1.vespa.oath.cloud", - "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + expectedRecords = List.of( "c0.app1.tenant1.us-central-1.vespa.oath.cloud", - "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", "c0.app2.tenant1.us-central-1.vespa.oath.cloud", - "c1.app2.tenant1.us-central-1.vespa.oath.cloud", "c0.app2.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app2.tenant1.us-central-1.vespa.oath.cloud", "c1.app2.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); @@ -327,11 +327,11 @@ public class RoutingPoliciesTest { tester.controllerTester().controller().applications().requireInstance(context2.instanceId()).deployments().keySet() .forEach(zone -> tester.controllerTester().controller().applications().deactivate(context2.instanceId(), zone)); context2.flushDnsUpdates(); - expectedRecords = Set.of( - "c0.app1.tenant1.us-west-1.vespa.oath.cloud", - "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + expectedRecords = List.of( "c0.app1.tenant1.us-central-1.vespa.oath.cloud", - "c1.app1.tenant1.us-central-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); assertTrue(tester.routingPolicies().read(context2.instanceId()).isEmpty(), "Removes stale routing policies " + context2.application()); @@ -350,11 +350,11 @@ public class RoutingPoliciesTest { context1.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); // Deployment creates records and policies for all clusters in all zones - Set<String> expectedRecords = Set.of( - "c0.app1.tenant1.us-west-1.vespa.oath.cloud", - "token-c0.app1.tenant1.us-west-1.vespa.oath.cloud", + List<String> expectedRecords = List.of( "c0.app1.tenant1.us-central-1.vespa.oath.cloud", - "token-c0.app1.tenant1.us-central-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "token-c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "token-c0.app1.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); assertEquals(2, tester.policiesOf(context1.instanceId()).size()); @@ -367,7 +367,7 @@ public class RoutingPoliciesTest { tester.provisionLoadBalancers(1, context.instanceId(), true, zone1, zone2); context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); assertEquals(0, tester.controllerTester().controller().curator().readNameServiceQueue().requests().size()); - assertEquals(Set.of(), tester.recordNames()); + assertEquals(List.of(), tester.recordNames()); assertEquals(2, tester.policiesOf(context.instanceId()).size()); } @@ -409,15 +409,17 @@ public class RoutingPoliciesTest { .build(); context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); - List<String> expectedRecords = List.of("c0.app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", - "c0.app1.tenant1.gcp-us-south1-b.z.vespa-app.cloud", - "c0.app1.tenant1.aws-us-east-1.w.vespa-app.cloud", - "c0.app1.tenant1.gcp-us-south1.w.vespa-app.cloud", - "r0.app1.tenant1.g.vespa-app.cloud"); - assertEquals(Set.copyOf(expectedRecords), tester.recordNames()); + List<String> expectedRecords = List.of( + "c0.app1.tenant1.aws-us-east-1.w.vespa-app.cloud", + "c0.app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", + "c0.app1.tenant1.gcp-us-south1-b.z.vespa-app.cloud", + "c0.app1.tenant1.gcp-us-south1.w.vespa-app.cloud", + "r0.app1.tenant1.g.vespa-app.cloud" + ); + assertEquals(expectedRecords, tester.recordNames()); - assertEquals(List.of("lb-0--tenant1.app1.default--prod.aws-us-east-1c."), tester.recordDataOf(Record.Type.CNAME, expectedRecords.get(0))); - assertEquals(List.of("10.0.0.0"), tester.recordDataOf(Record.Type.A, expectedRecords.get(1))); + assertEquals(List.of("lb-0--tenant1.app1.default--prod.aws-us-east-1c."), tester.recordDataOf(Record.Type.CNAME, expectedRecords.get(1))); + assertEquals(List.of("10.0.0.0"), tester.recordDataOf(Record.Type.A, expectedRecords.get(2))); assertEquals(List.of("weighted/10.0.0.0/prod.gcp-us-south1-b/1"), tester.recordDataOf(Record.Type.DIRECT, expectedRecords.get(3))); assertEquals(List.of("latency/c0.app1.tenant1.aws-us-east-1.w.vespa-app.cloud/dns-zone-1/prod.aws-us-east-1c", "latency/c0.app1.tenant1.gcp-us-south1.w.vespa-app.cloud/ignored/prod.gcp-us-south1-b"), @@ -443,11 +445,12 @@ public class RoutingPoliciesTest { tester.assertTargets(context.instanceId(), EndpointId.defaultId(), ClusterSpec.Id.from("default"), 0, Map.of(zone1, 1L, zone2, 1L)); - assertEquals(Set.of("app1.tenant1.aws-eu-west-1.w.vespa-app.cloud", - "app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud", - "app1.tenant1.aws-us-east-1.w.vespa-app.cloud", - "app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", - "app1.tenant1.g.vespa-app.cloud"), + assertEquals(List.of("app1.tenant1.aws-eu-west-1.w.vespa-app.cloud", + "app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud", + "app1.tenant1.aws-us-east-1.w.vespa-app.cloud", + "app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", + "app1.tenant1.g.vespa-app.cloud" + ), tester.recordNames(), "Registers expected DNS names"); } @@ -471,7 +474,7 @@ public class RoutingPoliciesTest { // Routing policy is created and DNS is updated assertEquals(1, tester.policiesOf(context.instanceId()).size()); - assertEquals(Set.of("app1.tenant1.us-east-1.dev.vespa.oath.cloud"), tester.recordNames()); + assertEquals(List.of("app1.tenant1.us-east-1.dev.vespa.oath.cloud"), tester.recordNames()); } @Test @@ -482,7 +485,7 @@ public class RoutingPoliciesTest { context.submit(applicationPackage).deploy(); var zone = ZoneId.from("dev", "us-east-1"); tester.controllerTester().setRoutingMethod(List.of(zone), RoutingMethod.exclusive); - var prodRecords = Set.of("app1.tenant1.us-central-1.vespa.oath.cloud", "app1.tenant1.us-west-1.vespa.oath.cloud"); + var prodRecords = List.of("app1.tenant1.us-central-1.vespa.oath.cloud", "app1.tenant1.us-west-1.vespa.oath.cloud"); assertEquals(prodRecords, tester.recordNames()); // Deploy to dev under different instance @@ -494,7 +497,8 @@ public class RoutingPoliciesTest { // Routing policy is created and DNS is updated assertEquals(1, tester.policiesOf(devContext.instanceId()).size()); - assertEquals(Sets.union(prodRecords, Set.of("user.app1.tenant1.us-east-1.dev.vespa.oath.cloud")), tester.recordNames()); + assertEquals(Stream.concat(prodRecords.stream(), Stream.of("user.app1.tenant1.us-east-1.dev.vespa.oath.cloud")).sorted().toList(), + tester.recordNames()); } @Test @@ -510,7 +514,7 @@ public class RoutingPoliciesTest { // Application is deployed context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); - var expectedRecords = Set.of( + var expectedRecords = List.of( "c0.app1.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); @@ -557,8 +561,8 @@ public class RoutingPoliciesTest { app.deploy(); // TXT records are cleaned up as we go—the last challenge is the last to go here, and we must flush it ourselves. - assertEquals(Set.of("a.t.aws-us-east-1a.vespa.oath.cloud", - "challenge--a.t.aws-us-east-1a.vespa.oath.cloud"), + assertEquals(List.of("a.t.aws-us-east-1a.vespa.oath.cloud", + "challenge--a.t.aws-us-east-1a.vespa.oath.cloud"), tester.recordNames()); app.flushDnsUpdates(); assertEquals(Set.of(new Record(Type.CNAME, @@ -773,7 +777,7 @@ public class RoutingPoliciesTest { tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone2), RoutingStatus.Value.out, RoutingStatus.Agent.tenant); } catch (IllegalArgumentException e) { - assertEquals("Cannot deactivate routing for tenant1.app1 in prod.us-central-1 as it's the last remaining active deployment in endpoint https://r0.app1.tenant1.global.vespa.oath.cloud/ [scope=global, legacy=false, routingMethod=exclusive]", e.getMessage()); + assertEquals("Cannot deactivate routing for tenant1.app1 in prod.us-central-1 as it's the last remaining active deployment in endpoint https://r0.app1.tenant1.global.vespa.oath.cloud/ [scope=global, legacy=false, routingMethod=exclusive, authMethod=mtls]", e.getMessage()); } context.flushDnsUpdates(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, zone2); @@ -841,9 +845,9 @@ public class RoutingPoliciesTest { // Application endpoints are not created until production jobs run betaContext.submit(applicationPackage) .runJob(DeploymentContext.systemTest); - assertEquals(Set.of("beta.app1.tenant1.us-east-1.test.vespa.oath.cloud"), tester.recordNames()); + assertEquals(List.of("beta.app1.tenant1.us-east-1.test.vespa.oath.cloud"), tester.recordNames()); betaContext.runJob(DeploymentContext.stagingTest); - assertEquals(Set.of("beta.app1.tenant1.us-east-3.staging.vespa.oath.cloud"), tester.recordNames()); + assertEquals(List.of("beta.app1.tenant1.us-east-3.staging.vespa.oath.cloud"), tester.recordNames()); // Deploy both instances betaContext.completeRollout(); @@ -958,7 +962,7 @@ public class RoutingPoliciesTest { tester.routingPolicies().setRoutingStatus(mainZone2, RoutingStatus.Value.out, RoutingStatus.Agent.tenant); fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("Cannot deactivate routing for tenant1.app1.main in prod.south as it's the last remaining active deployment in endpoint https://a0.app1.tenant1.a.vespa.oath.cloud/ [scope=application, legacy=false, routingMethod=exclusive]", + assertEquals("Cannot deactivate routing for tenant1.app1.main in prod.south as it's the last remaining active deployment in endpoint https://a0.app1.tenant1.a.vespa.oath.cloud/ [scope=application, legacy=false, routingMethod=exclusive, authMethod=mtls]", e.getMessage()); } @@ -1004,6 +1008,53 @@ public class RoutingPoliciesTest { "Policies removed"); } + @Test + public void generated_zone_endpoints() { + var tester = new RoutingPoliciesTester(SystemName.Public); + var context = tester.newDeploymentContext("tenant1", "app1", "default"); + tester.controllerTester().flagSource().withBooleanFlag(Flags.RANDOMIZED_ENDPOINT_NAMES.id(), true); + addCertificateToPool("cafed00d", UnassignedCertificate.State.ready, tester); + + // Deploy application + int clustersPerZone = 1; + var zone1 = ZoneId.from("prod", "aws-us-east-1c"); + var zone2 = ZoneId.from("prod", "aws-eu-west-1a"); + ApplicationPackage applicationPackage = applicationPackageBuilder().region(zone1.region()) + .region(zone2.region()) + .build(); + tester.provisionLoadBalancers(clustersPerZone, context.instanceId(), zone1, zone2); + context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); + + // Deployment creates generated zone names + List<String> expectedRecords = List.of( + "a9c8c045.cafed00d.z.vespa-app.cloud", + "c0.app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud", + "c0.app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", + "e144a11b.cafed00d.z.vespa-app.cloud" + ); + assertEquals(expectedRecords, tester.recordNames()); + assertEquals(2, tester.policiesOf(context.instanceId()).size()); + for (var zone : List.of(zone1, zone2)) { + EndpointList endpoints = tester.controllerTester().controller().routing().readEndpointsOf(context.deploymentIdIn(zone)); + assertEquals(1, endpoints.generated().size()); + } + + // Next deployment does not change generated names + context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); + assertEquals(expectedRecords, tester.recordNames()); + } + + private void addCertificateToPool(String id, UnassignedCertificate.State state, RoutingPoliciesTester tester) { + EndpointCertificateMetadata cert = new EndpointCertificateMetadata("testKey", "testCert", 1, 0, + "request-id", + Optional.of("leaf-request-uuid"), + List.of("name1", "name2"), + "", Optional.empty(), + Optional.empty(), Optional.of(id)); + UnassignedCertificate pooledCert = new UnassignedCertificate(cert, state); + tester.controllerTester().controller().curator().writeUnassignedCertificate(pooledCert); + } + /** Returns an application package builder that satisfies requirements for a directly routed endpoint */ private static ApplicationPackageBuilder applicationPackageBuilder() { return new ApplicationPackageBuilder().athenzIdentity(AthenzDomain.from("domain"), @@ -1113,11 +1164,13 @@ public class RoutingPoliciesTest { return tester.controller().routing().policies().read(instance); } - private Set<String> recordNames() { + private List<String> recordNames() { return tester.controllerTester().nameService().records().stream() .map(Record::name) .map(RecordName::asString) - .collect(Collectors.toSet()); + .distinct() + .sorted() + .toList(); } private Set<String> aliasDataOf(String name) { |