diff options
author | Jon Bratseth <bratseth@oath.com> | 2018-01-26 13:00:02 +0100 |
---|---|---|
committer | Jon Bratseth <bratseth@oath.com> | 2018-01-26 13:00:02 +0100 |
commit | 66348be0c92c43adf5c09def6664923373e4d6be (patch) | |
tree | d4dfa114915c7b99a270c12ad8c2baed5e156051 | |
parent | 8d0c9c8f584f29348f3efee6e566341e26da55e4 (diff) |
Make ApplicationChange mandatory
9 files changed, 121 insertions, 98 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 c054d81db35..bb1f64c121c 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 @@ -30,9 +30,9 @@ import java.util.stream.Collectors; /** * An instance of an application. - * + * * This is immutable. - * + * * @author bratseth */ public class Application { @@ -57,11 +57,11 @@ public class Application { } /** Used from persistence layer: Do not use */ - public Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, + public Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, List<Deployment> deployments, DeploymentJobs deploymentJobs, Optional<Change> deploying, boolean outstandingChange, Optional<IssueId> ownershipIssueId, ApplicationMetrics metrics, Optional<RotationId> rotation) { - this(id, deploymentSpec, validationOverrides, + this(id, deploymentSpec, validationOverrides, deployments.stream().collect(Collectors.toMap(Deployment::zone, d -> d)), deploymentJobs, deploying, outstandingChange, ownershipIssueId, metrics, rotation); } @@ -91,24 +91,24 @@ public class Application { } public ApplicationId id() { return id; } - - /** - * Returns the last deployed deployment spec of this application, - * or the empty deployment spec if it has never been deployed + + /** + * Returns the last deployed deployment spec of this application, + * or the empty deployment spec if it has never been deployed */ public DeploymentSpec deploymentSpec() { return deploymentSpec; } /** - * Returns the last deployed validation overrides of this application, + * Returns the last deployed validation overrides of this application, * or the empty validation overrides if it has never been deployed * (or was deployed with an empty/missing validation overrides) */ public ValidationOverrides validationOverrides() { return validationOverrides; } - + /** Returns an immutable map of the current deployments of this */ public Map<ZoneId, Deployment> deployments() { return deployments; } - /** + /** * Returns an immutable map of the current *production* deployments of this * (deployments also includes manually deployed environments) */ @@ -121,7 +121,7 @@ public class Application { public DeploymentJobs deploymentJobs() { return deploymentJobs; } /** - * Returns the change that is currently in the process of being deployed on this application, + * Returns the change that is currently in the process of being deployed on this application, * or empty if no change is currently being deployed. */ public Optional<Change> deploying() { return deploying; } @@ -166,8 +166,13 @@ public class Application { /** Returns the application version a deployment to this zone should use, or empty if we don't know */ public Optional<ApplicationVersion> deployApplicationVersionIn(ZoneId zone) { - if (deploying().isPresent() && deploying().get() instanceof Change.ApplicationChange) - return ((Change.ApplicationChange) deploying().get()).version(); + if (deploying().isPresent() && deploying().get() instanceof Change.ApplicationChange) { + ApplicationVersion version = ((Change.ApplicationChange) deploying().get()).version(); + if (version == ApplicationVersion.unknown) + return Optional.empty(); + else + return Optional.of(version); + } return applicationVersionIn(zone); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationVersion.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationVersion.java index aee178af275..304d82b2bec 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationVersion.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationVersion.java @@ -13,15 +13,25 @@ import java.util.Optional; */ public class ApplicationVersion { + // TODO: Remove the need for this + public static final ApplicationVersion unknown = new ApplicationVersion(); + // Never changes. Only used to create a valid version number for the bundle private static final String majorVersion = "1.0"; // TODO: Remove after introducing new application version private final Optional<String> applicationPackageHash; + // TODO: Make mandatory private final Optional<SourceRevision> source; private final Optional<Long> buildNumber; + private ApplicationVersion() { + this.applicationPackageHash = Optional.empty(); + this.source = Optional.empty(); + this.buildNumber = Optional.empty(); + } + private ApplicationVersion(Optional<String> applicationPackageHash, Optional<SourceRevision> source, Optional<Long> buildNumber) { Objects.requireNonNull(applicationPackageHash, "applicationPackageHash cannot be null"); @@ -30,14 +40,14 @@ public class ApplicationVersion { if (buildNumber.isPresent() && !source.isPresent()) { throw new IllegalArgumentException("both buildNumber and source must be set if buildNumber is set"); } - if (!buildNumber.isPresent() && !applicationPackageHash.isPresent()) { + if ( ! buildNumber.isPresent() && ! applicationPackageHash.isPresent()) { throw new IllegalArgumentException("applicationPackageHash must be given if buildNumber is unset"); } this.applicationPackageHash = applicationPackageHash; this.source = source; this.buildNumber = buildNumber; } - + /** Create an application package revision where there is no information about its source */ public static ApplicationVersion from(String applicationPackageHash) { return new ApplicationVersion(Optional.of(applicationPackageHash), Optional.empty(), Optional.empty()); @@ -61,7 +71,7 @@ public class ApplicationVersion { return String.format("%s.%d-%s", majorVersion, buildNumber.get(), abbreviateCommit(source.get().commit())); } - /** + /** * Returns information about the source of this revision, or empty if the source is not know/defined * (which is the case for command-line deployment from developers, but never for deployment jobs) */ @@ -69,17 +79,17 @@ public class ApplicationVersion { /** Returns the build number that built this version */ public Optional<Long> buildNumber() { return buildNumber; } - + @Override public int hashCode() { return applicationPackageHash.hashCode(); } - + @Override public boolean equals(Object other) { if (this == other) return true; if ( ! (other instanceof ApplicationVersion)) return false; return this.applicationPackageHash.equals(((ApplicationVersion)other).applicationPackageHash); } - + @Override public String toString() { if (buildNumber.isPresent()) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Change.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Change.java index 08291373656..aeed510fa7e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Change.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Change.java @@ -6,31 +6,29 @@ import com.yahoo.config.application.api.DeploymentSpec; import java.time.Instant; import java.util.Objects; -import java.util.Optional; /** * A change to an application - * + * * @author bratseth */ public abstract class Change { /** Returns true if this change is blocked by the given spec at the given instant */ public abstract boolean blockedBy(DeploymentSpec deploymentSpec, Instant instant); - + /** A change to the application package version of an application */ public static class ApplicationChange extends Change { - // TODO: Make non-optional - private final Optional<ApplicationVersion> version; - - private ApplicationChange(Optional<ApplicationVersion> version) { + private final ApplicationVersion version; + + private ApplicationChange(ApplicationVersion version) { Objects.requireNonNull(version, "version cannot be null"); this.version = version; } - + /** The application package version in this change, or empty if not known yet */ - public Optional<ApplicationVersion> version() { return version; } + public ApplicationVersion version() { return version; } @Override public boolean blockedBy(DeploymentSpec deploymentSpec, Instant instant) { @@ -39,7 +37,7 @@ public abstract class Change { @Override public int hashCode() { return version.hashCode(); } - + @Override public boolean equals(Object other) { if (this == other) return true; @@ -47,25 +45,25 @@ public abstract class Change { return ((ApplicationChange)other).version.equals(this.version); } - /** + /** * Creates an application change which we don't know anything about. * We are notified that a change has occurred by completion of the component job * but do not get to know about what the change is until a subsequent deployment * happens. */ public static ApplicationChange unknown() { - return new ApplicationChange(Optional.empty()); + return new ApplicationChange(ApplicationVersion.unknown); } - + public static ApplicationChange of(ApplicationVersion version) { - return new ApplicationChange(Optional.of(version)); + return new ApplicationChange(version); } @Override - public String toString() { - return "application change to " + version.map(ApplicationVersion::toString).orElse("an unknown version"); + public String toString() { + return "application change to " + version; } - + } /** A change to the Vespa version running an application */ @@ -97,8 +95,8 @@ public abstract class Change { } @Override - public String toString() { - return "version change to " + version; + public String toString() { + return "version change to " + version; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java index 71c4a380a29..6a29d4990e7 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobStatus.java @@ -55,6 +55,13 @@ public class JobStatus { return new JobStatus(type, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } + public JobStatus withTriggering(Version version, ApplicationVersion applicationVersion, + boolean upgrade, String reason, Instant triggerTime) { + return withTriggering(version, + applicationVersion == ApplicationVersion.unknown ? Optional.empty() : Optional.of(applicationVersion), + upgrade, reason, triggerTime); + } + public JobStatus withTriggering(Version version, Optional<ApplicationVersion> applicationVersion, boolean upgrade, String reason, Instant triggerTime) { return new JobStatus(type, jobError, Optional.of(new JobRun(-1, version, applicationVersion, upgrade, reason, triggerTime)), @@ -212,7 +219,10 @@ public class JobStatus { public boolean lastCompletedWas(Change change) { if (change instanceof Change.ApplicationChange) { Change.ApplicationChange applicationChange = (Change.ApplicationChange) change; - return applicationVersion().equals(applicationChange.version()); + if ( ! applicationVersion().isPresent()) + return applicationChange.version() == ApplicationVersion.unknown; + else + return applicationVersion().get().equals(applicationChange.version()); } else if (change instanceof Change.VersionChange) { Change.VersionChange versionChange = (Change.VersionChange) change; return version().equals(versionChange.version()); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java index 09768796445..6fd25f1a8c6 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java @@ -144,15 +144,14 @@ public class DeploymentTrigger { if (change instanceof VersionChange) { if (((VersionChange)change).version().isAfter(deployment.version())) return false; // later is ok } - else if (((Change.ApplicationChange)change).version().isPresent()) { - if ( ! ((Change.ApplicationChange)change).version().get().equals(deployment.applicationVersion())) return false; + else if (((Change.ApplicationChange)change).version() != ApplicationVersion.unknown) { + if ( ! ((Change.ApplicationChange)change).version().equals(deployment.applicationVersion())) return false; } else { return false; // If we don't yet know the application version we are deploying, then we are not complete } } - return true; } 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 1514d98610a..1664e984a27 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 @@ -38,7 +38,7 @@ import java.util.Optional; /** * Serializes applications to/from slime. * This class is multithread safe. - * + * * @author bratseth */ public class ApplicationSerializer { @@ -67,12 +67,12 @@ public class ApplicationSerializer { private final String repositoryField = "repositoryField"; private final String branchField = "branchField"; private final String commitField = "commitField"; - + // DeploymentJobs fields private final String projectIdField = "projectId"; private final String jobStatusField = "jobStatus"; private final String issueIdField = "jiraIssueId"; - + // JobStatus field private final String jobTypeField = "jobType"; private final String errorField = "jobError"; @@ -80,7 +80,7 @@ public class ApplicationSerializer { private final String lastCompletedField = "lastCompleted"; private final String firstFailingField = "firstFailing"; private final String lastSuccessField = "lastSuccess"; - + // JobRun fields private final String jobRunIdField = "id"; private final String versionField = "version"; @@ -116,7 +116,7 @@ public class ApplicationSerializer { // ------------------ Serialization - + public Slime toSlime(Application application) { Slime slime = new Slime(); Cursor root = slime.setObject(); @@ -138,7 +138,7 @@ public class ApplicationSerializer { for (Deployment deployment : deployments) deploymentToSlime(deployment, array.addObject()); } - + private void deploymentToSlime(Deployment deployment, Cursor object) { zoneIdToSlime(deployment.zone(), object.setObject(zoneField)); object.setString(versionField, deployment.version().toString()); @@ -196,19 +196,19 @@ public class ApplicationSerializer { object.setString(environmentField, zone.environment().value()); object.setString(regionField, zone.region().value()); } - + private void toSlime(ApplicationVersion applicationVersion, Cursor object) { object.setString(applicationPackageHashField, applicationVersion.id()); if (applicationVersion.source().isPresent()) toSlime(applicationVersion.source().get(), object.setObject(sourceRevisionField)); } - + private void toSlime(SourceRevision sourceRevision, Cursor object) { object.setString(repositoryField, sourceRevision.repository()); object.setString(branchField, sourceRevision.branch()); object.setString(commitField, sourceRevision.commit()); } - + private void toSlime(DeploymentJobs deploymentJobs, Cursor cursor) { deploymentJobs.projectId().ifPresent(projectId -> cursor.setLong(projectIdField, projectId)); jobStatusToSlime(deploymentJobs.jobStatus().values(), cursor.setArray(jobStatusField)); @@ -219,7 +219,7 @@ public class ApplicationSerializer { for (JobStatus jobStatus : jobStatuses) toSlime(jobStatus, jobStatusArray.addObject()); } - + private void toSlime(JobStatus jobStatus, Cursor object) { object.setString(jobTypeField, jobStatus.type().jobName()); if (jobStatus.jobError().isPresent()) @@ -230,7 +230,7 @@ public class ApplicationSerializer { jobRunToSlime(jobStatus.firstFailing(), object, firstFailingField); jobRunToSlime(jobStatus.lastSuccess(), object, lastSuccessField); } - + private void jobRunToSlime(Optional<JobStatus.JobRun> jobRun, Cursor parent, String jobRunObjectName) { if ( ! jobRun.isPresent()) return; Cursor object = parent.setObject(jobRunObjectName); @@ -242,22 +242,22 @@ public class ApplicationSerializer { object.setString(reasonField, jobRun.get().reason()); object.setLong(atField, jobRun.get().at().toEpochMilli()); } - + private void toSlime(Optional<Change> deploying, Cursor parentObject) { if ( ! deploying.isPresent()) return; Cursor object = parentObject.setObject(deployingField); if (deploying.get() instanceof Change.VersionChange) object.setString(versionField, ((Change.VersionChange)deploying.get()).version().toString()); - else if (((Change.ApplicationChange)deploying.get()).version().isPresent()) - toSlime(((Change.ApplicationChange)deploying.get()).version().get(), object); + else if (((Change.ApplicationChange)deploying.get()).version() != ApplicationVersion.unknown) + toSlime(((Change.ApplicationChange)deploying.get()).version(), object); } // ------------------ Deserialization public Application fromSlime(Slime slime) { Inspector root = slime.get(); - + ApplicationId id = ApplicationId.fromSerializedForm(root.field(idField).asString()); DeploymentSpec deploymentSpec = DeploymentSpec.fromXml(root.field(deploymentSpecField).asString(), false); ValidationOverrides validationOverrides = ValidationOverrides.fromXml(root.field(validationOverridesField).asString()); @@ -347,7 +347,7 @@ public class ApplicationSerializer { return sourceRevision.isPresent() ? Optional.of(ApplicationVersion.from(applicationPackageHash, sourceRevision.get())) : Optional.of(ApplicationVersion.from(applicationPackageHash)); } - + private Optional<SourceRevision> sourceRevisionFromSlime(Inspector object) { if ( ! object.valid()) return Optional.empty(); return Optional.of(new SourceRevision(object.field(repositoryField).asString(), @@ -373,13 +373,13 @@ public class ApplicationSerializer { else return Optional.of(Change.ApplicationChange.unknown()); } - + private List<JobStatus> jobStatusListFromSlime(Inspector array) { List<JobStatus> jobStatusList = new ArrayList<>(); array.traverse((ArrayTraverser) (int i, Inspector item) -> jobStatusList.add(jobStatusFromSlime(item))); return jobStatusList; } - + private JobStatus jobStatusFromSlime(Inspector object) { DeploymentJobs.JobType jobType = DeploymentJobs.JobType.fromJobName(object.field(jobTypeField).asString()); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index a489e1d9f63..3cca0fb6318 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -98,7 +98,7 @@ import java.util.logging.Level; /** * This implements the application/v4 API which is used to deploy and manage applications * on hosted Vespa. - * + * * @author bratseth * @author mpolden */ @@ -123,7 +123,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { public Duration getTimeout() { return Duration.ofMinutes(20); // deploys may take a long time; } - + @Override public HttpResponse handle(HttpRequest request) { try { @@ -156,7 +156,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { return ErrorResponse.internalServerError(Exceptions.toMessageString(e)); } } - + private HttpResponse handleGET(HttpRequest request) { Path path = new Path(request.getUri().getPath()); if (path.matches("/application/v4/")) return root(request); @@ -213,7 +213,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { return setGlobalRotationOverride(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), true, request); return ErrorResponse.notFoundError("Nothing at " + path); } - + private HttpResponse handleOPTIONS() { // We implement this to avoid redirect loops on OPTIONS requests from browsers, but do not really bother // spelling out the methods supported at each path, which we should @@ -235,7 +235,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { ? recursiveRoot(request) : new ResourceResponse(request, "user", "tenant", "tenant-pipeline", "athensDomain", "property", "cookiefreshness"); } - + private HttpResponse authenticatedUser(HttpRequest request) { String userIdString = request.getProperty("userOverride"); if (userIdString == null) @@ -243,7 +243,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { .map(UserId::id) .orElseThrow(() -> new ForbiddenException("You must be authenticated or specify userOverride")); UserId userId = new UserId(userIdString); - + List<Tenant> tenants = controller.tenants().asList(userId); Slime slime = new Slime(); @@ -255,7 +255,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { response.setBool("tenantExists", tenants.stream().map(Tenant::getId).anyMatch(id -> id.isTenantFor(userId))); return new SlimeJsonResponse(slime); } - + private HttpResponse tenants(HttpRequest request) { Slime slime = new Slime(); Cursor response = slime.setArray(); @@ -263,7 +263,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { tenantInTenantsListToSlime(tenant, request.getUri(), response.addObject()); return new SlimeJsonResponse(slime); } - + /** Lists the screwdriver project id for each application */ private HttpResponse tenantPipelines() { Slime slime = new Slime(); @@ -281,7 +281,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { response.setArray("brokenTenantPipelines"); // not used but may need to be present return new SlimeJsonResponse(slime); } - + private HttpResponse athenzDomains(HttpRequest request) { Slime slime = new Slime(); Cursor response = slime.setObject(); @@ -307,7 +307,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { private HttpResponse cookieFreshness(HttpRequest request) { Slime slime = new Slime(); String passThruHeader = request.getHeader(SetBouncerPassthruHeaderFilter.BOUNCER_PASSTHRU_HEADER_FIELD); - slime.setObject().setBool("shouldRefreshCookie", + slime.setObject().setBool("shouldRefreshCookie", ! SetBouncerPassthruHeaderFilter.BOUNCER_PASSTHRU_COOKIE_OK.equals(passThruHeader)); return new SlimeJsonResponse(slime); } @@ -332,7 +332,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { toSlime(application, array.addObject(), request); return new SlimeJsonResponse(slime); } - + private HttpResponse application(String tenantName, String applicationName, HttpRequest request) { ApplicationId applicationId = ApplicationId.from(tenantName, applicationName, "default"); Application application = @@ -352,8 +352,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler { Cursor deployingObject = object.setObject("deploying"); if (application.deploying().get() instanceof Change.VersionChange) deployingObject.setString("version", ((Change.VersionChange)application.deploying().get()).version().toString()); - else if (((Change.ApplicationChange)application.deploying().get()).version().isPresent()) - toSlime(((Change.ApplicationChange)application.deploying().get()).version().get(), deployingObject.setObject("revision")); + else if (((Change.ApplicationChange)application.deploying().get()).version() != ApplicationVersion.unknown) + toSlime(((Change.ApplicationChange)application.deploying().get()).version(), deployingObject.setObject("revision")); } // Jobs sorted according to deployment spec @@ -594,7 +594,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { response.setResponse(result, serviceName, restPath); return response; } - + private HttpResponse createUser(HttpRequest request) { Optional<UserId> user = userFrom(request); if ( ! user.isPresent() ) throw new ForbiddenException("Not authenticated."); 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 1127e739689..8c242ef1150 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 @@ -101,14 +101,15 @@ 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("Application version is currently not known", - ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).version().isPresent()); + assertEquals("Application version is currently not known", + ApplicationVersion.unknown, + ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).version()); tester.deployAndNotify(app1, applicationPackage, true, systemTest); tester.deployAndNotify(app1, applicationPackage, true, stagingTest); assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); - Optional<ApplicationVersion> applicationVersion = ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).version(); - assertTrue("Application version has been set during deployment", applicationVersion.isPresent()); + ApplicationVersion applicationVersion = ((Change.ApplicationChange)tester.controller().applications().require(app1.id()).deploying().get()).version(); + assertTrue("Application version has been set during deployment", applicationVersion != ApplicationVersion.unknown); assertStatus(JobStatus.initial(stagingTest) .withTriggering(version1, applicationVersion, false, "", tester.clock().instant().minus(Duration.ofMillis(1))) .withCompletion(42, Optional.empty(), tester.clock().instant(), tester.controller()), app1.id(), tester.controller()); @@ -166,7 +167,7 @@ public class ControllerTest { tester.deployAndNotify(app1, applicationPackage, true, productionUsEast3); assertEquals(5, applications.get(app1.id()).get().deploymentJobs().jobStatus().size()); - + // prod zone removal is not allowed applicationPackage = new ApplicationPackageBuilder() .environment(Environment.prod) @@ -178,7 +179,7 @@ public class ControllerTest { 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()); + 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())); @@ -223,7 +224,7 @@ public class ControllerTest { assertEquals(expectedVersionString, ((Change.ApplicationChange) tester.controller().applications() .require(app1.id()) .deploying() - .get()).version().get().id()); + .get()).version().id()); // Deploy without application package tester.deployAndNotify(app1, true, systemTest); @@ -325,7 +326,7 @@ 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)); } - + @Test public void testDeployVersion() { // Setup system @@ -415,7 +416,7 @@ public class ControllerTest { controller.updateVersionStatus(new VersionStatus(versions)); return newSystemVersion; } - + @Test public void testPullRequestDeployment() { // Setup system @@ -427,7 +428,7 @@ public class ControllerTest { ApplicationId app1 = tester.createAndDeploy("tenant1", "domain1", "application1", Environment.staging, app1ProjectId).id(); - + // pull-request deployment - uses different instance id ApplicationId app1pr = tester.createAndDeploy("tenant1", "domain1", "application1", "default-pr1", @@ -461,7 +462,7 @@ public class ControllerTest { .filter(app -> app.id().application().equals(app2.application())) .count()); } - + @Test public void testFailingSinceUpdates() { // Setup system @@ -474,13 +475,13 @@ public class ControllerTest { Instant initialFailure = tester.clock().instant(); tester.notifyJobCompletion(component, app, true); tester.deployAndNotify(app, applicationPackage, false, systemTest); - assertEquals("Failure age is right at initial failure", + assertEquals("Failure age is right at initial failure", initialFailure.plus(Duration.ofMillis(2)), firstFailing(app, tester).get().at()); // Failure again -- failingSince should remain the same tester.clock().advance(Duration.ofMillis(1000)); tester.deployAndNotify(app, applicationPackage, false, systemTest); - assertEquals("Failure age is right at second consecutive failure", + assertEquals("Failure age is right at second consecutive failure", initialFailure.plus(Duration.ofMillis(2)), firstFailing(app, tester).get().at()); // Success resets failingSince @@ -491,27 +492,27 @@ public class ControllerTest { // Complete deployment tester.deployAndNotify(app, applicationPackage, true, stagingTest); tester.deployAndNotify(app, applicationPackage, true, productionCorpUsEast1); - + // Two repeated failures again. // Initial failure tester.clock().advance(Duration.ofMillis(1000)); initialFailure = tester.clock().instant(); tester.notifyJobCompletion(component, app, true); tester.deployAndNotify(app, applicationPackage, false, systemTest); - assertEquals("Failure age is right at initial failure", + assertEquals("Failure age is right at initial failure", initialFailure.plus(Duration.ofMillis(2)), firstFailing(app, tester).get().at()); // Failure again -- failingSince should remain the same tester.clock().advance(Duration.ofMillis(1000)); tester.deployAndNotify(app, applicationPackage, false, systemTest); - assertEquals("Failure age is right at second consecutive failure", + assertEquals("Failure age is right at second consecutive failure", initialFailure.plus(Duration.ofMillis(2)), firstFailing(app, tester).get().at()); } private Optional<JobStatus.JobRun> firstFailing(Application application, DeploymentTester tester) { return tester.controller().applications().get(application.id()).get().deploymentJobs().jobStatus().get(systemTest).firstFailing(); } - + @Test public void testMigratingTenantToAthenzWillModifyAthenzDomainsCorrectly() { ControllerTester tester = new ControllerTester(); 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 f42a4c1deb3..3b099d7e7ed 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 @@ -149,16 +149,16 @@ public class ApplicationSerializerTest { .from("hash1")))); Application serialized2 = applicationSerializer.fromSlime(applicationSerializer.toSlime(original2)); assertEquals(original2.deploying(), serialized2.deploying()); - assertEquals(((Change.ApplicationChange)serialized2.deploying().get()).version().get().source(), - ((Change.ApplicationChange)original2.deploying().get()).version().get().source()); + assertEquals(((Change.ApplicationChange)serialized2.deploying().get()).version().source(), + ((Change.ApplicationChange)original2.deploying().get()).version().source()); 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()).version().get().source(), - ((Change.ApplicationChange)original3.deploying().get()).version().get().source()); + assertEquals(((Change.ApplicationChange)serialized3.deploying().get()).version().source(), + ((Change.ApplicationChange)original3.deploying().get()).version().source()); Application original4 = writable(original).withDeploying(Optional.empty()); Application serialized4 = applicationSerializer.fromSlime(applicationSerializer.toSlime(original4)); @@ -210,7 +210,7 @@ public class ApplicationSerializerTest { Application application = applicationSerializer.fromSlime(applicationSlime(false)); assertFalse(application.deploymentJobs().jobStatus().get(DeploymentJobs.JobType.systemTest).lastCompleted().get().upgrade()); } - + @Test public void testCompleteApplicationDeserialization() { applicationSerializer.fromSlime(SlimeUtils.jsonToSlime(longApplicationJson.getBytes(StandardCharsets.UTF_8))); @@ -253,6 +253,6 @@ public class ApplicationSerializerTest { " }\n" + "}\n"; } - + private final String longApplicationJson = "{\"id\":\"tripod:service-aggregation-vespa:default\",\"deploymentSpecField\":\"<deployment version='1.0'>\\n <test />\\n <!--<staging />-->\\n <prod global-service-id=\\\"tripod\\\">\\n <region active=\\\"true\\\">us-east-3</region>\\n <region active=\\\"true\\\">us-west-1</region>\\n </prod>\\n</deployment>\\n\",\"validationOverrides\":\"<validation-overrides>\\n <allow until=\\\"2016-04-28\\\" comment=\\\"Renaming content cluster\\\">content-cluster-removal</allow>\\n <allow until=\\\"2016-08-22\\\" comment=\\\"Migrating us-east-3 to C-2E\\\">cluster-size-reduction</allow>\\n <allow until=\\\"2017-06-30\\\" comment=\\\"Test Vespa upgrade tests\\\">force-automatic-tenant-upgrade-test</allow>\\n</validation-overrides>\\n\",\"deployments\":[{\"zone\":{\"environment\":\"prod\",\"region\":\"us-west-1\"},\"version\":\"6.173.62\",\"deployTime\":1510837817704,\"applicationPackageRevision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"clusterInfo\":{\"tripod\":{\"flavor\":\"d-3-16-100\",\"cost\":9,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"container\",\"hostnames\":[\"oxy-oxygen-2001-4998-c-2942--10d1.gq1.yahoo.com\",\"oxy-oxygen-2001-4998-c-2942--10e2.gq1.yahoo.com\"]},\"tripodaggregation\":{\"flavor\":\"d-12-64-400\",\"cost\":38,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"content\",\"hostnames\":[\"oxy-oxygen-2001-4998-c-2941--106a.gq1.yahoo.com\",\"zt74700-v6-23.ostk.bm2.prod.gq1.yahoo.com\",\"zt74714-v6-28.ostk.bm2.prod.gq1.yahoo.com\",\"zt74730-v6-13.ostk.bm2.prod.gq1.yahoo.com\",\"zt74717-v6-7.ostk.bm2.prod.gq1.yahoo.com\",\"2080260-v6-12.ostk.bm2.prod.gq1.yahoo.com\",\"zt74719-v6-23.ostk.bm2.prod.gq1.yahoo.com\",\"zt74722-v6-26.ostk.bm2.prod.gq1.yahoo.com\",\"zt74704-v6-9.ostk.bm2.prod.gq1.yahoo.com\",\"oxy-oxygen-2001-4998-c-2942--107d.gq1.yahoo.com\"]},\"tripodaggregationstream\":{\"flavor\":\"d-12-64-400\",\"cost\":38,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"content\",\"hostnames\":[\"zt74727-v6-21.ostk.bm2.prod.gq1.yahoo.com\",\"zt74773-v6-8.ostk.bm2.prod.gq1.yahoo.com\",\"zt74699-v6-25.ostk.bm2.prod.gq1.yahoo.com\",\"zt74766-v6-27.ostk.bm2.prod.gq1.yahoo.com\"]}},\"clusterUtils\":{\"tripod\":{\"cpu\":0.1720353499228221,\"mem\":0.4986146831512451,\"disk\":0.0617671330041831,\"diskbusy\":0},\"tripodaggregation\":{\"cpu\":0.07505730001866318,\"mem\":0.7936344432830811,\"disk\":0.2260549694485994,\"diskbusy\":0},\"tripodaggregationstream\":{\"cpu\":0.01712671480989384,\"mem\":0.0225852754983035,\"disk\":0.006084436856721915,\"diskbusy\":0}},\"metrics\":{\"queriesPerSecond\":1.25,\"writesPerSecond\":43.83199977874756,\"documentCount\":525880277.9999999,\"queryLatencyMillis\":5.607503938674927,\"writeLatencyMillis\":20.57866265104621}},{\"zone\":{\"environment\":\"test\",\"region\":\"us-east-1\"},\"version\":\"6.173.62\",\"deployTime\":1511256872316,\"applicationPackageRevision\":{\"applicationPackageHash\":\"ec548fa61cbfab7a270a51d46b1263ec1be5d9a8\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"234f3e4e77049d0b9538c9e1b356d17eb1dedb6a\"}},\"clusterInfo\":{},\"clusterUtils\":{},\"metrics\":{\"queriesPerSecond\":0,\"writesPerSecond\":0,\"documentCount\":0,\"queryLatencyMillis\":0,\"writeLatencyMillis\":0}},{\"zone\":{\"environment\":\"dev\",\"region\":\"us-east-1\"},\"version\":\"6.173.62\",\"deployTime\":1510597489464,\"applicationPackageRevision\":{\"applicationPackageHash\":\"59b883f263c2a3c23dfab249730097d7e0e1ed32\"},\"clusterInfo\":{\"tripod\":{\"flavor\":\"d-2-8-50\",\"cost\":5,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"container\",\"hostnames\":[\"zt40807-v6-29.ostk.bm2.prod.bf1.yahoo.com\"]},\"tripodaggregation\":{\"flavor\":\"d-2-8-50\",\"cost\":5,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"content\",\"hostnames\":[\"zt40807-v6-24.ostk.bm2.prod.bf1.yahoo.com\"]},\"tripodaggregationstream\":{\"flavor\":\"d-2-8-50\",\"cost\":5,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"content\",\"hostnames\":[\"zt40694-v6-21.ostk.bm2.prod.bf1.yahoo.com\"]}},\"clusterUtils\":{\"tripod\":{\"cpu\":0.191833330678661,\"mem\":0.4625738318415235,\"disk\":0.05582004563850269,\"diskbusy\":0},\"tripodaggregation\":{\"cpu\":0.2227037978608054,\"mem\":0.2051752598416401,\"disk\":0.05471533698695047,\"diskbusy\":0},\"tripodaggregationstream\":{\"cpu\":0.1869410834020498,\"mem\":0.1691722576000564,\"disk\":0.04977374774258153,\"diskbusy\":0}},\"metrics\":{\"queriesPerSecond\":0,\"writesPerSecond\":0,\"documentCount\":30916,\"queryLatencyMillis\":0,\"writeLatencyMillis\":0}},{\"zone\":{\"environment\":\"prod\",\"region\":\"us-east-3\"},\"version\":\"6.173.62\",\"deployTime\":1510817190016,\"applicationPackageRevision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"clusterInfo\":{\"tripod\":{\"flavor\":\"d-3-16-100\",\"cost\":9,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"container\",\"hostnames\":[\"zt40738-v6-13.ostk.bm2.prod.bf1.yahoo.com\",\"zt40783-v6-31.ostk.bm2.prod.bf1.yahoo.com\"]},\"tripodaggregation\":{\"flavor\":\"d-12-64-400\",\"cost\":38,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"content\",\"hostnames\":[\"zt40819-v6-7.ostk.bm2.prod.bf1.yahoo.com\",\"zt40661-v6-3.ostk.bm2.prod.bf1.yahoo.com\",\"zt40805-v6-30.ostk.bm2.prod.bf1.yahoo.com\",\"zt40702-v6-32.ostk.bm2.prod.bf1.yahoo.com\",\"zt40706-v6-3.ostk.bm2.prod.bf1.yahoo.com\",\"zt40691-v6-27.ostk.bm2.prod.bf1.yahoo.com\",\"zt40676-v6-15.ostk.bm2.prod.bf1.yahoo.com\",\"zt40788-v6-23.ostk.bm2.prod.bf1.yahoo.com\",\"zt40782-v6-30.ostk.bm2.prod.bf1.yahoo.com\",\"zt40802-v6-32.ostk.bm2.prod.bf1.yahoo.com\"]},\"tripodaggregationstream\":{\"flavor\":\"d-12-64-400\",\"cost\":38,\"flavorCpu\":0,\"flavorMem\":0,\"flavorDisk\":0,\"clusterType\":\"content\",\"hostnames\":[\"zt40779-v6-27.ostk.bm2.prod.bf1.yahoo.com\",\"zt40791-v6-15.ostk.bm2.prod.bf1.yahoo.com\",\"zt40733-v6-31.ostk.bm2.prod.bf1.yahoo.com\",\"zt40724-v6-30.ostk.bm2.prod.bf1.yahoo.com\"]}},\"clusterUtils\":{\"tripod\":{\"cpu\":0.2295038983007097,\"mem\":0.4627357390237263,\"disk\":0.05559941525894966,\"diskbusy\":0},\"tripodaggregation\":{\"cpu\":0.05340429087579549,\"mem\":0.8107630891552372,\"disk\":0.226444914138854,\"diskbusy\":0},\"tripodaggregationstream\":{\"cpu\":0.02148227413975218,\"mem\":0.02162174219104161,\"disk\":0.006057760545243265,\"diskbusy\":0}},\"metrics\":{\"queriesPerSecond\":1.734000012278557,\"writesPerSecond\":44.59999895095825,\"documentCount\":525868193.9999999,\"queryLatencyMillis\":5.65284947195106,\"writeLatencyMillis\":17.34593812832452}}],\"deploymentJobs\":{\"projectId\":102889,\"jobStatus\":[{\"jobType\":\"staging-test\",\"lastTriggered\":{\"id\":-1,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"system-test completed\",\"at\":1510830134259},\"lastCompleted\":{\"id\":1184,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"system-test completed\",\"at\":1510830684960},\"lastSuccess\":{\"id\":1184,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"system-test completed\",\"at\":1510830684960}},{\"jobType\":\"component\",\"lastCompleted\":{\"id\":849,\"version\":\"6.174.156\",\"upgrade\":false,\"reason\":\"Application commit\",\"at\":1511217733555},\"lastSuccess\":{\"id\":849,\"version\":\"6.174.156\",\"upgrade\":false,\"reason\":\"Application commit\",\"at\":1511217733555}},{\"jobType\":\"production-us-east-3\",\"lastTriggered\":{\"id\":-1,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"staging-test completed\",\"at\":1510830685127},\"lastCompleted\":{\"id\":923,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"staging-test completed\",\"at\":1510837650046},\"lastSuccess\":{\"id\":923,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"staging-test completed\",\"at\":1510837650046}},{\"jobType\":\"production-us-west-1\",\"lastTriggered\":{\"id\":-1,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"production-us-east-3 completed\",\"at\":1510837650139},\"lastCompleted\":{\"id\":646,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"production-us-east-3 completed\",\"at\":1510843559162},\"lastSuccess\":{\"id\":646,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"production-us-east-3 completed\",\"at\":1510843559162}},{\"jobType\":\"system-test\",\"jobError\":\"unknown\",\"lastTriggered\":{\"id\":-1,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"ec548fa61cbfab7a270a51d46b1263ec1be5d9a8\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"234f3e4e77049d0b9538c9e1b356d17eb1dedb6a\"}},\"upgrade\":false,\"reason\":\"Available change in component\",\"at\":1511256608649},\"lastCompleted\":{\"id\":1686,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"ec548fa61cbfab7a270a51d46b1263ec1be5d9a8\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"234f3e4e77049d0b9538c9e1b356d17eb1dedb6a\"}},\"upgrade\":false,\"reason\":\"Available change in component\",\"at\":1511256603353},\"firstFailing\":{\"id\":1659,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"ec548fa61cbfab7a270a51d46b1263ec1be5d9a8\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"234f3e4e77049d0b9538c9e1b356d17eb1dedb6a\"}},\"upgrade\":false,\"reason\":\"component completed\",\"at\":1511219070725},\"lastSuccess\":{\"id\":1658,\"version\":\"6.173.62\",\"revision\":{\"applicationPackageHash\":\"9db423e1021d7b452d37ec6372bc757d9c1bda87\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"49cd7bbb1ed9f4b922083cb042590b0885ffe22b\"}},\"upgrade\":true,\"reason\":\"Upgrading to 6.173.62\",\"at\":1511175754163}}]},\"deployingField\":{\"applicationPackageHash\":\"ec548fa61cbfab7a270a51d46b1263ec1be5d9a8\",\"sourceRevision\":{\"repositoryField\":\"git@git.ouroath.com:Tripod/service-aggregation-vespa.git\",\"branchField\":\"origin/master\",\"commitField\":\"234f3e4e77049d0b9538c9e1b356d17eb1dedb6a\"}},\"outstandingChangeField\":false,\"queryQuality\":100,\"writeQuality\":99.99894341115082}"; } |