diff options
author | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2018-09-21 13:22:03 +0200 |
---|---|---|
committer | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2018-09-21 13:22:03 +0200 |
commit | f64c07dff75caa6d9d03c60dd2b51a8c27db247d (patch) | |
tree | ed1aa2644f59779d7991309a999057201d0fda35 /controller-server | |
parent | 4773e5549177ab57297fd2ed01ee1fc2135fc5f3 (diff) |
Record time of creation of new applications
Diffstat (limited to 'controller-server')
7 files changed, 63 insertions, 41 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java index 5b77330eff4..a6c3f11470d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java @@ -41,6 +41,7 @@ import java.util.stream.Collectors; public class Application { private final ApplicationId id; + private final Instant createdAt; private final DeploymentSpec deploymentSpec; private final ValidationOverrides validationOverrides; private final Map<ZoneId, Deployment> deployments; @@ -53,28 +54,29 @@ public class Application { private final Map<HostName, RotationStatus> rotationStatus; /** Creates an empty application */ - public Application(ApplicationId id) { - this(id, DeploymentSpec.empty, ValidationOverrides.empty, Collections.emptyMap(), + public Application(ApplicationId id, Instant now) { + this(id, now, DeploymentSpec.empty, ValidationOverrides.empty, Collections.emptyMap(), new DeploymentJobs(OptionalLong.empty(), Collections.emptyList(), Optional.empty(), false), Change.empty(), Change.empty(), Optional.empty(), new ApplicationMetrics(0, 0), Optional.empty(), Collections.emptyMap()); } /** Used from persistence layer: Do not use */ - public Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, + public Application(ApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, List<Deployment> deployments, DeploymentJobs deploymentJobs, Change change, Change outstandingChange, Optional<IssueId> ownershipIssueId, ApplicationMetrics metrics, Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) { - this(id, deploymentSpec, validationOverrides, + this(id, createdAt, deploymentSpec, validationOverrides, deployments.stream().collect(Collectors.toMap(Deployment::zone, d -> d)), deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } - Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, + Application(ApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change, Change outstandingChange, Optional<IssueId> ownershipIssueId, ApplicationMetrics metrics, Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) { this.id = Objects.requireNonNull(id, "id cannot be null"); + this.createdAt = Objects.requireNonNull(createdAt, "instant of creation cannot be null"); this.deploymentSpec = Objects.requireNonNull(deploymentSpec, "deploymentSpec cannot be null"); this.validationOverrides = Objects.requireNonNull(validationOverrides, "validationOverrides cannot be null"); this.deployments = ImmutableMap.copyOf(Objects.requireNonNull(deployments, "deployments cannot be null")); @@ -89,6 +91,8 @@ public class Application { public ApplicationId id() { return id; } + public Instant createdAt() { return createdAt; } + /** * Returns the last deployed deployment spec of this application, * or the empty deployment spec if it has never been deployed diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java index af5b9198343..955faeb3e51 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java @@ -274,7 +274,7 @@ public class ApplicationController { zmsClient.addApplication(((AthenzTenant) tenant.get()).domain(), new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value())); } - LockedApplication application = new LockedApplication(new Application(id), lock); + LockedApplication application = new LockedApplication(new Application(id, clock.instant()), lock); store(application); log.info("Created " + application); return application.get(); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java index e8d50d72669..433a6d3ed38 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java @@ -42,6 +42,7 @@ public class LockedApplication { private final Lock lock; private final ApplicationId id; + private final Instant createdAt; private final DeploymentSpec deploymentSpec; private final ValidationOverrides validationOverrides; private final Map<ZoneId, Deployment> deployments; @@ -60,7 +61,7 @@ public class LockedApplication { * @param lock The lock for the application. */ LockedApplication(Application application, Lock lock) { - this(Objects.requireNonNull(lock, "lock cannot be null"), application.id(), + this(Objects.requireNonNull(lock, "lock cannot be null"), application.id(), application.createdAt(), application.deploymentSpec(), application.validationOverrides(), application.deployments(), application.deploymentJobs(), application.change(), application.outstandingChange(), @@ -69,13 +70,14 @@ public class LockedApplication { application.rotationStatus()); } - private LockedApplication(Lock lock, ApplicationId id, + private LockedApplication(Lock lock, ApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change, Change outstandingChange, Optional<IssueId> ownershipIssueId, ApplicationMetrics metrics, Optional<RotationId> rotation, Map<HostName, RotationStatus> rotationStatus) { this.lock = lock; this.id = id; + this.createdAt = createdAt; this.deploymentSpec = deploymentSpec; this.validationOverrides = validationOverrides; this.deployments = deployments; @@ -90,37 +92,37 @@ public class LockedApplication { /** Returns a read-only copy of this */ public Application get() { - return new Application(id, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, + return new Application(id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withBuiltInternally(boolean builtInternally) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs.withBuiltInternally(builtInternally), change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withProjectId(OptionalLong projectId) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs.withProjectId(projectId), change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withDeploymentIssueId(IssueId issueId) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs.with(issueId), change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withJobCompletion(long projectId, JobType jobType, JobStatus.JobRun completion, Optional<DeploymentJobs.JobError> jobError) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs.withCompletion(projectId, jobType, completion, jobError), change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withJobTriggering(JobType jobType, JobStatus.JobRun job) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs.withTriggering(jobType, job), change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } @@ -170,55 +172,55 @@ public class LockedApplication { } public LockedApplication withoutDeploymentJob(JobType jobType) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs.without(jobType), change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication with(DeploymentSpec deploymentSpec) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication with(ValidationOverrides validationOverrides) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withChange(Change change) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withOutstandingChange(Change outstandingChange) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication withOwnershipIssueId(IssueId issueId) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, Optional.ofNullable(issueId), metrics, rotation, rotationStatus); } public LockedApplication with(MetricsService.ApplicationMetrics metrics) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } public LockedApplication with(RotationId rotation) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, Optional.of(rotation), rotationStatus); } public LockedApplication withRotationStatus(Map<HostName, RotationStatus> rotationStatus) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } @@ -230,7 +232,7 @@ public class LockedApplication { } private LockedApplication with(Map<ZoneId, Deployment> deployments) { - return new LockedApplication(lock, id, deploymentSpec, validationOverrides, deployments, + return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java index b12c7a676e1..93578e97cc4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java @@ -49,22 +49,24 @@ public class ApplicationOwnershipConfirmer extends Maintainer { /** File an ownership issue with the owners of all applications we know about. */ private void confirmApplicationOwnerships() { ApplicationList.from(controller().applications().asList()) - .notPullRequest() - .hasProductionDeployment() - .asList() - .forEach(application -> { - try { - Tenant tenant = ownerOf(application.id()); - Optional<IssueId> ourIssueId = application.ownershipIssueId(); - ourIssueId = tenant instanceof AthenzTenant - ? ownershipIssues.confirmOwnership(ourIssueId, application.id(), propertyIdFor((AthenzTenant) tenant)) - : ownershipIssues.confirmOwnership(ourIssueId, application.id(), userFor(tenant)); - ourIssueId.ifPresent(issueId -> store(issueId, application.id())); - } - catch (RuntimeException e) { // Catch errors due to wrong data in the controller, or issues client timeout. - log.log(Level.WARNING, "Exception caught when attempting to file an issue for " + application.id(), e); - } - }); + .notPullRequest() + .hasProductionDeployment() + .asList() + .stream() + .filter(application -> application.createdAt().isBefore(controller().clock().instant().minus(Duration.ofDays(90)))) + .forEach(application -> { + try { + Tenant tenant = ownerOf(application.id()); + Optional<IssueId> ourIssueId = application.ownershipIssueId(); + ourIssueId = tenant instanceof AthenzTenant + ? ownershipIssues.confirmOwnership(ourIssueId, application.id(), propertyIdFor((AthenzTenant) tenant)) + : ownershipIssues.confirmOwnership(ourIssueId, application.id(), userFor(tenant)); + ourIssueId.ifPresent(issueId -> store(issueId, application.id())); + } + catch (RuntimeException e) { // Catch errors due to wrong data in the controller, or issues client timeout. + log.log(Level.WARNING, "Exception caught when attempting to file an issue for " + application.id(), e); + } + }); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java index 9ee23dbe51b..3975613835b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java @@ -53,6 +53,7 @@ public class ApplicationSerializer { // Application fields private final String idField = "id"; + private final String createdAtField = "createdAt"; private final String deploymentSpecField = "deploymentSpecField"; private final String validationOverridesField = "validationOverrides"; private final String deploymentsField = "deployments"; @@ -135,6 +136,7 @@ public class ApplicationSerializer { Slime slime = new Slime(); Cursor root = slime.setObject(); root.setString(idField, application.id().serializedForm()); + root.setLong(createdAtField, application.createdAt().toEpochMilli()); root.setString(deploymentSpecField, application.deploymentSpec().xmlForm()); root.setString(validationOverridesField, application.validationOverrides().xmlForm()); deploymentsToSlime(application.deployments().values(), root.setArray(deploymentsField)); @@ -287,6 +289,7 @@ public class ApplicationSerializer { Inspector root = slime.get(); ApplicationId id = ApplicationId.fromSerializedForm(root.field(idField).asString()); + Instant createdAt = Instant.ofEpochMilli(root.field(createdAtField).asLong()); DeploymentSpec deploymentSpec = DeploymentSpec.fromXml(root.field(deploymentSpecField).asString(), false); ValidationOverrides validationOverrides = ValidationOverrides.fromXml(root.field(validationOverridesField).asString()); List<Deployment> deployments = deploymentsFromSlime(root.field(deploymentsField)); @@ -299,7 +302,7 @@ public class ApplicationSerializer { Optional<RotationId> rotation = rotationFromSlime(root.field(rotationField)); Map<HostName, RotationStatus> rotationStatus = rotationStatusFromSlime(root.field(rotationStatusField)); - return new Application(id, deploymentSpec, validationOverrides, deployments, deploymentJobs, deploying, + return new Application(id, createdAt, deploymentSpec, validationOverrides, deployments, deploymentJobs, deploying, outstandingChange, ownershipIssueId, metrics, rotation, rotationStatus); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java index 555fdb338e8..2694e205a68 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java @@ -59,6 +59,13 @@ public class ApplicationOwnershipConfirmerTest { confirmer.maintain(); confirmer.maintain(); + assertFalse("No issue is stored for an application newer than 3 months.", propertyApp.get().ownershipIssueId().isPresent()); + assertFalse("No issue is stored for an application newer than 3 months.", userApp.get().ownershipIssueId().isPresent()); + + tester.clock().advance(Duration.ofDays(91)); + confirmer.maintain(); + confirmer.maintain(); + assertEquals("Confirmation issue has been filed for property owned application.", issueId, propertyApp.get().ownershipIssueId()); assertEquals("Confirmation issue has been filed for user owned application.", issueId, userApp.get().ownershipIssueId()); assertTrue("Both applications have had their responses ensured.", issues.escalatedForProperty && issues.escalatedForUser); 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 8d99dfc5c16..3e09c9078a0 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 @@ -32,6 +32,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -97,6 +99,7 @@ public class ApplicationSerializerTest { rotationStatus.put(HostName.from("rot2.fqdn"), RotationStatus.out); Application original = new Application(ApplicationId.from("t1", "a1", "i1"), + Instant.now().truncatedTo(ChronoUnit.MILLIS), deploymentSpec, validationOverrides, deployments, deploymentJobs, @@ -110,6 +113,7 @@ public class ApplicationSerializerTest { Application serialized = applicationSerializer.fromSlime(applicationSerializer.toSlime(original)); assertEquals(original.id(), serialized.id()); + assertEquals(original.createdAt(), serialized.createdAt()); assertEquals(original.deploymentSpec().xmlForm(), serialized.deploymentSpec().xmlForm()); assertEquals(original.validationOverrides().xmlForm(), serialized.validationOverrides().xmlForm()); |