diff options
author | Jon Bratseth <jonbratseth@yahoo.com> | 2018-01-29 10:05:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-29 10:05:43 +0100 |
commit | 89e160f6924c41e182fad7af44a409bef8604440 (patch) | |
tree | 4144559af991b7072c3dbb6046ca884adfdb61dc | |
parent | 13b092d7ccb756bad38b7ee26fb781ace108e51c (diff) | |
parent | 3dc07725bd350e6d7e7cc84265b962e3faa5a0f2 (diff) |
Merge pull request #4790 from vespa-engine/bratseth/cleanup-change
Bratseth/cleanup change
23 files changed, 359 insertions, 393 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..d5ce613b98d 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 @@ -13,7 +13,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.application.ApplicationRotation; import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.Change; -import com.yahoo.vespa.hosted.controller.application.Change.VersionChange; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.rotation.RotationId; @@ -30,9 +29,9 @@ import java.util.stream.Collectors; /** * An instance of an application. - * + * * This is immutable. - * + * * @author bratseth */ public class Application { @@ -42,7 +41,7 @@ public class Application { private final ValidationOverrides validationOverrides; private final Map<ZoneId, Deployment> deployments; private final DeploymentJobs deploymentJobs; - private final Optional<Change> deploying; + private final Change change; private final boolean outstandingChange; private final Optional<IssueId> ownershipIssueId; private final ApplicationMetrics metrics; @@ -52,22 +51,22 @@ public class Application { public Application(ApplicationId id) { this(id, DeploymentSpec.empty, ValidationOverrides.empty, Collections.emptyMap(), new DeploymentJobs(Optional.empty(), Collections.emptyList(), Optional.empty()), - Optional.empty(), false, Optional.empty(), new ApplicationMetrics(0, 0), + Change.empty(), false, Optional.empty(), new ApplicationMetrics(0, 0), Optional.empty()); } /** Used from persistence layer: Do not use */ - public Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, - List<Deployment> deployments, DeploymentJobs deploymentJobs, Optional<Change> deploying, + public Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, + List<Deployment> deployments, DeploymentJobs deploymentJobs, Change change, 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); + deploymentJobs, change, outstandingChange, ownershipIssueId, metrics, rotation); } Application(ApplicationId id, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides, - Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Optional<Change> deploying, + Map<ZoneId, Deployment> deployments, DeploymentJobs deploymentJobs, Change change, boolean outstandingChange, Optional<IssueId> ownershipIssueId, ApplicationMetrics metrics, Optional<RotationId> rotation) { Objects.requireNonNull(id, "id cannot be null"); @@ -75,7 +74,7 @@ public class Application { Objects.requireNonNull(validationOverrides, "validationOverrides cannot be null"); Objects.requireNonNull(deployments, "deployments cannot be null"); Objects.requireNonNull(deploymentJobs, "deploymentJobs cannot be null"); - Objects.requireNonNull(deploying, "deploying cannot be null"); + Objects.requireNonNull(change, "change cannot be null"); Objects.requireNonNull(metrics, "metrics cannot be null"); Objects.requireNonNull(rotation, "rotation cannot be null"); this.id = id; @@ -83,7 +82,7 @@ public class Application { this.validationOverrides = validationOverrides; this.deployments = ImmutableMap.copyOf(deployments); this.deploymentJobs = deploymentJobs; - this.deploying = deploying; + this.change = change; this.outstandingChange = outstandingChange; this.ownershipIssueId = ownershipIssueId; this.metrics = metrics; @@ -91,24 +90,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,10 +120,10 @@ public class Application { public DeploymentJobs deploymentJobs() { return deploymentJobs; } /** - * Returns the change that is currently in the process of being deployed on this application, - * or empty if no change is currently being deployed. + * Returns the change that should currently be deployed for this application, + * which is empty when no change is in progress. */ - public Optional<Change> deploying() { return deploying; } + public Change change() { return change; } /** * Returns whether this has an outstanding change (in the source repository), which @@ -152,10 +151,7 @@ public class Application { /** Returns the version a new deployment to this zone should use for this application */ public Version deployVersionIn(ZoneId zone, Controller controller) { - if (deploying().isPresent() && deploying().get() instanceof VersionChange) - return ((Change.VersionChange) deploying().get()).version(); - - return versionIn(zone, controller); + return change.platform().orElse(versionIn(zone, controller)); } /** Returns the current version this application has, or if none; should use, in the given zone */ @@ -166,8 +162,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 (change().application().isPresent()) { + ApplicationVersion version = change().application().get(); + 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/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java index 89b134cc228..83c3d6a5d11 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 @@ -39,7 +39,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerato 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.ApplicationVersion; -import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobReport; @@ -292,7 +291,7 @@ public class ApplicationController { version = application.versionIn(zone, controller); } else if (canDeployDirectlyTo(zone, options)) { version = options.vespaVersion.map(Version::new).orElse(controller.systemVersion()); - } else if ( ! application.deploying().isPresent() && ! zone.environment().isManuallyDeployed()) { + } else if (! application.change().isPresent() && ! zone.environment().isManuallyDeployed()) { return unexpectedDeployment(applicationId, zone, applicationPackageFromDeployer); } else { version = application.deployVersionIn(zone, controller); @@ -322,9 +321,9 @@ public class ApplicationController { validate(applicationPackage.deploymentSpec()); // TODO: Remove after introducing new application version number - if (!options.deployCurrentVersion && applicationPackageFromDeployer.isPresent()) { - if (application.deploying().isPresent() && application.deploying().get() instanceof Change.ApplicationChange) { - application = application.withDeploying(Optional.of(Change.ApplicationChange.of(applicationVersion))); + if ( ! options.deployCurrentVersion && applicationPackageFromDeployer.isPresent()) { + if (application.change().application().isPresent()) { + application = application.withDeploying(application.change().with(applicationVersion)); } if (!canDeployDirectlyTo(zone, options) && jobType.isPresent()) { // Update with (potentially) missing information about what we triggered: @@ -333,10 +332,10 @@ public class ApplicationController { // for future use. JobStatus.JobRun triggering = getOrCreateTriggering(application, version, jobType.get()); application = application.withJobTriggering(jobType.get(), - application.deploying(), + application.change(), triggering.at(), version, - Optional.of(applicationVersion), + applicationVersion, triggering.reason()); } } @@ -359,9 +358,9 @@ public class ApplicationController { // Validate automated deployment if (!canDeployDirectlyTo(zone, options)) { - if (!application.deploymentJobs().isDeployableTo(zone.environment(), application.deploying())) { + if (!application.deploymentJobs().isDeployableTo(zone.environment(), application.change())) { throw new IllegalArgumentException("Rejecting deployment of " + application + " to " + zone + - " as " + application.deploying().get() + " is not tested"); + " as " + application.change() + " is not tested"); } Deployment existingDeployment = application.deployments().get(zone); if (zone.environment().isProduction() && existingDeployment != null && @@ -474,7 +473,7 @@ public class ApplicationController { } private JobStatus.JobRun incompleteTriggeringEvent(Version version) { - return new JobStatus.JobRun(-1, version, Optional.empty(), false, "", clock.instant()); + return new JobStatus.JobRun(-1, version, ApplicationVersion.unknown, false, "", clock.instant()); } private DeployOptions withVersion(Version version, DeployOptions options) { 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 b50ecb82c50..e744df0da68 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 @@ -64,8 +64,8 @@ public class LockedApplication extends Application { return new LockedApplication(new Builder(this).with(deploymentJobs().withCompletion(report, notificationTime, controller))); } - public LockedApplication withJobTriggering(JobType type, Optional<Change> change, Instant triggerTime, - Version version, Optional<ApplicationVersion> applicationVersion, + public LockedApplication withJobTriggering(JobType type, Change change, Instant triggerTime, + Version version, ApplicationVersion applicationVersion, String reason) { return new LockedApplication(new Builder(this).with(deploymentJobs().withTriggering(type, change, version, applicationVersion, reason, triggerTime))); } @@ -119,7 +119,7 @@ public class LockedApplication extends Application { return new LockedApplication(new Builder(this).with(validationOverrides)); } - public LockedApplication withDeploying(Optional<Change> deploying) { + public LockedApplication withDeploying(Change deploying) { return new LockedApplication(new Builder(this).withDeploying(deploying)); } @@ -166,7 +166,7 @@ public class LockedApplication extends Application { private ValidationOverrides validationOverrides; private Map<ZoneId, Deployment> deployments; private DeploymentJobs deploymentJobs; - private Optional<Change> deploying; + private Change deploying; private boolean hasOutstandingChange; private Optional<IssueId> ownershipIssueId; private ApplicationMetrics metrics; @@ -178,7 +178,7 @@ public class LockedApplication extends Application { this.validationOverrides = application.validationOverrides(); this.deployments = application.deployments(); this.deploymentJobs = application.deploymentJobs(); - this.deploying = application.deploying(); + this.deploying = application.change(); this.hasOutstandingChange = application.hasOutstandingChange(); this.ownershipIssueId = application.ownershipIssueId(); this.metrics = application.metrics(); @@ -205,7 +205,7 @@ public class LockedApplication extends Application { return this; } - private Builder withDeploying(Optional<Change> deploying) { + private Builder withDeploying(Change deploying) { this.deploying = deploying; return this; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java index f8c7319fabc..777cb4011b6 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java @@ -17,7 +17,7 @@ import java.util.stream.Stream; /** * A list of applications which can be filtered in various ways. - * + * * @author bratseth */ public class ApplicationList { @@ -27,9 +27,9 @@ public class ApplicationList { private ApplicationList(Iterable<Application> applications) { this.list = ImmutableList.copyOf(applications); } - + // ----------------------------------- Factories - + public static ApplicationList from(Iterable<Application> applications) { return new ApplicationList(applications); } @@ -54,7 +54,7 @@ public class ApplicationList { /** Returns the subset of applications which are currently upgrading (to any version) */ public ApplicationList upgrading() { - return listOf(list.stream().filter(ApplicationList::isUpgrading)); + return listOf(list.stream().filter(application -> application.change().platform().isPresent())); } /** Returns the subset of applications which are currently upgrading to the given version */ @@ -67,7 +67,7 @@ public class ApplicationList { return listOf(list.stream().filter(application -> ! isUpgradingTo(version, application))); } - /** + /** * Returns the subset of applications which are currently not upgrading to the given version, * or returns all if no version is specified */ @@ -78,7 +78,7 @@ public class ApplicationList { /** Returns the subset of applications which is currently not deploying a change */ public ApplicationList notDeploying() { - return listOf(list.stream().filter(application -> ! application.deploying().isPresent())); + return listOf(list.stream().filter(application -> ! application.change().isPresent())); } /** Returns the subset of applications which currently does not have any failing jobs */ @@ -125,7 +125,7 @@ public class ApplicationList { public ApplicationList without(UpgradePolicy policy) { return listOf(list.stream().filter(a -> a.deploymentSpec().upgradePolicy() != policy)); } - + /** Returns the subset of applications which have at least one deployment on a lower version than the given one */ public ApplicationList onLowerVersionThan(Version version) { return listOf(list.stream() @@ -134,7 +134,7 @@ public class ApplicationList { } /** - * Returns the subset of applications which are not pull requests: + * Returns the subset of applications which are not pull requests: * Pull requests changes the application instance name to (default-pr)?[pull-request-number] */ public ApplicationList notPullRequest() { @@ -170,16 +170,8 @@ public class ApplicationList { // ----------------------------------- Internal helpers - private static boolean isUpgrading(Application application) { - if ( ! (application.deploying().isPresent()) ) return false; - if ( ! (application.deploying().get() instanceof Change.VersionChange) ) return false; - return true; - } - private static boolean isUpgradingTo(Version version, Application application) { - if ( ! (application.deploying().isPresent()) ) return false; - if ( ! (application.deploying().get() instanceof Change.VersionChange) ) return false; - return ((Change.VersionChange)application.deploying().get()).version().equals(version); + return application.change().platform().equals(Optional.of(version)); } private static boolean failingOn(Version version, Application application) { 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..13d66c8d083 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 @@ -9,98 +9,90 @@ import java.util.Objects; import java.util.Optional; /** - * A change to an application - * + * The changes to an application we currently wish to complete deploying. + * A goal of the system is to deploy platform and application versions separately. + * However, this goal must some times be traded against others, so a change can + * consist of both an application and platform version change. + * + * This is immutable. + * * @author bratseth */ -public abstract class Change { +public final class Change { + + private static final Change empty = new Change(Optional.empty(), Optional.empty()); + + /** The platform version we are upgrading to, or empty if none */ + private final Optional<Version> platform; + + /** The application version we are changing to, or empty if none */ + private final Optional<ApplicationVersion> application; + + private Change(Optional<Version> platform, Optional<ApplicationVersion> application) { + Objects.requireNonNull(platform, "platform cannot be null"); + Objects.requireNonNull(application, "application cannot be null"); + this.platform = platform; + this.application = application; + } /** 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) { - 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; } - - @Override - public boolean blockedBy(DeploymentSpec deploymentSpec, Instant instant) { - return ! deploymentSpec.canChangeRevisionAt(instant); - } - - @Override - public int hashCode() { return version.hashCode(); } - - @Override - public boolean equals(Object other) { - if (this == other) return true; - if ( ! (other instanceof ApplicationChange)) return false; - 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()); - } - - public static ApplicationChange of(ApplicationVersion version) { - return new ApplicationChange(Optional.of(version)); - } - - @Override - public String toString() { - return "application change to " + version.map(ApplicationVersion::toString).orElse("an unknown version"); - } - + public boolean blockedBy(DeploymentSpec deploymentSpec, Instant instant) { + if (platform.isPresent() && ! deploymentSpec.canUpgradeAt(instant)) return true; + if (application.isPresent() && ! deploymentSpec.canChangeRevisionAt(instant)) return true; + return false; } - /** A change to the Vespa version running an application */ - public static class VersionChange extends Change { + /** Returns whether a change shoudl currently be deployed */ + public boolean isPresent() { + return platform.isPresent() || application.isPresent(); + } - private final Version version; + /** Returns the platform version change which should currently be deployed, if any */ + public Optional<Version> platform() { return platform; } - public VersionChange(Version version) { - Objects.requireNonNull(version, "version cannot be null"); - this.version = version; - } + /** Returns the application version change which should currently be deployed, if any */ + public Optional<ApplicationVersion> application() { return application; } - /** The Vespa version this changes to */ - public Version version() { return version; } + /** Returns an instance representing no change */ + public static Change empty() { return empty; } - @Override - public boolean blockedBy(DeploymentSpec deploymentSpec, Instant instant) { - return ! deploymentSpec.canUpgradeAt(instant); - } + /** Returns a version of this change which replaces or adds this application change */ + public Change with(ApplicationVersion applicationVersion) { + return new Change(platform, Optional.of(applicationVersion)); + } - @Override - public int hashCode() { return version.hashCode(); } + @Override + public int hashCode() { return Objects.hash(platform, application); } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if ( ! (other instanceof Change)) return false; + Change o = (Change)other; + if ( ! o.platform.equals(this.platform)) return false; + if ( ! o.application.equals(this.application)) return false; + return true; + } - @Override - public boolean equals(Object other) { - if (this == other) return true; - if ( ! (other instanceof VersionChange)) return false; - return ((VersionChange)other).version.equals(this.version); - } + @Override + public String toString() { + String platformString = platform.map(v -> "upgrade to " + v).orElse(null); + String applicationString = application.map(v -> "application change to " + v).orElse(null); + if (platformString != null && applicationString != null) + return platformString + " and " + applicationString; + if (platformString != null) + return platformString; + if (applicationString != null) + return applicationString; + return "no change"; + } - @Override - public String toString() { - return "version change to " + version; - } + public static Change of(ApplicationVersion applicationVersion) { + return new Change(Optional.empty(), Optional.of(applicationVersion)); + } + public static Change of(Version platformChange) { + return new Change(Optional.of(platformChange), Optional.empty()); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java index c432503a790..bb7b39eed0f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/DeploymentJobs.java @@ -65,9 +65,9 @@ public class DeploymentJobs { } public DeploymentJobs withTriggering(JobType jobType, - Optional<Change> change, + Change change, Version version, - Optional<ApplicationVersion> applicationVersion, + ApplicationVersion applicationVersion, String reason, Instant triggerTime) { Map<JobType, JobStatus> status = new LinkedHashMap<>(this.status); @@ -75,7 +75,7 @@ public class DeploymentJobs { if (job == null) job = JobStatus.initial(jobType); return job.withTriggering(version, applicationVersion, - change.isPresent() && change.get() instanceof Change.VersionChange, + change.platform().isPresent(), reason, triggerTime); }); @@ -117,14 +117,14 @@ public class DeploymentJobs { } /** Returns whether change can be deployed to the given environment */ - public boolean isDeployableTo(Environment environment, Optional<Change> change) { + public boolean isDeployableTo(Environment environment, Change change) { if (environment == null || ! change.isPresent()) { return true; } if (environment == Environment.staging) { - return isSuccessful(change.get(), JobType.systemTest); + return isSuccessful(change, JobType.systemTest); } else if (environment == Environment.prod) { - return isSuccessful(change.get(), JobType.stagingTest); + return isSuccessful(change, JobType.stagingTest); } return true; // other environments do not have any preconditions } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java index 932229b6bb6..41060a7af4c 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/JobList.java @@ -172,7 +172,7 @@ public class JobList { private static boolean failingApplicationChange(JobStatus job) { if ( job.isSuccess()) return false; if ( ! job.lastSuccess().isPresent()) return true; // An application which never succeeded is surely bad. - if ( ! job.lastSuccess().get().applicationVersion().isPresent()) return true; // Indicates the component job, which is always an application change. + if ( job.lastSuccess().get().applicationVersion() == ApplicationVersion.unknown) return true; // Indicates the component job, which is always an application change. if ( ! job.firstFailing().get().version().equals(job.lastSuccess().get().version())) return false; // Version change may be to blame. return ! job.firstFailing().get().applicationVersion().equals(job.lastSuccess().get().applicationVersion()); // Return whether there is an application change. } 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..e165d3c9fe5 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,7 +55,7 @@ public class JobStatus { return new JobStatus(type, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); } - public JobStatus withTriggering(Version version, Optional<ApplicationVersion> applicationVersion, + public JobStatus withTriggering(Version version, ApplicationVersion applicationVersion, boolean upgrade, String reason, Instant triggerTime) { return new JobStatus(type, jobError, Optional.of(new JobRun(-1, version, applicationVersion, upgrade, reason, triggerTime)), lastCompleted, firstFailing, lastSuccess); @@ -63,12 +63,12 @@ public class JobStatus { public JobStatus withCompletion(long runId, Optional<DeploymentJobs.JobError> jobError, Instant completionTime, Controller controller) { Version version; - Optional<ApplicationVersion> applicationVersion; + ApplicationVersion applicationVersion; boolean upgrade; String reason; if (type == DeploymentJobs.JobType.component) { // not triggered by us version = controller.systemVersion(); - applicationVersion = Optional.empty(); + applicationVersion = ApplicationVersion.unknown; upgrade = false; reason = "Application commit"; } @@ -167,13 +167,12 @@ public class JobStatus { private final long id; private final Version version; - // TODO: Make non-optional after introducing new application version number - private final Optional<ApplicationVersion> applicationVersion; + private final ApplicationVersion applicationVersion; private final boolean upgrade; private final String reason; private final Instant at; - public JobRun(long id, Version version, Optional<ApplicationVersion> applicationVersion, + public JobRun(long id, Version version, ApplicationVersion applicationVersion, boolean upgrade, String reason, Instant at) { Objects.requireNonNull(version, "version cannot be null"); Objects.requireNonNull(applicationVersion, "applicationVersion cannot be null"); @@ -199,7 +198,7 @@ public class JobStatus { public Version version() { return version; } /** Returns the application version used for this run, or empty when not known */ - public Optional<ApplicationVersion> applicationVersion() { return applicationVersion; } + public ApplicationVersion applicationVersion() { return applicationVersion; } /** Returns a human-readable reason for this particular job run */ public String reason() { return reason; } @@ -210,14 +209,9 @@ public class JobStatus { // TODO: Consider a version and application version for each JobStatus, to compare against a Target (instead of Change, which is, really, a Target). /** Returns whether the job last completed for the given change */ public boolean lastCompletedWas(Change change) { - if (change instanceof Change.ApplicationChange) { - Change.ApplicationChange applicationChange = (Change.ApplicationChange) change; - return applicationVersion().equals(applicationChange.version()); - } else if (change instanceof Change.VersionChange) { - Change.VersionChange versionChange = (Change.VersionChange) change; - return version().equals(versionChange.version()); - } - throw new IllegalArgumentException("Unexpected change: " + change.getClass()); + if (change.platform().isPresent() && ! change.platform().get().equals(version())) return false; + if (change.application().isPresent() && ! change.application().get().equals(applicationVersion)) return false; + return true; } @Override diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java index d506e8f3dcd..66bb05df308 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentOrder.java @@ -46,7 +46,7 @@ public class DeploymentOrder { /** Returns a list of jobs to trigger after the given job */ // TODO: This does too much - should just tell us the order, as advertised public List<JobType> nextAfter(JobType job, LockedApplication application) { - if ( ! application.deploying().isPresent()) { // Change was cancelled + if ( ! application.change().isPresent()) { // Change was cancelled return Collections.emptyList(); } 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..1beab1307c1 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 @@ -12,7 +12,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationList; import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.Change; -import com.yahoo.vespa.hosted.controller.application.Change.VersionChange; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobError; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobReport; @@ -87,17 +86,11 @@ public class DeploymentTrigger { if (report.jobType() == JobType.component) { if (acceptNewApplicationVersionNow(application)) { // Set this as the change we are doing, unless we are already pushing a platform change - if ( ! ( application.deploying().isPresent() && - application.deploying().get() instanceof Change.VersionChange)) { - Change.ApplicationChange applicationChange = Change.ApplicationChange.unknown(); - // TODO: Remove guard when source is always reported by component - if (report.sourceRevision().isPresent()) { - applicationChange = Change.ApplicationChange.of( - ApplicationVersion.from(report.sourceRevision().get(), - report.buildNumber()) - ); - } - application = application.withDeploying(Optional.of(applicationChange)); + if ( ! ( application.change().platform().isPresent())) { + ApplicationVersion applicationVersion = ApplicationVersion.unknown; + if (report.sourceRevision().isPresent()) + applicationVersion = ApplicationVersion.from(report.sourceRevision().get(), report.buildNumber()); + application = application.withDeploying(Change.of(applicationVersion)); } } else { // postpone @@ -107,7 +100,7 @@ public class DeploymentTrigger { } else if (deploymentComplete(application)) { // change completed - application = application.withDeploying(Optional.empty()); + application = application.withDeploying(Change.empty()); } } @@ -128,8 +121,8 @@ public class DeploymentTrigger { /** Returns whether all production zones listed in deployment spec has this change (or a newer version, if upgrade) */ private boolean deploymentComplete(LockedApplication application) { - if ( ! application.deploying().isPresent()) return true; - Change change = application.deploying().get(); + if ( ! application.change().isPresent()) return true; + Change change = application.change(); for (JobType job : order.jobsFrom(application.deploymentSpec())) { if ( ! job.isProduction()) continue; @@ -141,18 +134,15 @@ public class DeploymentTrigger { if (deployment == null) return false; // Check actual job outcome (the deployment) - if (change instanceof VersionChange) { - if (((VersionChange)change).version().isAfter(deployment.version())) return false; // later is ok + if (change.platform().isPresent()) { + if (change.platform().get().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; + if (change.application().isPresent()) { + // If we don't yet know the application version we are deploying, then we are not complete + if (change.application().get() == ApplicationVersion.unknown) return false; + if ( ! change.application().get().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; } @@ -168,14 +158,14 @@ public class DeploymentTrigger { /** Find the next step to trigger if any, and triggers it */ public void triggerReadyJobs(LockedApplication application) { - if ( ! application.deploying().isPresent()) return; + if ( ! application.change().isPresent()) return; List<JobType> jobs = order.jobsFrom(application.deploymentSpec()); // Should the first step be triggered? if ( ! jobs.isEmpty() && jobs.get(0).equals(JobType.systemTest) ) { JobStatus systemTestStatus = application.deploymentJobs().jobStatus().get(JobType.systemTest); - if (application.deploying().get() instanceof Change.VersionChange) { - Version target = ((Change.VersionChange) application.deploying().get()).version(); + if (application.change().platform().isPresent()) { + Version target = application.change().platform().get(); if (systemTestStatus == null || ! systemTestStatus.lastTriggered().isPresent() || ! systemTestStatus.isSuccess() @@ -218,13 +208,11 @@ public class DeploymentTrigger { * which is newer (different) than the one last completed successfully in next */ private boolean changesAvailable(Application application, JobStatus previous, JobStatus next) { - if ( ! application.deploying().isPresent()) return false; + if ( ! application.change().isPresent()) return false; if (next == null) return true; - Change change = application.deploying().get(); - - if (change instanceof Change.VersionChange) { // Propagate upgrade while making sure we never downgrade - Version targetVersion = ((Change.VersionChange)change).version(); + if (application.change().platform().isPresent()) { // Propagate upgrade while making sure we never downgrade + Version targetVersion = application.change().platform().get(); if (next.type().isTest()) { // Is it not yet this job's turn to upgrade? @@ -256,7 +244,7 @@ public class DeploymentTrigger { else { // Application version changes do not need to handle downgrading if ( ! previous.lastSuccess().isPresent()) return false; if ( ! next.lastSuccess().isPresent()) return true; - return previous.lastSuccess().get().applicationVersion().isPresent() && + return previous.lastSuccess().get().applicationVersion() != ApplicationVersion.unknown && ! previous.lastSuccess().get().applicationVersion().equals(next.lastSuccess().get().applicationVersion()); } } @@ -269,14 +257,13 @@ public class DeploymentTrigger { */ public void triggerChange(ApplicationId applicationId, Change change) { applications().lockOrThrow(applicationId, application -> { - if (application.deploying().isPresent() && ! application.deploymentJobs().hasFailures()) + if (application.change().isPresent() && ! application.deploymentJobs().hasFailures()) throw new IllegalArgumentException("Could not start " + change + " on " + application + ": " + - application.deploying().get() + " is already in progress"); - application = application.withDeploying(Optional.of(change)); - if (change instanceof Change.ApplicationChange) + application.change() + " is already in progress"); + application = application.withDeploying(change); + if (change.application().isPresent()) application = application.withOutstandingChange(false); - application = trigger(JobType.systemTest, application, false, - (change instanceof Change.VersionChange ? "Upgrading to " + ((Change.VersionChange)change).version() : "Deploying " + change)); + application = trigger(JobType.systemTest, application, false, change.toString()); applications().store(application); }); } @@ -289,7 +276,7 @@ public class DeploymentTrigger { public void cancelChange(ApplicationId applicationId) { applications().lockOrThrow(applicationId, application -> { buildSystem.removeJobs(application.id()); - applications().store(application.withDeploying(Optional.empty())); + applications().store(application.withDeploying(Change.empty())); }); } @@ -353,7 +340,7 @@ public class DeploymentTrigger { if (jobType == null) return application; // we are passed null when the last job has been reached // Never allow untested changes to go through // Note that this may happen because a new change catches up and prevents an older one from continuing - if ( ! application.deploymentJobs().isDeployableTo(jobType.environment(), application.deploying())) { + if ( ! application.deploymentJobs().isDeployableTo(jobType.environment(), application.change())) { log.warning(String.format("Want to trigger %s for %s with reason %s, but change is untested", jobType, application, reason)); return application; @@ -361,14 +348,14 @@ public class DeploymentTrigger { if ( ! force && ! allowedTriggering(jobType, application)) return application; log.info(String.format("Triggering %s for %s, %s: %s", jobType, application, - application.deploying().map(d -> "deploying " + d).orElse("restarted deployment"), + application.change().isPresent() ? "deploying " + application.change() : "restarted deployment", reason)); buildSystem.addJob(application.id(), jobType, first); return application.withJobTriggering(jobType, - application.deploying(), + application.change(), clock.instant(), application.deployVersionFor(jobType, controller), - application.deployApplicationVersion(jobType, controller), + application.deployApplicationVersion(jobType, controller).orElse(ApplicationVersion.unknown), reason); } @@ -379,12 +366,12 @@ public class DeploymentTrigger { // this leads to some additional corner cases, and the possibility of blocking an application // fix to a version upgrade, so not doing it now - if (jobType.isProduction() && application.deploying().isPresent() && - application.deploying().get().blockedBy(application.deploymentSpec(), clock.instant())) return false; + if (jobType.isProduction() && application.change().isPresent() && + application.change().blockedBy(application.deploymentSpec(), clock.instant())) return false; // Don't downgrade or redeploy the same version in production needlessly - if (application.deploying().isPresent() && application.deploying().get() instanceof VersionChange && - jobType.isProduction() && alreadyDeployed(((VersionChange) application.deploying().get()).version(), application, jobType)) return false; + if (application.change().platform().isPresent() && + jobType.isProduction() && alreadyDeployed((application.change().platform().get()), application, jobType)) return false; if (application.deploymentJobs().isRunning(jobType, jobTimeoutLimit())) return false; if ( ! hasJob(jobType, application)) return false; @@ -418,9 +405,9 @@ public class DeploymentTrigger { } private boolean acceptNewApplicationVersionNow(LockedApplication application) { - if ( ! application.deploying().isPresent()) return true; + if ( ! application.change().isPresent()) return true; - if (application.deploying().get() instanceof Change.ApplicationChange) return true; // more changes are ok + if (application.change().application().isPresent()) return true; // more application changes are ok if (application.deploymentJobs().hasFailures()) return true; // allow changes to fix upgrade problems diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java index 4485a603f61..3dd63a511e1 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java @@ -4,17 +4,18 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.application.ApplicationList; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.Change; import java.time.Duration; /** * Deploys application changes which have been postponed due to an ongoing upgrade - * + * * @author bratseth */ public class OutstandingChangeDeployer extends Maintainer { - + public OutstandingChangeDeployer(Controller controller, Duration interval, JobControl jobControl) { super(controller, interval, jobControl); } @@ -23,9 +24,9 @@ public class OutstandingChangeDeployer extends Maintainer { protected void maintain() { ApplicationList applications = ApplicationList.from(controller().applications().asList()).notPullRequest(); for (Application application : applications.asList()) { - if (application.hasOutstandingChange() && ! application.deploying().isPresent()) + if (application.hasOutstandingChange() && ! application.change().isPresent()) controller().applications().deploymentTrigger().triggerChange(application.id(), - Change.ApplicationChange.unknown()); + Change.of(ApplicationVersion.unknown)); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java index 5b87f9eaa86..75f348904dd 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java @@ -20,7 +20,7 @@ import java.util.logging.Logger; /** * Maintenance job which schedules applications for Vespa version upgrade - * + * * @author bratseth * @author mpolden */ @@ -39,7 +39,7 @@ public class Upgrader extends Maintainer { * Schedule application upgrades. Note that this implementation must be idempotent. */ @Override - public void maintain() { + public void maintain() { // Determine target versions for each upgrade policy Optional<Version> canaryTarget = controller().versionStatus().systemVersion().map(VespaVersion::versionNumber); Optional<Version> defaultTarget = newestVersionWithConfidence(VespaVersion.Confidence.normal); @@ -66,26 +66,25 @@ public class Upgrader extends Maintainer { defaultTarget.ifPresent(target -> upgrade(applications().with(UpgradePolicy.defaultPolicy), target)); conservativeTarget.ifPresent(target -> upgrade(applications().with(UpgradePolicy.conservative), target)); } - + private Optional<Version> newestVersionWithConfidence(VespaVersion.Confidence confidence) { return reversed(controller().versionStatus().versions()).stream() .filter(v -> v.confidence().equalOrHigherThan(confidence)) .findFirst() .map(VespaVersion::versionNumber); } - + private List<VespaVersion> reversed(List<VespaVersion> versions) { List<VespaVersion> reversed = new ArrayList<>(versions.size()); for (int i = 0; i < versions.size(); i++) reversed.add(versions.get(versions.size() - 1 - i)); return reversed; } - + /** Returns a list of all applications */ private ApplicationList applications() { return ApplicationList.from(controller().applications().asList()); } - + private void upgrade(ApplicationList applications, Version version) { - Change.VersionChange change = new Change.VersionChange(version); applications = applications.notPullRequest(); // Pull requests are deployed as separate applications to test then deleted; No need to upgrade applications = applications.hasProductionDeployment(); applications = applications.onLowerVersionThan(version); @@ -96,7 +95,7 @@ public class Upgrader extends Maintainer { applications = applications.first(numberOfApplicationsToUpgrade()); // throttle upgrades for (Application application : applications.asList()) { try { - controller().applications().deploymentTrigger().triggerChange(application.id(), change); + controller().applications().deploymentTrigger().triggerChange(application.id(), Change.of(version)); } catch (IllegalArgumentException e) { log.log(Level.INFO, "Could not trigger change: " + Exceptions.toMessageString(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 1514d98610a..652f95a2d13 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(); @@ -125,7 +125,7 @@ public class ApplicationSerializer { root.setString(validationOverridesField, application.validationOverrides().xmlForm()); deploymentsToSlime(application.deployments().values(), root.setArray(deploymentsField)); toSlime(application.deploymentJobs(), root.setObject(deploymentJobsField)); - toSlime(application.deploying(), root); + toSlime(application.change(), root); root.setBool(outstandingChangeField, application.hasOutstandingChange()); application.ownershipIssueId().ifPresent(issueId -> root.setString(ownershipIssueIdField, issueId.value())); root.setDouble(queryQualityField, application.metrics().queryServiceQuality()); @@ -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,40 +230,40 @@ 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); object.setLong(jobRunIdField, jobRun.get().id()); object.setString(versionField, jobRun.get().version().toString()); - if ( jobRun.get().applicationVersion().isPresent()) - toSlime(jobRun.get().applicationVersion().get(), object.setObject(revisionField)); + if ( jobRun.get().applicationVersion() != ApplicationVersion.unknown) + toSlime(jobRun.get().applicationVersion(), object.setObject(revisionField)); object.setBool(upgradeField, jobRun.get().upgrade()); object.setString(reasonField, jobRun.get().reason()); object.setLong(atField, jobRun.get().at().toEpochMilli()); } - - private void toSlime(Optional<Change> deploying, Cursor parentObject) { + + private void toSlime(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); + if (deploying.platform().isPresent()) + object.setString(versionField, deploying.platform().get().toString()); + if (deploying.application().isPresent() && deploying.application().get() != ApplicationVersion.unknown) + toSlime(deploying.application().get(), 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()); List<Deployment> deployments = deploymentsFromSlime(root.field(deploymentsField)); DeploymentJobs deploymentJobs = deploymentJobsFromSlime(root.field(deploymentJobsField)); - Optional<Change> deploying = changeFromSlime(root.field(deployingField)); + Change deploying = changeFromSlime(root.field(deployingField)); boolean outstandingChange = root.field(outstandingChangeField).asBool(); Optional<IssueId> ownershipIssueId = optionalString(root.field(ownershipIssueIdField)).map(IssueId::from); ApplicationMetrics metrics = new ApplicationMetrics(root.field(queryQualityField).asDouble(), @@ -282,7 +282,7 @@ public class ApplicationSerializer { private Deployment deploymentFromSlime(Inspector deploymentObject) { return new Deployment(zoneIdFromSlime(deploymentObject.field(zoneField)), - applicationVersionFromSlime(deploymentObject.field(applicationPackageRevisionField)).get(), + applicationVersionFromSlime(deploymentObject.field(applicationPackageRevisionField)), Version.fromString(deploymentObject.field(versionField).asString()), Instant.ofEpochMilli(deploymentObject.field(deployTimeField).asLong()), clusterUtilsMapFromSlime(deploymentObject.field(clusterUtilsField)), @@ -340,14 +340,14 @@ public class ApplicationSerializer { return ZoneId.from(object.field(environmentField).asString(), object.field(regionField).asString()); } - private Optional<ApplicationVersion> applicationVersionFromSlime(Inspector object) { - if ( ! object.valid()) return Optional.empty(); + private ApplicationVersion applicationVersionFromSlime(Inspector object) { + if ( ! object.valid()) return ApplicationVersion.unknown; String applicationPackageHash = object.field(applicationPackageHashField).asString(); Optional<SourceRevision> sourceRevision = sourceRevisionFromSlime(object.field(sourceRevisionField)); - return sourceRevision.isPresent() ? Optional.of(ApplicationVersion.from(applicationPackageHash, sourceRevision.get())) - : Optional.of(ApplicationVersion.from(applicationPackageHash)); + return sourceRevision.isPresent() ? ApplicationVersion.from(applicationPackageHash, sourceRevision.get()) + : ApplicationVersion.from(applicationPackageHash); } - + private Optional<SourceRevision> sourceRevisionFromSlime(Inspector object) { if ( ! object.valid()) return Optional.empty(); return Optional.of(new SourceRevision(object.field(repositoryField).asString(), @@ -363,23 +363,25 @@ public class ApplicationSerializer { return new DeploymentJobs(projectId, jobStatusList, issueId); } - private Optional<Change> changeFromSlime(Inspector object) { - if ( ! object.valid()) return Optional.empty(); + private Change changeFromSlime(Inspector object) { + if ( ! object.valid()) return Change.empty(); Inspector versionFieldValue = object.field(versionField); + Change change = Change.empty(); if (versionFieldValue.valid()) - return Optional.of(new Change.VersionChange(Version.fromString(versionFieldValue.asString()))); - else if (object.field(applicationPackageHashField).valid()) - return Optional.of(Change.ApplicationChange.of(applicationVersionFromSlime(object).get())); - else - return Optional.of(Change.ApplicationChange.unknown()); + change = Change.of(Version.fromString(versionFieldValue.asString())); + if (object.field(applicationPackageHashField).valid()) + change = change.with(applicationVersionFromSlime(object)); + if ( ! change.isPresent()) // A deploy object with no fields -> unknown application change + change = Change.of(ApplicationVersion.unknown); + return change; } - + 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..9b2174b881d 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 = @@ -348,12 +348,12 @@ public class ApplicationApiHandler extends LoggingRequestHandler { object.setString("application", application.id().application().value()); object.setString("instance", application.id().instance().value()); // Currently deploying change - if (application.deploying().isPresent()) { + if (application.change().isPresent()) { 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")); + application.change().platform().ifPresent(v -> deployingObject.setString("version", v.toString())); + application.change().application() + .filter(v -> v != ApplicationVersion.unknown) + .ifPresent(v -> toSlime(v, 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."); @@ -711,11 +711,11 @@ public class ApplicationApiHandler extends LoggingRequestHandler { ApplicationId id = ApplicationId.from(tenantName, applicationName, "default"); controller.applications().lockOrThrow(id, application -> { - if (application.deploying().isPresent()) + if (application.change().isPresent()) throw new IllegalArgumentException("Can not start a deployment of " + application + " at this time: " + - application.deploying().get() + " is in progress"); + application.change() + " is in progress"); - controller.applications().deploymentTrigger().triggerChange(application.id(), new Change.VersionChange(version)); + controller.applications().deploymentTrigger().triggerChange(application.id(), Change.of(version)); }); return new MessageResponse("Triggered deployment of application '" + id + "' on version " + version); } @@ -724,14 +724,14 @@ public class ApplicationApiHandler extends LoggingRequestHandler { private HttpResponse cancelDeploy(String tenantName, String applicationName) { ApplicationId id = ApplicationId.from(tenantName, applicationName, "default"); Application application = controller.applications().require(id); - Optional<Change> change = application.deploying(); + Change change = application.change(); if ( ! change.isPresent()) return new MessageResponse("No deployment in progress for " + application + " at this time"); controller.applications().lockOrThrow(id, lockedApplication -> controller.applications().deploymentTrigger().cancelChange(id)); - return new MessageResponse("Cancelled " + change.get() + " for " + application); + return new MessageResponse("Cancelled " + change + " for " + application); } /** Schedule restart of deployment, or specific host in a deployment */ @@ -970,8 +970,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler { private void toSlime(JobStatus.JobRun jobRun, Cursor object) { object.setLong("id", jobRun.id()); object.setString("version", jobRun.version().toFullString()); - jobRun.applicationVersion().ifPresent(applicationVersion -> toSlime(applicationVersion, - object.setObject("revision"))); + if (jobRun.applicationVersion() != ApplicationVersion.unknown) + toSlime(jobRun.applicationVersion(), object.setObject("revision")); object.setString("reason", jobRun.reason()); object.setLong("at", jobRun.at().toEpochMilli()); } 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..8d03b4f7121 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, + tester.controller().applications().require(app1.id()).change().application().get()); 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 = tester.controller().applications().require(app1.id()).change().application().get(); + 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())); @@ -220,22 +221,21 @@ public class ControllerTest { 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()); + assertEquals(expectedVersionString, tester.controller().applications() + .require(app1.id()) + .change().application().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))) + .withTriggering(version1, 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()); + .withTriggering(version1, expectedVersion, false, "", tester.clock().instant()), app1.id(), tester.controller()); tester.clock().advance(Duration.ofSeconds(1)); // production job (failing) @@ -243,7 +243,7 @@ public class ControllerTest { assertEquals(4, applications.require(app1.id()).deploymentJobs().jobStatus().size()); JobStatus expectedJobStatus = JobStatus.initial(productionCorpUsEast1) - .withTriggering(version1, Optional.of(expectedVersion), false, "", tester.clock().instant()) + .withTriggering(version1, expectedVersion, false, "", tester.clock().instant()) .withCompletion(42, Optional.of(JobError.unknown), tester.clock().instant(), tester.controller()); assertStatus(expectedJobStatus, app1.id(), tester.controller()); @@ -269,20 +269,20 @@ public class ControllerTest { 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))) + .withTriggering(version1, 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))) + .withTriggering(version1, 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()), + .withTriggering(version1, expectedVersion, false, "", tester.clock().instant()), app1.id(), tester.controller()); tester.deployAndNotify(app1, Optional.empty(), true, true, productionUsEast3); @@ -325,7 +325,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 @@ -377,11 +377,10 @@ public class ControllerTest { app1 = applications.require(app1.id()); assertEquals("Application change preserves version", systemVersion, app1.oldestDeployedVersion().get()); assertEquals(systemVersion, tester.configServer().lastPrepareVersion().get()); - assertFalse("Change deployed", app1.deploying().isPresent()); + assertFalse("Change deployed", app1.change().isPresent()); // Version upgrade changes system version - Change.VersionChange change = new Change.VersionChange(newSystemVersion); - applications.deploymentTrigger().triggerChange(app1.id(), change); + applications.deploymentTrigger().triggerChange(app1.id(), Change.of(newSystemVersion)); tester.deployAndNotify(app1, applicationPackage, true, systemTest); tester.deployAndNotify(app1, applicationPackage, true, stagingTest); tester.deployAndNotify(app1, applicationPackage, true, productionUsWest1); @@ -415,7 +414,7 @@ public class ControllerTest { controller.updateVersionStatus(new VersionStatus(versions)); return newSystemVersion; } - + @Test public void testPullRequestDeployment() { // Setup system @@ -427,7 +426,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 +460,7 @@ public class ControllerTest { .filter(app -> app.id().application().equals(app2.application())) .count()); } - + @Test public void testFailingSinceUpdates() { // Setup system @@ -474,13 +473,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 +490,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(); @@ -669,13 +668,13 @@ public class ControllerTest { Application app = tester.createApplication(tenant, "app1", "default", 1); tester.controller().applications().lockOrThrow(app.id(), application -> { - application = application.withDeploying(Optional.of(new Change.VersionChange(Version.fromString("6.3")))); + application = application.withDeploying(Change.of(Version.fromString("6.3"))); applications.store(application); try { tester.deploy(app, ZoneId.from("prod", "us-east-3")); fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("Rejecting deployment of application 'tenant1.app1' to zone prod.us-east-3 as version change to 6.3 is not tested", e.getMessage()); + assertEquals("Rejecting deployment of application 'tenant1.app1' to zone prod.us-east-3 as upgrade to 6.3 is not tested", e.getMessage()); } }); } 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 8de32b4b531..d0dfe825558 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 @@ -18,8 +18,8 @@ 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.ReadyJobsTrigger; import com.yahoo.vespa.hosted.controller.maintenance.Upgrader; import com.yahoo.vespa.hosted.controller.versions.VersionStatus; @@ -94,12 +94,6 @@ public class DeploymentTester { return controller().applications().require(application); } - public Optional<Change.VersionChange> versionChange(ApplicationId application) { - return application(application).deploying() - .filter(c -> c instanceof Change.VersionChange) - .map(Change.VersionChange.class::cast); - } - public void updateVersionStatus() { controller().updateVersionStatus(VersionStatus.compute(controller(), tester.controller().systemVersion())); } @@ -154,7 +148,7 @@ public class DeploymentTester { /** Deploy application completely using the given application package */ public void deployCompletely(Application application, ApplicationPackage applicationPackage) { notifyJobCompletion(JobType.component, application, true); - assertTrue(applications().require(application.id()).deploying().isPresent()); + assertTrue(applications().require(application.id()).change().isPresent()); completeDeployment(application, applicationPackage, Optional.empty(), true); } @@ -178,7 +172,7 @@ public class DeploymentTester { /** Deploy application using the given application package, but expecting to stop after test phases */ public void deployTestOnly(Application application, ApplicationPackage applicationPackage) { notifyJobCompletion(JobType.component, application, true); - assertTrue(applications().require(application.id()).deploying().isPresent()); + assertTrue(applications().require(application.id()).change().isPresent()); completeDeployment(application, applicationPackage, Optional.empty(), false); } @@ -196,13 +190,13 @@ public class DeploymentTester { } } if (failOnJob.isPresent()) { - assertTrue(applications().require(application.id()).deploying().isPresent()); + assertTrue(applications().require(application.id()).change().isPresent()); assertTrue(applications().require(application.id()).deploymentJobs().hasFailures()); } else if (includingProductionZones) { - assertFalse(applications().require(application.id()).deploying().isPresent()); + assertFalse(applications().require(application.id()).change().isPresent()); } else { - assertTrue(applications().require(application.id()).deploying().isPresent()); + assertTrue(applications().require(application.id()).change().isPresent()); } } @@ -225,8 +219,8 @@ public class DeploymentTester { } public void completeUpgrade(Application application, Version version, ApplicationPackage applicationPackage) { - assertTrue(application + " has a deployment", applications().require(application.id()).deploying().isPresent()); - assertEquals(new Change.VersionChange(version), applications().require(application.id()).deploying().get()); + assertTrue(application + " has a deployment", applications().require(application.id()).change().isPresent()); + assertEquals(Change.of(version), applications().require(application.id()).change()); completeDeployment(application, applicationPackage, Optional.empty(), true); } @@ -239,8 +233,8 @@ public class DeploymentTester { } private void completeUpgradeWithError(Application application, Version version, ApplicationPackage applicationPackage, Optional<JobType> failOnJob) { - assertTrue(applications().require(application.id()).deploying().isPresent()); - assertEquals(new Change.VersionChange(version), applications().require(application.id()).deploying().get()); + assertTrue(applications().require(application.id()).change().isPresent()); + assertEquals(Change.of(version), applications().require(application.id()).change()); completeDeployment(application, applicationPackage, failOnJob, true); } 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 74d03240ec3..5c61e43f9cf 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 @@ -13,13 +13,12 @@ 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.maintenance.ReadyJobsTrigger; import com.yahoo.vespa.hosted.controller.maintenance.JobControl; +import com.yahoo.vespa.hosted.controller.maintenance.ReadyJobsTrigger; import org.junit.Test; import java.time.Duration; import java.time.Instant; -import java.util.Optional; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -221,13 +220,13 @@ public class DeploymentTriggerTest { tester.deploy(DeploymentJobs.JobType.productionUsWest1, app, applicationPackage); tester.notifyJobCompletion(DeploymentJobs.JobType.productionUsWest1, app, true); assertTrue("Change is present as not all jobs are complete", - tester.applications().require(app.id()).deploying().isPresent()); + tester.applications().require(app.id()).change().isPresent()); // All jobs complete tester.deploy(DeploymentJobs.JobType.productionUsEast3, app, applicationPackage); tester.notifyJobCompletion(DeploymentJobs.JobType.productionUsEast3, app, true); assertFalse("Change has been deployed", - tester.applications().require(app.id()).deploying().isPresent()); + tester.applications().require(app.id()).change().isPresent()); } @Test @@ -321,7 +320,7 @@ public class DeploymentTriggerTest { new JobControl(tester.controllerTester().curator())); LockedApplication app = (LockedApplication)tester.createAndDeploy("default0", 3, "default"); // Store that we are upgrading but don't start the system-tests job - tester.controller().applications().store(app.withDeploying(Optional.of(new Change.VersionChange(Version.fromString("6.2"))))); + tester.controller().applications().store(app.withDeploying(Change.of(Version.fromString("6.2")))); assertEquals(0, tester.buildSystem().jobs().size()); readyJobsTrigger.run(); assertEquals(1, tester.buildSystem().jobs().size()); @@ -350,7 +349,7 @@ public class DeploymentTriggerTest { // Extra notification for last job tester.notifyJobCompletion(JobType.productionCorpUsEast1, application, true); assertFalse("Change has been deployed", - tester.applications().require(application.id()).deploying().isPresent()); + tester.applications().require(application.id()).change().isPresent()); assertTrue("All jobs consumed", buildSystem.jobs().isEmpty()); } 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 b200e2d7e18..11e85c6be9f 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 @@ -58,7 +58,7 @@ public class FailureRedeployerTest { tester.clock().advance(Duration.ofSeconds(1)); // Advance time so that we can detect jobs in progress tester.deployAndNotify(app, applicationPackage, false, DeploymentJobs.JobType.productionUsEast3); assertEquals("Production job is retried", 1, tester.buildSystem().jobs().size()); - assertEquals("Application has pending upgrade to " + version, version, tester.versionChange(app.id()).get().version()); + assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get()); // Another version is released, which cancels any pending upgrades to lower versions version = Version.fromString("5.2"); @@ -66,7 +66,7 @@ public class FailureRedeployerTest { tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsEast3); // Finish previous production job. tester.upgrader().maintain(); assertEquals("Application starts upgrading to new version", 1, tester.buildSystem().jobs().size()); - assertEquals("Application has pending upgrade to " + version, version, tester.versionChange(app.id()).get().version()); + assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get()); // Failure redeployer does not retry failing job for prod.us-east-3 as there's an ongoing deployment tester.clock().advance(Duration.ofMinutes(1)); @@ -150,7 +150,7 @@ public class FailureRedeployerTest { tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); tester.upgrader().maintain(); - assertEquals("Application has pending upgrade to " + version, version, tester.versionChange(app.id()).get().version()); + assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get()); // system-test fails and exhausts all immediate retries tester.deployAndNotify(app, applicationPackage, false, DeploymentJobs.JobType.systemTest); @@ -164,7 +164,7 @@ public class FailureRedeployerTest { tester.updateVersionStatus(version); assertEquals(version, tester.controller().versionStatus().systemVersion().get().versionNumber()); tester.upgrader().maintain(); - assertEquals("Application has pending upgrade to " + version, version, tester.versionChange(app.id()).get().version()); + assertEquals("Application has pending upgrade to " + version, version, tester.application(app.id()).change().platform().get()); // Consume system-test job for 5.2 tester.buildSystem().takeJobsToRun(); @@ -225,12 +225,12 @@ public class FailureRedeployerTest { // Deployment notifies completeness but has not actually made a deployment tester.notifyJobCompletion(DeploymentJobs.JobType.productionCdUsCentral1, application, true); - assertTrue("Change not really deployed", tester.application(application.id()).deploying().isPresent()); + assertTrue("Change not really deployed", tester.application(application.id()).change().isPresent()); // Deployment actually deploys and notifies completeness tester.deploy(DeploymentJobs.JobType.productionCdUsCentral1, application, applicationPackage); tester.notifyJobCompletion(DeploymentJobs.JobType.productionCdUsCentral1, application, true); - assertFalse("Change not really deployed", tester.application(application.id()).deploying().isPresent()); + assertFalse("Change not really deployed", tester.application(application.id()).change().isPresent()); } @Test diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java index 13636122cfd..3d34e78c759 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java @@ -20,28 +20,28 @@ import static org.junit.Assert.assertTrue; * @author bratseth */ public class OutstandingChangeDeployerTest { - + @Test public void testChangeDeployer() { DeploymentTester tester = new DeploymentTester(); tester.configServer().setDefaultVersion(new Version(6, 1)); - OutstandingChangeDeployer deployer = new OutstandingChangeDeployer(tester.controller(), Duration.ofMinutes(10), + OutstandingChangeDeployer deployer = new OutstandingChangeDeployer(tester.controller(), Duration.ofMinutes(10), new JobControl(new MockCuratorDb())); tester.createAndDeploy("app1", 11, "default"); tester.createAndDeploy("app2", 22, "default"); Version version = new Version(6, 2); - tester.deploymentTrigger().triggerChange(tester.application("app1").id(), new Change.VersionChange(version)); - - assertEquals(new Change.VersionChange(version), tester.application("app1").deploying().get()); + tester.deploymentTrigger().triggerChange(tester.application("app1").id(), Change.of(version)); + + assertEquals(Change.of(version), tester.application("app1").change()); assertFalse(tester.application("app1").hasOutstandingChange()); tester.notifyJobCompletion(DeploymentJobs.JobType.component, tester.application("app1"), true); assertTrue(tester.application("app1").hasOutstandingChange()); assertEquals(1, tester.buildSystem().jobs().size()); - + deployer.maintain(); assertEquals("No effect as job is in progress", 1, tester.buildSystem().jobs().size()); - + tester.deployCompletely("app1"); assertEquals("Upgrade done", 0, tester.buildSystem().jobs().size()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java index fd13b99f25e..ccc029a9654 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java @@ -9,7 +9,6 @@ import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ControllerTester; 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.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -174,11 +173,11 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.normal, tester.controller().versionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); assertEquals("Upgrade of defaults are scheduled", 5, tester.buildSystem().jobs().size()); - assertEquals(version54, ((Change.VersionChange)tester.application(default0.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default1.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default2.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default3.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default4.id()).deploying().get()).version()); + assertEquals(version54, tester.application(default0.id()).change().platform().get()); + assertEquals(version54, tester.application(default1.id()).change().platform().get()); + assertEquals(version54, tester.application(default2.id()).change().platform().get()); + assertEquals(version54, tester.application(default3.id()).change().platform().get()); + assertEquals(version54, tester.application(default4.id()).change().platform().get()); tester.completeUpgrade(default0, version54, "default"); // State: Default applications started upgrading to 5.4 (and one completed) Version version55 = Version.fromString("5.5"); @@ -190,11 +189,11 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.normal, tester.controller().versionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); assertEquals("Upgrade of defaults are scheduled", 5, tester.buildSystem().jobs().size()); - assertEquals(version55, ((Change.VersionChange)tester.application(default0.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default1.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default2.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default3.id()).deploying().get()).version()); - assertEquals(version54, ((Change.VersionChange)tester.application(default4.id()).deploying().get()).version()); + assertEquals(version55, tester.application(default0.id()).change().platform().get()); + assertEquals(version54, tester.application(default1.id()).change().platform().get()); + assertEquals(version54, tester.application(default2.id()).change().platform().get()); + assertEquals(version54, tester.application(default3.id()).change().platform().get()); + assertEquals(version54, tester.application(default4.id()).change().platform().get()); tester.completeUpgrade(default1, version54, "default"); tester.completeUpgrade(default2, version54, "default"); tester.completeUpgradeWithError(default3, version54, "default", DeploymentJobs.JobType.stagingTest); @@ -216,7 +215,7 @@ public class UpgraderTest { assertEquals("Upgrade of defaults are scheduled on 5.4 instead, since 5.5 broken: " + "This is default3 since it failed upgrade on both 5.4 and 5.5", 1, tester.buildSystem().jobs().size()); - assertEquals("5.4", ((Change.VersionChange)tester.application(default3.id()).deploying().get()).version().toString()); + assertEquals("5.4", tester.application(default3.id()).change().platform().get().toString()); } @Test @@ -320,7 +319,7 @@ public class UpgraderTest { tester.notifyJobCompletion(DeploymentJobs.JobType.stagingTest, app, false); assertTrue("Retries exhausted", tester.buildSystem().jobs().isEmpty()); assertTrue("Failure is recorded", tester.application(app.id()).deploymentJobs().hasFailures()); - assertTrue("Application has pending change", tester.application(app.id()).deploying().isPresent()); + assertTrue("Application has pending change", tester.application(app.id()).change().isPresent()); // New version is released version = Version.fromString("5.2"); @@ -379,7 +378,7 @@ public class UpgraderTest { tester.upgrader().maintain(); // 5th app passes system-test, but does not trigger next job as upgrade is cancelled - assertFalse("No change present", tester.applications().require(default4.id()).deploying().isPresent()); + assertFalse("No change present", tester.applications().require(default4.id()).change().isPresent()); tester.notifyJobCompletion(DeploymentJobs.JobType.systemTest, default4, true); assertTrue("All jobs consumed", tester.buildSystem().jobs().isEmpty()); } @@ -476,7 +475,7 @@ public class UpgraderTest { assertEquals(v2, tester.application("default0").deployments().get(ZoneId.from("prod.us-west-1")).version()); assertEquals("Last zone is upgraded to v1", v1, tester.application("default0").deployments().get(ZoneId.from("prod.us-east-3")).version()); - assertFalse(tester.application("default0").deploying().isPresent()); + assertFalse(tester.application("default0").change().isPresent()); } @Test @@ -747,7 +746,7 @@ public class UpgraderTest { // 5th app never reports back and has a dead job, but no ongoing change Application deadLocked = tester.applications().require(default4.id()); assertTrue("Jobs in progress", deadLocked.deploymentJobs().isRunning(tester.controller().applications().deploymentTrigger().jobTimeoutLimit())); - assertFalse("No change present", deadLocked.deploying().isPresent()); + assertFalse("No change present", deadLocked.change().isPresent()); // 4 out of 5 applications are repaired and confidence is restored ApplicationPackage defaultApplicationPackageV2 = new ApplicationPackageBuilder() 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..ffb9ee57351 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 @@ -70,10 +70,10 @@ public class ApplicationSerializerTest { List<JobStatus> statusList = new ArrayList<>(); statusList.add(JobStatus.initial(DeploymentJobs.JobType.systemTest) - .withTriggering(Version.fromString("5.6.7"), Optional.empty(), true, "Test", Instant.ofEpochMilli(7)) + .withTriggering(Version.fromString("5.6.7"), ApplicationVersion.unknown, true, "Test", Instant.ofEpochMilli(7)) .withCompletion(30, Optional.empty(), Instant.ofEpochMilli(8), tester.controller())); statusList.add(JobStatus.initial(DeploymentJobs.JobType.stagingTest) - .withTriggering(Version.fromString("5.6.6"), Optional.empty(), true, "Test 2", Instant.ofEpochMilli(5)) + .withTriggering(Version.fromString("5.6.6"), ApplicationVersion.unknown, true, "Test 2", Instant.ofEpochMilli(5)) .withCompletion(11, Optional.of(JobError.unknown), Instant.ofEpochMilli(6), tester.controller())); DeploymentJobs deploymentJobs = new DeploymentJobs(projectId, statusList, Optional.empty()); @@ -82,7 +82,7 @@ public class ApplicationSerializerTest { deploymentSpec, validationOverrides, deployments, deploymentJobs, - Optional.of(new Change.VersionChange(Version.fromString("6.7"))), + Change.of(Version.fromString("6.7")), true, Optional.of(IssueId.from("1234")), new MetricsService.ApplicationMetrics(0.5, 0.9), @@ -114,7 +114,7 @@ public class ApplicationSerializerTest { assertEquals(original.ownershipIssueId(), serialized.ownershipIssueId()); - assertEquals(original.deploying(), serialized.deploying()); + assertEquals(original.change(), serialized.change()); assertEquals(original.rotation().get().id(), serialized.rotation().get().id()); // Test cluster utilization @@ -145,24 +145,22 @@ 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(ApplicationVersion - .from("hash1")))); + Application original2 = writable(original).withDeploying(Change.of(ApplicationVersion.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(original2.change(), serialized2.change()); + assertEquals(serialized2.change().application().get().source(), + original2.change().application().get().source()); - Application original3 = writable(original).withDeploying(Optional.of(Change.ApplicationChange.of(ApplicationVersion - .from("hash1", - new SourceRevision("a", "b", "c"))))); + Application original3 = writable(original).withDeploying(Change.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(original3.change(), serialized2.change()); + assertEquals(serialized3.change().application().get().source(), + original3.change().application().get().source()); - Application original4 = writable(original).withDeploying(Optional.empty()); + Application original4 = writable(original).withDeploying(Change.empty()); Application serialized4 = applicationSerializer.fromSlime(applicationSerializer.toSlime(original4)); - assertEquals(original4.deploying(), serialized4.deploying()); + assertEquals(original4.change(), serialized4.change()); } } @@ -210,7 +208,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 +251,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}"; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-deployment-cancelled.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-deployment-cancelled.json index d1e1ebe94fd..3b6d8ed71e9 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-deployment-cancelled.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-deployment-cancelled.json @@ -1 +1 @@ -{"message":"Cancelled version change to 6.1 for application 'tenant1.application1'"} +{"message":"Cancelled upgrade to 6.1 for application 'tenant1.application1'"} |