diff options
author | Martin Polden <mpolden@mpolden.no> | 2018-01-18 10:42:25 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2018-01-25 11:06:04 +0100 |
commit | b87cbc288ed5eb12d9a9c368c2f501523e4344ab (patch) | |
tree | a03b5b661de6152af3d497f3f629d4d01abd9143 /controller-server/src/test/java | |
parent | e0d9ae612ee405eb3e83ca2997b3cc8f155b80df (diff) |
Introduce application version number
Diffstat (limited to 'controller-server/src/test/java')
12 files changed, 282 insertions, 62 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ArtifactRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ArtifactRepositoryMock.java new file mode 100644 index 00000000000..efbc10e8deb --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ArtifactRepositoryMock.java @@ -0,0 +1,39 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository; +import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author mpolden + */ +public class ArtifactRepositoryMock implements ArtifactRepository { + + private final Map<Integer, byte[]> repository = new HashMap<>(); + + public ArtifactRepositoryMock put(ApplicationId applicationId, ApplicationPackage applicationPackage, + String applicationVersion) { + repository.put(artifactHash(applicationId, applicationVersion), applicationPackage.zippedContent()); + return this; + } + + @Override + public byte[] getApplicationPackage(ApplicationId applicationId, String applicationVersion) { + int artifactHash = artifactHash(applicationId, applicationVersion); + if (!repository.containsKey(artifactHash)) { + throw new IllegalArgumentException("No application package found for " + applicationId + " with version " + + applicationVersion); + } + return repository.get(artifactHash); + } + + private static int artifactHash(ApplicationId applicationId, String applicationVersion) { + return Objects.hash(applicationId, applicationVersion); + } + +} 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 ceed52d2dad..1127e739689 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 @@ -10,11 +10,11 @@ import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.api.Tenant; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus; -import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; @@ -26,12 +26,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; -import com.yahoo.vespa.hosted.controller.application.ApplicationRevision; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType; import com.yahoo.vespa.hosted.controller.application.JobStatus; +import com.yahoo.vespa.hosted.controller.application.SourceRevision; import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.BuildSystem; @@ -100,21 +101,21 @@ public class ControllerTest { Version version1 = Version.fromString("6.1"); // Set in config server mock Application app1 = tester.createApplication("app1", "tenant1", 1, 11L); tester.notifyJobCompletion(component, app1, true); - assertFalse("Revision is currently not known", - ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).revision().isPresent()); + assertFalse("Application version is currently not known", + ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).version().isPresent()); tester.deployAndNotify(app1, applicationPackage, true, systemTest); tester.deployAndNotify(app1, applicationPackage, true, stagingTest); assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); - Optional<ApplicationRevision> revision = ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).revision(); - assertTrue("Revision has been set during deployment", revision.isPresent()); + Optional<ApplicationVersion> applicationVersion = ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).version(); + assertTrue("Application version has been set during deployment", applicationVersion.isPresent()); assertStatus(JobStatus.initial(stagingTest) - .withTriggering(version1, revision, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()), app1.id(), tester.controller()); // Causes first deployment job to be triggered assertStatus(JobStatus.initial(productionCorpUsEast1) - .withTriggering(version1, revision, false, "", tester.clock().instant()), app1.id(), tester.controller()); + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant()), app1.id(), tester.controller()); tester.clock().advance(Duration.ofSeconds(1)); // production job (failing) @@ -122,9 +123,9 @@ public class ControllerTest { assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); JobStatus expectedJobStatus = JobStatus.initial(productionCorpUsEast1) - .withTriggering(version1, revision, false, "", tester.clock().instant()) // Triggered first without revision info + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant()) // Triggered first without application version info .withCompletion(42, Optional.of(JobError.unknown), tester.clock().instant(), tester.controller()) - .withTriggering(version1, revision, false, "", tester.clock().instant()); // Re-triggering (due to failure) has revision info + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant()); // Re-triggering (due to failure) has application version info assertStatus(expectedJobStatus, app1.id(), tester.controller()); @@ -147,20 +148,20 @@ public class ControllerTest { tester.notifyJobCompletion(component, app1, true); tester.deployAndNotify(app1, applicationPackage, true, false, systemTest); assertStatus(JobStatus.initial(systemTest) - .withTriggering(version1, revision, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()), app1.id(), tester.controller()); tester.deployAndNotify(app1, applicationPackage, true, stagingTest); // production job succeeding now tester.deployAndNotify(app1, applicationPackage, true, productionCorpUsEast1); expectedJobStatus = expectedJobStatus - .withTriggering(version1, revision, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()); assertStatus(expectedJobStatus, app1.id(), tester.controller()); // causes triggering of next production job assertStatus(JobStatus.initial(productionUsEast3) - .withTriggering(version1, revision, false, "", tester.clock().instant()), + .withTriggering(version1, applicationVersion, false, "", tester.clock().instant()), app1.id(), tester.controller()); tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3); @@ -198,6 +199,132 @@ public class ControllerTest { applications.require(app1.id()).deployments().get(productionCorpUsEast1.zone(SystemName.main).get())); assertNull("Deployment job was removed", applications.require(app1.id()).deploymentJobs().jobStatus().get(productionCorpUsEast1)); } + + // TODO: Replace above test with this one after introducing new application version number + @Test + public void testDeploymentWithApplicationVersion() { + // Setup system + DeploymentTester tester = new DeploymentTester(); + ApplicationController applications = tester.controller().applications(); + Version version1 = Version.fromString("6.1"); // Set in config server mock + Application app1 = tester.createApplication("app1", "tenant1", 1, 11L); + + // Component runs, uploads artifact and notifies completion + ApplicationPackage applicationPackage = new ApplicationPackageBuilder() + .environment(Environment.prod) + .region("corp-us-east-1") + .region("us-east-3") + .build(); + SourceRevision source = new SourceRevision("repo", "branch", "deadbeef"); + String expectedVersionString = "1.0.37-deadbeef"; + tester.artifactRepository().put(app1.id(), applicationPackage, expectedVersionString); + tester.notifyJobCompletion(component, app1, Optional.empty(), Optional.of(source), 37); + ApplicationVersion expectedVersion = ApplicationVersion.from(source, 37); + assertEquals(expectedVersionString, ((Change.ApplicationChange) tester.controller().applications() + .require(app1.id()) + .deploying() + .get()).version().get().id()); + + // Deploy without application package + tester.deployAndNotify(app1, true, systemTest); + tester.deployAndNotify(app1, true, stagingTest); + assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); + assertStatus(JobStatus.initial(stagingTest) + .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant().minus(Duration.ofMillis(1))) + .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()), app1.id(), tester.controller()); + + // Causes first deployment job to be triggered + assertStatus(JobStatus.initial(productionCorpUsEast1) + .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant()), app1.id(), tester.controller()); + tester.clock().advance(Duration.ofSeconds(1)); + + // production job (failing) + tester.deployAndNotify(app1, false, productionCorpUsEast1); + assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); + + JobStatus expectedJobStatus = JobStatus.initial(productionCorpUsEast1) + .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant()) + .withCompletion(42, Optional.of(JobError.unknown), tester.clock().instant(), tester.controller()); + + assertStatus(expectedJobStatus, app1.id(), tester.controller()); + + // Simulate restart + tester.restartController(); + applications = tester.controller().applications(); + + assertNotNull(tester.controller().tenants().tenant(new TenantId("tenant1"))); + assertNotNull(applications.get(ApplicationId.from(TenantName.from("tenant1"), + ApplicationName.from("application1"), + InstanceName.from("default")))); + assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); + + + tester.clock().advance(Duration.ofHours(1)); + + tester.notifyJobCompletion(productionCorpUsEast1, app1, false); // Need to complete the job, or new jobs won't start. + + // Component is triggered again + tester.artifactRepository().put(app1.id(), applicationPackage, "1.0.38-deadbeef"); + tester.notifyJobCompletion(component, app1, Optional.empty(), Optional.of(source), 38); + tester.deployAndNotify(app1, Optional.empty(), true, false, systemTest); + expectedVersion = ApplicationVersion.from(source, 38); + assertStatus(JobStatus.initial(systemTest) + .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant().minus(Duration.ofMillis(1))) + .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()), app1.id(), tester.controller()); + tester.deployAndNotify(app1, Optional.empty(), true, true, stagingTest); + + // production job succeeding now + tester.deployAndNotify(app1, Optional.empty(), true, true, productionCorpUsEast1); + expectedJobStatus = expectedJobStatus + .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant().minus(Duration.ofMillis(1))) + .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()); + assertStatus(expectedJobStatus, app1.id(), tester.controller()); + + // causes triggering of next production job + assertStatus(JobStatus.initial(productionUsEast3) + .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant()), + app1.id(), tester.controller()); + tester.deployAndNotify(app1, Optional.empty(), true, true, productionUsEast3); + + assertEquals(5, applications.get(app1.id()).get().deploymentJobs().jobStatus().size()); + + // prod zone removal is not allowed + applicationPackage = new ApplicationPackageBuilder() + .environment(Environment.prod) + .region("us-east-3") + .build(); + tester.artifactRepository().put(app1.id(), applicationPackage, "1.0.56-cafed00d"); + source = new SourceRevision("repo", "branch", "cafed00d"); + tester.notifyJobCompletion(component, app1, Optional.empty(), Optional.of(source), 56); + try { + tester.deploy(systemTest, app1, Optional.empty(), false); + fail("Expected exception due to unallowed production deployment removal"); + } + catch (IllegalArgumentException e) { + assertEquals("deployment-removal: application 'tenant1.app1' is deployed in corp-us-east-1, but does not include this zone in deployment.xml", e.getMessage()); + } + assertNotNull("Zone was not removed", + applications.require(app1.id()).deployments().get(productionCorpUsEast1.zone(SystemName.main).get())); + JobStatus jobStatus = applications.require(app1.id()).deploymentJobs().jobStatus().get(productionCorpUsEast1); + assertNotNull("Deployment job was not removed", jobStatus); + assertEquals(42, jobStatus.lastCompleted().get().id()); + assertEquals("staging-test completed", jobStatus.lastCompleted().get().reason()); + + // prod zone removal is allowed with override + applicationPackage = new ApplicationPackageBuilder() + .allow(ValidationId.deploymentRemoval) + .upgradePolicy("default") + .environment(Environment.prod) + .region("us-east-3") + .build(); + tester.artifactRepository().put(app1.id(), applicationPackage, "1.0.103-c00ffefe"); + source = new SourceRevision("repo", "branch", "c00ffefe"); + tester.notifyJobCompletion(component, app1, Optional.empty(), Optional.of(source), 103); + tester.deployAndNotify(app1, Optional.empty(), true, true, systemTest); + assertNull("Zone was removed", + applications.require(app1.id()).deployments().get(productionCorpUsEast1.zone(SystemName.main).get())); + assertNull("Deployment job was removed", applications.require(app1.id()).deploymentJobs().jobStatus().get(productionCorpUsEast1)); + } @Test public void testDeployVersion() { @@ -557,6 +684,7 @@ public class ControllerTest { public void testCleanupOfStaleDeploymentData() throws IOException { DeploymentTester tester = new DeploymentTester(); tester.controllerTester().zoneRegistry().setSystem(SystemName.cd); + tester.controllerTester().zoneRegistry().setZones(ZoneId.from("prod", "cd-us-central-1")); Supplier<Map<JobType, JobStatus>> statuses = () -> tester.application(ApplicationId.from("vespa", "canary", "default")).deploymentJobs().jobStatus(); @@ -764,6 +892,7 @@ public class ControllerTest { public void testDeployWithoutProjectId() { DeploymentTester tester = new DeploymentTester(); tester.controllerTester().zoneRegistry().setSystem(SystemName.cd); + tester.controllerTester().zoneRegistry().setZones(ZoneId.from("prod", "cd-us-central-1")); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .environment(Environment.prod) .region("cd-us-central-1") @@ -777,7 +906,7 @@ public class ControllerTest { // Same options as used in our integration tests DeployOptions options = new DeployOptions(Optional.empty(), Optional.empty(), false, false); - tester.controller().applications().deployApplication(app.id(), zone, applicationPackage, options); + tester.controller().applications().deployApplication(app.id(), zone, Optional.of(applicationPackage), options); assertTrue("Application deployed and activated", tester.controllerTester().configServer().activated().getOrDefault(app.id(), false)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index b1486c8ec00..3b574ac606b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository; import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.slime.Slime; import com.yahoo.test.ManualClock; @@ -64,30 +65,32 @@ public final class ControllerTester { private final CuratorDb curator; private final MemoryNameService nameService; private final RotationsConfig rotationsConfig; + private final ArtifactRepositoryMock artifactRepository; private Controller controller; public ControllerTester() { this(new MemoryControllerDb(), new AthenzDbMock(), new ManualClock(), new ConfigServerClientMock(), new ZoneRegistryMock(), new GitHubMock(), new MockCuratorDb(), defaultRotationsConfig(), - new MemoryNameService()); + new MemoryNameService(), new ArtifactRepositoryMock()); } public ControllerTester(ManualClock clock) { this(new MemoryControllerDb(), new AthenzDbMock(), clock, new ConfigServerClientMock(), new ZoneRegistryMock(), new GitHubMock(), new MockCuratorDb(), defaultRotationsConfig(), - new MemoryNameService()); + new MemoryNameService(), new ArtifactRepositoryMock()); } public ControllerTester(RotationsConfig rotationsConfig) { this(new MemoryControllerDb(), new AthenzDbMock(), new ManualClock(), new ConfigServerClientMock(), - new ZoneRegistryMock(), new GitHubMock(), new MockCuratorDb(), rotationsConfig, new MemoryNameService()); + new ZoneRegistryMock(), new GitHubMock(), new MockCuratorDb(), rotationsConfig, new MemoryNameService(), + new ArtifactRepositoryMock()); } private ControllerTester(ControllerDb db, AthenzDbMock athenzDb, ManualClock clock, ConfigServerClientMock configServer, ZoneRegistryMock zoneRegistry, GitHubMock gitHub, CuratorDb curator, RotationsConfig rotationsConfig, - MemoryNameService nameService) { + MemoryNameService nameService, ArtifactRepositoryMock artifactRepository) { this.db = db; this.athenzDb = athenzDb; this.clock = clock; @@ -97,8 +100,9 @@ public final class ControllerTester { this.curator = curator; this.nameService = nameService; this.rotationsConfig = rotationsConfig; + this.artifactRepository = artifactRepository; this.controller = createController(db, curator, rotationsConfig, configServer, clock, gitHub, zoneRegistry, - athenzDb, nameService); + athenzDb, nameService, artifactRepository); } public Controller controller() { return controller; } @@ -117,10 +121,12 @@ public final class ControllerTester { public GitHubMock gitHub() { return gitHub; } + public ArtifactRepositoryMock artifactRepository() { return artifactRepository; } + /** Create a new controller instance. Useful to verify that controller state is rebuilt from persistence */ public final void createNewController() { controller = createController(db, curator, rotationsConfig, configServer, clock, gitHub, zoneRegistry, athenzDb, - nameService); + nameService, artifactRepository); } /** Creates the given tenant and application and deploys it */ @@ -209,6 +215,10 @@ public final class ControllerTester { } public void deploy(Application application, ZoneId zone, ApplicationPackage applicationPackage, boolean deployCurrentVersion) { + deploy(application, zone, Optional.of(applicationPackage), deployCurrentVersion); + } + + public void deploy(Application application, ZoneId zone, Optional<ApplicationPackage> applicationPackage, boolean deployCurrentVersion) { ScrewdriverId app1ScrewdriverId = new ScrewdriverId(String.valueOf(application.deploymentJobs().projectId().get())); GitRevision app1RevisionId = new GitRevision(new GitRepository("repo"), new GitBranch("master"), new GitCommit("commit1")); controller().applications().deployApplication(application.id(), @@ -231,7 +241,8 @@ public final class ControllerTester { private static Controller createController(ControllerDb db, CuratorDb curator, RotationsConfig rotationsConfig, ConfigServerClientMock configServerClientMock, ManualClock clock, GitHubMock gitHubClientMock, ZoneRegistryMock zoneRegistryMock, - AthenzDbMock athensDb, MemoryNameService nameService) { + AthenzDbMock athensDb, MemoryNameService nameService, + ArtifactRepository artifactRepository) { Controller controller = new Controller(db, curator, rotationsConfig, @@ -247,7 +258,8 @@ public final class ControllerTester { new MockRoutingGenerator(), new ChefMock(), clock, - new AthenzClientFactoryMock(athensDb)); + new AthenzClientFactoryMock(athensDb), + artifactRepository); controller.updateVersionStatus(VersionStatus.compute(controller)); return controller; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java index c205357c7ef..63751cfaa98 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ZoneRegistryMock.java @@ -16,6 +16,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneFilterMock; import java.net.URI; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -34,9 +35,11 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry @Inject public ZoneRegistryMock() { - this.zones.add(ZoneId.from("prod", "corp-us-east-1")); - this.zones.add(ZoneId.from("prod", "us-east-3")); - this.zones.add(ZoneId.from("prod", "us-west-1")); + zones.add(ZoneId.from("prod", "corp-us-east-1")); + zones.add(ZoneId.from("prod", "us-east-3")); + zones.add(ZoneId.from("prod", "us-west-1")); + zones.add(ZoneId.from("prod", "us-central-1")); + zones.add(ZoneId.from("prod", "eu-west-1")); } public ZoneRegistryMock setDeploymentTimeToLive(ZoneId zone, Duration duration) { @@ -54,6 +57,10 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry return this; } + public ZoneRegistryMock setZones(ZoneId... zone) { + return setZones(Arrays.asList(zone)); + } + public ZoneRegistryMock setSystem(SystemName system) { this.system = system; return this; 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 3311cffa078..9d5fcb31288 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 @@ -71,8 +71,8 @@ public class ApplicationPackageBuilder { return this; } - public ApplicationPackageBuilder blockChange(boolean revision, boolean version, - String daySpec, String hourSpec, String zoneSpec) { + public ApplicationPackageBuilder blockChange(boolean revision, boolean version, String daySpec, String hourSpec, + String zoneSpec) { blockChange.append(" <block-change"); blockChange.append(" revision='").append(revision).append("'"); blockChange.append(" version='").append(version).append("'"); @@ -93,7 +93,8 @@ public class ApplicationPackageBuilder { } public ApplicationPackageBuilder athenzIdentity(AthenzDomain domain, AthenzService service) { - this.athenzIdentityAttributes = String.format("athenz-domain='%s' athenz-service='%s'", domain.value(), service.value()); + this.athenzIdentityAttributes = String.format("athenz-domain='%s' athenz-service='%s'", domain.value(), + service.value()); return this; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java index c9f0c6cba1d..8de32b4b531 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.Environment; import com.yahoo.test.ManualClock; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ApplicationController; +import com.yahoo.vespa.hosted.controller.ArtifactRepositoryMock; import com.yahoo.vespa.hosted.controller.ConfigServerClientMock; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.ControllerTester; @@ -16,6 +17,7 @@ import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType; +import com.yahoo.vespa.hosted.controller.application.SourceRevision; import com.yahoo.vespa.hosted.controller.maintenance.ReadyJobsTrigger; import com.yahoo.vespa.hosted.controller.maintenance.JobControl; import com.yahoo.vespa.hosted.controller.maintenance.Upgrader; @@ -43,6 +45,7 @@ public class DeploymentTester { // Set a long interval so that maintainers never do scheduled runs during tests private static final Duration maintenanceInterval = Duration.ofDays(1); + private static final int defaultBuildNumber = 42; private final ControllerTester tester; private final Upgrader upgrader; @@ -81,6 +84,8 @@ public class DeploymentTester { public ConfigServerClientMock configServer() { return tester.configServer(); } + public ArtifactRepositoryMock artifactRepository() { return tester.artifactRepository(); } + public Application application(String name) { return application(ApplicationId.from("tenant1", name, "default")); } @@ -154,16 +159,18 @@ public class DeploymentTester { } public static DeploymentJobs.JobReport jobReport(Application application, JobType jobType, boolean success) { - return jobReport(application, jobType, Optional.ofNullable(success ? null : unknown)); + return jobReport(application, jobType, Optional.ofNullable(success ? null : unknown), Optional.empty(), defaultBuildNumber); } - public static DeploymentJobs.JobReport jobReport(Application application, JobType jobType, Optional<DeploymentJobs.JobError> jobError) { + public static DeploymentJobs.JobReport jobReport(Application application, JobType jobType, + Optional<DeploymentJobs.JobError> jobError, + Optional<SourceRevision> sourceRevision, long buildNumber) { return new DeploymentJobs.JobReport( application.id(), jobType, application.deploymentJobs().projectId().get(), - 42, - Optional.empty(), + buildNumber, + sourceRevision, jobError ); } @@ -204,8 +211,13 @@ public class DeploymentTester { } public void notifyJobCompletion(JobType jobType, Application application, Optional<DeploymentJobs.JobError> jobError) { + notifyJobCompletion(jobType, application, jobError, Optional.empty(), defaultBuildNumber); + } + + public void notifyJobCompletion(JobType jobType, Application application, Optional<DeploymentJobs.JobError> jobError, + Optional<SourceRevision> source, long buildNumber) { clock().advance(Duration.ofMillis(1)); - applications().notifyJobCompletion(jobReport(application, jobType, jobError)); + applications().notifyJobCompletion(jobReport(application, jobType, jobError, source, buildNumber)); } public void completeUpgrade(Application application, Version version, String upgradePolicy) { @@ -233,11 +245,22 @@ public class DeploymentTester { } public void deploy(JobType job, Application application, ApplicationPackage applicationPackage) { - deploy(job, application, applicationPackage, false); + deploy(job, application, Optional.of(applicationPackage), false); + } + + public void deploy(JobType job, Application application, ApplicationPackage applicationPackage, + boolean deployCurrentVersion) { + deploy(job, application, Optional.of(applicationPackage), deployCurrentVersion); } - public void deploy(JobType job, Application application, ApplicationPackage applicationPackage, boolean deployCurrentVersion) { - job.zone(controller().system()).ifPresent(zone -> tester.deploy(application, zone, applicationPackage, deployCurrentVersion)); + public void deploy(JobType job, Application application, Optional<ApplicationPackage> applicationPackage, + boolean deployCurrentVersion) { + job.zone(controller().system()).ifPresent(zone -> tester.deploy(application, zone, applicationPackage, + deployCurrentVersion)); + } + + public void deployAndNotify(Application application, boolean success, JobType... job) { + deployAndNotify(application, Optional.empty(), success, true, job); } public void deployAndNotify(Application application, String upgradePolicy, boolean success, JobType... jobs) { @@ -251,10 +274,15 @@ public class DeploymentTester { public void deployAndNotify(Application application, ApplicationPackage applicationPackage, boolean success, boolean expectOnlyTheseJobs, JobType... jobs) { + deployAndNotify(application, Optional.of(applicationPackage), success, expectOnlyTheseJobs, jobs); + } + + public void deployAndNotify(Application application, Optional<ApplicationPackage> applicationPackage, + boolean success, boolean expectOnlyTheseJobs, JobType... jobs) { consumeJobs(application, expectOnlyTheseJobs, jobs); for (JobType job : jobs) { if (success) { - deploy(job, application, applicationPackage); + deploy(job, application, applicationPackage, false); } notifyJobCompletion(job, application, success); } 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 b71a9090c79..74d03240ec3 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 @@ -247,7 +247,7 @@ public class DeploymentTriggerTest { .region("corp-us-east-1") .region("us-central-1") .region("us-west-1") - .region("ap-northeast-1") + .region("eu-west-1") .build(); // Component job finishes @@ -260,7 +260,7 @@ public class DeploymentTriggerTest { tester.deployAndNotify(application, newApplicationPackage, true, JobType.productionCorpUsEast1); tester.deployAndNotify(application, newApplicationPackage, true, JobType.productionUsCentral1); tester.deployAndNotify(application, newApplicationPackage, true, JobType.productionUsWest1); - tester.deployAndNotify(application, newApplicationPackage, true, JobType.productionApNortheast1); + tester.deployAndNotify(application, newApplicationPackage, true, JobType.productionEuWest1); assertTrue("All jobs consumed", buildSystem.jobs().isEmpty()); } @@ -277,7 +277,7 @@ public class DeploymentTriggerTest { ApplicationPackageBuilder applicationPackageBuilder = new ApplicationPackageBuilder() .upgradePolicy("canary") - // Block revision changes on tuesday in hours 18 and 19 + // Block application version changes on tuesday in hours 18 and 19 .blockChange(true, false, "tue", "18-19", "UTC") .region("us-west-1") .region("us-central-1") diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java index d48f7b84ee6..513e5520d85 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java @@ -3,8 +3,8 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.RegionName; -import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.vespa.hosted.controller.Application; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -13,7 +13,6 @@ import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb; import org.junit.Before; import org.junit.Test; -import java.io.IOException; import java.time.Duration; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +32,7 @@ public class DeploymentExpirerTest { } @Test - public void testDeploymentExpiry() throws IOException, InterruptedException { + public void testDeploymentExpiry() { tester.controllerTester().zoneRegistry().setDeploymentTimeToLive( ZoneId.from(Environment.dev, RegionName.from("us-east-1")), Duration.ofDays(14) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java index 62e6d379c60..b200e2d7e18 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.SystemName; import com.yahoo.slime.Slime; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.Application; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -178,6 +179,7 @@ public class FailureRedeployerTest { public void retryIgnoresStaleJobData() throws Exception { DeploymentTester tester = new DeploymentTester(); tester.controllerTester().zoneRegistry().setSystem(SystemName.cd); + tester.controllerTester().zoneRegistry().setZones(ZoneId.from("prod", "cd-us-central-1")); // Current system version, matches version in test data Version version = Version.fromString("6.141.117"); 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 d7389ca94cd..f42a4c1deb3 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 @@ -6,14 +6,14 @@ import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.ValidationOverrides; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.slime.Slime; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.MetricsService; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; -import com.yahoo.vespa.hosted.controller.application.ApplicationRevision; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.ClusterInfo; import com.yahoo.vespa.hosted.controller.application.ClusterUtilization; @@ -26,7 +26,6 @@ import com.yahoo.vespa.hosted.controller.application.SourceRevision; import com.yahoo.vespa.hosted.controller.rotation.RotationId; import org.junit.Test; -import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; @@ -60,10 +59,11 @@ public class ApplicationSerializerTest { "</validation-overrides>"); List<Deployment> deployments = new ArrayList<>(); - ApplicationRevision revision1 = ApplicationRevision.from("appHash1"); - ApplicationRevision revision2 = ApplicationRevision.from("appHash2", new SourceRevision("repo1", "branch1", "commit1")); - deployments.add(new Deployment(zone1, revision1, Version.fromString("1.2.3"), Instant.ofEpochMilli(3))); // One deployment without cluster info and utils - deployments.add(new Deployment(zone2, revision2, Version.fromString("1.2.3"), Instant.ofEpochMilli(5), + ApplicationVersion applicationVersion1 = ApplicationVersion.from("appHash1"); + ApplicationVersion applicationVersion2 = ApplicationVersion + .from("appHash2", new SourceRevision("repo1", "branch1", "commit1")); + deployments.add(new Deployment(zone1, applicationVersion1, Version.fromString("1.2.3"), Instant.ofEpochMilli(3))); // One deployment without cluster info and utils + deployments.add(new Deployment(zone2, applicationVersion2, Version.fromString("1.2.3"), Instant.ofEpochMilli(5), createClusterUtils(3, 0.2), createClusterInfo(3, 4),new DeploymentMetrics(2,3,4,5,6))); Optional<Long> projectId = Optional.of(123L); @@ -96,8 +96,8 @@ public class ApplicationSerializerTest { assertEquals(original.validationOverrides().xmlForm(), serialized.validationOverrides().xmlForm()); assertEquals(2, serialized.deployments().size()); - assertEquals(original.deployments().get(zone1).revision(), serialized.deployments().get(zone1).revision()); - assertEquals(original.deployments().get(zone2).revision(), serialized.deployments().get(zone2).revision()); + assertEquals(original.deployments().get(zone1).applicationVersion(), serialized.deployments().get(zone1).applicationVersion()); + assertEquals(original.deployments().get(zone2).applicationVersion(), serialized.deployments().get(zone2).applicationVersion()); assertEquals(original.deployments().get(zone1).version(), serialized.deployments().get(zone1).version()); assertEquals(original.deployments().get(zone2).version(), serialized.deployments().get(zone2).version()); assertEquals(original.deployments().get(zone1).at(), serialized.deployments().get(zone1).at()); @@ -145,18 +145,20 @@ public class ApplicationSerializerTest { assertEquals(6, serialized.deployments().get(zone2).metrics().writeLatencyMillis(), Double.MIN_VALUE); { // test more deployment serialization cases - Application original2 = writable(original).withDeploying(Optional.of(Change.ApplicationChange.of(ApplicationRevision.from("hash1")))); + Application original2 = writable(original).withDeploying(Optional.of(Change.ApplicationChange.of(ApplicationVersion + .from("hash1")))); Application serialized2 = applicationSerializer.fromSlime(applicationSerializer.toSlime(original2)); assertEquals(original2.deploying(), serialized2.deploying()); - assertEquals(((Change.ApplicationChange)serialized2.deploying().get()).revision().get().source(), - ((Change.ApplicationChange)original2.deploying().get()).revision().get().source()); + assertEquals(((Change.ApplicationChange)serialized2.deploying().get()).version().get().source(), + ((Change.ApplicationChange)original2.deploying().get()).version().get().source()); - Application original3 = writable(original).withDeploying(Optional.of(Change.ApplicationChange.of(ApplicationRevision.from("hash1", - new SourceRevision("a", "b", "c"))))); + Application original3 = writable(original).withDeploying(Optional.of(Change.ApplicationChange.of(ApplicationVersion + .from("hash1", + new SourceRevision("a", "b", "c"))))); Application serialized3 = applicationSerializer.fromSlime(applicationSerializer.toSlime(original3)); assertEquals(original3.deploying(), serialized2.deploying()); - assertEquals(((Change.ApplicationChange)serialized3.deploying().get()).revision().get().source(), - ((Change.ApplicationChange)original3.deploying().get()).revision().get().source()); + assertEquals(((Change.ApplicationChange)serialized3.deploying().get()).version().get().source(), + ((Change.ApplicationChange)original3.deploying().get()).version().get().source()); Application original4 = writable(original).withDeploying(Optional.empty()); Application serialized4 = applicationSerializer.fromSlime(applicationSerializer.toSlime(original4)); @@ -195,7 +197,7 @@ public class ApplicationSerializerTest { } @Test - public void testLegacySerialization() throws IOException { + public void testLegacySerialization() { Application applicationWithSuccessfulJob = applicationSerializer.fromSlime(applicationSlime(false)); assertFalse("No job error for successful job", applicationWithSuccessfulJob.deploymentJobs().jobStatus().get(DeploymentJobs.JobType.systemTest).jobError().isPresent()); @@ -211,7 +213,7 @@ public class ApplicationSerializerTest { @Test public void testCompleteApplicationDeserialization() { - Application application = applicationSerializer.fromSlime(SlimeUtils.jsonToSlime(longApplicationJson.getBytes(StandardCharsets.UTF_8))); + applicationSerializer.fromSlime(SlimeUtils.jsonToSlime(longApplicationJson.getBytes(StandardCharsets.UTF_8))); // ok if no error } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java index fc0147dacef..5b806d580e2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java @@ -81,7 +81,7 @@ public class ContainerControllerTester { GitRevision app1RevisionId = new GitRevision(new GitRepository("repo"), new GitBranch("master"), new GitCommit("commit1")); controller().applications().deployApplication(application.id(), zone, - applicationPackage, + Optional.of(applicationPackage), new DeployOptions(Optional.of(new ScrewdriverBuildJob(app1ScrewdriverId, app1RevisionId)), Optional.empty(), false, false)); return application; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java index 028992e8f7d..abc5f9f8aa1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -67,6 +67,7 @@ public class ControllerContainerTest { " <component id='com.yahoo.vespa.hosted.controller.persistence.MemoryControllerDb'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.restapi.application.MockAuthorizer'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.routing.MockRoutingGenerator'/>\n" + + " <component id='com.yahoo.vespa.hosted.controller.ArtifactRepositoryMock'/>\n" + " <handler id='com.yahoo.vespa.hosted.controller.restapi.application.ApplicationApiHandler'>\n" + " <binding>http://*/application/v4/*</binding>\n" + " </handler>\n" + |