diff options
Diffstat (limited to 'controller-server/src/test/java')
56 files changed, 1252 insertions, 579 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java index 1215ddbc2ad..63c0193ba7f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java @@ -46,13 +46,13 @@ import org.junit.Test; import java.time.Duration; import java.time.Instant; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -237,14 +237,12 @@ public class ControllerTest { ZoneId usWest = ZoneId.from("prod.us-west-1"); ZoneId usCentral = ZoneId.from("prod.us-central-1"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() - .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service")) .instances("beta,default") .endpoint("default", "foo") .region(usWest.region()) .region(usCentral.region()) // Two deployments should result in each DNS alias being registered once .build(); tester.controllerTester().zoneRegistry().setRoutingMethod(List.of(ZoneApiMock.from(usWest), ZoneApiMock.from(usCentral)), - RoutingMethod.shared, RoutingMethod.sharedLayer4); betaContext.submit(applicationPackage).deploy(); @@ -253,12 +251,6 @@ public class ControllerTest { assertFalse(betaDeployments.isEmpty()); Set<ContainerEndpoint> containerEndpoints = Set.of(new ContainerEndpoint("foo", "global", - List.of("beta--app1--tenant1.global.vespa.oath.cloud", - "rotation-id-01"), - OptionalInt.empty(), - RoutingMethod.shared), - new ContainerEndpoint("foo", - "global", List.of("beta.app1.tenant1.global.vespa.oath.cloud", "rotation-id-01"), OptionalInt.empty(), @@ -277,12 +269,6 @@ public class ControllerTest { assertFalse(defaultDeployments.isEmpty()); Set<ContainerEndpoint> containerEndpoints = Set.of(new ContainerEndpoint("foo", "global", - List.of("app1--tenant1.global.vespa.oath.cloud", - "rotation-id-02"), - OptionalInt.empty(), - RoutingMethod.shared), - new ContainerEndpoint("foo", - "global", List.of("app1.tenant1.global.vespa.oath.cloud", "rotation-id-02"), OptionalInt.empty(), @@ -294,8 +280,8 @@ public class ControllerTest { defaultContext.flushDnsUpdates(); } - Map<String, String> rotationCnames = Map.of("beta--app1--tenant1.global.vespa.oath.cloud", "rotation-fqdn-01.", - "app1--tenant1.global.vespa.oath.cloud", "rotation-fqdn-02."); + Map<String, String> rotationCnames = Map.of("beta.app1.tenant1.global.vespa.oath.cloud", "rotation-fqdn-01.", + "app1.tenant1.global.vespa.oath.cloud", "rotation-fqdn-02."); rotationCnames.forEach((cname, data) -> { var record = tester.controllerTester().findCname(cname); assertTrue(record.isPresent()); @@ -303,10 +289,8 @@ public class ControllerTest { assertEquals(data, record.get().data().asString()); }); - Map<ApplicationId, Set<String>> globalDnsNamesByInstance = Map.of(betaContext.instanceId(), Set.of("beta--app1--tenant1.global.vespa.oath.cloud", - "beta.app1.tenant1.global.vespa.oath.cloud"), - defaultContext.instanceId(), Set.of("app1--tenant1.global.vespa.oath.cloud", - "app1.tenant1.global.vespa.oath.cloud")); + Map<ApplicationId, Set<String>> globalDnsNamesByInstance = Map.of(betaContext.instanceId(), Set.of("beta.app1.tenant1.global.vespa.oath.cloud"), + defaultContext.instanceId(), Set.of("app1.tenant1.global.vespa.oath.cloud")); globalDnsNamesByInstance.forEach((instance, dnsNames) -> { Set<String> actualDnsNames = tester.controller().routing().readDeclaredEndpointsOf(instance) @@ -333,35 +317,22 @@ public class ControllerTest { for (Deployment deployment : deployments) { assertEquals("Rotation names are passed to config server in " + deployment.zone(), Set.of("rotation-id-01", - "app1--tenant1.global.vespa.oath.cloud", - "app1.tenant1.global.vespa.yahooapis.com", - "app1--tenant1.global.vespa.yahooapis.com"), + "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(deployment.zone()))); } context.flushDnsUpdates(); - assertEquals(3, tester.controllerTester().nameService().records().size()); - - Optional<Record> record = tester.controllerTester().findCname("app1--tenant1.global.vespa.yahooapis.com"); - assertTrue(record.isPresent()); - assertEquals("app1--tenant1.global.vespa.yahooapis.com", record.get().name().asString()); - assertEquals("rotation-fqdn-01.", record.get().data().asString()); + assertEquals(1, tester.controllerTester().nameService().records().size()); - record = tester.controllerTester().findCname("app1--tenant1.global.vespa.oath.cloud"); + Optional<Record> record = tester.controllerTester().findCname("app1.tenant1.global.vespa.oath.cloud"); assertTrue(record.isPresent()); - assertEquals("app1--tenant1.global.vespa.oath.cloud", record.get().name().asString()); - assertEquals("rotation-fqdn-01.", record.get().data().asString()); - - record = tester.controllerTester().findCname("app1.tenant1.global.vespa.yahooapis.com"); - assertTrue(record.isPresent()); - assertEquals("app1.tenant1.global.vespa.yahooapis.com", record.get().name().asString()); + assertEquals("app1.tenant1.global.vespa.oath.cloud", record.get().name().asString()); assertEquals("rotation-fqdn-01.", record.get().data().asString()); List<String> globalDnsNames = tester.controller().routing().readDeclaredEndpointsOf(context.instanceId()) .scope(Endpoint.Scope.global) + .sortedBy(Comparator.comparing(Endpoint::dnsName)) .mapToList(Endpoint::dnsName); - assertEquals(List.of("app1--tenant1.global.vespa.oath.cloud", - "app1.tenant1.global.vespa.yahooapis.com", - "app1--tenant1.global.vespa.yahooapis.com"), + assertEquals(List.of("app1.tenant1.global.vespa.oath.cloud"), globalDnsNames); } @@ -382,11 +353,11 @@ public class ControllerTest { assertFalse(deployments.isEmpty()); var notWest = Set.of( - "rotation-id-01", "foobar--app1--tenant1.global.vespa.oath.cloud", - "rotation-id-02", "app1--tenant1.global.vespa.oath.cloud", - "rotation-id-03", "all--app1--tenant1.global.vespa.oath.cloud" + "rotation-id-01", "foobar.app1.tenant1.global.vespa.oath.cloud", + "rotation-id-02", "app1.tenant1.global.vespa.oath.cloud", + "rotation-id-03", "all.app1.tenant1.global.vespa.oath.cloud" ); - var west = Sets.union(notWest, Set.of("rotation-id-04", "west--app1--tenant1.global.vespa.oath.cloud")); + var west = Sets.union(notWest, Set.of("rotation-id-04", "west.app1.tenant1.global.vespa.oath.cloud")); for (Deployment deployment : deployments) { assertEquals("Rotation names are passed to config server in " + deployment.zone(), @@ -397,24 +368,24 @@ public class ControllerTest { assertEquals(4, tester.controllerTester().nameService().records().size()); - var record1 = tester.controllerTester().findCname("app1--tenant1.global.vespa.oath.cloud"); + var record1 = tester.controllerTester().findCname("app1.tenant1.global.vespa.oath.cloud"); assertTrue(record1.isPresent()); - assertEquals("app1--tenant1.global.vespa.oath.cloud", record1.get().name().asString()); + assertEquals("app1.tenant1.global.vespa.oath.cloud", record1.get().name().asString()); assertEquals("rotation-fqdn-02.", record1.get().data().asString()); - var record2 = tester.controllerTester().findCname("foobar--app1--tenant1.global.vespa.oath.cloud"); + var record2 = tester.controllerTester().findCname("foobar.app1.tenant1.global.vespa.oath.cloud"); assertTrue(record2.isPresent()); - assertEquals("foobar--app1--tenant1.global.vespa.oath.cloud", record2.get().name().asString()); + assertEquals("foobar.app1.tenant1.global.vespa.oath.cloud", record2.get().name().asString()); assertEquals("rotation-fqdn-01.", record2.get().data().asString()); - var record3 = tester.controllerTester().findCname("all--app1--tenant1.global.vespa.oath.cloud"); + var record3 = tester.controllerTester().findCname("all.app1.tenant1.global.vespa.oath.cloud"); assertTrue(record3.isPresent()); - assertEquals("all--app1--tenant1.global.vespa.oath.cloud", record3.get().name().asString()); + assertEquals("all.app1.tenant1.global.vespa.oath.cloud", record3.get().name().asString()); assertEquals("rotation-fqdn-03.", record3.get().data().asString()); - var record4 = tester.controllerTester().findCname("west--app1--tenant1.global.vespa.oath.cloud"); + var record4 = tester.controllerTester().findCname("west.app1.tenant1.global.vespa.oath.cloud"); assertTrue(record4.isPresent()); - assertEquals("west--app1--tenant1.global.vespa.oath.cloud", record4.get().name().asString()); + assertEquals("west.app1.tenant1.global.vespa.oath.cloud", record4.get().name().asString()); assertEquals("rotation-fqdn-04.", record4.get().data().asString()); } @@ -437,7 +408,7 @@ public class ControllerTest { for (var zone : List.of(west, central)) { assertEquals( "Zone " + zone + " is a member of global endpoint", - Set.of("rotation-id-01", "app1--tenant1.global.vespa.oath.cloud"), + Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)) ); } @@ -455,13 +426,13 @@ public class ControllerTest { for (var zone : List.of(west, central)) { assertEquals( "Zone " + zone + " is a member of global endpoint", - Set.of("rotation-id-01", "app1--tenant1.global.vespa.oath.cloud"), + Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)) ); } assertEquals( "Zone " + east + " is a member of global endpoint", - Set.of("rotation-id-02", "east--app1--tenant1.global.vespa.oath.cloud"), + Set.of("rotation-id-02", "east.app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(east)) ); @@ -478,9 +449,9 @@ public class ControllerTest { assertEquals( "Zone " + zone + " is a member of global endpoint", zone.equals(east) - ? Set.of("rotation-id-01", "app1--tenant1.global.vespa.oath.cloud", - "rotation-id-02", "east--app1--tenant1.global.vespa.oath.cloud") - : Set.of("rotation-id-01", "app1--tenant1.global.vespa.oath.cloud"), + ? Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud", + "rotation-id-02", "east.app1.tenant1.global.vespa.oath.cloud") + : Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)) ); } @@ -563,6 +534,7 @@ public class ControllerTest { @Test public void testDnsUpdatesWithChangeInRotationAssignment() { // Application 1 is deployed and deleted + String dnsName1 = "app1.tenant1.global.vespa.oath.cloud"; { var context = tester.newDeploymentContext("tenant1", "app1", "default"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() @@ -573,11 +545,10 @@ public class ControllerTest { context.submit(applicationPackage).deploy(); assertEquals(1, tester.controllerTester().nameService().records().size()); - { - Optional<Record> record = tester.controllerTester().findCname("app1--tenant1.global.vespa.oath.cloud"); + Optional<Record> record = tester.controllerTester().findCname(dnsName1); assertTrue(record.isPresent()); - assertEquals("app1--tenant1.global.vespa.oath.cloud", record.get().name().asString()); + assertEquals(dnsName1, record.get().name().asString()); assertEquals("rotation-fqdn-01.", record.get().data().asString()); } @@ -596,17 +567,13 @@ public class ControllerTest { } context.flushDnsUpdates(); - // Records are removed - List<String> removed = List.of("app1--tenant1.global.vespa.yahooapis.com", - "app1--tenant1.global.vespa.oath.cloud", - "app1.tenant1.global.vespa.yahooapis.com"); - for (var name : removed) { - Optional<Record> record = tester.controllerTester().findCname(name); - assertTrue(name + " is removed", record.isEmpty()); - } + // Record is removed + Optional<Record> record = tester.controllerTester().findCname(dnsName1); + assertTrue(dnsName1 + " is removed", record.isEmpty()); } // Application 2 is deployed and assigned same rotation as application 1 had before deletion + String dnsName2 = "app2.tenant2.global.vespa.oath.cloud"; { var context = tester.newDeploymentContext("tenant2", "app2", "default"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() @@ -617,9 +584,9 @@ public class ControllerTest { context.submit(applicationPackage).deploy(); assertEquals(1, tester.controllerTester().nameService().records().size()); - var record = tester.controllerTester().findCname("app2--tenant2.global.vespa.oath.cloud"); + var record = tester.controllerTester().findCname(dnsName2); assertTrue(record.isPresent()); - assertEquals("app2--tenant2.global.vespa.oath.cloud", record.get().name().asString()); + assertEquals(dnsName2, record.get().name().asString()); assertEquals("rotation-fqdn-01.", record.get().data().asString()); } @@ -637,11 +604,11 @@ public class ControllerTest { // DNS records are created for the newly assigned rotation assertEquals(2, tester.controllerTester().nameService().records().size()); - var record1 = tester.controllerTester().findCname("app1--tenant1.global.vespa.oath.cloud"); + var record1 = tester.controllerTester().findCname(dnsName1); assertTrue(record1.isPresent()); assertEquals("rotation-fqdn-02.", record1.get().data().asString()); - var record2 = tester.controllerTester().findCname("app2--tenant2.global.vespa.oath.cloud"); + var record2 = tester.controllerTester().findCname(dnsName2); assertTrue(record2.isPresent()); assertEquals("rotation-fqdn-01.", record2.get().data().asString()); } @@ -722,7 +689,7 @@ public class ControllerTest { var context = tester.newDeploymentContext(); ZoneId zone = ZoneId.from("dev", "us-east-1"); tester.controllerTester().zoneRegistry() - .setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.shared, RoutingMethod.sharedLayer4); + .setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.sharedLayer4); // Deploy context.runJob(zone, applicationPackage); @@ -738,7 +705,7 @@ public class ControllerTest { .stream() .map(Endpoint::routingMethod) .collect(Collectors.toSet()); - assertEquals(routingMethods, Set.of(RoutingMethod.shared, RoutingMethod.sharedLayer4)); + assertEquals(routingMethods, Set.of(RoutingMethod.sharedLayer4)); // Deployment has stored application meta. assertNotNull(tester.controllerTester().serviceRegistry().applicationStore() @@ -778,12 +745,11 @@ public class ControllerTest { public void testDeletingApplicationThatHasAlreadyBeenDeleted() { var context = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() - .region("us-east-3") .region("us-west-1") .build(); ZoneId zone = ZoneId.from(Environment.prod, RegionName.from("us-west-1")); - context.runJob(zone, applicationPackage); + context.submit(applicationPackage).runJob(zone, applicationPackage); tester.controller().applications().deactivate(context.instanceId(), zone); tester.controller().applications().deactivate(context.instanceId(), zone); } @@ -914,12 +880,10 @@ public class ControllerTest { .region(zone2.region()) .build(); - // Zone 1 supports shared and sharedLayer4 - tester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone1), RoutingMethod.shared, - RoutingMethod.sharedLayer4); + // Zone 1 supports sharedLayer4 + tester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone1), RoutingMethod.sharedLayer4); // Zone 2 supports shared and exclusive - tester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone2), RoutingMethod.shared, - RoutingMethod.exclusive); + tester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone2), RoutingMethod.exclusive); context.submit(applicationPackage).deploy(); var expectedRecords = List.of( @@ -935,20 +899,10 @@ public class ControllerTest { new LatencyAliasTarget(HostName.from("application.tenant.us-east-3-w.vespa.oath.cloud"), "dns-zone-1", ZoneId.from("prod.us-east-3")).pack()), - // The 'default' global endpoint, pointing to both zones with shared routing, via rotation - new Record(Record.Type.CNAME, - RecordName.from("application--tenant.global.vespa.oath.cloud"), - RecordData.from("rotation-fqdn-01.")), - // The zone-scoped endpoint pointing to zone 2 with exclusive routing new Record(Record.Type.CNAME, RecordName.from("application.tenant.us-east-3.vespa.oath.cloud"), - RecordData.from("lb-0--tenant:application:default--prod.us-east-3.")), - - // The 'east' global endpoint, pointing to zone 2 with shared routing, via rotation - new Record(Record.Type.CNAME, - RecordName.from("east--application--tenant.global.vespa.oath.cloud"), - RecordData.from("rotation-fqdn-02."))); + RecordData.from("lb-0--tenant:application:default--prod.us-east-3."))); assertEquals(expectedRecords, List.copyOf(tester.controllerTester().nameService().records())); } @@ -988,53 +942,6 @@ public class ControllerTest { } @Test - public void testDeploymentWithSharedAndDirectRouting() { - var context = tester.newDeploymentContext(); - var zone1 = ZoneId.from("prod", "us-west-1"); - var zone2 = ZoneId.from("prod", "us-east-3"); - var applicationPackageBuilder = new ApplicationPackageBuilder() - .region(zone1.region()) - .region(zone2.region()); - tester.controllerTester().zoneRegistry() - .setRoutingMethod(ZoneApiMock.from(zone1), RoutingMethod.shared, RoutingMethod.sharedLayer4) - .setRoutingMethod(ZoneApiMock.from(zone2), RoutingMethod.shared, RoutingMethod.sharedLayer4); - Supplier<Set<RoutingMethod>> routingMethods = () -> tester.controller().routing().readEndpointsOf(context.deploymentIdIn(zone1)) - .asList() - .stream() - .map(Endpoint::routingMethod) - .collect(Collectors.toSet()); - - // Without satisfying requirements - context.submit(applicationPackageBuilder.build()).deploy(); - assertEquals(Set.of(RoutingMethod.shared), routingMethods.get()); - - // Package satisfying all requirements is submitted, but not deployed yet - applicationPackageBuilder = applicationPackageBuilder.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service")); - var context2 = context.submit(applicationPackageBuilder.build()); - assertEquals("Direct routing endpoint is available after submission and before deploy", - Set.of(RoutingMethod.shared, RoutingMethod.sharedLayer4), routingMethods.get()); - context2.deploy(); - - // Global endpoint is added and includes directly routed endpoint name - applicationPackageBuilder = applicationPackageBuilder.endpoint("default", "default"); - context2.submit(applicationPackageBuilder.build()).deploy(); - for (var zone : List.of(zone1, zone2)) { - assertEquals(Set.of("rotation-id-01", - "application.tenant.global.vespa.oath.cloud", - "application--tenant.global.vespa.oath.cloud"), - tester.configServer().containerEndpointNames(context.deploymentIdIn(zone))); - } - List<String> zoneDnsNames = tester.controller().routing().readEndpointsOf(context.deploymentIdIn(zone1)) - .scope(Endpoint.Scope.zone) - .mapToList(Endpoint::dnsName); - assertEquals(List.of("application--tenant.us-west-1.vespa.oath.cloud", - "application.tenant.us-west-1.prod.vespa.yahooapis.com", - "application--tenant.us-west-1.prod.vespa.yahooapis.com", - "application.tenant.us-west-1.vespa.oath.cloud"), - zoneDnsNames); - } - - @Test public void testChangeEndpointCluster() { var context = tester.newDeploymentContext(); var west = ZoneId.from("prod", "us-west-1"); 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 e50c32d0e5d..35cca0e1f1f 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 @@ -33,26 +33,6 @@ public class EndpointTest { 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, 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, 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, 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, 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, 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, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), @@ -99,26 +79,6 @@ public class EndpointTest { 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, 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, 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, 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, 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, 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, cluster, List.of(deployment1)).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), @@ -161,33 +121,25 @@ public class EndpointTest { var testZone = new DeploymentId(instance1, ZoneId.from("test", "us-north-2")); Map<String, Endpoint> tests = Map.of( - // Legacy endpoint (always contains environment) - "http://a1.t1.us-north-1.prod.vespa.yahooapis.com:4080/", - Endpoint.of(instance1).target(cluster, prodZone).on(Port.plain(4080)).legacy().in(SystemName.main), - - // Secure legacy endpoint - "https://a1--t1.us-north-1.prod.vespa.yahooapis.com:4443/", - Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls(4443)).legacy().in(SystemName.main), - // Prod endpoint in main - "https://a1--t1.us-north-1.vespa.oath.cloud:4443/", - Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.main), + "https://a1.t1.us-north-1.vespa.oath.cloud/", + Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls()).in(SystemName.main), // Prod endpoint in CD - "https://cd--a1--t1.us-north-1.vespa.oath.cloud:4443/", - Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.cd), + "https://cd.a1.t1.us-north-1.vespa.oath.cloud/", + Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls()).in(SystemName.cd), // Test endpoint in main - "https://a1--t1.us-north-2.test.vespa.oath.cloud:4443/", - Endpoint.of(instance1).target(cluster, testZone).on(Port.tls(4443)).in(SystemName.main), + "https://a1.t1.us-north-2.test.vespa.oath.cloud/", + Endpoint.of(instance1).target(cluster, testZone).on(Port.tls()).in(SystemName.main), // Non-default cluster in main - "https://c1--a1--t1.us-north-1.vespa.oath.cloud/", + "https://c1.a1.t1.us-north-1.vespa.oath.cloud/", Endpoint.of(instance1).target(ClusterSpec.Id.from("c1"), prodZone).on(Port.tls()).in(SystemName.main), // Non-default instance in main - "https://i2--a2--t2.us-north-1.vespa.oath.cloud:4443/", - Endpoint.of(instance2).target(cluster, prodZone2).on(Port.tls(4443)).in(SystemName.main), + "https://i2.a2.t2.us-north-1.vespa.oath.cloud/", + Endpoint.of(instance2).target(cluster, prodZone2).on(Port.tls()).in(SystemName.main), // Non-default cluster in public "https://c1.a1.t1.us-north-1.z.vespa-app.cloud/", @@ -195,11 +147,7 @@ public class EndpointTest { // Non-default cluster and instance in public "https://c2.i2.a2.t2.us-north-1.z.vespa-app.cloud/", - Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone2).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public), - - // Endpoint in main using shared layer 4 - "https://a1.t1.us-north-1.vespa.oath.cloud/", - Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls()).routingMethod(RoutingMethod.sharedLayer4).in(SystemName.main) + Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone2).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); @@ -365,11 +313,11 @@ public class EndpointTest { var tests1 = Map.of( // With default cluster "a1.t1.us-north-1.prod", - Endpoint.of(instance1).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main), + Endpoint.of(instance1).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone)).on(Port.tls()).in(SystemName.main), // With non-default cluster "c1.a1.t1.us-north-1.prod", - Endpoint.of(instance1).target(EndpointId.of("ignored1"), ClusterSpec.Id.from("c1"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main), + Endpoint.of(instance1).target(EndpointId.of("ignored1"), ClusterSpec.Id.from("c1"), List.of(zone)).on(Port.tls()).in(SystemName.main), // With application endpoint "c2.a1.t1.us-north-1.prod", @@ -381,11 +329,11 @@ public class EndpointTest { var tests2 = Map.of( // With non-default instance and default cluster "i2.a2.t2.us-north-1.prod", - Endpoint.of(instance2).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone2)).on(Port.tls(4443)).in(SystemName.main), + Endpoint.of(instance2).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone2)).on(Port.tls()).in(SystemName.main), // With non-default instance and cluster "c2.i2.a2.t2.us-north-1.prod", - Endpoint.of(instance2).target(EndpointId.of("ignored2"), ClusterSpec.Id.from("c2"), List.of(zone2)).on(Port.tls(4443)).in(SystemName.main) + Endpoint.of(instance2).target(EndpointId.of("ignored2"), ClusterSpec.Id.from("c2"), List.of(zone2)).on(Port.tls()).in(SystemName.main) ); tests1.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamName(zone))); tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamName(zone2))); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java index 2ee03457046..cf0b46dfba2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java @@ -7,6 +7,8 @@ import org.junit.Assert; import org.junit.Test; import java.io.ByteArrayInputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Instant; import java.util.List; import java.util.Map; @@ -14,6 +16,7 @@ import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; /** @@ -108,6 +111,21 @@ public class ApplicationPackageTest { } } + @Test + public void testBundleHashesAreSameWithDifferentDeploymentXml() throws Exception { + var originalPackage = getApplicationZip("original.zip"); + var changedDeploymentXml = getApplicationZip("changed-deployment-xml.zip"); + var changedServices = getApplicationZip("changed-services-xml.zip"); + + // services.xml is changed -> different bundle hash + assertNotEquals(originalPackage.bundleHash(), changedServices.bundleHash()); + assertNotEquals(originalPackage.hash(), changedServices.hash()); + + // deployment.xml is changed -> same bundle hash + assertEquals(originalPackage.bundleHash(), changedDeploymentXml.bundleHash()); + assertNotEquals(originalPackage.hash(), changedDeploymentXml.hash()); + } + private static Map<String, String> unzip(byte[] zip) { return new ZipStreamReader(new ByteArrayInputStream(zip), __ -> true, 1 << 10, true) .entries().stream() @@ -115,4 +133,8 @@ public class ApplicationPackageTest { entry -> new String(entry.contentOrThrow(), UTF_8))); } + private ApplicationPackage getApplicationZip(String path) throws Exception { + return new ApplicationPackage(Files.readAllBytes(Path.of("src/test/resources/application-packages/" + path)), true); + } + } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java index d6ec01a2d57..1a052b6a578 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; public class CuratorArchiveBucketDbTest { @@ -29,19 +29,22 @@ public class CuratorArchiveBucketDbTest { Set.of(new ArchiveBucket("existingBucket", "keyArn").withTenant(TenantName.defaultName()))); // Finds existing bucket in db - assertEquals(Optional.of(URI.create("s3://existingBucket/default/")), bucketDb.archiveUriFor(ZoneId.defaultId(), TenantName.defaultName())); + assertEquals(Optional.of(URI.create("s3://existingBucket/default/")), bucketDb.archiveUriFor(ZoneId.defaultId(), TenantName.defaultName(), true)); // Assigns to existing bucket while there is space IntStream.range(0, 29).forEach(i -> assertEquals( Optional.of(URI.create("s3://existingBucket/tenant" + i + "/")), bucketDb - .archiveUriFor(ZoneId.defaultId(), TenantName.from("tenant" + i)))); + .archiveUriFor(ZoneId.defaultId(), TenantName.from("tenant" + i), true))); // Creates new bucket when existing buckets are full - assertEquals(Optional.of(URI.create("s3://bucketName/lastDrop/")), bucketDb.archiveUriFor(ZoneId.defaultId(), TenantName.from("lastDrop"))); + assertEquals(Optional.of(URI.create("s3://bucketName/lastDrop/")), bucketDb.archiveUriFor(ZoneId.defaultId(), TenantName.from("lastDrop"), true)); // Creates new bucket when there are no existing buckets in zone - assertEquals(Optional.of(URI.create("s3://bucketName/firstInZone/")), bucketDb.archiveUriFor(ZoneId.from("prod.us-east-3"), TenantName.from("firstInZone"))); + assertEquals(Optional.of(URI.create("s3://bucketName/firstInZone/")), bucketDb.archiveUriFor(ZoneId.from("prod.us-east-3"), TenantName.from("firstInZone"), true)); + + // Does not create bucket if not required + assertEquals(Optional.empty(), bucketDb.archiveUriFor(ZoneId.from("prod.us-east-3"), TenantName.from("newTenant"), false)); // Lists all buckets by zone Set<TenantName> existingBucketTenants = Streams.concat(Stream.of(TenantName.defaultName()), IntStream.range(0, 29).mapToObj(i -> TenantName.from("tenant" + i))).collect(Collectors.toUnmodifiableSet()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java index 91a12d3b465..bc5a70b1fa0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java @@ -57,9 +57,10 @@ public class ApplicationPackageBuilder { private OptionalInt majorVersion = OptionalInt.empty(); private String instances = "default"; private String upgradePolicy = null; + private String upgradeRevision = "latest"; private String upgradeRollout = null; private String globalServiceId = null; - private String athenzIdentityAttributes = null; + private String athenzIdentityAttributes = "athenz-domain='domain' athenz-service='service'"; private String searchDefinition = "search test { }"; private boolean explicitSystemTest = false; private boolean explicitStagingTest = false; @@ -80,6 +81,11 @@ public class ApplicationPackageBuilder { return this; } + public ApplicationPackageBuilder upgradeRevision(String upgradeRevision) { + this.upgradeRevision = upgradeRevision; + return this; + } + public ApplicationPackageBuilder upgradeRollout(String upgradeRollout) { this.upgradeRollout = upgradeRollout; return this; @@ -195,7 +201,12 @@ public class ApplicationPackageBuilder { public ApplicationPackageBuilder athenzIdentity(AthenzDomain domain, AthenzService service) { this.athenzIdentityAttributes = Text.format("athenz-domain='%s' athenz-service='%s'", domain.value(), - service.value()); + service.value()); + return this; + } + + public ApplicationPackageBuilder withoutAthenzIdentity() { + this.athenzIdentityAttributes = null; return this; } @@ -248,9 +259,10 @@ public class ApplicationPackageBuilder { } xml.append(">\n"); xml.append(" <instance id='").append(instances).append("'>\n"); - if (upgradePolicy != null || upgradeRollout != null) { + if (upgradePolicy != null || upgradeRevision != null || upgradeRollout != null) { xml.append(" <upgrade "); if (upgradePolicy != null) xml.append("policy='").append(upgradePolicy).append("' "); + if (upgradeRevision != null) xml.append("revision='").append(upgradeRevision).append("' "); if (upgradeRollout != null) xml.append("rollout='").append(upgradeRollout).append("' "); xml.append("/>\n"); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java index 699721b128c..9c5f7e3376a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java @@ -164,14 +164,13 @@ public class DeploymentContext { } - /** Completely deploy the latest change */ + /** Completely deploy the current change */ public DeploymentContext deploy() { Application application = application(); assertTrue("Application package submitted", application.latestVersion().isPresent()); assertFalse("Submission is not already deployed", application.instances().values().stream() .anyMatch(instance -> instance.deployments().values().stream() .anyMatch(deployment -> deployment.applicationVersion().equals(lastSubmission)))); - assertEquals(application.latestVersion(), instance().change().application()); completeRollout(application.deploymentSpec().instances().size() > 1); for (var instance : application().instances().values()) { assertFalse(instance.change().hasTargets()); @@ -334,20 +333,20 @@ public class DeploymentContext { /** Runs and returns all remaining jobs for the application, at most once, and asserts the current change is rolled out. */ public DeploymentContext completeRollout(boolean multiInstance) { triggerJobs(); - Map<ApplicationId, Set<JobType>> jobsByInstance = new HashMap<>(); + Map<ApplicationId, Map<JobType, Versions>> jobsByInstance = new HashMap<>(); List<Run> activeRuns; while ( ! (activeRuns = this.jobs.active(applicationId)).isEmpty()) for (Run run : activeRuns) { - Set<JobType> jobs = jobsByInstance.computeIfAbsent(run.id().application(), k -> new HashSet<>()); - if (jobs.add(run.id().type())) { - runJob(run.id().type(), run.id().application()); - if (multiInstance) { - tester.outstandingChangeDeployer().run(); - } - triggerJobs(); - } else { - throw new AssertionError("Job '" + run.id() + "' was run twice"); + Map<JobType, Versions> jobs = jobsByInstance.computeIfAbsent(run.id().application(), k -> new HashMap<>()); + Versions previous = jobs.put(run.id().type(), run.versions()); + if (run.versions().equals(previous)) { + throw new AssertionError("Job '" + run.id() + "' was run twice on same versions"); } + runJob(run.id().type(), run.id().application()); + if (multiInstance) { + tester.outstandingChangeDeployer().run(); + } + triggerJobs(); } assertFalse("Change should have no targets, but was " + instance().change(), instance().change().hasTargets()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java index 102dfde16ec..67fa2f87794 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java @@ -107,6 +107,40 @@ public class DeploymentTriggerTest { } @Test + public void separateRevisionMakesApplicationChangeWaitForPreviousToComplete() { + DeploymentContext app = tester.newDeploymentContext(); + ApplicationPackage applicationPackage = new ApplicationPackageBuilder() + .upgradeRevision(null) // separate by default, but we override this in test builder + .region("us-east-3") + .test("us-east-3") + .build(); + + app.submit(applicationPackage).runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3); + Optional<ApplicationVersion> v0 = app.lastSubmission(); + + app.submit(applicationPackage); + Optional<ApplicationVersion> v1 = app.lastSubmission(); + assertEquals(v0, app.instance().change().application()); + + // Eager tests still run before new revision rolls out. + app.runJob(systemTest).runJob(stagingTest); + + // v0 rolls out completely. + app.runJob(testUsEast3); + assertEquals(Optional.empty(), app.instance().change().application()); + + // v1 starts rolling when v0 is done. + tester.outstandingChangeDeployer().run(); + assertEquals(v1, app.instance().change().application()); + + // v1 fails, so v2 starts immediately. + app.runJob(productionUsEast3).failDeployment(testUsEast3); + app.submit(applicationPackage); + Optional<ApplicationVersion> v2 = app.lastSubmission(); + assertEquals(v2, app.instance().change().application()); + } + + @Test public void leadingUpgradeAllowsApplicationChangeWhileUpgrading() { var applicationPackage = new ApplicationPackageBuilder().region("us-east-3") .upgradeRollout("leading") @@ -155,9 +189,10 @@ public class DeploymentTriggerTest { tester.controllerTester().upgradeSystem(new Version("8.9")); tester.upgrader().maintain(); app.runJob(systemTest).runJob(stagingTest); + tester.clock().advance(Duration.ofMinutes(1)); tester.triggerJobs(); - // Jobs are not aborted when the new submission remains outstanding. + // Upgrade is allowed to proceed ahead of revision change, and is not aborted. app.submit(); app.runJob(systemTest).runJob(stagingTest); tester.triggerJobs(); @@ -347,17 +382,15 @@ public class DeploymentTriggerTest { // Application on (6.1, 1.0.1) Version v1 = Version.fromString("6.1"); - // Application is mid-upgrade when block window begins, and has an outstanding change. + // Application is mid-upgrade when block window begins, and gets an outstanding change. Version v2 = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(v2); tester.upgrader().maintain(); - app.submit(applicationPackage); - app.runJob(stagingTest).runJob(systemTest); // Entering block window will keep the outstanding change in place. tester.clock().advance(Duration.ofHours(1)); - tester.outstandingChangeDeployer().run(); + app.submit(applicationPackage); app.runJob(productionUsWest1); assertEquals(1, app.instanceJobs().get(productionUsWest1).lastSuccess().get().versions().targetApplication().buildNumber().getAsLong()); assertEquals(2, app.deploymentStatus().outstandingChange(app.instance().name()).application().get().buildNumber().getAsLong()); @@ -465,18 +498,45 @@ public class DeploymentTriggerTest { } @Test - public void settingANoOpChangeIsANoOp() { + public void downgradingApplicationVersionWorks() { var app = tester.newDeploymentContext().submit().deploy(); ApplicationVersion appVersion0 = app.lastSubmission().get(); + assertEquals(Optional.of(appVersion0), app.instance().latestDeployed()); + app.submit().deploy(); ApplicationVersion appVersion1 = app.lastSubmission().get(); + assertEquals(Optional.of(appVersion1), app.instance().latestDeployed()); - // Triggering a roll-out of an already deployed application is a no-op. - assertEquals(Change.empty(), app.instance().change()); + // Downgrading application version. tester.deploymentTrigger().forceChange(app.instanceId(), Change.of(appVersion0)); + assertEquals(Change.of(appVersion0), app.instance().change()); + app.runJob(stagingTest) + .runJob(productionUsCentral1) + .runJob(productionUsEast3) + .runJob(productionUsWest1); + assertEquals(Change.empty(), app.instance().change()); + assertEquals(appVersion0, app.instance().deployments().get(productionUsEast3.zone(tester.controller().system())).applicationVersion()); + assertEquals(Optional.of(appVersion0), app.instance().latestDeployed()); + } + + @Test + public void settingANoOpChangeIsANoOp() { + var app = tester.newDeploymentContext().submit(); + assertEquals(Optional.empty(), app.instance().latestDeployed()); + + app.deploy(); + ApplicationVersion appVersion0 = app.lastSubmission().get(); + assertEquals(Optional.of(appVersion0), app.instance().latestDeployed()); + + app.submit().deploy(); + ApplicationVersion appVersion1 = app.lastSubmission().get(); + assertEquals(Optional.of(appVersion1), app.instance().latestDeployed()); + + // Triggering a roll-out of an already deployed application is a no-op. assertEquals(Change.empty(), app.instance().change()); tester.deploymentTrigger().forceChange(app.instanceId(), Change.of(appVersion1)); assertEquals(Change.empty(), app.instance().change()); + assertEquals(Optional.of(appVersion1), app.instance().latestDeployed()); } @Test @@ -538,19 +598,23 @@ public class DeploymentTriggerTest { // Change has a higher application version than what is deployed -- deployment should trigger. app1.timeOutUpgrade(productionUsCentral1); - assertEquals(version2, app1.instance().deployments().get(productionUsCentral1.zone(main)).version()); + assertEquals(version2, app1.deployment(productionUsCentral1.zone(main)).version()); assertEquals(revision2, app1.deployment(productionUsCentral1.zone(main)).applicationVersion()); // Change is again strictly dominated, and us-central-1 is skipped, even though it is still failing. - tester.clock().advance(Duration.ofHours(2).plus(Duration.ofSeconds(1))); // Enough time for retry + tester.clock().advance(Duration.ofHours(3)); // Enough time for retry tester.triggerJobs(); // Failing job is not retried as change has been deployed app1.assertNotRunning(productionUsCentral1); // Last job has a different deployment target, so tests need to run again. - app1.runJob(systemTest).runJob(stagingTest).runJob(productionEuWest1); - assertFalse(app1.instance().change().hasTargets()); - assertFalse(app1.instanceJobs().get(productionUsCentral1).isSuccess()); + app1.runJob(systemTest) + .runJob(stagingTest) // Eager test of outstanding change, assuming upgrade in west succeeds. + .runJob(productionEuWest1) // Upgrade completes, and revision is the only change. + .runJob(productionUsCentral1) // With only revision change, central should run to cover a previous failure. + .runJob(productionEuWest1); // Finally, west changes revision. + assertEquals(Change.empty(), app1.instance().change()); + assertEquals(Optional.of(RunStatus.success), app1.instanceJobs().get(productionUsCentral1).lastStatus()); } @Test @@ -792,6 +856,115 @@ public class DeploymentTriggerTest { } @Test + public void testMultipleInstancesWithDifferentChanges() { + DeploymentContext i1 = tester.newDeploymentContext("t", "a", "i1"); + DeploymentContext i2 = tester.newDeploymentContext("t", "a", "i2"); + DeploymentContext i3 = tester.newDeploymentContext("t", "a", "i3"); + DeploymentContext i4 = tester.newDeploymentContext("t", "a", "i4"); + ApplicationPackage applicationPackage = ApplicationPackageBuilder + .fromDeploymentXml("<deployment version='1'>\n" + + " <upgrade revision='separate' />\n" + + " <parallel>\n" + + " <instance id='i1'>\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <delay hours='6' />\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='i2'>\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " </prod>\n" + + " </instance>\n" + + " </parallel>\n" + + " <instance id='i3'>\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <delay hours='18' />\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='i4'>\n" + + " <test />\n" + + " <staging />\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " </prod>\n" + + " </instance>\n" + + "</deployment>\n"); + + // Package is submitted, and change propagated to the two first instances. + i1.submit(applicationPackage); + Optional<ApplicationVersion> v0 = i1.lastSubmission(); + tester.outstandingChangeDeployer().run(); + assertEquals(v0, i1.instance().change().application()); + assertEquals(v0, i2.instance().change().application()); + assertEquals(Optional.empty(), i3.instance().change().application()); + assertEquals(Optional.empty(), i4.instance().change().application()); + + // Tests run in i4, as they're declared there, and i1 and i2 get to work + i4.runJob(systemTest).runJob(stagingTest); + i1.runJob(productionUsEast3); + i2.runJob(productionUsEast3); + + // Since the post-deployment delay of i1 is incomplete, i3 doesn't yet get the change. + tester.outstandingChangeDeployer().run(); + assertEquals(v0, i1.instance().latestDeployed()); + assertEquals(v0, i2.instance().latestDeployed()); + assertEquals(Optional.empty(), i1.instance().change().application()); + assertEquals(Optional.empty(), i2.instance().change().application()); + assertEquals(Optional.empty(), i3.instance().change().application()); + assertEquals(Optional.empty(), i4.instance().change().application()); + + // When the delay is done, i3 gets the change. + tester.clock().advance(Duration.ofHours(6)); + tester.outstandingChangeDeployer().run(); + assertEquals(Optional.empty(), i1.instance().change().application()); + assertEquals(Optional.empty(), i2.instance().change().application()); + assertEquals(v0, i3.instance().change().application()); + assertEquals(Optional.empty(), i4.instance().change().application()); + + // v0 begins roll-out in i3, and v1 is submitted and rolls out in i1 and i2 some time later + i3.runJob(productionUsEast3); // v0 + tester.clock().advance(Duration.ofHours(12)); + i1.submit(applicationPackage); + Optional<ApplicationVersion> v1 = i1.lastSubmission(); + i4.runJob(systemTest).runJob(stagingTest); + i1.runJob(productionUsEast3); // v1 + i2.runJob(productionUsEast3); // v1 + assertEquals(v1, i1.instance().latestDeployed()); + assertEquals(v1, i2.instance().latestDeployed()); + assertEquals(Optional.empty(), i1.instance().change().application()); + assertEquals(Optional.empty(), i2.instance().change().application()); + assertEquals(v0, i3.instance().change().application()); + assertEquals(Optional.empty(), i4.instance().change().application()); + + // After some time, v2 also starts rolling out to i1 and i2, but does not complete in i2 + tester.clock().advance(Duration.ofHours(3)); + i1.submit(applicationPackage); + Optional<ApplicationVersion> v2 = i1.lastSubmission(); + i4.runJob(systemTest).runJob(stagingTest); + i1.runJob(productionUsEast3); // v2 + tester.clock().advance(Duration.ofHours(3)); + + // v1 is all done in i1 and i2, but does not yet roll out in i3; v2 is not completely rolled out there yet. + tester.outstandingChangeDeployer().run(); + assertEquals(v0, i3.instance().change().application()); + + // i3 completes v0, which rolls out to i4; v1 is ready for i3, but v2 is not. + i3.runJob(testUsEast3); + assertEquals(Optional.empty(), i3.instance().change().application()); + tester.outstandingChangeDeployer().run(); + assertEquals(v2, i1.instance().latestDeployed()); + assertEquals(v1, i2.instance().latestDeployed()); + assertEquals(v0, i3.instance().latestDeployed()); + assertEquals(Optional.empty(), i1.instance().change().application()); + assertEquals(v2, i2.instance().change().application()); + assertEquals(v1, i3.instance().change().application()); + assertEquals(v0, i4.instance().change().application()); + } + + @Test public void testMultipleInstances() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .instances("instance1,instance2") @@ -998,48 +1171,65 @@ public class DeploymentTriggerTest { assertEquals(Change.empty(), app2.instance().change()); assertEquals(Change.empty(), app3.instance().change()); - // Upgrade instance 1; a failure in any instance allows an application change to accompany the upgrade. + // Upgrade instance 1; upgrade rolls out first, with revision following. // The new platform won't roll out to the conservative instance until the normal one is upgraded. - app2.failDeployment(systemTest); app1.submit(applicationPackage); assertEquals(Change.of(version).with(app1.application().latestVersion().get()), app1.instance().change()); + // Upgrade platform. app2.runJob(systemTest); - app1.jobAborted(stagingTest) - .runJob(stagingTest) + app1.runJob(stagingTest) .runJob(productionUsWest1) .runJob(productionUsEast3); - app1.runJob(stagingTest); // Tests with only the outstanding application change. - app2.runJob(systemTest); // Tests with only the outstanding application change. + // Upgrade revision + tester.clock().advance(Duration.ofSeconds(1)); // Ensure we see revision as rolling after upgrade. + app2.runJob(systemTest); // R + app1.runJob(stagingTest) // R + .runJob(productionUsWest1); // R + // productionUsEast3 won't change revision before its production test has completed for the upgrade, which is one of the last jobs! tester.clock().advance(Duration.ofHours(2)); app1.runJob(productionEuWest1); tester.clock().advance(Duration.ofHours(1)); app1.runJob(productionAwsUsEast1a); - tester.triggerJobs(); app1.runJob(testAwsUsEast1a); + tester.clock().advance(Duration.ofSeconds(1)); + app1.runJob(productionAwsUsEast1a); // R + app1.runJob(testAwsUsEast1a); // R app1.runJob(productionApNortheast2); app1.runJob(productionApNortheast1); tester.clock().advance(Duration.ofHours(1)); app1.runJob(testApNortheast1); app1.runJob(testApNortheast2); + app1.runJob(productionApNortheast2); // R + app1.runJob(productionApNortheast1); // R app1.runJob(testUsEast3); app1.runJob(productionApSoutheast1); + tester.clock().advance(Duration.ofSeconds(1)); + app1.runJob(productionUsEast3); // R + tester.clock().advance(Duration.ofHours(2)); + app1.runJob(productionEuWest1); // R + tester.clock().advance(Duration.ofMinutes(330)); + app1.runJob(testApNortheast1); // R + app1.runJob(testApNortheast2); // R + app1.runJob(testUsEast3); // R + app1.runJob(productionApSoutheast1); // R + + app1.runJob(stagingTest); // Tests with only the outstanding application change. + app2.runJob(systemTest); // Tests with only the outstanding application change. // Confidence rises to high, for the new version, and instance 2 starts to upgrade. tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.outstandingChangeDeployer().run(); tester.triggerJobs(); - assertEquals(2, tester.jobs().active().size()); + assertEquals(tester.jobs().active().toString(), 1, tester.jobs().active().size()); assertEquals(Change.empty(), app1.instance().change()); assertEquals(Change.of(version), app2.instance().change()); assertEquals(Change.empty(), app3.instance().change()); - app1.runJob(stagingTest); // Never completed successfully with just the upgrade. - app2.runJob(systemTest) // Never completed successfully with just the upgrade. - .runJob(productionEuWest1) + app2.runJob(productionEuWest1) .failDeployment(testEuWest1); - // Instance 2 failed the last job, and now exist block window, letting application change roll out with the upgrade. + // Instance 2 failed the last job, and now exits block window, letting application change roll out with the upgrade. tester.clock().advance(Duration.ofDays(1)); // Leave block window for revisions. tester.upgrader().maintain(); tester.outstandingChangeDeployer().run(); @@ -1054,18 +1244,19 @@ public class DeploymentTriggerTest { assertEquals(Change.empty(), app2.instance().change()); assertEquals(Change.empty(), app3.instance().change()); - // Two first instances upgraded and with new revision — last instance gets change from whatever maintainer runs first. + // Two first instances upgraded and with new revision — last instance gets both changes as well. tester.upgrader().maintain(); tester.outstandingChangeDeployer().run(); - assertEquals(Change.of(version), app3.instance().change()); + assertEquals(Change.of(version).with(app1.lastSubmission().get()), app3.instance().change()); tester.deploymentTrigger().cancelChange(app3.instanceId(), ALL); tester.outstandingChangeDeployer().run(); tester.upgrader().maintain(); - assertEquals(Change.of(app1.application().latestVersion().get()), app3.instance().change()); + assertEquals(Change.of(app1.lastSubmission().get()), app3.instance().change()); app3.runJob(productionEuWest1); tester.upgrader().maintain(); + app1.runJob(stagingTest); app3.runJob(productionEuWest1); tester.triggerJobs(); assertEquals(List.of(), tester.jobs().active()); @@ -1073,22 +1264,410 @@ public class DeploymentTriggerTest { } @Test - public void testChangeCompletion() { - var app = tester.newDeploymentContext().submit().deploy(); - var version = new Version("7.1"); - tester.controllerTester().upgradeSystem(version); + public void testRevisionJoinsUpgradeWithSeparateRollout() { + var appPackage = new ApplicationPackageBuilder().region("us-central-1") + .region("us-east-3") + .region("us-west-1") + .upgradeRollout("separate") + .build(); + var app = tester.newDeploymentContext().submit(appPackage).deploy(); + + // Platform rolls through first production zone. + var version0 = tester.controller().readSystemVersion(); + var version1 = new Version("7.1"); + tester.controllerTester().upgradeSystem(version1); tester.upgrader().maintain(); app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); + tester.clock().advance(Duration.ofMinutes(1)); - app.submit(); - tester.triggerJobs(); + // Revision starts rolling, but stays behind. + var revision0 = app.lastSubmission(); + app.submit(appPackage); + var revision1 = app.lastSubmission(); + assertEquals(Change.of(version1).with(revision1.get()), app.instance().change()); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); + + // Upgrade got here first, so attempts to proceed alone, but the upgrade fails. + app.triggerJobs(); + assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + app.timeOutConvergence(productionUsEast3); + + // Revision is allowed to join. + app.triggerJobs(); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version1), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + app.runJob(productionUsEast3); + + // Platform and revision now proceed together. + app.runJob(stagingTest); + app.triggerJobs(); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + app.runJob(productionUsWest1); + assertEquals(Change.empty(), app.instance().change()); + } + + @Test + public void testProductionTestBlockingDeploymentWithSeparateRollout() { + var appPackage = new ApplicationPackageBuilder().region("us-east-3") + .region("us-west-1") + .delay(Duration.ofHours(1)) + .test("us-east-3") + .upgradeRollout("separate") + .build(); + var app = tester.newDeploymentContext().submit(appPackage) + .runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(productionUsWest1); + tester.clock().advance(Duration.ofHours(1)); + app.runJob(testUsEast3); + assertEquals(Change.empty(), app.instance().change()); + + // Platform rolls through first production zone. + var version0 = tester.controller().readSystemVersion(); + var version1 = new Version("7.1"); + tester.controllerTester().upgradeSystem(version1); + tester.upgrader().maintain(); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3); + + // Revision starts rolling, but waits for production test to verify the upgrade. + var revision0 = app.lastSubmission(); + app.submit(appPackage); + var revision1 = app.lastSubmission(); + assertEquals(Change.of(version1).with(revision1.get()), app.instance().change()); + app.runJob(systemTest).runJob(stagingTest).triggerJobs(); + app.assertRunning(productionUsWest1); + app.assertNotRunning(productionUsEast3); + + // Upgrade got here first, so attempts to proceed alone, but the upgrade fails. + app.triggerJobs(); + assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + app.timeOutConvergence(productionUsWest1).triggerJobs(); + + // Upgrade now fails between us-east-3 deployment and test, so test is abandoned, and revision unblocked. + app.assertRunning(productionUsEast3); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version1), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + app.runJob(productionUsEast3).triggerJobs() + .jobAborted(productionUsWest1).runJob(productionUsWest1); + tester.clock().advance(Duration.ofHours(1)); + app.runJob(testUsEast3); + assertEquals(Change.empty(), app.instance().change()); + } + + @Test + public void testProductionTestNotBlockingDeploymentWithSimultaneousRollout() { + var appPackage = new ApplicationPackageBuilder().region("us-east-3") + .region("us-central-1") + .region("us-west-1") + .delay(Duration.ofHours(1)) + .test("us-east-3") + .test("us-west-1") + .upgradeRollout("simultaneous") + .build(); + var app = tester.newDeploymentContext().submit(appPackage) + .runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(productionUsCentral1).runJob(productionUsWest1); + tester.clock().advance(Duration.ofHours(1)); + app.runJob(testUsEast3).runJob(testUsWest1); + assertEquals(Change.empty(), app.instance().change()); + + // Platform rolls through first production zone. + var version0 = tester.controller().readSystemVersion(); + var version1 = new Version("7.1"); + tester.controllerTester().upgradeSystem(version1); + tester.upgrader().maintain(); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3); + + // Revision starts rolling, and causes production test to abort when it reaches deployment. + var revision0 = app.lastSubmission(); + app.submit(appPackage); + var revision1 = app.lastSubmission(); + assertEquals(Change.of(version1).with(revision1.get()), app.instance().change()); + app.runJob(systemTest).runJob(stagingTest).triggerJobs(); + app.assertRunning(productionUsCentral1); + app.assertRunning(productionUsEast3); + + // Revision deploys to first prod zone. + app.triggerJobs(); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version1), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.clock().advance(Duration.ofSeconds(1)); + app.runJob(productionUsEast3); + + // Revision catches up in second prod zone. + app.runJob(systemTest).runJob(stagingTest).runJob(stagingTest).triggerJobs(); + app.jobAborted(productionUsCentral1).triggerJobs(); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsCentral1).get().versions()); + app.runJob(productionUsCentral1).triggerJobs(); + + // Revision proceeds alone in third prod zone, making test targets different for the two prod tests. + assertEquals(new Versions(version0, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + app.runJob(productionUsWest1); + app.triggerJobs(); + app.assertNotRunning(testUsEast3); + tester.clock().advance(Duration.ofHours(1)); + + // Test lets revision proceed alone, and us-west-1 is blocked until tested. + app.runJob(testUsEast3).triggerJobs(); + app.assertNotRunning(productionUsWest1); + app.runJob(testUsWest1).runJob(productionUsWest1).runJob(testUsWest1); // Test for us-east-3 is not re-run. + assertEquals(Change.empty(), app.instance().change()); + } + + @Test + public void testVeryLengthyPipelineRevisions() { + String lengthyDeploymentSpec = + "<deployment version='1.0'>\n" + + " <instance id='alpha'>\n" + + " <test />\n" + + " <staging />\n" + + " <upgrade revision='latest' />\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='beta'>\n" + + " <upgrade revision='separate' />\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='gamma'>\n" + + " <upgrade revision='separate' />\n" + // TODO: change to new, even stricter policy. + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + "</deployment>\n"; + var appPackage = ApplicationPackageBuilder.fromDeploymentXml(lengthyDeploymentSpec); + var alpha = tester.newDeploymentContext("t", "a", "alpha"); + var beta = tester.newDeploymentContext("t", "a", "beta"); + var gamma = tester.newDeploymentContext("t", "a", "gamma"); + alpha.submit(appPackage).deploy(); + + // revision2 is submitted, and rolls through alpha. + var revision1 = alpha.lastSubmission(); + alpha.submit(appPackage); + var revision2 = alpha.lastSubmission(); + + alpha.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(testUsEast3); + assertEquals(Optional.empty(), alpha.instance().change().application()); + + // revision3 is submitted when revision2 is half-way. tester.outstandingChangeDeployer().run(); - assertEquals(Change.of(version), app.instance().change()); + beta.runJob(productionUsEast3); + alpha.submit(appPackage); + var revision3 = alpha.lastSubmission(); + beta.runJob(testUsEast3); + assertEquals(Optional.empty(), beta.instance().change().application()); - app.runJob(productionUsEast3).runJob(productionUsWest1); - tester.triggerJobs(); + // revision3 is the target for alpha, beta is done, version1 is the target for gamma. + tester.outstandingChangeDeployer().run(); + assertEquals(revision3, alpha.instance().change().application()); + assertEquals(Optional.empty(), beta.instance().change().application()); + assertEquals(revision2, gamma.instance().change().application()); + + // revision3 rolls to beta, then a couple of new revisions are submitted to alpha, and the latter is the new target. + alpha.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(testUsEast3); + tester.outstandingChangeDeployer().run(); + assertEquals(Optional.empty(), alpha.instance().change().application()); + assertEquals(revision3, beta.instance().change().application()); + + // revision5 supersedes revision4 + alpha.submit(appPackage); + var revision4 = alpha.lastSubmission(); + alpha.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3); + alpha.submit(appPackage); + var revision5 = alpha.lastSubmission(); + alpha.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(Change.of(app.lastSubmission().get()), app.instance().change()); + assertEquals(Optional.empty(), alpha.instance().change().application()); + assertEquals(revision3, beta.instance().change().application()); + + // revision6 rolls through alpha, and becomes the next target for beta + alpha.submit(appPackage); + var revision6 = alpha.lastSubmission(); + alpha.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3) + .runJob(testUsEast3); + beta.runJob(productionUsEast3).runJob(testUsEast3); + tester.outstandingChangeDeployer().run(); + assertEquals(Optional.empty(), alpha.instance().change().application()); + assertEquals(revision6, beta.instance().change().application()); + + // revision6 rolls through beta, but revision3 is the next target for the strictest revision policy, in gamma + alpha.jobAborted(stagingTest).runJob(stagingTest); + beta.runJob(productionUsEast3).runJob(testUsEast3); + gamma.runJob(productionUsEast3).runJob(testUsEast3); + tester.outstandingChangeDeployer().run(); + assertEquals(Optional.empty(), alpha.instance().change().application()); + assertEquals(Optional.empty(), beta.instance().change().application()); + // TODO: assertEquals(revision3, gamma.instance().change().application()); + } + + @Test + public void testVeryLengthyPipelineUpgrade() { + String lengthyDeploymentSpec = + "<deployment version='1.0'>\n" + + " <instance id='alpha'>\n" + + " <test />\n" + + " <staging />\n" + + " <upgrade rollout='simultaneous' />\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='beta'>\n" + + " <upgrade rollout='simultaneous' />\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='gamma'>\n" + + " <upgrade rollout='separate' />\n" + + " <prod>\n" + + " <region>us-east-3</region>\n" + + " <test>us-east-3</test>\n" + + " </prod>\n" + + " </instance>\n" + + "</deployment>\n"; + var appPackage = ApplicationPackageBuilder.fromDeploymentXml(lengthyDeploymentSpec); + var alpha = tester.newDeploymentContext("t", "a", "alpha"); + var beta = tester.newDeploymentContext("t", "a", "beta"); + var gamma = tester.newDeploymentContext("t", "a", "gamma"); + alpha.submit(appPackage).deploy(); + + // A version releases, but when the first upgrade has gotten through alpha, beta, and gamma, a newer version has high confidence. + var version0 = tester.controller().readSystemVersion(); + var version1 = new Version("7.1"); + var version2 = new Version("7.2"); + tester.controllerTester().upgradeSystem(version1); + + tester.upgrader().maintain(); + alpha.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(testUsEast3); + assertEquals(Change.empty(), alpha.instance().change()); + + tester.upgrader().maintain(); + beta.runJob(productionUsEast3); + tester.controllerTester().upgradeSystem(version2); + beta.runJob(testUsEast3); + assertEquals(Change.empty(), beta.instance().change()); + + tester.upgrader().maintain(); + assertEquals(Change.of(version2), alpha.instance().change()); + assertEquals(Change.empty(), beta.instance().change()); + assertEquals(Change.of(version1), gamma.instance().change()); + } + + @Test + public void testRevisionJoinsUpgradeWithLeadingRollout() { + var appPackage = new ApplicationPackageBuilder().region("us-central-1") + .region("us-east-3") + .region("us-west-1") + .upgradeRollout("leading") + .build(); + var app = tester.newDeploymentContext().submit(appPackage).deploy(); + + // Platform rolls through first production zone. + var version0 = tester.controller().readSystemVersion(); + var version1 = new Version("7.1"); + tester.controllerTester().upgradeSystem(version1); + tester.upgrader().maintain(); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); + tester.clock().advance(Duration.ofMinutes(1)); + + // Revision starts rolling, and catches up. + var revision0 = app.lastSubmission(); + app.submit(appPackage); + var revision1 = app.lastSubmission(); + assertEquals(Change.of(version1).with(revision1.get()), app.instance().change()); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); + + // Upgrade got here first, and has triggered, but is now obsolete. + app.triggerJobs(); + assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + assertEquals(RunStatus.running, tester.jobs().last(app.instanceId(), productionUsEast3).get().status()); + + // Once staging tests verify the joint upgrade, the job is replaced with that. + app.runJob(stagingTest); + app.triggerJobs(); + app.jobAborted(productionUsEast3).runJob(productionUsEast3); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + + // Platform and revision now proceed together. + app.triggerJobs(); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + app.runJob(productionUsWest1); + assertEquals(Change.empty(), app.instance().change()); + } + + @Test + public void testRevisionPassesUpgradeWithSimultaneousRollout() { + var appPackage = new ApplicationPackageBuilder().region("us-central-1") + .region("us-east-3") + .region("us-west-1") + .upgradeRollout("simultaneous") + .build(); + var app = tester.newDeploymentContext().submit(appPackage).deploy(); + + // Platform rolls through first production zone. + var version0 = tester.controller().readSystemVersion(); + var version1 = new Version("7.1"); + tester.controllerTester().upgradeSystem(version1); + tester.upgrader().maintain(); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); + tester.clock().advance(Duration.ofMinutes(1)); + + // Revision starts rolling, and catches up. + var revision0 = app.lastSubmission(); + app.submit(appPackage); + var revision1 = app.lastSubmission(); + assertEquals(Change.of(version1).with(revision1.get()), app.instance().change()); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); + + // Upgrade got here first, and has triggered, but is now obsolete. + app.triggerJobs(); + app.assertRunning(productionUsEast3); + assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + assertEquals(RunStatus.running, tester.jobs().last(app.instanceId(), productionUsEast3).get().status()); + + // Once staging tests verify the joint upgrade, the job is replaced with that. + app.runJob(systemTest).runJob(stagingTest).runJob(stagingTest); + app.triggerJobs(); + app.jobAborted(productionUsEast3).runJob(productionUsEast3); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + + // Revision now proceeds alone. + app.triggerJobs(); + assertEquals(new Versions(version0, revision1.get(), Optional.of(version0), revision0), + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + app.runJob(productionUsWest1); + + // Upgrade follows. + app.triggerJobs(); + assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision1), + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + app.runJob(productionUsWest1); + assertEquals(Change.empty(), app.instance().change()); } @Test @@ -1102,7 +1681,7 @@ public class DeploymentTriggerTest { ZoneId.from("prod.cd-aws-us-east-1a")); tester.controllerTester() .setZones(zones, SystemName.cd) - .setRoutingMethod(zones, RoutingMethod.shared); + .setRoutingMethod(zones, RoutingMethod.sharedLayer4); tester.controllerTester().upgradeSystem(Version.fromString("6.1")); tester.controllerTester().computeVersionStatus(); var app = tester.newDeploymentContext(); @@ -1114,9 +1693,10 @@ public class DeploymentTriggerTest { tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionCdUsEast1, "user", false); app.runJob(productionCdUsEast1) .abortJob(stagingTest) // Complete failing run. - .runJob(stagingTest) + .runJob(stagingTest) // Run staging-test for production zone with no prior deployment. .runJob(productionCdAwsUsEast1a); + // Manually deploy to east again, then upgrade the system. app.runJob(productionCdUsEast1, cdPackage); var version = new Version("7.1"); tester.controllerTester().upgradeSystem(version); @@ -1124,16 +1704,16 @@ public class DeploymentTriggerTest { // System and staging tests both require unknown versions, and are broken. tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionCdUsEast1, "user", false); app.runJob(productionCdUsEast1) - .jobAborted(systemTest) + .abortJob(systemTest) .jobAborted(stagingTest) - .runJob(systemTest) - .runJob(stagingTest) + .runJob(systemTest) // Run test for aws zone again. + .runJob(stagingTest) // Run test for aws zone again. .runJob(productionCdAwsUsEast1a); + // Deploy manually again, then submit new package. app.runJob(productionCdUsEast1, cdPackage); app.submit(cdPackage); - app.jobAborted(systemTest) - .runJob(systemTest); + app.runJob(systemTest); // Staging test requires unknown initial version, and is broken. tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionCdUsEast1, "user", false); app.runJob(productionCdUsEast1) @@ -1145,7 +1725,7 @@ public class DeploymentTriggerTest { @Test public void testsInSeparateInstance() { String deploymentSpec = - "<deployment version='1.0'>\n" + + "<deployment version='1.0' athenz-domain='domain' athenz-service='service'>\n" + " <instance id='canary'>\n" + " <upgrade policy='canary' />\n" + " <test />\n" + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java index 061cc69fc26..d9b10ec933c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java @@ -28,6 +28,7 @@ import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.config.ControllerConfig; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; +import com.yahoo.vespa.hosted.controller.maintenance.JobRunner; import org.junit.Before; import org.junit.Test; @@ -53,6 +54,9 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.app import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTester.instanceId; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.deploymentFailed; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.installationFailed; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.reset; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.success; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished; @@ -336,6 +340,47 @@ public class InternalStepRunnerTest { } @Test + public void testCanBeReset() { + RunId id = app.startSystemTestTests(); + tester.cloud().add(new LogEntry(0, Instant.ofEpochMilli(123), info, "Not enough data!")); + tester.cloud().set(TesterCloud.Status.INCONCLUSIVE); + + long lastId1 = tester.jobs().details(id).get().lastId().getAsLong(); + Instant instant1 = tester.clock().instant(); + tester.runner().run(); + assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.endTests)); + assertEquals(running, tester.jobs().run(id).get().status()); + tester.cloud().clearLog(); + + // Test sleeps for a while. + tester.runner().run(); + assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.deployTester)); + tester.clock().advance(Duration.ofSeconds(899)); + tester.runner().run(); + assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.deployTester)); + + tester.clock().advance(JobRunner.jobTimeout); + var testZone = JobType.systemTest.zone(tester.controller().system()); + tester.runner().run(); + app.flushDnsUpdates(); + tester.configServer().convergeServices(app.instanceId(), testZone); + tester.configServer().convergeServices(app.testerId().id(), testZone); + tester.runner().run(); + assertEquals(unfinished, tester.jobs().run(id).get().stepStatuses().get(Step.endTests)); + assertTrue(tester.jobs().run(id).get().steps().get(Step.endTests).startTime().isPresent()); + + tester.cloud().set(TesterCloud.Status.SUCCESS); + long lastId2 = tester.jobs().details(id).get().lastId().getAsLong(); + tester.runner().run(); + assertEquals(success, tester.jobs().run(id).get().status()); + + assertTestLogEntries(id, Step.endTests, + new LogEntry(lastId1 + 1, Instant.ofEpochMilli(123), info, "Not enough data!"), + new LogEntry(lastId1 + 2, instant1, info, "Tests were inconclusive, and will run again in 15 minutes."), + new LogEntry(lastId2 + 1, tester.clock().instant(), info, "Tests completed successfully.")); + } + + @Test public void deployToDev() { ZoneId zone = JobType.devUsEast1.zone(system()); tester.jobs().deploy(app.instanceId(), JobType.devUsEast1, Optional.empty(), applicationPackage()); @@ -482,7 +527,7 @@ public class InternalStepRunnerTest { tester.clock().advance(InternalStepRunner.Timeouts.of(system()).testerCertificate().plus(Duration.ofSeconds(1))); tester.runner().run(); - assertEquals(RunStatus.aborted, tester.jobs().run(id).get().status()); + assertEquals(RunStatus.error, tester.jobs().run(id).get().status()); } private void assertTestLogEntries(RunId id, Step step, LogEntry... entries) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java index b81b3ae5d66..74c06d7ca1a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java @@ -11,15 +11,15 @@ import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveService; import com.yahoo.vespa.hosted.controller.api.integration.archive.MockArchiveService; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AccessControlService; import com.yahoo.vespa.hosted.controller.api.integration.athenz.MockAccessControlService; -import com.yahoo.vespa.hosted.controller.api.integration.aws.MockRoleService; -import com.yahoo.vespa.hosted.controller.api.integration.aws.RoleService; import com.yahoo.vespa.hosted.controller.api.integration.aws.MockCloudEventFetcher; import com.yahoo.vespa.hosted.controller.api.integration.aws.MockResourceTagger; +import com.yahoo.vespa.hosted.controller.api.integration.aws.MockRoleService; import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger; +import com.yahoo.vespa.hosted.controller.api.integration.aws.RoleService; import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController; import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingDatabaseClient; -import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController; import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingDatabaseClientMock; +import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController; import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistry; import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistryMock; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock; @@ -32,10 +32,8 @@ import com.yahoo.vespa.hosted.controller.api.integration.horizon.MockHorizonClie import com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever; import com.yahoo.vespa.hosted.controller.api.integration.organization.MockIssueHandler; import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumerMock; -import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceDatabaseClientMock; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceDatabaseClient; -import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService; -import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceDatabaseClientMock; import com.yahoo.vespa.hosted.controller.api.integration.secrets.NoopTenantSecretService; import com.yahoo.vespa.hosted.controller.api.integration.stubs.DummyOwnershipIssues; import com.yahoo.vespa.hosted.controller.api.integration.stubs.DummySystemMonitor; @@ -59,7 +57,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg private final ZoneRegistryMock zoneRegistryMock; private final ConfigServerMock configServerMock; private final MemoryNameService memoryNameService = new MemoryNameService(); - private final MemoryGlobalRoutingService memoryGlobalRoutingService = new MemoryGlobalRoutingService(); private final MockMailer mockMailer = new MockMailer(); private final EndpointCertificateMock endpointCertificateMock = new EndpointCertificateMock(); private final EndpointCertificateValidatorMock endpointCertificateValidatorMock = new EndpointCertificateValidatorMock(); @@ -116,11 +113,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg } @Override - public GlobalRoutingService globalRoutingService() { - return memoryGlobalRoutingService; - } - - @Override public MockMailer mailer() { return mockMailer; } @@ -279,10 +271,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg return configServerMock; } - public MemoryGlobalRoutingService globalRoutingServiceMock() { - return memoryGlobalRoutingService; - } - public MockContactRetriever contactRetrieverMock() { return mockContactRetriever; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java index 23ab91aaf8c..a4c30cca29e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java @@ -52,26 +52,28 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry */ public ZoneRegistryMock(SystemName system) { this.system = system; - this.zones = system.isPublic() ? - List.of(ZoneApiMock.fromId("test.aws-us-east-1c"), - ZoneApiMock.fromId("staging.aws-us-east-1c"), - ZoneApiMock.fromId("prod.aws-us-east-1c"), - ZoneApiMock.fromId("prod.aws-eu-west-1a")) : - List.of(ZoneApiMock.fromId("test.us-east-1"), - ZoneApiMock.fromId("staging.us-east-3"), - ZoneApiMock.fromId("dev.us-east-1"), - ZoneApiMock.fromId("dev.aws-us-east-2a"), - ZoneApiMock.fromId("perf.us-east-3"), - ZoneApiMock.fromId("prod.aws-us-east-1a"), - ZoneApiMock.fromId("prod.ap-northeast-1"), - ZoneApiMock.fromId("prod.ap-northeast-2"), - ZoneApiMock.fromId("prod.ap-southeast-1"), - ZoneApiMock.fromId("prod.us-east-3"), - ZoneApiMock.fromId("prod.us-west-1"), - ZoneApiMock.fromId("prod.us-central-1"), - ZoneApiMock.fromId("prod.eu-west-1")); - // All zones use a shared routing method by default - setRoutingMethod(this.zones, system.isPublic() ? RoutingMethod.exclusive : RoutingMethod.shared); + if (system.isPublic()) { + this.zones = List.of(ZoneApiMock.fromId("test.aws-us-east-1c"), + ZoneApiMock.fromId("staging.aws-us-east-1c"), + ZoneApiMock.fromId("prod.aws-us-east-1c"), + ZoneApiMock.fromId("prod.aws-eu-west-1a")); + setRoutingMethod(this.zones, RoutingMethod.exclusive); + } else { + this.zones = List.of(ZoneApiMock.fromId("test.us-east-1"), + ZoneApiMock.fromId("staging.us-east-3"), + ZoneApiMock.fromId("dev.us-east-1"), + ZoneApiMock.fromId("dev.aws-us-east-2a"), + ZoneApiMock.fromId("perf.us-east-3"), + ZoneApiMock.fromId("prod.aws-us-east-1a"), + ZoneApiMock.fromId("prod.ap-northeast-1"), + ZoneApiMock.fromId("prod.ap-northeast-2"), + ZoneApiMock.fromId("prod.ap-southeast-1"), + ZoneApiMock.fromId("prod.us-east-3"), + ZoneApiMock.fromId("prod.us-west-1"), + ZoneApiMock.fromId("prod.us-central-1"), + ZoneApiMock.fromId("prod.eu-west-1")); + setRoutingMethod(this.zones, RoutingMethod.sharedLayer4); + } } public ZoneRegistryMock setDeploymentTimeToLive(ZoneId zone, Duration duration) { @@ -117,18 +119,15 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry } public ZoneRegistryMock setRoutingMethod(ZoneApi zone, RoutingMethod... routingMethods) { - return setRoutingMethod(zone, List.of(routingMethods)); + return setRoutingMethod(zone, Set.of(routingMethods)); } public ZoneRegistryMock setRoutingMethod(List<? extends ZoneApi> zones, RoutingMethod... routingMethods) { - zones.forEach(zone -> setRoutingMethod(zone, List.of(routingMethods))); + zones.forEach(zone -> setRoutingMethod(zone, Set.of(routingMethods))); return this; } - public ZoneRegistryMock setRoutingMethod(ZoneApi zone, List<RoutingMethod> routingMethods) { - if (routingMethods.stream().distinct().count() != routingMethods.size()) { - throw new IllegalArgumentException("Routing methods must be distinct"); - } + private ZoneRegistryMock setRoutingMethod(ZoneApi zone, Set<RoutingMethod> routingMethods) { this.zoneRoutingMethods.put(zone, List.copyOf(routingMethods)); return this; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java index d6e43c07ec8..df2b462914e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java @@ -15,6 +15,7 @@ import org.junit.Test; import java.time.Duration; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; @@ -35,14 +36,16 @@ public class ArchiveAccessMaintainerTest { createTenantWithAccessRole(tester, "tenant2", tenant2role); ZoneId testZone = ZoneId.from("prod.aws-us-east-1c"); - tester.controller().archiveBucketDb().archiveUriFor(testZone, tenant1); + tester.controller().archiveBucketDb().archiveUriFor(testZone, tenant1, true); var testBucket = new ArchiveBucket("bucketName", "keyArn").withTenant(tenant1); MockArchiveService archiveService = (MockArchiveService) tester.controller().serviceRegistry().archiveService(); - assertNull(archiveService.authorizedIamRoles.get(testBucket)); + assertNull(archiveService.authorizedIamRolesForBucket.get(testBucket)); + assertNull(archiveService.authorizedIamRolesForKey.get(testBucket.keyArn())); MockMetric metric = new MockMetric(); new ArchiveAccessMaintainer(tester.controller(), metric, Duration.ofMinutes(10)).maintain(); - assertEquals(Map.of(tenant1, tenant1role), archiveService.authorizedIamRoles.get(testBucket)); + assertEquals(Map.of(tenant1, tenant1role), archiveService.authorizedIamRolesForBucket.get(testBucket)); + assertEquals(Set.of(tenant1role), archiveService.authorizedIamRolesForKey.get(testBucket.keyArn())); var expected = Map.of("archive.bucketCount", tester.controller().zoneRegistry().zones().all().ids().stream() diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java index 451991f9604..0a2f5d9a236 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java @@ -43,6 +43,9 @@ public class ArchiveUriUpdaterTest { // Initially we should not set any archive URIs as the archive service does not return any updater.maintain(); assertArchiveUris(Map.of(), zone); + // but the controller zone is always present + assertArchiveUris(Map.of(TenantName.from("hosted-vespa"), "s3://bucketName/hosted-vespa/"), + ZoneId.from("prod", "controller")); // Archive service now has URI for tenant1, but tenant1 is not deployed in zone setBucketNameInService(Map.of(tenant1, "uri-1"), zone); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java index 0e365ffd135..7c4203d253c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java @@ -56,6 +56,22 @@ public class DeploymentIssueReporterTest { } @Test + public void nonProductionAppGetsNoIssues() { + tester.controllerTester().upgradeSystem(Version.fromString("6.2")); + var app = tester.newDeploymentContext("application", "tenant", "default"); + Contact contact = tester.controllerTester().serviceRegistry().contactRetrieverMock().contact(); + tester.controller().tenants().lockOrThrow(app.instanceId().tenant(), LockedTenant.Athenz.class, tenant -> + tester.controller().tenants().store(tenant.with(contact))); + + // app submits a package with no production deployments, and shall not receive issues. + app.submit(new ApplicationPackageBuilder().systemTest().stagingTest().build()).runJob(systemTest).failDeployment(stagingTest); + + // Advance to where deployment issues should be detected. + tester.clock().advance(maxFailureAge.plus(Duration.ofDays(1))); + assertFalse("No issues are produced for app.", issues.isOpenFor(app.application().id())); + } + + @Test public void testDeploymentFailureReporting() { tester.controllerTester().upgradeSystem(Version.fromString("6.2")); @@ -72,6 +88,7 @@ public class DeploymentIssueReporterTest { tester.controller().tenants().lockOrThrow(app3.instanceId().tenant(), LockedTenant.Athenz.class, tenant -> tester.controller().tenants().store(tenant.with(contact))); + // NOTE: All maintenance should be idempotent within a small enough time interval, so maintain is called twice in succession throughout. // app 1 fails staging tests. diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java index d97f1d58043..455e802e87b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java @@ -283,7 +283,7 @@ public class MetricsReporterTest { context.submit(applicationPackage).deploy(); reporter.maintain(); - assertEquals("Deployment queues name services requests", 6, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue()); + assertEquals("Deployment queues name services requests", 2, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue()); context.flushDnsUpdates(); reporter.maintain(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java index 8e90cfc69f3..acaa8b24d9d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; @@ -30,42 +31,34 @@ public class OutstandingChangeDeployerTest { OutstandingChangeDeployer deployer = new OutstandingChangeDeployer(tester.controller(), Duration.ofMinutes(10)); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") + .upgradeRevision("separate") .build(); - var app1 = tester.newDeploymentContext("tenant", "app1", "default").submit(applicationPackage).deploy(); + var app = tester.newDeploymentContext().submit(applicationPackage).deploy(); Version version = new Version(6, 2); - tester.deploymentTrigger().triggerChange(app1.instanceId(), Change.of(version)); - tester.deploymentTrigger().triggerReadyJobs(); + tester.deploymentTrigger().triggerChange(app.instanceId(), Change.of(version)); + assertEquals(Change.of(version), app.instance().change()); + assertFalse(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); - assertEquals(Change.of(version), app1.instance().change()); - assertFalse(app1.deploymentStatus().outstandingChange(app1.instance().name()).hasTargets()); + app.submit(applicationPackage); + Optional<ApplicationVersion> revision = app.lastSubmission(); + assertFalse(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); + assertEquals(Change.of(version).with(revision.get()), app.instance().change()); - assertEquals(1, app1.application().latestVersion().get().buildNumber().getAsLong()); - app1.submit(applicationPackage, Optional.of(new SourceRevision("repository1", "master", "cafed00d"))); + app.submit(applicationPackage); + Optional<ApplicationVersion> outstanding = app.lastSubmission(); + assertTrue(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); + assertEquals(Change.of(version).with(revision.get()), app.instance().change()); - assertTrue(app1.deploymentStatus().outstandingChange(app1.instance().name()).hasTargets()); - assertEquals("1.0.2-cafed00d", app1.deploymentStatus().outstandingChange(app1.instance().name()).application().get().id()); - app1.assertRunning(JobType.systemTest); - app1.assertRunning(JobType.stagingTest); - assertEquals(2, tester.jobs().active().size()); + tester.outstandingChangeDeployer().run(); + assertTrue(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); + assertEquals(Change.of(version).with(revision.get()), app.instance().change()); - deployer.maintain(); - tester.triggerJobs(); - assertEquals("No effect as job is in progress", 2, tester.jobs().active().size()); - assertEquals("1.0.2-cafed00d", app1.deploymentStatus().outstandingChange(app1.instance().name()).application().get().id()); - - app1.runJob(JobType.systemTest).runJob(JobType.stagingTest).runJob(JobType.productionUsWest1) - .runJob(JobType.stagingTest).runJob(JobType.systemTest); - assertEquals("Upgrade done", 0, tester.jobs().active().size()); - - deployer.maintain(); - tester.triggerJobs(); - assertEquals("1.0.2-cafed00d", app1.instance().change().application().get().id()); - List<Run> runs = tester.jobs().active(); - assertEquals(1, runs.size()); - app1.assertRunning(JobType.productionUsWest1); - assertFalse(app1.deploymentStatus().outstandingChange(app1.instance().name()).hasTargets()); + app.deploy(); + tester.outstandingChangeDeployer().run(); + assertFalse(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); + assertEquals(Change.of(outstanding.get()), app.instance().change()); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java index f9a976fcadc..265dedec8d0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java @@ -35,6 +35,7 @@ import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobTy import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.systemTest; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.ALL; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PIN; +import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PLATFORM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -167,7 +168,6 @@ public class UpgraderTest { // --- Failing application is repaired by changing the application, causing confidence to move above 'high' threshold // Deploy application change default0.submit(applicationPackage("default")); - default0.triggerJobs().jobAborted(stagingTest); default0.deploy(); tester.controllerTester().computeVersionStatus(); @@ -534,7 +534,7 @@ public class UpgraderTest { } @Test - public void testBlockVersionChangeHalfwayThoughThenNewRevision() { + public void testBlockVersionChangeHalfwayThroughThenNewRevision() { // Friday, 16:00 tester.at(Instant.parse("2017-09-29T16:00:00.00Z")); @@ -542,7 +542,7 @@ public class UpgraderTest { tester.controllerTester().upgradeSystem(version); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() - // Block upgrades on weekends and ouside working hours + // Block upgrades on weekends and outside working hours .blockChange(false, true, "mon-fri", "00-09,17-23", "UTC") .blockChange(false, true, "sat-sun", "00-23", "UTC") .region("us-west-1") @@ -570,19 +570,20 @@ public class UpgraderTest { // A new revision is submitted and starts rolling out. app.submit(applicationPackage); - // production-us-central-1 isn't triggered, as the revision + platform is the new change to roll out. + // production-us-central-1 is re-triggered with upgrade until revision catches up. tester.triggerJobs(); - assertEquals(2, tester.jobs().active().size()); + assertEquals(3, tester.jobs().active().size()); app.runJob(systemTest).runJob(stagingTest).runJob(productionUsWest1); // us-central-1 has an older version, and needs a new staging test to begin. - app.runJob(stagingTest); + app.runJob(stagingTest).triggerJobs().jobAborted(productionUsCentral1); // Retry will include revision. tester.triggerJobs(); // Triggers us-central-1 before platform upgrade is cancelled. - // A new version is also released, cancelling the upgrade, since it is failing on a now outdated version. + // A new version is also released, and someone cancels the upgrade, suspecting it is faulty. tester.clock().advance(Duration.ofHours(17)); version = Version.fromString("6.4"); tester.controllerTester().upgradeSystem(version); tester.upgrader().maintain(); + tester.deploymentTrigger().cancelChange(app.instanceId(), PLATFORM); // us-central-1 succeeds upgrade to 6.3, with the revision, but us-east-3 wants to proceed with only the revision change. app.runJob(productionUsCentral1); @@ -799,8 +800,10 @@ public class UpgraderTest { app.instance().change().application().get().id().equals(applicationVersion)); // Deployment completes - app.runJob(systemTest).runJob(stagingTest).runJob(productionUsWest1).runJob(productionUsEast3); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + app.runJob(systemTest).runJob(stagingTest) + .runJob(productionUsWest1) + .runJob(productionUsEast3); + assertEquals("All jobs consumed", List.of(), tester.jobs().active()); for (Deployment deployment : app.instance().deployments().values()) { assertEquals(version, deployment.version()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java index b33f8f6f7e7..e509a199c82 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java @@ -42,6 +42,8 @@ import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import static org.junit.Assert.assertEquals; @@ -89,12 +91,14 @@ public class ApplicationSerializerTest { Optional.of(Instant.ofEpochMilli(666)), Optional.empty(), Optional.of("best commit"), - true); + true, + Optional.of("hash1")); assertEquals("https://github/org/repo/tree/commit1", applicationVersion1.sourceUrl().get()); ApplicationVersion applicationVersion2 = ApplicationVersion .from(new SourceRevision("repo1", "branch1", "commit1"), 32, "a@b", Version.fromString("6.3.1"), Instant.ofEpochMilli(496)); + SortedSet<ApplicationVersion> versions = new TreeSet<>(Set.of(applicationVersion2)); Instant activityAt = Instant.parse("2018-06-01T10:15:30.00Z"); deployments.add(new Deployment(zone1, applicationVersion1, Version.fromString("1.2.3"), Instant.ofEpochMilli(3), DeploymentMetrics.none, DeploymentActivity.none, QuotaUsage.none, OptionalDouble.empty())); @@ -120,13 +124,15 @@ public class ApplicationSerializerTest { Map.of(JobType.systemTest, Instant.ofEpochMilli(333)), List.of(AssignedRotation.fromStrings("foo", "default", "my-rotation", Set.of("us-west-1"))), rotationStatus, - Change.of(new Version("6.1"))), + Change.of(new Version("6.1")), + Optional.of(applicationVersion2)), new Instance(id3, List.of(), Map.of(), List.of(), RotationStatus.EMPTY, - Change.of(Version.fromString("6.7")).withPin())); + Change.of(Version.fromString("6.7")).withPin(), + Optional.empty())); Application original = new Application(TenantAndApplicationId.from(id1), Instant.now().truncatedTo(ChronoUnit.MILLIS), @@ -140,6 +146,7 @@ public class ApplicationSerializerTest { Set.of(publicKey, otherPublicKey), projectId, Optional.of(applicationVersion1), + versions, instances); Application serialized = APPLICATION_SERIALIZER.fromSlime(SlimeUtils.toJsonBytes(APPLICATION_SERIALIZER.toSlime(original))); @@ -151,6 +158,10 @@ public class ApplicationSerializerTest { assertEquals(original.latestVersion().get().buildTime(), serialized.latestVersion().get().buildTime()); assertEquals(original.latestVersion().get().sourceUrl(), serialized.latestVersion().get().sourceUrl()); assertEquals(original.latestVersion().get().commit(), serialized.latestVersion().get().commit()); + assertEquals(original.latestVersion().get().bundleHash(), serialized.latestVersion().get().bundleHash()); + assertEquals(original.versions(), serialized.versions()); + assertEquals(original.versions(), serialized.versions()); + assertEquals(original.deploymentSpec().xmlForm(), serialized.deploymentSpec().xmlForm()); assertEquals(original.validationOverrides().xmlForm(), serialized.validationOverrides().xmlForm()); @@ -182,6 +193,9 @@ public class ApplicationSerializerTest { assertEquals(original.require(id1.instance()).change(), serialized.require(id1.instance()).change()); assertEquals(original.require(id3.instance()).change(), serialized.require(id3.instance()).change()); + assertEquals(original.require(id1.instance()).latestDeployed(), serialized.require(id1.instance()).latestDeployed()); + assertEquals(original.require(id3.instance()).latestDeployed(), serialized.require(id3.instance()).latestDeployed()); + // Test metrics assertEquals(original.metrics().queryServiceQuality(), serialized.metrics().queryServiceQuality(), Double.MIN_VALUE); assertEquals(original.metrics().writeServiceQuality(), serialized.metrics().writeServiceQuality(), Double.MIN_VALUE); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java index eca2b17a5f1..f4f52b20325 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java @@ -91,7 +91,8 @@ public class RunSerializerTest { Optional.of(Instant.ofEpochMilli(100)), Optional.empty(), Optional.empty(), - true); + true, + Optional.empty()); assertEquals(applicationVersion, run.versions().targetApplication()); assertEquals(applicationVersion.authorEmail(), run.versions().targetApplication().authorEmail()); assertEquals(applicationVersion.buildTime(), run.versions().targetApplication().buildTime()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json index 7b9131a38dd..54cde2bacef 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json @@ -4,6 +4,7 @@ "type": "production-us-east-3", "number": 112358, "start": 1196676930000, + "sleepUntil": 1196676930100, "status": "running", "lastTestRecord": 3, "lastVespaLogTimestamp": 1196676930000432, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java index a6d061e7c80..2fd8026319b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java @@ -92,15 +92,7 @@ public class ControllerContainerCloudTest extends ControllerContainerTest { Request request = new Request("http://localhost:8080" + path, data, method, principal); request.getAttributes().put(SecurityContext.ATTRIBUTE_NAME, new SecurityContext(principal, roles)); if (user != null) { - Map<String, String> userAttributes = new HashMap<>(); - userAttributes.put("email", user.email()); - if (user.name() != null) - userAttributes.put("name", user.name()); - if (user.nickname() != null) - userAttributes.put("nickname", user.nickname()); - if (user.picture() != null) - userAttributes.put("picture", user.picture()); - request.getAttributes().put(User.ATTRIBUTE_NAME, Map.copyOf(userAttributes)); + request.getAttributes().put(User.ATTRIBUTE_NAME, user); } request.getHeaders().put("Content-Type", contentType); return request; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java index a4de6ab7700..92055c85a53 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java @@ -238,7 +238,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { @Test public void create_application_on_deploy() { var application = ApplicationName.from("unique"); - var applicationPackage = new ApplicationPackageBuilder().build(); + var applicationPackage = new ApplicationPackageBuilder().withoutAthenzIdentity().build(); assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isEmpty()); @@ -256,6 +256,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { var application = ApplicationName.from("unique"); var applicationPackage = new ApplicationPackageBuilder() .trustDefaultCertificate() + .withoutAthenzIdentity() .build(); assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isEmpty()); @@ -273,6 +274,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { private ApplicationPackageBuilder prodBuilder() { return new ApplicationPackageBuilder() + .withoutAthenzIdentity() .instances("default") .region("aws-us-east-1c"); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index 8eaa190e9fa..516911b3c7b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -26,8 +26,6 @@ import com.yahoo.vespa.athenz.api.AthenzPrincipal; import com.yahoo.vespa.athenz.api.AthenzUser; import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.athenz.api.OktaIdentityToken; -import com.yahoo.vespa.flags.Flags; -import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.Instance; @@ -126,6 +124,7 @@ public class ApplicationApiTest extends ControllerContainerTest { private static final String accessDenied = "{\n \"code\" : 403,\n \"message\" : \"Access denied\"\n}"; private static final ApplicationPackage applicationPackageDefault = new ApplicationPackageBuilder() + .withoutAthenzIdentity() .instances("default") .globalServiceId("foo") .region("us-central-1") @@ -135,6 +134,7 @@ public class ApplicationApiTest extends ControllerContainerTest { .build(); private static final ApplicationPackage applicationPackageInstance1 = new ApplicationPackageBuilder() + .withoutAthenzIdentity() .instances("instance1") .globalServiceId("foo") .region("us-central-1") @@ -338,6 +338,7 @@ public class ApplicationApiTest extends ControllerContainerTest { app1.runJob(JobType.systemTest).runJob(JobType.stagingTest).runJob(JobType.productionUsCentral1); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() + .withoutAthenzIdentity() .instances("instance1") .globalServiceId("foo") .region("us-west-1") @@ -448,7 +449,6 @@ public class ApplicationApiTest extends ControllerContainerTest { deploymentTester.upgrader().overrideConfidence(Version.fromString("6.1"), VespaVersion.Confidence.broken); deploymentTester.controllerTester().computeVersionStatus(); setDeploymentMaintainedInfo(); - setZoneInRotation("rotation-fqdn-1", ZoneId.from("prod", "us-central-1")); // GET tenant application deployments tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", GET) @@ -817,6 +817,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // Sixth attempt has a multi-instance deployment spec, and is accepted. ApplicationPackage multiInstanceSpec = new ApplicationPackageBuilder() + .withoutAthenzIdentity() .instances("instance1,instance2") .region("us-central-1") .parallel("us-west-1", "us-east-3") @@ -949,7 +950,6 @@ public class ApplicationApiTest extends ControllerContainerTest { 404); // GET global rotation status - setZoneInRotation("rotation-fqdn-1", ZoneId.from("prod", "us-west-1")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation", GET) .userIdentity(USER_ID), new File("global-rotation.json")); @@ -1001,10 +1001,6 @@ public class ApplicationApiTest extends ControllerContainerTest { var app = deploymentTester.newDeploymentContext("tenant1", "application1", "instance1"); app.submit(applicationPackage).deploy(); - setZoneInRotation("rotation-fqdn-2", ZoneId.from("prod", "us-west-1")); - setZoneInRotation("rotation-fqdn-2", ZoneId.from("prod", "us-east-3")); - setZoneInRotation("rotation-fqdn-1", ZoneId.from("prod", "eu-west-1")); - // GET global rotation status without specifying endpointId fails tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation", GET) .userIdentity(USER_ID), @@ -1528,7 +1524,7 @@ public class ApplicationApiTest extends ControllerContainerTest { var app = deploymentTester.newDeploymentContext(createTenantAndApplication()); var zone = ZoneId.from(Environment.prod, RegionName.from("us-west-1")); deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone), - List.of(RoutingMethod.exclusive, RoutingMethod.shared)); + RoutingMethod.exclusive); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("domain"), AthenzService.from("service")) .instances("instance1") @@ -1547,15 +1543,6 @@ public class ApplicationApiTest extends ControllerContainerTest { .userIdentity(USER_ID), new File("deployment-with-routing-policy.json")); - // GET deployment including legacy endpoints - tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/instance1", GET) - .userIdentity(USER_ID) - .properties(Map.of("includeLegacyEndpoints", "true")), - new File("deployment-with-routing-policy-legacy.json")); - - // Hide shared endpoints - ((InMemoryFlagSource) tester.controller().flagSource()).withBooleanFlag(Flags.HIDE_SHARED_ROUTING_ENDPOINT.id(), true); - // GET deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/instance1", GET) .userIdentity(USER_ID), @@ -1566,8 +1553,7 @@ public class ApplicationApiTest extends ControllerContainerTest { public void support_access() { var app = deploymentTester.newDeploymentContext(createTenantAndApplication()); var zone = ZoneId.from(Environment.prod, RegionName.from("us-west-1")); - deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone), - List.of(RoutingMethod.exclusive, RoutingMethod.shared)); + deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.exclusive); addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR)); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("domain"), AthenzService.from("service")) @@ -1817,11 +1803,6 @@ public class ApplicationApiTest extends ControllerContainerTest { } } - private void setZoneInRotation(String rotationName, ZoneId zone) { - tester.serviceRegistry().globalRoutingServiceMock().setStatus(rotationName, zone, com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus.IN); - //new RotationStatusUpdater(tester.controller(), Duration.ofDays(1)).run(); - } - private void updateContactInformation() { Contact contact = new Contact(URI.create("www.contacts.tld/1234"), URI.create("www.properties.tld/1234"), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java index 2ae755ac8fe..4935ab22586 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java @@ -192,7 +192,7 @@ public class JobControllerApiHandlerHelperTest { private void compare(HttpResponse response, String expected) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); response.render(baos); - JsonTestHelper.assertJsonEquals(expected, baos.toString()); + JsonTestHelper.assertJsonEquals(baos.toString(), expected); } private void assertResponse(HttpResponse response, String fileName) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java index 5bf22ede19d..2c81b1a7fd8 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.restapi.application; -import com.google.inject.Key; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.jdisc.Container; import com.yahoo.jdisc.Request; @@ -72,9 +71,6 @@ public class MultipartParserTest { public RequestHandler resolveHandler(Request request) { return null; } @Override - public <T> T getInstance(Key<T> key) { return null; } - - @Override public <T> T getInstance(Class<T> aClass) { return null; } @Override diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json index a214969485d..fd4093ca332 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json @@ -37,7 +37,6 @@ }, "status": "complete", "quota": "(ignore)", - "archiveUri": "s3://bucketName/scoober/", "activity": {}, "metrics": { "queriesPerSecond": 0.0, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json index b8c48eb3d0c..6223cbbb2b9 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json @@ -1223,5 +1223,40 @@ } ] } + ], + "builds": [ + { + "hash": "1.0.3-commit1", + "build": 3, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + }, + { + "hash": "1.0.2-commit1", + "build": 2, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + }, + { + "hash": "1.0.1-commit1", + "build": 1, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + } ] } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json index abe3d4100d9..e3fb8eec9c4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json @@ -21,7 +21,7 @@ "platform": { "platform": "6.1.0", "at": "(ignore)", - "upgrade": false, + "upgrade": true, "blockers": [] }, "application": { @@ -536,7 +536,7 @@ "platform": { "platform": "6.1.0", "at": "(ignore)", - "upgrade": false, + "upgrade": true, "blockers": [] }, "application": { @@ -547,7 +547,7 @@ "commit": "commit1" }, "at": 1000, - "upgrade": false, + "upgrade": true, "blockers": [] } } @@ -630,6 +630,52 @@ ], "runs": [] } + ], + "builds": [ + { + "hash": "1.0.4-commit1", + "build": 4, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + }, + { + "hash": "1.0.3-commit1", + "build": 3, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + }, + { + "hash": "1.0.2-commit1", + "build": 2, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + }, + { + "hash": "1.0.1-commit1", + "build": 1, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + } ] } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy-legacy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy-legacy.json deleted file mode 100644 index eb508b2459e..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy-legacy.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "tenant": "tenant1", - "application": "application1", - "instance": "instance1", - "environment": "prod", - "region": "us-west-1", - "endpoints": [ - { - "cluster": "default", - "tls": true, - "url": "https://instance1.application1.tenant1.us-west-1.vespa.oath.cloud/", - "scope": "zone", - "routingMethod": "exclusive", - "legacy": false - }, - { - "cluster": "default", - "tls": true, - "url": "https://instance1--application1--tenant1.us-west-1.vespa.oath.cloud:4443/", - "scope": "zone", - "routingMethod": "shared", - "legacy": false - }, - { - "cluster": "default", - "tls": false, - "url": "http://instance1.application1.tenant1.us-west-1.prod.vespa.yahooapis.com:4080/", - "scope": "zone", - "routingMethod": "shared", - "legacy": true - }, - { - "cluster": "default", - "tls": true, - "url": "https://instance1--application1--tenant1.us-west-1.prod.vespa.yahooapis.com:4443/", - "scope": "zone", - "routingMethod": "shared", - "legacy": true - } - ], - "clusters": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/clusters", - "nodes": "http://localhost:8080/zone/v2/prod/us-west-1/nodes/v2/node/?recursive=true&application=tenant1.application1.instance1", - "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-west-1&application=tenant1.application1.instance1", - "version": "6.1.0", - "revision": "1.0.1-commit1", - "deployTimeEpochMs": "(ignore)", - "screwdriverId": "1000", - "gitRepository": "repository1", - "gitBranch": "master", - "gitCommit": "commit1", - "applicationVersion": { - "hash": "1.0.1-commit1", - "build": 1, - "source": { - "gitRepository": "repository1", - "gitBranch": "master", - "gitCommit": "commit1" - }, - "sourceUrl": "repository1/tree/commit1", - "commit": "commit1" - }, - "status": "complete", - "quota": "(ignore)", - "activity": {}, - "metrics": { - "queriesPerSecond": 0.0, - "writesPerSecond": 0.0, - "documentCount": 0.0, - "queryLatencyMillis": 0.0, - "writeLatencyMillis": 0.0 - } -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json index 97ac87fb5a0..4457bede34e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json @@ -12,14 +12,6 @@ "scope": "zone", "routingMethod": "exclusive", "legacy": false - }, - { - "cluster": "default", - "tls": true, - "url": "https://instance1--application1--tenant1.us-west-1.vespa.oath.cloud:4443/", - "scope": "zone", - "routingMethod": "shared", - "legacy": false } ], "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/clusters", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json index ab2a3bf945c..621617f1b1c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json @@ -8,17 +8,17 @@ { "cluster": "default", "tls": true, - "url": "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/", + "url": "https://instance1.application1.tenant1.us-central-1.vespa.oath.cloud/", "scope": "zone", - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "legacy": false }, { "cluster": "foo", "tls": true, - "url": "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/", + "url": "https://instance1.application1.tenant1.global.vespa.oath.cloud/", "scope": "global", - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "legacy": false }, { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json index 45df6aad67c..175c45eb2cd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json @@ -11,7 +11,7 @@ { "at": 0, "type": "info", - "message": " |-- https://application--tenant.us-east-1.dev.vespa.oath.cloud:4443/ (cluster 'default')" + "message": " |-- https://application.tenant.us-east-1.dev.vespa.oath.cloud/ (cluster 'default')" }, { "at": 0, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json index f2f8e14f093..2ca520c0122 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json @@ -8,9 +8,9 @@ { "cluster": "default", "tls": true, - "url": "https://instance1--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/", + "url": "https://instance1.application1.tenant1.us-east-1.dev.vespa.oath.cloud/", "scope": "zone", - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "legacy": false } ], diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json index 62ad3a2db7e..d9ec8e4dfef 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json @@ -11,17 +11,17 @@ { "cluster": "default", "tls": true, - "url": "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/", + "url": "https://instance1.application1.tenant1.us-central-1.vespa.oath.cloud/", "scope": "zone", - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "legacy": false }, { "cluster": "foo", "tls": true, - "url": "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/", + "url": "https://instance1.application1.tenant1.global.vespa.oath.cloud/", "scope": "global", - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "legacy": false }, { @@ -33,7 +33,7 @@ "legacy": false } ], - "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/clusters", + "clusters": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/clusters", "nodes": "http://localhost:8080/zone/v2/prod/us-central-1/nodes/v2/node/?recursive=true&application=tenant1.application1.instance1", "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-central-1&application=tenant1.application1.instance1", "version": "(ignore)", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json index 0525f059dd0..6b1d48f4a08 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json @@ -117,16 +117,26 @@ { "at": 14503000, "type": "info", - "message": "host-tenant:application:default-staging.us-east-3: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 14503000, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 14503000, + "type": "debug", + "message": "host-tenant:application:default-staging.us-east-3: unorchestrated" + }, + { + "at": 14503000, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 14503000, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { @@ -150,7 +160,7 @@ } ] }, - "lastId": 27, + "lastId": 29, "steps": { "deployTester": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json index 7ee3952a8b5..377b8c6ed69 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json @@ -137,91 +137,151 @@ { "at": "(ignore)", "type": "info", - "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": "(ignore)", "type": "info", + "message": "1 application services upgrading" + }, + { + "at": "(ignore)", + "type": "debug", + "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + }, + { + "at": "(ignore)", + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": "(ignore)", - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": "(ignore)", "type": "info", - "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": "(ignore)", "type": "info", + "message": "1 application services upgrading" + }, + { + "at": "(ignore)", + "type": "debug", + "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + }, + { + "at": "(ignore)", + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": "(ignore)", - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": "(ignore)", "type": "info", - "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": "(ignore)", "type": "info", + "message": "1 application services upgrading" + }, + { + "at": "(ignore)", + "type": "debug", + "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + }, + { + "at": "(ignore)", + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": "(ignore)", - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": "(ignore)", "type": "info", - "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": "(ignore)", "type": "info", + "message": "1 application services upgrading" + }, + { + "at": "(ignore)", + "type": "debug", + "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + }, + { + "at": "(ignore)", + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": "(ignore)", - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": "(ignore)", "type": "info", - "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": "(ignore)", "type": "info", + "message": "1 application services upgrading" + }, + { + "at": "(ignore)", + "type": "debug", + "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + }, + { + "at": "(ignore)", + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": "(ignore)", - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": "(ignore)", "type": "info", - "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": "(ignore)", "type": "info", + "message": "1 application services upgrading" + }, + { + "at": "(ignore)", + "type": "debug", + "message": "host-tenant1:application1:instance1-test.us-east-1: unorchestrated" + }, + { + "at": "(ignore)", + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": "(ignore)", - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { @@ -237,7 +297,7 @@ { "at": "(ignore)", "type": "info", - "message": " |-- https://instance1--application1--tenant1.us-east-1.test.vespa.oath.cloud:4443/ (cluster 'default')" + "message": " |-- https://instance1.application1.tenant1.us-east-1.test.vespa.oath.cloud/ (cluster 'default')" }, { "at": "(ignore)", @@ -264,7 +324,7 @@ { "at": "(ignore)", "type": "info", - "message": " |-- https://instance1--application1--tenant1.us-east-1.test.vespa.oath.cloud:4443/ (cluster 'default')" + "message": " |-- https://instance1.application1.tenant1.us-east-1.test.vespa.oath.cloud/ (cluster 'default')" }, { "at": "(ignore)", @@ -294,7 +354,7 @@ } ] }, - "lastId": 54, + "lastId": 66, "steps": { "deployTester": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json index 7f59eaf75c2..66173ec4976 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json @@ -132,91 +132,151 @@ { "at": 0, "type": "info", - "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 0, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 0, + "type": "debug", + "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + }, + { + "at": 0, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 0, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": 0, "type": "info", - "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 0, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 0, + "type": "debug", + "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + }, + { + "at": 0, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 0, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": 0, "type": "info", - "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 0, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 0, + "type": "debug", + "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + }, + { + "at": 0, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 0, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": 0, "type": "info", - "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 0, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 0, + "type": "debug", + "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + }, + { + "at": 0, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 0, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": 0, "type": "info", - "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 0, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 0, + "type": "debug", + "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + }, + { + "at": 0, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 0, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { "at": 0, "type": "info", - "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + "message": "Waiting for convergence of 1 services across 1 nodes" }, { "at": 0, "type": "info", + "message": "1 application services upgrading" + }, + { + "at": 0, + "type": "debug", + "message": "host-tenant:application:default-test.us-east-1: unorchestrated" + }, + { + "at": 0, + "type": "debug", "message": "--- platform vespa/vespa:6.1" }, { "at": 0, - "type": "info", + "type": "debug", "message": "--- container on port 43 has config generation 1, wanted is 2" }, { @@ -232,7 +292,7 @@ { "at": 0, "type": "info", - "message": " |-- https://application--tenant.us-east-1.test.vespa.oath.cloud:4443/ (cluster 'default')" + "message": " |-- https://application.tenant.us-east-1.test.vespa.oath.cloud/ (cluster 'default')" }, { "at": 0, @@ -259,7 +319,7 @@ { "at": 0, "type": "info", - "message": " |-- https://application--tenant.us-east-1.test.vespa.oath.cloud:4443/ (cluster 'default')" + "message": " |-- https://application.tenant.us-east-1.test.vespa.oath.cloud/ (cluster 'default')" }, { "at": 0, @@ -289,7 +349,7 @@ } ] }, - "lastId": 54, + "lastId": 66, "steps": { "deployTester": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json index 0632ab7a67b..3a5e6dc5dc3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config-dev.json @@ -5,18 +5,18 @@ "isCI": false, "endpoints": { "dev.us-east-1": [ - "https://my-user--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/" + "https://my-user.application1.tenant1.us-east-1.dev.vespa.oath.cloud/" ], "prod.us-central-1": [ - "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/" + "https://application1.tenant1.us-central-1.vespa.oath.cloud/" ] }, "zoneEndpoints": { "dev.us-east-1": { - "default": "https://my-user--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/" + "default": "https://my-user.application1.tenant1.us-east-1.dev.vespa.oath.cloud/" }, "prod.us-central-1": { - "default": "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/" + "default": "https://application1.tenant1.us-central-1.vespa.oath.cloud/" } }, "clusters": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json index c81ed767239..0a9236655ba 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/test-config.json @@ -5,12 +5,12 @@ "isCI": false, "endpoints": { "prod.us-central-1": [ - "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/" + "https://application1.tenant1.us-central-1.vespa.oath.cloud/" ] }, "zoneEndpoints": { "prod.us-central-1": { - "default": "https://application1--tenant1.us-central-1.vespa.oath.cloud:4443/" + "default": "https://application1.tenant1.us-central-1.vespa.oath.cloud/" } }, "clusters": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json index 2edf1867fd3..0dad88e645b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json @@ -13,6 +13,9 @@ "name": "ArchiveUriUpdater" }, { + "name": "BillingDatabaseMaintainer" + }, + { "name": "ChangeRequestMaintainer" }, { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java index 9e17b44c9a6..eab3a37a9c3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.restapi.filter; import com.fasterxml.jackson.databind.ObjectMapper; import com.yahoo.application.container.handler.Request; import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; import com.yahoo.jdisc.http.HttpRequest.Method; import com.yahoo.jdisc.http.filter.DiscFilterRequest; import com.yahoo.vespa.hosted.controller.ControllerTester; @@ -75,6 +76,18 @@ public class ControllerAuthorizationFilterTest { assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", securityContext))); } + @Test + public void hostedDeveloper() { + ControllerTester tester = new ControllerTester(); + TenantName tenantName = TenantName.defaultName(); + SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.hostedDeveloper(tenantName))); + + ControllerAuthorizationFilter filter = createFilter(tester); + assertIsAllowed(invokeFilter(filter, createRequest(Method.POST, "/application/v4/tenant/" + tenantName.value() + "/application/app/instance/default/environment/dev/region/region/deploy", securityContext))); + assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/application/v4/tenant/" + tenantName.value() + "/application/app/instance/default/environment/prod/region/region/deploy", securityContext))); + assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/application/v4/tenant/" + tenantName.value() + "/application/app/submit", securityContext))); + } + private static void assertIsAllowed(Optional<AuthorizationResponse> response) { assertFalse("Expected no response from filter, but got \"" + response.map(r -> r.message + "\" (" + r.statusCode + ")").orElse(""), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java index 5b2fabcaff8..0b128ebf7a5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java @@ -258,7 +258,7 @@ public class RoutingApiTest extends ControllerContainerTest { // One shared and one exclusive zone deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(westZone), - RoutingMethod.shared); + RoutingMethod.sharedLayer4); deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(eastZone), RoutingMethod.exclusive); @@ -273,7 +273,7 @@ public class RoutingApiTest extends ControllerContainerTest { .build(); context.submit(applicationPackage).deploy(); - // GET status for zone using shared routing + // GET status for zone using sharedLayer4 routing tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", "", Request.Method.GET), new File("rotation/deployment-status-initial.json")); @@ -331,4 +331,5 @@ public class RoutingApiTest extends ControllerContainerTest { tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1/instance/default/endpoint", "", Request.Method.GET), new File("endpoint/endpoints.json")); } + } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/endpoint/endpoints.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/endpoint/endpoints.json index f78f913cb7e..75369a19ea7 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/endpoint/endpoints.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/endpoint/endpoints.json @@ -2,13 +2,13 @@ "endpoints": [ { "name": "default", - "dnsName": "a1--t1.global.vespa.oath.cloud", - "routingMethod": "shared", + "dnsName": "a1.t1.global.vespa.oath.cloud", + "routingMethod": "sharedLayer4", "cluster": "default", "scope": "global", "zones": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a1:default", "environment": "prod", "region": "us-east-3", @@ -17,7 +17,7 @@ "changedAt": 1497618757000 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a1:default", "environment": "prod", "region": "us-west-1", @@ -28,4 +28,4 @@ ] } ] -}
\ No newline at end of file +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/application.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/application.json index e0b0e5e9b7a..06fb2b92c53 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/application.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/application.json @@ -1,7 +1,7 @@ { "deployments": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a1:default", "environment": "prod", "region": "us-east-3", @@ -10,7 +10,7 @@ "changedAt": "(ignore)" }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a1:default", "environment": "prod", "region": "us-west-1", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/environment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/environment.json index f0dd0b7310d..1711bb1f856 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/environment.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/environment.json @@ -1,7 +1,7 @@ { "zones": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "test", "region": "us-east-1", "status": "in", @@ -9,7 +9,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "staging", "region": "us-east-3", "status": "in", @@ -17,7 +17,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "dev", "region": "us-east-1", "status": "in", @@ -25,7 +25,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "dev", "region": "aws-us-east-2a", "status": "in", @@ -33,7 +33,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "perf", "region": "us-east-3", "status": "in", @@ -41,7 +41,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "aws-us-east-1a", "status": "in", @@ -49,7 +49,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "ap-northeast-1", "status": "in", @@ -57,7 +57,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "ap-northeast-2", "status": "in", @@ -65,7 +65,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "ap-southeast-1", "status": "in", @@ -73,7 +73,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "us-east-3", "status": "in", @@ -81,7 +81,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "us-west-1", "status": "in", @@ -89,7 +89,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "us-central-1", "status": "in", @@ -97,7 +97,7 @@ "changedAt": 0 }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "eu-west-1", "status": "in", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/tenant.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/tenant.json index 1ee4e1b82ba..5de12d9b1ec 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/tenant.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/recursion/tenant.json @@ -1,7 +1,7 @@ { "deployments": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a1:default", "environment": "prod", "region": "us-east-3", @@ -10,7 +10,7 @@ "changedAt": "(ignore)" }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a1:default", "environment": "prod", "region": "us-west-1", @@ -19,7 +19,7 @@ "changedAt": "(ignore)" }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a2:default", "environment": "prod", "region": "us-east-3", @@ -28,7 +28,7 @@ "changedAt": "(ignore)" }, { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "t1:a2:default", "environment": "prod", "region": "us-west-1", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-in.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-in.json index 5b15b72752c..4eb51c1e907 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-in.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-in.json @@ -1,7 +1,7 @@ { "deployments": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "tenant:application:default", "environment": "prod", "region": "us-west-1", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-initial.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-initial.json index 90b2317c1b3..615ce4b4a6e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-initial.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-initial.json @@ -1,7 +1,7 @@ { "deployments": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "tenant:application:default", "environment": "prod", "region": "us-west-1", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-out.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-out.json index 85e345c01d0..816bc810048 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-out.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/deployment-status-out.json @@ -1,7 +1,7 @@ { "deployments": [ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "instance": "tenant:application:default", "environment": "prod", "region": "us-west-1", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-in.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-in.json index eb06e9ee11d..8460cc5ec8a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-in.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-in.json @@ -1,5 +1,5 @@ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "us-west-1", "status": "in", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-initial.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-initial.json index eb06e9ee11d..8460cc5ec8a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-initial.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-initial.json @@ -1,5 +1,5 @@ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "us-west-1", "status": "in", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-out.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-out.json index 440b80bc4d0..88fddcbd955 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-out.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/responses/rotation/zone-status-out.json @@ -1,5 +1,5 @@ { - "routingMethod": "shared", + "routingMethod": "sharedLayer4", "environment": "prod", "region": "us-west-1", "status": "out", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java index 537f6c48bdf..a93e9f55e30 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java @@ -67,9 +67,16 @@ public class UserApiTest extends ControllerContainerCloudTest { tester.assertResponse(request("/application/v4/tenant/my-tenant", POST) .roles(operator) .principal("administrator@tenant") + .user(new User("administrator@tenant", "administrator", "admin", "picture")) .data("{\"token\":\"hello\"}"), new File("tenant-without-applications.json")); + // GET at tenant/info with contact information. + tester.assertResponse(request("/application/v4/tenant/my-tenant/info") + .roles(operator) + .principal("administrator@tenant"), + new File("tenant-info-after-created.json")); + // GET at user/v1 root fails as no access control is defined there. tester.assertResponse(request("/user/v1/"), accessDenied, 403); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-info-after-created.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-info-after-created.json new file mode 100644 index 00000000000..942b5c1db45 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-info-after-created.json @@ -0,0 +1,8 @@ +{ + "name": "", + "email": "", + "website":"", + "invoiceEmail":"", + "contactName": "administrator", + "contactEmail": "administrator@tenant" +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json index 7cc1a51a114..54585767d51 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json @@ -1,6 +1,7 @@ { "tenant": "my-tenant", "type": "CLOUD", + "creator": "administrator@tenant", "pemDeveloperKeys": [ { "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9\nz/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw==\n-----END PUBLIC KEY-----\n", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json index 1662484ade8..1cd2fb41263 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json @@ -1,6 +1,7 @@ { "tenant": "my-tenant", "type": "CLOUD", + "creator": "administrator@tenant", "pemDeveloperKeys": [ { "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFELzPyinTfQ/sZnTmRp5E4Ve/sbE\npDhJeqczkyFcT2PysJ5sZwm7rKPEeXDOhzTPCyRvbUqc2SGdWbKUGGa/Yw==\n-----END PUBLIC KEY-----\n", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java index 9a56123e8e3..d7847da2404 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java @@ -60,8 +60,8 @@ public class RotationRepositoryTest { Rotation expected = new Rotation(new RotationId("foo-1"), "foo-1.com"); assertEquals(List.of(expected.id()), rotationIds(application.instance().rotations())); - assertEquals(URI.create("https://app1--tenant1.global.vespa.oath.cloud:4443/"), - tester.controller().routing().readDeclaredEndpointsOf(application.instanceId()).primary().get().url()); + assertEquals(URI.create("https://app1.tenant1.global.vespa.oath.cloud/"), + tester.controller().routing().readDeclaredEndpointsOf(application.instanceId()).direct().first().get().url()); try (RotationLock lock = repository.lock()) { List<AssignedRotation> rotations = repository.getOrAssignRotations(application.application().deploymentSpec(), application.instance(), @@ -151,13 +151,13 @@ public class RotationRepositoryTest { ZoneApiMock.fromId("prod.cd-us-west-1")); tester.controllerTester().zoneRegistry() .setZones(zones) - .setRoutingMethod(zones, RoutingMethod.shared) + .setRoutingMethod(zones, RoutingMethod.sharedLayer4) .setSystemName(SystemName.cd); tester.configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), SystemApplication.notController()); var application2 = tester.newDeploymentContext("tenant2", "app2", "default"); application2.submit(applicationPackage).deploy(); assertEquals(List.of(new RotationId("foo-1")), rotationIds(application2.instance().rotations())); - assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/", + assertEquals("https://cd.app2.tenant2.global.vespa.oath.cloud/", tester.controller().routing().readDeclaredEndpointsOf(application2.instanceId()).primary().get().url().toString()); } @@ -175,10 +175,10 @@ public class RotationRepositoryTest { var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2"); assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations())); assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations())); - assertEquals(URI.create("https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"), - tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).primary().get().url()); - assertEquals(URI.create("https://instance2--application1--tenant1.global.vespa.oath.cloud:4443/"), - tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).primary().get().url()); + assertEquals(URI.create("https://instance1.application1.tenant1.global.vespa.oath.cloud/"), + tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).direct().first().get().url()); + assertEquals(URI.create("https://instance2.application1.tenant1.global.vespa.oath.cloud/"), + tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).direct().first().get().url()); } @Test @@ -197,10 +197,10 @@ public class RotationRepositoryTest { assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations())); assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations())); - assertEquals(URI.create("https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"), - tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).primary().get().url()); - assertEquals(URI.create("https://instance2--application1--tenant1.global.vespa.oath.cloud:4443/"), - tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).primary().get().url()); + assertEquals(URI.create("https://instance1.application1.tenant1.global.vespa.oath.cloud/"), + tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).direct().first().get().url()); + assertEquals(URI.create("https://instance2.application1.tenant1.global.vespa.oath.cloud/"), + tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).direct().first().get().url()); } private void assertSingleRotation(Rotation expected, List<AssignedRotation> assignedRotations, RotationRepository repository) { |