diff options
author | jonmv <venstad@gmail.com> | 2022-04-11 21:34:42 +0200 |
---|---|---|
committer | jonmv <venstad@gmail.com> | 2022-04-11 21:38:00 +0200 |
commit | 683c74c6baa349be93c3d878638214458b8e2bb4 (patch) | |
tree | 6fca498336b09a95a7d897b614e5f79567d4a6b0 | |
parent | 27af44b14a95177126ec4c1cd3deaf9a1fb4c6e5 (diff) |
Use RevisionId instead of ApplicationVersion as keys
59 files changed, 746 insertions, 721 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationStore.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationStore.java index fef95155440..c4db0de539e 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationStore.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationStore.java @@ -16,8 +16,8 @@ import java.util.Optional; */ public interface ApplicationStore { - /** Returns the tenant application package of the given version. */ - byte[] get(DeploymentId deploymentId, ApplicationVersion applicationVersion); + /** Returns the application package of the given revision. */ + byte[] get(DeploymentId deploymentId, RevisionId revisionId); /** Returns the application package diff, compared to the previous build, for the given tenant, application and build number */ Optional<byte[]> getDiff(TenantName tenantName, ApplicationName applicationName, long buildNumber); @@ -34,16 +34,16 @@ public interface ApplicationStore { } /** Stores the given tenant application and test packages of the given revision, and diff since previous version. */ - void put(TenantName tenant, ApplicationName application, ApplicationVersion applicationVersion, byte[] applicationPackage, byte[] testPackage, byte[] diff); + void put(TenantName tenant, ApplicationName application, RevisionId revision, byte[] applicationPackage, byte[] testPackage, byte[] diff); - /** Removes application and test packages older than the given version, for the given application. */ - void prune(TenantName tenant, ApplicationName application, ApplicationVersion olderThanVersion); + /** Removes application and test packages older than the given revision, for the given application. */ + void prune(TenantName tenant, ApplicationName application, RevisionId revision); /** Removes all application and test packages for the given application, including any development package. */ void removeAll(TenantName tenant, ApplicationName application); - /** Returns the tester application package of the given version. Does NOT contain the services.xml. */ - byte[] getTester(TenantName tenant, ApplicationName application, ApplicationVersion applicationVersion); + /** Returns the tester application package of the given revision. Does NOT contain the services.xml. */ + byte[] getTester(TenantName tenant, ApplicationName application, RevisionId revision); /** Returns the application package diff, compared to the previous build, for the given deployment and build number */ Optional<byte[]> getDevDiff(DeploymentId deploymentId, long buildNumber); @@ -51,8 +51,8 @@ public interface ApplicationStore { /** Removes diffs for dev packages before the given build number */ void pruneDevDiffs(DeploymentId deploymentId, long beforeBuildNumber); - /** Stores the given application package as the development package for the given deployment and version and diff since previous version. */ - void putDev(DeploymentId deploymentId, ApplicationVersion version, byte[] applicationPackage, byte[] diff); + /** Stores the given application package as the development package for the given deployment and revision and diff since previous version. */ + void putDev(DeploymentId deploymentId, RevisionId revision, byte[] applicationPackage, byte[] diff); /** Stores the given application metadata with the current time as part of the path. */ void putMeta(TenantName tenant, ApplicationName application, Instant now, byte[] metaZip); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationVersion.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationVersion.java index 29d7005ddc7..445ca0435d4 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationVersion.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/ApplicationVersion.java @@ -20,36 +20,26 @@ import static ai.vespa.validation.Validation.requireAtLeast; */ public class ApplicationVersion implements Comparable<ApplicationVersion> { - /** Should not be used, but may still exist in serialized data :S */ - public static final ApplicationVersion unknown = new ApplicationVersion(Optional.empty(), OptionalLong.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), true, Optional.empty(), false, true, Optional.empty(), 0); - // This never changes and is only used to create a valid semantic version number, as required by application bundles private static final String majorVersion = "1.0"; + private final RevisionId id; private final Optional<SourceRevision> source; private final Optional<String> authorEmail; - private final OptionalLong buildNumber; private final Optional<Version> compileVersion; private final Optional<Instant> buildTime; private final Optional<String> sourceUrl; private final Optional<String> commit; - private final boolean deployedDirectly; private final Optional<String> bundleHash; private final boolean hasPackage; private final boolean shouldSkip; private final Optional<String> description; private final int risk; - public ApplicationVersion(Optional<SourceRevision> source, OptionalLong buildNumber, Optional<String> authorEmail, + public ApplicationVersion(RevisionId id, Optional<SourceRevision> source, Optional<String> authorEmail, Optional<Version> compileVersion, Optional<Instant> buildTime, Optional<String> sourceUrl, - Optional<String> commit, boolean deployedDirectly, Optional<String> bundleHash, + Optional<String> commit, Optional<String> bundleHash, boolean hasPackage, boolean shouldSkip, Optional<String> description, int risk) { - if (buildNumber.isEmpty() && ( source.isPresent() || authorEmail.isPresent() || compileVersion.isPresent() - || buildTime.isPresent() || sourceUrl.isPresent() || commit.isPresent())) - throw new IllegalArgumentException("Build number must be present if any other attribute is"); - - if (buildNumber.isPresent() && buildNumber.getAsLong() <= 0) - throw new IllegalArgumentException("Build number must be > 0"); if (commit.isPresent() && commit.get().length() > 128) throw new IllegalArgumentException("Commit may not be longer than 128 characters"); @@ -60,14 +50,13 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { if (compileVersion.isPresent() && compileVersion.get().equals(Version.emptyVersion)) throw new IllegalArgumentException("The empty version is not a legal compile version."); + this.id = id; this.source = source; - this.buildNumber = buildNumber; this.authorEmail = authorEmail; this.compileVersion = compileVersion; this.buildTime = buildTime; this.sourceUrl = Objects.requireNonNull(sourceUrl, "sourceUrl cannot be null"); this.commit = Objects.requireNonNull(commit, "commit cannot be null"); - this.deployedDirectly = deployedDirectly; this.bundleHash = bundleHash; this.hasPackage = hasPackage; this.shouldSkip = shouldSkip; @@ -76,44 +65,39 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { } public RevisionId id() { - return isDeployedDirectly() ? RevisionId.forDevelopment(buildNumber().orElse(0)) - : RevisionId.forProduction(buildNumber().orElseThrow()); + return id; } /** Create an application package version from a completed build, without an author email */ - public static ApplicationVersion from(SourceRevision source, long buildNumber) { - return new ApplicationVersion(Optional.of(source), OptionalLong.of(buildNumber), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), false, Optional.empty(), true, false, Optional.empty(), 0); + public static ApplicationVersion from(RevisionId id, SourceRevision source) { + return new ApplicationVersion(id, Optional.of(source), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), true, false, Optional.empty(), 0); } /** Creates a version from a completed build, an author email, and build metadata. */ - public static ApplicationVersion from(SourceRevision source, long buildNumber, String authorEmail, - Version compileVersion, Instant buildTime) { - return new ApplicationVersion(Optional.of(source), OptionalLong.of(buildNumber), Optional.of(authorEmail), Optional.of(compileVersion), Optional.of(buildTime), Optional.empty(), Optional.empty(), false, Optional.empty(), true, false, Optional.empty(), 0); + public static ApplicationVersion from(RevisionId id, SourceRevision source, String authorEmail, Version compileVersion, Instant buildTime) { + return new ApplicationVersion(id, Optional.of(source), Optional.of(authorEmail), Optional.of(compileVersion), Optional.of(buildTime), Optional.empty(), Optional.empty(), Optional.empty(), true, false, Optional.empty(), 0); } /** Creates a minimal version for a development build. */ - public static ApplicationVersion forDevelopment(long buildNumber, Optional<Version> compileVersion) { - return new ApplicationVersion(Optional.empty(), OptionalLong.of(buildNumber), Optional.empty(), compileVersion, Optional.empty(), Optional.empty(), Optional.empty(), true, Optional.empty(), true, false, Optional.empty(), 0); + public static ApplicationVersion forDevelopment(RevisionId id, Optional<Version> compileVersion) { + return new ApplicationVersion(id, Optional.empty(), Optional.empty(), compileVersion, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), true, false, Optional.empty(), 0); } /** Creates a version from a completed build, an author email, and build metadata. */ - public static ApplicationVersion forProduction(Optional<SourceRevision> source, long buildNumber, Optional<String> authorEmail, - Optional<Version> compileVersion, Optional<Instant> buildTime, - Optional<String> sourceUrl, Optional<String> commit, boolean deployedDirectly, - Optional<String> bundleHash, Optional<String> description, int risk) { - return new ApplicationVersion(source, OptionalLong.of(buildNumber), authorEmail, compileVersion, buildTime, - sourceUrl, commit, deployedDirectly, bundleHash, true, false, description, risk); + public static ApplicationVersion forProduction(RevisionId id, Optional<SourceRevision> source, Optional<String> authorEmail, + Optional<Version> compileVersion, Optional<Instant> buildTime, Optional<String> sourceUrl, + Optional<String> commit, Optional<String> bundleHash, Optional<String> description, int risk) { + return new ApplicationVersion(id, source, authorEmail, compileVersion, buildTime, + sourceUrl, commit, bundleHash, true, false, description, risk); } /** Returns a unique identifier for this version or "unknown" if version is not known */ // TODO jonmv: kill public String stringId() { - if (isUnknown()) return "unknown"; - return source.map(SourceRevision::commit).map(ApplicationVersion::abbreviateCommit) .or(this::commit) - .map(commit -> String.format("%s.%d-%s", majorVersion, buildNumber.getAsLong(), commit)) - .orElseGet(() -> majorVersion + "." + buildNumber.getAsLong()); + .map(commit -> String.format("%s.%d-%s", majorVersion, buildNumber().getAsLong(), commit)) + .orElseGet(() -> majorVersion + "." + buildNumber().getAsLong()); } /** @@ -123,7 +107,7 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { public Optional<SourceRevision> source() { return source; } /** Returns the build number that built this version */ - public OptionalLong buildNumber() { return buildNumber; } + public OptionalLong buildNumber() { return OptionalLong.of(id.number()); } /** Returns the email of the author of commit of this version, if known */ public Optional<String> authorEmail() { return authorEmail; } @@ -154,19 +138,14 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { /** Returns the commit name of this application version. */ public Optional<String> commit() { return commit.or(() -> source.map(SourceRevision::commit)); } - /** Returns whether this is unknown */ - public boolean isUnknown() { - return this.equals(unknown); - } - /** Returns whether the application package for this version was deployed directly to zone */ public boolean isDeployedDirectly() { - return deployedDirectly; + return ! id.isProduction(); } /** Returns a copy of this without a package stored. */ public ApplicationVersion withoutPackage() { - return new ApplicationVersion(source, buildNumber, authorEmail, compileVersion, buildTime, sourceUrl, commit, deployedDirectly, bundleHash, false, shouldSkip, description, risk); + return new ApplicationVersion(id, source, authorEmail, compileVersion, buildTime, sourceUrl, commit, bundleHash, false, shouldSkip, description, risk); } /** Whether we still have the package for this revision. */ @@ -176,7 +155,7 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { /** Returns a copy of this which will not be rolled out to production. */ public ApplicationVersion skipped() { - return new ApplicationVersion(source, buildNumber, authorEmail, compileVersion, buildTime, sourceUrl, commit, deployedDirectly, bundleHash, hasPackage, true, description, risk); + return new ApplicationVersion(id, source, authorEmail, compileVersion, buildTime, sourceUrl, commit, bundleHash, hasPackage, true, description, risk); } /** Whether we still have the package for this revision. */ @@ -204,23 +183,21 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { if (this == o) return true; if ( ! (o instanceof ApplicationVersion)) return false; ApplicationVersion that = (ApplicationVersion) o; - return Objects.equals(buildNumber, that.buildNumber) - && Objects.equals(commit(), that.commit()) - && deployedDirectly == that.deployedDirectly; + return id.equals(that.id); } @Override public int hashCode() { - return Objects.hash(buildNumber, commit(), deployedDirectly); + return id.hashCode(); } @Override public String toString() { - return "Application package version: " + stringId() - + source.map(s -> ", " + s.toString()).orElse("") - + authorEmail.map(e -> ", by " + e).orElse("") - + compileVersion.map(v -> ", built against " + v).orElse("") - + buildTime.map(t -> " at " + t).orElse("") ; + return id + + source.map(s -> ", " + s).orElse("") + + authorEmail.map(e -> ", by " + e).orElse("") + + compileVersion.map(v -> ", built against " + v).orElse("") + + buildTime.map(t -> " at " + t).orElse("") ; } /** Abbreviate given commit hash to 9 characters */ @@ -230,13 +207,7 @@ public class ApplicationVersion implements Comparable<ApplicationVersion> { @Override public int compareTo(ApplicationVersion o) { - if (buildNumber().isEmpty() || o.buildNumber().isEmpty()) - return Boolean.compare(buildNumber().isPresent(), o.buildNumber.isPresent()); // Unknown version sorts first - - if (deployedDirectly != o.deployedDirectly) - return Boolean.compare( ! deployedDirectly, ! o.deployedDirectly); // Directly deployed versions sort first - - return Long.compare(buildNumber().getAsLong(), o.buildNumber().getAsLong()); + return id.compareTo(o.id); } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RevisionId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RevisionId.java index 1e9f412d4da..2ab419b5388 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RevisionId.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/deployment/RevisionId.java @@ -12,48 +12,52 @@ import static ai.vespa.validation.Validation.requireAtLeast; public class RevisionId implements Comparable<RevisionId> { private final long number; - private final boolean production; + private final JobId job; - private RevisionId(long number, boolean production) { + private RevisionId(long number, JobId job) { this.number = number; - this.production = production; + this.job = job; } public static RevisionId forProduction(long number) { - return new RevisionId(requireAtLeast(number, "build number", 1L), true); + return new RevisionId(requireAtLeast(number, "build number", 1L), null); } - public static RevisionId forDevelopment(long number) { - return new RevisionId(requireAtLeast(number, "build number", 0L), false); + public static RevisionId forDevelopment(long number, JobId job) { + return new RevisionId(requireAtLeast(number, "build number", 0L), job); } public long number() { return number; } - public boolean isProduction() { return production; } + public boolean isProduction() { return job == null; } + + /** Returns the job for this, if a development revision, or throws if this is a production revision. */ + public JobId job() { return Objects.requireNonNull(job, "production revisions have no associated job"); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RevisionId that = (RevisionId) o; - return number == that.number && production == that.production; + return number == that.number && Objects.equals(job, that.job); } @Override public int hashCode() { - return Objects.hash(number, production); + return Objects.hash(number, job); } /** Unknown, manual builds sort first, then known manual builds, then production builds, by increasing build number */ @Override public int compareTo(RevisionId o) { - return production != o.production ? Boolean.compare(production, o.production) - : Long.compare(number, o.number); + return isProduction() != o.isProduction() ? Boolean.compare(isProduction(), o.isProduction()) + : Long.compare(number, o.number); } @Override public String toString() { - return (production ? "prod" : "dev") + " build " + number; + return isProduction() ? "build " + number + : "dev build " + number + " for " + job.type() + " of " + job.application().instance(); } } 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 ccdf4390d61..af8965bdeff 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 @@ -7,7 +7,7 @@ import com.yahoo.config.application.api.ValidationOverrides; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.zone.ZoneId; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.application.ApplicationActivity; @@ -186,10 +186,10 @@ public class Application { /** * Returns the oldest application version this has deployed in a permanent zone (not test or staging). */ - public Optional<ApplicationVersion> oldestDeployedApplication() { + public Optional<RevisionId> oldestDeployedRevision() { return productionDeployments().values().stream().flatMap(List::stream) - .map(Deployment::applicationVersion) - .filter(version -> ! version.isUnknown() && ! version.isDeployedDirectly()) + .map(Deployment::revision) + .filter(RevisionId::isProduction) .min(Comparator.naturalOrder()); } @@ -215,15 +215,6 @@ public class Application { /** Returns the set of deploy keys for this application. */ public Set<PublicKey> deployKeys() { return deployKeys; } - private static Optional<ApplicationVersion> requireNotUnknown(Optional<ApplicationVersion> latestVersion) { - Objects.requireNonNull(latestVersion, "latestVersion cannot be null"); - latestVersion.ifPresent(version -> { - if (version.isUnknown()) - throw new IllegalArgumentException("latestVersion cannot be unknown"); - }); - return latestVersion; - } - @Override public boolean equals(Object o) { if (this == o) return true; 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 01abca1b2be..5b61996083f 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 @@ -27,7 +27,6 @@ import com.yahoo.vespa.flags.StringFlag; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeploymentData; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.identifiers.InstanceId; -import com.yahoo.vespa.hosted.controller.api.identifiers.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController; import com.yahoo.vespa.hosted.controller.api.integration.billing.Quota; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata; @@ -42,6 +41,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationV import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.RestartFilter; import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore; @@ -163,25 +163,7 @@ public class ApplicationController { for (InstanceName instance : application.get().deploymentSpec().instanceNames()) if ( ! application.get().instances().containsKey(instance)) application = withNewInstance(application, id.instance(instance)); - // TODO jonmv: remove when data is migrated - // Each controller will know only about the revisions which we have packages for when they upgrade. - // The last controller will populate any missing, historic data after it upgrades. - // When all controllers are upgraded, we can start using the data, and remove this. - Set<ApplicationVersion> production = new HashSet<>(); - Map<JobId, Set<ApplicationVersion>> development = new HashMap<>(); - for (InstanceName instance : application.get().instances().keySet()) { - for (JobType type : JobType.allIn(controller.system())) { - for (Run run : controller.jobController().runs(id.instance(instance), type).values()) { - ApplicationVersion revision = run.versions().targetApplication(); - if ( ! revision.isDeployedDirectly()) production.add(revision); - else development.computeIfAbsent(run.id().job(), __ -> new HashSet<>()).add(revision); - } - } - } - application = application.withRevisions(revisions -> { - production.addAll(revisions.production()); // These are already properly set, and we want ot keep their hasPackage status. - return RevisionHistory.ofRevisions(production, development); // All the added data is just written for now. We'll use it later. - }); + store(application); }); count++; @@ -485,7 +467,7 @@ public class ApplicationController { throw new IllegalStateException("No deployment expected for " + job + " now, as no job is running"); Version platform = run.versions().sourcePlatform().filter(__ -> deploySourceVersions).orElse(run.versions().targetPlatform()); - ApplicationVersion revision = run.versions().sourceApplication().filter(__ -> deploySourceVersions).orElse(run.versions().targetApplication()); + RevisionId revision = run.versions().sourceRevision().filter(__ -> deploySourceVersions).orElse(run.versions().targetRevision()); ApplicationPackage applicationPackage = new ApplicationPackage(applicationStore.get(deployment, revision)); try (Lock lock = lock(applicationId)) { @@ -637,7 +619,7 @@ public class ApplicationController { deploymentQuota, tenantSecretStores, operatorCertificates, dryRun)); - return new ActivateResult(new RevisionId(applicationPackage.hash()), preparedApplication.prepareResponse(), + return new ActivateResult(new com.yahoo.vespa.hosted.controller.api.identifiers.RevisionId(applicationPackage.hash()), preparedApplication.prepareResponse(), applicationPackage.zippedContent().length); } finally { // Even if prepare fails, routing configuration may need to be updated diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java index 9ca23ca5d15..402a4bf49a8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java @@ -6,8 +6,8 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.zone.ZoneId; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.application.AssignedRotation; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; @@ -63,16 +63,16 @@ public class Instance { this.change = Objects.requireNonNull(change, "change cannot be null"); } - public Instance withNewDeployment(ZoneId zone, ApplicationVersion applicationVersion, Version version, + public Instance withNewDeployment(ZoneId zone, RevisionId revision, Version version, Instant instant, Map<DeploymentMetrics.Warning, Integer> warnings, QuotaUsage quotaUsage) { // Use info from previous deployment if available, otherwise create a new one. - Deployment previousDeployment = deployments.getOrDefault(zone, new Deployment(zone, applicationVersion, + Deployment previousDeployment = deployments.getOrDefault(zone, new Deployment(zone, revision, version, instant, DeploymentMetrics.none, DeploymentActivity.none, QuotaUsage.none, OptionalDouble.empty())); - Deployment newDeployment = new Deployment(zone, applicationVersion, version, instant, + Deployment newDeployment = new Deployment(zone, revision, version, instant, previousDeployment.metrics().with(warnings), previousDeployment.activity(), quotaUsage, 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 b5a4a2a71d7..64cad599168 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 @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.application; import com.yahoo.component.Version; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import java.util.Objects; import java.util.Optional; @@ -28,22 +29,22 @@ public final class Change { private final Optional<Version> platform; /** The application version we are changing to, or empty if none */ - private final Optional<ApplicationVersion> application; + private final Optional<RevisionId> revision; /** Whether this change is a pin to its contained Vespa version, or to the application's current. */ private final boolean pinned; - private Change(Optional<Version> platform, Optional<ApplicationVersion> application, boolean pinned) { + private Change(Optional<Version> platform, Optional<RevisionId> revision, boolean pinned) { this.platform = requireNonNull(platform, "platform cannot be null"); - this.application = requireNonNull(application, "application cannot be null"); - if (application.isPresent() && (application.get().isUnknown() || application.get().isDeployedDirectly())) { + this.revision = requireNonNull(revision, "revision cannot be null"); + if (revision.isPresent() && ( ! revision.get().isProduction())) { throw new IllegalArgumentException("Application version to deploy must be a known version"); } this.pinned = pinned; } public Change withoutPlatform() { - return new Change(Optional.empty(), application, pinned); + return new Change(Optional.empty(), revision, pinned); } public Change withoutApplication() { @@ -52,7 +53,7 @@ public final class Change { /** Returns whether a change should currently be deployed */ public boolean hasTargets() { - return platform.isPresent() || application.isPresent(); + return platform.isPresent() || revision.isPresent(); } /** Returns whether this is the empty change. */ @@ -64,7 +65,7 @@ public final class Change { public Optional<Version> platform() { return platform; } /** Returns the application version carried by this. */ - public Optional<ApplicationVersion> application() { return application; } + public Optional<RevisionId> revision() { return revision; } public boolean isPinned() { return pinned; } @@ -76,30 +77,30 @@ public final class Change { if (pinned) throw new IllegalArgumentException("Not allowed to set a platform version when pinned."); - return new Change(Optional.of(platformVersion), application, pinned); + return new Change(Optional.of(platformVersion), revision, pinned); } - /** 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), pinned); + /** Returns a version of this change which replaces or adds this revision change */ + public Change with(RevisionId revision) { + return new Change(platform, Optional.of(revision), pinned); } /** Returns a change with the versions of this, and with the platform version pinned. */ public Change withPin() { - return new Change(platform, application, true); + return new Change(platform, revision, true); } /** Returns a change with the versions of this, and with the platform version unpinned. */ public Change withoutPin() { - return new Change(platform, application, false); + return new Change(platform, revision, false); } /** Returns the change obtained when overwriting elements of the given change with any present in this */ public Change onTopOf(Change other) { if (platform.isPresent()) other = other.with(platform.get()); - if (application.isPresent()) - other = other.with(application.get()); + if (revision.isPresent()) + other = other.with(revision.get()); if (pinned) other = other.withPin(); return other; @@ -112,12 +113,12 @@ public final class Change { Change change = (Change) o; return pinned == change.pinned && Objects.equals(platform, change.platform) && - Objects.equals(application, change.application); + Objects.equals(revision, change.revision); } @Override public int hashCode() { - return Objects.hash(platform, application, pinned); + return Objects.hash(platform, revision, pinned); } @Override @@ -126,23 +127,23 @@ public final class Change { if (pinned) changes.add("pin to " + platform.map(Version::toString).orElse("current platform")); else - platform.ifPresent(version -> changes.add("upgrade to " + version.toString())); - application.ifPresent(version -> changes.add("application change to " + version.stringId())); + platform.ifPresent(version -> changes.add("upgrade to " + version)); + revision.ifPresent(revision -> changes.add("revision change to " + revision)); changes.setEmptyValue("no change"); return changes.toString(); } - public static Change of(ApplicationVersion applicationVersion) { - return new Change(Optional.empty(), Optional.of(applicationVersion), false); + public static Change of(RevisionId revision) { + return new Change(Optional.empty(), Optional.of(revision), false); } public static Change of(Version platformChange) { return new Change(Optional.of(platformChange), Optional.empty(), false); } - /** Returns whether this change carries an application downgrade relative to the given version. */ - public boolean downgrades(ApplicationVersion version) { - return application.map(version::compareTo).orElse(0) > 0; + /** Returns whether this change carries a revision downgrade relative to the given revision. */ + public boolean downgrades(RevisionId revision) { + return this.revision.map(revision::compareTo).orElse(0) > 0; } /** Returns whether this change carries a platform downgrade relative to the given version. */ @@ -150,9 +151,9 @@ public final class Change { return platform.map(version::compareTo).orElse(0) > 0; } - /** Returns whether this change carries an application upgrade relative to the given version. */ - public boolean upgrades(ApplicationVersion version) { - return application.map(version::compareTo).orElse(0) < 0; + /** Returns whether this change carries a revision upgrade relative to the given revision. */ + public boolean upgrades(RevisionId revision) { + return this.revision.map(revision::compareTo).orElse(0) < 0; } /** Returns whether this change carries a platform upgrade relative to the given version. */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Deployment.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Deployment.java index 2b79bd3f3e9..2e4afb4e004 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Deployment.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Deployment.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.application; import com.yahoo.component.Version; import com.yahoo.config.provision.zone.ZoneId; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import java.time.Instant; import java.util.Objects; @@ -18,7 +18,7 @@ import java.util.OptionalDouble; public class Deployment { private final ZoneId zone; - private final ApplicationVersion applicationVersion; + private final RevisionId revision; private final Version version; private final Instant deployTime; private final DeploymentMetrics metrics; @@ -26,10 +26,10 @@ public class Deployment { private final QuotaUsage quota; private final OptionalDouble cost; - public Deployment(ZoneId zone, ApplicationVersion applicationVersion, Version version, Instant deployTime, + public Deployment(ZoneId zone, RevisionId revision, Version version, Instant deployTime, DeploymentMetrics metrics, DeploymentActivity activity, QuotaUsage quota, OptionalDouble cost) { this.zone = Objects.requireNonNull(zone, "zone cannot be null"); - this.applicationVersion = Objects.requireNonNull(applicationVersion, "applicationVersion cannot be null"); + this.revision = Objects.requireNonNull(revision, "revision cannot be null"); this.version = Objects.requireNonNull(version, "version cannot be null"); this.deployTime = Objects.requireNonNull(deployTime, "deployTime cannot be null"); this.metrics = Objects.requireNonNull(metrics, "deploymentMetrics cannot be null"); @@ -41,8 +41,8 @@ public class Deployment { /** Returns the zone this was deployed to */ public ZoneId zone() { return zone; } - /** Returns the deployed application version */ - public ApplicationVersion applicationVersion() { return applicationVersion; } + /** Returns the deployed application revision */ + public RevisionId revision() { return revision; } /** Returns the deployed Vespa version */ public Version version() { return version; } @@ -65,26 +65,26 @@ public class Deployment { public OptionalDouble cost() { return cost; } public Deployment recordActivityAt(Instant instant) { - return new Deployment(zone, applicationVersion, version, deployTime, metrics, + return new Deployment(zone, revision, version, deployTime, metrics, activity.recordAt(instant, metrics), quota, cost); } public Deployment withMetrics(DeploymentMetrics metrics) { - return new Deployment(zone, applicationVersion, version, deployTime, metrics, activity, quota, cost); + return new Deployment(zone, revision, version, deployTime, metrics, activity, quota, cost); } public Deployment withQuota(QuotaUsage quota) { - return new Deployment(zone, applicationVersion, version, deployTime, metrics, activity, quota, cost); + return new Deployment(zone, revision, version, deployTime, metrics, activity, quota, cost); } public Deployment withCost(double cost) { if (this.cost.isPresent() && Double.compare(this.cost.getAsDouble(), cost) == 0) return this; - return new Deployment(zone, applicationVersion, version, deployTime, metrics, activity, quota, OptionalDouble.of(cost)); + return new Deployment(zone, revision, version, deployTime, metrics, activity, quota, OptionalDouble.of(cost)); } public Deployment withoutCost() { if (cost.isEmpty()) return this; - return new Deployment(zone, applicationVersion, version, deployTime, metrics, activity, quota, OptionalDouble.empty()); + return new Deployment(zone, revision, version, deployTime, metrics, activity, quota, OptionalDouble.empty()); } @Override @@ -93,7 +93,7 @@ public class Deployment { if (o == null || getClass() != o.getClass()) return false; Deployment that = (Deployment) o; return zone.equals(that.zone) && - applicationVersion.equals(that.applicationVersion) && + revision.equals(that.revision) && version.equals(that.version) && deployTime.equals(that.deployTime) && metrics.equals(that.metrics) && @@ -104,12 +104,12 @@ public class Deployment { @Override public int hashCode() { - return Objects.hash(zone, applicationVersion, version, deployTime, metrics, activity, quota, cost); + return Objects.hash(zone, revision, version, deployTime, metrics, activity, quota, cost); } @Override public String toString() { - return "deployment to " + zone + " of " + applicationVersion + " on version " + version + " at " + deployTime; + return "deployment to " + zone + " of " + revision + " on version " + version + " at " + deployTime; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/InstanceList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/InstanceList.java index 99b14bf289a..030f7678b8c 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/InstanceList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/InstanceList.java @@ -42,7 +42,7 @@ public class InstanceList extends AbstractFilteringList<ApplicationId, InstanceL */ public InstanceList compatibleWithPlatform(Version platform, Function<ApplicationId, VersionCompatibility> compatibility) { return matching(id -> instance(id).productionDeployments().values().stream() - .flatMap(deployment -> deployment.applicationVersion().compileVersion().stream()) + .flatMap(deployment -> application(id).revisions().get(deployment.revision()).compileVersion().stream()) .noneMatch(version -> compatibility.apply(id).refuse(platform, version))); } @@ -95,7 +95,7 @@ public class InstanceList extends AbstractFilteringList<ApplicationId, InstanceL /** Returns the subset of instances which are currently deploying a new revision */ public InstanceList changingRevision() { - return matching(id -> instance(id).change().application().isPresent()); + return matching(id -> instance(id).change().revision().isPresent()); } /** Returns the subset of instances which currently have failing jobs on the given version */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java index a82e77da130..a2c5b8f4a9b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java @@ -19,6 +19,7 @@ import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; @@ -126,9 +127,9 @@ public class DeploymentStatus { } /** Whether any job is failing on versions selected by the given filter, with errors other than lack of capacity in a test zone.. */ - public boolean hasFailures(Predicate<ApplicationVersion> versionFilter) { + public boolean hasFailures(Predicate<RevisionId> revisionFilter) { return ! allJobs.failingHard() - .matching(job -> versionFilter.test(job.lastTriggered().get().versions().targetApplication())) + .matching(job -> revisionFilter.test(job.lastTriggered().get().versions().targetRevision())) .isEmpty(); } @@ -263,10 +264,10 @@ public class DeploymentStatus { Change candidate = Change.empty(); for (ApplicationVersion version : application.revisions().deployable(ascending)) { // A revision is only a candidate if it upgrades, and does not downgrade, this instance. - Change change = Change.of(version); + Change change = Change.of(version.id()); if (application.productionDeployments().getOrDefault(instance, List.of()).stream() - .anyMatch(deployment -> change.downgrades(deployment.applicationVersion()))) continue; - if ( ! application.require(instance).change().application().map(change::upgrades).orElse(true)) continue; + .anyMatch(deployment -> change.downgrades(deployment.revision()))) continue; + if ( ! application.require(instance).change().revision().map(change::upgrades).orElse(true)) continue; if (hasCompleted(instance, change)) if (ascending) continue; // Keep looking for the next revision which is an upgrade, or ... else return Change.empty(); // ... if the latest is already complete, there's nothing outstanding. @@ -275,7 +276,7 @@ public class DeploymentStatus { skippedCumulativeRisk += version.risk(); nextRisk = nextRisk > 0 ? nextRisk : version.risk(); // If it's not yet ready to roll out, we keep looking. - Optional<Instant> readyAt = status.dependenciesCompletedAt(Change.of(version), Optional.empty()); + Optional<Instant> readyAt = status.dependenciesCompletedAt(Change.of(version.id()), Optional.empty()); if (readyAt.map(now::isBefore).orElse(true)) continue; // It's ready. If looking for the latest, max risk is 0, and we'll return now; otherwise, we _may_ keep on looking for more. @@ -337,9 +338,9 @@ public class DeploymentStatus { // When computing eager test jobs for outstanding changes, assume current change completes successfully. Optional<Deployment> deployment = deploymentFor(job); Optional<Version> existingPlatform = deployment.map(Deployment::version); - Optional<ApplicationVersion> existingApplication = deployment.map(Deployment::applicationVersion); - boolean deployingCompatibilityChange = areIncompatible(existingPlatform, change.application(), instance) - || areIncompatible(change.platform(), existingApplication, instance); + Optional<RevisionId> existingRevision = deployment.map(Deployment::revision); + boolean deployingCompatibilityChange = areIncompatible(existingPlatform, change.revision(), job) + || areIncompatible(change.platform(), existingRevision, job); if (assumeUpgradesSucceed) { if (deployingCompatibilityChange) // No eager tests for this. return; @@ -347,34 +348,36 @@ public class DeploymentStatus { Change currentChange = application.require(instance).change(); Versions target = Versions.from(currentChange, application, deployment, systemVersion); existingPlatform = Optional.of(target.targetPlatform()); - existingApplication = Optional.of(target.targetApplication()); + existingRevision = Optional.of(target.targetRevision()); } List<Job> toRun = new ArrayList<>(); List<Change> changes = deployingCompatibilityChange ? List.of(change) : changes(job, step, change); for (Change partial : changes) { Job jobToRun = new Job(job.type(), - Versions.from(partial, application, existingPlatform, existingApplication, systemVersion), + Versions.from(partial, application, existingPlatform, existingRevision, systemVersion), step.readyAt(partial, Optional.of(job)), partial); toRun.add(jobToRun); // Assume first partial change is applied before the second. existingPlatform = Optional.of(jobToRun.versions.targetPlatform()); - existingApplication = Optional.of(jobToRun.versions.targetApplication()); + existingRevision = Optional.of(jobToRun.versions.targetRevision()); } jobs.put(job, toRun); }); return jobs; } - private boolean areIncompatible(Optional<Version> platform, Optional<ApplicationVersion> application, InstanceName instance) { + private boolean areIncompatible(Optional<Version> platform, Optional<RevisionId> revision, JobId job) { + Optional<Version> compileVersion = revision.map(application.revisions()::get) + .flatMap(ApplicationVersion::compileVersion); return platform.isPresent() - && application.flatMap(ApplicationVersion::compileVersion).isPresent() - && versionCompatibility.apply(instance).refuse(platform.get(), application.get().compileVersion().get()); + && compileVersion.isPresent() + && versionCompatibility.apply(job.application().instance()).refuse(platform.get(), compileVersion.get()); } /** Changes to deploy with the given job, possibly split in two steps. */ private List<Change> changes(JobId job, StepStatus step, Change change) { - if (change.platform().isEmpty() || change.application().isEmpty() || change.isPinned()) + if (change.platform().isEmpty() || change.revision().isEmpty() || change.isPinned()) return List.of(change); if ( step.completedAt(change.withoutApplication(), Optional.of(job)).isPresent() @@ -749,7 +752,7 @@ public class DeploymentStatus { @Override Optional<Instant> completedAt(Change change, Optional<JobId> dependent) { return ( (change.platform().isEmpty() || change.platform().equals(instance.change().platform())) - && (change.application().isEmpty() || change.application().equals(instance.change().application())) + && (change.revision().isEmpty() || change.revision().equals(instance.change().revision())) || step().steps().stream().noneMatch(step -> step.concerns(prod))) ? dependenciesCompletedAt(change, dependent).or(() -> Optional.of(Instant.EPOCH).filter(__ -> change.hasTargets())) : Optional.empty(); @@ -763,7 +766,7 @@ public class DeploymentStatus { while ( blocker.window().includes(current) && now.plus(Duration.ofDays(7)).isAfter(current) && ( change.platform().isPresent() && blocker.blocksVersions() - || change.application().isPresent() && blocker.blocksRevisions())) { + || change.revision().isPresent() && blocker.blocksRevisions())) { blocked = true; current = current.plus(Duration.ofHours(1)).truncatedTo(ChronoUnit.HOURS); } @@ -804,7 +807,7 @@ public class DeploymentStatus { if (job.firstFailing().isEmpty() || ! job.firstFailing().get().hasEnded()) return Optional.empty(); Versions lastVersions = job.lastCompleted().get().versions(); if (change.platform().isPresent() && ! change.platform().get().equals(lastVersions.targetPlatform())) return Optional.empty(); - if (change.application().isPresent() && ! change.application().get().equals(lastVersions.targetApplication())) return Optional.empty(); + if (change.revision().isPresent() && ! change.revision().get().equals(lastVersions.targetRevision())) return Optional.empty(); if (job.id().type().environment().isTest() && job.isNodeAllocationFailure()) return Optional.empty(); Instant firstFailing = job.firstFailing().get().end().get(); @@ -841,14 +844,14 @@ public class DeploymentStatus { && ! existingDeployment.map(Deployment::version).equals(change.platform())) return Optional.empty(); - if ( change.application().isPresent() - && ! existingDeployment.map(Deployment::applicationVersion).equals(change.application()) + if ( change.revision().isPresent() + && ! existingDeployment.map(Deployment::revision).equals(change.revision()) && dependent.equals(job())) // Job should (re-)run in this case, but other dependents need not wait. return Optional.empty(); Change fullChange = status.application().require(job.id().application().instance()).change(); - if (existingDeployment.map(deployment -> ! (change.upgrades(deployment.version()) || change.upgrades(deployment.applicationVersion())) - && (fullChange.downgrades(deployment.version()) || fullChange.downgrades(deployment.applicationVersion()))) + if (existingDeployment.map(deployment -> ! (change.upgrades(deployment.version()) || change.upgrades(deployment.revision())) + && (fullChange.downgrades(deployment.version()) || fullChange.downgrades(deployment.revision()))) .orElse(false)) return job.lastCompleted().flatMap(Run::end); @@ -901,7 +904,7 @@ public class DeploymentStatus { Optional.of(deployment), status.systemVersion))) .orElseGet(() -> (change.platform().isEmpty() || change.platform().get().equals(run.versions().targetPlatform())) - && (change.application().isEmpty() || change.application().get().equals(run.versions().targetApplication())))) + && (change.revision().isEmpty() || change.revision().get().equals(run.versions().targetRevision())))) .status(RunStatus.success) .asList().stream() .map(run -> run.end().get()) 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 889ac6a6de4..54703308102 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 @@ -17,6 +17,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.application.ApplicationList; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; @@ -88,11 +89,11 @@ public class DeploymentTrigger { DeploymentStatus status = jobs.deploymentStatus(application.get()); for (InstanceName instanceName : application.get().deploymentSpec().instanceNames()) { Change outstanding = outstandingChange(status, instanceName); - if ( outstanding.hasTargets() + if (outstanding.hasTargets() && status.instanceSteps().get(instanceName) .readyAt(outstanding) .map(readyAt -> ! readyAt.isAfter(clock.instant())).orElse(false) - && acceptNewApplicationVersion(status, instanceName, outstanding.application().get())) { + && acceptNewRevision(status, instanceName, outstanding.revision().get())) { application = application.with(instanceName, instance -> withRemainingChange(instance, outstanding.onTopOf(instance.change()), status)); } @@ -104,7 +105,9 @@ public class DeploymentTrigger { /** Returns any outstanding change for the given instance, coupled with any necessary platform upgrade. */ private Change outstandingChange(DeploymentStatus status, InstanceName instance) { Change outstanding = status.outstandingChange(instance); - Optional<Version> compileVersion = outstanding.application().flatMap(ApplicationVersion::compileVersion); + Optional<Version> compileVersion = outstanding.revision() + .map(status.application().revisions()::get) + .flatMap(ApplicationVersion::compileVersion); // If the outstanding revision requires a certain platform for compatibility, add that here. VersionCompatibility compatibility = applications().versionCompatibility(status.application().id().instance(instance)); @@ -234,7 +237,7 @@ public class DeploymentTrigger { DeploymentStatus status = jobs.deploymentStatus(application); Change change = instance.change(); - if ( ! upgradeRevision && change.application().isPresent()) change = change.withoutApplication(); + if ( ! upgradeRevision && change.revision().isPresent()) change = change.withoutApplication(); if ( ! upgradePlatform && change.platform().isPresent()) change = change.withoutPlatform(); Versions versions = Versions.from(change, application, status.deploymentFor(job), controller.readSystemVersion()); DeploymentStatus.Job toTrigger = new DeploymentStatus.Job(job.type(), versions, Optional.of(controller.clock().instant()), instance.change()); @@ -255,9 +258,9 @@ public class DeploymentTrigger { private List<JobId> forceTriggerManualJob(JobId job, String reason) { Run last = jobs.last(job).orElseThrow(() -> new IllegalArgumentException(job + " has never been run")); Versions target = new Versions(controller.readSystemVersion(), - last.versions().targetApplication(), + last.versions().targetRevision(), Optional.of(last.versions().targetPlatform()), - Optional.of(last.versions().targetApplication())); + Optional.of(last.versions().targetRevision())); jobs.start(job.application(), job.type(), target, true, Optional.of(reason)); return List.of(job); } @@ -433,16 +436,16 @@ public class DeploymentTrigger { // ---------- Change management o_O ---------- - private boolean acceptNewApplicationVersion(DeploymentStatus status, InstanceName instance, ApplicationVersion version) { + private boolean acceptNewRevision(DeploymentStatus status, InstanceName instance, RevisionId revision) { if (status.application().deploymentSpec().instance(instance).isEmpty()) return false; // Unknown instance. - boolean isChangingRevision = status.application().require(instance).change().application().isPresent(); + boolean isChangingRevision = status.application().require(instance).change().revision().isPresent(); DeploymentInstanceSpec spec = status.application().deploymentSpec().requireInstance(instance); - Predicate<ApplicationVersion> versionFilter = spec.revisionTarget() == DeploymentSpec.RevisionTarget.next - ? failing -> status.application().require(instance).change().application().get().compareTo(failing) == 0 - : failing -> version.compareTo(failing) > 0; + Predicate<RevisionId> revisionFilter = spec.revisionTarget() == DeploymentSpec.RevisionTarget.next + ? failing -> status.application().require(instance).change().revision().get().compareTo(failing) == 0 + : failing -> revision.compareTo(failing) > 0; switch (spec.revisionChange()) { case whenClear: return ! isChangingRevision; - case whenFailing: return ! isChangingRevision || status.hasFailures(versionFilter); + case whenFailing: return ! isChangingRevision || status.hasFailures(revisionFilter); case always: return true; default: throw new IllegalStateException("Unknown revision upgrade policy"); } @@ -460,7 +463,7 @@ public class DeploymentTrigger { // ---------- Version and job helpers ---------- private Job deploymentJob(Instance instance, Versions versions, JobType jobType, JobStatus jobStatus, Instant availableSince) { - return new Job(instance, versions, jobType, availableSince, jobStatus.isNodeAllocationFailure(), instance.change().application().isPresent()); + return new Job(instance, versions, jobType, availableSince, jobStatus.isNodeAllocationFailure(), instance.change().revision().isPresent()); } // ---------- Data containers ---------- @@ -495,7 +498,7 @@ public class DeploymentTrigger { public String toString() { return jobType + " for " + instanceId + " on (" + versions.targetPlatform() + versions.sourcePlatform().map(version -> " <-- " + version).orElse("") + - ", " + versions.targetApplication().stringId() + versions.sourceApplication().map(version -> " <-- " + version.stringId()).orElse("") + + ", " + versions.targetRevision() + versions.sourceRevision().map(version -> " <-- " + version).orElse("") + "), ready since " + availableSince; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java index 0df4c0015bd..ce4bb70c174 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java @@ -38,6 +38,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareRes import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId; @@ -184,15 +185,15 @@ public class InternalStepRunner implements StepRunner { Versions versions = controller.jobController().run(id).get().versions(); logger.log("Deploying platform version " + versions.sourcePlatform().orElse(versions.targetPlatform()) + - " and application version " + - versions.sourceApplication().orElse(versions.targetApplication()).stringId() + " ..."); + " and application " + + versions.sourceRevision().orElse(versions.targetRevision()) + " ..."); return deployReal(id, true, logger); } private Optional<RunStatus> deployReal(RunId id, DualLogger logger) { Versions versions = controller.jobController().run(id).get().versions(); logger.log("Deploying platform version " + versions.targetPlatform() + - " and application version " + versions.targetApplication().stringId() + " ..."); + " and application " + versions.targetRevision() + " ..."); return deployReal(id, false, logger); } @@ -786,14 +787,14 @@ public class InternalStepRunner implements StepRunner { Application application = controller.applications().requireApplication(TenantAndApplicationId.from(run.id().application())); Notifications notifications = application.deploymentSpec().requireInstance(run.id().application().instance()).notifications(); - boolean newCommit = application.require(run.id().application().instance()).change().application() - .map(run.versions().targetApplication()::equals) + boolean newCommit = application.require(run.id().application().instance()).change().revision() + .map(run.versions().targetRevision()::equals) .orElse(false); When when = newCommit ? failingCommit : failing; List<String> recipients = new ArrayList<>(notifications.emailAddressesFor(when)); if (notifications.emailRolesFor(when).contains(author)) - run.versions().targetApplication().authorEmail().ifPresent(recipients::add); + application.revisions().get(run.versions().targetRevision()).authorEmail().ifPresent(recipients::add); if (recipients.isEmpty()) return; @@ -904,7 +905,7 @@ public class InternalStepRunner implements StepRunner { /** Returns the application package for the tester application, assembled from a generated config, fat-jar and services.xml. */ private ApplicationPackage testerPackage(RunId id) { - ApplicationVersion version = controller.jobController().run(id).get().versions().targetApplication(); + RevisionId revision = controller.jobController().run(id).get().versions().targetRevision(); DeploymentSpec spec = controller.applications().requireApplication(TenantAndApplicationId.from(id.application())).deploymentSpec(); ZoneId zone = id.type().zone(controller.system()); @@ -914,7 +915,7 @@ public class InternalStepRunner implements StepRunner { useTesterCertificate, testerResourcesFor(zone, spec.requireInstance(id.application().instance())), controller.controllerConfig().steprunner().testerapp()); - byte[] testPackage = controller.applications().applicationStore().getTester(id.application().tenant(), id.application().application(), version); + byte[] testPackage = controller.applications().applicationStore().getTester(id.application().tenant(), id.application().application(), revision); byte[] deploymentXml = deploymentXml(id.tester(), spec.athenzDomain(), spec.requireInstance(id.application().instance()).athenzService(zone.environment(), zone.region())); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java index 46533b8b063..57c817e6cd5 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java @@ -17,6 +17,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.LogEntry; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport; @@ -119,7 +120,7 @@ public class JobController { for (ApplicationId id : instances()) for (JobType type : jobs(id)) { locked(id, type, runs -> { // Runs are not modified here, and are written as they were. - curator.readLastRun(id, type).ifPresent(curator::writeLastRun); + curator.readLastRun(id, type).ifPresent(run -> curator.writeLastRun(run, controller.applications().requireApplication(TenantAndApplicationId.from(id)))); }); } } @@ -452,12 +453,14 @@ public class JobController { .map(ApplicationPackage::new); long previousBuild = previousVersion.map(latestVersion -> latestVersion.buildNumber().getAsLong()).orElse(0L); String packageHash = applicationPackage.bundleHash() + ApplicationPackage.calculateHash(testPackageBytes); - version.set(ApplicationVersion.forProduction(revision, 1 + previousBuild, authorEmail, + RevisionId revisionId = RevisionId.forProduction(1 + previousBuild); + version.set(ApplicationVersion.forProduction(revisionId, + revision, + authorEmail, applicationPackage.compileVersion(), applicationPackage.buildTime(), sourceUrl, revision.map(SourceRevision::commit), - false, Optional.of(packageHash), description, risk)); @@ -466,7 +469,7 @@ public class JobController { .orElseGet(() -> ApplicationPackageDiff.diffAgainstEmpty(applicationPackage)); applications.applicationStore().put(id.tenant(), id.application(), - version.get(), + version.get().id(), applicationPackage.zippedContent(), testPackageBytes, diff); @@ -487,12 +490,12 @@ public class JobController { private LockedApplication withPrunedPackages(LockedApplication application){ TenantAndApplicationId id = application.get().id(); - Optional<ApplicationVersion> oldestDeployed = application.get().oldestDeployedApplication(); + Optional<RevisionId> oldestDeployed = application.get().oldestDeployedRevision(); if (oldestDeployed.isPresent()) { controller.applications().applicationStore().prune(id.tenant(), id.application(), oldestDeployed.get()); for (ApplicationVersion version : application.get().revisions().withPackage()) - if (version.compareTo(oldestDeployed.get()) < 0) + if (version.id().compareTo(oldestDeployed.get()) < 0) application = application.withRevisions(revisions -> revisions.with(version.withoutPackage())); } return application; @@ -501,11 +504,11 @@ public class JobController { /** Forget revisions no longer present in any relevant job history. */ private void pruneRevisions(Run run) { TenantAndApplicationId applicationId = TenantAndApplicationId.from(run.id().application()); - boolean isProduction = run.versions().targetApplication().id().isProduction(); + boolean isProduction = run.versions().targetRevision().isProduction(); (isProduction ? deploymentStatus(controller.applications().requireApplication(applicationId)).jobs().asList().stream() : Stream.of(jobStatus(run.id().job()))) .flatMap(jobs -> jobs.runs().values().stream()) - .map(r -> r.versions().targetApplication().id()) + .map(r -> r.versions().targetRevision()) .filter(id -> id.isProduction() == isProduction) .min(naturalOrder()) .ifPresent(oldestRevision -> { @@ -529,11 +532,12 @@ public class JobController { /** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */ public void start(ApplicationId id, JobType type, Versions versions, boolean isRedeployment, JobProfile profile, Optional<String> reason) { - if (versions.targetApplication().compileVersion() + ApplicationVersion revision = controller.applications().requireApplication(TenantAndApplicationId.from(id)).revisions().get(versions.targetRevision()); + if (revision.compileVersion() .map(version -> controller.applications().versionCompatibility(id).refuse(versions.targetPlatform(), version)) .orElse(false)) throw new IllegalArgumentException("Will not start a job with incompatible platform version (" + versions.targetPlatform() + ") " + - "and compile versions (" + versions.targetApplication().compileVersion().get() + ")"); + "and compile versions (" + revision.compileVersion().get() + ")"); locked(id, type, __ -> { Optional<Run> last = last(id, type); @@ -541,7 +545,7 @@ public class JobController { throw new IllegalArgumentException("Cannot start " + type + " for " + id + "; it is already running!"); RunId newId = new RunId(id, type, last.map(run -> run.id().number()).orElse(0L) + 1); - curator.writeLastRun(Run.initial(newId, versions, isRedeployment, controller.clock().instant(), profile, reason)); + curator.writeLastRun(Run.initial(newId, versions, isRedeployment, controller.clock().instant(), profile, reason), controller.applications().requireApplication(TenantAndApplicationId.from(id))); metric.jobStarted(newId.job()); }); } @@ -565,21 +569,22 @@ public class JobController { Optional<Run> lastRun = last(id, type); lastRun.filter(run -> ! run.hasEnded()).ifPresent(run -> abortAndWait(run.id())); - long build = 1 + lastRun.map(run -> run.versions().targetApplication().buildNumber().orElse(0)).orElse(0L); - ApplicationVersion version = ApplicationVersion.forDevelopment(build, applicationPackage.compileVersion()); + long build = 1 + lastRun.map(run -> run.versions().targetRevision().number()).orElse(0L); + RevisionId revisionId = RevisionId.forDevelopment(build, new JobId(id, type)); + ApplicationVersion version = ApplicationVersion.forDevelopment(revisionId, applicationPackage.compileVersion()); byte[] diff = getDiff(applicationPackage, deploymentId, lastRun); controller.applications().lockApplicationOrThrow(TenantAndApplicationId.from(id), application -> { - controller.applications().applicationStore().putDev(deploymentId, version, applicationPackage.zippedContent(), diff); + controller.applications().applicationStore().putDev(deploymentId, version.id(), applicationPackage.zippedContent(), diff); Version targetPlatform = platform.orElseGet(() -> findTargetPlatform(applicationPackage, lastRun, id)); + controller.applications().store(application.withRevisions(revisions -> revisions.with(version))); start(id, type, - new Versions(targetPlatform, version, lastRun.map(run -> run.versions().targetPlatform()), lastRun.map(run -> run.versions().targetApplication())), + new Versions(targetPlatform, version.id(), lastRun.map(run -> run.versions().targetPlatform()), lastRun.map(run -> run.versions().targetRevision())), false, dryRun ? JobProfile.developmentDryRun : JobProfile.development, Optional.empty()); - controller.applications().store(application.withRevisions(revisions -> revisions.with(version, new JobId(id, type)))); }); locked(id, type, __ -> { @@ -589,7 +594,7 @@ public class JobController { /* Application package diff against previous version, or against empty version if previous does not exist or is invalid */ private byte[] getDiff(ApplicationPackage applicationPackage, DeploymentId deploymentId, Optional<Run> lastRun) { - return lastRun.map(run -> run.versions().targetApplication()) + return lastRun.map(run -> run.versions().targetRevision()) .map(prevVersion -> { ApplicationPackage previous; try { @@ -678,19 +683,21 @@ public class JobController { /** Locks all runs and modifies the list of historic runs for the given application and job type. */ private void locked(ApplicationId id, JobType type, Consumer<SortedMap<RunId, Run>> modifications) { + Application application = controller.applications().requireApplication(TenantAndApplicationId.from(id)); try (Lock __ = curator.lock(id, type)) { SortedMap<RunId, Run> runs = new TreeMap<>(curator.readHistoricRuns(id, type)); modifications.accept(runs); - curator.writeHistoricRuns(id, type, runs.values()); + curator.writeHistoricRuns(id, type, runs.values(), application); } } /** Locks and modifies the run with the given id, provided it is still active. */ public void locked(RunId id, UnaryOperator<Run> modifications) { + Application application = controller.applications().requireApplication(TenantAndApplicationId.from(id.application())); try (Lock __ = curator.lock(id.application(), id.type())) { active(id).ifPresent(run -> { run = modifications.apply(run); - curator.writeLastRun(run); + curator.writeLastRun(run, application); }); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java index d06bdc45583..639702128d3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.InstanceName; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import java.time.Instant; import java.util.Collection; @@ -174,8 +175,8 @@ public class JobList extends AbstractFilteringList<JobStatus, JobList> { } /** Returns the subset of jobs where the run of the indicated type was on the given version */ - public JobList on(ApplicationVersion version) { - return matching(run -> run.versions().targetApplication().equals(version)); + public JobList on(RevisionId revision) { + return matching(run -> run.versions().targetRevision().equals(revision)); } /** Returns the subset of jobs where the run of the indicated type was on the given version */ @@ -196,7 +197,7 @@ public class JobList extends AbstractFilteringList<JobStatus, JobList> { if (job.isSuccess()) return false; if (job.lastSuccess().isEmpty()) return true; // An application which never succeeded is surely bad. if ( ! job.firstFailing().get().versions().targetPlatform().equals(job.lastSuccess().get().versions().targetPlatform())) return false; // Version change may be to blame. - return ! job.firstFailing().get().versions().targetApplication().equals(job.lastSuccess().get().versions().targetApplication()); // Return whether there is an application change. + return ! job.firstFailing().get().versions().targetRevision().equals(job.lastSuccess().get().versions().targetRevision()); // Return whether there is an application change. } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RevisionHistory.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RevisionHistory.java index cfab02ea35f..5bdc980f11a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RevisionHistory.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RevisionHistory.java @@ -1,5 +1,6 @@ package com.yahoo.vespa.hosted.controller.deployment; +import ai.vespa.validation.Validation; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; @@ -16,6 +17,7 @@ import java.util.Optional; import java.util.OptionalLong; import java.util.TreeMap; +import static ai.vespa.validation.Validation.require; import static java.util.Collections.emptyNavigableMap; import static java.util.stream.Collectors.toList; @@ -77,39 +79,35 @@ public class RevisionHistory { return new RevisionHistory(production, development); } - /** Returns a copy of this with the production revision added or updated. */ + /** Returns a copy of this with the revision added or updated. */ public RevisionHistory with(ApplicationVersion revision) { - if ( ! production.isEmpty() && revision.bundleHash().flatMap(hash -> production.lastEntry().getValue().bundleHash().map(hash::equals)).orElse(false)) - revision = revision.skipped(); + if (revision.id().isProduction()) { + if ( ! production.isEmpty() && revision.bundleHash().flatMap(hash -> production.lastEntry().getValue().bundleHash().map(hash::equals)).orElse(false)) + revision = revision.skipped(); - NavigableMap<RevisionId, ApplicationVersion> production = new TreeMap<>(this.production); - production.put(revision.id(), revision); - return new RevisionHistory(production, development); - } - - /** Returns a copy of this with the new development revision added, and the previous version without a package. */ - public RevisionHistory with(ApplicationVersion revision, JobId job) { - NavigableMap<JobId, NavigableMap<RevisionId, ApplicationVersion>> development = new TreeMap<>(this.development); - NavigableMap<RevisionId, ApplicationVersion> revisions = development.compute(job, (__, old) -> new TreeMap<>(old != null ? old : emptyNavigableMap())); - if ( ! revisions.isEmpty()) revisions.compute(revisions.lastKey(), (__, last) -> last.withoutPackage()); - revisions.put(revision.id(), revision); - return new RevisionHistory(production, development); + NavigableMap<RevisionId, ApplicationVersion> production = new TreeMap<>(this.production); + production.put(revision.id(), revision); + return new RevisionHistory(production, development); + } + else { + NavigableMap<JobId, NavigableMap<RevisionId, ApplicationVersion>> development = new TreeMap<>(this.development); + NavigableMap<RevisionId, ApplicationVersion> revisions = development.compute(revision.id().job(), (__, old) -> new TreeMap<>(old != null ? old : emptyNavigableMap())); + if ( ! revisions.isEmpty()) revisions.compute(revisions.lastKey(), (__, last) -> last.withoutPackage()); + revisions.put(revision.id(), revision); + return new RevisionHistory(production, development); + } } // Fallback for when an application version isn't known for the given key. - private static ApplicationVersion revisionOf(RevisionId id, boolean production) { - return new ApplicationVersion(Optional.empty(), OptionalLong.of(id.number()), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), ! production, Optional.empty(), false, false, Optional.empty(), 0); + private static ApplicationVersion revisionOf(RevisionId id) { + return new ApplicationVersion(id, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), false, false, Optional.empty(), 0); } /** Returns the production {@link ApplicationVersion} with this revision ID. */ public ApplicationVersion get(RevisionId id) { - return production.getOrDefault(id, revisionOf(id, true)); - } - - /** Returns the development {@link ApplicationVersion} for the give job, with this revision ID. */ - public ApplicationVersion get(RevisionId id, JobId job) { - return development.getOrDefault(job, emptyNavigableMap()) - .getOrDefault(id, revisionOf(id, false)); + return id.isProduction() ? production.getOrDefault(id, revisionOf(id)) + : development.getOrDefault(id.job(), emptyNavigableMap()) + .getOrDefault(id, revisionOf(id)); } /** Returns the last submitted production build. */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java index 5299c59b774..f4c4b8bebd4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java @@ -5,6 +5,7 @@ import com.yahoo.component.Version; import com.yahoo.text.Text; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; @@ -22,24 +23,24 @@ import static java.util.Objects.requireNonNull; public class Versions { private final Version targetPlatform; - private final ApplicationVersion targetApplication; + private final RevisionId targetRevision; private final Optional<Version> sourcePlatform; - private final Optional<ApplicationVersion> sourceApplication; + private final Optional<RevisionId> sourceRevision; - public Versions(Version targetPlatform, ApplicationVersion targetApplication, Optional<Version> sourcePlatform, - Optional<ApplicationVersion> sourceApplication) { - if (sourcePlatform.isPresent() ^ sourceApplication.isPresent()) + public Versions(Version targetPlatform, RevisionId targetRevision, Optional<Version> sourcePlatform, + Optional<RevisionId> sourceRevision) { + if (sourcePlatform.isPresent() ^ sourceRevision.isPresent()) throw new IllegalArgumentException("Sources must both be present or absent."); this.targetPlatform = requireNonNull(targetPlatform); - this.targetApplication = requireNonNull(targetApplication); + this.targetRevision = requireNonNull(targetRevision); this.sourcePlatform = requireNonNull(sourcePlatform); - this.sourceApplication = requireNonNull(sourceApplication); + this.sourceRevision = requireNonNull(sourceRevision); } /** A copy of this, without source versions. */ public Versions withoutSources() { - return new Versions(targetPlatform, targetApplication, Optional.empty(), Optional.empty()); + return new Versions(targetPlatform, targetRevision, Optional.empty(), Optional.empty()); } /** Target platform version for this */ @@ -47,9 +48,9 @@ public class Versions { return targetPlatform; } - /** Target application version for this */ - public ApplicationVersion targetApplication() { - return targetApplication; + /** Target revision for this */ + public RevisionId targetRevision() { + return targetRevision; } /** Source platform version for this */ @@ -58,27 +59,27 @@ public class Versions { } /** Source application version for this */ - public Optional<ApplicationVersion> sourceApplication() { - return sourceApplication; + public Optional<RevisionId> sourceRevision() { + return sourceRevision; } /** Returns whether source versions are present and match those of the given job other versions. */ public boolean sourcesMatchIfPresent(Versions versions) { return (sourcePlatform.map(targetPlatform::equals).orElse(true) || sourcePlatform.equals(versions.sourcePlatform())) && - (sourceApplication.map(targetApplication::equals).orElse(true) || - sourceApplication.equals(versions.sourceApplication())); + (sourceRevision.map(targetRevision::equals).orElse(true) || + sourceRevision.equals(versions.sourceRevision())); } public boolean targetsMatch(Versions versions) { return targetPlatform.equals(versions.targetPlatform()) && - targetApplication.equals(versions.targetApplication()); + targetRevision.equals(versions.targetRevision()); } /** Returns wheter this change could result in the given target versions. */ public boolean targetsMatch(Change change) { return change.platform().map(targetPlatform::equals).orElse(true) - && change.application().map(targetApplication::equals).orElse(true); + && change.revision().map(targetRevision::equals).orElse(true); } @Override @@ -87,43 +88,43 @@ public class Versions { if ( ! (o instanceof Versions)) return false; Versions versions = (Versions) o; return Objects.equals(targetPlatform, versions.targetPlatform) && - Objects.equals(targetApplication, versions.targetApplication) && + Objects.equals(targetRevision, versions.targetRevision) && Objects.equals(sourcePlatform, versions.sourcePlatform) && - Objects.equals(sourceApplication, versions.sourceApplication); + Objects.equals(sourceRevision, versions.sourceRevision); } @Override public int hashCode() { - return Objects.hash(targetPlatform, targetApplication, sourcePlatform, sourceApplication); + return Objects.hash(targetPlatform, targetRevision, sourcePlatform, sourceRevision); } @Override public String toString() { - return Text.format("platform %s%s, application %s%s", - sourcePlatform.filter(source -> !source.equals(targetPlatform)) + return Text.format("platform %s%s, revision %s%s", + sourcePlatform.filter(source -> ! source.equals(targetPlatform)) .map(source -> source + " -> ").orElse(""), targetPlatform, - sourceApplication.filter(source -> !source.equals(targetApplication)) - .map(source -> source.stringId() + " -> ").orElse(""), - targetApplication.stringId()); + sourceRevision.filter(source -> ! source.equals(targetRevision)) + .map(source -> source + " -> ").orElse(""), + targetRevision); } /** Create versions using given change and application */ public static Versions from(Change change, Application application, Optional<Version> existingPlatform, - Optional<ApplicationVersion> existingApplication, Version defaultPlatformVersion) { + Optional<RevisionId> existingRevision, Version defaultPlatformVersion) { return new Versions(targetPlatform(application, change, existingPlatform, defaultPlatformVersion), - targetApplication(application, change, existingApplication), + targetRevision(application, change, existingRevision), existingPlatform, - existingApplication); + existingRevision); } /** Create versions using given change and application */ public static Versions from(Change change, Application application, Optional<Deployment> deployment, Version defaultPlatformVersion) { return new Versions(targetPlatform(application, change, deployment.map(Deployment::version), defaultPlatformVersion), - targetApplication(application, change, deployment.map(Deployment::applicationVersion)), + targetRevision(application, change, deployment.map(Deployment::revision)), deployment.map(Deployment::version), - deployment.map(Deployment::applicationVersion)); + deployment.map(Deployment::revision)); } private static Version targetPlatform(Application application, Change change, Optional<Version> existing, @@ -135,16 +136,16 @@ public class Versions { .orElseGet(() -> application.oldestDeployedPlatform().orElse(defaultVersion)); } - private static ApplicationVersion targetApplication(Application application, Change change, - Optional<ApplicationVersion> existing) { - return change.application() + private static RevisionId targetRevision(Application application, Change change, + Optional<RevisionId> existing) { + return change.revision() .or(() -> existing) - .orElseGet(() -> defaultApplicationVersion(application)); + .orElseGet(() -> defaultRevision(application)); } - private static ApplicationVersion defaultApplicationVersion(Application application) { - return application.oldestDeployedApplication() - .or(() -> application.revisions().last()) + private static RevisionId defaultRevision(Application application) { + return application.oldestDeployedRevision() + .or(() -> application.revisions().last().map(ApplicationVersion::id)) .orElseThrow(() -> new IllegalStateException("no known prod revisions, but asked for one, for " + application)); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java index 7392b4c6e38..144c27b9e5e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java @@ -51,8 +51,8 @@ public class DeploymentUpgrader extends ControllerMaintainer { if ( ! deployment.zone().environment().isManuallyDeployed()) continue; Run last = controller().jobController().last(job).get(); - Versions target = new Versions(targetPlatform, last.versions().targetApplication(), Optional.of(last.versions().targetPlatform()), Optional.of(last.versions().targetApplication())); - if (last.versions().targetApplication().compileVersion() + Versions target = new Versions(targetPlatform, last.versions().targetRevision(), Optional.of(last.versions().targetPlatform()), Optional.of(last.versions().targetRevision())); + if (application.revisions().get(last.versions().targetRevision()).compileVersion() .map(version -> controller().applications().versionCompatibility(instance.id()).refuse(version, target.targetPlatform())) .orElse(false)) continue; if ( ! deployment.version().isBefore(target.targetPlatform())) continue; 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 68965ba8ef9..a2483e41a64 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 @@ -4,9 +4,11 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.component.Version; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.ValidationOverrides; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.security.KeyUtils; import com.yahoo.slime.ArrayTraverser; @@ -20,6 +22,7 @@ import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; @@ -51,6 +54,7 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; +import java.util.function.Function; /** * Serializes {@link Application}s to/from slime. @@ -154,6 +158,12 @@ public class ApplicationSerializer { private static final String deploymentCostField = "cost"; + private final SystemName system; + + public ApplicationSerializer(SystemName system) { + this.system = system; + } + // ------------------ Serialization public Slime toSlime(Application application) { @@ -171,7 +181,6 @@ public class ApplicationSerializer { root.setDouble(queryQualityField, application.metrics().queryServiceQuality()); root.setDouble(writeQualityField, application.metrics().writeServiceQuality()); deployKeysToSlime(application.deployKeys(), root.setArray(pemDeployKeysField)); - revisionsToSlime(application.revisions().withPackage(), root.setArray(versionsField)); revisionsToSlime(application.revisions(), root.setArray(prodVersionsField), root.setArray(devVersionsField)); instancesToSlime(application, root.setArray(instancesField)); return slime; @@ -181,11 +190,11 @@ public class ApplicationSerializer { for (Instance instance : application.instances().values()) { Cursor instanceObject = array.addObject(); instanceObject.setString(instanceNameField, instance.name().value()); - deploymentsToSlime(instance.deployments().values(), instanceObject.setArray(deploymentsField)); + deploymentsToSlime(instance.deployments().values(), application, instanceObject.setArray(deploymentsField)); toSlime(instance.jobPauses(), instanceObject.setObject(deploymentJobsField)); assignedRotationsToSlime(instance.rotations(), instanceObject); toSlime(instance.rotationStatus(), instanceObject.setArray(rotationStatusField)); - toSlime(instance.change(), instanceObject, deployingField); + toSlime(instance.change(), application, instanceObject, deployingField); } } @@ -193,16 +202,16 @@ public class ApplicationSerializer { deployKeys.forEach(key -> array.addString(KeyUtils.toPem(key))); } - private void deploymentsToSlime(Collection<Deployment> deployments, Cursor array) { + private void deploymentsToSlime(Collection<Deployment> deployments, Application application, Cursor array) { for (Deployment deployment : deployments) - deploymentToSlime(deployment, array.addObject()); + deploymentToSlime(deployment, application.revisions()::get, array.addObject()); } - private void deploymentToSlime(Deployment deployment, Cursor object) { + private void deploymentToSlime(Deployment deployment, Function<RevisionId, ApplicationVersion> compatibility, Cursor object) { zoneIdToSlime(deployment.zone(), object.setObject(zoneField)); object.setString(versionField, deployment.version().toString()); object.setLong(deployTimeField, deployment.at().toEpochMilli()); - toSlime(deployment.applicationVersion(), object.setObject(applicationPackageRevisionField)); + toSlime(compatibility.apply(deployment.revision()), object.setObject(applicationPackageRevisionField)); deploymentMetricsToSlime(deployment.metrics(), object); deployment.activity().lastQueried().ifPresent(instant -> object.setLong(lastQueriedField, instant.toEpochMilli())); deployment.activity().lastWritten().ifPresent(instant -> object.setLong(lastWrittenField, instant.toEpochMilli())); @@ -276,14 +285,14 @@ public class ApplicationSerializer { }); } - private void toSlime(Change deploying, Cursor parentObject, String fieldName) { + private void toSlime(Change deploying, Application application, Cursor parentObject, String fieldName) { if (deploying.isEmpty()) return; Cursor object = parentObject.setObject(fieldName); if (deploying.platform().isPresent()) object.setString(versionField, deploying.platform().get().toString()); - if (deploying.application().isPresent()) - toSlime(deploying.application().get(), object); + if (deploying.revision().isPresent()) + toSlime(application.revisions().get(deploying.revision().get()), object); if (deploying.isPinned()) object.setBool(pinnedField, true); } @@ -338,21 +347,20 @@ public class ApplicationSerializer { Set<PublicKey> deployKeys = deployKeysFromSlime(root.field(pemDeployKeysField)); List<Instance> instances = instancesFromSlime(id, root.field(instancesField)); OptionalLong projectId = SlimeUtils.optionalLong(root.field(projectIdField)); - RevisionHistory revisions = revisionsFromSlime(root.field(versionsField), root.field(prodVersionsField), root.field(devVersionsField), id); + RevisionHistory revisions = revisionsFromSlime(root.field(prodVersionsField), root.field(devVersionsField), id); return new Application(id, createdAt, deploymentSpec, validationOverrides, deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys, projectId, revisions, instances); } - // TODO jonmv: read only from prodVersionsArray, once data is migrated. - private RevisionHistory revisionsFromSlime(Inspector versionsArray, Inspector prodVersionsArray, Inspector devVersionsArray, TenantAndApplicationId id) { - // Once last controller updates storage after upgrade, these should be in sync. - List<ApplicationVersion> revisions = prodVersionsArray.valid() ? revisionsFromSlime(prodVersionsArray) - : revisionsFromSlime(versionsArray); + private RevisionHistory revisionsFromSlime(Inspector prodVersionsArray, Inspector devVersionsArray, TenantAndApplicationId id) { + List<ApplicationVersion> revisions = revisionsFromSlime(prodVersionsArray, null); Map<JobId, List<ApplicationVersion>> devRevisions = new HashMap<>(); - devVersionsArray.traverse((ArrayTraverser) (__, devRevisionsObject) -> - devRevisions.put(jobIdFromSlime(id, devRevisionsObject), revisionsFromSlime(devRevisionsObject.field(versionsField)))); + devVersionsArray.traverse((ArrayTraverser) (__, devRevisionsObject) -> { + JobId job = jobIdFromSlime(id, devRevisionsObject); + devRevisions.put(jobIdFromSlime(id, devRevisionsObject), revisionsFromSlime(devRevisionsObject.field(versionsField), job)); + }); return RevisionHistory.ofRevisions(revisions, devRevisions); } @@ -362,9 +370,9 @@ public class ApplicationSerializer { JobType.fromJobName(idObject.field(jobTypeField).asString())); } - private List<ApplicationVersion> revisionsFromSlime(Inspector versionsArray) { + private List<ApplicationVersion> revisionsFromSlime(Inspector versionsArray, JobId job) { List<ApplicationVersion> revisions = new ArrayList<>(); - versionsArray.traverse((ArrayTraverser) (__, revisionObject) -> revisions.add(applicationVersionFromSlime(revisionObject))); + versionsArray.traverse((ArrayTraverser) (__, revisionObject) -> revisions.add(applicationVersionFromSlime(revisionObject, job))); return revisions; } @@ -372,7 +380,7 @@ public class ApplicationSerializer { List<Instance> instances = new ArrayList<>(); field.traverse((ArrayTraverser) (name, object) -> { InstanceName instanceName = InstanceName.from(object.field(instanceNameField).asString()); - List<Deployment> deployments = deploymentsFromSlime(object.field(deploymentsField)); + List<Deployment> deployments = deploymentsFromSlime(object.field(deploymentsField), id.instance(instanceName)); Map<JobType, Instant> jobPauses = jobPausesFromSlime(object.field(deploymentJobsField)); List<AssignedRotation> assignedRotations = assignedRotationsFromSlime(object); RotationStatus rotationStatus = rotationStatusFromSlime(object); @@ -393,15 +401,16 @@ public class ApplicationSerializer { return keys; } - private List<Deployment> deploymentsFromSlime(Inspector array) { + private List<Deployment> deploymentsFromSlime(Inspector array, ApplicationId id) { List<Deployment> deployments = new ArrayList<>(); - array.traverse((ArrayTraverser) (int i, Inspector item) -> deployments.add(deploymentFromSlime(item))); + array.traverse((ArrayTraverser) (int i, Inspector item) -> deployments.add(deploymentFromSlime(item, id))); return deployments; } - private Deployment deploymentFromSlime(Inspector deploymentObject) { - return new Deployment(zoneIdFromSlime(deploymentObject.field(zoneField)), - applicationVersionFromSlime(deploymentObject.field(applicationPackageRevisionField)), + private Deployment deploymentFromSlime(Inspector deploymentObject, ApplicationId id) { + ZoneId zone = zoneIdFromSlime(deploymentObject.field(zoneField)); + return new Deployment(zone, + revisionFromSlime(deploymentObject.field(applicationPackageRevisionField), new JobId(id, JobType.from(system, zone).get())), Version.fromString(deploymentObject.field(versionField).asString()), SlimeUtils.instant(deploymentObject.field(deployTimeField)), deploymentMetricsFromSlime(deploymentObject.field(deploymentMetricsField)), @@ -458,27 +467,30 @@ public class ApplicationSerializer { return ZoneId.from(object.field(environmentField).asString(), object.field(regionField).asString()); } - private ApplicationVersion applicationVersionFromSlime(Inspector object) { - if ( ! object.valid()) return ApplicationVersion.unknown; - OptionalLong applicationBuildNumber = SlimeUtils.optionalLong(object.field(applicationBuildNumberField)); - if (applicationBuildNumber.isEmpty()) - return ApplicationVersion.unknown; + private RevisionId revisionFromSlime(Inspector object, JobId job) { + long build = object.field(applicationBuildNumberField).asLong(); + boolean production = object.field(deployedDirectlyField).valid() // TODO jonmv: remove after migration + && build > 0 + && ! object.field(deployedDirectlyField).asBool(); + return production ? RevisionId.forProduction(build) : RevisionId.forDevelopment(build, job); + } + private ApplicationVersion applicationVersionFromSlime(Inspector object, JobId job) { + RevisionId id = revisionFromSlime(object, job); Optional<SourceRevision> sourceRevision = sourceRevisionFromSlime(object.field(sourceRevisionField)); Optional<String> authorEmail = SlimeUtils.optionalString(object.field(authorEmailField)); Optional<Version> compileVersion = SlimeUtils.optionalString(object.field(compileVersionField)).map(Version::fromString); Optional<Instant> buildTime = SlimeUtils.optionalInstant(object.field(buildTimeField)); Optional<String> sourceUrl = SlimeUtils.optionalString(object.field(sourceUrlField)); Optional<String> commit = SlimeUtils.optionalString(object.field(commitField)); - boolean deployedDirectly = object.field(deployedDirectlyField).asBool(); - boolean hasPackage = ! object.field(hasPackageField).valid() || object.field(hasPackageField).asBool(); // TODO jonmv: remove default + boolean hasPackage = object.field(hasPackageField).asBool(); boolean shouldSkip = object.field(shouldSkipField).asBool(); Optional<String> description = SlimeUtils.optionalString(object.field(descriptionField)); int risk = (int) object.field(riskField).asLong(); Optional<String> bundleHash = SlimeUtils.optionalString(object.field(bundleHashField)); - return new ApplicationVersion(sourceRevision, applicationBuildNumber, authorEmail, compileVersion, buildTime, sourceUrl, - commit, deployedDirectly, bundleHash, hasPackage, shouldSkip, description, risk); + return new ApplicationVersion(id, sourceRevision, authorEmail, compileVersion, buildTime, sourceUrl, + commit, bundleHash, hasPackage, shouldSkip, description, risk); } private Optional<SourceRevision> sourceRevisionFromSlime(Inspector object) { @@ -504,7 +516,7 @@ public class ApplicationSerializer { if (versionFieldValue.valid()) change = Change.of(Version.fromString(versionFieldValue.asString())); if (object.field(applicationBuildNumberField).valid()) - change = change.with(applicationVersionFromSlime(object)); + change = change.with(revisionFromSlime(object, null)); if (object.field(pinnedField).asBool()) change = change.withPin(); return change; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java index e5327cb5362..0bfdd53eeab 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java @@ -7,6 +7,7 @@ import com.yahoo.component.Version; import com.yahoo.concurrent.UncheckedTimeoutException; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostName; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.path.Path; @@ -20,11 +21,13 @@ import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.flags.StringFlag; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; +import com.yahoo.vespa.hosted.controller.api.integration.ServiceRegistry; import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveBucket; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; import com.yahoo.vespa.hosted.controller.auditlog.AuditLog; import com.yahoo.vespa.hosted.controller.deployment.RetriggerEntry; @@ -103,7 +106,6 @@ public class CuratorDb { private final ControllerVersionSerializer controllerVersionSerializer = new ControllerVersionSerializer(); private final ConfidenceOverrideSerializer confidenceOverrideSerializer = new ConfidenceOverrideSerializer(); private final TenantSerializer tenantSerializer = new TenantSerializer(); - private final ApplicationSerializer applicationSerializer = new ApplicationSerializer(); private final RunSerializer runSerializer = new RunSerializer(); private final OsVersionSerializer osVersionSerializer = new OsVersionSerializer(); private final OsVersionTargetSerializer osVersionTargetSerializer = new OsVersionTargetSerializer(osVersionSerializer); @@ -112,6 +114,7 @@ public class CuratorDb { private final ZoneRoutingPolicySerializer zoneRoutingPolicySerializer = new ZoneRoutingPolicySerializer(routingPolicySerializer); private final AuditLogSerializer auditLogSerializer = new AuditLogSerializer(); private final NameServiceQueueSerializer nameServiceQueueSerializer = new NameServiceQueueSerializer(); + private final ApplicationSerializer applicationSerializer; private final Curator curator; private final Duration tryLockTimeout; @@ -125,14 +128,15 @@ public class CuratorDb { private final Map<Path, Pair<Integer, NavigableMap<RunId, Run>>> cachedHistoricRuns = new ConcurrentHashMap<>(); @Inject - public CuratorDb(Curator curator, FlagSource flagSource) { - this(curator, defaultTryLockTimeout, flagSource); + public CuratorDb(Curator curator, FlagSource flagSource, ServiceRegistry services) { + this(curator, defaultTryLockTimeout, flagSource, services.zoneRegistry().system()); } - CuratorDb(Curator curator, Duration tryLockTimeout, FlagSource flagSource) { + CuratorDb(Curator curator, Duration tryLockTimeout, FlagSource flagSource, SystemName system) { this.curator = curator; this.tryLockTimeout = tryLockTimeout; this.lockScheme = Flags.CONTROLLER_LOCK_SCHEME.bindTo(flagSource); + this.applicationSerializer = new ApplicationSerializer(system); } /** Returns all hostnames configured to be part of this ZooKeeper cluster */ @@ -383,7 +387,7 @@ public class CuratorDb { } public Optional<Tenant> readTenant(TenantName name) { - return readSlime(tenantPath(name)).map(bytes -> tenantSerializer.tenantFrom(bytes)); + return readSlime(tenantPath(name)).map(tenantSerializer::tenantFrom); } public List<Tenant> readTenants() { @@ -415,7 +419,7 @@ public class CuratorDb { .map(stat -> cachedApplications.compute(path, (__, old) -> old != null && old.getFirst() == stat.getVersion() ? old - : new Pair<>(stat.getVersion(), read(path, applicationSerializer::fromSlime).get())).getSecond()); + : new Pair<>(stat.getVersion(), read(path, bytes -> applicationSerializer.fromSlime(bytes)).get())).getSecond()); } public List<Application> readApplications(boolean canFail) { @@ -458,13 +462,13 @@ public class CuratorDb { // -------------- Job Runs ------------------------------------------------ - public void writeLastRun(Run run) { - curator.set(lastRunPath(run.id().application(), run.id().type()), asJson(runSerializer.toSlime(run))); + public void writeLastRun(Run run, Application application) { + curator.set(lastRunPath(run.id().application(), run.id().type()), asJson(runSerializer.toSlime(run, application))); } - public void writeHistoricRuns(ApplicationId id, JobType type, Iterable<Run> runs) { + public void writeHistoricRuns(ApplicationId id, JobType type, Iterable<Run> runs, Application application) { Path path = runsPath(id, type); - curator.set(path, asJson(runSerializer.toSlime(runs))); + curator.set(path, asJson(runSerializer.toSlime(runs, application))); } public Optional<Run> readLastRun(ApplicationId id, JobType type) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/MockCuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/MockCuratorDb.java index 65c9859ad72..d7391c69bf6 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/MockCuratorDb.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/MockCuratorDb.java @@ -2,6 +2,8 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.google.inject.Inject; +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.curator.mock.MockCurator; import com.yahoo.vespa.flags.InMemoryFlagSource; @@ -18,21 +20,21 @@ public class MockCuratorDb extends CuratorDb { private final MockCurator curator; @Inject - public MockCuratorDb() { - this("test-controller:2222"); + public MockCuratorDb(ConfigserverConfig config) { + this("test-controller:2222", SystemName.from(config.system())); } - public MockCuratorDb(String zooKeeperEnsembleConnectionSpec) { - this(new MockCurator() { - @Override - public String zooKeeperEnsembleConnectionSpec() { - return zooKeeperEnsembleConnectionSpec; - } - }); + public MockCuratorDb(SystemName system) { + this("test-controller:2222", system); } - public MockCuratorDb(MockCurator curator) { - super(curator, Duration.ofMillis(100), new InMemoryFlagSource()); + public MockCuratorDb(String zooKeeperEnsembleConnectionSpec, SystemName system) { + this(new MockCurator() { @Override public String zooKeeperEnsembleConnectionSpec() { return zooKeeperEnsembleConnectionSpec; } }, + system); + } + + public MockCuratorDb(MockCurator curator, SystemName system) { + super(curator, Duration.ofMillis(100), new InMemoryFlagSource(), system); this.curator = curator; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java index afe45bf6a1b..ea932fd19c8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java @@ -10,8 +10,10 @@ import com.yahoo.slime.Inspector; import com.yahoo.slime.ObjectTraverser; import com.yahoo.slime.Slime; import com.yahoo.slime.SlimeUtils; +import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.deployment.ConvergenceSummary; @@ -30,6 +32,7 @@ import java.util.NavigableMap; import java.util.Optional; import java.util.OptionalLong; import java.util.TreeMap; +import java.util.function.Function; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.aborted; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.deploymentFailed; @@ -74,7 +77,6 @@ class RunSerializer { // - REMOVING FIELDS: Stop reading the field first. Stop writing it on a later version. // - CHANGING THE FORMAT OF A FIELD: Don't do it bro. - // TODO: Remove "steps" when there are no traces of it in the controllers private static final String stepsField = "steps"; private static final String stepDetailsField = "stepDetails"; private static final String startTimeField = "startTime"; @@ -134,11 +136,12 @@ class RunSerializer { steps.put(typedStep, new StepInfo(typedStep, stepStatusOf(status.asString()), startTime)); }); - return new Run(new RunId(ApplicationId.fromSerializedForm(runObject.field(applicationField).asString()), + RunId id = new RunId(ApplicationId.fromSerializedForm(runObject.field(applicationField).asString()), JobType.fromJobName(runObject.field(jobTypeField).asString()), - runObject.field(numberField).asLong()), + runObject.field(numberField).asLong()); + return new Run(id, steps, - versionsFromSlime(runObject.field(versionsField)), + versionsFromSlime(runObject.field(versionsField), id), runObject.field(isRedeploymentField).asBool(), SlimeUtils.instant(runObject.field(startField)), SlimeUtils.optionalInstant(runObject.field(endField)), @@ -155,38 +158,26 @@ class RunSerializer { SlimeUtils.optionalString(runObject.field(reasonField))); } - private Versions versionsFromSlime(Inspector versionsObject) { + private Versions versionsFromSlime(Inspector versionsObject, RunId id) { Version targetPlatformVersion = Version.fromString(versionsObject.field(platformVersionField).asString()); - ApplicationVersion targetApplicationVersion = applicationVersionFrom(versionsObject); + RevisionId targetRevision = revisionFrom(versionsObject, id); Optional<Version> sourcePlatformVersion = versionsObject.field(sourceField).valid() ? Optional.of(Version.fromString(versionsObject.field(sourceField).field(platformVersionField).asString())) : Optional.empty(); - Optional<ApplicationVersion> sourceApplicationVersion = versionsObject.field(sourceField).valid() - ? Optional.of(applicationVersionFrom(versionsObject.field(sourceField))) + Optional<RevisionId> sourceRevision = versionsObject.field(sourceField).valid() + ? Optional.of(revisionFrom(versionsObject.field(sourceField), id)) : Optional.empty(); - return new Versions(targetPlatformVersion, targetApplicationVersion, sourcePlatformVersion, sourceApplicationVersion); + return new Versions(targetPlatformVersion, targetRevision, sourcePlatformVersion, sourceRevision); } - private ApplicationVersion applicationVersionFrom(Inspector versionObject) { - if ( ! versionObject.field(buildField).valid()) - return ApplicationVersion.unknown; - + private RevisionId revisionFrom(Inspector versionObject, RunId id) { long buildNumber = versionObject.field(buildField).asLong(); - Optional<SourceRevision> source = Optional.of(new SourceRevision(versionObject.field(repositoryField).asString(), - versionObject.field(branchField).asString(), - versionObject.field(commitField).asString())) - .filter(revision -> ! revision.commit().isBlank() && ! revision.repository().isBlank() && ! revision.branch().isBlank()); - Optional<String> authorEmail = SlimeUtils.optionalString(versionObject.field(authorEmailField)); - Optional<Version> compileVersion = SlimeUtils.optionalString(versionObject.field(compileVersionField)).map(Version::fromString); - Optional<Instant> buildTime = SlimeUtils.optionalInstant(versionObject.field(buildTimeField)); - Optional<String> sourceUrl = SlimeUtils.optionalString(versionObject.field(sourceUrlField)); - Optional<String> commit = SlimeUtils.optionalString(versionObject.field(commitField)); - boolean deployedDirectly = versionObject.field(deployedDirectlyField).asBool(); - Optional<String> bundleHash = SlimeUtils.optionalString(versionObject.field(bundleHashField)); - - return new ApplicationVersion(source, OptionalLong.of(buildNumber), authorEmail, compileVersion, buildTime, sourceUrl, commit, deployedDirectly, bundleHash, false, false, Optional.empty(), 0); + boolean production = versionObject.field(deployedDirectlyField).valid() // TODO jonmv: remove after migration + && buildNumber > 0 + && ! versionObject.field(deployedDirectlyField).asBool(); + return production ? RevisionId.forProduction(buildNumber) : RevisionId.forDevelopment(buildNumber, id.job()); } // Don't change this — introduce a separate array instead. @@ -211,20 +202,24 @@ class RunSerializer { summaryArray.entry(12).asLong())); } - Slime toSlime(Iterable<Run> runs) { + Slime toSlime(Iterable<Run> runs, Application application) { Slime slime = new Slime(); Cursor runArray = slime.setArray(); - runs.forEach(run -> toSlime(run, runArray.addObject())); + runs.forEach(run -> toSlime(run, application.revisions()::get, runArray.addObject())); return slime; } - Slime toSlime(Run run) { + Slime toSlime(Run run, Application application) { + return toSlime(run, application.revisions()::get); + } + + Slime toSlime(Run run, Function<RevisionId, ApplicationVersion> compatilibity) { Slime slime = new Slime(); - toSlime(run, slime.setObject()); + toSlime(run, compatilibity, slime.setObject()); return slime; } - private void toSlime(Run run, Cursor runObject) { + private void toSlime(Run run, Function<RevisionId, ApplicationVersion> compatibility, Cursor runObject) { runObject.setString(applicationField, run.id().application().serializedForm()); runObject.setString(jobTypeField, run.id().type().jobName()); runObject.setBool(isRedeploymentField, run.isRedeployment()); @@ -249,10 +244,10 @@ class RunSerializer { stepDetailsObject.setObject(valueOf(step)).setLong(startTimeField, valueOf(startTime)))); Cursor versionsObject = runObject.setObject(versionsField); - toSlime(run.versions().targetPlatform(), run.versions().targetApplication(), versionsObject); + toSlime(run.versions().targetPlatform(), compatibility.apply(run.versions().targetRevision()), versionsObject); run.versions().sourcePlatform().ifPresent(sourcePlatformVersion -> { toSlime(sourcePlatformVersion, - run.versions().sourceApplication() + run.versions().sourceRevision().map(compatibility) .orElseThrow(() -> new IllegalArgumentException("Source versions must be both present or absent.")), versionsObject.setObject(sourceField)); }); 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 620e4101c36..e635fc20053 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 @@ -5,6 +5,7 @@ import ai.vespa.hosted.api.Signatures; import ai.vespa.http.DomainName; import ai.vespa.http.HttpURL; import ai.vespa.http.HttpURL.Query; +import ai.vespa.validation.Validation; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Joiner; @@ -259,7 +260,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/deploying")) return deploying(path.get("tenant"), path.get("application"), path.get("instance"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/deploying/pin")) return deploying(path.get("tenant"), path.get("application"), path.get("instance"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job")) return JobControllerApiHandlerHelper.jobTypeResponse(controller, appIdFromPath(path), request.getUri()); - if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}")) return JobControllerApiHandlerHelper.runResponse(controller.jobController().runs(appIdFromPath(path), jobTypeFromPath(path)).descendingMap(), Optional.ofNullable(request.getProperty("limit")), request.getUri()); + if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}")) return JobControllerApiHandlerHelper.runResponse(controller.applications().requireApplication(TenantAndApplicationId.from(path.get("tenant"), path.get("application"))), controller.jobController().runs(appIdFromPath(path), jobTypeFromPath(path)).descendingMap(), Optional.ofNullable(request.getProperty("limit")), request.getUri()); // (((\(✘෴✘)/))) if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}/package")) return devApplicationPackage(appIdFromPath(path), jobTypeFromPath(path)); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}/diff/{number}")) return devApplicationPackageDiff(runIdFromPath(path)); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{jobtype}/test-config")) return testConfig(appIdFromPath(path), jobTypeFromPath(path)); @@ -804,8 +805,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { throw new IllegalArgumentException("Only manually deployed zones have dev packages"); ZoneId zone = type.zone(controller.system()); - ApplicationVersion version = controller.jobController().last(id, type).get().versions().targetApplication(); - byte[] applicationPackage = controller.applications().applicationStore().get(new DeploymentId(id, zone), version); + RevisionId revision = controller.jobController().last(id, type).get().versions().targetRevision(); + byte[] applicationPackage = controller.applications().applicationStore().get(new DeploymentId(id, zone), revision); return new ZipResponse(id.toFullString() + "." + zone.value() + ".zip", applicationPackage); } @@ -817,30 +818,27 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { } private HttpResponse applicationPackage(String tenantName, String applicationName, HttpRequest request) { - var tenantAndApplication = TenantAndApplicationId.from(tenantName, applicationName); - List<ApplicationVersion> versions = controller.applications().requireApplication(tenantAndApplication).revisions().withPackage(); - if (versions.isEmpty()) - throw new NotExistsException("No application package has been submitted for '" + tenantAndApplication + "'"); - - ApplicationVersion version = Optional.ofNullable(request.getProperty("build")) - .map(build -> { - try { - return Long.parseLong(build); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid build number", e); - } - }) - .map(build -> versions.stream() - .filter(ver -> ver.buildNumber().orElse(-1) == build) - .findFirst() - .orElseThrow(() -> new NotExistsException("No application package found for '" + tenantAndApplication + "' with build number " + build))) - .orElseGet(() -> versions.get(versions.size() - 1)); - + TenantAndApplicationId tenantAndApplication = TenantAndApplicationId.from(tenantName, applicationName); + long build; + String parameter = request.getProperty("build"); + if (parameter != null) + try { + build = Validation.requireAtLeast(Long.parseLong(request.getProperty("build")), "build number", 1L); + } + catch (NumberFormatException e) { + throw new IllegalArgumentException("invalid value for request parameter 'build'", e); + } + else { + build = controller.applications().requireApplication(tenantAndApplication).revisions().last() + .map(version -> version.id().number()) + .orElseThrow(() -> new NotExistsException("no application package has been submitted for " + tenantAndApplication)); + } + RevisionId revision = RevisionId.forProduction(build); boolean tests = request.getBooleanProperty("tests"); byte[] applicationPackage = tests ? - controller.applications().applicationStore().getTester(tenantAndApplication.tenant(), tenantAndApplication.application(), version) : - controller.applications().applicationStore().get(new DeploymentId(tenantAndApplication.defaultInstance(), ZoneId.defaultId()), version); - String filename = tenantAndApplication + (tests ? "-tests" : "-build") + version.buildNumber().getAsLong() + ".zip"; + controller.applications().applicationStore().getTester(tenantAndApplication.tenant(), tenantAndApplication.application(), revision) : + controller.applications().applicationStore().get(new DeploymentId(tenantAndApplication.defaultInstance(), ZoneId.defaultId()), revision); + String filename = tenantAndApplication + (tests ? "-tests" : "-build") + revision.number() + ".zip"; return new ZipResponse(filename, applicationPackage); } @@ -1326,11 +1324,11 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { application.instances().values().stream().findFirst().ifPresent(instance -> { // Currently deploying change if ( ! instance.change().isEmpty()) - toSlime(object.setObject("deploying"), instance.change()); + toSlime(object.setObject("deploying"), instance.change(), application); // Outstanding change if ( ! status.outstandingChange(instance.name()).isEmpty()) - toSlime(object.setObject("outstandingChange"), status.outstandingChange(instance.name())); + toSlime(object.setObject("outstandingChange"), status.outstandingChange(instance.name()), application); }); application.majorVersion().ifPresent(majorVersion -> object.setLong("majorVersion", majorVersion)); @@ -1370,11 +1368,11 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { .sortedJobs(status.instanceJobs(instance.name()).values()); if ( ! instance.change().isEmpty()) - toSlime(object.setObject("deploying"), instance.change()); + toSlime(object.setObject("deploying"), instance.change(), status.application()); // Outstanding change if ( ! status.outstandingChange(instance.name()).isEmpty()) - toSlime(object.setObject("outstandingChange"), status.outstandingChange(instance.name())); + toSlime(object.setObject("outstandingChange"), status.outstandingChange(instance.name()), status.application()); // Change blockers Cursor changeBlockers = object.setArray("changeBlockers"); @@ -1455,11 +1453,11 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { .sortedJobs(status.instanceJobs(instance.name()).values()); if ( ! instance.change().isEmpty()) - toSlime(object.setObject("deploying"), instance.change()); + toSlime(object.setObject("deploying"), instance.change(), application); // Outstanding change if ( ! status.outstandingChange(instance.name()).isEmpty()) - toSlime(object.setObject("outstandingChange"), status.outstandingChange(instance.name())); + toSlime(object.setObject("outstandingChange"), status.outstandingChange(instance.name()), application); // Change blockers Cursor changeBlockers = object.setArray("changeBlockers"); @@ -1574,11 +1572,9 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { return new SlimeJsonResponse(slime); } - private void toSlime(Cursor object, Change change) { + private void toSlime(Cursor object, Change change, Application application) { change.platform().ifPresent(version -> object.setString("version", version.toString())); - change.application() - .filter(version -> !version.isUnknown()) - .ifPresent(version -> JobControllerApiHandlerHelper.toSlime(object.setObject("revision"), version)); + change.revision().ifPresent(revision -> JobControllerApiHandlerHelper.toSlime(object.setObject("revision"), application.revisions().get(revision))); } private void toSlime(Endpoint endpoint, Cursor object) { @@ -1623,7 +1619,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { response.setString("nodes", withPathAndQuery("/zone/v2/" + deploymentId.zoneId().environment() + "/" + deploymentId.zoneId().region() + "/nodes/v2/node/", "recursive=true&application=" + deploymentId.applicationId().tenant() + "." + deploymentId.applicationId().application() + "." + deploymentId.applicationId().instance(), request.getUri()).toString()); response.setString("yamasUrl", monitoringSystemUri(deploymentId).toString()); response.setString("version", deployment.version().toFullString()); - response.setString("revision", deployment.applicationVersion().stringId()); + response.setString("revision", application.revisions().get(deployment.revision()).stringId()); // TODO jonmv or freva: ƪ(`▿▿▿▿´ƪ) + response.setLong("build", deployment.revision().number()); Instant lastDeploymentStart = lastDeploymentStart(deploymentId.applicationId(), deployment); response.setLong("deployTimeEpochMs", lastDeploymentStart.toEpochMilli()); controller.zoneRegistry().getDeploymentTimeToLive(deploymentId.zoneId()) @@ -1642,8 +1639,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { .map(type -> new JobId(instance.id(), type)) .map(status.jobSteps()::get) .ifPresent(stepStatus -> { - JobControllerApiHandlerHelper.toSlime( - response.setObject("applicationVersion"), deployment.applicationVersion()); + JobControllerApiHandlerHelper.toSlime(response.setObject("applicationVersion"), application.revisions().get(deployment.revision())); if ( ! status.jobsToRun().containsKey(stepStatus.job().get())) response.setString("status", "complete"); else if (stepStatus.readyAt(instance.change()).map(controller.clock().instant()::isBefore).orElse(true)) @@ -1770,7 +1766,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { Cursor root = slime.setObject(); if ( ! instance.change().isEmpty()) { instance.change().platform().ifPresent(version -> root.setString("platform", version.toString())); - instance.change().application().ifPresent(applicationVersion -> root.setString("application", applicationVersion.stringId())); + instance.change().revision().ifPresent(revision -> root.setString("application", revision.toString())); root.setBool("pinned", instance.change().isPinned()); } return new SlimeJsonResponse(slime); @@ -1915,18 +1911,19 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { StringBuilder response = new StringBuilder(); controller.applications().lockApplicationOrThrow(TenantAndApplicationId.from(id), application -> { - ApplicationVersion version = build == -1 ? application.get().revisions().last().get() - : getApplicationVersion(application.get(), build); - Change change = Change.of(version); + RevisionId revision = build == -1 ? application.get().revisions().last().get().id() + : getRevision(application.get(), build); + Change change = Change.of(revision); controller.applications().deploymentTrigger().forceChange(id, change); response.append("Triggered ").append(change).append(" for ").append(id); }); return new MessageResponse(response.toString()); } - private ApplicationVersion getApplicationVersion(Application application, Long build) { + private RevisionId getRevision(Application application, long build) { return application.revisions().withPackage().stream() - .filter(version -> version.buildNumber().stream().anyMatch(build::equals)) + .map(ApplicationVersion::id) + .filter(version -> version.number() == build) .findFirst() .filter(version -> controller.applications().applicationStore().hasBuild(application.id().tenant(), application.id().application(), @@ -2546,14 +2543,16 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { } } + /* private void toSlime(Run run, Cursor object) { object.setLong("id", run.id().number()); object.setString("version", run.versions().targetPlatform().toFullString()); - if ( ! run.versions().targetApplication().isUnknown()) - JobControllerApiHandlerHelper.toSlime(object.setObject("revision"), run.versions().targetApplication()); + if ( ! run.versions().targetRevision().isUnknown()) + JobControllerApiHandlerHelper.toSlime(object.setObject("revision"), run.versions().targetRevision()); object.setString("reason", "unknown reason"); object.setLong("at", run.end().orElse(run.start()).toEpochMilli()); } + */ private Slime toSlime(InputStream jsonStream) { try { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java index 51bd29f9838..cf3642144b1 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java @@ -20,6 +20,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.LogEntry; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.application.Change; @@ -86,19 +87,19 @@ class JobControllerApiHandlerHelper { Cursor jobObject = jobsArray.addObject(); jobObject.setString("jobName", job.type().jobName()); - toSlime(jobObject.setArray("runs"), runs, 10, baseUriForJobs); + toSlime(jobObject.setArray("runs"), runs, controller.applications().requireApplication(TenantAndApplicationId.from(id)), 10, baseUriForJobs); }); return new SlimeJsonResponse(slime); } /** Returns a response with the runs for the given job type. */ - static HttpResponse runResponse(Map<RunId, Run> runs, Optional<String> limitStr, URI baseUriForJobType) { + static HttpResponse runResponse(Application application, Map<RunId, Run> runs, Optional<String> limitStr, URI baseUriForJobType) { Slime slime = new Slime(); Cursor cursor = slime.setObject(); int limit = limitStr.map(Integer::parseInt).orElse(Integer.MAX_VALUE); - toSlime(cursor.setArray("runs"), runs.values(), limit, baseUriForJobType); + toSlime(cursor.setArray("runs"), runs.values(), application, limit, baseUriForJobType); return new SlimeJsonResponse(slime); } @@ -200,7 +201,7 @@ class JobControllerApiHandlerHelper { ApplicationVersion version = jobController.submit(TenantAndApplicationId.from(tenant, application), sourceRevision, authorEmail, sourceUrl, projectId, applicationPackage, testPackage, description, risk); - return new MessageResponse(version.toString()); + return new MessageResponse("application " + version); } /** Aborts any job of the given type. */ @@ -270,8 +271,8 @@ class JobControllerApiHandlerHelper { stepStatus.coolingDownUntil(change).ifPresent(until -> stepObject.setLong("coolingDownUntil", until.toEpochMilli())); stepStatus.blockedUntil(Change.of(controller.systemVersion(versionStatus))) // Dummy version — just anything with a platform. .ifPresent(until -> stepObject.setLong("platformBlockedUntil", until.toEpochMilli())); - application.revisions().last().map(Change::of).flatMap(stepStatus::blockedUntil) // Dummy version — just anything with an application. - .ifPresent(until -> stepObject.setLong("applicationBlockedUntil", until.toEpochMilli())); + stepStatus.blockedUntil(Change.of(RevisionId.forProduction(1))) // Dummy version — just anything with an application. + .ifPresent(until -> stepObject.setLong("applicationBlockedUntil", until.toEpochMilli())); if (stepStatus.type() == DeploymentStatus.StepType.delay) stepStatus.completedAt(change).ifPresent(completed -> stepObject.setLong("completedAt", completed.toEpochMilli())); @@ -280,7 +281,7 @@ class JobControllerApiHandlerHelper { Cursor deployingObject = stepObject.setObject("deploying"); if ( ! change.isEmpty()) { change.platform().ifPresent(version -> deployingObject.setString("platform", version.toFullString())); - change.application().ifPresent(version -> toSlime(deployingObject.setObject("application"), version)); + change.revision().ifPresent(revision -> toSlime(deployingObject.setObject("application"), application.revisions().get(revision))); } Cursor latestVersionsObject = stepObject.setObject("latestVersions"); @@ -315,20 +316,20 @@ class JobControllerApiHandlerHelper { Cursor latestApplicationObject = latestVersionsObject.setObject("application"); toSlime(latestApplicationObject.setObject("application"), latestApplication); latestApplicationObject.setLong("at", latestApplication.buildTime().orElse(Instant.EPOCH).toEpochMilli()); - latestApplicationObject.setBool("upgrade", change.application().map(latestApplication::compareTo).orElse(1) > 0 && deployments.isEmpty() - || deployments.stream().anyMatch(deployment -> deployment.applicationVersion().compareTo(latestApplication) < 0)); + latestApplicationObject.setBool("upgrade", change.revision().map(latestApplication.id()::compareTo).orElse(1) > 0 && deployments.isEmpty() + || deployments.stream().anyMatch(deployment -> deployment.revision().compareTo(latestApplication.id()) < 0)); Cursor availableArray = latestApplicationObject.setArray("available"); for (ApplicationVersion available : availableApplications) { - if ( deployments.stream().anyMatch(deployment -> deployment.applicationVersion().compareTo(available) > 0) - || deployments.stream().noneMatch(deployment -> deployment.applicationVersion().compareTo(available) < 0) && ! deployments.isEmpty() - || status.hasCompleted(stepStatus.instance(), Change.of(available)) - || change.application().map(available::compareTo).orElse(1) <= 0) + if ( deployments.stream().anyMatch(deployment -> deployment.revision().compareTo(available.id()) > 0) + || deployments.stream().noneMatch(deployment -> deployment.revision().compareTo(available.id()) < 0) && ! deployments.isEmpty() + || status.hasCompleted(stepStatus.instance(), Change.of(available.id())) + || change.revision().map(available.id()::compareTo).orElse(1) <= 0) break; toSlime(availableArray.addObject().setObject("application"), available); } - change.application().ifPresent(version -> toSlime(availableArray.addObject().setObject("application"), version)); + change.revision().ifPresent(revision -> toSlime(availableArray.addObject().setObject("application"), application.revisions().get(revision))); toSlime(latestApplicationObject.setArray("blockers"), blockers.stream().filter(ChangeBlocker::blocksRevisions)); } } @@ -345,7 +346,7 @@ class JobControllerApiHandlerHelper { if (job.type().isProduction() && job.type().isDeployment()) { status.deploymentFor(job).ifPresent(deployment -> { stepObject.setString("currentPlatform", deployment.version().toFullString()); - toSlime(stepObject.setObject("currentApplication"), deployment.applicationVersion()); + toSlime(stepObject.setObject("currentApplication"), application.revisions().get(deployment.revision())); }); } @@ -361,10 +362,10 @@ class JobControllerApiHandlerHelper { continue; // Run will be contained in the "runs" array. Cursor runObject = toRunArray.addObject(); - toSlime(runObject.setObject("versions"), versions.versions()); + toSlime(runObject.setObject("versions"), versions.versions(), application); } - toSlime(stepObject.setArray("runs"), jobStatus.runs().descendingMap().values(), 10, baseUriForJob); + toSlime(stepObject.setArray("runs"), jobStatus.runs().descendingMap().values(), application, 10, baseUriForJob); }); } @@ -388,11 +389,11 @@ class JobControllerApiHandlerHelper { version.commit().ifPresent(commit -> versionObject.setString("commit", commit)); } - private static void toSlime(Cursor versionsObject, Versions versions) { + private static void toSlime(Cursor versionsObject, Versions versions, Application application) { versionsObject.setString("targetPlatform", versions.targetPlatform().toFullString()); - toSlime(versionsObject.setObject("targetApplication"), versions.targetApplication()); + toSlime(versionsObject.setObject("targetApplication"), application.revisions().get(versions.targetRevision())); versions.sourcePlatform().ifPresent(platform -> versionsObject.setString("sourcePlatform", platform.toFullString())); - versions.sourceApplication().ifPresent(application -> toSlime(versionsObject.setObject("sourceApplication"), application)); + versions.sourceRevision().ifPresent(revision -> toSlime(versionsObject.setObject("sourceApplication"), application.revisions().get(revision))); } private static void toSlime(Cursor blockersArray, Stream<ChangeBlocker> blockers) { @@ -428,7 +429,7 @@ class JobControllerApiHandlerHelper { return candidates; } - private static void toSlime(Cursor runsArray, Collection<Run> runs, int limit, URI baseUriForJob) { + private static void toSlime(Cursor runsArray, Collection<Run> runs, Application application, int limit, URI baseUriForJob) { runs.stream().limit(limit).forEach(run -> { Cursor runObject = runsArray.addObject(); runObject.setLong("id", run.id().number()); @@ -436,7 +437,7 @@ class JobControllerApiHandlerHelper { runObject.setLong("start", run.start().toEpochMilli()); run.end().ifPresent(end -> runObject.setLong("end", end.toEpochMilli())); runObject.setString("status", run.status().name()); - toSlime(runObject.setObject("versions"), run.versions()); + toSlime(runObject.setObject("versions"), run.versions(), application); Cursor runStepsArray = runObject.setArray("steps"); run.steps().forEach((step, info) -> { Cursor runStepObject = runStepsArray.addObject(); 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 816a7370bcd..687820d15cc 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 @@ -23,6 +23,8 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.dns.LatencyAliasTarget; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; @@ -59,6 +61,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static com.yahoo.config.provision.SystemName.main; +import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.devUsEast1; import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsEast3; import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionUsWest1; import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.stagingTest; @@ -92,13 +95,13 @@ public class ControllerTest { var context = tester.newDeploymentContext(); context.submit(applicationPackage); assertEquals("Application version is known from completion of initial job", - ApplicationVersion.from(DeploymentContext.defaultSourceRevision, 1, "a@b", new Version("6.1"), Instant.ofEpochSecond(1)), - context.instance().change().application().get()); + ApplicationVersion.from(RevisionId.forProduction(1), DeploymentContext.defaultSourceRevision, "a@b", new Version("6.1"), Instant.ofEpochSecond(1)), + context.application().revisions().get(context.instance().change().revision().get())); context.runJob(systemTest); context.runJob(stagingTest); - ApplicationVersion applicationVersion = context.instance().change().application().get(); - assertFalse("Application version has been set during deployment", applicationVersion.isUnknown()); + RevisionId applicationVersion = context.instance().change().revision().get(); + assertTrue("Application version has been set during deployment", applicationVersion.isProduction()); tester.triggerJobs(); // Causes first deployment job to be triggered @@ -735,7 +738,7 @@ public class ControllerTest { context.runJob(zone, new ApplicationPackageBuilder().compileVersion(version1).build()); assertEquals(version2, context.deployment(zone).version()); - assertEquals(Optional.of(version1), context.deployment(zone).applicationVersion().compileVersion()); + assertEquals(Optional.of(version1), context.application().revisions().get(context.deployment(zone).revision()).compileVersion()); try { context.runJob(zone, new ApplicationPackageBuilder().compileVersion(version1).majorVersion(8).build()); @@ -764,11 +767,11 @@ public class ControllerTest { context.runJob(zone, new ApplicationPackageBuilder().compileVersion(version3).majorVersion(8).build()); assertEquals(version3, context.deployment(zone).version()); - assertEquals(Optional.of(version3), context.deployment(zone).applicationVersion().compileVersion()); + assertEquals(Optional.of(version3), context.application().revisions().get(context.deployment(zone).revision()).compileVersion()); context.runJob(zone, new ApplicationPackageBuilder().compileVersion(version3).build()); assertEquals(version3, context.deployment(zone).version()); - assertEquals(Optional.of(version3), context.deployment(zone).applicationVersion().compileVersion()); + assertEquals(Optional.of(version3), context.application().revisions().get(context.deployment(zone).revision()).compileVersion()); } @Test @@ -1040,7 +1043,7 @@ public class ControllerTest { @Test public void testReadableApplications() { - var db = new MockCuratorDb(); + var db = new MockCuratorDb(tester.controller().system()); var tester = new DeploymentTester(new ControllerTester(db)); // Create and deploy two applications diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index b4bf4a6c135..a2eefd47b56 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -74,6 +74,7 @@ import java.util.logging.Handler; import java.util.logging.Logger; import java.util.stream.Collectors; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -109,11 +110,11 @@ public final class ControllerTester { } public ControllerTester(ServiceRegistryMock serviceRegistryMock) { - this(new AthenzDbMock(), new MockCuratorDb(), defaultRotationsConfig(), serviceRegistryMock); + this(new AthenzDbMock(), new MockCuratorDb(serviceRegistryMock.zoneRegistry().system()), defaultRotationsConfig(), serviceRegistryMock); } public ControllerTester(RotationsConfig rotationsConfig, SystemName system) { - this(new AthenzDbMock(), new MockCuratorDb(), rotationsConfig, new ServiceRegistryMock(system)); + this(new AthenzDbMock(), new MockCuratorDb(system), rotationsConfig, new ServiceRegistryMock(system)); } public ControllerTester(MockCuratorDb curatorDb) { @@ -121,11 +122,11 @@ public final class ControllerTester { } public ControllerTester() { - this(defaultRotationsConfig(), new MockCuratorDb()); + this(defaultRotationsConfig(), new MockCuratorDb(new ServiceRegistryMock().zoneRegistry().system())); } public ControllerTester(SystemName system) { - this(new AthenzDbMock(), new MockCuratorDb(), defaultRotationsConfig(), new ServiceRegistryMock(system)); + this(new AthenzDbMock(), new MockCuratorDb(system), defaultRotationsConfig(), new ServiceRegistryMock(system)); } private ControllerTester(AthenzDbMock athenzDb, boolean inContainer, CuratorDb curator, @@ -215,9 +216,9 @@ public final class ControllerTester { } /** Set the zones and system for this and bootstrap infrastructure nodes */ - public ControllerTester setZones(List<ZoneId> zones, SystemName system) { - zoneRegistry().setZones(zones.stream().map(ZoneApiMock::from).collect(Collectors.toList())) - .setSystemName(system); + public ControllerTester setZones(List<ZoneId> zones) { + ZoneApiMock.Builder builder = ZoneApiMock.newBuilder().withSystem(zoneRegistry().system()); + zoneRegistry().setZones(zones.stream().map(zone -> builder.with(zone).build()).collect(Collectors.toList())); configServer().bootstrap(zones, SystemApplication.notController()); return this; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java index 9739ee219b8..96b0e3137cb 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/DeploymentQuotaCalculatorTest.java @@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.integration.billing.Quota; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.ApplicationData; import com.yahoo.vespa.hosted.controller.deployment.RevisionHistory; import com.yahoo.vespa.hosted.controller.metric.ApplicationMetrics; @@ -62,9 +63,8 @@ public class DeploymentQuotaCalculatorTest { var existing_dev_deployment = new Application(TenantAndApplicationId.from(ApplicationId.defaultId()), Instant.EPOCH, DeploymentSpec.empty, ValidationOverrides.empty, Optional.empty(), Optional.empty(), Optional.empty(), OptionalInt.empty(), new ApplicationMetrics(1, 1), Set.of(), OptionalLong.empty(), RevisionHistory.empty(), - List.of(new Instance(ApplicationId.defaultId()).withNewDeployment( - ZoneId.from(Environment.dev, RegionName.defaultName()), ApplicationVersion.unknown, Version.emptyVersion, Instant.EPOCH, Map.of(), - QuotaUsage.create(0.53d)))); + List.of(new Instance(ApplicationId.defaultId()).withNewDeployment(ZoneId.from(Environment.dev, RegionName.defaultName()), + RevisionId.forProduction(1), Version.emptyVersion, Instant.EPOCH, Map.of(), QuotaUsage.create(0.53d)))); Quota calculated = DeploymentQuotaCalculator.calculate(Quota.unlimited().withBudget(2), List.of(existing_dev_deployment), ApplicationId.defaultId(), ZoneId.defaultId(), DeploymentSpec.fromXml( diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java index 7191ec92440..899e567d7cc 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java @@ -24,6 +24,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud; @@ -107,7 +108,7 @@ public class DeploymentContext { private final JobRunner runner; private final DeploymentTester tester; - private ApplicationVersion lastSubmission = null; + private RevisionId lastSubmission = null; private boolean deferDnsUpdates = false; public DeploymentContext(ApplicationId instanceId, DeploymentTester tester) { @@ -172,7 +173,7 @@ public class DeploymentContext { assertTrue("Application package submitted", application.revisions().last().isPresent()); assertFalse("Submission is not already deployed", application.instances().values().stream() .anyMatch(instance -> instance.deployments().values().stream() - .anyMatch(deployment -> deployment.applicationVersion().equals(lastSubmission)))); + .anyMatch(deployment -> deployment.revision().equals(lastSubmission)))); completeRollout(application.deploymentSpec().instances().size() > 1); for (var instance : application().instances().values()) { assertFalse(instance.change().hasTargets()); @@ -187,7 +188,7 @@ public class DeploymentContext { .anyMatch(instance -> instance.deployments().values().stream() .anyMatch(deployment -> deployment.version().equals(version)))); assertEquals(version, instance().change().platform().get()); - assertFalse(instance().change().application().isPresent()); + assertFalse(instance().change().revision().isPresent()); completeRollout(); @@ -288,7 +289,7 @@ public class DeploymentContext { .projectId() .orElse(1000); // These are really set through submission, so just pick one if it hasn't been set. var testerpackage = new byte[]{ (byte) (salt >> 56), (byte) (salt >> 48), (byte) (salt >> 40), (byte) (salt >> 32), (byte) (salt >> 24), (byte) (salt >> 16), (byte) (salt >> 8), (byte) salt }; - lastSubmission = jobs.submit(applicationId, sourceRevision, Optional.of("a@b"), Optional.empty(), projectId, applicationPackage, testerpackage, Optional.empty(), risk); + lastSubmission = jobs.submit(applicationId, sourceRevision, Optional.of("a@b"), Optional.empty(), projectId, applicationPackage, testerpackage, Optional.empty(), risk).id(); return this; } @@ -349,7 +350,7 @@ public class DeploymentContext { } /** Returns the last submitted application version */ - public Optional<ApplicationVersion> lastSubmission() { + public Optional<RevisionId> lastSubmission() { return Optional.ofNullable(lastSubmission); } 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 d90f7c5fd64..04ee21117a9 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 @@ -7,13 +7,15 @@ import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.Instance; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; +import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; import org.junit.Assert; import org.junit.Test; @@ -29,6 +31,7 @@ import java.util.OptionalLong; import java.util.stream.Collectors; import static ai.vespa.validation.Validation.require; +import static com.yahoo.config.provision.SystemName.cd; import static com.yahoo.config.provision.SystemName.main; import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionApNortheast1; import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.productionApNortheast2; @@ -121,28 +124,28 @@ public class DeploymentTriggerTest { .build(); app.submit(applicationPackage).runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3); - Optional<ApplicationVersion> v0 = app.lastSubmission(); + Optional<RevisionId> v0 = app.lastSubmission(); app.submit(applicationPackage); - Optional<ApplicationVersion> v1 = app.lastSubmission(); - assertEquals(v0, app.instance().change().application()); + Optional<RevisionId> v1 = app.lastSubmission(); + assertEquals(v0, app.instance().change().revision()); // Eager tests still run before new revision rolls out. app.runJob(systemTest).runJob(stagingTest); // v0 rolls out completely. app.runJob(testUsEast3); - assertEquals(Optional.empty(), app.instance().change().application()); + assertEquals(Optional.empty(), app.instance().change().revision()); // v1 starts rolling when v0 is done. tester.outstandingChangeDeployer().run(); - assertEquals(v1, app.instance().change().application()); + assertEquals(v1, app.instance().change().revision()); // v1 fails, so v2 starts immediately. app.runJob(productionUsEast3).failDeployment(testUsEast3); app.submit(applicationPackage); - Optional<ApplicationVersion> v2 = app.lastSubmission(); - assertEquals(v2, app.instance().change().application()); + Optional<RevisionId> v2 = app.lastSubmission(); + assertEquals(v2, app.instance().change().revision()); } @Test @@ -223,7 +226,7 @@ public class DeploymentTriggerTest { DeploymentContext app = tester.newDeploymentContext().submit(firstPackage, 5417, 0); var version = app.lastSubmission(); - assertEquals(version, app.instance().change().application()); + assertEquals(version, app.instance().change().revision()); app.runJob(systemTest) .runJob(stagingTest) .runJob(productionUsEast3); @@ -241,7 +244,7 @@ public class DeploymentTriggerTest { app.submit(secondPackage, 5417, 0); app.triggerJobs(); assertEquals(List.of(), tester.jobs().active()); - assertEquals(version, app.instance().change().application()); + assertEquals(version, app.instance().change().revision()); tester.clock().advance(Duration.ofHours(1)); app.runJob(testUsEast3); @@ -263,45 +266,45 @@ public class DeploymentTriggerTest { .build(); DeploymentContext app = tester.newDeploymentContext() .submit(appPackage); - Optional<ApplicationVersion> revision1 = app.lastSubmission(); + Optional<RevisionId> revision1 = app.lastSubmission(); app.submit(appPackage); - Optional<ApplicationVersion> revision2 = app.lastSubmission(); + Optional<RevisionId> revision2 = app.lastSubmission(); app.submit(appPackage); - Optional<ApplicationVersion> revision3 = app.lastSubmission(); + Optional<RevisionId> revision3 = app.lastSubmission(); app.submit(appPackage); - Optional<ApplicationVersion> revision4 = app.lastSubmission(); + Optional<RevisionId> revision4 = app.lastSubmission(); app.submit(appPackage); - Optional<ApplicationVersion> revision5 = app.lastSubmission(); + Optional<RevisionId> revision5 = app.lastSubmission(); // 5 revisions submitted; the first is rolling out, and the others are queued. tester.outstandingChangeDeployer().run(); - assertEquals(revision1, app.instance().change().application()); - assertEquals(revision2, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).application()); + assertEquals(revision1, app.instance().change().revision()); + assertEquals(revision2, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).revision()); // The second revision is set as the target by user interaction. tester.deploymentTrigger().forceChange(app.instanceId(), Change.of(revision2.get())); tester.outstandingChangeDeployer().run(); - assertEquals(revision2, app.instance().change().application()); - assertEquals(revision3, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).application()); + assertEquals(revision2, app.instance().change().revision()); + assertEquals(revision3, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).revision()); // The second revision deploys completely, and the third starts rolling out. app.runJob(systemTest).runJob(stagingTest) .runJob(productionUsEast3); tester.outstandingChangeDeployer().run(); tester.outstandingChangeDeployer().run(); - assertEquals(revision3, app.instance().change().application()); - assertEquals(revision4, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).application()); + assertEquals(revision3, app.instance().change().revision()); + assertEquals(revision4, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).revision()); // The third revision fails, and the fourth is chosen to replace it. app.triggerJobs().timeOutConvergence(systemTest); tester.outstandingChangeDeployer().run(); tester.outstandingChangeDeployer().run(); - assertEquals(revision4, app.instance().change().application()); - assertEquals(revision5, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).application()); + assertEquals(revision4, app.instance().change().revision()); + assertEquals(revision5, app.deploymentStatus().outstandingChange(InstanceName.defaultName()).revision()); // Tests for outstanding change are relevant when current revision completes. app.runJob(systemTest).runJob(systemTest) @@ -309,7 +312,7 @@ public class DeploymentTriggerTest { .runJob(productionUsEast3); tester.outstandingChangeDeployer().run(); tester.outstandingChangeDeployer().run(); - assertEquals(revision5, app.instance().change().application()); + assertEquals(revision5, app.instance().change().revision()); assertEquals(Change.empty(), app.deploymentStatus().outstandingChange(InstanceName.defaultName())); app.runJob(productionUsEast3); } @@ -504,8 +507,8 @@ public class DeploymentTriggerTest { tester.clock().advance(Duration.ofHours(1)); app.submit(applicationPackage); app.runJob(productionUsWest1); - assertEquals(1, app.instanceJobs().get(productionUsWest1).lastSuccess().get().versions().targetApplication().buildNumber().getAsLong()); - assertEquals(2, app.deploymentStatus().outstandingChange(app.instance().name()).application().get().buildNumber().getAsLong()); + assertEquals(1, app.instanceJobs().get(productionUsWest1).lastSuccess().get().versions().targetRevision().number()); + assertEquals(2, app.deploymentStatus().outstandingChange(app.instance().name()).revision().get().number()); tester.triggerJobs(); // Platform upgrade keeps rolling, since it began before block window, and tests for the new revision have also started. @@ -585,8 +588,8 @@ public class DeploymentTriggerTest { .runJob(stagingTest) .timeOutUpgrade(productionUsCentral1); - ApplicationVersion appVersion1 = app.lastSubmission().get(); - assertEquals(appVersion1, app.deployment(ZoneId.from("prod.us-central-1")).applicationVersion()); + RevisionId appVersion1 = app.lastSubmission().get(); + assertEquals(appVersion1, app.deployment(ZoneId.from("prod.us-central-1")).revision()); // Verify the application change is not removed when platform change is cancelled. tester.deploymentTrigger().cancelChange(app.instanceId(), PLATFORM); @@ -608,12 +611,12 @@ public class DeploymentTriggerTest { // Finally, the two production jobs complete, in order. app.runJob(productionUsCentral1).runJob(productionEuWest1); - assertEquals(appVersion1, app.deployment(ZoneId.from("prod.us-central-1")).applicationVersion()); + assertEquals(appVersion1, app.deployment(ZoneId.from("prod.us-central-1")).revision()); } - ApplicationVersion latestDeployed(Instance instance) { + RevisionId latestDeployed(Instance instance) { return instance.productionDeployments().values().stream() - .map(Deployment::applicationVersion) + .map(Deployment::revision) .reduce((o, n) -> require(o.equals(n), n, "all versions should be equal, but got " + o + " and " + n)) .orElseThrow(() -> new AssertionError("no versions deployed")); } @@ -621,11 +624,11 @@ public class DeploymentTriggerTest { @Test public void downgradingApplicationVersionWorks() { var app = tester.newDeploymentContext().submit().deploy(); - ApplicationVersion appVersion0 = app.lastSubmission().get(); + RevisionId appVersion0 = app.lastSubmission().get(); assertEquals(appVersion0, latestDeployed(app.instance())); app.submit().deploy(); - ApplicationVersion appVersion1 = app.lastSubmission().get(); + RevisionId appVersion1 = app.lastSubmission().get(); assertEquals(appVersion1, latestDeployed(app.instance())); // Downgrading application version. @@ -636,7 +639,7 @@ public class DeploymentTriggerTest { .runJob(productionUsEast3) .runJob(productionUsWest1); assertEquals(Change.empty(), app.instance().change()); - assertEquals(appVersion0, app.instance().deployments().get(productionUsEast3.zone(tester.controller().system())).applicationVersion()); + assertEquals(appVersion0, app.instance().deployments().get(productionUsEast3.zone(tester.controller().system())).revision()); assertEquals(appVersion0, latestDeployed(app.instance())); } @@ -645,11 +648,11 @@ public class DeploymentTriggerTest { var app = tester.newDeploymentContext().submit(); app.deploy(); - ApplicationVersion appVersion0 = app.lastSubmission().get(); + RevisionId appVersion0 = app.lastSubmission().get(); assertEquals(appVersion0, latestDeployed(app.instance())); app.submit().deploy(); - ApplicationVersion appVersion1 = app.lastSubmission().get(); + RevisionId appVersion1 = app.lastSubmission().get(); assertEquals(appVersion1, latestDeployed(app.instance())); // Triggering a roll-out of an already deployed application is a no-op. @@ -705,9 +708,9 @@ public class DeploymentTriggerTest { assertEquals(triggered, app1.instanceJobs().get(productionUsCentral1).lastTriggered().get().start()); // Roll out a new application version, which gives a dual change -- this should trigger us-central-1, but only as long as it hasn't yet deployed there. - ApplicationVersion revision1 = app1.lastSubmission().get(); + RevisionId revision1 = app1.lastSubmission().get(); app1.submit(applicationPackage); - ApplicationVersion revision2 = app1.lastSubmission().get(); + RevisionId revision2 = app1.lastSubmission().get(); app1.runJob(systemTest) // Tests for new revision on version2 .runJob(stagingTest) .runJob(systemTest) // Tests for new revision on version1 @@ -716,13 +719,13 @@ public class DeploymentTriggerTest { tester.triggerJobs(); app1.assertRunning(productionUsCentral1); assertEquals(version2, app1.instance().deployments().get(productionUsCentral1.zone(main)).version()); - assertEquals(revision1, app1.deployment(productionUsCentral1.zone(main)).applicationVersion()); + assertEquals(revision1, app1.deployment(productionUsCentral1.zone(main)).revision()); assertTrue(triggered.isBefore(app1.instanceJobs().get(productionUsCentral1).lastTriggered().get().start())); // Change has a higher application version than what is deployed -- deployment should trigger. app1.timeOutUpgrade(productionUsCentral1); assertEquals(version2, app1.deployment(productionUsCentral1.zone(main)).version()); - assertEquals(revision2, app1.deployment(productionUsCentral1.zone(main)).applicationVersion()); + assertEquals(revision2, app1.deployment(productionUsCentral1.zone(main)).revision()); // Change is again strictly dominated, and us-central-1 is skipped, even though it is still failing. tester.clock().advance(Duration.ofHours(3)); // Enough time for retry @@ -781,8 +784,8 @@ public class DeploymentTriggerTest { app.failDeployment(productionEuWest1).failDeployment(productionUsEast3) .runJob(productionEuWest1).runJob(productionUsEast3); assertFalse(app.instance().change().hasTargets()); - assertEquals(2, app.instanceJobs().get(productionEuWest1).lastSuccess().get().versions().targetApplication().buildNumber().getAsLong()); - assertEquals(2, app.instanceJobs().get(productionUsEast3).lastSuccess().get().versions().targetApplication().buildNumber().getAsLong()); + assertEquals(2, app.instanceJobs().get(productionEuWest1).lastSuccess().get().versions().targetRevision().number()); + assertEquals(2, app.instanceJobs().get(productionUsEast3).lastSuccess().get().versions().targetRevision().number()); } @Test @@ -949,9 +952,9 @@ public class DeploymentTriggerTest { app3.abortJob(stagingTest); assertEquals(0, tester.jobs().active().size()); - assertTrue(app1.instance().change().application().isPresent()); - assertFalse(app2.instance().change().application().isPresent()); - assertFalse(app3.instance().change().application().isPresent()); + assertTrue(app1.instance().change().revision().isPresent()); + assertFalse(app2.instance().change().revision().isPresent()); + assertFalse(app3.instance().change().revision().isPresent()); tester.readyJobsTrigger().maintain(); app1.assertRunning(stagingTest); @@ -1016,12 +1019,12 @@ public class DeploymentTriggerTest { // Package is submitted, and change propagated to the two first instances. i1.submit(applicationPackage); - Optional<ApplicationVersion> v0 = i1.lastSubmission(); + Optional<RevisionId> v0 = i1.lastSubmission(); tester.outstandingChangeDeployer().run(); - assertEquals(v0, i1.instance().change().application()); - assertEquals(v0, i2.instance().change().application()); - assertEquals(Optional.empty(), i3.instance().change().application()); - assertEquals(Optional.empty(), i4.instance().change().application()); + assertEquals(v0, i1.instance().change().revision()); + assertEquals(v0, i2.instance().change().revision()); + assertEquals(Optional.empty(), i3.instance().change().revision()); + assertEquals(Optional.empty(), i4.instance().change().revision()); // Tests run in i4, as they're declared there, and i1 and i2 get to work i4.runJob(systemTest).runJob(stagingTest); @@ -1032,57 +1035,57 @@ public class DeploymentTriggerTest { tester.outstandingChangeDeployer().run(); assertEquals(v0, Optional.of(latestDeployed(i1.instance()))); assertEquals(v0, Optional.of(latestDeployed(i2.instance()))); - assertEquals(Optional.empty(), i1.instance().change().application()); - assertEquals(Optional.empty(), i2.instance().change().application()); - assertEquals(Optional.empty(), i3.instance().change().application()); - assertEquals(Optional.empty(), i4.instance().change().application()); + assertEquals(Optional.empty(), i1.instance().change().revision()); + assertEquals(Optional.empty(), i2.instance().change().revision()); + assertEquals(Optional.empty(), i3.instance().change().revision()); + assertEquals(Optional.empty(), i4.instance().change().revision()); // When the delay is done, i3 gets the change. tester.clock().advance(Duration.ofHours(6)); tester.outstandingChangeDeployer().run(); - assertEquals(Optional.empty(), i1.instance().change().application()); - assertEquals(Optional.empty(), i2.instance().change().application()); - assertEquals(v0, i3.instance().change().application()); - assertEquals(Optional.empty(), i4.instance().change().application()); + assertEquals(Optional.empty(), i1.instance().change().revision()); + assertEquals(Optional.empty(), i2.instance().change().revision()); + assertEquals(v0, i3.instance().change().revision()); + assertEquals(Optional.empty(), i4.instance().change().revision()); // v0 begins roll-out in i3, and v1 is submitted and rolls out in i1 and i2 some time later i3.runJob(productionUsEast3); // v0 tester.clock().advance(Duration.ofHours(12)); i1.submit(applicationPackage); - Optional<ApplicationVersion> v1 = i1.lastSubmission(); + Optional<RevisionId> v1 = i1.lastSubmission(); i4.runJob(systemTest).runJob(stagingTest); i1.runJob(productionUsEast3); // v1 i2.runJob(productionUsEast3); // v1 assertEquals(v1, Optional.of(latestDeployed(i1.instance()))); assertEquals(v1, Optional.of(latestDeployed(i2.instance()))); - assertEquals(Optional.empty(), i1.instance().change().application()); - assertEquals(Optional.empty(), i2.instance().change().application()); - assertEquals(v0, i3.instance().change().application()); - assertEquals(Optional.empty(), i4.instance().change().application()); + assertEquals(Optional.empty(), i1.instance().change().revision()); + assertEquals(Optional.empty(), i2.instance().change().revision()); + assertEquals(v0, i3.instance().change().revision()); + assertEquals(Optional.empty(), i4.instance().change().revision()); // After some time, v2 also starts rolling out to i1 and i2, but does not complete in i2 tester.clock().advance(Duration.ofHours(3)); i1.submit(applicationPackage); - Optional<ApplicationVersion> v2 = i1.lastSubmission(); + Optional<RevisionId> v2 = i1.lastSubmission(); i4.runJob(systemTest).runJob(stagingTest); i1.runJob(productionUsEast3); // v2 tester.clock().advance(Duration.ofHours(3)); // v1 is all done in i1 and i2, but does not yet roll out in i3; v2 is not completely rolled out there yet. tester.outstandingChangeDeployer().run(); - assertEquals(v0, i3.instance().change().application()); + assertEquals(v0, i3.instance().change().revision()); // i3 completes v0, which rolls out to i4; v1 is ready for i3, but v2 is not. i3.runJob(testUsEast3); - assertEquals(Optional.empty(), i3.instance().change().application()); + assertEquals(Optional.empty(), i3.instance().change().revision()); tester.outstandingChangeDeployer().run(); assertEquals(v2, Optional.of(latestDeployed(i1.instance()))); assertEquals(v1, Optional.of(latestDeployed(i2.instance()))); assertEquals(v0, Optional.of(latestDeployed(i3.instance()))); - assertEquals(Optional.empty(), i1.instance().change().application()); - assertEquals(v2, i2.instance().change().application()); - assertEquals(v1, i3.instance().change().application()); - assertEquals(v0, i4.instance().change().application()); + assertEquals(Optional.empty(), i1.instance().change().revision()); + assertEquals(v2, i2.instance().change().revision()); + assertEquals(v1, i3.instance().change().revision()); + assertEquals(v0, i4.instance().change().revision()); } @Test @@ -1105,7 +1108,7 @@ public class DeploymentTriggerTest { DeploymentContext alpha = tester.newDeploymentContext("t", "a", "alpha"); DeploymentContext beta = tester.newDeploymentContext("t", "a", "beta"); alpha.submit(applicationPackage).deploy(); - Optional<ApplicationVersion> revision1 = alpha.lastSubmission(); + Optional<RevisionId> revision1 = alpha.lastSubmission(); Version version1 = new Version("7.1"); tester.controllerTester().upgradeSystem(version1); @@ -1128,7 +1131,7 @@ public class DeploymentTriggerTest { beta.assertNotRunning(testUsEast3); alpha.submit(applicationPackage); - Optional<ApplicationVersion> revision2 = alpha.lastSubmission(); + Optional<RevisionId> revision2 = alpha.lastSubmission(); assertEquals(Change.of(revision2.get()), alpha.instance().change()); assertEquals(Change.of(version1), beta.instance().change()); @@ -1380,7 +1383,7 @@ public class DeploymentTriggerTest { // Upgrade instance 1; upgrade rolls out first, with revision following. // The new platform won't roll out to the conservative instance until the normal one is upgraded. app1.submit(applicationPackage); - assertEquals(Change.of(version).with(app1.application().revisions().last().get()), app1.instance().change()); + assertEquals(Change.of(version).with(app1.application().revisions().last().get().id()), app1.instance().change()); // Upgrade platform. app2.runJob(systemTest); app1.runJob(stagingTest) @@ -1443,7 +1446,7 @@ public class DeploymentTriggerTest { tester.triggerJobs(); assertEquals(1, tester.jobs().active().size()); assertEquals(Change.empty(), app1.instance().change()); - assertEquals(Change.of(version).with(app1.application().revisions().last().get()), app2.instance().change()); + assertEquals(Change.of(version).with(app1.application().revisions().last().get().id()), app2.instance().change()); app2.runJob(productionEuWest1) .runJob(testEuWest1); @@ -1680,7 +1683,7 @@ public class DeploymentTriggerTest { alpha.runJob(systemTest).runJob(stagingTest) .runJob(productionUsEast3).runJob(testUsEast3); - assertEquals(Optional.empty(), alpha.instance().change().application()); + assertEquals(Optional.empty(), alpha.instance().change().revision()); // revision3 is submitted when revision2 is half-way. tester.outstandingChangeDeployer().run(); @@ -1688,20 +1691,20 @@ public class DeploymentTriggerTest { alpha.submit(appPackage, 2); // Will only roll out to gamma together with the next revision. var revision3 = alpha.lastSubmission(); beta.runJob(testUsEast3); - assertEquals(Optional.empty(), beta.instance().change().application()); + assertEquals(Optional.empty(), beta.instance().change().revision()); // revision3 is the target for alpha, beta is done, revision2 is the target for gamma. tester.outstandingChangeDeployer().run(); - assertEquals(revision3, alpha.instance().change().application()); - assertEquals(Optional.empty(), beta.instance().change().application()); - assertEquals(revision2, gamma.instance().change().application()); + assertEquals(revision3, alpha.instance().change().revision()); + assertEquals(Optional.empty(), beta.instance().change().revision()); + assertEquals(revision2, gamma.instance().change().revision()); // revision3 rolls to beta, then a couple of new revisions are submitted to alpha, and the latter is the new target. alpha.runJob(systemTest).runJob(stagingTest) .runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(Optional.empty(), alpha.instance().change().application()); - assertEquals(revision3, beta.instance().change().application()); + assertEquals(Optional.empty(), alpha.instance().change().revision()); + assertEquals(revision3, beta.instance().change().revision()); // revision5 supersedes revision4 alpha.submit(appPackage, 3); @@ -1713,8 +1716,8 @@ public class DeploymentTriggerTest { alpha.runJob(systemTest).runJob(stagingTest) .runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(Optional.empty(), alpha.instance().change().application()); - assertEquals(revision3, beta.instance().change().application()); + assertEquals(Optional.empty(), alpha.instance().change().revision()); + assertEquals(revision3, beta.instance().change().revision()); // revision6 rolls through alpha, and becomes the next target for beta, which also completes revision3. alpha.submit(appPackage, 6); @@ -1724,37 +1727,37 @@ public class DeploymentTriggerTest { .runJob(testUsEast3); beta.runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(Optional.empty(), alpha.instance().change().application()); - assertEquals(revision6, beta.instance().change().application()); + assertEquals(Optional.empty(), alpha.instance().change().revision()); + assertEquals(revision6, beta.instance().change().revision()); // revision 2 fails in gamma, but this does not bring on revision 3 gamma.failDeployment(productionUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(revision2, gamma.instance().change().application()); + assertEquals(revision2, gamma.instance().change().revision()); // revision 2 completes in gamma gamma.runJob(productionUsEast3) .runJob(testUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(Optional.empty(), alpha.instance().change().application()); - assertEquals(Optional.empty(), gamma.instance().change().application()); // no other revisions after 3 are ready, so gamma waits + assertEquals(Optional.empty(), alpha.instance().change().revision()); + assertEquals(Optional.empty(), gamma.instance().change().revision()); // no other revisions after 3 are ready, so gamma waits // revision6 rolls through beta, and revision3 is the next target for gamma with "when-clear" change-revision, now that 6 is blocking 4 and 5 alpha.jobAborted(stagingTest).runJob(stagingTest); beta.runJob(productionUsEast3).runJob(testUsEast3); - assertEquals(Optional.empty(), beta.instance().change().application()); + assertEquals(Optional.empty(), beta.instance().change().revision()); tester.outstandingChangeDeployer().run(); - assertEquals(Optional.empty(), alpha.instance().change().application()); - assertEquals(Optional.empty(), beta.instance().change().application()); - assertEquals(revision3, gamma.instance().change().application()); // revision4 never became ready, but 5 did, so 4 is skipped, and 3 rolls out alone instead. + assertEquals(Optional.empty(), alpha.instance().change().revision()); + assertEquals(Optional.empty(), beta.instance().change().revision()); + assertEquals(revision3, gamma.instance().change().revision()); // revision4 never became ready, but 5 did, so 4 is skipped, and 3 rolls out alone instead. // revision 6 is next, once 3 is done // revision 3 completes gamma.runJob(productionUsEast3) .runJob(testUsEast3); tester.outstandingChangeDeployer().run(); - assertEquals(revision6, gamma.instance().change().application()); + assertEquals(revision6, gamma.instance().change().revision()); // revision 7 becomes ready for gamma, but must wait for the idle time of 8 hours before being deployed alpha.submit(appPackage, 1); @@ -1762,11 +1765,11 @@ public class DeploymentTriggerTest { alpha.deploy(); tester.outstandingChangeDeployer(); assertEquals(Change.empty(), gamma.instance().change()); - assertEquals(revision6.get(), gamma.deployment(ZoneId.from("prod.us-east-3")).applicationVersion()); + assertEquals(revision6.get(), gamma.deployment(ZoneId.from("prod.us-east-3")).revision()); tester.clock().advance(Duration.ofHours(8)); tester.outstandingChangeDeployer().run(); - assertEquals(revision7, gamma.instance().change().application()); + assertEquals(revision7, gamma.instance().change().revision()); // revision 8 is has too low risk to roll out on its own, but will start rolling immediately when revision 9 is submitted gamma.deploy(); @@ -1775,11 +1778,11 @@ public class DeploymentTriggerTest { alpha.deploy(); tester.outstandingChangeDeployer(); assertEquals(Change.empty(), gamma.instance().change()); - assertEquals(revision7.get(), gamma.deployment(ZoneId.from("prod.us-east-3")).applicationVersion()); + assertEquals(revision7.get(), gamma.deployment(ZoneId.from("prod.us-east-3")).revision()); alpha.submit(appPackage, 5); tester.outstandingChangeDeployer().run(); - assertEquals(revision8, gamma.instance().change().application()); + assertEquals(revision8, gamma.instance().change().revision()); } @Test @@ -1941,15 +1944,17 @@ public class DeploymentTriggerTest { ApplicationPackage cdPackage = new ApplicationPackageBuilder().region("cd-us-east-1") .region("cd-aws-us-east-1a") .build(); - var zones = List.of(ZoneId.from("test.cd-us-west-1"), - ZoneId.from("staging.cd-us-west-1"), - ZoneId.from("prod.cd-us-east-1"), - ZoneId.from("prod.cd-aws-us-east-1a")); - tester.controllerTester() - .setZones(zones, SystemName.cd) - .setRoutingMethod(zones, RoutingMethod.sharedLayer4); - tester.controllerTester().upgradeSystem(Version.fromString("6.1")); - tester.controllerTester().computeVersionStatus(); + List<ZoneId> zones = List.of(ZoneId.from("test.cd-us-west-1"), + ZoneId.from("staging.cd-us-west-1"), + ZoneId.from("prod.cd-us-east-1"), + ZoneId.from("prod.cd-aws-us-east-1a")); + ControllerTester wrapped = new ControllerTester(cd); + wrapped.setZones(zones) + .setRoutingMethod(zones, RoutingMethod.sharedLayer4); + wrapped.upgradeSystem(Version.fromString("6.1")); + wrapped.computeVersionStatus(); + + DeploymentTester tester = new DeploymentTester(wrapped); var app = tester.newDeploymentContext(); app.runJob(productionCdUsEast1, cdPackage); @@ -2039,12 +2044,12 @@ public class DeploymentTriggerTest { // Start upgrade, then receive new submission. Version version1 = new Version("7.8.9"); - ApplicationVersion build1 = app.lastSubmission().get(); + RevisionId build1 = app.lastSubmission().get(); tester.controllerTester().upgradeSystem(version1); tester.upgrader().maintain(); app.runJob(stagingTest); app.submit(); - ApplicationVersion build2 = app.lastSubmission().get(); + RevisionId build2 = app.lastSubmission().get(); assertNotEquals(build1, build2); // App now free to start system tests eagerly, for new submission. These should run assuming upgrade succeeds. @@ -2053,16 +2058,16 @@ public class DeploymentTriggerTest { assertEquals(version1, app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetPlatform()); assertEquals(build1, - app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetApplication()); + app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetRevision()); assertEquals(version1, app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourcePlatform().get()); assertEquals(build1, - app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourceApplication().get()); + app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourceRevision().get()); assertEquals(version1, app.instanceJobs().get(stagingTest).lastTriggered().get().versions().targetPlatform()); assertEquals(build2, - app.instanceJobs().get(stagingTest).lastTriggered().get().versions().targetApplication()); + app.instanceJobs().get(stagingTest).lastTriggered().get().versions().targetRevision()); // App completes upgrade, and outstanding change is triggered. This should let relevant, running jobs finish. app.runJob(systemTest) @@ -2151,7 +2156,7 @@ public class DeploymentTriggerTest { .build()); app.deploy(); assertEquals(version2, tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform()); - assertEquals(version2, tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetApplication().compileVersion().get()); + assertEquals(version2, app.application().revisions().get(tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetRevision()).compileVersion().get()); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java index 4e538acb8f2..98cbf33fb2b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java @@ -12,6 +12,7 @@ import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.slime.Inspector; import com.yahoo.slime.SlimeUtils; +import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions; import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.RestartAction; import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ServiceInfo; @@ -480,15 +481,16 @@ public class InternalStepRunnerTest { @Test public void realDeploymentRequiresForTesterCert() { - tester.controllerTester().zoneRegistry().setSystemName(SystemName.Public); - var zones = List.of(ZoneApiMock.fromId("test.aws-us-east-1c"), - ZoneApiMock.fromId("staging.aws-us-east-1c"), - ZoneApiMock.fromId("prod.aws-us-east-1c")); - tester.controllerTester().zoneRegistry() - .setZones(zones) - .setRoutingMethod(zones, RoutingMethod.exclusive); + List<ZoneApiMock> zones = List.of(ZoneApiMock.fromId("test.aws-us-east-1c"), + ZoneApiMock.fromId("staging.aws-us-east-1c"), + ZoneApiMock.fromId("prod.aws-us-east-1c")); + ControllerTester wrapped = new ControllerTester(SystemName.Public); + wrapped.zoneRegistry() + .setZones(zones) + .setRoutingMethod(zones, RoutingMethod.exclusive); + tester = new DeploymentTester(wrapped); tester.configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), SystemApplication.values()); - ZoneId testZone = JobType.systemTest.zone(tester.controller().system()); + app = tester.newDeploymentContext(); RunId id = app.newRun(JobType.systemTest); tester.configServer().throwOnPrepare(instanceId -> { if (instanceId.instance().isTester()) @@ -517,14 +519,16 @@ public class InternalStepRunnerTest { @Test public void certificateTimeoutAbortsJob() { - tester.controllerTester().zoneRegistry().setSystemName(SystemName.Public); - var zones = List.of(ZoneApiMock.fromId("test.aws-us-east-1c"), - ZoneApiMock.fromId("staging.aws-us-east-1c"), - ZoneApiMock.fromId("prod.aws-us-east-1c")); - tester.controllerTester().zoneRegistry() - .setZones(zones) - .setRoutingMethod(zones, RoutingMethod.exclusive); + List<ZoneApiMock> zones = List.of(ZoneApiMock.fromId("test.aws-us-east-1c"), + ZoneApiMock.fromId("staging.aws-us-east-1c"), + ZoneApiMock.fromId("prod.aws-us-east-1c")); + ControllerTester wrapped = new ControllerTester(SystemName.Public); + wrapped.zoneRegistry() + .setZones(zones) + .setRoutingMethod(zones, RoutingMethod.exclusive); + tester = new DeploymentTester(wrapped); tester.configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), SystemApplication.values()); + app = tester.newDeploymentContext(); RunId id = app.startSystemTestTests(); List<X509Certificate> trusted = new ArrayList<>(DeploymentContext.publicApplicationPackage().trustedCertificates()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ApplicationStoreMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ApplicationStoreMock.java index 3dbe307fa5a..8ed38761c95 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ApplicationStoreMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ApplicationStoreMock.java @@ -5,9 +5,10 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.NotExistsException; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationStore; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; @@ -29,7 +30,7 @@ public class ApplicationStoreMock implements ApplicationStore { private static final byte[] tombstone = new byte[0]; - private final Map<ApplicationId, Map<ApplicationVersion, byte[]>> store = new ConcurrentHashMap<>(); + private final Map<ApplicationId, Map<RevisionId, byte[]>> store = new ConcurrentHashMap<>(); private final Map<DeploymentId, byte[]> devStore = new ConcurrentHashMap<>(); private final Map<ApplicationId, Map<Long, byte[]>> diffs = new ConcurrentHashMap<>(); private final Map<DeploymentId, Map<Long, byte[]>> devDiffs = new ConcurrentHashMap<>(); @@ -45,15 +46,14 @@ public class ApplicationStoreMock implements ApplicationStore { } @Override - public byte[] get(DeploymentId deploymentId, ApplicationVersion applicationVersion) { - if (applicationVersion.isDeployedDirectly()) + public byte[] get(DeploymentId deploymentId, RevisionId revisionId) { + if ( ! revisionId.isProduction()) return requireNonNull(devStore.get(deploymentId)); TenantAndApplicationId tenantAndApplicationId = TenantAndApplicationId.from(deploymentId.applicationId()); - byte[] bytes = store.get(appId(tenantAndApplicationId.tenant(), tenantAndApplicationId.application())).get(applicationVersion); + byte[] bytes = store.get(appId(tenantAndApplicationId.tenant(), tenantAndApplicationId.application())).get(revisionId); if (bytes == null) - throw new IllegalArgumentException("No application package found for " + tenantAndApplicationId + - " with version " + applicationVersion.stringId()); + throw new NotExistsException("No " + revisionId + " found for " + tenantAndApplicationId); return bytes; } @@ -71,21 +71,20 @@ public class ApplicationStoreMock implements ApplicationStore { @Override public Optional<byte[]> find(TenantName tenant, ApplicationName application, long buildNumber) { return store.getOrDefault(appId(tenant, application), Map.of()).entrySet().stream() - .filter(kv -> kv.getKey().buildNumber().orElse(Long.MIN_VALUE) == buildNumber) + .filter(kv -> kv.getKey().number() == buildNumber) .map(Map.Entry::getValue) .findFirst(); } @Override - public void put(TenantName tenant, ApplicationName application, ApplicationVersion applicationVersion, byte[] bytes, byte[] tests, byte[] diff) { - store.computeIfAbsent(appId(tenant, application), __ -> new ConcurrentHashMap<>()).put(applicationVersion, bytes); - store.computeIfAbsent(testerId(tenant, application), key -> new ConcurrentHashMap<>()) .put(applicationVersion, tests); - applicationVersion.buildNumber().ifPresent(buildNumber -> - diffs.computeIfAbsent(appId(tenant, application), __ -> new ConcurrentHashMap<>()).put(buildNumber, diff)); + public void put(TenantName tenant, ApplicationName application, RevisionId revision, byte[] bytes, byte[] tests, byte[] diff) { + store.computeIfAbsent(appId(tenant, application), __ -> new ConcurrentHashMap<>()).put(revision, bytes); + store.computeIfAbsent(testerId(tenant, application), key -> new ConcurrentHashMap<>()) .put(revision, tests); + diffs.computeIfAbsent(appId(tenant, application), __ -> new ConcurrentHashMap<>()).put(revision.number(), diff); } @Override - public void prune(TenantName tenant, ApplicationName application, ApplicationVersion oldestToRetain) { + public void prune(TenantName tenant, ApplicationName application, RevisionId oldestToRetain) { store.getOrDefault(appId(tenant, application), Map.of()).keySet().removeIf(version -> version.compareTo(oldestToRetain) < 0); store.getOrDefault(testerId(tenant, application), Map.of()).keySet().removeIf(version -> version.compareTo(oldestToRetain) < 0); } @@ -97,8 +96,8 @@ public class ApplicationStoreMock implements ApplicationStore { } @Override - public byte[] getTester(TenantName tenant, ApplicationName application, ApplicationVersion applicationVersion) { - return requireNonNull(store.get(testerId(tenant, application)).get(applicationVersion)); + public byte[] getTester(TenantName tenant, ApplicationName application, RevisionId revision) { + return requireNonNull(store.get(testerId(tenant, application)).get(revision)); } @@ -114,10 +113,9 @@ public class ApplicationStoreMock implements ApplicationStore { } @Override - public void putDev(DeploymentId deploymentId, ApplicationVersion applicationVersion, byte[] applicationPackage, byte[] diff) { + public void putDev(DeploymentId deploymentId, RevisionId revision, byte[] applicationPackage, byte[] diff) { devStore.put(deploymentId, applicationPackage); - applicationVersion.buildNumber().ifPresent(buildNumber -> - devDiffs.computeIfAbsent(deploymentId, __ -> new ConcurrentHashMap<>()).put(buildNumber, diff)); + devDiffs.computeIfAbsent(deploymentId, __ -> new ConcurrentHashMap<>()).put(revision.number(), diff); } @Override diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java index a4c30cca29e..1f4218af1b6 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java @@ -41,9 +41,11 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry private final Map<CloudName, UpgradePolicy> osUpgradePolicies = new HashMap<>(); private final Map<ZoneApi, List<RoutingMethod>> zoneRoutingMethods = new HashMap<>(); private final Set<ZoneApi> reprovisionToUpgradeOs = new HashSet<>(); + private final SystemName system; // Don't even think about making it non-final! ƪ(`▿▿▿▿´ƪ) + + private List<? extends ZoneApi> zones; - private SystemName system; private UpgradePolicy upgradePolicy = null; /** @@ -95,11 +97,6 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry return setZones(List.of(zone)); } - public ZoneRegistryMock setSystemName(SystemName system) { - this.system = system; - return this; - } - public ZoneRegistryMock setUpgradePolicy(UpgradePolicy upgradePolicy) { this.upgradePolicy = upgradePolicy; return this; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java index d50c69196c6..8b155644fb4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; @@ -77,10 +78,7 @@ public class JobRunnerTest { private static final ApplicationPackage applicationPackage = new ApplicationPackage(new byte[0]); private static final Versions versions = new Versions(Version.fromString("1.2.3"), - ApplicationVersion.from(new SourceRevision("repo", - "branch", - "bada55"), - 321), + RevisionId.forProduction(321), Optional.empty(), Optional.empty()); @@ -95,7 +93,7 @@ public class JobRunnerTest { TenantAndApplicationId appId = tester.createApplication("tenant", "real", "default").id(); ApplicationId id = appId.defaultInstance(); byte[] testPackageBytes = new byte[0]; - jobs.submit(appId, versions.targetApplication().source(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); + jobs.submit(appId, Optional.empty(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); start(jobs, id, systemTest); try { @@ -127,7 +125,7 @@ public class JobRunnerTest { TenantAndApplicationId appId = tester.createApplication("tenant", "real", "default").id(); ApplicationId id = appId.defaultInstance(); byte[] testPackageBytes = new byte[0]; - jobs.submit(appId, versions.targetApplication().source(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); + jobs.submit(appId, Optional.empty(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); Supplier<Run> run = () -> jobs.last(id, systemTest).get(); start(jobs, id, systemTest); @@ -235,7 +233,7 @@ public class JobRunnerTest { TenantAndApplicationId appId = tester.createApplication("tenant", "real", "default").id(); ApplicationId id = appId.defaultInstance(); byte[] testPackageBytes = new byte[0]; - jobs.submit(appId, versions.targetApplication().source(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); + jobs.submit(appId, Optional.empty(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); RunId runId = new RunId(id, systemTest, 1); start(jobs, id, systemTest); @@ -273,7 +271,7 @@ public class JobRunnerTest { ApplicationId instanceId = appId.defaultInstance(); JobId jobId = new JobId(instanceId, systemTest); byte[] testPackageBytes = new byte[0]; - jobs.submit(appId, versions.targetApplication().source(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); + jobs.submit(appId, Optional.empty(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); assertFalse(jobs.lastSuccess(jobId).isPresent()); for (int i = 0; i < jobs.historyLength(); i++) { @@ -369,7 +367,7 @@ public class JobRunnerTest { TenantAndApplicationId appId = tester.createApplication("tenant", "real", "default").id(); ApplicationId id = appId.defaultInstance(); byte[] testPackageBytes = new byte[0]; - jobs.submit(appId, versions.targetApplication().source(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); + jobs.submit(appId, Optional.empty(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); start(jobs, id, systemTest); tester.clock().advance(JobRunner.jobTimeout.plus(Duration.ofSeconds(1))); @@ -387,7 +385,7 @@ public class JobRunnerTest { TenantAndApplicationId appId = tester.createApplication("tenant", "real", "default").id(); ApplicationId id = appId.defaultInstance(); byte[] testPackageBytes = new byte[0]; - jobs.submit(appId, versions.targetApplication().source(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); + jobs.submit(appId, Optional.empty(), Optional.empty(), Optional.empty(), 2, applicationPackage, testPackageBytes, Optional.empty(), 0); for (Step step : JobProfile.of(systemTest).steps()) outcomes.put(step, running); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java index 455e802e87b..658fbccd660 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporterTest.java @@ -259,7 +259,7 @@ public class MetricsReporterTest { var context = tester.newDeploymentContext() .submit(applicationPackage) .deploy(); - assertEquals(1000, context.lastSubmission().get().buildTime().get().toEpochMilli()); + assertEquals(1000, context.application().revisions().get(context.lastSubmission().get()).buildTime().get().toEpochMilli()); MetricsReporter reporter = createReporter(tester.controller()); reporter.maintain(); @@ -483,7 +483,6 @@ public class MetricsReporterTest { @Test public void tenant_counter() { var tester = new ControllerTester(SystemName.Public); - tester.zoneRegistry().setSystemName(SystemName.Public); tester.createTenant("foo", Tenant.Type.cloud); tester.createTenant("bar", Tenant.Type.cloud); tester.createTenant("fix", Tenant.Type.cloud); 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 0a0b94bd6c9..e989486c595 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 @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -36,12 +37,12 @@ public class OutstandingChangeDeployerTest { assertFalse(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); app.submit(applicationPackage); - Optional<ApplicationVersion> revision = app.lastSubmission(); + Optional<RevisionId> revision = app.lastSubmission(); assertFalse(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); assertEquals(Change.of(version).with(revision.get()), app.instance().change()); app.submit(applicationPackage); - Optional<ApplicationVersion> outstanding = app.lastSubmission(); + Optional<RevisionId> outstanding = app.lastSubmission(); assertTrue(app.deploymentStatus().outstandingChange(app.instance().name()).hasTargets()); assertEquals(Change.of(version).with(revision.get()), app.instance().change()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java index 3fb57402b35..ef4bfdb568e 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 @@ -5,6 +5,7 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.test.ManualClock; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; @@ -792,12 +793,12 @@ public class UpgraderTest { // New application change app.submit(applicationPackage("default")); - String applicationVersion = app.lastSubmission().get().stringId(); + RevisionId revision = app.lastSubmission().get(); // Application change recorded together with ongoing upgrade assertTrue("Change contains both upgrade and application change", app.instance().change().platform().get().equals(version) && - app.instance().change().application().get().stringId().equals(applicationVersion)); + app.instance().change().revision().get().equals(revision)); // Deployment completes app.runJob(systemTest).runJob(stagingTest) @@ -807,7 +808,7 @@ public class UpgraderTest { for (Deployment deployment : app.instance().deployments().values()) { assertEquals(version, deployment.version()); - assertEquals(applicationVersion, deployment.applicationVersion().stringId()); + assertEquals(revision, deployment.revision()); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java index d0dbb23ad1b..28be68cf850 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.notification; import com.google.common.collect.ImmutableBiMap; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.path.Path; @@ -66,7 +67,7 @@ public class NotificationsDbTest { notification(1501, Type.deployment, Level.warning, NotificationSource.from(new RunId(ApplicationId.from(tenant.value(), "app1", "instance1"), JobType.devUsEast1, 4)), "run id msg")); private final ManualClock clock = new ManualClock(Instant.ofEpochSecond(12345)); - private final MockCuratorDb curatorDb = new MockCuratorDb(); + private final MockCuratorDb curatorDb = new MockCuratorDb(SystemName.Public); private final MockMailer mailer = new MockMailer(); private final NotificationsDb notificationsDb = new NotificationsDb(clock, curatorDb, new Notifier(curatorDb, mailer)); 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 742bbaec918..1fc99b0da1c 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 @@ -5,6 +5,7 @@ import com.yahoo.component.Version; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.ValidationOverrides; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.security.KeyUtils; import com.yahoo.slime.SlimeUtils; @@ -13,6 +14,7 @@ import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; @@ -53,7 +55,7 @@ import static org.junit.Assert.assertEquals; public class ApplicationSerializerTest { - private static final ApplicationSerializer APPLICATION_SERIALIZER = new ApplicationSerializer(); + private static final ApplicationSerializer APPLICATION_SERIALIZER = new ApplicationSerializer(SystemName.main); private static final Path testData = Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/"); private static final ZoneId zone1 = ZoneId.from("prod", "us-west-1"); private static final ZoneId zone2 = ZoneId.from("prod", "us-east-3"); @@ -83,21 +85,31 @@ public class ApplicationSerializerTest { OptionalLong projectId = OptionalLong.of(123L); + ApplicationId id1 = ApplicationId.from("t1", "a1", "i1"); + ApplicationId id3 = ApplicationId.from("t1", "a1", "i3"); List<Deployment> deployments = new ArrayList<>(); - ApplicationVersion applicationVersion1 = new ApplicationVersion(Optional.of(new SourceRevision("git@github:org/repo.git", "branch1", "commit1")), - OptionalLong.of(31), Optional.of("william@shakespeare"), - Optional.of(Version.fromString("1.2.3")), Optional.of(Instant.ofEpochMilli(666)), - Optional.empty(), Optional.of("best commit"), true, Optional.of("hash1"), - true, false, Optional.of("~(˘▾˘)~"), 3); + ApplicationVersion applicationVersion1 = new ApplicationVersion(RevisionId.forProduction(31), + Optional.of(new SourceRevision("git@github:org/repo.git", "branch1", "commit1")), + Optional.of("william@shakespeare"), + Optional.of(Version.fromString("1.2.3")), + Optional.of(Instant.ofEpochMilli(666)), + Optional.empty(), + Optional.of("best commit"), + Optional.of("hash1"), + true, + false, + Optional.of("~(˘▾˘)~"), + 3); assertEquals("https://github/org/repo/tree/commit1", applicationVersion1.sourceUrl().get()); - ApplicationVersion applicationVersion2 = ApplicationVersion - .from(new SourceRevision("repo1", "branch1", "commit1"), 32, "a@b", - Version.fromString("6.3.1"), Instant.ofEpochMilli(496)); + ApplicationVersion applicationVersion2 = ApplicationVersion.from(RevisionId.forDevelopment(31, new JobId(id1, JobType.productionUsEast3)), + new SourceRevision("repo1", "branch1", "commit1"), "a@b", + Version.fromString("6.3.1"), + Instant.ofEpochMilli(496)); Instant activityAt = Instant.parse("2018-06-01T10:15:30.00Z"); - deployments.add(new Deployment(zone1, applicationVersion1, Version.fromString("1.2.3"), Instant.ofEpochMilli(3), + deployments.add(new Deployment(zone1, applicationVersion1.id(), Version.fromString("1.2.3"), Instant.ofEpochMilli(3), DeploymentMetrics.none, DeploymentActivity.none, QuotaUsage.none, OptionalDouble.empty())); - deployments.add(new Deployment(zone2, applicationVersion2, Version.fromString("1.2.3"), Instant.ofEpochMilli(5), + deployments.add(new Deployment(zone2, applicationVersion2.id(), Version.fromString("1.2.3"), Instant.ofEpochMilli(5), new DeploymentMetrics(2, 3, 4, 5, 6, Optional.of(Instant.now().truncatedTo(ChronoUnit.MILLIS)), Map.of(DeploymentMetrics.Warning.all, 3)), @@ -112,10 +124,8 @@ public class ApplicationSerializerTest { ZoneId.from("prod", "us-east-3"), RotationState.out), Instant.ofEpochMilli(42)))); - ApplicationId id1 = ApplicationId.from("t1", "a1", "i1"); - ApplicationId id3 = ApplicationId.from("t1", "a1", "i3"); RevisionHistory revisions = RevisionHistory.ofRevisions(List.of(applicationVersion1), - Map.of(new JobId(id3, JobType.devUsEast1), List.of(applicationVersion2))); + Map.of(new JobId(id1, JobType.productionUsEast3), List.of(applicationVersion2))); List<Instance> instances = List.of(new Instance(id1, deployments, Map.of(JobType.systemTest, Instant.ofEpochMilli(333)), @@ -140,7 +150,8 @@ public class ApplicationSerializerTest { new ApplicationMetrics(0.5, 0.9), Set.of(publicKey, otherPublicKey), projectId, - revisions, instances + revisions, + instances ); Application serialized = APPLICATION_SERIALIZER.fromSlime(SlimeUtils.toJsonBytes(APPLICATION_SERIALIZER.toSlime(original))); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java index a9baeb9589d..e13c598f2a9 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStoreTest.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.hosted.controller.api.integration.LogEntry; import com.yahoo.vespa.hosted.controller.api.integration.RunDataStore; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; @@ -28,7 +29,7 @@ public class BufferedLogStoreTest { public void chunkingAndFlush() { int chunkSize = 1 << 10; int maxChunks = 1 << 5; - CuratorDb buffer = new MockCuratorDb(); + CuratorDb buffer = new MockCuratorDb(SystemName.main); RunDataStore store = new MockRunDataStore(); BufferedLogStore logs = new BufferedLogStore(chunkSize, chunkSize * maxChunks, buffer, store); RunId id = new RunId(ApplicationId.from("tenant", "application", "instance"), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java index 5f7d280a230..f5922fd74aa 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializerTest.java @@ -8,6 +8,7 @@ import com.yahoo.security.X509CertificateUtils; import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.deployment.ConvergenceSummary; @@ -19,11 +20,14 @@ import com.yahoo.vespa.hosted.controller.deployment.StepInfo; import org.junit.Test; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalLong; @@ -46,6 +50,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.report; import static com.yahoo.vespa.hosted.controller.deployment.Step.startStagingSetup; import static com.yahoo.vespa.hosted.controller.deployment.Step.startTests; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.time.temporal.ChronoUnit.MILLIS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -83,22 +88,27 @@ public class RunSerializerTest { assertEquals(running, run.status()); assertEquals(3, run.lastTestLogEntry()); assertEquals(new Version(1, 2, 3), run.versions().targetPlatform()); - ApplicationVersion applicationVersion = new ApplicationVersion(Optional.of(new SourceRevision("git@github.com:user/repo.git", - "master", - "f00bad")), OptionalLong.of(123), Optional.of("a@b"), Optional.of(Version.fromString("6.3.1")), Optional.of(Instant.ofEpochMilli(100)), Optional.empty(), Optional.empty(), true, Optional.empty(), true, false, Optional.empty(), 0); - assertEquals(applicationVersion, run.versions().targetApplication()); - assertEquals(applicationVersion.authorEmail(), run.versions().targetApplication().authorEmail()); - assertEquals(applicationVersion.buildTime(), run.versions().targetApplication().buildTime()); - assertEquals(applicationVersion.compileVersion(), run.versions().targetApplication().compileVersion()); - assertEquals("f00bad", run.versions().targetApplication().commit().get()); - assertEquals("https://github.com/user/repo/tree/f00bad", run.versions().targetApplication().sourceUrl().get()); + RevisionId revision1 = RevisionId.forDevelopment(123, id.job()); + RevisionId revision2 = RevisionId.forProduction(122); + ApplicationVersion applicationVersion1 = new ApplicationVersion(revision1, + Optional.of(new SourceRevision("git@github.com:user/repo.git", "master", "f00bad")), + Optional.of("a@b"), + Optional.of(Version.fromString("6.3.1")), + Optional.of(Instant.ofEpochMilli(100)), + Optional.empty(), + Optional.empty(), + Optional.empty(), + true, + false, + Optional.empty(), + 0); + ApplicationVersion applicationVersion2 = ApplicationVersion.from(revision2, new SourceRevision("git@github.com:user/repo.git", + "master", + "badb17")); + assertEquals(revision1, run.versions().targetRevision()); assertEquals("because", run.reason().get()); assertEquals(new Version(1, 2, 2), run.versions().sourcePlatform().get()); - assertEquals(ApplicationVersion.from(new SourceRevision("git@github.com:user/repo.git", - "master", - "badb17"), - 122), - run.versions().sourceApplication().get()); + assertEquals(revision2, run.versions().sourceRevision().get()); assertEquals(Optional.of(new ConvergenceSummary(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233)), run.convergenceSummary()); assertEquals(X509CertificateUtils.fromPem("-----BEGIN CERTIFICATE-----\n" + @@ -128,6 +138,8 @@ public class RunSerializerTest { .build(), run.steps()); + Map<RevisionId, ApplicationVersion> revisions = Map.of(revision1, applicationVersion1, revision2, applicationVersion2); + run = run.with(1L << 50) .with(Instant.now().truncatedTo(MILLIS)) .noNodesDownSince(Instant.now().truncatedTo(MILLIS)) @@ -136,7 +148,8 @@ public class RunSerializerTest { assertEquals(aborted, run.status()); assertTrue(run.hasEnded()); - Run phoenix = serializer.runsFromSlime(serializer.toSlime(Collections.singleton(run))).get(id); + // Run phoenix = serializer.runsFromSlime(serializer.toSlime(List.of(run))); // TODO jonmv: use runs again, once compatability code is gone. + Run phoenix = serializer.runFromSlime(serializer.toSlime(run, revisions::get)); assertEquals(run.id(), phoenix.id()); assertEquals(run.start(), phoenix.start()); assertEquals(run.end(), phoenix.end()); @@ -149,8 +162,11 @@ public class RunSerializerTest { assertEquals(run.isDryRun(), phoenix.isDryRun()); assertEquals(run.reason(), phoenix.reason()); + assertEquals(new String(SlimeUtils.toJsonBytes(serializer.toSlime(run, revisions::get).get(), false), UTF_8), + new String(SlimeUtils.toJsonBytes(serializer.toSlime(phoenix, revisions::get).get(), false), UTF_8)); + Run initial = Run.initial(id, run.versions(), run.isRedeployment(), run.start(), JobProfile.production, Optional.empty()); - assertEquals(initial, serializer.runFromSlime(serializer.toSlime(initial))); + assertEquals(initial, serializer.runFromSlime(serializer.toSlime(initial, revisions::get))); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java index 07f00e8c989..bed2bb97aeb 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java @@ -38,7 +38,7 @@ import static org.junit.Assert.fail; */ public class ContainerTester { - private static final boolean writeResponses = false; + private static final boolean writeResponses = true; private final JDisc container; private final String responseFilePath; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java index 6e81c4280c2..d0176ffd5be 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java @@ -365,7 +365,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { request("/application/v4/tenant/scoober/application/unique/submit", POST) .data(data) .roles(Set.of(Role.developer(tenantName))), - "{\"message\":\"Application package version: 1.0.1-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isPresent()); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index c8d88101304..8c639628fdf 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -329,7 +329,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit", POST) .screwdriverIdentity(SCREWDRIVER_ID) .data(createApplicationSubmissionData(applicationPackageInstance1, 123)), - "{\"message\":\"Application package version: 1.0.1-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); app1.runJob(JobType.systemTest).runJob(JobType.stagingTest).runJob(JobType.productionUsCentral1); @@ -358,7 +358,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/submit", POST) .screwdriverIdentity(SCREWDRIVER_ID) .data(createApplicationSubmissionData(applicationPackage, 1000)), - "{\"message\":\"Application package version: 1.0.1-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); deploymentTester.triggerJobs(); @@ -518,18 +518,18 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST a roll-out of the latest application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/application", POST) .userIdentity(USER_ID), - "{\"message\":\"Triggered application change to 1.0.1-commit1 for tenant1.application1.instance1\"}"); + "{\"message\":\"Triggered revision change to build 1 for tenant1.application1.instance1\"}"); // POST a roll-out of a given revision tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/application", POST) .data("{ \"build\": 1 }") .userIdentity(USER_ID), - "{\"message\":\"Triggered application change to 1.0.1-commit1 for tenant1.application1.instance1\"}"); + "{\"message\":\"Triggered revision change to build 1 for tenant1.application1.instance1\"}"); // DELETE (cancel) ongoing change tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", DELETE) .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"message\":\"Changed deployment from 'application change to 1.0.1-commit1' to 'no change' for tenant1.application1.instance1\"}"); + "{\"message\":\"Changed deployment from 'revision change to build 1' to 'no change' for tenant1.application1.instance1\"}"); // DELETE (cancel) again is a no-op tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", DELETE) @@ -773,7 +773,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) .screwdriverIdentity(SCREWDRIVER_ID) .data(createApplicationSubmissionData(packageWithService, 123)), - "{\"message\":\"Application package version: 1.0.2-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 2, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/diff/2", GET).userIdentity(HOSTED_VESPA_OPERATOR), (response) -> assertTrue(response.getBodyAsString(), @@ -818,7 +818,7 @@ public class ApplicationApiTest extends ControllerContainerTest { .screwdriverIdentity(SCREWDRIVER_ID) .header("X-Content-Hash", Base64.getEncoder().encodeToString(Signatures.sha256Digest(streamer::data))) .data(streamer), - "{\"message\":\"Application package version: 1.0.3-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 3, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); // Sixth attempt has a multi-instance deployment spec, and is accepted. ApplicationPackage multiInstanceSpec = new ApplicationPackageBuilder() @@ -831,7 +831,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) .screwdriverIdentity(SCREWDRIVER_ID) .data(createApplicationSubmissionData(multiInstanceSpec, 123)), - "{\"message\":\"Application package version: 1.0.4-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 4, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); // DELETE submitted build, to mark it as non-deployable @@ -1196,7 +1196,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // GET non-existent application package tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET).userIdentity(HOSTED_VESPA_OPERATOR), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"No application package has been submitted for 'tenant1.application1'\"}", + "{\"error-code\":\"NOT_FOUND\",\"message\":\"no application package has been submitted for tenant1.application1\"}", 404); // GET non-existent application package of specific build @@ -1204,11 +1204,11 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit", POST) .screwdriverIdentity(SCREWDRIVER_ID) .data(createApplicationSubmissionData(applicationPackageInstance1, 1000)), - "{\"message\":\"Application package version: 1.0.1-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET) .properties(Map.of("build", "42")) .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"No application package found for 'tenant1.application1' with build number 42\"}", + "{\"error-code\":\"NOT_FOUND\",\"message\":\"No build 42 found for tenant1.application1\"}", 404); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/deployment", DELETE).userIdentity(USER_ID).oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"All deployments removed\"}"); @@ -1217,7 +1217,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET) .properties(Map.of("build", "foobar")) .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid build number: For input string: \\\"foobar\\\"\"}", + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"invalid value for request parameter 'build': For input string: \\\"foobar\\\"\"}", 400); // POST (deploy) an application to legacy deploy path @@ -1419,7 +1419,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit/", POST) .data(createApplicationSubmissionData(applicationPackage, 123)) .screwdriverIdentity(screwdriverId), - "{\"message\":\"Application package version: 1.0.1-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); + "{\"message\":\"application build 1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}"); } @Test diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java index b7dc3bcc499..ee91787a36e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java @@ -5,10 +5,9 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.slime.SlimeUtils; -import com.yahoo.test.json.JsonTestHelper; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -17,7 +16,6 @@ import org.junit.Test; import java.io.ByteArrayOutputStream; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -67,14 +65,14 @@ public class JobControllerApiHandlerHelperTest { // Revision 1 gets deployed everywhere. app.submit(applicationPackage).deploy(); - ApplicationVersion revision1 = app.lastSubmission().get(); + RevisionId revision1 = app.lastSubmission().get(); assertEquals(1000, tester.application().projectId().getAsLong()); // System test includes test report assertResponse(JobControllerApiHandlerHelper.runDetailsResponse(tester.jobs(), tester.jobs().last(app.instanceId(), systemTest).get().id(), "0"), "system-test-log.json"); tester.clock().advance(Duration.ofMillis(1000)); // Revision 2 gets deployed everywhere except in us-east-3. - ApplicationVersion revision2 = app.submit(applicationPackage).lastSubmission().get(); + RevisionId revision2 = app.submit(applicationPackage).lastSubmission().get(); app.runJob(systemTest); app.runJob(stagingTest); app.runJob(productionUsCentral1); @@ -91,9 +89,9 @@ public class JobControllerApiHandlerHelperTest { tester.clock().advance(Duration.ofHours(4).plusSeconds(1)); tester.runner().run(); assertEquals(installationFailed, tester.jobs().last(app.instanceId(), productionUsWest1).get().status()); - assertEquals(revision2, app.deployment(productionUsCentral1.zone(tester.controller().system())).applicationVersion()); - assertEquals(revision1, app.deployment(productionUsEast3.zone(tester.controller().system())).applicationVersion()); - assertEquals(revision2, app.deployment(productionUsWest1.zone(tester.controller().system())).applicationVersion()); + assertEquals(revision2, app.deployment(productionUsCentral1.zone(tester.controller().system())).revision()); + assertEquals(revision1, app.deployment(productionUsEast3.zone(tester.controller().system())).revision()); + assertEquals(revision2, app.deployment(productionUsWest1.zone(tester.controller().system())).revision()); tester.clock().advance(Duration.ofMillis(1000)); @@ -133,14 +131,14 @@ public class JobControllerApiHandlerHelperTest { // Only us-east-3 is verified, on revision1. // staging-test has 5 runs: one success without sources on revision1, one success from revision1 to revision2, // one success from revision2 to revision3 and two failures from revision1 to revision3. - assertResponse(JobControllerApiHandlerHelper.runResponse(tester.jobs().runs(app.instanceId(), stagingTest), Optional.empty(), URI.create("https://some.url:43/root")), "staging-runs.json"); + assertResponse(JobControllerApiHandlerHelper.runResponse(app.application(), tester.jobs().runs(app.instanceId(), stagingTest), Optional.empty(), URI.create("https://some.url:43/root")), "staging-runs.json"); assertResponse(JobControllerApiHandlerHelper.runDetailsResponse(tester.jobs(), tester.jobs().last(app.instanceId(), stagingTest).get().id(), "0"), "staging-test-log.json"); assertResponse(JobControllerApiHandlerHelper.runDetailsResponse(tester.jobs(), tester.jobs().last(app.instanceId(), productionUsEast3).get().id(), "0"), "us-east-3-log-without-first.json"); assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.controller(), app.instanceId(), URI.create("https://some.url:43/root/")), "overview.json"); var userApp = tester.newDeploymentContext(app.instanceId().tenant().value(), app.instanceId().application().value(), "user"); userApp.runJob(devAwsUsEast2a, applicationPackage); - assertResponse(JobControllerApiHandlerHelper.runResponse(tester.jobs().runs(userApp.instanceId(), devAwsUsEast2a), Optional.empty(), URI.create("https://some.url:43/root")), "dev-aws-us-east-2a-runs.json"); + assertResponse(JobControllerApiHandlerHelper.runResponse(app.application(), tester.jobs().runs(userApp.instanceId(), devAwsUsEast2a), Optional.empty(), URI.create("https://some.url:43/root")), "dev-aws-us-east-2a-runs.json"); assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.controller(), userApp.instanceId(), URI.create("https://some.url:43/root/")), "overview-user-instance.json"); assertResponse(JobControllerApiHandlerHelper.overviewResponse(tester.controller(), app.application().id(), URI.create("https://some.url:43/root/")), "deployment-overview-2.json"); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json index 7b8ddf3b90d..80e04d58614 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json @@ -19,6 +19,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=aws-us-east-1c&application=scoober.albums", "version": "7.164.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "1000", "applicationVersion": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json index 86571f3bf9a..7244cf1493a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json @@ -19,6 +19,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-west-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "1000", "applicationVersion": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json index 86571f3bf9a..7244cf1493a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json @@ -19,6 +19,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-west-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "1000", "applicationVersion": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json index 6965b246176..1448b79385b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json @@ -35,6 +35,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-central-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "endpointStatus": [ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json index bfaa4d5c813..2e9618c890d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json @@ -6,7 +6,7 @@ { "at": 0, "type": "info", - "message": "Deploying platform version 6.1 and application version 1.0.1 ..." + "message": "Deploying platform version 6.1 and application dev build 1 for devUsEast1 of default ..." }, { "at": 0, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json index bb8b9ba9a02..4bd328f605f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json @@ -62,6 +62,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=dev®ion=us-east-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "status": "complete", @@ -121,6 +122,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-central-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "endpointStatus": [ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json index 495958cab53..bfbeda9233d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json @@ -69,6 +69,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=dev®ion=us-east-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "status": "complete", @@ -128,6 +129,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-central-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "endpointStatus": [ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json index 7c1f7223e9e..ba65b962a73 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json @@ -80,7 +80,7 @@ { "at": 14503000, "type": "info", - "message": "Deploying platform version 6.1 and application version 1.0.1-commit1 ..." + "message": "Deploying platform version 6.1 and application build 1 ..." }, { "at": 14503000, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json index 83a09e8a3f3..3b505bc11fd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json @@ -100,7 +100,7 @@ { "at": 1600000000000, "type": "info", - "message": "Deploying platform version 6.1 and application version 1.0.1-commit1 ..." + "message": "Deploying platform version 6.1 and application build 1 ..." }, { "at": 1600000000000, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json index bfb5d8e6cbc..e31058d349b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json @@ -95,7 +95,7 @@ { "at": 0, "type": "info", - "message": "Deploying platform version 6.1 and application version 1.0.1-commit1 ..." + "message": "Deploying platform version 6.1 and application build 1 ..." }, { "at": 0, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json index 7503cb68337..054aaec0fcd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json @@ -68,6 +68,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=dev®ion=us-east-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "status": "complete", @@ -127,6 +128,7 @@ "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-central-1&application=tenant1.application1.instance1", "version": "6.1.0", "revision": "1.0.1-commit1", + "build": 1, "deployTimeEpochMs": 1600000000000, "screwdriverId": "123", "endpointStatus": [ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java index eab3a37a9c3..be3a50c38b0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java @@ -66,8 +66,7 @@ public class ControllerAuthorizationFilterTest { @Test public void unprivilegedInPublic() { - ControllerTester tester = new ControllerTester(); - tester.zoneRegistry().setSystemName(SystemName.Public); + ControllerTester tester = new ControllerTester(SystemName.Public); SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.everyone())); ControllerAuthorizationFilter filter = createFilter(tester); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiTest.java index cf4deb7b4bf..5c210616cb1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiTest.java @@ -47,13 +47,17 @@ public class OsApiTest extends ControllerContainerTest { private ContainerTester tester; private List<OsUpgrader> osUpgraders; + @Override + protected SystemName system() { + return SystemName.cd; + } + @Before public void before() { tester = new ContainerTester(container, responses); tester.serviceRegistry().clock().setInstant(Instant.ofEpochMilli(1234)); addUserToHostedOperatorRole(operator); - zoneRegistryMock().setSystemName(SystemName.cd) - .setZones(zone1, zone2, zone3) + zoneRegistryMock().setZones(zone1, zone2, zone3) .reprovisionToUpgradeOsIn(zone3) .setOsUpgradePolicy(cloud1, UpgradePolicy.builder().upgrade(zone1).upgrade(zone2).build()) .setOsUpgradePolicy(cloud2, UpgradePolicy.builder().upgrade(zone3).build()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java index 83b6f05c0b0..fca3dde8f7d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java @@ -322,7 +322,7 @@ public class RoutingPoliciesTest { @Test public void zone_routing_policies_without_dns_update() { - var tester = new RoutingPoliciesTester(new DeploymentTester(), SystemName.main, false); + var tester = new RoutingPoliciesTester(new DeploymentTester(), false); var context = tester.newDeploymentContext("tenant1", "app1", "default"); tester.provisionLoadBalancers(1, context.instanceId(), true, zone1, zone2); context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); @@ -892,23 +892,21 @@ public class RoutingPoliciesTest { } public RoutingPoliciesTester(SystemName system) { - this(new DeploymentTester(system.isPublic() - ? new ControllerTester(new RotationsConfig.Builder().build(), system) - : new ControllerTester()), - system, + this(new DeploymentTester(system.isPublic() ? new ControllerTester(new RotationsConfig.Builder().build(), system) + : new ControllerTester(system)), true); } - public RoutingPoliciesTester(DeploymentTester tester, SystemName system, boolean exclusiveRouting) { + public RoutingPoliciesTester(DeploymentTester tester, boolean exclusiveRouting) { this.tester = tester; List<ZoneId> zones; - if (system.isPublic()) { + if (tester.controller().system().isPublic()) { zones = publicZones(); } else { zones = new ArrayList<>(tester.controllerTester().zoneRegistry().zones().all().ids()); // Default zones zones.add(zone4); // Missing from default ZoneRegistryMock zones } - tester.controllerTester().setZones(zones, system); + tester.controllerTester().setZones(zones); if (exclusiveRouting) { tester.controllerTester().setRoutingMethod(zones, RoutingMethod.exclusive); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java index d7847da2404..02154470de2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java @@ -149,10 +149,10 @@ public class RotationRepositoryTest { ZoneApiMock.fromId("staging.cd-us-west-1"), ZoneApiMock.fromId("prod.cd-us-east-1"), ZoneApiMock.fromId("prod.cd-us-west-1")); + DeploymentTester tester = new DeploymentTester(new ControllerTester(rotationsConfig, SystemName.cd)); tester.controllerTester().zoneRegistry() .setZones(zones) - .setRoutingMethod(zones, RoutingMethod.sharedLayer4) - .setSystemName(SystemName.cd); + .setRoutingMethod(zones, RoutingMethod.sharedLayer4); tester.configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), SystemApplication.notController()); var application2 = tester.newDeploymentContext("tenant2", "app2", "default"); application2.submit(applicationPackage).deploy(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java index 9902f9bd07d..297410112d5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java @@ -5,6 +5,7 @@ import com.yahoo.component.Version; import com.yahoo.component.Vtag; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostName; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.ControllerTester; @@ -89,7 +90,8 @@ public class VersionStatusTest { HostName controller3 = HostName.of("controller-3"); MockCuratorDb db = new MockCuratorDb(Stream.of(controller1, controller2, controller3) .map(hostName -> hostName.value() + ":2222") - .collect(Collectors.joining(","))); + .collect(Collectors.joining(",")), + SystemName.main); ControllerTester tester = new ControllerTester(db); writeControllerVersion(controller1, Version.fromString("6.2"), db); @@ -498,7 +500,8 @@ public class VersionStatusTest { HostName controller3 = HostName.of("controller-3"); MockCuratorDb db = new MockCuratorDb(Stream.of(controller1, controller2, controller3) .map(hostName -> hostName.value() + ":2222") - .collect(Collectors.joining(","))); + .collect(Collectors.joining(",")), + SystemName.main); DeploymentTester tester = new DeploymentTester(new ControllerTester(db)); // Commit details are set for initial version |