diff options
Diffstat (limited to 'controller-server/src')
178 files changed, 4107 insertions, 3706 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java index af8965bdeff..bdb68f655ff 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 @@ -33,6 +33,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * An application. Belongs to a {@link Tenant}, and may have multiple {@link Instance}s. @@ -161,7 +162,7 @@ public class Application { public ApplicationActivity activity() { return ApplicationActivity.from(instances.values().stream() .flatMap(instance -> instance.deployments().values().stream()) - .collect(Collectors.toUnmodifiableList())); + .toList()); } public Map<InstanceName, List<Deployment>> productionDeployments() { @@ -183,33 +184,44 @@ public class Application { .min(Comparator.naturalOrder()); } - /** - * Returns the oldest application version this has deployed in a permanent zone (not test or staging). - */ + /** Returns the oldest application version this has deployed in a permanent zone (not test or staging) */ public Optional<RevisionId> oldestDeployedRevision() { + return productionRevisions().min(Comparator.naturalOrder()); + } + + /** Returns the latest application version this has deployed in a permanent zone (not test or staging) */ + public Optional<RevisionId> latestDeployedRevision() { + return productionRevisions().max(Comparator.naturalOrder()); + } + + private Stream<RevisionId> productionRevisions() { return productionDeployments().values().stream().flatMap(List::stream) .map(Deployment::revision) - .filter(RevisionId::isProduction) - .min(Comparator.naturalOrder()); + .filter(RevisionId::isProduction); } /** Returns the total quota usage for this application, excluding temporary deployments */ public QuotaUsage quotaUsage() { return instances().values().stream() - .map(Instance::quotaUsage).reduce(QuotaUsage::add).orElse(QuotaUsage.none); + .map(Instance::quotaUsage) + .reduce(QuotaUsage::add) + .orElse(QuotaUsage.none); } /** Returns the total quota usage for manual deployments for this application */ public QuotaUsage manualQuotaUsage() { return instances().values().stream() - .map(Instance::manualQuotaUsage).reduce(QuotaUsage::add).orElse(QuotaUsage.none); + .map(Instance::manualQuotaUsage) + .reduce(QuotaUsage::add) + .orElse(QuotaUsage.none); } /** Returns the total quota usage for this application, excluding one specific deployment (and temporary deployments) */ public QuotaUsage quotaUsage(ApplicationId application, ZoneId zone) { return instances().values().stream() .map(instance -> instance.quotaUsageExcluding(application, zone)) - .reduce(QuotaUsage::add).orElse(QuotaUsage.none); + .reduce(QuotaUsage::add) + .orElse(QuotaUsage.none); } /** Returns the set of deploy keys for this application. */ 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 e48ad7596ea..78063a383dc 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 @@ -237,7 +237,7 @@ public class ApplicationController { } /** Sets the default target major version. Set to empty to determine target version normally (by confidence) */ - public void setTargetMajorVersion(Optional<Integer> targetMajorVersion) { + public void setTargetMajorVersion(OptionalInt targetMajorVersion) { curator.writeTargetMajorVersion(targetMajorVersion); } @@ -340,7 +340,10 @@ public class ApplicationController { Version oldestInstalledPlatform = oldestInstalledPlatform(id); // Target platforms are all versions not older than the oldest installed platform, unless forcing a major version change. - Predicate<Version> isTargetPlatform = targetMajor.isEmpty() || targetMajor.getAsInt() == oldestInstalledPlatform.getMajor() + // Only major version specified in deployment spec is enough to force a downgrade, while all sources may force an upgrade. + Predicate<Version> isTargetPlatform = targetMajor.isEmpty() + || targetMajor.getAsInt() == oldestInstalledPlatform.getMajor() + || wantedMajor.isEmpty() && targetMajor.getAsInt() <= oldestInstalledPlatform.getMajor() ? version -> ! version.isBefore(oldestInstalledPlatform) : version -> targetMajor.getAsInt() == version.getMajor(); Set<Version> platformVersions = versionStatus.versions().stream() @@ -446,7 +449,7 @@ public class ApplicationController { } /** Deploys an application package for an existing application instance. */ - public ActivateResult deploy(JobId job, boolean deploySourceVersions) { + public ActivateResult deploy(JobId job, boolean deploySourceVersions, Consumer<String> deployLogger) { if (job.application().instance().isTester()) throw new IllegalArgumentException("'" + job.application() + "' is a tester application!"); @@ -479,6 +482,7 @@ public class ApplicationController { applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get()); endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec()); + containerEndpoints = controller.routing().of(deployment).prepare(application); } // Release application lock while doing the deployment, which is a lengthy task. @@ -487,6 +491,8 @@ public class ApplicationController { ActivateResult result = deploy(job.application(), applicationPackage, zone, platform, containerEndpoints, endpointCertificateMetadata, run.isDryRun()); + endpointCertificateMetadata.ifPresent(e -> deployLogger.accept("Using CA signed certificate version %s".formatted(e.version()))); + // Record the quota usage for this application var quotaUsage = deploymentQuotaUsage(zone, job.application()); @@ -544,10 +550,9 @@ public class ApplicationController { controller.jobController().deploymentStatus(application.get()); for (Notification notification : controller.notificationsDb().listNotifications(NotificationSource.from(application.get().id()), true)) { - if ( ! notification.source().instance().map(declaredInstances::contains).orElse(true)) - controller.notificationsDb().removeNotifications(notification.source()); - if (notification.source().instance().isPresent() && - ! notification.source().zoneId().map(application.get().require(notification.source().instance().get()).deployments()::containsKey).orElse(false)) + if ( notification.source().instance().isPresent() + && ( ! declaredInstances.contains(notification.source().instance().get()) + || ! notification.source().zoneId().map(application.get().require(notification.source().instance().get()).deployments()::containsKey).orElse(false))) controller.notificationsDb().removeNotifications(notification.source()); } @@ -641,7 +646,7 @@ public class ApplicationController { .filter(zone -> deploymentSpec.instance(instance).isEmpty() || ! deploymentSpec.requireInstance(instance).deploysTo(zone.environment(), zone.region())) - .collect(toList()); + .toList(); if (deploymentsToRemove.isEmpty()) return application; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java index 4f58e87035b..ac7c6319c1b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java @@ -129,25 +129,27 @@ public abstract class LockedTenant { private final TenantInfo info; private final List<TenantSecretStore> tenantSecretStores; private final ArchiveAccess archiveAccess; + private final Optional<Instant> invalidateUserSessionsBefore; private Cloud(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys, TenantInfo info, - List<TenantSecretStore> tenantSecretStores, ArchiveAccess archiveAccess) { + List<TenantSecretStore> tenantSecretStores, ArchiveAccess archiveAccess, Optional<Instant> invalidateUserSessionsBefore) { super(name, createdAt, lastLoginInfo); this.developerKeys = ImmutableBiMap.copyOf(developerKeys); this.creator = creator; this.info = info; this.tenantSecretStores = tenantSecretStores; this.archiveAccess = archiveAccess; + this.invalidateUserSessionsBefore = invalidateUserSessionsBefore; } private Cloud(CloudTenant tenant) { - this(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), tenant.creator(), tenant.developerKeys(), tenant.info(), tenant.tenantSecretStores(), tenant.archiveAccess()); + this(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), tenant.creator(), tenant.developerKeys(), tenant.info(), tenant.tenantSecretStores(), tenant.archiveAccess(), tenant.invalidateUserSessionsBefore()); } @Override public CloudTenant get() { - return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess); + return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); } public Cloud withDeveloperKey(PublicKey key, Principal principal) { @@ -155,38 +157,42 @@ public abstract class LockedTenant { if (keys.containsKey(key)) throw new IllegalArgumentException("Key " + KeyUtils.toPem(key) + " is already owned by " + keys.get(key)); keys.put(key, principal); - return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); } public Cloud withoutDeveloperKey(PublicKey key) { BiMap<PublicKey, Principal> keys = HashBiMap.create(developerKeys); keys.remove(key); - return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); } public Cloud withInfo(TenantInfo newInfo) { - return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, newInfo, tenantSecretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, newInfo, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); } @Override public LockedTenant with(LastLoginInfo lastLoginInfo) { - return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); } public Cloud withSecretStore(TenantSecretStore tenantSecretStore) { ArrayList<TenantSecretStore> secretStores = new ArrayList<>(tenantSecretStores); secretStores.add(tenantSecretStore); - return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess, invalidateUserSessionsBefore); } public Cloud withoutSecretStore(TenantSecretStore tenantSecretStore) { ArrayList<TenantSecretStore> secretStores = new ArrayList<>(tenantSecretStores); secretStores.remove(tenantSecretStore); - return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores, archiveAccess, invalidateUserSessionsBefore); } public Cloud withArchiveAccess(ArchiveAccess archiveAccess) { - return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess); + return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); + } + + public Cloud withInvalidateUserSessionsBefore(Instant invalidateUserSessionsBefore) { + return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, Optional.of(invalidateUserSessionsBefore)); } } 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 9f93033a1a2..1d7d75d9193 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 @@ -2,7 +2,6 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.google.common.collect.ImmutableMap; -import com.yahoo.collections.Iterables; import com.yahoo.component.Version; import com.yahoo.component.VersionCompatibility; import com.yahoo.config.application.api.DeploymentInstanceSpec; @@ -231,9 +230,9 @@ public class DeploymentStatus { firstProductionJobWithDeploymentInCloud.flatMap(this::deploymentFor), fallbackPlatform(change, job)); if (step.completedAt(change, firstProductionJobWithDeploymentInCloud).isEmpty()) { - JobType actualType = job.type().isSystemTest() ? systemTest(firstProductionJobWithDeploymentInCloud.map(JobId::type).orElse(null)) - : stagingTest(firstProductionJobWithDeploymentInCloud.map(JobId::type).orElse(null)); - jobs.merge(job, List.of(new Job(actualType, versions, step.readyAt(change), change)), DeploymentStatus::union); + CloudName cloud = firstProductionJobWithDeploymentInCloud.map(JobId::type).map(this::findCloud).orElse(zones.systemZone().getCloudName()); + JobType typeWithZone = job.type().isSystemTest() ? JobType.systemTest(zones, cloud) : JobType.stagingTest(zones, cloud); + jobs.merge(job, List.of(new Job(typeWithZone, versions, step.readyAt(change), change)), DeploymentStatus::union); } } }); @@ -291,19 +290,16 @@ public class DeploymentStatus { } private <T extends Comparable<T>> Optional<T> newestTested(InstanceName instance, Function<Run, T> runMapper) { - Set<CloudName> clouds = jobSteps.keySet().stream() - .filter(job -> job.type().isProduction()) - .map(job -> findCloud(job.type())) - .collect(toSet()); + Set<CloudName> clouds = Stream.concat(Stream.of(zones.systemZone().getCloudName()), + jobSteps.keySet().stream() + .filter(job -> job.type().isProduction()) + .map(job -> findCloud(job.type()))) + .collect(toSet()); List<ZoneId> testZones = new ArrayList<>(); - if (application.deploymentSpec().requireInstance(instance).concerns(test)) { - if (clouds.isEmpty()) testZones.add(JobType.systemTest(zones, null).zone()); - else for (CloudName cloud: clouds) testZones.add(JobType.systemTest(zones, cloud).zone()); - } - if (application.deploymentSpec().requireInstance(instance).concerns(staging)) { - if (clouds.isEmpty()) testZones.add(JobType.stagingTest(zones, null).zone()); - else for (CloudName cloud: clouds) testZones.add(JobType.stagingTest(zones, cloud).zone()); - } + if (application.deploymentSpec().requireInstance(instance).concerns(test)) + for (CloudName cloud: clouds) testZones.add(JobType.systemTest(zones, cloud).zone()); + if (application.deploymentSpec().requireInstance(instance).concerns(staging)) + for (CloudName cloud: clouds) testZones.add(JobType.stagingTest(zones, cloud).zone()); Map<ZoneId, Optional<T>> newestPerZone = instanceJobs().get(application.id().instance(instance)) .type(systemTest(null), stagingTest(null)) @@ -548,7 +544,9 @@ public class DeploymentStatus { if (job.type().isProduction() && job.type().isDeployment()) { declaredTest(job.application(), testType).ifPresent(testJob -> { for (Job productionJob : versionsList) - if (allJobs.successOn(testType, productionJob.versions()).asList().isEmpty()) + if (allJobs.successOn(testType, productionJob.versions()) + .instance(testJob.application().instance()) + .asList().isEmpty()) testJobs.merge(testJob, List.of(new Job(testJob.type(), productionJob.versions(), jobSteps().get(testJob).readyAt(productionJob.change), @@ -580,7 +578,7 @@ public class DeploymentStatus { } private CloudName findCloud(JobType job) { - return zones.zones().all().get(job.zone()).map(ZoneApi::getCloudName).orElse(null); + return zones.zones().all().get(job.zone()).map(ZoneApi::getCloudName).orElse(zones.systemZone().getCloudName()); } private JobId firstDeclaredOrElseImplicitTest(JobType testJob) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java index 22df5ca559e..4a00a272c75 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.yahoo.collections.AbstractFilteringList; import com.yahoo.component.Version; +import com.yahoo.vespa.hosted.controller.application.Change; import java.time.Instant; import java.util.Collection; @@ -36,8 +37,10 @@ public class DeploymentStatusList extends AbstractFilteringList<DeploymentStatus /** Returns the subset of applications which have been failing an application change since the given instant */ public DeploymentStatusList failingApplicationChangeSince(Instant threshold) { - return matching(status -> status.instanceJobs().values().stream() - .anyMatch(jobs -> failingApplicationChangeSince(jobs, threshold))); + return matching(status -> status.instanceJobs().entrySet().stream() + .anyMatch(jobs -> failingApplicationChangeSince(jobs.getValue(), + status.application().require(jobs.getKey().instance()).change(), + threshold))); } private static boolean failingUpgradeToVersionSince(JobList jobs, Version version, Instant threshold) { @@ -47,10 +50,8 @@ public class DeploymentStatusList extends AbstractFilteringList<DeploymentStatus .isEmpty(); } - private static boolean failingApplicationChangeSince(JobList jobs, Instant threshold) { - return ! jobs.failingApplicationChange() - .firstFailing().endedNoLaterThan(threshold) - .isEmpty(); + private static boolean failingApplicationChangeSince(JobList jobs, Change change, Instant threshold) { + return change.revision().map(revision -> ! jobs.failingWithBrokenRevisionSince(revision, threshold).isEmpty()).orElse(false); } } 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 c28f94bc4d7..d83f552ab25 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 @@ -61,6 +61,7 @@ import static java.util.stream.Collectors.toMap; public class DeploymentTrigger { public static final Duration maxPause = Duration.ofDays(3); + public static final Duration maxFailingRevisionTime = Duration.ofDays(5); private final static Logger log = Logger.getLogger(DeploymentTrigger.class.getName()); private final Controller controller; @@ -227,10 +228,9 @@ public class DeploymentTrigger { Instance instance = application.require(applicationId.instance()); JobId job = new JobId(instance.id(), jobType); JobStatus jobStatus = jobs.jobStatus(new JobId(applicationId, jobType)); - Versions versions = jobStatus.lastTriggered() - .orElseThrow(() -> new IllegalArgumentException(job + " has never been triggered")) - .versions(); - trigger(deploymentJob(instance, versions, jobType, jobStatus, clock.instant()), reason); + Run last = jobStatus.lastTriggered() + .orElseThrow(() -> new IllegalArgumentException(job + " has never been triggered")); + trigger(deploymentJob(instance, last.versions(), last.id().type(), jobStatus.isNodeAllocationFailure(), clock.instant()), reason); return job; } @@ -258,7 +258,12 @@ public class DeploymentTrigger { .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); jobs.forEach((jobId, versionsList) -> { - trigger(deploymentJob(application.require(job.application().instance()), versionsList.get(0).versions(), jobId.type(), status.jobs().get(jobId).get(), clock.instant()), reason); + trigger(deploymentJob(application.require(job.application().instance()), + versionsList.get(0).versions(), + jobId.type(), + status.jobs().get(jobId).get().isNodeAllocationFailure(), + clock.instant()), + reason); }); return List.copyOf(jobs.keySet()); } @@ -387,7 +392,7 @@ public class DeploymentTrigger { jobs.add(deploymentJob(status.application().require(jobId.application().instance()), job.versions(), job.type(), - status.instanceJobs(jobId.application().instance()).get(jobId.type()), + status.instanceJobs(jobId.application().instance()).get(jobId.type()).isNodeAllocationFailure(), job.readyAt().get())); }); return Collections.unmodifiableList(jobs); @@ -448,6 +453,8 @@ public class DeploymentTrigger { private boolean acceptNewRevision(DeploymentStatus status, InstanceName instance, RevisionId revision) { if (status.application().deploymentSpec().instance(instance).isEmpty()) return false; // Unknown instance. + if ( ! status.jobs().failingWithBrokenRevisionSince(revision, clock.instant().minus(maxFailingRevisionTime)) + .isEmpty()) return false; // Don't deploy a broken revision. boolean isChangingRevision = status.application().require(instance).change().revision().isPresent(); DeploymentInstanceSpec spec = status.application().deploymentSpec().requireInstance(instance); Predicate<RevisionId> revisionFilter = spec.revisionTarget() == DeploymentSpec.RevisionTarget.next @@ -472,8 +479,8 @@ 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().revision().isPresent()); + private Job deploymentJob(Instance instance, Versions versions, JobType jobType, boolean isNodeAllocationFailure, Instant availableSince) { + return new Job(instance, versions, jobType, availableSince, isNodeAllocationFailure, instance.change().revision().isPresent()); } // ---------- Data containers ---------- 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 813e3454e80..ef3474e0c1e 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 @@ -174,7 +174,7 @@ public class InternalStepRunner implements StepRunner { private Optional<RunStatus> deployReal(RunId id, boolean setTheStage, DualLogger logger) { Optional<X509Certificate> testerCertificate = controller.jobController().run(id).testerCertificate(); - return deploy(() -> controller.applications().deploy(id.job(), setTheStage), + return deploy(() -> controller.applications().deploy(id.job(), setTheStage, logger::log), controller.jobController().run(id) .stepInfo(setTheStage ? deployInitialReal : deployReal).get() .startTime().get(), @@ -224,6 +224,9 @@ public class InternalStepRunner implements StepRunner { // Retry certain failures for up to one hour. Optional<RunStatus> result = startTime.isBefore(controller.clock().instant().minus(Duration.ofHours(1))) ? Optional.of(deploymentFailed) : Optional.empty(); + if (result.isPresent()) + logger.log(WARNING, "Deployment failed for one hour; giving up now!"); + switch (e.code()) { case CERTIFICATE_NOT_READY: logger.log("No valid CA signed certificate for app available to config server"); @@ -424,10 +427,14 @@ public class InternalStepRunner implements StepRunner { Optional<ServiceConvergence> services = controller.serviceRegistry().configServer().serviceConvergence(new DeploymentId(testerId, zone), Optional.of(platform)); if (services.isEmpty()) { - logger.log("Config status not currently available -- will retry."); - return run.stepInfo(installTester).get().startTime().get().isBefore(controller.clock().instant().minus(Duration.ofMinutes(5))) - ? Optional.of(error) - : Optional.empty(); + if (run.stepInfo(installTester).get().startTime().get().isBefore(controller.clock().instant().minus(Duration.ofMinutes(30)))) { + logger.log(WARNING, "Config status not available after 30 minutes; giving up!"); + return Optional.of(error); + } + else { + logger.log("Config status not currently available -- will retry."); + return Optional.empty(); + } } List<Node> nodes = controller.serviceRegistry().configServer().nodeRepository().list(zone, NodeFilter.all() @@ -649,10 +656,13 @@ public class InternalStepRunner implements StepRunner { controller.jobController().updateTestReport(id); return Optional.of(testFailure); case INCONCLUSIVE: - long sleepMinutes = Math.max(15, Math.min(120, Duration.between(deployment.get().at(), controller.clock().instant()).toMinutes() / 20)); - logger.log("Tests were inconclusive, and will run again in " + sleepMinutes + " minutes."); controller.jobController().updateTestReport(id); - controller.jobController().locked(id, run -> run.sleepingUntil(controller.clock().instant().plusSeconds(60 * sleepMinutes))); + controller.jobController().locked(id, run -> { + Instant nextAttemptAt = run.start(); + while ( ! nextAttemptAt.isAfter(controller.clock().instant())) nextAttemptAt = nextAttemptAt.plusSeconds(1800); + logger.log("Tests were inconclusive, and will run again at " + nextAttemptAt + "."); + return run.sleepingUntil(nextAttemptAt); + }); return Optional.of(reset); case ERROR: logger.log(INFO, "Tester failed running its tests!"); @@ -799,6 +809,7 @@ public class InternalStepRunner implements StepRunner { Consumer<String> updater = msg -> controller.notificationsDb().setNotification(source, Notification.Type.deployment, Notification.Level.error, msg); switch (run.status()) { case aborted: return; // wait and see how the next run goes. + case noTests: case running: case success: controller.notificationsDb().removeNotification(source, Notification.Type.deployment); @@ -815,10 +826,6 @@ public class InternalStepRunner implements StepRunner { case testFailure: updater.accept("one or more verification tests against the deployment failed. Please review test output in the deployment job log."); return; - case noTests: - controller.notificationsDb().setNotification(source, Notification.Type.deployment, Notification.Level.warning, - "no tests were found for this job type. Please review test output in the deployment job log."); - return; case error: case endpointCertificateTimeout: break; 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 5113d386b23..881107fa0f9 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 @@ -158,7 +158,7 @@ public class JobController { /** Stores the given log entries for the given run and step. */ public void log(RunId id, Step step, List<LogEntry> entries) { locked(id, __ -> { - logs.append(id.application(), id.type(), step, entries); + logs.append(id.application(), id.type(), step, entries, true); return __; }); } @@ -211,7 +211,7 @@ public class JobController { if (log.isEmpty()) return run; - logs.append(id.application(), id.type(), Step.copyVespaLogs, log); + logs.append(id.application(), id.type(), Step.copyVespaLogs, log, false); return run.with(log.get(log.size() - 1).at()); }); } @@ -230,7 +230,7 @@ public class JobController { if (entries.isEmpty()) return run; - logs.append(id.application(), id.type(), step.get(), entries); + logs.append(id.application(), id.type(), step.get(), entries, false); return run.with(entries.stream().mapToLong(LogEntry::id).max().getAsLong()); }); } @@ -408,11 +408,6 @@ public class JobController { locked(id, run -> run.with(status, step)); } - /** Invoked when starting the step */ - public void setStartTimestamp(RunId id, Instant timestamp, LockedStep step) { - locked(id, run -> run.with(timestamp, step)); - } - /** * Changes the status of the given run to inactive, and stores it as a historic run. * Throws TimeoutException if some step in this job is still being run. @@ -774,7 +769,8 @@ public class JobController { public void locked(RunId id, UnaryOperator<Run> modifications) { try (Mutex __ = curator.lock(id.application(), id.type())) { active(id).ifPresent(run -> { - curator.writeLastRun(modifications.apply(run)); + Run modified = modifications.apply(run); + if (modified != null) curator.writeLastRun(modified); }); } } 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 551f841233e..3074c9ac3ba 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 @@ -4,7 +4,6 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.yahoo.collections.AbstractFilteringList; import com.yahoo.component.Version; 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; @@ -74,6 +73,14 @@ public class JobList extends AbstractFilteringList<JobStatus, JobList> { return matching(JobList::failingApplicationChange); } + /** Returns the subset of jobs which are failing because of an application change, and have been since the threshold, on the given revision. */ + public JobList failingWithBrokenRevisionSince(RevisionId broken, Instant threshold) { + return failingApplicationChange().matching(job -> job.runs().values().stream() + .anyMatch(run -> run.versions().targetRevision().equals(broken) + && run.hasFailed() + && run.start().isBefore(threshold))); + } + /** Returns the subset of jobs which are failing with the given run status. */ public JobList withStatus(RunStatus status) { return matching(job -> job.lastStatus().map(status::equals).orElse(false)); 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 f4c4b8bebd4..d683f1cb5c7 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 @@ -76,7 +76,7 @@ public class Versions { targetRevision.equals(versions.targetRevision()); } - /** Returns wheter this change could result in the given target versions. */ + /** Returns whether this change could result in the given target versions. */ public boolean targetsMatch(Change change) { return change.platform().map(targetPlatform::equals).orElse(true) && change.revision().map(targetRevision::equals).orElse(true); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java index 09e0fec41d1..c8c5a1834c7 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationMetaDataGarbageCollector.java @@ -14,14 +14,17 @@ public class ApplicationMetaDataGarbageCollector extends ControllerMaintainer { private static final Logger log = Logger.getLogger(ApplicationMetaDataGarbageCollector.class.getName()); + private final Duration timeToLive; + public ApplicationMetaDataGarbageCollector(Controller controller, Duration interval) { super(controller, interval); + this.timeToLive = controller.system().isCd() ? Duration.ofDays(7) : Duration.ofDays(365); } @Override protected double maintain() { try { - controller().applications().applicationStore().pruneMeta(controller().clock().instant().minus(Duration.ofDays(365))); + controller().applications().applicationStore().pruneMeta(controller().clock().instant().minus(timeToLive)); return 1.0; } catch (Exception e) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainer.java index aa36d204c09..a279cf46415 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainer.java @@ -70,6 +70,7 @@ public class ChangeRequestMaintainer extends ControllerMaintainer { var vcmr = existingChangeRequests .getOrDefault(changeRequest.getId(), new VespaChangeRequest(changeRequest, zone)) .withSource(changeRequest.getChangeRequestSource()) + .withImpact(changeRequest.getImpact()) .withApproval(changeRequest.getApproval()); logger.fine(() -> "Storing " + vcmr); curator.writeChangeRequest(vcmr); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java index b1b7e80e9a0..18ef47759f4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java @@ -13,14 +13,13 @@ import com.yahoo.vespa.hosted.controller.tenant.Tenant; import java.time.Duration; import java.util.List; import java.util.Optional; -import java.util.function.Consumer; import java.util.function.Predicate; import java.util.logging.Logger; import java.util.stream.Collectors; /** * Expires unused tenants from Vespa Cloud. - * + * <p> * TODO: Should support sending notifications some time before the various expiry events happen. * * @author ogronnesby @@ -29,7 +28,7 @@ public class CloudTrialExpirer extends ControllerMaintainer { private static final Logger log = Logger.getLogger(CloudTrialExpirer.class.getName()); private static final Duration nonePlanAfter = Duration.ofDays(14); - private static final Duration tombstoneAfter = Duration.ofDays(365); + private static final Duration tombstoneAfter = Duration.ofDays(183); private final ListFlag<String> extendedTrialTenants; public CloudTrialExpirer(Controller controller, Duration interval) { @@ -39,38 +38,43 @@ public class CloudTrialExpirer extends ControllerMaintainer { @Override protected double maintain() { - tombstoneNonePlanTenants(); - moveInactiveTenantsToNonePlan(); - return 1.0; + var a = tombstoneNonePlanTenants(); + var b = moveInactiveTenantsToNonePlan(); + return (a ? 0.5 : 0.0) + (b ? 0.5 : 0.0); } - private void moveInactiveTenantsToNonePlan() { - var predicate = tenantReadersNotLoggedIn(nonePlanAfter) - .and(this::tenantHasTrialPlan); - - forTenant("'none' plan", predicate, this::setPlanNone); - } + private boolean moveInactiveTenantsToNonePlan() { + var idleTrialTenants = controller().tenants().asList().stream() + .filter(this::tenantIsCloudTenant) + .filter(this::tenantIsNotExemptFromExpiry) + .filter(this::tenantHasNoDeployments) + .filter(this::tenantHasTrialPlan) + .filter(tenantReadersNotLoggedIn(nonePlanAfter)) + .toList(); + + if (! idleTrialTenants.isEmpty()) { + var tenants = idleTrialTenants.stream().map(Tenant::name).map(TenantName::value).collect(Collectors.joining(", ")); + log.info("Setting tenants to 'none' plan: " + tenants); + } - private void tombstoneNonePlanTenants() { - var predicate = tenantReadersNotLoggedIn(tombstoneAfter).and(this::tenantHasNonePlan); - forTenant("tombstoned", predicate, this::tombstoneTenants); + return setPlanNone(idleTrialTenants); } - private void forTenant(String name, Predicate<Tenant> p, Consumer<List<Tenant>> c) { - var predicate = p.and(this::tenantIsCloudTenant) - .and(this::tenantIsNotExemptFromExpiry) - .and(this::tenantHasNoDeployments); - - var tenants = controller().tenants().asList().stream() - .filter(predicate) - .collect(Collectors.toList()); - - if (! tenants.isEmpty()) { - var tenantNames = tenants.stream().map(Tenant::name).map(TenantName::value).collect(Collectors.joining(", ")); - log.info("Setting tenants as " + name + ": " + tenantNames); + private boolean tombstoneNonePlanTenants() { + var idleOldPlanTenants = controller().tenants().asList().stream() + .filter(this::tenantIsCloudTenant) + .filter(this::tenantIsNotExemptFromExpiry) + .filter(this::tenantHasNoDeployments) + .filter(this::tenantHasNonePlan) + .filter(tenantReadersNotLoggedIn(tombstoneAfter)) + .toList(); + + if (! idleOldPlanTenants.isEmpty()) { + var tenants = idleOldPlanTenants.stream().map(Tenant::name).map(TenantName::value).collect(Collectors.joining(", ")); + log.info("Setting tenants as tombstoned: " + tenants); } - c.accept(tenants); + return tombstoneTenants(idleOldPlanTenants); } private boolean tenantIsCloudTenant(Tenant tenant) { @@ -98,7 +102,7 @@ public class CloudTrialExpirer extends ControllerMaintainer { } private boolean tenantIsNotExemptFromExpiry(Tenant tenant) { - return ! extendedTrialTenants.value().contains(tenant.name().value()); + return !extendedTrialTenants.value().contains(tenant.name().value()); } private boolean tenantHasNoDeployments(Tenant tenant) { @@ -108,23 +112,46 @@ public class CloudTrialExpirer extends ControllerMaintainer { .sum() == 0; } - private void setPlanNone(List<Tenant> tenants) { - tenants.forEach(tenant -> { - controller().serviceRegistry().billingController().setPlan(tenant.name(), PlanId.from("none"), false, false); - }); + private boolean setPlanNone(List<Tenant> tenants) { + var success = true; + for (var tenant : tenants) { + try { + controller().serviceRegistry().billingController().setPlan(tenant.name(), PlanId.from("none"), false, false); + } catch (RuntimeException e) { + log.info("Could not change plan for " + tenant.name() + ": " + e.getMessage()); + success = false; + } + } + return success; } - private void tombstoneTenants(List<Tenant> tenants) { - tenants.forEach(tenant -> { - deleteApplicationsWithNoDeployments(tenant); - controller().tenants().delete(tenant.name(), Optional.empty(), false); - }); + private boolean tombstoneTenants(List<Tenant> tenants) { + var success = true; + for (var tenant : tenants) { + success &= deleteApplicationsWithNoDeployments(tenant); + log.fine("Tombstoning empty tenant: " + tenant.name()); + try { + controller().tenants().delete(tenant.name(), Optional.empty(), false); + } catch (RuntimeException e) { + log.info("Could not tombstone tenant " + tenant.name() + ": " + e.getMessage()); + success = false; + } + } + return success; } - private void deleteApplicationsWithNoDeployments(Tenant tenant) { - controller().applications().asList(tenant.name()).forEach(application -> { - // this only removes applications with no active deployments - controller().applications().deleteApplication(application.id(), Optional.empty()); - }); + private boolean deleteApplicationsWithNoDeployments(Tenant tenant) { + // this method only removes applications with no active deployments in them + var success = true; + for (var application : controller().applications().asList(tenant.name())) { + try { + log.fine("Removing empty application: " + application.id()); + controller().applications().deleteApplication(application.id(), Optional.empty()); + } catch (RuntimeException e) { + log.info("Could not removing application " + application.id() + ": " + e.getMessage()); + success = false; + } + } + return success; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index 4aeecdcd4ff..ab2e0312b15 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -33,6 +33,7 @@ import static java.time.temporal.ChronoUnit.SECONDS; public class ControllerMaintenance extends AbstractComponent { private final Upgrader upgrader; + private final OsUpgradeScheduler osUpgradeScheduler; private final List<Maintainer> maintainers = new CopyOnWriteArrayList<>(); @Inject @@ -40,7 +41,9 @@ public class ControllerMaintenance extends AbstractComponent { public ControllerMaintenance(Controller controller, Metric metric, UserManagement userManagement, AthenzClientFactory athenzClientFactory) { Intervals intervals = new Intervals(controller.system()); upgrader = new Upgrader(controller, intervals.defaultInterval); + osUpgradeScheduler = new OsUpgradeScheduler(controller, intervals.osUpgradeScheduler); maintainers.add(upgrader); + maintainers.add(osUpgradeScheduler); maintainers.addAll(osUpgraders(controller, intervals.osUpgrader)); maintainers.add(new DeploymentExpirer(controller, intervals.defaultInterval)); maintainers.add(new DeploymentUpgrader(controller, intervals.defaultInterval)); @@ -54,7 +57,6 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new SystemUpgrader(controller, intervals.systemUpgrader)); maintainers.add(new JobRunner(controller, intervals.jobRunner)); maintainers.add(new OsVersionStatusUpdater(controller, intervals.osVersionStatusUpdater)); - maintainers.add(new OsUpgradeScheduler(controller, intervals.osUpgradeScheduler)); maintainers.add(new ContactInformationMaintainer(controller, intervals.contactInformationMaintainer)); maintainers.add(new NameServiceDispatcher(controller, intervals.nameServiceDispatcher)); maintainers.add(new CostReportMaintainer(controller, intervals.costReportMaintainer, controller.serviceRegistry().costReportConsumer())); @@ -70,7 +72,7 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new ArchiveAccessMaintainer(controller, metric, intervals.archiveAccessMaintainer)); maintainers.add(new TenantRoleMaintainer(controller, intervals.tenantRoleMaintainer)); maintainers.add(new ChangeRequestMaintainer(controller, intervals.changeRequestMaintainer)); - maintainers.add(new VcmrMaintainer(controller, intervals.vcmrMaintainer)); + maintainers.add(new VcmrMaintainer(controller, intervals.vcmrMaintainer, metric)); maintainers.add(new CloudTrialExpirer(controller, intervals.defaultInterval)); maintainers.add(new RetriggerMaintainer(controller, intervals.retriggerMaintainer)); maintainers.add(new UserManagementMaintainer(controller, intervals.userManagementMaintainer, controller.serviceRegistry().roleMaintainer())); @@ -80,6 +82,8 @@ public class ControllerMaintenance extends AbstractComponent { public Upgrader upgrader() { return upgrader; } + public OsUpgradeScheduler osUpgradeScheduler() { return osUpgradeScheduler; } + @Override public void deconstruct() { maintainers.forEach(Maintainer::shutdown); @@ -156,7 +160,7 @@ public class ControllerMaintenance extends AbstractComponent { this.containerImageExpirer = duration(12, HOURS); this.hostInfoUpdater = duration(12, HOURS); this.reindexingTriggerer = duration(1, HOURS); - this.endpointCertificateMaintainer = duration(12, HOURS); + this.endpointCertificateMaintainer = duration(1, HOURS); this.trafficFractionUpdater = duration(5, MINUTES); this.archiveUriUpdater = duration(5, MINUTES); this.archiveAccessMaintainer = duration(10, MINUTES); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java index f3256237284..2e2680cd34a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java @@ -7,7 +7,6 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.container.jdisc.secretstore.SecretNotFoundException; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.transaction.Mutex; -import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.Instance; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateDetails; @@ -15,6 +14,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCe import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateProvider; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateRequestMetadata; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; +import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; @@ -23,6 +23,8 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -48,6 +50,7 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer { private final CuratorDb curator; private final SecretStore secretStore; private final EndpointCertificateProvider endpointCertificateProvider; + final Comparator<EligibleJob> oldestFirst = Comparator.comparing(e -> e.deployment.at()); @Inject public EndpointCertificateMaintainer(Controller controller, Duration interval) { @@ -92,11 +95,14 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer { })); } + record EligibleJob(Deployment deployment, ApplicationId applicationId, JobType job) {} /** - * If it's been four days since the cert has been refreshed, re-trigger all prod deployment jobs. + * If it's been four days since the cert has been refreshed, re-trigger prod deployment jobs (one at a time). */ private void deployRefreshedCertificates() { var now = clock.instant(); + var eligibleJobs = new ArrayList<EligibleJob>(); + curator.readAllEndpointCertificateMetadata().forEach((applicationId, endpointCertificateMetadata) -> endpointCertificateMetadata.lastRefreshed().ifPresent(lastRefreshTime -> { Instant refreshTime = Instant.ofEpochSecond(lastRefreshTime); @@ -105,13 +111,19 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer { .ifPresent(instance -> instance.productionDeployments().forEach((zone, deployment) -> { if (deployment.at().isBefore(refreshTime)) { JobType job = JobType.deploymentTo(zone); - deploymentTrigger.reTrigger(applicationId, job, "re-triggered by EndpointCertificateMaintainer"); - log.info("Re-triggering deployment job " + job.jobName() + " for instance " + - applicationId.serializedForm() + " to roll out refreshed endpoint certificate"); + eligibleJobs.add(new EligibleJob(deployment, applicationId, job)); } })); } })); + + eligibleJobs.stream() + .min(oldestFirst) + .ifPresent(e -> { + deploymentTrigger.reTrigger(e.applicationId, e.job, "re-triggered by EndpointCertificateMaintainer"); + log.info("Re-triggering deployment job " + e.job.jobName() + " for instance " + + e.applicationId.serializedForm() + " to roll out refreshed endpoint certificate"); + }); } private OptionalInt latestVersionInSecretStore(EndpointCertificateMetadata originalCertificateMetadata) { @@ -156,8 +168,8 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer { List<EndpointCertificateRequestMetadata> endpointCertificateMetadata = endpointCertificateProvider.listCertificates(); Map<ApplicationId, EndpointCertificateMetadata> storedEndpointCertificateMetadata = curator.readAllEndpointCertificateMetadata(); - List<String> leafRequestIds = storedEndpointCertificateMetadata.values().stream().flatMap(m -> m.leafRequestId().stream()).collect(Collectors.toList()); - List<String> rootRequestIds = storedEndpointCertificateMetadata.values().stream().map(EndpointCertificateMetadata::rootRequestId).collect(Collectors.toList()); + List<String> leafRequestIds = storedEndpointCertificateMetadata.values().stream().flatMap(m -> m.leafRequestId().stream()).toList(); + List<String> rootRequestIds = storedEndpointCertificateMetadata.values().stream().map(EndpointCertificateMetadata::rootRequestId).toList(); for (var providerCertificateMetadata : endpointCertificateMetadata) { if (!rootRequestIds.contains(providerCertificateMetadata.requestId()) && !leafRequestIds.contains(providerCertificateMetadata.requestId())) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java index 82413f21222..b051590ac5a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.NodeSlice; import com.yahoo.config.provision.zone.UpgradePolicy; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.text.Text; @@ -25,6 +26,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.logging.Logger; +import java.util.stream.Collectors; /** * Base class for maintainers that upgrade zone infrastructure. @@ -57,22 +59,22 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten int failures = 0; // Invert zone order if we're downgrading UpgradePolicy policy = target.downgrade() ? upgradePolicy.inverted() : upgradePolicy; - for (Set<ZoneApi> step : policy.steps()) { + for (UpgradePolicy.Step step : policy.steps()) { boolean converged = true; - for (ZoneApi zone : step) { + for (ZoneApi zone : step.zones()) { try { attempts++; - converged &= upgradeAll(target, applications, zone); + converged &= upgradeAll(target, applications, zone, step.nodeSlice()); } catch (UnreachableNodeRepositoryException e) { failures++; converged = false; log.warning(Text.format("%s: Failed to communicate with node repository in %s, continuing with next parallel zone: %s", - this, zone, Exceptions.toMessageString(e))); + this, zone, Exceptions.toMessageString(e))); } catch (Exception e) { failures++; converged = false; log.warning(Text.format("%s: Failed to upgrade zone: %s, continuing with next parallel zone: %s", - this, zone, Exceptions.toMessageString(e))); + this, zone, Exceptions.toMessageString(e))); } } if (!converged) { @@ -83,7 +85,7 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten } /** Returns whether all applications have converged to the target version in zone */ - private boolean upgradeAll(TARGET target, List<SystemApplication> applications, ZoneApi zone) { + private boolean upgradeAll(TARGET target, List<SystemApplication> applications, ZoneApi zone, NodeSlice nodeSlice) { Map<SystemApplication, Set<SystemApplication>> dependenciesByApplication = new HashMap<>(); if (target.downgrade()) { // Invert dependencies when we're downgrading for (var application : applications) { @@ -100,20 +102,17 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten for (var kv : dependenciesByApplication.entrySet()) { SystemApplication application = kv.getKey(); Set<SystemApplication> dependencies = kv.getValue(); - if (convergedOn(target, dependencies, zone)) { + boolean allConverged = dependencies.stream().allMatch(app -> convergedOn(target, app, zone, nodeSlice)); + if (allConverged) { if (changeTargetTo(target, application, zone)) { upgrade(target, application, zone); } - converged &= convergedOn(target, application, zone); + converged &= convergedOn(target, application, zone, nodeSlice); } } return converged; } - private boolean convergedOn(TARGET target, Set<SystemApplication> applications, ZoneApi zone) { - return applications.stream().allMatch(application -> convergedOn(target, application, zone)); - } - /** Returns whether target version for application in zone should be changed */ protected abstract boolean changeTargetTo(TARGET target, SystemApplication application, ZoneApi zone); @@ -121,7 +120,7 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten protected abstract void upgrade(TARGET target, SystemApplication application, ZoneApi zone); /** Returns whether application has converged to target version in zone */ - protected abstract boolean convergedOn(TARGET target, SystemApplication application, ZoneApi zone); + protected abstract boolean convergedOn(TARGET target, SystemApplication application, ZoneApi zone, NodeSlice nodeSlice); /** Returns the version target for the component upgraded by this, if any */ protected abstract Optional<TARGET> target(); @@ -129,19 +128,34 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten /** Returns whether the upgrader should expect given node to upgrade */ protected abstract boolean expectUpgradeOf(Node node, SystemApplication application, ZoneApi zone); - /** Find the minimum value of a version field in a zone by comparing all nodes */ - protected final Optional<Version> minVersion(ZoneApi zone, SystemApplication application, Function<Node, Version> versionField) { + /** Find the highest version used by nodes satisfying nodeSlice in zone. If no such slice exists, the lowest known version is returned */ + protected final Optional<Version> versionOf(NodeSlice nodeSlice, ZoneApi zone, SystemApplication application, Function<Node, Version> versionField) { try { - return controller().serviceRegistry().configServer() - .nodeRepository() - .list(zone.getVirtualId(), NodeFilter.all().applications(application.id())) - .stream() - .filter(node -> expectUpgradeOf(node, application, zone)) - .map(versionField) - .min(Comparator.naturalOrder()); + Map<Version, Long> nodeCountByVersion = controller().serviceRegistry().configServer() + .nodeRepository() + .list(zone.getVirtualId(), NodeFilter.all().applications(application.id())) + .stream() + .filter(node -> expectUpgradeOf(node, application, zone)) + .collect(Collectors.groupingBy(versionField, + Collectors.counting())); + long totalNodes = nodeCountByVersion.values().stream().reduce(Long::sum).orElse(0L); + Set<Version> versionsOfMatchingSlices = new HashSet<>(); + for (var kv : nodeCountByVersion.entrySet()) { + long nodesOnVersion = kv.getValue(); + if (nodeSlice.satisfiedBy(nodesOnVersion, totalNodes)) { + versionsOfMatchingSlices.add(kv.getKey()); + } + } + if (!versionsOfMatchingSlices.isEmpty()) { + // Choose the highest version in case we have several matching slices + return versionsOfMatchingSlices.stream().max(Comparator.naturalOrder()); + } + // No matching slices found, fall back to the lowest known version + return nodeCountByVersion.keySet().stream().min(Comparator.naturalOrder()); } catch (Exception e) { throw new UnreachableNodeRepositoryException(Text.format("Failed to get version for %s in %s: %s", - application.id(), zone, Exceptions.toMessageString(e))); + application.id(), zone, + Exceptions.toMessageString(e))); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java index 94ec4129744..cd48d6839f3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunner.java @@ -8,7 +8,6 @@ import com.yahoo.vespa.hosted.controller.deployment.InternalStepRunner; import com.yahoo.vespa.hosted.controller.deployment.JobController; import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.deployment.Step; -import com.yahoo.vespa.hosted.controller.deployment.StepInfo; import com.yahoo.vespa.hosted.controller.deployment.StepRunner; import java.time.Duration; @@ -75,18 +74,26 @@ public class JobRunner extends ControllerMaintainer { } } - /** Advances each of the ready steps for the given run, or marks it as finished, and stashes it. Public for testing. */ public void advance(Run run) { - if ( ! run.hasFailed() - && controller().clock().instant().isAfter(run.sleepUntil().orElse(run.start()).plus(jobTimeout))) - executors.execute(() -> { - jobs.abort(run.id(), "job timeout of " + jobTimeout + " reached"); - advance(jobs.run(run.id())); - }); - else if (run.readySteps().isEmpty()) - executors.execute(() -> finish(run.id())); - else if (run.hasFailed() || run.sleepUntil().map(sleepUntil -> ! sleepUntil.isAfter(controller().clock().instant())).orElse(true)) - run.readySteps().forEach(step -> executors.execute(() -> advance(run.id(), step))); + advance(run.id()); + } + + /** Advances each of the ready steps for the given run, or marks it as finished, and stashes it. Public for testing. */ + public void advance(RunId id) { + jobs.locked(id, run -> { + if ( ! run.hasFailed() + && controller().clock().instant().isAfter(run.sleepUntil().orElse(run.start()).plus(jobTimeout))) + executors.execute(() -> { + jobs.abort(run.id(), "job timeout of " + jobTimeout + " reached"); + advance(run.id()); + }); + else if (run.readySteps().isEmpty()) + executors.execute(() -> finish(run.id())); + else if (run.hasFailed() || run.sleepUntil().map(sleepUntil -> ! sleepUntil.isAfter(controller().clock().instant())).orElse(true)) + run.readySteps().forEach(step -> executors.execute(() -> advance(run.id(), step))); + + return null; + }); } private void finish(RunId id) { @@ -108,23 +115,24 @@ public class JobRunner extends ControllerMaintainer { try { AtomicBoolean changed = new AtomicBoolean(false); jobs.locked(id.application(), id.type(), step, lockedStep -> { - jobs.locked(id, run -> run); // Memory visibility. - jobs.active(id).ifPresent(run -> { // The run may have become inactive, so we bail out. + jobs.locked(id, run -> { if ( ! run.readySteps().contains(step)) { changed.set(true); - return; // Someone may have updated the run status, making this step obsolete, so we bail out. + return run; // Someone may have updated the run status, making this step obsolete, so we bail out. } - StepInfo stepInfo = run.stepInfo(lockedStep.get()).orElseThrow(); - if (stepInfo.startTime().isEmpty()) { - jobs.setStartTimestamp(run.id(), controller().clock().instant(), lockedStep); - } + if (run.stepInfo(lockedStep.get()).orElseThrow().startTime().isEmpty()) + run = run.with(controller().clock().instant(), lockedStep); - runner.run(lockedStep, run.id()).ifPresent(status -> { - jobs.update(run.id(), status, lockedStep); + return run; + }); + + if ( ! changed.get()) { + runner.run(lockedStep, id).ifPresent(status -> { + jobs.update(id, status, lockedStep); changed.set(true); }); - }); + } }); if (changed.get()) jobs.active(id).ifPresent(this::advance); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainer.java index eadbdf74c3c..519b1001be4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainer.java @@ -51,14 +51,15 @@ public class MeteringMonitorMaintainer extends ControllerMaintainer { return controller().applications().asList() .stream() .flatMap(app -> app.instances().values().stream()) - .flatMap(this::instancesToDeployments) + .flatMap(this::toProdDeployments) .collect(Collectors.toSet()); } - private Stream<DeploymentId> instancesToDeployments(Instance instance) { + private Stream<DeploymentId> toProdDeployments(Instance instance) { return instance.deployments() .keySet() .stream() + .filter(deployment -> deployment.environment().isProduction()) .map(deployment -> new DeploymentId(instance.id(), deployment)); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java index 3bd1c7bb358..ddcfef23d86 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java @@ -9,10 +9,14 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepo import com.yahoo.vespa.hosted.controller.api.integration.deployment.OsRelease; import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget; +import java.time.DayOfWeek; import java.time.Duration; import java.time.Instant; +import java.time.LocalDate; import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.Objects; import java.util.Optional; @@ -30,23 +34,24 @@ public class OsUpgradeScheduler extends ControllerMaintainer { @Override protected double maintain() { Instant now = controller().clock().instant(); - if (!canTriggerAt(now)) return 1.0; for (var cloud : controller().clouds()) { - Release release = releaseIn(cloud); - upgradeTo(release, cloud, now); + Optional<Change> change = changeIn(cloud); + if (change.isEmpty()) continue; + if (!change.get().scheduleAt(now)) continue; + controller().upgradeOsIn(cloud, change.get().version(), change.get().upgradeBudget(), false); } return 1.0; } - /** Upgrade to given release in cloud */ - private void upgradeTo(Release release, CloudName cloud, Instant now) { + /** Returns the wanted change for given cloud, if any */ + public Optional<Change> changeIn(CloudName cloud) { Optional<OsVersionTarget> currentTarget = controller().osVersionTarget(cloud); - if (currentTarget.isEmpty()) return; - if (upgradingToNewMajor(cloud)) return; // Skip further upgrades until major version upgrade is complete + if (currentTarget.isEmpty()) return Optional.empty(); + if (upgradingToNewMajor(cloud)) return Optional.empty(); // Skip further upgrades until major version upgrade is complete - Version version = release.version(currentTarget.get(), now); - if (!version.isAfter(currentTarget.get().osVersion().version())) return; - controller().upgradeOsIn(cloud, version, release.upgradeBudget(), false); + Release release = releaseIn(cloud); + Instant now = controller().clock().instant(); + return release.change(currentTarget.get().version(), now); } private boolean upgradingToNewMajor(CloudName cloud) { @@ -56,54 +61,69 @@ public class OsUpgradeScheduler extends ControllerMaintainer { .count() > 1; } - private boolean canTriggerAt(Instant instant) { - int hourOfDay = instant.atZone(ZoneOffset.UTC).getHour(); - int dayOfWeek = instant.atZone(ZoneOffset.UTC).getDayOfWeek().getValue(); - // Upgrade can only be scheduled between 07:00 (02:00 in CD systems) and 12:59 UTC, Monday-Thursday - int startHour = controller().system().isCd() ? 2 : 7; - return hourOfDay >= startHour && hourOfDay <= 12 && dayOfWeek < 5; - } - private Release releaseIn(CloudName cloud) { boolean useTaggedRelease = controller().zoneRegistry().zones().all().reprovisionToUpgradeOs().in(cloud) - .zones().isEmpty(); + .zones().isEmpty(); if (useTaggedRelease) { return new TaggedRelease(controller().system(), controller().serviceRegistry().artifactRepository()); } return new CalendarVersionedRelease(controller().system()); } - private interface Release { + private static boolean canTriggerAt(Instant instant, boolean isCd) { + ZonedDateTime dateTime = instant.atZone(ZoneOffset.UTC); + int hourOfDay = dateTime.getHour(); + int dayOfWeek = dateTime.getDayOfWeek().getValue(); + // Upgrade can only be scheduled between 07:00 (02:00 in CD systems) and 12:59 UTC, Monday-Thursday + int startHour = isCd ? 2 : 7; + return hourOfDay >= startHour && hourOfDay <= 12 && dayOfWeek < 5; + } - /** The version number of this */ - Version version(OsVersionTarget currentTarget, Instant now); + /** Returns the earliest time an upgrade can be scheduled on the day of instant, in given system */ + private static Instant schedulingInstant(Instant instant, SystemName system) { + instant = instant.truncatedTo(ChronoUnit.DAYS); + while (!canTriggerAt(instant, system.isCd())) { + instant = instant.plus(Duration.ofHours(1)); + } + return instant; + } - /** The budget to use when upgrading to this */ - Duration upgradeBudget(); + private interface Release { + + /** The pending change for this release at given instant, if any */ + Optional<Change> change(Version currentVersion, Instant instant); } - /** OS release based on a tag */ - private static class TaggedRelease implements Release { + /** OS version change, its budget and the earliest time it can be scheduled */ + public record Change(Version version, Duration upgradeBudget, Instant scheduleAt) { - private final SystemName system; - private final ArtifactRepository artifactRepository; + public Change { + Objects.requireNonNull(version); + Objects.requireNonNull(upgradeBudget); + Objects.requireNonNull(scheduleAt); + } - private TaggedRelease(SystemName system, ArtifactRepository artifactRepository) { - this.system = Objects.requireNonNull(system); - this.artifactRepository = Objects.requireNonNull(artifactRepository); + /** Returns whether this can be scheduled at given instant */ + public boolean scheduleAt(Instant instant) { + return !instant.isBefore(scheduleAt); } - @Override - public Version version(OsVersionTarget currentTarget, Instant now) { - OsRelease release = artifactRepository.osRelease(currentTarget.osVersion().version().getMajor(), tag()); - boolean cooldownPassed = !release.taggedAt().plus(cooldown()).isAfter(now); - return cooldownPassed ? release.version() : currentTarget.osVersion().version(); + } + + /** OS release based on a tag */ + private record TaggedRelease(SystemName system, ArtifactRepository artifactRepository) implements Release { + + public TaggedRelease { + Objects.requireNonNull(system); + Objects.requireNonNull(artifactRepository); } - @Override - public Duration upgradeBudget() { - return Duration.ZERO; // Upgrades to tagged releases happen in-place so no budget is required + public Optional<Change> change(Version currentVersion, Instant instant) { + OsRelease release = artifactRepository.osRelease(currentVersion.getMajor(), tag()); + if (!release.version().isAfter(currentVersion)) return Optional.empty(); + Instant scheduleAt = schedulingInstant(release.taggedAt().plus(cooldown()), system); + return Optional.of(new Change(release.version(), Duration.ZERO, scheduleAt)); } /** Returns the release tag tracked by this system */ @@ -119,48 +139,65 @@ public class OsUpgradeScheduler extends ControllerMaintainer { } /** OS release based on calendar-versioning */ - private static class CalendarVersionedRelease implements Release { + record CalendarVersionedRelease(SystemName system) implements Release { - /** The time to wait before scheduling upgrade to next version */ - private static final Duration SCHEDULING_INTERVAL = Duration.ofDays(45); + /** A fixed point in time which the release schedule is calculated from */ + private static final Instant START_OF_SCHEDULE = LocalDate.of(2022, 1, 1) + .atStartOfDay() + .toInstant(ZoneOffset.UTC); - /** - * The interval at which new versions become available. We use this to avoid scheduling upgrades to a version - * that has not been released yet. Example: Version N is the latest one and target is set to N+1. If N+1 does - * not exist the zone will not converge until N+1 has been released and we may end up triggering multiple - * rounds of upgrades. - */ - private static final Duration AVAILABILITY_INTERVAL = Duration.ofDays(7); + /** The time that should elapse between versions */ + private static final Duration SCHEDULING_STEP = Duration.ofDays(60); - private static final DateTimeFormatter CALENDAR_VERSION_PATTERN = DateTimeFormatter.ofPattern("yyyyMMdd"); + /** The day of week new releases are published */ + private static final DayOfWeek RELEASE_DAY = DayOfWeek.MONDAY; - private final SystemName system; + private static final DateTimeFormatter CALENDAR_VERSION_PATTERN = DateTimeFormatter.ofPattern("yyyyMMdd"); - public CalendarVersionedRelease(SystemName system) { - this.system = Objects.requireNonNull(system); + public CalendarVersionedRelease { + Objects.requireNonNull(system); } @Override - public Version version(OsVersionTarget currentTarget, Instant now) { - Instant scheduledAt = currentTarget.scheduledAt(); - Version currentVersion = currentTarget.osVersion().version(); - if (scheduledAt.isBefore(now.minus(SCHEDULING_INTERVAL))) { - String calendarVersion = now.minus(AVAILABILITY_INTERVAL) - .atZone(ZoneOffset.UTC) - .format(CALENDAR_VERSION_PATTERN); - return new Version(currentVersion.getMajor(), - currentVersion.getMinor(), - currentVersion.getMicro(), - calendarVersion); + public Optional<Change> change(Version currentVersion, Instant instant) { + Version wantedVersion = asVersion(dateOfWantedVersion(instant), currentVersion); + while (!wantedVersion.isAfter(currentVersion)) { + wantedVersion = asVersion(dateOfWantedVersion(instant), currentVersion); + instant = instant.plus(Duration.ofDays(1)); } - return currentVersion; // New version should not be scheduled yet + return Optional.of(new Change(wantedVersion, upgradeBudget(), schedulingInstant(instant, system))); } - @Override - public Duration upgradeBudget() { + private Duration upgradeBudget() { return system.isCd() ? Duration.ZERO : Duration.ofDays(14); } + /** + * Calculate the date of the wanted version relative to now. A given zone will choose the oldest release + * available which is not older than this date. + */ + static LocalDate dateOfWantedVersion(Instant now) { + Instant candidate = START_OF_SCHEDULE; + while (!candidate.plus(SCHEDULING_STEP).isAfter(now)) { + candidate = candidate.plus(SCHEDULING_STEP); + } + LocalDate date = LocalDate.ofInstant(candidate, ZoneOffset.UTC); + return releaseDayOf(date); + } + + private static LocalDate releaseDayOf(LocalDate date) { + int releaseDayDelta = RELEASE_DAY.getValue() - date.getDayOfWeek().getValue(); + return date.plusDays(releaseDayDelta); + } + + private static Version asVersion(LocalDate dateOfVersion, Version currentVersion) { + String calendarVersion = dateOfVersion.format(CALENDAR_VERSION_PATTERN); + return new Version(currentVersion.getMajor(), + currentVersion.getMinor(), + currentVersion.getMicro(), + calendarVersion); + } + } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java index fa64a2677f4..f4dcf7f6088 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgrader.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; +import com.yahoo.config.provision.zone.NodeSlice; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.text.Text; import com.yahoo.vespa.hosted.controller.Controller; @@ -54,15 +55,16 @@ public class OsUpgrader extends InfrastructureUpgrader<OsVersionTarget> { } @Override - protected boolean convergedOn(OsVersionTarget target, SystemApplication application, ZoneApi zone) { - return !currentVersion(zone, application, target.osVersion().version()).isBefore(target.osVersion().version()); + protected boolean convergedOn(OsVersionTarget target, SystemApplication application, ZoneApi zone, NodeSlice nodeSlice) { + Version currentVersion = versionOf(nodeSlice, zone, application, Node::currentOsVersion).orElse(target.osVersion().version()); + return !currentVersion.isBefore(target.osVersion().version()); } @Override protected boolean expectUpgradeOf(Node node, SystemApplication application, ZoneApi zone) { return cloud.equals(zone.getCloudName()) && // Cloud is managed by this upgrader application.shouldUpgradeOs() && // Application should upgrade in this cloud - canUpgrade(node); // Node is in an upgradable state + canUpgrade(node, false); } @Override @@ -83,29 +85,23 @@ public class OsUpgrader extends InfrastructureUpgrader<OsVersionTarget> { .orElse(true); } - private Version currentVersion(ZoneApi zone, SystemApplication application, Version defaultVersion) { - return minVersion(zone, application, Node::currentOsVersion).orElse(defaultVersion); - } - /** Returns the available upgrade budget for given zone */ private Duration zoneBudgetOf(Duration totalBudget, ZoneApi zone) { if (!spendBudgetOn(zone)) return Duration.ZERO; long consecutiveZones = upgradePolicy.steps().stream() - .filter(parallelZones -> parallelZones.stream().anyMatch(this::spendBudgetOn)) + .filter(step -> step.zones().stream().anyMatch(this::spendBudgetOn)) .count(); return totalBudget.dividedBy(consecutiveZones); } /** Returns whether to spend upgrade budget on given zone */ private boolean spendBudgetOn(ZoneApi zone) { - if (!zone.getEnvironment().isProduction()) return false; - if (controller().zoneRegistry().systemZone().getVirtualId().equals(zone.getVirtualId())) return false; // Controller zone - return true; + return !controller().zoneRegistry().systemZone().getVirtualId().equals(zone.getVirtualId()); // Do not spend budget on controller zone } - /** Returns whether node is in a state where it can be upgraded */ - public static boolean canUpgrade(Node node) { - return upgradableNodeStates.contains(node.state()); + /** Returns whether node currently allows upgrades */ + public static boolean canUpgrade(Node node, boolean includeDeferring) { + return (includeDeferring || !node.deferOsUpgrade()) && upgradableNodeStates.contains(node.state()); } private static String name(CloudName cloud) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java index 892ad669e4b..205fb7e0e79 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java @@ -100,7 +100,7 @@ public class ResourceMeterMaintainer extends ControllerMaintainer { } if (systemName.isPublic()) reportResourceSnapshots(resourceSnapshots); - if (systemName.isPublic() && systemName.isCd()) reportAllScalingEvents(); + if (systemName.isPublic()) reportAllScalingEvents(); updateDeploymentCost(resourceSnapshots); return 1.0; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java index 8d5851be62f..8e74ef9a983 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgrader.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; +import com.yahoo.config.provision.zone.NodeSlice; import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.text.Text; @@ -39,12 +40,12 @@ public class SystemUpgrader extends InfrastructureUpgrader<VespaVersionTarget> { } @Override - protected boolean convergedOn(VespaVersionTarget target, SystemApplication application, ZoneApi zone) { - Optional<Version> minVersion = minVersion(zone, application, Node::currentVersion); + protected boolean convergedOn(VespaVersionTarget target, SystemApplication application, ZoneApi zone, NodeSlice nodeSlice) { + Optional<Version> currentVersion = versionOf(nodeSlice, zone, application, Node::currentVersion); // Skip application convergence check if there are no nodes belonging to the application in the zone - if (minVersion.isEmpty()) return true; + if (currentVersion.isEmpty()) return true; - return minVersion.get().equals(target.version()) && + return currentVersion.get().equals(target.version()) && application.configConvergedIn(zone.getId(), controller(), Optional.of(target.version())); } @@ -79,10 +80,9 @@ public class SystemUpgrader extends InfrastructureUpgrader<VespaVersionTarget> { // the wanted version of each node. boolean zoneHasSharedRouting = controller().zoneRegistry().routingMethods(zone.getId()).stream() .anyMatch(RoutingMethod::isShared); - return minVersion(zone, application, Node::wantedVersion) + return versionOf(NodeSlice.ALL, zone, application, Node::wantedVersion) .map(wantedVersion -> !wantedVersion.equals(target.version())) .orElse(zoneHasSharedRouting); // Always upgrade if zone uses shared routing, but has no nodes allocated yet - } return controller().serviceRegistry().configServer().nodeRepository() .targetVersionsOf(zone.getId()) diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java index dad836ca2de..820c67f2d44 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java @@ -33,21 +33,15 @@ public class TenantRoleMaintainer extends ControllerMaintainer { .map(Tenant::name) .collect(Collectors.toList()); roleService.maintainRoles(tenantsWithRoles); + + var deletedTenants = controller().tenants().asList(true).stream() + .filter(tenant -> tenant.type() == Tenant.Type.deleted) + .map(Tenant::name) + .toList(); + roleService.cleanupRoles(deletedTenants); + return 1.0; } - private boolean hasProductionDeployment(TenantName tenant) { - return controller().applications().asList(tenant).stream() - .map(Application::productionInstances) - .anyMatch(Predicate.not(Map::isEmpty)); - } - private boolean hasPerfDeployment(TenantName tenant) { - List<ZoneId> perfZones = controller().zoneRegistry().zones().controllerUpgraded().in(Environment.perf).ids(); - return controller().applications().asList(tenant).stream() - .map(Application::instances) - .flatMap(instances -> instances.values().stream()) - .flatMap(instance -> instance.deployments().values().stream()) - .anyMatch(x -> perfZones.contains(x.zone())); - } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java index d654f63fff2..1932dc65657 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java @@ -5,24 +5,27 @@ import com.yahoo.component.Version; import com.yahoo.config.application.api.DeploymentSpec.UpgradePolicy; import com.yahoo.config.provision.ApplicationId; import com.yahoo.transaction.Mutex; -import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.application.ApplicationList; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.InstanceList; +import com.yahoo.vespa.hosted.controller.deployment.DeploymentStatusList; +import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger; +import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; import com.yahoo.vespa.hosted.controller.versions.VespaVersion.Confidence; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.OptionalInt; import java.util.Random; +import java.util.Set; import java.util.function.UnaryOperator; import java.util.logging.Level; import java.util.logging.Logger; @@ -58,18 +61,22 @@ public class Upgrader extends ControllerMaintainer { cancelBrokenUpgrades(versionStatus); OptionalInt targetMajorVersion = targetMajorVersion(); - InstanceList instances = instances(versionStatus); + DeploymentStatusList deploymentStatuses = deploymentStatuses(versionStatus); for (UpgradePolicy policy : UpgradePolicy.values()) - updateTargets(versionStatus, instances, policy, targetMajorVersion); + updateTargets(versionStatus, deploymentStatuses, policy, targetMajorVersion); return 1.0; } + private DeploymentStatusList deploymentStatuses(VersionStatus versionStatus) { + return controller().jobController().deploymentStatuses(ApplicationList.from(controller().applications().readable()) + .withProjectId(), + versionStatus); + } + /** Returns a list of all production application instances, except those which are pinned, which we should not manipulate here. */ - private InstanceList instances(VersionStatus versionStatus) { - return InstanceList.from(controller().jobController().deploymentStatuses(ApplicationList.from(controller().applications().readable()) - .withProjectId(), - versionStatus)) + private InstanceList instances(DeploymentStatusList deploymentStatuses) { + return InstanceList.from(deploymentStatuses) .withDeclaredJobs() .shuffle(random) .byIncreasingDeployedVersion() @@ -78,7 +85,7 @@ public class Upgrader extends ControllerMaintainer { private void cancelBrokenUpgrades(VersionStatus versionStatus) { // Cancel upgrades to broken targets (let other ongoing upgrades complete to avoid starvation) - InstanceList instances = instances(controller().readVersionStatus()); + InstanceList instances = instances(deploymentStatuses(controller().readVersionStatus())); for (VespaVersion version : versionStatus.versions()) { if (version.confidence() == Confidence.broken) cancelUpgradesOf(instances.upgradingTo(version.versionNumber()).not().with(UpgradePolicy.canary), @@ -86,8 +93,12 @@ public class Upgrader extends ControllerMaintainer { } } - private void updateTargets(VersionStatus versionStatus, InstanceList instances, UpgradePolicy policy, OptionalInt targetMajorVersion) { + private void updateTargets(VersionStatus versionStatus, DeploymentStatusList deploymentStatuses, UpgradePolicy policy, OptionalInt targetMajorVersion) { + InstanceList instances = instances(deploymentStatuses); InstanceList remaining = instances.with(policy); + Instant failureThreshold = controller().clock().instant().minus(DeploymentTrigger.maxFailingRevisionTime); + Set<ApplicationId> failingRevision = InstanceList.from(deploymentStatuses.failingApplicationChangeSince(failureThreshold)).asSet(); + List<Version> targetAndNewer = new ArrayList<>(); UnaryOperator<InstanceList> cancellationCriterion = policy == UpgradePolicy.canary ? i -> i.not().upgradingTo(targetAndNewer) : i -> i.failing() @@ -103,13 +114,16 @@ public class Upgrader extends ControllerMaintainer { // Prefer the newest target for each instance. remaining = remaining.not().matching(eligible.asList()::contains) .not().hasCompleted(Change.of(version)); - for (ApplicationId id : outdated.and(eligible.not().upgrading()).not().changingRevision()) + for (ApplicationId id : outdated.and(eligible.not().upgrading())) targets.put(id, version); } int numberToUpgrade = policy == UpgradePolicy.canary ? instances.size() : numberOfApplicationsToUpgrade(); for (ApplicationId id : instances.matching(targets.keySet()::contains).first(numberToUpgrade)) { log.log(Level.INFO, "Triggering upgrade to " + targets.get(id) + " for " + id); + if (failingRevision.contains(id)) + controller().applications().deploymentTrigger().cancelChange(id, ChangesToCancel.APPLICATION); + controller().applications().deploymentTrigger().triggerChange(id, Change.of(targets.get(id))); } } @@ -167,7 +181,7 @@ public class Upgrader extends ControllerMaintainer { } /** Sets the default target major version. Set to empty to determine target version normally (by confidence) */ - public void setTargetMajorVersion(Optional<Integer> targetMajorVersion) { + public void setTargetMajorVersion(OptionalInt targetMajorVersion) { controller().applications().setTargetMajorVersion(targetMajorVersion); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java index 551f803f368..daba7e74f34 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java @@ -5,6 +5,7 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.jdisc.Metric; import com.yahoo.text.Text; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; @@ -46,26 +47,28 @@ public class VcmrMaintainer extends ControllerMaintainer { private static final Logger LOG = Logger.getLogger(VcmrMaintainer.class.getName()); private static final int DAYS_TO_RETIRE = 2; private static final Duration ALLOWED_POSTPONEMENT_TIME = Duration.ofDays(7); + protected static final String TRACKED_CMRS_METRIC = "cmr.tracked"; private final CuratorDb curator; private final NodeRepository nodeRepository; private final ChangeRequestClient changeRequestClient; private final SystemName system; + private final Metric metric; - public VcmrMaintainer(Controller controller, Duration interval) { + public VcmrMaintainer(Controller controller, Duration interval, Metric metric) { super(controller, interval, null, SystemName.allOf(Predicate.not(SystemName::isPublic))); this.curator = controller.curator(); this.nodeRepository = controller.serviceRegistry().configServer().nodeRepository(); this.changeRequestClient = controller.serviceRegistry().changeRequestClient(); this.system = controller.system(); + this.metric = metric; } @Override protected double maintain() { var changeRequests = curator.readChangeRequests() .stream() - .filter(shouldUpdate()) - .collect(Collectors.toList()); + .filter(shouldUpdate()).toList(); var nodesByZone = nodesByZone(); @@ -86,6 +89,7 @@ public class VcmrMaintainer extends ControllerMaintainer { }); } }); + updateMetrics(); return 1.0; } @@ -357,4 +361,15 @@ public class VcmrMaintainer extends ControllerMaintainer { return time; } + private void updateMetrics() { + var cmrsByStatus = curator.readChangeRequests() + .stream() + .collect(Collectors.groupingBy(VespaChangeRequest::getStatus)); + + for (var status : Status.values()) { + var count = cmrsByStatus.getOrDefault(status, List.of()).size(); + metric.set(TRACKED_CMRS_METRIC, count, metric.createContext(Map.of("status", status.name()))); + } + } + } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java index 49c819548fe..f2c9d55b2a2 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java @@ -29,6 +29,16 @@ import java.util.stream.Collectors; * @author enygaard */ public class Notifier { + private static final String header = """ + <div style="background: #00598c; height: 55px; width: 100%"> + <img + src="https://vespa.ai/assets/vespa-logo.png" + style="width: auto; height: 34px; margin: 10px" + /> + </div> + <br> + """; + private final CuratorDb curatorDb; private final Mailer mailer; private final FlagSource flagSource; @@ -111,14 +121,15 @@ public class Notifier { public Mail mailOf(FormattedNotification content, Collection<String> recipients) { var notification = content.notification(); var subject = Text.format("[%s] %s Vespa Notification for %s", notification.level().toString().toUpperCase(), content.prettyType(), applicationIdSource(notification.source())); - var body = new StringBuilder(); - body.append(content.messagePrefix()).append("\n") + String body = new StringBuilder() + .append(content.messagePrefix()).append("\n") .append(notification.messages().stream().map(m -> " * " + m).collect(Collectors.joining("\n"))).append("\n") .append("\n") .append("Vespa Console link:\n") - .append(content.uri().toString()); - var html = new StringBuilder(); - html.append(content.messagePrefix()).append("<br>\n") + .append(content.uri().toString()).toString(); + String html = new StringBuilder() + .append(header) + .append(content.messagePrefix()).append("<br>\n") .append("<ul>\n") .append(notification.messages().stream() .map(Notifier::linkify) @@ -126,8 +137,8 @@ public class Notifier { .collect(Collectors.joining("<br>\n"))) .append("</ul>\n") .append("<br>\n") - .append("<a href=\"" + content.uri() + "\">Vespa Console</a>"); - return new Mail(recipients, subject, body.toString(), html.toString()); + .append("<a href=\"" + content.uri() + "\">Vespa Console</a>").toString(); + return new Mail(recipients, subject, body, html); } @VisibleForTesting diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java index 9721026c628..ecb9db8195f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/BufferedLogStore.java @@ -49,7 +49,7 @@ public class BufferedLogStore { } /** Appends to the log of the given, active run, reassigning IDs as counted here, and converting to Vespa log levels. */ - public void append(ApplicationId id, JobType type, Step step, List<LogEntry> entries) { + public void append(ApplicationId id, JobType type, Step step, List<LogEntry> entries, boolean forceLog) { if (entries.isEmpty()) return; @@ -58,7 +58,7 @@ public class BufferedLogStore { long lastEntryId = buffer.readLastLogEntryId(id, type).orElse(-1L); long lastChunkId = buffer.getLogChunkIds(id, type).max().orElse(0); long numberOfChunks = Math.max(1, buffer.getLogChunkIds(id, type).count()); - if (numberOfChunks > maxLogSize / chunkSize) + if (numberOfChunks > maxLogSize / chunkSize && ! forceLog) return; // Max size exceeded — store no more. byte[] emptyChunk = "[]".getBytes(); @@ -72,8 +72,12 @@ public class BufferedLogStore { buffer.writeLastLogEntryId(id, type, lastEntryId); buffer.writeLog(id, type, lastChunkId, logSerializer.toJson(log)); lastChunkId = lastEntryId + 1; - if (++numberOfChunks > maxLogSize / chunkSize) { - log = Map.of(step, List.of(new LogEntry(++lastEntryId, entry.at(), LogEntry.Type.warning, "Max log size of " + (maxLogSize >> 20) + "Mb exceeded; further entries are discarded."))); + if (++numberOfChunks > maxLogSize / chunkSize && ! forceLog) { + log = Map.of(step, List.of(new LogEntry(++lastEntryId, + entry.at(), + LogEntry.Type.warning, + "Max log size of " + (maxLogSize >> 20) + + "Mb exceeded; further user entries are discarded."))); break; } log = new HashMap<>(); 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 f02f49e7114..54e98877ba3 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 @@ -1,9 +1,9 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.persistence; -import com.yahoo.component.annotation.Inject; import com.yahoo.collections.Pair; import com.yahoo.component.Version; +import com.yahoo.component.annotation.Inject; import com.yahoo.concurrent.UncheckedTimeoutException; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostName; @@ -41,6 +41,7 @@ import com.yahoo.vespa.hosted.controller.versions.OsVersionStatus; import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget; import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; + import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; @@ -53,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.Optional; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeoutException; @@ -271,9 +273,9 @@ public class CuratorDb { return read(targetMajorVersionPath(), ByteBuffer::wrap).map(ByteBuffer::getInt); } - public void writeTargetMajorVersion(Optional<Integer> targetMajorVersion) { + public void writeTargetMajorVersion(OptionalInt targetMajorVersion) { if (targetMajorVersion.isPresent()) - curator.set(targetMajorVersionPath(), ByteBuffer.allocate(Integer.BYTES).putInt(targetMajorVersion.get()).array()); + curator.set(targetMajorVersionPath(), ByteBuffer.allocate(Integer.BYTES).putInt(targetMajorVersion.getAsInt()).array()); else curator.delete(targetMajorVersionPath()); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java index e7cf0c34511..e91fbe8b1b7 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java @@ -81,6 +81,7 @@ public class TenantSerializer { private static final String archiveAccessField = "archiveAccess"; private static final String awsArchiveAccessRoleField = "awsArchiveAccessRole"; private static final String gcpArchiveAccessMemberField = "gcpArchiveAccessMember"; + private static final String invalidateUserSessionsBeforeField = "invalidateUserSessionsBefore"; private static final String awsIdField = "awsId"; private static final String roleField = "role"; @@ -123,6 +124,7 @@ public class TenantSerializer { toSlime(tenant.info(), root); toSlime(tenant.tenantSecretStores(), root); toSlime(tenant.archiveAccess(), root); + tenant.invalidateUserSessionsBefore().ifPresent(instant -> root.setLong(invalidateUserSessionsBeforeField, instant.toEpochMilli())); } private void toSlime(ArchiveAccess archiveAccess, Cursor root) { @@ -187,7 +189,8 @@ public class TenantSerializer { TenantInfo info = tenantInfoFromSlime(tenantObject.field(tenantInfoField)); List<TenantSecretStore> tenantSecretStores = secretStoresFromSlime(tenantObject.field(secretStoresField)); ArchiveAccess archiveAccess = archiveAccessFromSlime(tenantObject); - return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess); + Optional<Instant> invalidateUserSessionsBefore = SlimeUtils.optionalInstant(tenantObject.field(invalidateUserSessionsBeforeField)); + return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores, archiveAccess, invalidateUserSessionsBefore); } private DeletedTenant deletedTenantFrom(Inspector tenantObject) { 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 670cb775c69..8d2fac84bc0 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 @@ -10,8 +10,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; -import com.yahoo.component.annotation.Inject; import com.yahoo.component.Version; +import com.yahoo.component.annotation.Inject; import com.yahoo.config.application.api.DeploymentInstanceSpec; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.provision.ApplicationId; @@ -75,6 +75,7 @@ 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.noderepository.RestartFilter; import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.RoleDefinition; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; @@ -137,8 +138,6 @@ import java.security.PublicKey; import java.time.DayOfWeek; import java.time.Duration; import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Base64; @@ -955,19 +954,24 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { private HttpResponse applicationPackage(String tenantName, String applicationName, HttpRequest request) { 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 { + final long build; + String requestedBuild = request.getProperty("build"); + if (requestedBuild != null) { + if (requestedBuild.equals("latestDeployed")) { + build = controller.applications().requireApplication(tenantAndApplication).latestDeployedRevision() + .map(RevisionId::number) + .orElseThrow(() -> new NotExistsException("no application package has been deployed in production for " + tenantAndApplication)); + } else { + 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)); + .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"); @@ -1470,6 +1474,15 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { private HttpResponse trigger(ApplicationId id, JobType type, HttpRequest request) { + // JobType.fromJobName doesn't properly initiate test jobs. Triggering these without context isn't _really_ + // necessary, but triggering a test in the default cloud is better than failing with a weird error. + ZoneRegistry zones = controller.zoneRegistry(); + type = switch (type.environment()) { + case test -> JobType.systemTest(zones, zones.systemZone().getCloudName()); + case staging -> JobType.stagingTest(zones, zones.systemZone().getCloudName()); + default -> type; + }; + Inspector requestObject = toSlime(request.getData()).get(); boolean requireTests = ! requestObject.field("skipTests").asBool(); boolean reTrigger = requestObject.field("reTrigger").asBool(); @@ -1831,7 +1844,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { response.setString("status", "complete"); else if (stepStatus.readyAt(instance.change()).map(controller.clock().instant()::isBefore).orElse(true)) response.setString("status", "pending"); - else response.setString("status", "running"); + else + response.setString("status", "running"); }); } else { var deploymentRun = controller.jobController().last(deploymentId.applicationId(), JobType.deploymentTo(deploymentId.zoneId())); @@ -2544,12 +2558,11 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { cloudTenant.tenantSecretStores()); try { - var tenantQuota = controller.serviceRegistry().billingController().getQuota(tenant.name()); var usedQuota = applications.stream() .map(Application::quotaUsage) .reduce(QuotaUsage.none, QuotaUsage::add); - toSlime(tenantQuota, usedQuota, object.setObject("quota")); + toSlime(object.setObject("quota"), usedQuota); } catch (Exception e) { log.warning(String.format("Failed to get quota for tenant %s: %s", tenant.name(), Exceptions.toMessageString(e))); } @@ -2592,15 +2605,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { archiveAccess.gcpMember().ifPresent(member -> object.setString("gcpMember", member)); } - private void toSlime(Quota quota, QuotaUsage usage, Cursor object) { - quota.budget().ifPresentOrElse( - budget -> object.setDouble("budget", budget.doubleValue()), - () -> object.setNix("budget") - ); + private void toSlime(Cursor object, QuotaUsage usage) { object.setDouble("budgetUsed", usage.rate()); - - // TODO: Retire when we no longer use maxClusterSize as a meaningful limit - quota.maxClusterSize().ifPresent(maxClusterSize -> object.setLong("clusterSize", maxClusterSize)); } private void toSlime(ClusterResources resources, Cursor object) { 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 e28bf89e734..25953c16bf0 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 @@ -115,6 +115,7 @@ class JobControllerApiHandlerHelper { Run run = jobController.run(runId); detailsObject.setBool("active", ! run.hasEnded()); detailsObject.setString("status", nameOf(run.status())); + run.reason().ifPresent(reason -> detailsObject.setString("reason", reason)); try { jobController.updateTestLog(runId); jobController.updateVespaLog(runId); @@ -325,7 +326,7 @@ class JobControllerApiHandlerHelper { "/job/" + job.type().jobName()).normalize(); stepObject.setString("url", baseUriForJob.toString()); stepObject.setString("environment", job.type().environment().value()); - stepObject.setString("region", job.type().zone().value()); + if ( ! job.type().environment().isTest()) stepObject.setString("region", job.type().zone().value()); if (job.type().isProduction() && job.type().isDeployment()) { status.deploymentFor(job).ifPresent(deployment -> { @@ -421,6 +422,7 @@ class JobControllerApiHandlerHelper { runObject.setLong("start", run.start().toEpochMilli()); run.end().ifPresent(end -> runObject.setLong("end", end.toEpochMilli())); runObject.setString("status", run.status().name()); + run.reason().ifPresent(reason -> runObject.setString("reason", reason)); toSlime(runObject.setObject("versions"), run.versions(), application); Cursor runStepsArray = runObject.setArray("steps"); run.steps().forEach((step, info) -> { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java index 44a8b636ae0..4532e0c2c18 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java @@ -14,6 +14,7 @@ import com.yahoo.slime.Cursor; import com.yahoo.slime.Inspector; import com.yahoo.slime.Slime; import com.yahoo.slime.Type; +import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ApplicationController; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.TenantController; @@ -23,8 +24,10 @@ import com.yahoo.vespa.hosted.controller.api.integration.billing.CollectionMetho import com.yahoo.vespa.hosted.controller.api.integration.billing.Plan; import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId; import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistry; +import com.yahoo.vespa.hosted.controller.api.integration.billing.Quota; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; +import com.yahoo.vespa.hosted.controller.application.QuotaUsage; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; @@ -200,11 +203,13 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler var response = new Slime(); var tenantsResponse = response.setObject().setArray("tenants"); + tenants.asList().stream().sorted(Comparator.comparing(Tenant::name)).forEach(tenant -> { var usage = Optional.ofNullable(usagePerTenant.get(tenant.name())); var tenantResponse = tenantsResponse.addObject(); tenantResponse.setString("tenant", tenant.name().value()); toSlime(tenantResponse.setObject("plan"), planFor(tenant.name())); + toSlime(tenantResponse.setObject("quota"), billing.getQuota(tenant.name())); tenantResponse.setString("collection", billing.getCollectionMethod(tenant.name()).name()); tenantResponse.setString("lastBill", usage.map(Bill::getStartDate).map(DateTimeFormatter.ISO_DATE::format).orElse(null)); tenantResponse.setString("unbilled", usage.map(Bill::sum).map(BigDecimal::toPlainString).orElse("0.00")); @@ -357,6 +362,10 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler cursor.setString("name", plan.displayName()); } + private void toSlime(Cursor cursor, Quota quota) { + cursor.setDouble("budget", quota.budget().map(BigDecimal::doubleValue).orElse(-1.0)); + } + private Plan planFor(TenantName tenant) { var planId = billing.getPlan(tenant); return planRegistry.plan(planId) diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java index 25ac90ac0ea..776fcbfd03b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java @@ -35,6 +35,7 @@ import java.security.Principal; import java.security.cert.X509Certificate; import java.time.Instant; import java.util.Optional; +import java.util.OptionalInt; import java.util.Scanner; import java.util.function.Function; import java.util.logging.Level; @@ -60,13 +61,13 @@ public class ControllerApiHandler extends AuditLoggingRequestHandler { @Override public HttpResponse auditAndHandle(HttpRequest request) { try { - switch (request.getMethod()) { - case GET: return get(request); - case POST: return post(request); - case DELETE: return delete(request); - case PATCH: return patch(request); - default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported"); - } + return switch (request.getMethod()) { + case GET -> get(request); + case POST -> post(request); + case DELETE -> delete(request); + case PATCH -> patch(request); + default -> ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported"); + }; } catch (IllegalArgumentException e) { return ErrorResponse.badRequest(Exceptions.toMessageString(e)); @@ -165,8 +166,8 @@ public class ControllerApiHandler extends AuditLoggingRequestHandler { if (inspect.field(upgradesPerMinuteField).valid()) { upgrader.setUpgradesPerMinute(inspect.field(upgradesPerMinuteField).asDouble()); } else if (inspect.field(targetMajorVersionField).valid()) { - int target = (int)inspect.field(targetMajorVersionField).asLong(); - upgrader.setTargetMajorVersion(Optional.ofNullable(target == 0 ? null : target)); // 0 is the default value + int target = (int) inspect.field(targetMajorVersionField).asLong(); + upgrader.setTargetMajorVersion(target == 0 ? OptionalInt.empty() : OptionalInt.of(target)); // 0 is the default value } else { return ErrorResponse.badRequest("No such modifiable field(s)"); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java index 19f1ac5449f..96a3c9f177d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java @@ -30,6 +30,8 @@ public class StatsResponse extends SlimeJsonResponse { if (stats.applicationStats().isEmpty()) continue; // skip empty zones Cursor zoneObject = zonesArray.addObject(); zoneObject.setString("id", zone.toString()); + zoneObject.setDouble("totalCost", stats.totalCost()); + zoneObject.setDouble("totalAllocatedCost", stats.totalAllocatedCost()); toSlime(stats.load(), zoneObject.setObject("load")); toSlime(stats.activeLoad(), zoneObject.setObject("activeLoad")); Cursor applicationsArray = zoneObject.setArray("applications"); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java index 853739ee9c3..0e764b98514 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/os/OsApiHandler.java @@ -22,6 +22,9 @@ import com.yahoo.slime.SlimeUtils; import com.yahoo.slime.Type; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler; +import com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance; +import com.yahoo.vespa.hosted.controller.maintenance.OsUpgradeScheduler; +import com.yahoo.vespa.hosted.controller.maintenance.OsUpgradeScheduler.Change; import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget; import com.yahoo.yolean.Exceptions; @@ -47,22 +50,24 @@ import java.util.stream.Collectors; public class OsApiHandler extends AuditLoggingRequestHandler { private final Controller controller; + private final OsUpgradeScheduler osUpgradeScheduler; - public OsApiHandler(Context ctx, Controller controller) { + public OsApiHandler(Context ctx, Controller controller, ControllerMaintenance controllerMaintenance) { super(ctx, controller.auditLogger()); this.controller = controller; + this.osUpgradeScheduler = controllerMaintenance.osUpgradeScheduler(); } @Override public HttpResponse auditAndHandle(HttpRequest request) { try { - switch (request.getMethod()) { - case GET: return get(request); - case POST: return post(request); - case DELETE: return delete(request); - case PATCH: return patch(request); - default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is unsupported"); - } + return switch (request.getMethod()) { + case GET -> get(request); + case POST -> post(request); + case DELETE -> delete(request); + case PATCH -> patch(request); + default -> ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is unsupported"); + }; } catch (IllegalArgumentException e) { return ErrorResponse.badRequest(Exceptions.toMessageString(e)); } catch (RuntimeException e) { @@ -159,8 +164,16 @@ public class OsApiHandler extends AuditLoggingRequestHandler { currentVersionObject.setString("version", osVersion.version().toFullString()); Optional<OsVersionTarget> target = targets.stream().filter(t -> t.osVersion().equals(osVersion)).findFirst(); currentVersionObject.setBool("targetVersion", target.isPresent()); - target.ifPresent(t -> currentVersionObject.setString("upgradeBudget", t.upgradeBudget().toString())); - target.ifPresent(t -> currentVersionObject.setLong("scheduledAt", t.scheduledAt().toEpochMilli())); + target.ifPresent(t -> { + currentVersionObject.setString("upgradeBudget", t.upgradeBudget().toString()); + currentVersionObject.setLong("scheduledAt", t.scheduledAt().toEpochMilli()); + Optional<Change> nextChange = osUpgradeScheduler.changeIn(t.osVersion().cloud()); + nextChange.ifPresent(c -> { + currentVersionObject.setString("nextVersion", c.version().toFullString()); + currentVersionObject.setLong("nextScheduledAt", c.scheduleAt().toEpochMilli()); + }); + }); + currentVersionObject.setString("cloud", osVersion.cloud().value()); Cursor nodesArray = currentVersionObject.setArray("nodes"); nodeVersions.forEach(nodeVersion -> { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java index fce2d283da2..a407e5aa211 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java @@ -111,7 +111,6 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { private HttpResponse handlePOST(Path path, HttpRequest request) { if (path.matches("/user/v1/tenant/{tenant}")) return addTenantRoleMember(path.get("tenant"), request); - if (path.matches("/user/v1/tenant/{tenant}/application/{application}")) return addApplicationRoleMember(path.get("tenant"), path.get("application"), request); return ErrorResponse.notFoundError(Text.format("No '%s' handler at '%s'", request.getMethod(), request.getUri().getPath())); @@ -119,7 +118,6 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { private HttpResponse handleDELETE(Path path, HttpRequest request) { if (path.matches("/user/v1/tenant/{tenant}")) return removeTenantRoleMember(path.get("tenant"), request); - if (path.matches("/user/v1/tenant/{tenant}/application/{application}")) return removeApplicationRoleMember(path.get("tenant"), path.get("application"), request); return ErrorResponse.notFoundError(Text.format("No '%s' handler at '%s'", request.getMethod(), request.getUri().getPath())); @@ -255,21 +253,6 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { private HttpResponse addTenantRoleMember(String tenantName, HttpRequest request) { Inspector requestObject = bodyInspector(request); - if (requestObject.field("roles").valid()) { - return addMultipleTenantRoleMembers(tenantName, requestObject); - } - return addTenantRoleMember(tenantName, requestObject); - } - - private HttpResponse addTenantRoleMember(String tenantName, Inspector requestObject) { - String roleName = require("roleName", Inspector::asString, requestObject); - UserId user = new UserId(require("user", Inspector::asString, requestObject)); - Role role = Roles.toRole(TenantName.from(tenantName), roleName); - users.addUsers(role, List.of(user)); - return new MessageResponse(user + " is now a member of " + role); - } - - private HttpResponse addMultipleTenantRoleMembers(String tenantName, Inspector requestObject) { var tenant = TenantName.from(tenantName); var user = new UserId(require("user", Inspector::asString, requestObject)); var roles = SlimeStream.fromArray(requestObject.field("roles"), Inspector::asString) @@ -280,37 +263,8 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { return new MessageResponse(user + " is now a member of " + roles.stream().map(Role::toString).collect(Collectors.joining(", "))); } - private HttpResponse addApplicationRoleMember(String tenantName, String applicationName, HttpRequest request) { - Inspector requestObject = bodyInspector(request); - String roleName = require("roleName", Inspector::asString, requestObject); - UserId user = new UserId(require("user", Inspector::asString, requestObject)); - Role role = Roles.toRole(TenantName.from(tenantName), ApplicationName.from(applicationName), roleName); - users.addUsers(role, List.of(user)); - return new MessageResponse(user + " is now a member of " + role); - } - private HttpResponse removeTenantRoleMember(String tenantName, HttpRequest request) { Inspector requestObject = bodyInspector(request); - if (requestObject.field("roles").valid()) { - return removeMultipleTenantRoleMembers(tenantName, requestObject); - } - return removeTenantRoleMember(tenantName, requestObject); - } - - private HttpResponse removeTenantRoleMember(String tenantName, Inspector requestObject) { - TenantName tenant = TenantName.from(tenantName); - String roleName = require("roleName", Inspector::asString, requestObject); - UserId user = new UserId(require("user", Inspector::asString, requestObject)); - List<Role> roles = Collections.singletonList(Roles.toRole(tenant, roleName)); - - enforceLastAdminOfTenant(tenant, user, roles); - removeDeveloperKey(tenant, user, roles); - users.removeFromRoles(user, roles); - - return new MessageResponse(user + " is no longer a member of " + roles.stream().map(Role::toString).collect(Collectors.joining(", "))); - } - - private HttpResponse removeMultipleTenantRoleMembers(String tenantName, Inspector requestObject) { var tenant = TenantName.from(tenantName); var user = new UserId(require("user", Inspector::asString, requestObject)); var roles = SlimeStream.fromArray(requestObject.field("roles"), Inspector::asString) @@ -321,6 +275,11 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { removeDeveloperKey(tenant, user, roles); users.removeFromRoles(user, roles); + controller.tenants().lockIfPresent(tenant, LockedTenant.class, lockedTenant -> { + if (lockedTenant instanceof LockedTenant.Cloud cloudTenant) + controller.tenants().store(cloudTenant.withInvalidateUserSessionsBefore(controller.clock().instant())); + }); + return new MessageResponse(user + " is no longer a member of " + roles.stream().map(Role::toString).collect(Collectors.joining(", "))); } @@ -348,15 +307,6 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { } } - private HttpResponse removeApplicationRoleMember(String tenantName, String applicationName, HttpRequest request) { - Inspector requestObject = bodyInspector(request); - String roleName = require("roleName", Inspector::asString, requestObject); - UserId user = new UserId(require("user", Inspector::asString, requestObject)); - Role role = Roles.toRole(TenantName.from(tenantName), ApplicationName.from(applicationName), roleName); - users.removeUsers(role, List.of(user)); - return new MessageResponse(user + " is no longer a member of " + role); - } - private boolean hasTrialCapacity() { if (! controller.system().isPublic()) return true; var existing = controller.tenants().asList().stream().map(Tenant::name).collect(Collectors.toList()); @@ -384,18 +334,12 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { } private static Collection<TenantRole> filterTenantRoles(Role role) { - if (!(role instanceof TenantRole)) - return Set.of(); - - TenantRole tenantRole = (TenantRole) role; - if (tenantRole.definition() == RoleDefinition.administrator - || tenantRole.definition() == RoleDefinition.developer - || tenantRole.definition() == RoleDefinition.reader) - return Set.of(tenantRole); - - if (tenantRole.definition() == RoleDefinition.athenzTenantAdmin) - return Roles.tenantRoles(tenantRole.tenant()); - + if (role instanceof TenantRole tenantRole) { + switch (tenantRole.definition()) { + case administrator, developer, reader, hostedDeveloper: return Set.of(tenantRole); + case athenzTenantAdmin: return Roles.tenantRoles(tenantRole.tenant()); + } + } return Set.of(); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java new file mode 100644 index 00000000000..e2b5083abae --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java @@ -0,0 +1,50 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.security; + +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.flags.LongFlag; +import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.TenantController; +import com.yahoo.vespa.hosted.controller.api.integration.user.UserSessionManager; +import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; +import com.yahoo.vespa.hosted.controller.api.role.TenantRole; +import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; + +import java.time.Instant; + +/** + * @author freva + */ +public class CloudUserSessionManager implements UserSessionManager { + + private final TenantController tenantController; + private final LongFlag invalidateConsoleSessions; + + public CloudUserSessionManager(Controller controller) { + this.tenantController = controller.tenants(); + this.invalidateConsoleSessions = PermanentFlags.INVALIDATE_CONSOLE_SESSIONS.bindTo(controller.flagSource()); + } + + @Override + public boolean shouldExpireSessionFor(SecurityContext context) { + if (context.issuedAt().isBefore(Instant.ofEpochSecond(invalidateConsoleSessions.value()))) + return true; + + return context.roles().stream() + .filter(TenantRole.class::isInstance) + .map(TenantRole.class::cast) + .map(TenantRole::tenant) + .distinct() + .anyMatch(tenantName -> shouldExpireSessionFor(tenantName, context.issuedAt())); + } + + private boolean shouldExpireSessionFor(TenantName tenantName, Instant contextIssuedAt) { + return tenantController.get(tenantName) + .filter(CloudTenant.class::isInstance) + .map(CloudTenant.class::cast) + .flatMap(CloudTenant::invalidateUserSessionsBefore) + .map(contextIssuedAt::isBefore) + .orElse(false); + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/OsVersionStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/OsVersionStatus.java index 8ee891ae8a6..6f9888b79e0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/OsVersionStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/OsVersionStatus.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.versions; import com.google.common.collect.ImmutableMap; import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; +import com.yahoo.config.provision.zone.UpgradePolicy; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter; @@ -66,7 +67,7 @@ public record OsVersionStatus(Map<OsVersion, List<NodeVersion>> versions) { .orElse(Version.emptyVersion); for (var node : controller.serviceRegistry().configServer().nodeRepository().list(zone.getVirtualId(), NodeFilter.all().applications(application.id()))) { - if (!OsUpgrader.canUpgrade(node)) continue; + if (!OsUpgrader.canUpgrade(node, true)) continue; Optional<Instant> suspendedAt = node.suspendedSince(); NodeVersion nodeVersion = new NodeVersion(node.hostname(), zone.getVirtualId(), node.currentOsVersion(), targetOsVersion, suspendedAt); @@ -83,6 +84,7 @@ public record OsVersionStatus(Map<OsVersion, List<NodeVersion>> versions) { private static List<ZoneApi> zonesToUpgrade(Controller controller) { return controller.zoneRegistry().osUpgradePolicies().stream() .flatMap(upgradePolicy -> upgradePolicy.steps().stream()) + .map(UpgradePolicy.Step::zones) .flatMap(Collection::stream) .toList(); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java index 7f33f612cd0..e078df0267f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java @@ -15,7 +15,8 @@ import static com.yahoo.config.application.api.DeploymentSpec.UpgradePolicy; /** * Information about a particular Vespa version. - * VespaVersions are identified by their version number and ordered by increasing version numbers. + * + * Vespa versions are identified by their version number and ordered by increasing version numbers. * * @author bratseth */ @@ -29,8 +30,11 @@ public record VespaVersion(Version version, Confidence confidence) implements Comparable<VespaVersion> { public static Confidence confidenceFrom(DeploymentStatistics statistics, Controller controller) { + int thisMajorVersion = statistics.version().getMajor(); + int defaultMajorVersion = controller.applications().targetMajorVersion().orElse(thisMajorVersion); InstanceList all = InstanceList.from(controller.jobController().deploymentStatuses(ApplicationList.from(controller.applications().asList()) - .withProductionDeployment())); + .withProductionDeployment())) + .allowingMajorVersion(thisMajorVersion, defaultMajorVersion); // 'production on this': All production deployment jobs upgrading to this version have completed without failure InstanceList productionOnThis = all.matching(instance -> statistics.productionSuccesses().stream().anyMatch(run -> run.id().application().equals(instance))) .not().failingUpgrade() 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 28536f36e20..b5177cd1d3e 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 @@ -49,7 +49,7 @@ import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContex import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId; import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock; import com.yahoo.vespa.hosted.rotation.config.RotationsConfig; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -69,13 +69,13 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.pro import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.productionUsWest1; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.stagingTest; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.systemTest; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author bratseth @@ -86,7 +86,7 @@ public class ControllerTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void testDeployment() { + void testDeployment() { // Setup system ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -97,14 +97,14 @@ public class ControllerTest { Version version1 = tester.configServer().initialVersion(); var context = tester.newDeploymentContext(); context.submit(applicationPackage); - assertEquals("Application version is known from completion of initial job", - 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())); + assertEquals(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()), + "Application version is known from completion of initial job"); context.runJob(systemTest); context.runJob(stagingTest); RevisionId applicationVersion = context.instance().change().revision().get(); - assertTrue("Application version has been set during deployment", applicationVersion.isProduction()); + assertTrue(applicationVersion.isProduction(), "Application version has been set during deployment"); tester.triggerJobs(); // Causes first deployment job to be triggered @@ -171,11 +171,11 @@ public class ControllerTest { } catch (IllegalArgumentException e) { assertEquals("deployment-removal: application 'tenant.application' is deployed in us-west-1, but does not include this zone in deployment.xml. " + - ValidationOverrides.toAllowMessage(ValidationId.deploymentRemoval), - e.getMessage()); + ValidationOverrides.toAllowMessage(ValidationId.deploymentRemoval), + e.getMessage()); } - assertNotNull("Zone was not removed", - context.instance().deployments().get(productionUsWest1.zone())); + assertNotNull(context.instance().deployments().get(productionUsWest1.zone()), + "Zone was not removed"); // prod zone removal is allowed with override applicationPackage = new ApplicationPackageBuilder() @@ -184,14 +184,14 @@ public class ControllerTest { .region("us-east-3") .build(); context.submit(applicationPackage); - assertNull("Zone was removed", - context.instance().deployments().get(productionUsWest1.zone())); - assertNull("Deployment job was removed", context.instanceJobs().get(productionUsWest1)); + assertNull(context.instance().deployments().get(productionUsWest1.zone()), + "Zone was removed"); + assertNull(context.instanceJobs().get(productionUsWest1), "Deployment job was removed"); // Submission has stored application meta. assertNotNull(tester.controllerTester().serviceRegistry().applicationStore() - .getMeta(context.instanceId()) - .get(tester.clock().instant())); + .getMeta(context.instanceId()) + .get(tester.clock().instant())); // Meta data tombstone placed on delete tester.clock().advance(Duration.ofSeconds(1)); @@ -199,18 +199,18 @@ public class ControllerTest { tester.clock().advance(Duration.ofSeconds(1)); context.submit(ApplicationPackage.deploymentRemoval()); tester.applications().deleteApplication(context.application().id(), - tester.controllerTester().credentialsFor(context.instanceId().tenant())); + tester.controllerTester().credentialsFor(context.instanceId().tenant())); assertArrayEquals(new byte[0], - tester.controllerTester().serviceRegistry().applicationStore() - .getMeta(context.instanceId()) - .get(tester.clock().instant())); + tester.controllerTester().serviceRegistry().applicationStore() + .getMeta(context.instanceId()) + .get(tester.clock().instant())); assertNull(tester.controllerTester().serviceRegistry().applicationStore() - .getMeta(context.deploymentIdIn(productionUsWest1.zone()))); + .getMeta(context.deploymentIdIn(productionUsWest1.zone()))); } @Test - public void testGlobalRotationStatus() { + void testGlobalRotationStatus() { var context = tester.newDeploymentContext(); var zone1 = ZoneId.from("prod", "us-west-1"); var zone2 = ZoneId.from("prod", "us-east-3"); @@ -238,7 +238,7 @@ public class ControllerTest { } @Test - public void testDnsUpdatesForGlobalEndpoint() { + void testDnsUpdatesForGlobalEndpoint() { var betaContext = tester.newDeploymentContext("tenant1", "app1", "beta"); var defaultContext = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -251,23 +251,23 @@ public class ControllerTest { .region(usCentral.region()) // Two deployments should result in each DNS alias being registered once .build(); tester.controllerTester().zoneRegistry().setRoutingMethod(List.of(ZoneApiMock.from(usWest), ZoneApiMock.from(usCentral)), - RoutingMethod.sharedLayer4); + RoutingMethod.sharedLayer4); betaContext.submit(applicationPackage).deploy(); { // Expected rotation names are passed to beta instance deployments Collection<Deployment> betaDeployments = betaContext.instance().deployments().values(); assertFalse(betaDeployments.isEmpty()); Set<ContainerEndpoint> containerEndpoints = Set.of(new ContainerEndpoint("foo", - "global", - List.of("beta.app1.tenant1.global.vespa.oath.cloud", - "rotation-id-01"), - OptionalInt.empty(), - RoutingMethod.sharedLayer4)); + "global", + List.of("beta.app1.tenant1.global.vespa.oath.cloud", + "rotation-id-01"), + OptionalInt.empty(), + RoutingMethod.sharedLayer4)); for (Deployment deployment : betaDeployments) { assertEquals(containerEndpoints, - tester.configServer().containerEndpoints() - .get(betaContext.deploymentIdIn(deployment.zone()))); + tester.configServer().containerEndpoints() + .get(betaContext.deploymentIdIn(deployment.zone()))); } betaContext.flushDnsUpdates(); } @@ -276,20 +276,20 @@ public class ControllerTest { Collection<Deployment> defaultDeployments = defaultContext.instance().deployments().values(); assertFalse(defaultDeployments.isEmpty()); Set<ContainerEndpoint> containerEndpoints = Set.of(new ContainerEndpoint("foo", - "global", - List.of("app1.tenant1.global.vespa.oath.cloud", - "rotation-id-02"), - OptionalInt.empty(), - RoutingMethod.sharedLayer4)); + "global", + List.of("app1.tenant1.global.vespa.oath.cloud", + "rotation-id-02"), + OptionalInt.empty(), + RoutingMethod.sharedLayer4)); for (Deployment deployment : defaultDeployments) { assertEquals(containerEndpoints, - tester.configServer().containerEndpoints().get(defaultContext.deploymentIdIn(deployment.zone()))); + tester.configServer().containerEndpoints().get(defaultContext.deploymentIdIn(deployment.zone()))); } defaultContext.flushDnsUpdates(); } Map<String, String> rotationCnames = Map.of("beta.app1.tenant1.global.vespa.oath.cloud", "rotation-fqdn-01.", - "app1.tenant1.global.vespa.oath.cloud", "rotation-fqdn-02."); + "app1.tenant1.global.vespa.oath.cloud", "rotation-fqdn-02."); rotationCnames.forEach((cname, data) -> { var record = tester.controllerTester().findCname(cname); assertTrue(record.isPresent()); @@ -298,20 +298,20 @@ public class ControllerTest { }); Map<ApplicationId, Set<String>> globalDnsNamesByInstance = Map.of(betaContext.instanceId(), Set.of("beta.app1.tenant1.global.vespa.oath.cloud"), - defaultContext.instanceId(), Set.of("app1.tenant1.global.vespa.oath.cloud")); + defaultContext.instanceId(), Set.of("app1.tenant1.global.vespa.oath.cloud")); globalDnsNamesByInstance.forEach((instance, dnsNames) -> { Set<String> actualDnsNames = tester.controller().routing().readDeclaredEndpointsOf(instance) - .scope(Endpoint.Scope.global) - .asList().stream() - .map(Endpoint::dnsName) - .collect(Collectors.toSet()); - assertEquals("Global DNS names for " + instance, dnsNames, actualDnsNames); + .scope(Endpoint.Scope.global) + .asList().stream() + .map(Endpoint::dnsName) + .collect(Collectors.toSet()); + assertEquals(dnsNames, actualDnsNames, "Global DNS names for " + instance); }); } @Test - public void testDnsUpdatesForGlobalEndpointLegacySyntax() { + void testDnsUpdatesForGlobalEndpointLegacySyntax() { var context = tester.newDeploymentContext("tenant1", "app1", "default"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .globalServiceId("foo") @@ -323,10 +323,10 @@ public class ControllerTest { Collection<Deployment> deployments = context.instance().deployments().values(); assertFalse(deployments.isEmpty()); for (Deployment deployment : deployments) { - assertEquals("Rotation names are passed to config server in " + deployment.zone(), - Set.of("rotation-id-01", + assertEquals(Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), - tester.configServer().containerEndpointNames(context.deploymentIdIn(deployment.zone()))); + tester.configServer().containerEndpointNames(context.deploymentIdIn(deployment.zone())), + "Rotation names are passed to config server in " + deployment.zone()); } context.flushDnsUpdates(); assertEquals(1, tester.controllerTester().nameService().records().size()); @@ -337,15 +337,15 @@ public class ControllerTest { assertEquals("rotation-fqdn-01.", record.get().data().asString()); List<String> globalDnsNames = tester.controller().routing().readDeclaredEndpointsOf(context.instanceId()) - .scope(Endpoint.Scope.global) - .sortedBy(Comparator.comparing(Endpoint::dnsName)) - .mapToList(Endpoint::dnsName); + .scope(Endpoint.Scope.global) + .sortedBy(Comparator.comparing(Endpoint::dnsName)) + .mapToList(Endpoint::dnsName); assertEquals(List.of("app1.tenant1.global.vespa.oath.cloud"), - globalDnsNames); + globalDnsNames); } @Test - public void testDnsUpdatesForMultipleGlobalEndpoints() { + void testDnsUpdatesForMultipleGlobalEndpoints() { var context = tester.newDeploymentContext("tenant1", "app1", "default"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .endpoint("foobar", "qrs", "us-west-1", "us-central-1") // Rotation 01 @@ -368,9 +368,9 @@ public class ControllerTest { var west = Sets.union(notWest, Set.of("rotation-id-04", "west.app1.tenant1.global.vespa.oath.cloud")); for (Deployment deployment : deployments) { - assertEquals("Rotation names are passed to config server in " + deployment.zone(), - ZoneId.from("prod.us-west-1").equals(deployment.zone()) ? west : notWest, - tester.configServer().containerEndpointNames(context.deploymentIdIn(deployment.zone()))); + assertEquals(ZoneId.from("prod.us-west-1").equals(deployment.zone()) ? west : notWest, + tester.configServer().containerEndpointNames(context.deploymentIdIn(deployment.zone())), + "Rotation names are passed to config server in " + deployment.zone()); } context.flushDnsUpdates(); @@ -398,7 +398,7 @@ public class ControllerTest { } @Test - public void testDnsUpdatesForGlobalEndpointChanges() { + void testDnsUpdatesForGlobalEndpointChanges() { var context = tester.newDeploymentContext("tenant1", "app1", "default"); var west = ZoneId.from("prod", "us-west-1"); var central = ZoneId.from("prod", "us-central-1"); @@ -415,10 +415,10 @@ public class ControllerTest { for (var zone : List.of(west, central)) { assertEquals( - "Zone " + zone + " is a member of global endpoint", Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)) - ); + , + "Zone " + zone + " is a member of global endpoint"); } // Application is deployed with an additional endpoint @@ -433,16 +433,16 @@ public class ControllerTest { for (var zone : List.of(west, central)) { assertEquals( - "Zone " + zone + " is a member of global endpoint", Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)) - ); + , + "Zone " + zone + " is a member of global endpoint"); } assertEquals( - "Zone " + east + " is a member of global endpoint", Set.of("rotation-id-02", "east.app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(east)) - ); + , + "Zone " + east + " is a member of global endpoint"); // Application is deployed with default endpoint pointing to 3/3 zones ApplicationPackage applicationPackage3 = new ApplicationPackageBuilder() @@ -455,13 +455,13 @@ public class ControllerTest { context.submit(applicationPackage3).deploy(); for (var zone : List.of(west, central, east)) { assertEquals( - "Zone " + zone + " is a member of global endpoint", zone.equals(east) ? Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud", - "rotation-id-02", "east.app1.tenant1.global.vespa.oath.cloud") + "rotation-id-02", "east.app1.tenant1.global.vespa.oath.cloud") : Set.of("rotation-id-01", "app1.tenant1.global.vespa.oath.cloud"), tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)) - ); + , + "Zone " + zone + " is a member of global endpoint"); } // Region is removed from an endpoint without override @@ -477,11 +477,11 @@ public class ControllerTest { fail("Expected exception"); } catch (IllegalArgumentException e) { assertEquals("global-endpoint-change: application 'tenant1.app1' has endpoints " + - "[endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1, endpoint 'east' (cluster qrs) -> us-east-3], " + - "but does not include all of these in deployment.xml. Deploying given deployment.xml " + - "will remove [endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1] " + - "and add [endpoint 'default' (cluster qrs) -> us-central-1, us-west-1]. " + - ValidationOverrides.toAllowMessage(ValidationId.globalEndpointChange), e.getMessage()); + "[endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1, endpoint 'east' (cluster qrs) -> us-east-3], " + + "but does not include all of these in deployment.xml. Deploying given deployment.xml " + + "will remove [endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1] " + + "and add [endpoint 'default' (cluster qrs) -> us-central-1, us-west-1]. " + + ValidationOverrides.toAllowMessage(ValidationId.globalEndpointChange), e.getMessage()); } // Entire endpoint is removed without override @@ -496,10 +496,10 @@ public class ControllerTest { fail("Expected exception"); } catch (IllegalArgumentException e) { assertEquals("global-endpoint-change: application 'tenant1.app1' has endpoints " + - "[endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1, endpoint 'east' (cluster qrs) -> us-east-3], " + - "but does not include all of these in deployment.xml. Deploying given deployment.xml " + - "will remove [endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1]. " + - ValidationOverrides.toAllowMessage(ValidationId.globalEndpointChange), e.getMessage()); + "[endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1, endpoint 'east' (cluster qrs) -> us-east-3], " + + "but does not include all of these in deployment.xml. Deploying given deployment.xml " + + "will remove [endpoint 'default' (cluster qrs) -> us-central-1, us-east-3, us-west-1]. " + + ValidationOverrides.toAllowMessage(ValidationId.globalEndpointChange), e.getMessage()); } // ... override is added @@ -514,7 +514,7 @@ public class ControllerTest { } @Test - public void testUnassignRotations() { + void testUnassignRotations() { var context = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .endpoint("default", "qrs", "us-west-1", "us-central-1") @@ -540,7 +540,7 @@ public class ControllerTest { } @Test - public void testDnsUpdatesWithChangeInRotationAssignment() { + void testDnsUpdatesWithChangeInRotationAssignment() { // Application 1 is deployed and deleted String dnsName1 = "app1.tenant1.global.vespa.oath.cloud"; { @@ -567,17 +567,17 @@ public class ControllerTest { .build(); context.submit(applicationPackage); tester.applications().deleteApplication(context.application().id(), - tester.controllerTester().credentialsFor(context.application().id().tenant())); + tester.controllerTester().credentialsFor(context.application().id().tenant())); try (RotationLock lock = tester.controller().routing().rotations().lock()) { - assertTrue("Rotation is unassigned", - tester.controller().routing().rotations().availableRotations(lock) - .containsKey(new RotationId("rotation-id-01"))); + assertTrue(tester.controller().routing().rotations().availableRotations(lock) + .containsKey(new RotationId("rotation-id-01")), + "Rotation is unassigned"); } context.flushDnsUpdates(); // Record is removed Optional<Record> record = tester.controllerTester().findCname(dnsName1); - assertTrue(dnsName1 + " is removed", record.isEmpty()); + assertTrue(record.isEmpty(), dnsName1 + " is removed"); } // Application 2 is deployed and assigned same rotation as application 1 had before deletion @@ -624,7 +624,7 @@ public class ControllerTest { } @Test - public void testDnsUpdatesForApplicationEndpoint() { + void testDnsUpdatesForApplicationEndpoint() { ApplicationId beta = ApplicationId.from("tenant1", "app1", "beta"); ApplicationId main = ApplicationId.from("tenant1", "app1", "main"); var context = tester.newDeploymentContext(beta); @@ -633,14 +633,14 @@ public class ControllerTest { .region("us-west-1") .region("us-east-3") .applicationEndpoint("a", "default", "us-west-1", - Map.of(beta.instance(), 2, - main.instance(), 8)) + Map.of(beta.instance(), 2, + main.instance(), 8)) .applicationEndpoint("b", "default", "us-west-1", - Map.of(beta.instance(), 1, - main.instance(), 1)) + Map.of(beta.instance(), 1, + main.instance(), 1)) .applicationEndpoint("c", "default", "us-east-3", - Map.of(beta.instance(), 4, - main.instance(), 6)) + Map.of(beta.instance(), 4, + main.instance(), 6)) .build(); context.submit(applicationPackage).deploy(); @@ -649,63 +649,63 @@ public class ControllerTest { // Expected container endpoints are passed to each deployment Map<DeploymentId, Map<String, Integer>> deploymentEndpoints = Map.of( new DeploymentId(beta, usWest), Map.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", 2, - "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", 1), + "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", 1), new DeploymentId(main, usWest), Map.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", 8, - "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", 1), + "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", 1), new DeploymentId(beta, usEast), Map.of("c.app1.tenant1.us-east-3-r.vespa.oath.cloud", 4), new DeploymentId(main, usEast), Map.of("c.app1.tenant1.us-east-3-r.vespa.oath.cloud", 6) ); deploymentEndpoints.forEach((deployment, endpoints) -> { Set<ContainerEndpoint> expected = endpoints.entrySet().stream() - .map(kv -> new ContainerEndpoint("default", "application", - List.of(kv.getKey()), - OptionalInt.of(kv.getValue()), - RoutingMethod.sharedLayer4)) - .collect(Collectors.toSet()); - assertEquals("Endpoint names for " + deployment + " are passed to config server", - expected, - tester.configServer().containerEndpoints().get(deployment)); + .map(kv -> new ContainerEndpoint("default", "application", + List.of(kv.getKey()), + OptionalInt.of(kv.getValue()), + RoutingMethod.sharedLayer4)) + .collect(Collectors.toSet()); + assertEquals(expected, + tester.configServer().containerEndpoints().get(deployment), + "Endpoint names for " + deployment + " are passed to config server"); }); context.flushDnsUpdates(); // DNS records are created for each endpoint Set<Record> records = tester.controllerTester().nameService().records(); assertEquals(Set.of(new Record(Record.Type.CNAME, - RecordName.from("a.app1.tenant1.us-west-1-r.vespa.oath.cloud"), - RecordData.from("vip.prod.us-west-1.")), - new Record(Record.Type.CNAME, - RecordName.from("b.app1.tenant1.us-west-1-r.vespa.oath.cloud"), - RecordData.from("vip.prod.us-west-1.")), - new Record(Record.Type.CNAME, - RecordName.from("c.app1.tenant1.us-east-3-r.vespa.oath.cloud"), - RecordData.from("vip.prod.us-east-3."))), - records); + RecordName.from("a.app1.tenant1.us-west-1-r.vespa.oath.cloud"), + RecordData.from("vip.prod.us-west-1.")), + new Record(Record.Type.CNAME, + RecordName.from("b.app1.tenant1.us-west-1-r.vespa.oath.cloud"), + RecordData.from("vip.prod.us-west-1.")), + new Record(Record.Type.CNAME, + RecordName.from("c.app1.tenant1.us-east-3-r.vespa.oath.cloud"), + RecordData.from("vip.prod.us-east-3."))), + records); List<String> endpointDnsNames = tester.controller().routing().declaredEndpointsOf(context.application()) - .scope(Endpoint.Scope.application) - .mapToList(Endpoint::dnsName); + .scope(Endpoint.Scope.application) + .mapToList(Endpoint::dnsName); assertEquals(List.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", - "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", - "c.app1.tenant1.us-east-3-r.vespa.oath.cloud"), - endpointDnsNames); + "b.app1.tenant1.us-west-1-r.vespa.oath.cloud", + "c.app1.tenant1.us-east-3-r.vespa.oath.cloud"), + endpointDnsNames); } @Test - public void testDevDeployment() { + void testDevDeployment() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder().build(); // Create application var context = tester.newDeploymentContext(); ZoneId zone = ZoneId.from("dev", "us-east-1"); tester.controllerTester().zoneRegistry() - .setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.sharedLayer4); + .setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.sharedLayer4); // Deploy context.runJob(zone, applicationPackage); - assertTrue("Application deployed and activated", - tester.configServer().application(context.instanceId(), zone).get().activated()); - assertTrue("No job status added", - context.instanceJobs().isEmpty()); - assertEquals("DeploymentSpec is not stored", DeploymentSpec.empty, context.application().deploymentSpec()); + assertTrue(tester.configServer().application(context.instanceId(), zone).get().activated(), + "Application deployed and activated"); + assertTrue(context.instanceJobs().isEmpty(), + "No job status added"); + assertEquals(DeploymentSpec.empty, context.application().deploymentSpec(), "DeploymentSpec is not stored"); // Verify zone supports shared layer 4 and shared routing methods Set<RoutingMethod> routingMethods = tester.controller().routing().readEndpointsOf(context.deploymentIdIn(zone)) @@ -717,20 +717,20 @@ public class ControllerTest { // Deployment has stored application meta. assertNotNull(tester.controllerTester().serviceRegistry().applicationStore() - .getMeta(new DeploymentId(context.instanceId(), zone)) - .get(tester.clock().instant())); + .getMeta(new DeploymentId(context.instanceId(), zone)) + .get(tester.clock().instant())); // Meta data tombstone placed on delete tester.clock().advance(Duration.ofSeconds(1)); tester.controller().applications().deactivate(context.instanceId(), zone); assertArrayEquals(new byte[0], - tester.controllerTester().serviceRegistry().applicationStore() - .getMeta(new DeploymentId(context.instanceId(), zone)) - .get(tester.clock().instant())); + tester.controllerTester().serviceRegistry().applicationStore() + .getMeta(new DeploymentId(context.instanceId(), zone)) + .get(tester.clock().instant())); } @Test - public void testDevDeploymentWithIncompatibleVersions() { + void testDevDeploymentWithIncompatibleVersions() { Version version1 = new Version("7"); Version version2 = new Version("7.5"); Version version3 = new Version("8"); @@ -778,12 +778,12 @@ public class ControllerTest { } @Test - public void testSuspension() { + void testSuspension() { var context = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() - .region("us-west-1") - .region("us-east-3") - .build(); + .region("us-west-1") + .region("us-east-3") + .build(); context.submit(applicationPackage).deploy(); DeploymentId deployment1 = context.deploymentIdIn(ZoneId.from(Environment.prod, RegionName.from("us-west-1"))); @@ -798,7 +798,7 @@ public class ControllerTest { // Application may already have been deleted, or deployment failed without response, test that deleting a // second time will not fail @Test - public void testDeletingApplicationThatHasAlreadyBeenDeleted() { + void testDeletingApplicationThatHasAlreadyBeenDeleted() { var context = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -811,7 +811,7 @@ public class ControllerTest { } @Test - public void testDeployApplicationWithWarnings() { + void testDeployApplicationWithWarnings() { var context = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -821,11 +821,11 @@ public class ControllerTest { tester.configServer().generateWarnings(context.deploymentIdIn(zone), warnings); context.submit(applicationPackage).deploy(); assertEquals(warnings, context.deployment(zone) - .metrics().warnings().get(DeploymentMetrics.Warning.all).intValue()); + .metrics().warnings().get(DeploymentMetrics.Warning.all).intValue()); } @Test - public void testDeploySelectivelyProvisionsCertificate() { + void testDeploySelectivelyProvisionsCertificate() { Function<Instance, Optional<EndpointCertificateMetadata>> certificate = (application) -> tester.controller().curator().readEndpointCertificateMetadata(application.id()); // Create app1 @@ -835,22 +835,22 @@ public class ControllerTest { var testZone = ZoneId.from("test", "us-east-1"); tester.controllerTester().zoneRegistry().exclusiveRoutingIn(ZoneApiMock.from(prodZone)); var applicationPackage = new ApplicationPackageBuilder().athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service")) - .region(prodZone.region()) - .build(); + .region(prodZone.region()) + .build(); // Deploy app1 in production context1.submit(applicationPackage).deploy(); var cert = certificate.apply(context1.instance()); - assertTrue("Provisions certificate in " + Environment.prod, cert.isPresent()); + assertTrue(cert.isPresent(), "Provisions certificate in " + Environment.prod); assertEquals(Stream.concat(Stream.of("vznqtz7a5ygwjkbhhj7ymxvlrekgt4l6g.vespa.oath.cloud", - "app1.tenant1.global.vespa.oath.cloud", - "*.app1.tenant1.global.vespa.oath.cloud"), - Stream.of(prodZone, testZone, stagingZone) - .flatMap(zone -> Stream.of("", "*.") - .map(prefix -> prefix + "app1.tenant1." + zone.region().value() + - (zone.environment() == Environment.prod ? "" : "." + zone.environment().value()) + - ".vespa.oath.cloud"))) - .collect(Collectors.toUnmodifiableSet()), - Set.copyOf(tester.controllerTester().serviceRegistry().endpointCertificateMock().dnsNamesOf(context1.instanceId()))); + "app1.tenant1.global.vespa.oath.cloud", + "*.app1.tenant1.global.vespa.oath.cloud"), + Stream.of(prodZone, testZone, stagingZone) + .flatMap(zone -> Stream.of("", "*.") + .map(prefix -> prefix + "app1.tenant1." + zone.region().value() + + (zone.environment() == Environment.prod ? "" : "." + zone.environment().value()) + + ".vespa.oath.cloud"))) + .collect(Collectors.toUnmodifiableSet()), + Set.copyOf(tester.controllerTester().serviceRegistry().endpointCertificateMock().dnsNamesOf(context1.instanceId()))); // Next deployment reuses certificate context1.submit(applicationPackage).deploy(); @@ -862,13 +862,13 @@ public class ControllerTest { // Deploy app2 in a zone with shared routing context2.runJob(devZone, applicationPackage); - assertTrue("Application deployed and activated", - tester.configServer().application(context2.instanceId(), devZone).get().activated()); - assertTrue("Provisions certificate also in zone with routing layer", certificate.apply(context2.instance()).isPresent()); + assertTrue(tester.configServer().application(context2.instanceId(), devZone).get().activated(), + "Application deployed and activated"); + assertTrue(certificate.apply(context2.instance()).isPresent(), "Provisions certificate also in zone with routing layer"); } @Test - public void testDeployWithGlobalEndpointsInMultipleClouds() { + void testDeployWithGlobalEndpointsInMultipleClouds() { tester.controllerTester().zoneRegistry().setZones( ZoneApiMock.fromId("test.us-west-1"), ZoneApiMock.fromId("staging.us-west-1"), @@ -904,7 +904,7 @@ public class ControllerTest { } @Test - public void testDeployWithoutSourceRevision() { + void testDeployWithoutSourceRevision() { var context = tester.newDeploymentContext(); var applicationPackage = new ApplicationPackageBuilder() .upgradePolicy("default") @@ -913,12 +913,12 @@ public class ControllerTest { // Submit without source revision context.submit(applicationPackage, Optional.empty()) - .deploy(); - assertEquals("Deployed application", 1, context.instance().deployments().size()); + .deploy(); + assertEquals(1, context.instance().deployments().size(), "Deployed application"); } @Test - public void testDeployWithGlobalEndpointsAndMultipleRoutingMethods() { + void testDeployWithGlobalEndpointsAndMultipleRoutingMethods() { var context = tester.newDeploymentContext(); var zone1 = ZoneId.from("prod", "us-west-1"); var zone2 = ZoneId.from("prod", "us-east-3"); @@ -939,25 +939,25 @@ public class ControllerTest { var expectedRecords = List.of( // The weighted record for zone 2's region new Record(Record.Type.ALIAS, - RecordName.from("application.tenant.us-east-3-w.vespa.oath.cloud"), - new WeightedAliasTarget(HostName.of("lb-0--tenant.application.default--prod.us-east-3"), - "dns-zone-1", ZoneId.from("prod.us-east-3"), 1).pack()), + RecordName.from("application.tenant.us-east-3-w.vespa.oath.cloud"), + new WeightedAliasTarget(HostName.of("lb-0--tenant.application.default--prod.us-east-3"), + "dns-zone-1", ZoneId.from("prod.us-east-3"), 1).pack()), // The 'east' global endpoint, pointing to the weighted record for zone 2's region new Record(Record.Type.ALIAS, - RecordName.from("east.application.tenant.global.vespa.oath.cloud"), - new LatencyAliasTarget(HostName.of("application.tenant.us-east-3-w.vespa.oath.cloud"), - "dns-zone-1", ZoneId.from("prod.us-east-3")).pack()), + RecordName.from("east.application.tenant.global.vespa.oath.cloud"), + new LatencyAliasTarget(HostName.of("application.tenant.us-east-3-w.vespa.oath.cloud"), + "dns-zone-1", ZoneId.from("prod.us-east-3")).pack()), // The zone-scoped endpoint pointing to zone 2 with exclusive routing new Record(Record.Type.CNAME, - RecordName.from("application.tenant.us-east-3.vespa.oath.cloud"), - RecordData.from("lb-0--tenant.application.default--prod.us-east-3."))); + RecordName.from("application.tenant.us-east-3.vespa.oath.cloud"), + RecordData.from("lb-0--tenant.application.default--prod.us-east-3."))); assertEquals(expectedRecords, List.copyOf(tester.controllerTester().nameService().records())); } @Test - public void testDeploymentDirectRouting() { + void testDeploymentDirectRouting() { // Rotation-less system DeploymentTester tester = new DeploymentTester(new ControllerTester(new RotationsConfig.Builder().build(), main)); var context = tester.newDeploymentContext(); @@ -965,7 +965,7 @@ public class ControllerTest { var zone2 = ZoneId.from("prod", "us-east-3"); var zone3 = ZoneId.from("prod", "eu-west-1"); tester.controllerTester().zoneRegistry() - .exclusiveRoutingIn(ZoneApiMock.from(zone1), ZoneApiMock.from(zone2), ZoneApiMock.from(zone3)); + .exclusiveRoutingIn(ZoneApiMock.from(zone1), ZoneApiMock.from(zone2), ZoneApiMock.from(zone3)); var applicationPackageBuilder = new ApplicationPackageBuilder() .region(zone1.region()) @@ -979,20 +979,20 @@ public class ControllerTest { // Deployment passes container endpoints to config server for (var zone : List.of(zone1, zone2)) { - assertEquals("Expected container endpoints in " + zone, - Set.of("application.tenant.global.vespa.oath.cloud", - "foo.application.tenant.global.vespa.oath.cloud", - "us.application.tenant.global.vespa.oath.cloud"), - tester.configServer().containerEndpointNames(context.deploymentIdIn(zone))); + assertEquals(Set.of("application.tenant.global.vespa.oath.cloud", + "foo.application.tenant.global.vespa.oath.cloud", + "us.application.tenant.global.vespa.oath.cloud"), + tester.configServer().containerEndpointNames(context.deploymentIdIn(zone)), + "Expected container endpoints in " + zone); } - assertEquals("Expected container endpoints in " + zone3, - Set.of("application.tenant.global.vespa.oath.cloud", - "foo.application.tenant.global.vespa.oath.cloud"), - tester.configServer().containerEndpointNames(context.deploymentIdIn(zone3))); + assertEquals(Set.of("application.tenant.global.vespa.oath.cloud", + "foo.application.tenant.global.vespa.oath.cloud"), + tester.configServer().containerEndpointNames(context.deploymentIdIn(zone3)), + "Expected container endpoints in " + zone3); } @Test - public void testChangeEndpointCluster() { + void testChangeEndpointCluster() { var context = tester.newDeploymentContext(); var west = ZoneId.from("prod", "us-west-1"); var east = ZoneId.from("prod", "us-east-3"); @@ -1005,7 +1005,7 @@ public class ControllerTest { .build(); context.submit(applicationPackage).deploy(); assertEquals(ClusterSpec.Id.from("foo"), tester.applications().requireInstance(context.instanceId()) - .rotations().get(0).clusterId()); + .rotations().get(0).clusterId()); // Redeploy with endpoint cluster changed needs override applicationPackage = new ApplicationPackageBuilder() @@ -1018,12 +1018,12 @@ public class ControllerTest { fail("Expected exception"); } catch (IllegalArgumentException e) { assertEquals("global-endpoint-change: application 'tenant.application' has endpoints [endpoint " + - "'default' (cluster foo) -> us-east-3, us-west-1], but does not include all of these in " + - "deployment.xml. Deploying given deployment.xml will remove " + - "[endpoint 'default' (cluster foo) -> us-east-3, us-west-1] and add " + - "[endpoint 'default' (cluster bar) -> us-east-3, us-west-1]. To allow this add " + - "<allow until='yyyy-mm-dd'>global-endpoint-change</allow> to validation-overrides.xml, see " + - "https://docs.vespa.ai/en/reference/validation-overrides.html", e.getMessage()); + "'default' (cluster foo) -> us-east-3, us-west-1], but does not include all of these in " + + "deployment.xml. Deploying given deployment.xml will remove " + + "[endpoint 'default' (cluster foo) -> us-east-3, us-west-1] and add " + + "[endpoint 'default' (cluster bar) -> us-east-3, us-west-1]. To allow this add " + + "<allow until='yyyy-mm-dd'>global-endpoint-change</allow> to validation-overrides.xml, see " + + "https://docs.vespa.ai/en/reference/validation-overrides.html", e.getMessage()); } // Redeploy with override succeeds @@ -1035,26 +1035,26 @@ public class ControllerTest { .build(); context.submit(applicationPackage).deploy(); assertEquals(ClusterSpec.Id.from("bar"), tester.applications().requireInstance(context.instanceId()) - .rotations().get(0).clusterId()); + .rotations().get(0).clusterId()); } @Test - public void testReadableApplications() { + void testReadableApplications() { var db = new MockCuratorDb(tester.controller().system()); var tester = new DeploymentTester(new ControllerTester(db)); // Create and deploy two applications var app1 = tester.newDeploymentContext("t1", "a1", "default") - .submit() - .deploy(); + .submit() + .deploy(); var app2 = tester.newDeploymentContext("t2", "a2", "default") - .submit() - .deploy(); + .submit() + .deploy(); assertEquals(2, tester.applications().readable().size()); // Write invalid data to one application db.curator().set(Path.fromString("/controller/v1/applications/" + app2.application().id().serialized()), - new byte[]{(byte) 0xDE, (byte) 0xAD}); + new byte[]{(byte) 0xDE, (byte) 0xAD}); // Can read the remaining readable assertEquals(1, tester.applications().readable().size()); @@ -1071,57 +1071,57 @@ public class ControllerTest { } @Test - public void testClashingEndpointIdAndInstanceName() { + void testClashingEndpointIdAndInstanceName() { String deploymentXml = "<deployment version='1.0' athenz-domain='domain' athenz-service='service'>\n" + - " <instance id=\"default\">\n" + - " <prod>\n" + - " <region active=\"true\">us-west-1</region>\n" + - " </prod>\n" + - " <endpoints>\n" + - " <endpoint id=\"dev\" container-id=\"qrs\"/>\n" + - " </endpoints>\n" + - " </instance>\n" + - " <instance id=\"dev\">\n" + - " <prod>\n" + - " <region active=\"true\">us-west-1</region>\n" + - " </prod>\n" + - " <endpoints>\n" + - " <endpoint id=\"default\" container-id=\"qrs\"/>\n" + - " </endpoints>\n" + - " </instance>\n" + - "</deployment>\n"; + " <instance id=\"default\">\n" + + " <prod>\n" + + " <region active=\"true\">us-west-1</region>\n" + + " </prod>\n" + + " <endpoints>\n" + + " <endpoint id=\"dev\" container-id=\"qrs\"/>\n" + + " </endpoints>\n" + + " </instance>\n" + + " <instance id=\"dev\">\n" + + " <prod>\n" + + " <region active=\"true\">us-west-1</region>\n" + + " </prod>\n" + + " <endpoints>\n" + + " <endpoint id=\"default\" container-id=\"qrs\"/>\n" + + " </endpoints>\n" + + " </instance>\n" + + "</deployment>\n"; ApplicationPackage applicationPackage = ApplicationPackageBuilder.fromDeploymentXml(deploymentXml); try { tester.newDeploymentContext().submit(applicationPackage); fail("Expected exception"); } catch (IllegalArgumentException e) { assertEquals("Endpoint with ID 'default' in instance 'dev' clashes with endpoint 'dev' in instance 'default'", - e.getMessage()); + e.getMessage()); } } @Test - public void testTestPackageWarnings() { + void testTestPackageWarnings() { String deploymentXml = "<deployment version='1.0'>\n" + - " <prod>\n" + - " <region>us-west-1</region>\n" + - " </prod>\n" + - "</deployment>\n"; + " <prod>\n" + + " <region>us-west-1</region>\n" + + " </prod>\n" + + "</deployment>\n"; ApplicationPackage applicationPackage = ApplicationPackageBuilder.fromDeploymentXml(deploymentXml); byte[] testPackage = ApplicationPackage.filesZip(Map.of("tests/staging-test/foo.json", new byte[0])); var app = tester.newDeploymentContext(); tester.jobs().submit(app.application().id(), Submission.basic(applicationPackage, testPackage), 1); assertEquals(List.of(new Notification(tester.clock().instant(), - Type.testPackage, - Level.warning, - NotificationSource.from(app.application().id()), - List.of("test package has staging tests, so it should also include staging setup", - "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"))), - tester.controller().notificationsDb().listNotifications(NotificationSource.from(app.application().id()), true)); + Type.testPackage, + Level.warning, + NotificationSource.from(app.application().id()), + List.of("test package has staging tests, so it should also include staging setup", + "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"))), + tester.controller().notificationsDb().listNotifications(NotificationSource.from(app.application().id()), true)); } @Test - public void testCompileVersion() { + void testCompileVersion() { DeploymentContext context = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder().region("us-west-1").build(); TenantAndApplicationId application = TenantAndApplicationId.from(context.instanceId()); @@ -1153,7 +1153,7 @@ public class ControllerTest { assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.of(8))); // Default major version is set to 8. - tester.applications().setTargetMajorVersion(Optional.of(8)); + tester.applications().setTargetMajorVersion(OptionalInt.of(8)); assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7))); assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty())); @@ -1166,10 +1166,27 @@ public class ControllerTest { tester.applications().lockApplicationOrThrow(application, locked -> tester.applications().store(locked.withMajorVersion(8))); assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7))); assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty())); + + // Application upgrades to major 8; only major version from deployment spec should cause a downgrade. + context.submit(new ApplicationPackageBuilder().region("us-west-1").compileVersion(version2).build()).deploy(); + tester.applications().setTargetMajorVersion(OptionalInt.empty()); + tester.applications().lockApplicationOrThrow(application, locked -> tester.applications().store(locked.withMajorVersion(null))); + assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7))); + assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty())); + + // Default major version across all apps should not cause a downgrade. + tester.applications().setTargetMajorVersion(OptionalInt.of(7)); + assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7))); + assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty())); + + // Default major version for specific app should not cause a downgrade. + tester.applications().lockApplicationOrThrow(application, locked -> tester.applications().store(locked.withMajorVersion(7))); + assertEquals(version1, tester.applications().compileVersion(application, OptionalInt.of(7))); + assertEquals(version2, tester.applications().compileVersion(application, OptionalInt.empty())); } @Test - public void testCloudAccount() { + void testCloudAccount() { DeploymentContext context = tester.newDeploymentContext(); ZoneId zone = ZoneId.from("prod", "us-west-1"); String cloudAccount = "012345678912"; @@ -1180,7 +1197,8 @@ public class ControllerTest { try { context.submit(applicationPackage).deploy(); fail("Expected exception"); // Account invalid for tenant - } catch (IllegalArgumentException ignored) {} + } catch (IllegalArgumentException ignored) { + } tester.controllerTester().flagSource().withListFlag(PermanentFlags.CLOUD_ACCOUNTS.id(), List.of(cloudAccount), String.class); context.submit(applicationPackage).deploy(); @@ -1188,7 +1206,7 @@ public class ControllerTest { } @Test - public void testSubmitWithElementDeprecatedOnPreviousMajor() { + void testSubmitWithElementDeprecatedOnPreviousMajor() { DeploymentContext context = tester.newDeploymentContext(); var applicationPackage = new ApplicationPackageBuilder() .compileVersion(Version.fromString("8.1")) 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 a2eefd47b56..10fd57ce032 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,9 +74,8 @@ 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; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Convenience methods for controller tests. 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 11b3ad0d45a..3163c8b6439 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 @@ -17,7 +17,7 @@ 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; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.file.Files; import java.nio.file.Paths; @@ -29,13 +29,13 @@ import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DeploymentQuotaCalculatorTest { @Test - public void quota_is_divided_among_prod_instances() { + void quota_is_divided_among_prod_instances() { Quota calculated = DeploymentQuotaCalculator.calculate(Quota.unlimited().withBudget(10), List.of(), ApplicationId.defaultId(), ZoneId.defaultId(), DeploymentSpec.fromXml( "<deployment version='1.0'>\n" + @@ -59,12 +59,12 @@ public class DeploymentQuotaCalculatorTest { } @Test - public void quota_is_divided_among_prod_and_manual_instances() { + void quota_is_divided_among_prod_and_manual_instances() { 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()), - RevisionId.forProduction(1), Version.emptyVersion, Instant.EPOCH, Map.of(), QuotaUsage.create(0.53d)))); + 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()), + 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( @@ -84,19 +84,19 @@ public class DeploymentQuotaCalculatorTest { } @Test - public void unlimited_quota_remains_unlimited() { + void unlimited_quota_remains_unlimited() { Quota calculated = DeploymentQuotaCalculator.calculate(Quota.unlimited(), List.of(), ApplicationId.defaultId(), ZoneId.defaultId(), DeploymentSpec.empty); assertTrue(calculated.isUnlimited()); } @Test - public void zero_quota_remains_zero() { + void zero_quota_remains_zero() { Quota calculated = DeploymentQuotaCalculator.calculate(Quota.zero(), List.of(), ApplicationId.defaultId(), ZoneId.defaultId(), DeploymentSpec.empty); assertEquals(calculated.budget().orElseThrow().doubleValue(), 0, 1e-5); } @Test - public void using_highest_resource_use() throws Exception { + void using_highest_resource_use() throws Exception { var content = new String(Files.readAllBytes(Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/application/response/application.json"))); var mapper = new ObjectMapper(); var application = mapper.readValue(content, ApplicationData.class).toApplication(); @@ -105,7 +105,7 @@ public class DeploymentQuotaCalculatorTest { } @Test - public void tenant_quota_in_pipeline() { + void tenant_quota_in_pipeline() { var tenantQuota = Quota.unlimited().withBudget(42); var calculated = DeploymentQuotaCalculator.calculate(tenantQuota, List.of(), ApplicationId.defaultId(), ZoneId.from("test", "apac1"), DeploymentSpec.empty); assertEquals(tenantQuota, calculated); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java index 81fc610d1f6..f3324e0c1f3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java @@ -8,12 +8,12 @@ import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -26,7 +26,7 @@ public class EndpointTest { private static final TenantAndApplicationId app2 = TenantAndApplicationId.from(instance2); @Test - public void global_endpoints() { + void global_endpoints() { DeploymentId deployment1 = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); DeploymentId deployment2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1")); ClusterSpec.Id cluster = ClusterSpec.Id.from("default"); @@ -72,7 +72,7 @@ public class EndpointTest { } @Test - public void global_endpoints_with_endpoint_id() { + void global_endpoints_with_endpoint_id() { DeploymentId deployment1 = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); DeploymentId deployment2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1")); ClusterSpec.Id cluster = ClusterSpec.Id.from("default"); @@ -114,7 +114,7 @@ public class EndpointTest { } @Test - public void zone_endpoints() { + void zone_endpoints() { var cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing var prodZone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); var prodZone2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1")); @@ -172,7 +172,7 @@ public class EndpointTest { } @Test - public void certificate_endpoints() { + void certificate_endpoints() { var defaultCluster = ClusterSpec.Id.from("default"); var prodZone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); var testZone = new DeploymentId(instance1, ZoneId.from("test", "us-north-2")); @@ -237,7 +237,7 @@ public class EndpointTest { } @Test - public void region_endpoints() { + void region_endpoints() { var cluster = ClusterSpec.Id.from("default"); var prodZone = ZoneId.from("prod", "us-north-2"); Map<String, Endpoint> tests = Map.of( @@ -268,42 +268,42 @@ public class EndpointTest { ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); - assertEquals("Availability zone is removed from region", - "aws-us-north-1", - tests.get("https://a1.t1.aws-us-north-1.w.vespa-app.cloud/").targets().get(0).deployment().zoneId().region().value()); - assertEquals("Availability zone is removed from region", - "gcp-us-south1", - tests.get("https://a1.t1.gcp-us-south1.w.vespa-app.cloud/").targets().get(0).deployment().zoneId().region().value()); + assertEquals("aws-us-north-1", + tests.get("https://a1.t1.aws-us-north-1.w.vespa-app.cloud/").targets().get(0).deployment().zoneId().region().value(), + "Availability zone is removed from region"); + assertEquals("gcp-us-south1", + tests.get("https://a1.t1.gcp-us-south1.w.vespa-app.cloud/").targets().get(0).deployment().zoneId().region().value(), + "Availability zone is removed from region"); } @Test - public void application_endpoints() { + void application_endpoints() { Map<String, Endpoint> tests = Map.of( "https://weighted.a1.t1.us-west-1.r.vespa-app.cloud/", Endpoint.of(app1) .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), - Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) + Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), "https://weighted.a1.t1.us-west-1.r.cd.vespa-app.cloud/", Endpoint.of(app1) .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), - Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) + Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.PublicCd), "https://a2.t2.us-east-3-r.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), - Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) + Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.main), "https://cd.a2.t2.us-east-3-r.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), - Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) + Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.cd) @@ -312,7 +312,7 @@ public class EndpointTest { } @Test - public void upstream_name() { + void upstream_name() { var zone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); var zone2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1")); var tests1 = Map.of( diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageDiffTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageDiffTest.java index 977304db3fb..a92c035b502 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageDiffTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageDiffTest.java @@ -1,7 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.application.pkg; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -14,7 +14,7 @@ import java.util.zip.ZipOutputStream; import static com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackageDiff.diff; import static com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackageDiff.diffAgainstEmpty; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author freva @@ -24,12 +24,12 @@ public class ApplicationPackageDiffTest { private static final ApplicationPackage app2 = applicationPackage(Map.of("file1", "updated contents\nof the\nfirst file\nafter some changes", "dir/myfile2", "Second file", "dir/binary", "øøøø")); @Test - public void no_diff() { + void no_diff() { assertEquals("No diff\n", new String(diff(app1, app1))); } @Test - public void diff_against_empty() { + void diff_against_empty() { assertEquals("--- dir/binary\n" + "Diff skipped: File is binary (new file -> 8B)\n" + "\n" + @@ -45,7 +45,7 @@ public class ApplicationPackageDiffTest { } @Test - public void full_diff() { + void full_diff() { // Even though dir/binary is binary file, we can see they are identical, so it should not print "Diff skipped" assertEquals("--- dir/myfile\n" + "@@ -1,1 +1,0 @@\n" + @@ -66,7 +66,7 @@ public class ApplicationPackageDiffTest { } @Test - public void skips_diff_for_too_large_files() { + void skips_diff_for_too_large_files() { assertEquals("--- dir/myfile\n" + "@@ -1,1 +1,0 @@\n" + "- Second file\n" + @@ -81,7 +81,7 @@ public class ApplicationPackageDiffTest { } @Test - public void skips_diff_if_file_diff_is_too_large() { + void skips_diff_if_file_diff_is_too_large() { assertEquals("--- dir/myfile\n" + "@@ -1,1 +1,0 @@\n" + "- Second file\n" + @@ -96,7 +96,7 @@ public class ApplicationPackageDiffTest { } @Test - public void skips_diff_if_total_diff_is_too_large() { + void skips_diff_if_total_diff_is_too_large() { assertEquals("--- dir/myfile\n" + "@@ -1,1 +1,0 @@\n" + "- Second file\n" + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java index 0458f77fc00..ce7fa5784e8 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ApplicationPackageTest.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.application.pkg; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.ValidationId; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.file.Files; import java.nio.file.Path; @@ -13,10 +13,10 @@ import java.util.Map; import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author valerijf @@ -55,7 +55,7 @@ public class ApplicationPackageTest { "</nodes>"; @Test - public void test_createEmptyForDeploymentRemoval() { + void test_createEmptyForDeploymentRemoval() { ApplicationPackage app = ApplicationPackage.deploymentRemoval(); assertEquals(DeploymentSpec.empty, app.deploymentSpec()); assertEquals(List.of(), app.trustedCertificates()); @@ -66,22 +66,22 @@ public class ApplicationPackageTest { } @Test - public void testMetaData() { + void testMetaData() { byte[] zip = ApplicationPackage.filesZip(Map.of("services.xml", servicesXml.getBytes(UTF_8), - "jdisc.xml", jdiscXml.getBytes(UTF_8), - "content/content.xml", contentXml.getBytes(UTF_8), - "content/nodes.xml", nodesXml.getBytes(UTF_8), - "gurba", "gurba".getBytes(UTF_8))); + "jdisc.xml", jdiscXml.getBytes(UTF_8), + "content/content.xml", contentXml.getBytes(UTF_8), + "content/nodes.xml", nodesXml.getBytes(UTF_8), + "gurba", "gurba".getBytes(UTF_8))); assertEquals(Map.of("services.xml", servicesXml, - "jdisc.xml", jdiscXml, - "content/content.xml", contentXml, - "content/nodes.xml", nodesXml), - unzip(new ApplicationPackage(zip, false).metaDataZip())); + "jdisc.xml", jdiscXml, + "content/content.xml", contentXml, + "content/nodes.xml", nodesXml), + unzip(new ApplicationPackage(zip, false).metaDataZip())); } @Test - public void testMetaDataWithMissingFiles() { + void testMetaDataWithMissingFiles() { byte[] zip = ApplicationPackage.filesZip(Map.of("services.xml", servicesXml.getBytes(UTF_8))); try { @@ -94,7 +94,7 @@ public class ApplicationPackageTest { } @Test - public void testAbsoluteInclude() throws Exception { + void testAbsoluteInclude() throws Exception { try { getApplicationZip("include-absolute.zip"); fail("Should fail on include file outside zip"); @@ -105,7 +105,7 @@ public class ApplicationPackageTest { } @Test - public void testParentInclude() throws Exception { + void testParentInclude() throws Exception { try { getApplicationZip("include-parent.zip"); fail("Should fail on include file outside zip"); @@ -116,7 +116,7 @@ public class ApplicationPackageTest { } @Test - public void testBundleHashesAreSameWithDifferentDeploymentXml() throws Exception { + void testBundleHashesAreSameWithDifferentDeploymentXml() throws Exception { var originalPackage = getApplicationZip("original.zip"); var changedServices = getApplicationZip("changed-services-xml.zip"); var changedDeploymentXml = getApplicationZip("changed-deployment-xml.zip"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/LinesComparatorTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/LinesComparatorTest.java index 92137094f62..8d9f88ca0ca 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/LinesComparatorTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/LinesComparatorTest.java @@ -1,12 +1,12 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.application.pkg; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Optional; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LinesComparatorTest { private static final String text1 = "This part of the\n" + @@ -64,7 +64,7 @@ public class LinesComparatorTest { "to this document."; @Test - public void diff_test() { + void diff_test() { assertDiff(null, "", ""); assertDiff(null, text1, text1); assertDiff(text1.lines().map(line -> "- " + line).collect(Collectors.joining("\n", "@@ -1,24 +1,0 @@\n", "\n")), text1, ""); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/TestPackageTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/TestPackageTest.java index 8588bb9ea16..5bc1c386134 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/TestPackageTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/TestPackageTest.java @@ -5,7 +5,7 @@ import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.pkg.TestPackage.TestSummary; import com.yahoo.vespa.hosted.controller.config.ControllerConfig; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -22,7 +22,7 @@ import static com.yahoo.vespa.hosted.controller.api.integration.deployment.Teste import static com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud.Suite.system; import static com.yahoo.vespa.hosted.controller.application.pkg.TestPackage.validateTests; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -75,65 +75,65 @@ public class TestPackageTest { } @Test - public void testBundleValidation() throws IOException { + void testBundleValidation() throws IOException { byte[] testZip = ApplicationPackage.filesZip(Map.of("components/foo-tests.jar", testsJar("SystemTest", "StagingSetup", "ProductionTest"), - "artifacts/key", new byte[0])); + "artifacts/key", new byte[0])); TestSummary summary = validateTests(List.of(system), testZip); assertEquals(List.of(system, staging_setup, production), summary.suites()); assertEquals(List.of("test package contains 'artifacts/key'; this conflicts with credentials used to run tests in Vespa Cloud", - "test package has staging setup, so it should also include staging tests", - "test package has production tests, but no production tests are declared in deployment.xml", - "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"), - summary.problems()); + "test package has staging setup, so it should also include staging tests", + "test package has production tests, but no production tests are declared in deployment.xml", + "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"), + summary.problems()); } @Test - public void testFatTestsValidation() { + void testFatTestsValidation() { byte[] testZip = ApplicationPackage.filesZip(Map.of("artifacts/foo-tests.jar", new byte[0])); TestSummary summary = validateTests(List.of(staging, production), testZip); assertEquals(List.of(staging, production), summary.suites()); assertEquals(List.of("test package has staging tests, so it should also include staging setup", - "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"), - summary.problems()); + "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"), + summary.problems()); } @Test - public void testBasicTestsValidation() { + void testBasicTestsValidation() { byte[] testZip = ApplicationPackage.filesZip(Map.of("tests/staging-test/foo.json", new byte[0], - "tests/staging-setup/foo.json", new byte[0])); + "tests/staging-setup/foo.json", new byte[0])); TestSummary summary = validateTests(List.of(system, production), testZip); assertEquals(List.of(staging_setup, staging), summary.suites()); assertEquals(List.of("test package has no system tests, but <test /> is declared in deployment.xml", - "test package has no production tests, but production tests are declared in deployment.xml", - "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"), - summary.problems()); + "test package has no production tests, but production tests are declared in deployment.xml", + "see https://docs.vespa.ai/en/testing.html for details on how to write system tests for Vespa"), + summary.problems()); } @Test - public void generates_correct_tester_flavor() { + void generates_correct_tester_flavor() { DeploymentSpec spec = DeploymentSpec.fromXml("<deployment version='1.0' athenz-domain='domain' athenz-service='service'>\n" + - " <instance id='first'>\n" + - " <test tester-flavor=\"d-6-16-100\" />\n" + - " <prod>\n" + - " <region active=\"true\">us-west-1</region>\n" + - " <test>us-west-1</test>\n" + - " </prod>\n" + - " </instance>\n" + - " <instance id='second'>\n" + - " <test />\n" + - " <staging />\n" + - " <prod tester-flavor=\"d-6-16-100\">\n" + - " <parallel>\n" + - " <region active=\"true\">us-east-3</region>\n" + - " <region active=\"true\">us-central-1</region>\n" + - " </parallel>\n" + - " <region active=\"true\">us-west-1</region>\n" + - " <test>us-west-1</test>\n" + - " </prod>\n" + - " </instance>\n" + - "</deployment>\n"); + " <instance id='first'>\n" + + " <test tester-flavor=\"d-6-16-100\" />\n" + + " <prod>\n" + + " <region active=\"true\">us-west-1</region>\n" + + " <test>us-west-1</test>\n" + + " </prod>\n" + + " </instance>\n" + + " <instance id='second'>\n" + + " <test />\n" + + " <staging />\n" + + " <prod tester-flavor=\"d-6-16-100\">\n" + + " <parallel>\n" + + " <region active=\"true\">us-east-3</region>\n" + + " <region active=\"true\">us-central-1</region>\n" + + " </parallel>\n" + + " <region active=\"true\">us-west-1</region>\n" + + " <test>us-west-1</test>\n" + + " </prod>\n" + + " </instance>\n" + + "</deployment>\n"); NodeResources firstResources = TestPackage.testerResourcesFor(ZoneId.from("prod", "us-west-1"), spec.requireInstance("first")); assertEquals(TestPackage.DEFAULT_TESTER_RESOURCES, firstResources); @@ -145,13 +145,13 @@ public class TestPackageTest { } @Test - public void generates_correct_services_xml() throws IOException { + void generates_correct_services_xml() throws IOException { assertEquals(Files.readString(Paths.get("src/test/resources/test_runner_services.xml-cd")), - new String(TestPackage.servicesXml(true, - false, - new NodeResources(2, 12, 75, 1, NodeResources.DiskSpeed.fast, NodeResources.StorageType.local), - new ControllerConfig.Steprunner.Testerapp.Builder().build()), - UTF_8)); + new String(TestPackage.servicesXml(true, + false, + new NodeResources(2, 12, 75, 1, NodeResources.DiskSpeed.fast, NodeResources.StorageType.local), + new ControllerConfig.Steprunner.Testerapp.Builder().build()), + UTF_8)); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ZipEntriesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ZipEntriesTest.java index 6908464640b..37062e1002b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ZipEntriesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/pkg/ZipEntriesTest.java @@ -5,7 +5,7 @@ import com.yahoo.security.KeyAlgorithm; import com.yahoo.security.KeyUtils; import com.yahoo.security.SignatureAlgorithm; import com.yahoo.security.X509CertificateBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.security.auth.x500.X500Principal; import java.math.BigInteger; @@ -16,7 +16,7 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -24,22 +24,22 @@ import static org.junit.Assert.assertEquals; public class ZipEntriesTest { @Test - public void test_replacement() { + void test_replacement() { ApplicationPackage applicationPackage = new ApplicationPackage(new byte[0]); List<X509Certificate> certificates = IntStream.range(0, 3) - .mapToObj(i -> { - KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256); - X500Principal subject = new X500Principal("CN=subject" + i); - return X509CertificateBuilder.fromKeypair(keyPair, - subject, - Instant.now(), - Instant.now().plusSeconds(1), - SignatureAlgorithm.SHA512_WITH_ECDSA, - BigInteger.valueOf(1)) - .build(); - }) - .collect(Collectors.toUnmodifiableList()); - + .mapToObj(i -> { + KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256); + X500Principal subject = new X500Principal("CN=subject" + i); + return X509CertificateBuilder.fromKeypair(keyPair, + subject, + Instant.now(), + Instant.now().plusSeconds(1), + SignatureAlgorithm.SHA512_WITH_ECDSA, + BigInteger.valueOf(1)) + .build(); + }) + .collect(Collectors.toUnmodifiableList()); + assertEquals(List.of(), applicationPackage.trustedCertificates()); for (int i = 0; i < certificates.size(); i++) { applicationPackage = applicationPackage.withTrustedCertificate(certificates.get(i)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java index 4c3d76b1b17..081056e5184 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDbTest.java @@ -7,7 +7,7 @@ import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveBucket; import org.apache.curator.shaded.com.google.common.collect.Streams; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URI; import java.util.Optional; @@ -16,12 +16,12 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class CuratorArchiveBucketDbTest { @Test - public void archiveUriFor() { + void archiveUriFor() { ControllerTester tester = new ControllerTester(SystemName.Public); CuratorArchiveBucketDb bucketDb = new CuratorArchiveBucketDb(tester.controller()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java index 7a209b105b3..5c5abea0276 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLoggerTest.java @@ -5,7 +5,7 @@ import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.jdisc.http.HttpRequest.Method; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.auditlog.AuditLog.Entry; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.net.URI; @@ -15,8 +15,8 @@ import java.time.Instant; import java.util.function.Supplier; import static java.time.temporal.ChronoUnit.MILLIS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -27,11 +27,11 @@ public class AuditLoggerTest { private final Supplier<AuditLog> log = () -> tester.controller().auditLogger().readLog(); @Test - public void test_logging() { + void test_logging() { { // GET request is ignored HttpRequest request = testRequest(Method.GET, URI.create("http://localhost:8080/os/v1/"), ""); tester.controller().auditLogger().log(request); - assertTrue("Not logged", log.get().entries().isEmpty()); + assertTrue(log.get().entries().isEmpty(), "Not logged"); } { // PATCH request is logged in audit log @@ -47,7 +47,7 @@ public class AuditLoggerTest { { // Another PATCH request is logged tester.clock().advance(Duration.ofDays(1)); HttpRequest request = testRequest(Method.PATCH, URI.create("http://localhost:8080/os/v1/"), - "{\"cloud\":\"cloud9\",\"version\":\"43.0\"}"); + "{\"cloud\":\"cloud9\",\"version\":\"43.0\"}"); tester.controller().auditLogger().log(request); assertEntry(Entry.Method.PATCH, 2, "/os/v1/"); } @@ -55,7 +55,7 @@ public class AuditLoggerTest { { // PUT is logged tester.clock().advance(Duration.ofDays(1)); HttpRequest request = testRequest(Method.PUT, URI.create("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/state/dirty/node1/"), - ""); + ""); tester.controller().auditLogger().log(request); assertEntry(Entry.Method.PUT, 3, "/zone/v2/prod/us-north-1/nodes/v2/state/dirty/node1/"); } @@ -63,7 +63,7 @@ public class AuditLoggerTest { { // DELETE is logged tester.clock().advance(Duration.ofDays(1)); HttpRequest request = testRequest(Method.DELETE, URI.create("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/node/node1"), - ""); + ""); tester.controller().auditLogger().log(request); assertEntry(Entry.Method.DELETE, 4, "/zone/v2/prod/us-north-1/nodes/v2/node/node1"); } @@ -71,7 +71,7 @@ public class AuditLoggerTest { { // POST is logged tester.clock().advance(Duration.ofDays(1)); HttpRequest request = testRequest(Method.POST, URI.create("http://localhost:8080/controller/v1/jobs/upgrader/confidence/6.42"), - "6.42"); + "6.42"); tester.controller().auditLogger().log(request); assertEntry(Entry.Method.POST, 5, "/controller/v1/jobs/upgrader/confidence/6.42"); } @@ -79,7 +79,7 @@ public class AuditLoggerTest { { // 15 days pass and another PATCH request is logged. Older entries are removed due to expiry tester.clock().advance(Duration.ofDays(15)); HttpRequest request = testRequest(Method.PATCH, URI.create("http://localhost:8080/os/v1/"), - "{\"cloud\":\"cloud9\",\"version\":\"44.0\"}"); + "{\"cloud\":\"cloud9\",\"version\":\"44.0\"}"); tester.controller().auditLogger().log(request); assertEntry(Entry.Method.PATCH, 1, "/os/v1/"); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java index 1691f902358..9a2d9eddba7 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java @@ -25,8 +25,8 @@ import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import javax.security.auth.x500.X500Principal; import java.security.KeyPair; @@ -39,9 +39,9 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author andreer @@ -105,7 +105,7 @@ public class EndpointCertificatesTest { private final String testCertName = "testCertName"; private ZoneId testZone; - @Before + @BeforeEach public void setUp() { tester.zoneRegistry().exclusiveRoutingIn(tester.zoneRegistry().zones().all().zones()); testZone = tester.zoneRegistry().zones().all().routingMethod(RoutingMethod.exclusive).in(Environment.prod).zones().stream().findFirst().orElseThrow().getId(); @@ -115,7 +115,7 @@ public class EndpointCertificatesTest { } @Test - public void provisions_new_certificate_in_dev() { + void provisions_new_certificate_in_dev() { ZoneId testZone = tester.zoneRegistry().zones().all().routingMethod(RoutingMethod.exclusive).in(Environment.dev).zones().stream().findFirst().orElseThrow().getId(); Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty); assertTrue(endpointCertificateMetadata.isPresent()); @@ -126,7 +126,7 @@ public class EndpointCertificatesTest { } @Test - public void provisions_new_certificate_in_prod() { + void provisions_new_certificate_in_prod() { Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, DeploymentSpec.empty); assertTrue(endpointCertificateMetadata.isPresent()); assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.default.default.*-key")); @@ -136,7 +136,7 @@ public class EndpointCertificatesTest { } @Test - public void provisions_new_certificate_in_public_prod() { + void provisions_new_certificate_in_public_prod() { ControllerTester tester = new ControllerTester(SystemName.Public); EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock); EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator); @@ -160,7 +160,7 @@ public class EndpointCertificatesTest { } @Test - public void reuses_stored_certificate_metadata() { + void reuses_stored_certificate_metadata() { mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, 7, 0, "request_id", Optional.of("leaf-request-uuid"), List.of("vt2ktgkqme5zlnp4tj4ttyor7fj3v7q5o.vespa.oath.cloud", "default.default.global.vespa.oath.cloud", @@ -178,7 +178,7 @@ public class EndpointCertificatesTest { } @Test - public void reprovisions_certificate_when_necessary() { + void reprovisions_certificate_when_necessary() { mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "root-request-uuid", Optional.of("leaf-request-uuid"), List.of(), "issuer", Optional.empty(), Optional.empty())); secretStore.setSecret("vespa.tls.default.default.default-key", KeyUtils.toPem(testKeyPair.getPrivate()), 0); secretStore.setSecret("vespa.tls.default.default.default-cert", X509CertificateUtils.toPem(testCertificate) + X509CertificateUtils.toPem(testCertificate), 0); @@ -189,7 +189,7 @@ public class EndpointCertificatesTest { } @Test - public void reprovisions_certificate_with_added_sans_when_deploying_to_new_zone() { + void reprovisions_certificate_with_added_sans_when_deploying_to_new_zone() { ZoneId testZone = tester.zoneRegistry().zones().all().routingMethod(RoutingMethod.exclusive).in(Environment.prod).zones().stream().skip(1).findFirst().orElseThrow().getId(); mockCuratorDb.writeEndpointCertificateMetadata(testInstance.id(), new EndpointCertificateMetadata(testKeyName, testCertName, -1, 0, "original-request-uuid", Optional.of("leaf-request-uuid"), expectedSans, "mockCa", Optional.empty(), Optional.empty())); @@ -209,16 +209,19 @@ public class EndpointCertificatesTest { } @Test - public void includes_zones_in_deployment_spec_when_deploying_to_staging() { + void includes_zones_in_deployment_spec_when_deploying_to_staging() { DeploymentSpec deploymentSpec = new DeploymentSpecXmlReader(true).read( - "<deployment version=\"1.0\">\n" + - " <instance id=\"default\">\n" + - " <prod>\n" + - " <region active=\"true\">aws-us-east-1a</region>\n" + - " <region active=\"true\">ap-northeast-1</region>\n" + - " </prod>\n" + - " </instance>\n" + - "</deployment>\n"); + """ + <deployment version="1.0"> + <instance id="default"> + <prod> + <region active="true">aws-us-east-1a</region> + <region active="true">ap-northeast-1</region> + </prod> + </instance> + </deployment> + """ + ); ZoneId testZone = tester.zoneRegistry().zones().all().in(Environment.staging).zones().stream().findFirst().orElseThrow().getId(); Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(testInstance, testZone, deploymentSpec); @@ -230,7 +233,7 @@ public class EndpointCertificatesTest { } @Test - public void includes_application_endpoint_when_declared() { + void includes_application_endpoint_when_declared() { Instance instance = new Instance(ApplicationId.from("t1", "a1", "default")); ZoneId zone1 = ZoneId.from(Environment.prod, RegionName.from("aws-us-east-1c")); ZoneId zone2 = ZoneId.from(Environment.prod, RegionName.from("aws-us-west-2a")); @@ -239,14 +242,14 @@ public class EndpointCertificatesTest { .region(zone1.region()) .region(zone2.region()) .applicationEndpoint("a", "qrs", zone2.region().value(), - Map.of(InstanceName.from("beta"), 2, - InstanceName.from("main"), 8)) + Map.of(InstanceName.from("beta"), 2, + InstanceName.from("main"), 8)) .applicationEndpoint("b", "qrs", zone2.region().value(), - Map.of(InstanceName.from("beta"), 1, - InstanceName.from("main"), 1)) + Map.of(InstanceName.from("beta"), 1, + InstanceName.from("main"), 1)) .applicationEndpoint("c", "qrs", zone1.region().value(), - Map.of(InstanceName.from("beta"), 4, - InstanceName.from("main"), 6)) + Map.of(InstanceName.from("beta"), 4, + InstanceName.from("main"), 6)) .build(); ControllerTester tester = new ControllerTester(SystemName.Public); EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/concurrent/OnceTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/concurrent/OnceTest.java index e9426e96a80..c1a8f77f2e9 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/concurrent/OnceTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/concurrent/OnceTest.java @@ -1,21 +1,23 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.concurrent; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden */ public class OnceTest { - @Test(timeout = 60_000) - public void test_run() throws Exception { + @Test + @Timeout(60_000) + void test_run() throws Exception { CountDownLatch latch = new CountDownLatch(1); Once.after(Duration.ZERO, latch::countDown); 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 ad236fcd4de..934c6b07c29 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 @@ -59,11 +59,11 @@ import java.util.function.Supplier; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * A deployment context for an application. This allows fine-grained control of the deployment of an application's @@ -194,10 +194,10 @@ public class DeploymentContext { /** Completely deploy the current change */ public DeploymentContext deploy() { Application application = application(); - assertTrue("Application package submitted", application.revisions().last().isPresent()); - assertFalse("Submission is not already deployed", application.instances().values().stream() + assertTrue(application.revisions().last().isPresent(), "Application package submitted"); + assertFalse(application.instances().values().stream() .anyMatch(instance -> instance.deployments().values().stream() - .anyMatch(deployment -> deployment.revision().equals(lastSubmission)))); + .anyMatch(deployment -> deployment.revision().equals(lastSubmission))), "Submission is not already deployed"); completeRollout(application.deploymentSpec().instances().size() > 1); for (var instance : application().instances().values()) { assertFalse(instance.change().hasTargets()); @@ -249,8 +249,8 @@ public class DeploymentContext { /** Flush all pending DNS updates */ public DeploymentContext flushDnsUpdates() { flushDnsUpdates(Integer.MAX_VALUE); - assertTrue("All name service requests dispatched", - tester.controller().curator().readNameServiceQueue().requests().isEmpty()); + assertTrue(tester.controller().curator().readNameServiceQueue().requests().isEmpty(), + "All name service requests dispatched"); return this; } @@ -362,12 +362,12 @@ public class DeploymentContext { .filter(kv -> kv.getValue() == failed) .map(Entry::getKey) .findFirst(); - assertTrue("Found failing step", firstFailing.isPresent()); + assertTrue(firstFailing.isPresent(), "Found failing step"); Optional<RunLog> details = jobs.details(id); - assertTrue("Found log entries for run " + id, details.isPresent()); - assertTrue("Found log message containing '" + messagePart.get() + "'", - details.get().get(firstFailing.get()).stream() - .anyMatch(entry -> entry.message().contains(messagePart.get()))); + assertTrue(details.isPresent(), "Found log entries for run " + id); + assertTrue(details.get().get(firstFailing.get()).stream() + .anyMatch(entry -> entry.message().contains(messagePart.get())), + "Found log message containing '" + messagePart.get() + "'"); } return this; } @@ -400,7 +400,7 @@ public class DeploymentContext { triggerJobs(); } - assertFalse("Change should have no targets, but was " + instance().change(), instance().change().hasTargets()); + assertFalse(instance().change().hasTargets(), "Change should have no targets, but was " + instance().change()); return this; } @@ -545,13 +545,13 @@ public class DeploymentContext { } public void assertRunning(JobType type) { - assertTrue(jobId(type) + " should be among the active: " + jobs.active(), - jobs.active().stream().anyMatch(run -> run.id().application().equals(instanceId) && run.id().type().equals(type))); + assertTrue(jobs.active().stream().anyMatch(run -> run.id().application().equals(instanceId) && run.id().type().equals(type)), + jobId(type) + " should be among the active: " + jobs.active()); } public void assertNotRunning(JobType type) { - assertFalse(jobId(type) + " should not be among the active: " + jobs.active(), - jobs.active().stream().anyMatch(run -> run.id().application().equals(instanceId) && run.id().type().equals(type))); + assertFalse(jobs.active().stream().anyMatch(run -> run.id().application().equals(instanceId) && run.id().type().equals(type)), + jobId(type) + " should not be among the active: " + jobs.active()); } /** Deploys tester and real app, and completes tester and initial staging installation first if needed. */ @@ -604,8 +604,8 @@ public class DeploymentContext { Run run = jobs.last(job) .filter(r -> r.id().type().equals(job.type())) .orElseThrow(() -> new AssertionError(job.type() + " is not among the active: " + jobs.active())); - assertFalse(run.id() + " should not have failed yet: " + run, run.hasFailed()); - assertFalse(run.id() + " should not have ended yet: " + run, run.hasEnded()); + assertFalse(run.hasFailed(), run.id() + " should not have failed yet: " + run); + assertFalse(run.hasEnded(), run.id() + " should not have ended yet: " + run); return run; } @@ -622,7 +622,7 @@ public class DeploymentContext { assertTrue(jobs.run(id).hasEnded()); return; } - assertEquals("Status of " + id, succeeded, jobs.run(id).stepStatuses().get(Step.installReal)); + assertEquals(succeeded, jobs.run(id).stepStatuses().get(Step.installReal), "Status of " + id); } /** Installs tester and starts tests. */ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java index 2b4a2baa17e..c9553ef5bf1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java @@ -27,7 +27,7 @@ import java.time.temporal.TemporalAdjusters; import java.util.logging.Level; import java.util.logging.Logger; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author jonmv 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 e5000a85f71..62fafa12993 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 @@ -24,8 +24,7 @@ import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -61,10 +60,11 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.tes import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.ALL; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PLATFORM; import static java.util.Collections.emptyList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests a wide variety of deployment scenarios and configurations @@ -78,7 +78,7 @@ public class DeploymentTriggerTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void testTriggerFailing() { + void testTriggerFailing() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .upgradePolicy("default") .region("us-west-1") @@ -95,7 +95,7 @@ public class DeploymentTriggerTest { // staging-test fails deployment and is retried app.failDeployment(stagingTest); tester.triggerJobs(); - assertEquals("Retried dead job", 2, tester.jobs().active().size()); + assertEquals(2, tester.jobs().active().size(), "Retried dead job"); app.assertRunning(stagingTest); app.runJob(stagingTest); @@ -106,7 +106,7 @@ public class DeploymentTriggerTest { // system-test fails and is retried app.timeOutUpgrade(systemTest); tester.triggerJobs(); - assertEquals("Job is retried on failure", 1, tester.jobs().active().size()); + assertEquals(1, tester.jobs().active().size(), "Job is retried on failure"); app.runJob(systemTest); tester.triggerJobs(); @@ -117,11 +117,11 @@ public class DeploymentTriggerTest { tester.applications().store(locked.withProjectId(OptionalLong.empty()))); app.timeOutConvergence(productionUsWest1); tester.triggerJobs(); - assertEquals("Job is not triggered when no projectId is present", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "Job is not triggered when no projectId is present"); } @Test - public void revisionChangeWhenFailingMakesApplicationChangeWaitForPreviousToComplete() { + void revisionChangeWhenFailingMakesApplicationChangeWaitForPreviousToComplete() { DeploymentContext app = tester.newDeploymentContext(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .revisionChange(null) // separate by default, but we override this in test builder @@ -155,10 +155,10 @@ public class DeploymentTriggerTest { } @Test - public void leadingUpgradeAllowsApplicationChangeWhileUpgrading() { + void leadingUpgradeAllowsApplicationChangeWhileUpgrading() { var applicationPackage = new ApplicationPackageBuilder().region("us-east-3") - .upgradeRollout("leading") - .build(); + .upgradeRollout("leading") + .build(); var app = tester.newDeploymentContext(); app.submit(applicationPackage).deploy(); @@ -176,11 +176,11 @@ public class DeploymentTriggerTest { } @Test - public void abortsJobsOnNewApplicationChange() { + void abortsJobsOnNewApplicationChange() { var app = tester.newDeploymentContext(); app.submit() - .runJob(systemTest) - .runJob(stagingTest); + .runJob(systemTest) + .runJob(stagingTest); tester.triggerJobs(); RunId id = tester.jobs().last(app.instanceId(), productionUsCentral1).get().id(); @@ -220,12 +220,12 @@ public class DeploymentTriggerTest { tester.triggerJobs(); tester.runner().run(); assertEquals(Set.of(productionUsCentral1), tester.jobs().active().stream() - .map(run -> run.id().type()) - .collect(Collectors.toCollection(HashSet::new))); + .map(run -> run.id().type()) + .collect(Collectors.toCollection(HashSet::new))); } @Test - public void similarDeploymentSpecsAreNotRolledOut() { + void similarDeploymentSpecsAreNotRolledOut() { ApplicationPackage firstPackage = new ApplicationPackageBuilder() .region("us-east-3") .build(); @@ -234,8 +234,8 @@ public class DeploymentTriggerTest { var version = app.lastSubmission(); assertEquals(version, app.instance().change().revision()); app.runJob(systemTest) - .runJob(stagingTest) - .runJob(productionUsEast3); + .runJob(stagingTest) + .runJob(productionUsEast3); assertEquals(Change.empty(), app.instance().change()); // A similar application package is submitted. Since a new job is added, the original revision is again a target. @@ -265,13 +265,13 @@ public class DeploymentTriggerTest { } @Test - public void testOutstandingChangeWithNextRevisionTarget() { + void testOutstandingChangeWithNextRevisionTarget() { ApplicationPackage appPackage = new ApplicationPackageBuilder().revisionTarget("next") - .revisionChange("when-failing") - .region("us-east-3") - .build(); + .revisionChange("when-failing") + .region("us-east-3") + .build(); DeploymentContext app = tester.newDeploymentContext() - .submit(appPackage); + .submit(appPackage); Optional<RevisionId> revision1 = app.lastSubmission(); app.submit(appPackage); @@ -299,7 +299,7 @@ public class DeploymentTriggerTest { // The second revision deploys completely, and the third starts rolling out. app.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3); + .runJob(productionUsEast3); tester.outstandingChangeDeployer().run(); tester.outstandingChangeDeployer().run(); assertEquals(revision3, app.instance().change().revision()); @@ -314,8 +314,8 @@ public class DeploymentTriggerTest { // Tests for outstanding change are relevant when current revision completes. app.runJob(systemTest).runJob(systemTest) - .jobAborted(stagingTest).runJob(stagingTest).runJob(stagingTest) - .runJob(productionUsEast3); + .jobAborted(stagingTest).runJob(stagingTest).runJob(stagingTest) + .runJob(productionUsEast3); tester.outstandingChangeDeployer().run(); tester.outstandingChangeDeployer().run(); assertEquals(revision5, app.instance().change().revision()); @@ -324,7 +324,7 @@ public class DeploymentTriggerTest { } @Test - public void deploymentSpecWithDelays() { + void deploymentSpecWithDelays() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .systemTest() .delay(Duration.ofSeconds(30)) @@ -362,27 +362,27 @@ public class DeploymentTriggerTest { // Delayed trigger does nothing as not enough time has passed after us-west-1 completion tester.triggerJobs(); - assertTrue("No more jobs triggered at this time", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "No more jobs triggered at this time"); // 3 minutes pass, us-central-1 is still not triggered tester.clock().advance(Duration.ofMinutes(3)); tester.triggerJobs(); - assertTrue("No more jobs triggered at this time", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "No more jobs triggered at this time"); // 4 minutes pass, us-central-1 is triggered tester.clock().advance(Duration.ofMinutes(1)); tester.triggerJobs(); app.runJob(productionUsCentral1); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); // Delayed trigger job runs again, with nothing to trigger tester.clock().advance(Duration.ofMinutes(10)); tester.triggerJobs(); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); } @Test - public void deploymentSpecWithParallelDeployments() { + void deploymentSpecWithParallelDeployments() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-central-1") .parallel("us-west-1", "us-east-3") @@ -415,11 +415,11 @@ public class DeploymentTriggerTest { tester.triggerJobs(); assertEquals(1, tester.jobs().active().size()); app.runJob(productionEuWest1); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); } @Test - public void testNoOtherChangesDuringSuspension() { + void testNoOtherChangesDuringSuspension() { // Application is deployed in 3 regions: ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-central-1") @@ -432,9 +432,9 @@ public class DeploymentTriggerTest { // A new change needs to be pushed out, but should not go beyond the suspended zone: application.submit() - .runJob(systemTest) - .runJob(stagingTest) - .runJob(productionUsCentral1); + .runJob(systemTest) + .runJob(stagingTest) + .runJob(productionUsCentral1); tester.triggerJobs(); application.assertNotRunning(productionUsEast3); application.assertNotRunning(productionUsWest1); @@ -447,7 +447,7 @@ public class DeploymentTriggerTest { } @Test - public void testBlockRevisionChange() { + void testBlockRevisionChange() { // Tuesday, 17:30 tester.at(Instant.parse("2017-09-26T17:30:00.00Z")); @@ -490,7 +490,7 @@ public class DeploymentTriggerTest { } @Test - public void testCompletionOfPartOfChangeDuringBlockWindow() { + void testCompletionOfPartOfChangeDuringBlockWindow() { // Tuesday, 17:30 tester.at(Instant.parse("2017-09-26T17:30:00.00Z")); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() @@ -538,7 +538,7 @@ public class DeploymentTriggerTest { } @Test - public void testJobPause() { + void testJobPause() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") .region("us-east-3") @@ -548,9 +548,9 @@ public class DeploymentTriggerTest { tester.upgrader().maintain(); tester.deploymentTrigger().pauseJob(app.instanceId(), productionUsWest1, - tester.clock().instant().plus(Duration.ofSeconds(1))); + tester.clock().instant().plus(Duration.ofSeconds(1))); tester.deploymentTrigger().pauseJob(app.instanceId(), productionUsEast3, - tester.clock().instant().plus(Duration.ofSeconds(3))); + tester.clock().instant().plus(Duration.ofSeconds(3))); // us-west-1 does not trigger when paused. app.runJob(systemTest).runJob(stagingTest); @@ -577,11 +577,11 @@ public class DeploymentTriggerTest { app.assertRunning(productionUsEast3); assertFalse(app.instance().jobPause(productionUsEast3).isPresent()); assertEquals(app.deployment(productionUsEast3.zone()).version(), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform()); } @Test - public void applicationVersionIsNotDowngraded() { + void applicationVersionIsNotDowngraded() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-central-1") .region("eu-west-1") @@ -590,9 +590,9 @@ public class DeploymentTriggerTest { // productionUsCentral1 fails after deployment, causing a mismatch between deployed and successful state. app.submit(applicationPackage) - .runJob(systemTest) - .runJob(stagingTest) - .timeOutUpgrade(productionUsCentral1); + .runJob(systemTest) + .runJob(stagingTest) + .timeOutUpgrade(productionUsCentral1); RevisionId appVersion1 = app.lastSubmission().get(); assertEquals(appVersion1, app.deployment(ZoneId.from("prod.us-central-1")).revision()); @@ -628,7 +628,7 @@ public class DeploymentTriggerTest { } @Test - public void downgradingApplicationVersionWorks() { + void downgradingApplicationVersionWorks() { var app = tester.newDeploymentContext().submit().deploy(); RevisionId appVersion0 = app.lastSubmission().get(); assertEquals(appVersion0, latestDeployed(app.instance())); @@ -641,16 +641,16 @@ public class DeploymentTriggerTest { tester.deploymentTrigger().forceChange(app.instanceId(), Change.of(appVersion0)); assertEquals(Change.of(appVersion0), app.instance().change()); app.runJob(stagingTest) - .runJob(productionUsCentral1) - .runJob(productionUsEast3) - .runJob(productionUsWest1); + .runJob(productionUsCentral1) + .runJob(productionUsEast3) + .runJob(productionUsWest1); assertEquals(Change.empty(), app.instance().change()); assertEquals(appVersion0, app.instance().deployments().get(productionUsEast3.zone()).revision()); assertEquals(appVersion0, latestDeployed(app.instance())); } @Test - public void settingANoOpChangeIsANoOp() { + void settingANoOpChangeIsANoOp() { var app = tester.newDeploymentContext().submit(); app.deploy(); @@ -669,7 +669,7 @@ public class DeploymentTriggerTest { } @Test - public void stepIsCompletePreciselyWhenItShouldBe() { + void stepIsCompletePreciselyWhenItShouldBe() { var app1 = tester.newDeploymentContext("tenant1", "app1", "default"); var app2 = tester.newDeploymentContext("tenant1", "app2", "default"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() @@ -706,11 +706,11 @@ public class DeploymentTriggerTest { tester.upgrader().overrideConfidence(version2, VespaVersion.Confidence.broken); tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); // Cancel upgrades to broken version - assertEquals("Change becomes latest non-broken version", Change.of(version1), app1.instance().change()); + assertEquals(Change.of(version1), app1.instance().change(), "Change becomes latest non-broken version"); // version1 proceeds 'til the last job, where it fails; us-central-1 is skipped, as current change is strictly dominated by what's deployed there. app1.runJob(systemTest).runJob(stagingTest) - .failDeployment(productionEuWest1); + .failDeployment(productionEuWest1); 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. @@ -718,9 +718,9 @@ public class DeploymentTriggerTest { app1.submit(applicationPackage); RevisionId revision2 = app1.lastSubmission().get(); app1.runJob(systemTest) // Tests for new revision on version2 - .runJob(stagingTest) - .runJob(systemTest) // Tests for new revision on version1 - .runJob(stagingTest); + .runJob(stagingTest) + .runJob(systemTest) // Tests for new revision on version1 + .runJob(stagingTest); assertEquals(Change.of(version1).with(revision2), app1.instance().change()); tester.triggerJobs(); app1.assertRunning(productionUsCentral1); @@ -741,14 +741,14 @@ public class DeploymentTriggerTest { // Last job has a different deployment target, so tests need to run again. app1.runJob(productionEuWest1) // Upgrade completes, and revision is the only change. - .runJob(productionUsCentral1) // With only revision change, central should run to cover a previous failure. - .runJob(productionEuWest1); // Finally, west changes revision. + .runJob(productionUsCentral1) // With only revision change, central should run to cover a previous failure. + .runJob(productionEuWest1); // Finally, west changes revision. assertEquals(Change.empty(), app1.instance().change()); assertEquals(Optional.of(RunStatus.success), app1.instanceJobs().get(productionUsCentral1).lastStatus()); } @Test - public void eachParallelDeployTargetIsTested() { + void eachParallelDeployTargetIsTested() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .parallel("eu-west-1", "us-east-3") .build(); @@ -788,14 +788,14 @@ public class DeploymentTriggerTest { // Both jobs fail again, and must be re-triggered -- this is ok, as they are both already triggered on their current targets. app.failDeployment(productionEuWest1).failDeployment(productionUsEast3) - .runJob(productionEuWest1).runJob(productionUsEast3); + .runJob(productionEuWest1).runJob(productionUsEast3); assertFalse(app.instance().change().hasTargets()); assertEquals(2, app.instanceJobs().get(productionEuWest1).lastSuccess().get().versions().targetRevision().number()); assertEquals(2, app.instanceJobs().get(productionUsEast3).lastSuccess().get().versions().targetRevision().number()); } @Test - public void retriesFailingJobs() { + void retriesFailingJobs() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-central-1") .build(); @@ -840,11 +840,11 @@ public class DeploymentTriggerTest { // Another application change is deployed and fixes system-test. Change is triggered immediately as target changes app.submit(applicationPackage).deploy(); - assertTrue("Deployment completed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "Deployment completed"); } @Test - public void testPlatformVersionSelection() { + void testPlatformVersionSelection() { // Setup system ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -855,7 +855,7 @@ public class DeploymentTriggerTest { // First deployment: An application change app1.submit(applicationPackage).deploy(); - assertEquals("First deployment gets system version", version1, app1.application().oldestDeployedPlatform().get()); + assertEquals(version1, app1.application().oldestDeployedPlatform().get(), "First deployment gets system version"); assertEquals(version1, tester.configServer().lastPrepareVersion().get()); // Application change after a new system version, and a region added @@ -867,20 +867,19 @@ public class DeploymentTriggerTest { .region("us-east-3") .build(); app1.submit(applicationPackage).deploy(); - assertEquals("Application change preserves version, and new region gets oldest version too", - version1, app1.application().oldestDeployedPlatform().get()); + assertEquals(version1, app1.application().oldestDeployedPlatform().get(), "Application change preserves version, and new region gets oldest version too"); assertEquals(version1, tester.configServer().lastPrepareVersion().get()); - assertFalse("Change deployed", app1.instance().change().hasTargets()); + assertFalse(app1.instance().change().hasTargets(), "Change deployed"); tester.upgrader().maintain(); app1.deployPlatform(version2); - assertEquals("Version upgrade changes version", version2, app1.application().oldestDeployedPlatform().get()); + assertEquals(version2, app1.application().oldestDeployedPlatform().get(), "Version upgrade changes version"); assertEquals(version2, tester.configServer().lastPrepareVersion().get()); } @Test - public void requeueNodeAllocationFailureStagingJob() { + void requeueNodeAllocationFailureStagingJob() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-east-3") .build(); @@ -979,14 +978,14 @@ public class DeploymentTriggerTest { } @Test - public void testUserInstancesNotInDeploymentSpec() { + void testUserInstancesNotInDeploymentSpec() { var app = tester.newDeploymentContext(); tester.controller().applications().createInstance(app.application().id().instance("user")); app.submit().deploy(); } @Test - public void testMultipleInstancesWithDifferentChanges() { + void testMultipleInstancesWithDifferentChanges() { DeploymentContext i1 = tester.newDeploymentContext("t", "a", "i1"); DeploymentContext i2 = tester.newDeploymentContext("t", "a", "i2"); DeploymentContext i3 = tester.newDeploymentContext("t", "a", "i3"); @@ -1097,7 +1096,7 @@ public class DeploymentTriggerTest { } @Test - public void testMultipleInstancesWithRevisionCatchingUpToUpgrade() { + void testMultipleInstancesWithRevisionCatchingUpToUpgrade() { String spec = """ <deployment> <instance id='alpha'> @@ -1160,28 +1159,28 @@ public class DeploymentTriggerTest { beta.assertNotRunning(testUsEast3); beta.runJob(productionUsEast3) - .runJob(testUsEast3); + .runJob(testUsEast3); assertEquals(Change.empty(), beta.instance().change()); } @Test - public void testMultipleInstances() { + void testMultipleInstances() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .instances("instance1,instance2") .region("us-east-3") .build(); var app = tester.newDeploymentContext("tenant1", "application1", "instance1") - .submit(applicationPackage) - .completeRollout(); + .submit(applicationPackage) + .completeRollout(); assertEquals(2, app.application().instances().size()); assertEquals(2, app.application().productionDeployments().values().stream() - .mapToInt(Collection::size) - .sum()); + .mapToInt(Collection::size) + .sum()); } @Test - public void testDeclaredProductionTests() { + void testDeclaredProductionTests() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-east-3") .delay(Duration.ofMinutes(1)) @@ -1198,8 +1197,8 @@ public class DeploymentTriggerTest { tester.clock().advance(Duration.ofMinutes(1)); app.runJob(testUsEast3) - .runJob(productionUsWest1).runJob(productionUsCentral1) - .runJob(testUsCentral1).runJob(testUsWest1); + .runJob(productionUsWest1).runJob(productionUsCentral1) + .runJob(testUsCentral1).runJob(testUsWest1); assertEquals(Change.empty(), app.instance().change()); // Application starts upgrade, but is confidence is broken cancelled after first zone. Tests won't run. @@ -1249,7 +1248,7 @@ public class DeploymentTriggerTest { } @Test - public void testDeployComplicatedDeploymentSpec() { + void testDeployComplicatedDeploymentSpec() { String complicatedDeploymentSpec = """ <deployment version='1.0' athenz-domain='domain' athenz-service='service'> @@ -1399,14 +1398,14 @@ public class DeploymentTriggerTest { // Upgrade platform. app2.runJob(systemTest); app1.runJob(stagingTest) - .runJob(productionUsWest1) - .runJob(productionUsEast3); + .runJob(productionUsWest1) + .runJob(productionUsEast3); // Upgrade revision tester.clock().advance(Duration.ofSeconds(1)); // Ensure we see revision as rolling after upgrade. app2.runJob(systemTest); // R app1.runJob(stagingTest) // R - .runJob(productionUsWest1); // R - // productionUsEast3 won't change revision before its production test has completed for the upgrade, which is one of the last jobs! + .runJob(productionUsWest1); // R + // productionUsEast3 won't change revision before its production test has completed for the upgrade, which is one of the last jobs! tester.clock().advance(Duration.ofHours(2)); app1.runJob(productionEuWest1); tester.clock().advance(Duration.ofHours(1)); @@ -1442,13 +1441,13 @@ public class DeploymentTriggerTest { tester.upgrader().maintain(); tester.outstandingChangeDeployer().run(); tester.triggerJobs(); - assertEquals(tester.jobs().active().toString(), 1, tester.jobs().active().size()); + assertEquals(1, tester.jobs().active().size(), tester.jobs().active().toString()); assertEquals(Change.empty(), app1.instance().change()); assertEquals(Change.of(version), app2.instance().change()); assertEquals(Change.empty(), app3.instance().change()); app2.runJob(productionEuWest1) - .failDeployment(testEuWest1); + .failDeployment(testEuWest1); // Instance 2 failed the last job, and now exits block window, letting application change roll out with the upgrade. tester.clock().advance(Duration.ofDays(1)); // Leave block window for revisions. @@ -1461,7 +1460,7 @@ public class DeploymentTriggerTest { assertEquals(Change.of(version).with(app1.application().revisions().last().get().id()), app2.instance().change()); app2.runJob(productionEuWest1) - .runJob(testEuWest1); + .runJob(testEuWest1); assertEquals(Change.empty(), app2.instance().change()); assertEquals(Change.empty(), app3.instance().change()); @@ -1485,12 +1484,12 @@ public class DeploymentTriggerTest { } @Test - public void testRevisionJoinsUpgradeWithSeparateRollout() { + void testRevisionJoinsUpgradeWithSeparateRollout() { var appPackage = new ApplicationPackageBuilder().region("us-central-1") - .region("us-east-3") - .region("us-west-1") - .upgradeRollout("separate") - .build(); + .region("us-east-3") + .region("us-west-1") + .upgradeRollout("separate") + .build(); var app = tester.newDeploymentContext().submit(appPackage).deploy(); // Platform rolls through first production zone. @@ -1511,20 +1510,20 @@ public class DeploymentTriggerTest { // Upgrade got here first, so attempts to proceed alone, but the upgrade fails. app.triggerJobs(); assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); app.timeOutConvergence(productionUsEast3); // Revision is allowed to join. app.triggerJobs(); assertEquals(new Versions(version1, revision1.get(), Optional.of(version1), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); app.runJob(productionUsEast3); // Platform and revision now proceed together. app.runJob(stagingTest); app.triggerJobs(); assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); app.runJob(productionUsWest1); assertEquals(Change.empty(), app.instance().change()); @@ -1538,23 +1537,23 @@ public class DeploymentTriggerTest { app.submit(appPackage); app.runJob(systemTest).runJob(stagingTest) // Tests run with combined upgrade. - .runJob(productionUsCentral1) // Combined upgrade stays together. - .runJob(productionUsEast3).runJob(productionUsWest1); + .runJob(productionUsCentral1) // Combined upgrade stays together. + .runJob(productionUsEast3).runJob(productionUsWest1); assertEquals(Map.of(), app.deploymentStatus().jobsToRun()); assertEquals(Change.empty(), app.instance().change()); } @Test - public void testProductionTestBlockingDeploymentWithSeparateRollout() { + void testProductionTestBlockingDeploymentWithSeparateRollout() { var appPackage = new ApplicationPackageBuilder().region("us-east-3") - .region("us-west-1") - .delay(Duration.ofHours(1)) - .test("us-east-3") - .upgradeRollout("separate") - .build(); + .region("us-west-1") + .delay(Duration.ofHours(1)) + .test("us-east-3") + .upgradeRollout("separate") + .build(); var app = tester.newDeploymentContext().submit(appPackage) - .runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3).runJob(productionUsWest1); + .runJob(systemTest).runJob(stagingTest) + .runJob(productionUsEast3).runJob(productionUsWest1); tester.clock().advance(Duration.ofHours(1)); app.runJob(testUsEast3); assertEquals(Change.empty(), app.instance().change()); @@ -1578,13 +1577,13 @@ public class DeploymentTriggerTest { // Upgrade got here first, so attempts to proceed alone, but the upgrade fails. app.triggerJobs(); assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); app.timeOutConvergence(productionUsWest1).triggerJobs(); // Upgrade now fails between us-east-3 deployment and test, so test is abandoned, and revision unblocked. app.assertRunning(productionUsEast3); assertEquals(new Versions(version1, revision1.get(), Optional.of(version1), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); app.runJob(productionUsEast3).triggerJobs() .jobAborted(productionUsWest1).runJob(productionUsWest1); tester.clock().advance(Duration.ofHours(1)); @@ -1593,15 +1592,15 @@ public class DeploymentTriggerTest { } @Test - public void testProductionTestNotBlockingDeploymentWithSimultaneousRollout() { + void testProductionTestNotBlockingDeploymentWithSimultaneousRollout() { var appPackage = new ApplicationPackageBuilder().region("us-east-3") - .region("us-central-1") - .region("us-west-1") - .delay(Duration.ofHours(1)) - .test("us-east-3") - .test("us-west-1") - .upgradeRollout("simultaneous") - .build(); + .region("us-central-1") + .region("us-west-1") + .delay(Duration.ofHours(1)) + .test("us-east-3") + .test("us-west-1") + .upgradeRollout("simultaneous") + .build(); var app = tester.newDeploymentContext().submit(appPackage) .runJob(systemTest).runJob(stagingTest) .runJob(productionUsEast3).runJob(productionUsCentral1).runJob(productionUsWest1); @@ -1628,7 +1627,7 @@ public class DeploymentTriggerTest { // Revision deploys to first prod zone. app.triggerJobs(); assertEquals(new Versions(version1, revision1.get(), Optional.of(version1), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); tester.clock().advance(Duration.ofSeconds(1)); app.runJob(productionUsEast3); @@ -1636,12 +1635,12 @@ public class DeploymentTriggerTest { app.runJob(systemTest).runJob(stagingTest).runJob(stagingTest).triggerJobs(); app.jobAborted(productionUsCentral1).triggerJobs(); assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsCentral1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsCentral1).get().versions()); app.runJob(productionUsCentral1).triggerJobs(); // Revision proceeds alone in third prod zone, making test targets different for the two prod tests. assertEquals(new Versions(version0, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); app.runJob(productionUsWest1); app.triggerJobs(); app.assertNotRunning(testUsEast3); @@ -1655,7 +1654,7 @@ public class DeploymentTriggerTest { } @Test - public void testVeryLengthyPipelineRevisions() { + void testVeryLengthyPipelineRevisions() { String lengthyDeploymentSpec = """ <deployment version='1.0'> @@ -1696,7 +1695,7 @@ public class DeploymentTriggerTest { var revision2 = alpha.lastSubmission(); alpha.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3).runJob(testUsEast3); + .runJob(productionUsEast3).runJob(testUsEast3); assertEquals(Optional.empty(), alpha.instance().change().revision()); // revision3 is submitted when revision2 is half-way. @@ -1715,7 +1714,7 @@ public class DeploymentTriggerTest { // 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); + .runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); assertEquals(Optional.empty(), alpha.instance().change().revision()); assertEquals(revision3, beta.instance().change().revision()); @@ -1724,11 +1723,11 @@ public class DeploymentTriggerTest { alpha.submit(appPackage, 3); var revision4 = alpha.lastSubmission(); alpha.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3); + .runJob(productionUsEast3); alpha.submit(appPackage, 2); var revision5 = alpha.lastSubmission(); alpha.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3).runJob(testUsEast3); + .runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); assertEquals(Optional.empty(), alpha.instance().change().revision()); assertEquals(revision3, beta.instance().change().revision()); @@ -1737,8 +1736,8 @@ public class DeploymentTriggerTest { alpha.submit(appPackage, 6); var revision6 = alpha.lastSubmission(); alpha.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3) - .runJob(testUsEast3); + .runJob(productionUsEast3) + .runJob(testUsEast3); beta.runJob(productionUsEast3).runJob(testUsEast3); tester.outstandingChangeDeployer().run(); assertEquals(Optional.empty(), alpha.instance().change().revision()); @@ -1751,7 +1750,7 @@ public class DeploymentTriggerTest { // revision 2 completes in gamma gamma.runJob(productionUsEast3) - .runJob(testUsEast3); + .runJob(testUsEast3); tester.outstandingChangeDeployer().run(); assertEquals(Optional.empty(), alpha.instance().change().revision()); assertEquals(Optional.empty(), gamma.instance().change().revision()); // no other revisions after 3 are ready, so gamma waits @@ -1769,7 +1768,7 @@ public class DeploymentTriggerTest { // revision 6 is next, once 3 is done // revision 3 completes gamma.runJob(productionUsEast3) - .runJob(testUsEast3); + .runJob(testUsEast3); tester.outstandingChangeDeployer().run(); assertEquals(revision6, gamma.instance().change().revision()); @@ -1800,7 +1799,7 @@ public class DeploymentTriggerTest { } @Test - public void testVeryLengthyPipelineUpgrade() { + void testVeryLengthyPipelineUpgrade() { String lengthyDeploymentSpec = """ <deployment version='1.0'> @@ -1843,7 +1842,7 @@ public class DeploymentTriggerTest { tester.upgrader().maintain(); alpha.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsEast3).runJob(testUsEast3); + .runJob(productionUsEast3).runJob(testUsEast3); assertEquals(Change.empty(), alpha.instance().change()); tester.upgrader().maintain(); @@ -1859,12 +1858,12 @@ public class DeploymentTriggerTest { } @Test - public void testRevisionJoinsUpgradeWithLeadingRollout() { + void testRevisionJoinsUpgradeWithLeadingRollout() { var appPackage = new ApplicationPackageBuilder().region("us-central-1") - .region("us-east-3") - .region("us-west-1") - .upgradeRollout("leading") - .build(); + .region("us-east-3") + .region("us-west-1") + .upgradeRollout("leading") + .build(); var app = tester.newDeploymentContext().submit(appPackage).deploy(); // Platform rolls through first production zone. @@ -1885,7 +1884,7 @@ public class DeploymentTriggerTest { // Upgrade got here first, and has triggered, but is now obsolete. app.triggerJobs(); assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); assertEquals(RunStatus.running, tester.jobs().last(app.instanceId(), productionUsEast3).get().status()); // Once staging tests verify the joint upgrade, the job is replaced with that. @@ -1893,23 +1892,23 @@ public class DeploymentTriggerTest { app.triggerJobs(); app.jobAborted(productionUsEast3).runJob(productionUsEast3); assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); // Platform and revision now proceed together. app.triggerJobs(); assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); app.runJob(productionUsWest1); assertEquals(Change.empty(), app.instance().change()); } @Test - public void testRevisionPassesUpgradeWithSimultaneousRollout() { + void testRevisionPassesUpgradeWithSimultaneousRollout() { var appPackage = new ApplicationPackageBuilder().region("us-central-1") - .region("us-east-3") - .region("us-west-1") - .upgradeRollout("simultaneous") - .build(); + .region("us-east-3") + .region("us-west-1") + .upgradeRollout("simultaneous") + .build(); var app = tester.newDeploymentContext().submit(appPackage).deploy(); // Platform rolls through first production zone. @@ -1931,7 +1930,7 @@ public class DeploymentTriggerTest { app.triggerJobs(); app.assertRunning(productionUsEast3); assertEquals(new Versions(version1, revision0.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); assertEquals(RunStatus.running, tester.jobs().last(app.instanceId(), productionUsEast3).get().status()); // Once staging tests verify the joint upgrade, the job is replaced with that. @@ -1939,27 +1938,27 @@ public class DeploymentTriggerTest { app.triggerJobs(); app.jobAborted(productionUsEast3).runJob(productionUsEast3); assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); + tester.jobs().last(app.instanceId(), productionUsEast3).get().versions()); // Revision now proceeds alone. app.triggerJobs(); assertEquals(new Versions(version0, revision1.get(), Optional.of(version0), revision0), - tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); app.runJob(productionUsWest1); // Upgrade follows. app.triggerJobs(); assertEquals(new Versions(version1, revision1.get(), Optional.of(version0), revision1), - tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); + tester.jobs().last(app.instanceId(), productionUsWest1).get().versions()); app.runJob(productionUsWest1); assertEquals(Change.empty(), app.instance().change()); } @Test - public void mixedDirectAndPipelineJobsInProduction() { + void mixedDirectAndPipelineJobsInProduction() { ApplicationPackage cdPackage = new ApplicationPackageBuilder().region("us-east-3") - .region("aws-us-east-1a") - .build(); + .region("aws-us-east-1a") + .build(); ControllerTester wrapped = new ControllerTester(cd); wrapped.upgradeSystem(Version.fromString("6.1")); wrapped.computeVersionStatus(); @@ -1973,9 +1972,9 @@ public class DeploymentTriggerTest { // Staging test requires unknown initial version, and is broken. tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionUsEast3, "user", false, true, true); app.runJob(productionUsEast3) - .abortJob(stagingTest) // Complete failing run. - .runJob(stagingTest) // Run staging-test for production zone with no prior deployment. - .runJob(productionAwsUsEast1a); + .abortJob(stagingTest) // Complete failing run. + .runJob(stagingTest) // Run staging-test for production zone with no prior deployment. + .runJob(productionAwsUsEast1a); // Manually deploy to east again, then upgrade the system. app.runJob(productionUsEast3, cdPackage); @@ -1985,12 +1984,12 @@ public class DeploymentTriggerTest { // System and staging tests both require unknown versions, and are broken. tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionUsEast3, "user", false, true, true); app.runJob(productionUsEast3) - .triggerJobs() - .jobAborted(systemTest) - .jobAborted(stagingTest) - .runJob(systemTest) // Run test for aws zone again. - .runJob(stagingTest) // Run test for aws zone again. - .runJob(productionAwsUsEast1a); + .triggerJobs() + .jobAborted(systemTest) + .jobAborted(stagingTest) + .runJob(systemTest) // Run test for aws zone again. + .runJob(stagingTest) // Run test for aws zone again. + .runJob(productionAwsUsEast1a); // Deploy manually again, then submit new package. app.runJob(productionUsEast3, cdPackage); @@ -1999,13 +1998,13 @@ public class DeploymentTriggerTest { // Staging test requires unknown initial version, and is broken. tester.controller().applications().deploymentTrigger().forceTrigger(app.instanceId(), productionUsEast3, "user", false, true, true); app.runJob(productionUsEast3) - .jobAborted(stagingTest) - .runJob(stagingTest) - .runJob(productionAwsUsEast1a); + .jobAborted(stagingTest) + .runJob(stagingTest) + .runJob(productionAwsUsEast1a); } @Test - public void testsInSeparateInstance() { + void testsInSeparateInstance() { String deploymentSpec = """ <deployment version='1.0' athenz-domain='domain' athenz-service='service'> @@ -2028,30 +2027,30 @@ public class DeploymentTriggerTest { var conservative = tester.newDeploymentContext("t", "a", "default"); canary.runJob(systemTest) - .runJob(stagingTest); + .runJob(stagingTest); conservative.runJob(productionEuWest1) - .runJob(testEuWest1); + .runJob(testEuWest1); canary.submit(applicationPackage) - .runJob(systemTest) - .runJob(stagingTest); + .runJob(systemTest) + .runJob(stagingTest); tester.outstandingChangeDeployer().run(); conservative.runJob(productionEuWest1) - .runJob(testEuWest1); + .runJob(testEuWest1); tester.controllerTester().upgradeSystem(new Version("7.7.7")); tester.upgrader().maintain(); canary.runJob(systemTest) - .runJob(stagingTest); + .runJob(stagingTest); tester.upgrader().maintain(); conservative.runJob(productionEuWest1) - .runJob(testEuWest1); + .runJob(testEuWest1); } @Test - public void testEagerTests() { + void testEagerTests() { var app = tester.newDeploymentContext().submit().deploy(); // Start upgrade, then receive new submission. @@ -2068,24 +2067,24 @@ public class DeploymentTriggerTest { tester.triggerJobs(); app.assertRunning(stagingTest); assertEquals(version1, - app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetPlatform()); + app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetPlatform()); assertEquals(build1, - app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetRevision()); + app.instanceJobs().get(stagingTest).lastCompleted().get().versions().targetRevision()); assertEquals(version1, - app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourcePlatform().get()); + app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourcePlatform().get()); assertEquals(build1, - app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourceRevision().get()); + app.instanceJobs().get(stagingTest).lastTriggered().get().versions().sourceRevision().get()); assertEquals(version1, - app.instanceJobs().get(stagingTest).lastTriggered().get().versions().targetPlatform()); + app.instanceJobs().get(stagingTest).lastTriggered().get().versions().targetPlatform()); assertEquals(build2, - app.instanceJobs().get(stagingTest).lastTriggered().get().versions().targetRevision()); + 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) - .runJob(productionUsCentral1) - .runJob(productionUsEast3) - .runJob(productionUsWest1); + .runJob(productionUsCentral1) + .runJob(productionUsEast3) + .runJob(productionUsWest1); tester.outstandingChangeDeployer().run(); assertEquals(RunStatus.running, tester.jobs().last(app.instanceId(), stagingTest).get().status()); @@ -2095,12 +2094,12 @@ public class DeploymentTriggerTest { } @Test - public void testTriggeringOfIdleTestJobsWhenFirstDeploymentIsOnNewerVersionThanChange() { + void testTriggeringOfIdleTestJobsWhenFirstDeploymentIsOnNewerVersionThanChange() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder().systemTest() - .stagingTest() - .region("us-east-3") - .region("us-west-1") - .build(); + .stagingTest() + .region("us-east-3") + .region("us-west-1") + .build(); var app = tester.newDeploymentContext().submit(applicationPackage).deploy(); var appToAvoidVersionGC = tester.newDeploymentContext("g", "c", "default").submit().deploy(); @@ -2121,9 +2120,9 @@ public class DeploymentTriggerTest { assertEquals(Optional.of(version2), app.instance().change().platform()); app.runJob(systemTest) - .runJob(productionUsEast3) - .runJob(stagingTest) - .runJob(productionUsWest1); + .runJob(productionUsEast3) + .runJob(stagingTest) + .runJob(productionUsWest1); assertEquals(version3, app.instanceJobs().get(productionUsEast3).lastSuccess().get().versions().targetPlatform()); assertEquals(version2, app.instanceJobs().get(productionUsWest1).lastSuccess().get().versions().targetPlatform()); @@ -2133,7 +2132,7 @@ public class DeploymentTriggerTest { } @Test - public void testRetriggerQueue() { + void testRetriggerQueue() { var app = tester.newDeploymentContext().submit().deploy(); app.submit(); tester.triggerJobs(); @@ -2143,44 +2142,59 @@ public class DeploymentTriggerTest { tester.deploymentTrigger().reTriggerOrAddToQueue(app.deploymentIdIn(ZoneId.from("prod", "us-east-3")), null); List<RetriggerEntry> retriggerEntries = tester.controller().curator().readRetriggerEntries(); - Assert.assertEquals(1, retriggerEntries.size()); + assertEquals(1, retriggerEntries.size()); } @Test - public void testOrchestrationWithIncompatibleVersionPairs() { + void testOrchestrationWithIncompatibleVersionPairs() { Version version1 = new Version("7"); Version version2 = new Version("8"); tester.controllerTester().flagSource().withListFlag(PermanentFlags.INCOMPATIBLE_VERSIONS.id(), List.of("8"), String.class); + // App deploys on version1. tester.controllerTester().upgradeSystem(version1); DeploymentContext app = tester.newDeploymentContext() - .submit(new ApplicationPackageBuilder().region("us-east-3") - .compileVersion(version1) - .build()) - .deploy(); + .submit(new ApplicationPackageBuilder().region("us-east-3") + .compileVersion(version1) + .build()) + .deploy(); + // System upgrades to version2, but the app is not upgraded. tester.controllerTester().upgradeSystem(version2); tester.upgrader().run(); assertEquals(Change.empty(), app.instance().change()); + // App compiles against version2, and upgrades. app.submit(new ApplicationPackageBuilder().region("us-east-3") - .compileVersion(version2) - .build()); + .compileVersion(version2) + .build()); app.deploy(); assertEquals(version2, tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform()); assertEquals(version2, app.application().revisions().get(tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetRevision()).compileVersion().get()); + + // App specifies version1 in deployment spec, compiles against version1, pins to version1, and then downgrades. + app.submit(new ApplicationPackageBuilder().region("us-east-3") + .majorVersion(7) + .compileVersion(version1) + .build()); + tester.deploymentTrigger().forceChange(app.instanceId(), app.instance().change().withPin()); + app.deploy(); + assertEquals(version1, tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetPlatform()); + assertEquals(version1, app.application().revisions().get(tester.jobs().last(app.instanceId(), productionUsEast3).get().versions().targetRevision()).compileVersion().get()); + + // A new app, compiled against version1, is deployed on version1. DeploymentContext newApp = tester.newDeploymentContext("new", "app", "default") - .submit(new ApplicationPackageBuilder().region("us-east-3") - .compileVersion(version1) - .build()) - .deploy(); + .submit(new ApplicationPackageBuilder().region("us-east-3") + .compileVersion(version1) + .build()) + .deploy(); assertEquals(version1, tester.jobs().last(newApp.instanceId(), productionUsEast3).get().versions().targetPlatform()); assertEquals(version1, newApp.application().revisions().get(tester.jobs().last(newApp.instanceId(), productionUsEast3).get().versions().targetRevision()).compileVersion().get()); } @Test - public void testInstanceWithOnlySystemTestInTwoClouds() { + void testInstanceWithOnlySystemTestInTwoClouds() { String spec = """ <deployment> <instance id='tests'> @@ -2256,7 +2270,7 @@ public class DeploymentTriggerTest { Version version3 = new Version("9"); tester.controllerTester().upgradeSystem(version3); tests.runJob(systemTest) // Success in default cloud. - .failDeployment(systemTest); // Failure in centauri cloud. + .failDeployment(systemTest); // Failure in centauri cloud. tester.upgrader().run(); assertEquals(Change.of(version3), tests.instance().change()); @@ -2353,7 +2367,7 @@ public class DeploymentTriggerTest { } @Test - public void testNoTests() { + void testNoTests() { DeploymentContext app = tester.newDeploymentContext(); app.submit(new ApplicationPackageBuilder().systemTest().region("us-east-3").build()); @@ -2365,7 +2379,7 @@ public class DeploymentTriggerTest { } @Test - public void testJobNames() { + void testJobNames() { ZoneRegistryMock zones = new ZoneRegistryMock(SystemName.main); List<ZoneApi> existing = new ArrayList<>(zones.zones().all().zones()); existing.add(ZoneApiMock.newBuilder().withCloud("pink-clouds").withId("test.zone").build()); @@ -2382,10 +2396,14 @@ public class DeploymentTriggerTest { assertEquals(defaultSystemTest, JobType.fromJobName("system-test", zones)); assertEquals(ZoneId.from("test", "us-east-1"), defaultSystemTest.zone()); - assertEquals(ZoneId.from("staging", "us-east-3"), JobType.stagingTest(zones, null).zone()); + assertEquals(ZoneId.from("staging", "us-east-3"), JobType.stagingTest(zones, zones.systemZone().getCloudName()).zone()); assertEquals(ZoneId.from("test", "zone"), pinkSystemTest.zone()); assertEquals(ZoneId.from("staging", "us-east-3"), JobType.stagingTest(zones, CloudName.from("pink-clouds")).zone()); + + assertThrows(IllegalStateException.class, JobType.systemTest(zones, null)::zone); + assertThrows(IllegalStateException.class, JobType.fromJobName("system-test", zones)::zone); + assertThrows(IllegalStateException.class, JobType.fromJobName("staging-test", zones)::zone); } } 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 d781b1f1d3f..493c0945ecc 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 @@ -25,8 +25,8 @@ import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.maintenance.JobRunner; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.security.cert.X509Certificate; import java.time.Duration; @@ -51,10 +51,10 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished; import static java.time.temporal.ChronoUnit.SECONDS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author jonmv @@ -65,7 +65,7 @@ public class InternalStepRunnerTest { private DeploymentTester tester; private DeploymentContext app; - @Before + @BeforeEach public void setup() { tester = new DeploymentTester(); app = tester.newDeploymentContext(); @@ -358,7 +358,8 @@ public class InternalStepRunnerTest { // Test sleeps for a while. tester.runner().run(); assertEquals(unfinished, tester.jobs().run(id).stepStatuses().get(Step.deployTester)); - tester.clock().advance(Duration.ofSeconds(899)); + Instant nextAttemptAt = tester.clock().instant().plusSeconds(1800); + tester.clock().advance(Duration.ofSeconds(1799)); tester.runner().run(); assertEquals(unfinished, tester.jobs().run(id).stepStatuses().get(Step.deployTester)); @@ -380,8 +381,8 @@ public class InternalStepRunnerTest { assertTestLogEntries(id, Step.endTests, new LogEntry(lastId1 + 1, Instant.ofEpochMilli(123), info, "Not enough data!"), - new LogEntry(lastId1 + 2, instant1, info, "Tests were inconclusive, and will run again in 15 minutes."), - new LogEntry(lastId1 + 15, instant1, info, "### Run will reset, and start over at " + instant1.plusSeconds(900).truncatedTo(SECONDS)), + new LogEntry(lastId1 + 2, instant1, info, "Tests were inconclusive, and will run again at " + nextAttemptAt + "."), + new LogEntry(lastId1 + 15, instant1, info, "### Run will reset, and start over at " + nextAttemptAt), new LogEntry(lastId1 + 16, instant1, info, ""), new LogEntry(lastId2 + 1, tester.clock().instant(), info, "Tests completed successfully.")); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/QuotaUsageTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/QuotaUsageTest.java index 772d65fa80e..44ca248614b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/QuotaUsageTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/QuotaUsageTest.java @@ -2,9 +2,9 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.yahoo.config.provision.zone.ZoneId; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author ogronnesby @@ -12,7 +12,7 @@ import static org.junit.Assert.assertEquals; public class QuotaUsageTest { @Test - public void testQuotaUsageIsPersisted() { + void testQuotaUsageIsPersisted() { var tester = new DeploymentTester(); var context = tester.newDeploymentContext().submit().deploy(); assertEquals(1.304, context.deployment(ZoneId.from("prod.us-west-1")).quota().rate(), 0.01); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java index 67583891765..6493eafcde5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.EndpointId; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; @@ -21,7 +21,7 @@ import java.util.List; import java.util.Map; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTester.instanceId; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -29,24 +29,24 @@ import static org.junit.Assert.assertEquals; public class TestConfigSerializerTest { @Test - public void testConfig() throws IOException { + void testConfig() throws IOException { ZoneId zone = DeploymentContext.systemTest.zone(); byte[] json = new TestConfigSerializer(SystemName.PublicCd).configJson(instanceId, - DeploymentContext.systemTest, - true, - Version.fromString("1.2.3"), - RevisionId.forProduction(321), - Instant.ofEpochMilli(222), - Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId()) - .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"), - List.of(new DeploymentId(ApplicationId.defaultId(), - ZoneId.defaultId()))) - .on(Endpoint.Port.tls()) - .in(SystemName.main))), - Map.of(zone, List.of("facts"))); + DeploymentContext.systemTest, + true, + Version.fromString("1.2.3"), + RevisionId.forProduction(321), + Instant.ofEpochMilli(222), + Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId()) + .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"), + List.of(new DeploymentId(ApplicationId.defaultId(), + ZoneId.defaultId()))) + .on(Endpoint.Port.tls()) + .in(SystemName.main))), + Map.of(zone, List.of("facts"))); byte[] expected = Files.readAllBytes(Paths.get("src/test/resources/testConfig.json")); assertEquals(new String(SlimeUtils.toJsonBytes(SlimeUtils.jsonToSlime(expected))), - new String(json)); + new String(json)); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java index f48658af7e3..8fe48f805df 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ZipBuilderTest.java @@ -1,7 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.deployment; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -13,7 +13,7 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author freva @@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals; public class ZipBuilderTest { @Test - public void test() { + void test() { Map<String, String> expected = new HashMap<>(); expected.put("dir/myfile", "my content"); expected.put("rootfile", "this is root"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueueTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueueTest.java index b5fd3d750ef..efb04615125 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueueTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/dns/NameServiceQueueTest.java @@ -9,13 +9,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue.Priority; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -23,18 +23,18 @@ import static org.junit.Assert.assertTrue; public class NameServiceQueueTest { @Test - public void test_queue() { + void test_queue() { var nameService = new MemoryNameService(); var r1 = new Record(Record.Type.CNAME, RecordName.from("cname.vespa.oath.cloud"), RecordData.from("example.com")); var r2 = new Record(Record.Type.TXT, RecordName.from("txt.example.com"), RecordData.from("text")); var r3 = List.of(new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"), - new LatencyAliasTarget(HostName.of("alias1"), - "dns-zone-01", - ZoneId.from("prod", "us-north-1")).pack()), - new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"), - new LatencyAliasTarget(HostName.of("alias2"), - "dns-zone-02", - ZoneId.from("prod", "us-north-2")).pack())); + new LatencyAliasTarget(HostName.of("alias1"), + "dns-zone-01", + ZoneId.from("prod", "us-north-1")).pack()), + new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"), + new LatencyAliasTarget(HostName.of("alias2"), + "dns-zone-02", + ZoneId.from("prod", "us-north-2")).pack())); var req1 = new CreateRecord(r1); var req2 = new CreateRecords(List.of(r2)); var req3 = new CreateRecords(r3); @@ -64,17 +64,17 @@ public class NameServiceQueueTest { // Dispatch removals queue = queue.with(req4).with(req5).dispatchTo(nameService, 2); - assertTrue("Removed " + r2, nameService.findRecords(r2.type(), r2.name()).isEmpty()); - assertTrue("Removed " + r3, nameService.findRecords(Record.Type.ALIAS, r3.get(0).name()).isEmpty()); + assertTrue(nameService.findRecords(r2.type(), r2.name()).isEmpty(), "Removed " + r2); + assertTrue(nameService.findRecords(Record.Type.ALIAS, r3.get(0).name()).isEmpty(), "Removed " + r3); // Dispatch removals by data queue = queue.with(req6).dispatchTo(nameService, 1); assertTrue(queue.requests().isEmpty()); - assertTrue("Removed " + r1, nameService.findRecords(Record.Type.CNAME, r1.name()).isEmpty()); + assertTrue(nameService.findRecords(Record.Type.CNAME, r1.name()).isEmpty(), "Removed " + r1); // Keep n last requests queue = queue.with(req1).with(req2).with(req3).with(req4).with(req6) - .last(2); + .last(2); assertEquals(List.of(req4, req6), List.copyOf(queue.requests())); assertSame(queue, queue.last(2)); assertSame(queue, queue.last(10)); @@ -82,7 +82,7 @@ public class NameServiceQueueTest { // Keep n first requests queue = NameServiceQueue.EMPTY.with(req1).with(req2).with(req3).with(req4).with(req6) - .first(3); + .first(3); assertEquals(List.of(req1, req2, req3), List.copyOf(queue.requests())); assertSame(queue, queue.first(3)); assertSame(queue, queue.first(10)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java index 3f1ca3f9706..93ebdcf3171 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java @@ -107,7 +107,7 @@ public class NodeRepositoryMock implements NodeRepository { .collect(Collectors.toList()) : List.of(); - return new NodeRepoStats(Load.zero(), Load.zero(), applicationStats); + return new NodeRepoStats(0.0, 0.0, Load.zero(), Load.zero(), applicationStats); } @Override diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneApiMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneApiMock.java index d173fcb0e18..4bb35d748db 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneApiMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneApiMock.java @@ -76,6 +76,11 @@ public class ZoneApiMock implements ZoneApi { return Objects.hash(id); } + @Override + public String toString() { + return id.toString(); + } + public static class Builder { private SystemName systemName = SystemName.defaultSystem(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java index 1e258e8febc..b643d3e90d2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmerTest.java @@ -9,17 +9,17 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipIssues; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; import java.util.Optional; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTester.appId; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author jonmv @@ -30,7 +30,7 @@ public class ApplicationOwnershipConfirmerTest { private ApplicationOwnershipConfirmer confirmer; private DeploymentTester tester; - @Before + @BeforeEach public void setup() { tester = new DeploymentTester(); issues = new MockOwnershipIssues(); @@ -38,7 +38,7 @@ public class ApplicationOwnershipConfirmerTest { } @Test - public void testConfirmation() { + void testConfirmation() { Optional<Contact> contact = Optional.of(tester.controllerTester().serviceRegistry().contactRetrieverMock().contact()); var app = tester.newDeploymentContext(); tester.controller().tenants().lockOrThrow(appId.tenant(), LockedTenant.Athenz.class, tenant -> @@ -48,55 +48,55 @@ public class ApplicationOwnershipConfirmerTest { var appWithoutContact = tester.newDeploymentContext("other", "application", "default"); appWithoutContact.submit().deploy(); - assertFalse("No issue is initially stored for a new application.", app.application().ownershipIssueId().isPresent()); - assertFalse("No issue is initially stored for a new application.", appWithoutContact.application().ownershipIssueId().isPresent()); - assertFalse("No escalation has been attempted for a new application", issues.escalated); + assertFalse(app.application().ownershipIssueId().isPresent(), "No issue is initially stored for a new application."); + assertFalse(appWithoutContact.application().ownershipIssueId().isPresent(), "No issue is initially stored for a new application."); + assertFalse(issues.escalated, "No escalation has been attempted for a new application"); // Set response from the issue mock, which will be obtained by the maintainer on issue filing. Optional<IssueId> issueId = Optional.of(IssueId.from("1")); issues.response = issueId; confirmer.maintain(); - assertFalse("No issue is stored for an application newer than 3 months.", app.application().ownershipIssueId().isPresent()); - assertFalse("No issue is stored for an application newer than 3 months.", appWithoutContact.application().ownershipIssueId().isPresent()); + assertFalse(app.application().ownershipIssueId().isPresent(), "No issue is stored for an application newer than 3 months."); + assertFalse(appWithoutContact.application().ownershipIssueId().isPresent(), "No issue is stored for an application newer than 3 months."); tester.clock().advance(Duration.ofDays(91)); confirmer.maintain(); - assertEquals("Confirmation issue has been filed for application with contact.", issueId, app.application().ownershipIssueId()); - assertTrue("The confirmation issue response has been ensured.", issues.escalated); - assertEquals("No confirmation issue has been filed for application without contact.", Optional.empty(), appWithoutContact.application().ownershipIssueId()); + assertEquals(issueId, app.application().ownershipIssueId(), "Confirmation issue has been filed for application with contact."); + assertTrue(issues.escalated, "The confirmation issue response has been ensured."); + assertEquals(Optional.empty(), appWithoutContact.application().ownershipIssueId(), "No confirmation issue has been filed for application without contact."); // No new issue is created, so return empty now. issues.response = Optional.empty(); confirmer.maintain(); - assertEquals("Confirmation issue reference is not updated when no issue id is returned.", issueId, app.application().ownershipIssueId()); + assertEquals(issueId, app.application().ownershipIssueId(), "Confirmation issue reference is not updated when no issue id is returned."); // Time has passed, and a new confirmation issue is in order for the property which is still in production. Optional<IssueId> issueId2 = Optional.of(IssueId.from("2")); issues.response = issueId2; confirmer.maintain(); - assertEquals("A new confirmation issue id is stored when something is returned to the maintainer.", issueId2, app.application().ownershipIssueId()); + assertEquals(issueId2, app.application().ownershipIssueId(), "A new confirmation issue id is stored when something is returned to the maintainer."); - assertFalse("No owner is stored for application", app.application().owner().isPresent()); + assertFalse(app.application().owner().isPresent(), "No owner is stored for application"); issues.owner = Optional.of(User.from("username")); confirmer.maintain(); - assertEquals("Owner has been added to application", app.application().owner().get().username(), "username"); + assertEquals(app.application().owner().get().username(), "username", "Owner has been added to application"); // The app deletes all production deployments — see that the issue is forgotten. - assertEquals("Confirmation issue for application is still open.", issueId2, app.application().ownershipIssueId()); + assertEquals(issueId2, app.application().ownershipIssueId(), "Confirmation issue for application is still open."); app.application().productionDeployments().values().stream().flatMap(List::stream) - .forEach(deployment -> tester.controller().applications().deactivate(app.instanceId(), deployment.zone())); - assertTrue("No production deployments are listed for user.", app.application().require(InstanceName.defaultName()).productionDeployments().isEmpty()); + .forEach(deployment -> tester.controller().applications().deactivate(app.instanceId(), deployment.zone())); + assertTrue(app.application().require(InstanceName.defaultName()).productionDeployments().isEmpty(), "No production deployments are listed for user."); confirmer.maintain(); // Time has passed, and a new confirmation issue is in order for the property which is still in production. issues.response = Optional.of(IssueId.from("3")); confirmer.maintain(); - assertEquals("Confirmation issue for application without production deployments has not been filed.", issueId2, app.application().ownershipIssueId()); + assertEquals(issueId2, app.application().ownershipIssueId(), "Confirmation issue for application without production deployments has not been filed."); } private static class MockOwnershipIssues implements OwnershipIssues { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java index 3535417c586..c10b77d853a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainerTest.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveBucket; import com.yahoo.vespa.hosted.controller.api.integration.archive.MockArchiveService; import com.yahoo.vespa.hosted.controller.tenant.ArchiveAccess; import com.yahoo.vespa.hosted.controller.tenant.Tenant; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Map; @@ -19,8 +19,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author andreer @@ -28,7 +27,7 @@ import static org.junit.Assert.assertNull; public class ArchiveAccessMaintainerTest { @Test - public void grantsRoleAccess() { + void grantsRoleAccess() { var tester = new ControllerTester(SystemName.Public); String tenant1role = "arn:aws:iam::123456789012:role/my-role"; @@ -49,10 +48,10 @@ public class ArchiveAccessMaintainerTest { assertEquals(new ArchiveAccess().withAWSRole(tenant2role), archiveService.authorizeAccessByTenantName.get(tenant2)); var expected = Map.of("archive.bucketCount", - tester.controller().zoneRegistry().zonesIncludingSystem().all().ids().stream() - .collect(Collectors.toMap( - zone -> Map.of("zone", zone.value(), "cloud", "default"), - zone -> zone.equals(testZone) ? 1d : 0d))); + tester.controller().zoneRegistry().zonesIncludingSystem().all().ids().stream() + .collect(Collectors.toMap( + zone -> Map.of("zone", zone.value(), "cloud", "default"), + zone -> zone.equals(testZone) ? 1d : 0d))); assertEquals(expected, metric.metrics()); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java index d208657c1c4..cb5a84654d0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdaterTest.java @@ -13,7 +13,7 @@ import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URI; import java.time.Duration; @@ -21,7 +21,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author freva @@ -31,7 +31,7 @@ public class ArchiveUriUpdaterTest { private final DeploymentTester tester = new DeploymentTester(new ControllerTester(SystemName.Public)); @Test - public void archive_uri_test() { + void archive_uri_test() { var updater = new ArchiveUriUpdater(tester.controller(), Duration.ofDays(1)); var tenant1 = TenantName.from("tenant1"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java index 7703266c9ba..c0f0322d192 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirerTest.java @@ -6,13 +6,13 @@ import com.yahoo.config.provision.CloudName; import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.ArtifactRegistryMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals; public class ArtifactExpirerTest { @Test - public void maintain() { + void maintain() { DeploymentTester tester = new DeploymentTester(); ArtifactExpirer expirer = new ArtifactExpirer(tester.controller(), Duration.ofDays(1)); ArtifactRegistryMock registry = tester.controllerTester().serviceRegistry().artifactRegistry(CloudName.defaultName()).orElseThrow(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeManagementAssessorTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeManagementAssessorTest.java index add1a319384..084fd243769 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeManagementAssessorTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeManagementAssessorTest.java @@ -7,14 +7,14 @@ import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author smorgrav @@ -24,7 +24,7 @@ public class ChangeManagementAssessorTest { private final ChangeManagementAssessor changeManagementAssessor = new ChangeManagementAssessor(new NodeRepositoryMock()); @Test - public void empty_input_variations() { + void empty_input_variations() { ZoneId zone = ZoneId.from("prod", "eu-trd"); List<String> hostNames = new ArrayList<>(); List<Node> allNodesInZone = new ArrayList<>(); @@ -36,16 +36,16 @@ public class ChangeManagementAssessorTest { } @Test - public void one_host_one_cluster_no_groups() { + void one_host_one_cluster_no_groups() { ZoneId zone = ZoneId.from("prod", "eu-trd"); List<String> hostNames = Collections.singletonList("host1"); List<Node> allNodesInZone = new ArrayList<>(); - allNodesInZone.add(createNode("node1", "host1", "default", 0 )); - allNodesInZone.add(createNode("node2", "host1", "default", 0 )); - allNodesInZone.add(createNode("node3", "host1", "default", 0 )); + allNodesInZone.add(createNode("node1", "host1", "default", 0)); + allNodesInZone.add(createNode("node2", "host1", "default", 0)); + allNodesInZone.add(createNode("node3", "host1", "default", 0)); // Add an not impacted hosts - allNodesInZone.add(createNode("node4", "host2", "default", 0 )); + allNodesInZone.add(createNode("node4", "host2", "default", 0)); // Add tenant hosts allNodesInZone.add(createHost("host1", NodeType.host)); @@ -67,27 +67,27 @@ public class ChangeManagementAssessorTest { } @Test - public void one_of_two_groups_in_one_of_two_clusters() { + void one_of_two_groups_in_one_of_two_clusters() { ZoneId zone = ZoneId.from("prod", "eu-trd"); List<String> hostNames = List.of("host1", "host2", "host5"); List<Node> allNodesInZone = new ArrayList<>(); // Two impacted nodes on host1 - allNodesInZone.add(createNode("node1", "host1", "default", 0 )); - allNodesInZone.add(createNode("node2", "host1", "default", 0 )); + allNodesInZone.add(createNode("node1", "host1", "default", 0)); + allNodesInZone.add(createNode("node2", "host1", "default", 0)); // One impacted nodes on host2 - allNodesInZone.add(createNode("node3", "host2", "default", 0 )); + allNodesInZone.add(createNode("node3", "host2", "default", 0)); // Another group on hosts not impacted - allNodesInZone.add(createNode("node4", "host3", "default", 1 )); - allNodesInZone.add(createNode("node5", "host3", "default", 1 )); - allNodesInZone.add(createNode("node6", "host3", "default", 1 )); + allNodesInZone.add(createNode("node4", "host3", "default", 1)); + allNodesInZone.add(createNode("node5", "host3", "default", 1)); + allNodesInZone.add(createNode("node6", "host3", "default", 1)); // Another cluster on hosts not impacted - this one also with three different groups (should all be ignored here) - allNodesInZone.add(createNode("node4", "host4", "myman", 4 )); - allNodesInZone.add(createNode("node5", "host4", "myman", 5 )); - allNodesInZone.add(createNode("node6", "host4", "myman", 6 )); + allNodesInZone.add(createNode("node4", "host4", "myman", 4)); + allNodesInZone.add(createNode("node5", "host4", "myman", 5)); + allNodesInZone.add(createNode("node6", "host4", "myman", 6)); // Add tenant hosts allNodesInZone.add(createHost("host1", NodeType.host)); @@ -114,14 +114,14 @@ public class ChangeManagementAssessorTest { assertEquals(2, hostAssessments.size()); assertTrue(hostAssessments.stream().anyMatch(hostAssessment -> hostAssessment.hostName.equals("host1") && - hostAssessment.switchName.equals("switch1") && - hostAssessment.numberOfChildren == 2 && - hostAssessment.numberOfProblematicChildren == 2 + hostAssessment.switchName.equals("switch1") && + hostAssessment.numberOfChildren == 2 && + hostAssessment.numberOfProblematicChildren == 2 )); } @Test - public void two_config_nodes() { + void two_config_nodes() { var zone = ZoneId.from("prod", "eu-trd"); var hostNames = List.of("config1", "config2"); var allNodesInZone = new ArrayList<Node>(); @@ -139,7 +139,7 @@ public class ChangeManagementAssessorTest { } @Test - public void one_of_three_proxy_nodes() { + void one_of_three_proxy_nodes() { var zone = ZoneId.from("prod", "eu-trd"); var hostNames = List.of("routing1"); var allNodesInZone = new ArrayList<Node>(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainerTest.java index 7607a4f602d..a20a2e26d22 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ChangeRequestMaintainerTest.java @@ -8,13 +8,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestSourc import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestSource.Status; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.MockChangeRequestClient; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.ZonedDateTime; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author olaa @@ -26,7 +26,7 @@ public class ChangeRequestMaintainerTest { private final ChangeRequestMaintainer changeRequestMaintainer = new ChangeRequestMaintainer(tester.controller(), Duration.ofMinutes(1)); @Test - public void updates_status_time_and_approval() { + void updates_status_time_and_approval() { var time = ZonedDateTime.now(); var persistedChangeRequest = persistedChangeRequest("some-id", time.minusDays(5), Status.WAITING_FOR_APPROVAL); tester.curator().writeChangeRequest(persistedChangeRequest); @@ -43,7 +43,7 @@ public class ChangeRequestMaintainerTest { } @Test - public void deletes_old_change_requests() { + void deletes_old_change_requests() { var now = ZonedDateTime.now(); var before = now.minus(Duration.ofDays(8)); var newChangeRequest = persistedChangeRequest("new", now, Status.CLOSED); @@ -60,7 +60,7 @@ public class ChangeRequestMaintainerTest { } @Test - public void approves_change_request_if_non_prod() { + void approves_change_request_if_non_prod() { var time = ZonedDateTime.now(); var prodChangeRequest = newChangeRequest("id1", ChangeRequest.Approval.REQUESTED, time, Status.WAITING_FOR_APPROVAL); var nonProdApprovalRequested = newChangeRequest("id2", "unknown-node", ChangeRequest.Approval.REQUESTED, time, Status.WAITING_FOR_APPROVAL); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java index 187b8f932cf..c5c70998624 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirerTest.java @@ -9,16 +9,15 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo; import com.yahoo.vespa.hosted.controller.tenant.Tenant; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author ogronnesby @@ -30,63 +29,63 @@ public class CloudTrialExpirerTest { private final CloudTrialExpirer expirer = new CloudTrialExpirer(tester.controller(), Duration.ofMinutes(5)); @Test - public void expire_inactive_tenant() { + void expire_inactive_tenant() { registerTenant("trial-tenant", "trial", Duration.ofDays(14).plusMillis(1)); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertPlan("trial-tenant", "none"); } @Test - public void tombstone_inactive_none() { - registerTenant("none-tenant", "none", Duration.ofDays(365).plusMillis(1)); - expirer.maintain(); + void tombstone_inactive_none() { + registerTenant("none-tenant", "none", Duration.ofDays(183).plusMillis(1)); + assertEquals(1.0, expirer.maintain()); assertEquals(Tenant.Type.deleted, tester.controller().tenants().get(TenantName.from("none-tenant"), true).get().type()); } @Test - public void keep_inactive_nontrial_tenants() { + void keep_inactive_nontrial_tenants() { registerTenant("not-a-trial-tenant", "pay-as-you-go", Duration.ofDays(30)); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertPlan("not-a-trial-tenant", "pay-as-you-go"); } @Test - public void keep_active_trial_tenants() { + void keep_active_trial_tenants() { registerTenant("active-trial-tenant", "trial", Duration.ofHours(14).minusMillis(1)); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertPlan("active-trial-tenant", "trial"); } @Test - public void keep_inactive_exempt_tenants() { + void keep_inactive_exempt_tenants() { registerTenant("exempt-trial-tenant", "trial", Duration.ofDays(40)); ((InMemoryFlagSource) tester.controller().flagSource()).withListFlag(PermanentFlags.EXTENDED_TRIAL_TENANTS.id(), List.of("exempt-trial-tenant"), String.class); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertPlan("exempt-trial-tenant", "trial"); } @Test - public void keep_inactive_trial_tenants_with_deployments() { + void keep_inactive_trial_tenants_with_deployments() { registerTenant("with-deployments", "trial", Duration.ofDays(30)); registerDeployment("with-deployments", "my-app", "default"); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertPlan("with-deployments", "trial"); } @Test - public void delete_tenants_with_applications_with_no_deployments() { - registerTenant("with-apps", "trial", Duration.ofDays(366)); + void delete_tenants_with_applications_with_no_deployments() { + registerTenant("with-apps", "trial", Duration.ofDays(184)); tester.createApplication("with-apps", "app1", "instance1"); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertPlan("with-apps", "none"); - expirer.maintain(); + assertEquals(1.0, expirer.maintain()); assertTrue(tester.controller().tenants().get("with-apps").isEmpty()); } @Test - public void keep_tenants_without_applications_that_are_idle() { - registerTenant("active", "none", Duration.ofDays(364)); - expirer.maintain(); + void keep_tenants_without_applications_that_are_idle() { + registerTenant("active", "none", Duration.ofDays(182)); + assertEquals(1.0, expirer.maintain()); assertPlan("active", "none"); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java index 37b138527fe..f0c11c0ddbd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainerTest.java @@ -6,8 +6,8 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.net.URI; import java.time.Duration; @@ -16,8 +16,8 @@ import java.util.List; import java.util.Optional; import java.util.function.Supplier; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author mpolden @@ -27,30 +27,32 @@ public class ContactInformationMaintainerTest { private ControllerTester tester; private ContactInformationMaintainer maintainer; - @Before + @BeforeEach public void before() { tester = new ControllerTester(); maintainer = new ContactInformationMaintainer(tester.controller(), Duration.ofDays(1)); } @Test - public void updates_contact_information() { + void updates_contact_information() { PropertyId propertyId1 = new PropertyId("1"); PropertyId propertyId2 = new PropertyId("2"); TenantName name1 = tester.createTenant("tenant1", "domain1", 1L); TenantName name2 = tester.createTenant("zenant1", "domain2", 2L); Supplier<AthenzTenant> tenant1 = () -> (AthenzTenant) tester.controller().tenants().require(name1); Supplier<AthenzTenant> tenant2 = () -> (AthenzTenant) tester.controller().tenants().require(name2); - assertFalse("No contact information initially", tenant1.get().contact().isPresent()); - assertFalse("No contact information initially", tenant2.get().contact().isPresent()); + assertFalse(tenant1.get().contact().isPresent(), "No contact information initially"); + assertFalse(tenant2.get().contact().isPresent(), "No contact information initially"); Contact contact = testContact(); - tester.serviceRegistry().contactRetriever().addContact(propertyId1, () -> { throw new RuntimeException("ERROR"); }); + tester.serviceRegistry().contactRetriever().addContact(propertyId1, () -> { + throw new RuntimeException("ERROR"); + }); tester.serviceRegistry().contactRetriever().addContact(propertyId2, () -> contact); maintainer.maintain(); - assertEquals("No contact information added due to error", Optional.empty(), tenant1.get().contact()); - assertEquals("Contact information added", Optional.of(contact), tenant2.get().contact()); + assertEquals(Optional.empty(), tenant1.get().contact(), "No contact information added due to error"); + assertEquals(Optional.of(contact), tenant2.get().contact(), "Contact information added"); } private static Contact testContact() { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java index a452d550488..63e2c99cb6e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java @@ -5,14 +5,14 @@ import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.integration.MetricsMock; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.EnumSet; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -21,13 +21,13 @@ public class ControllerMaintainerTest { private ControllerTester tester; - @Before + @BeforeEach public void before() { tester = new ControllerTester(); } @Test - public void only_runs_in_permitted_systems() { + void only_runs_in_permitted_systems() { AtomicInteger executions = new AtomicInteger(); new TestControllerMaintainer(tester.controller(), SystemName.cd, executions).run(); new TestControllerMaintainer(tester.controller(), SystemName.main, executions).run(); @@ -35,7 +35,7 @@ public class ControllerMaintainerTest { } @Test - public void records_metric() { + void records_metric() { TestControllerMaintainer maintainer = new TestControllerMaintainer(tester.controller(), SystemName.main, new AtomicInteger()); maintainer.run(); assertEquals(1.0, successFactorMetric(), 0.0000001); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java index 294272041b6..7cfe5dee3de 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java @@ -7,13 +7,13 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumerMock; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; import java.util.Map; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author ldalves @@ -23,7 +23,7 @@ public class CostReportMaintainerTest { private final ControllerTester tester = new ControllerTester(); @Test - public void maintain() { + void maintain() { tester.clock().setInstant(Instant.EPOCH); tester.zoneRegistry().setZones( ZoneApiMock.newBuilder().withId("prod.us-east-3").withCloud("yahoo").build(), @@ -35,9 +35,9 @@ public class CostReportMaintainerTest { CostReportConsumerMock costReportConsumer = new CostReportConsumerMock( (csv) -> assertEquals( "Date,Property,Reserved Cpu Cores,Reserved Memory GB,Reserved Disk Space GB,Usage Fraction\n" + - "1970-01-01,Property1,96.0,96.0,2000.0,0.3055555555555555\n" + - "1970-01-01,Property3,128.0,96.0,2000.0,0.3333333333333333\n" + - "1970-01-01,Property2,160.0,96.0,2000.0,0.3611111111111111", + "1970-01-01,Property1,96.0,96.0,2000.0,0.3055555555555555\n" + + "1970-01-01,Property3,128.0,96.0,2000.0,0.3333333333333333\n" + + "1970-01-01,Property2,160.0,96.0,2000.0,0.3611111111111111", csv), Map.of(new Property("Property3"), new ResourceAllocation(256, 192, 4000, NodeResources.Architecture.getDefault())) ); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java index 10a8bb79c2e..0c238ea7c9d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirerTest.java @@ -12,13 +12,13 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.deployment.RunStatus; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Optional; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; /** * @author bratseth @@ -28,7 +28,7 @@ public class DeploymentExpirerTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void testDeploymentExpiry() { + void testDeploymentExpiry() { ZoneId devZone = ZoneId.from(Environment.dev, RegionName.from("us-east-1")); tester.controllerTester().zoneRegistry().setDeploymentTimeToLive(devZone, Duration.ofDays(14)); DeploymentExpirer expirer = new DeploymentExpirer(tester.controller(), Duration.ofDays(1)); @@ -59,9 +59,10 @@ public class DeploymentExpirerTest { Run lastRun = tester.jobs().last(devApp.instanceId(), DeploymentContext.devUsEast1).get(); assertSame(RunStatus.error, lastRun.status()); Deployment deployment = tester.applications().requireInstance(devApp.instanceId()) - .deployments().get(devZone); - assertEquals("Time of last run is after time of deployment", Duration.ofDays(12), - Duration.between(deployment.at(), lastRun.end().get())); + .deployments().get(devZone); + assertEquals(Duration.ofDays(12), + Duration.between(deployment.at(), lastRun.end().get()), + "Time of last run is after time of deployment"); // Dev application does not expire based on time of successful deployment tester.clock().advance(Duration.ofDays(2)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java index 637a8832533..2f84b58dbae 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporterTest.java @@ -13,8 +13,8 @@ import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.HashMap; @@ -27,9 +27,9 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.sys import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentIssueReporter.maxFailureAge; import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentIssueReporter.maxInactivity; import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentIssueReporter.upgradeGracePeriod; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author jonmv @@ -48,7 +48,7 @@ public class DeploymentIssueReporterTest { private DeploymentIssueReporter reporter; private MockDeploymentIssues issues; - @Before + @BeforeEach public void setup() { tester = new DeploymentTester(); issues = new MockDeploymentIssues(); @@ -56,7 +56,7 @@ public class DeploymentIssueReporterTest { } @Test - public void nonProductionAppGetsNoIssues() { + void nonProductionAppGetsNoIssues() { tester.controllerTester().upgradeSystem(Version.fromString("6.2")); var app = tester.newDeploymentContext("application", "tenant", "default"); Contact contact = tester.controllerTester().serviceRegistry().contactRetrieverMock().contact(); @@ -68,11 +68,11 @@ public class DeploymentIssueReporterTest { // Advance to where deployment issues should be detected. tester.clock().advance(maxFailureAge.plus(Duration.ofDays(1))); - assertFalse("No issues are produced for app.", issues.isOpenFor(app.application().id())); + assertFalse(issues.isOpenFor(app.application().id()), "No issues are produced for app."); } @Test - public void testDeploymentFailureReporting() { + void testDeploymentFailureReporting() { tester.controllerTester().upgradeSystem(Version.fromString("6.2")); // Create and deploy one application for each of three tenants. @@ -102,7 +102,7 @@ public class DeploymentIssueReporterTest { reporter.maintain(); reporter.maintain(); - assertEquals("No deployments are detected as failing for a long time initially.", 0, issues.size()); + assertEquals(0, issues.size(), "No deployments are detected as failing for a long time initially."); // Advance to where deployment issues should be detected. @@ -110,18 +110,18 @@ public class DeploymentIssueReporterTest { reporter.maintain(); reporter.maintain(); - assertTrue("One issue is produced for app1.", issues.isOpenFor(app1.application().id())); - assertFalse("No issues are produced for app2.", issues.isOpenFor(app2.application().id())); - assertTrue("One issue is produced for app3.", issues.isOpenFor(app3.application().id())); + assertTrue(issues.isOpenFor(app1.application().id()), "One issue is produced for app1."); + assertFalse(issues.isOpenFor(app2.application().id()), "No issues are produced for app2."); + assertTrue(issues.isOpenFor(app3.application().id()), "One issue is produced for app3."); // app3 closes their issue prematurely; see that it is refiled. issues.closeFor(app3.application().id()); - assertFalse("No issue is open for app3.", issues.isOpenFor(app3.application().id())); + assertFalse(issues.isOpenFor(app3.application().id()), "No issue is open for app3."); reporter.maintain(); reporter.maintain(); - assertTrue("Issue is re-filed for app3.", issues.isOpenFor(app3.application().id())); + assertTrue(issues.isOpenFor(app3.application().id()), "Issue is re-filed for app3."); // Some time passes; tenant1 leaves her issue unattended, while tenant3 starts work and updates the issue. tester.clock().advance(maxInactivity.plus(maxFailureAge)); @@ -129,7 +129,7 @@ public class DeploymentIssueReporterTest { reporter.maintain(); reporter.maintain(); - assertEquals("The issue for app1 is escalated once.", 1, issues.escalationLevelFor(app1.application().id())); + assertEquals(1, issues.escalationLevelFor(app1.application().id()), "The issue for app1 is escalated once."); // app3 fixes their problems, but the ticket for app3 is left open; see the resolved ticket is not escalated when another escalation period has passed. @@ -138,9 +138,9 @@ public class DeploymentIssueReporterTest { reporter.maintain(); reporter.maintain(); - assertFalse("We no longer have a platform issue.", issues.platformIssue()); - assertEquals("The issue for app1 is escalated once more.", 2, issues.escalationLevelFor(app1.application().id())); - assertEquals("The issue for app3 is not escalated.", 0, issues.escalationLevelFor(app3.application().id())); + assertFalse(issues.platformIssue(), "We no longer have a platform issue."); + assertEquals(2, issues.escalationLevelFor(app1.application().id()), "The issue for app1 is escalated once more."); + assertEquals(0, issues.escalationLevelFor(app3.application().id()), "The issue for app3 is not escalated."); // app3 now has a new failure past max failure age; see that a new issue is filed. @@ -149,7 +149,7 @@ public class DeploymentIssueReporterTest { reporter.maintain(); reporter.maintain(); - assertTrue("A new issue is filed for app3.", issues.isOpenFor(app3.application().id())); + assertTrue(issues.isOpenFor(app3.application().id()), "A new issue is filed for app3."); // app2 is changed to be a canary @@ -167,15 +167,15 @@ public class DeploymentIssueReporterTest { tester.controllerTester().upgradeSystem(version); assertEquals(VespaVersion.Confidence.broken, tester.controller().readVersionStatus().systemVersion().get().confidence()); - assertFalse("We have no platform issues initially.", issues.platformIssue()); + assertFalse(issues.platformIssue(), "We have no platform issues initially."); reporter.maintain(); reporter.maintain(); - assertFalse("We have no platform issue before the grace period is out for the failing canary.", issues.platformIssue()); + assertFalse(issues.platformIssue(), "We have no platform issue before the grace period is out for the failing canary."); tester.clock().advance(upgradeGracePeriod.plus(upgradeGracePeriod)); reporter.maintain(); reporter.maintain(); - assertTrue("We get a platform issue when confidence is broken", issues.platformIssue()); - assertFalse("No deployment issue is filed for app2, which has a version upgrade failure.", issues.isOpenFor(app2.application().id())); + assertTrue(issues.platformIssue(), "We get a platform issue when confidence is broken"); + assertFalse(issues.isOpenFor(app2.application().id()), "No deployment issue is filed for app2, which has a version upgrade failure."); app2.runJob(systemTest); tester.controllerTester().upgradeSystem(version); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java index 112519bb717..6ac0509c0f8 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainerTest.java @@ -13,7 +13,7 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -23,8 +23,8 @@ import java.util.Map; import java.util.function.Supplier; import static java.time.temporal.ChronoUnit.MILLIS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author smorgrav @@ -35,7 +35,7 @@ public class DeploymentMetricsMaintainerTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void updates_metrics() { + void updates_metrics() { var application = tester.newDeploymentContext(); application.runJob(DeploymentContext.devUsEast1, new ApplicationPackage(new byte[0]), Version.fromString("7.1")); @@ -46,17 +46,17 @@ public class DeploymentMetricsMaintainerTest { // No metrics gathered yet assertEquals(0, app.get().metrics().queryServiceQuality(), 0); assertEquals(0, deployment.get().metrics().documentCount(), 0); - assertFalse("No timestamp set", deployment.get().metrics().instant().isPresent()); - assertFalse("Never received any queries", deployment.get().activity().lastQueried().isPresent()); - assertFalse("Never received any writes", deployment.get().activity().lastWritten().isPresent()); + assertFalse(deployment.get().metrics().instant().isPresent(), "No timestamp set"); + assertFalse(deployment.get().activity().lastQueried().isPresent(), "Never received any queries"); + assertFalse(deployment.get().activity().lastWritten().isPresent(), "Never received any writes"); // Metrics are gathered and saved to application application.runJob(DeploymentContext.devUsEast1, new ApplicationPackage(new byte[0]), Version.fromString("7.5.5")); var metrics0 = Map.of(ClusterMetrics.QUERIES_PER_SECOND, 1D, - ClusterMetrics.FEED_PER_SECOND, 2D, - ClusterMetrics.DOCUMENT_COUNT, 3D, - ClusterMetrics.QUERY_LATENCY, 4D, - ClusterMetrics.FEED_LATENCY, 5D); + ClusterMetrics.FEED_PER_SECOND, 2D, + ClusterMetrics.DOCUMENT_COUNT, 3D, + ClusterMetrics.QUERY_LATENCY, 4D, + ClusterMetrics.FEED_LATENCY, 5D); setMetrics(application.application().id().defaultInstance(), metrics0); maintainer.maintain(); Instant t1 = tester.clock().instant().truncatedTo(MILLIS); @@ -107,7 +107,7 @@ public class DeploymentMetricsMaintainerTest { } @Test - public void cluster_metric_aggregation_test() { + void cluster_metric_aggregation_test() { List<ClusterMetrics> clusterMetrics = List.of( new ClusterMetrics("niceCluster", "container", Map.of("queriesPerSecond", 23.0, "queryLatency", 1337.0), Map.of()), new ClusterMetrics("alsoNiceCluster", "container", Map.of("queriesPerSecond", 11.0, "queryLatency", 12.0), Map.of())); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgraderTest.java index 653ad2bb08a..df0afda838e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgraderTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgraderTest.java @@ -8,7 +8,7 @@ import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -17,8 +17,8 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.dev import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.productionUsWest1; import static com.yahoo.vespa.hosted.controller.maintenance.DeploymentUpgrader.mostLikelyWeeHour; import static java.time.temporal.ChronoUnit.MILLIS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author jonmv @@ -28,7 +28,7 @@ public class DeploymentUpgraderTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void testDeploymentUpgrading() { + void testDeploymentUpgrading() { ZoneId devZone = ZoneId.from(Environment.dev, RegionName.from("us-east-1")); DeploymentUpgrader upgrader = new DeploymentUpgrader(tester.controller(), Duration.ofDays(1)); var devApp = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -76,11 +76,11 @@ public class DeploymentUpgraderTest { } @Test - public void testNight() { - assertEquals(16, mostLikelyWeeHour(new int[]{ 0, 1, 2, 3, 4, 5, 6 })); - assertEquals(14, mostLikelyWeeHour(new int[]{ 22, 23, 0, 1, 2, 3, 4 })); - assertEquals(18, mostLikelyWeeHour(new int[]{ 6, 5, 4, 3, 2, 1, 0 })); - assertEquals(20, mostLikelyWeeHour(new int[]{ 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 11 })); + void testNight() { + assertEquals(16, mostLikelyWeeHour(new int[]{0, 1, 2, 3, 4, 5, 6})); + assertEquals(14, mostLikelyWeeHour(new int[]{22, 23, 0, 1, 2, 3, 4})); + assertEquals(18, mostLikelyWeeHour(new int[]{6, 5, 4, 3, 2, 1, 0})); + assertEquals(20, mostLikelyWeeHour(new int[]{0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 11})); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java index 6bcb4284a14..47a1b44d196 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java @@ -1,26 +1,39 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.maintenance; +import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMock; +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.Deployment; +import com.yahoo.vespa.hosted.controller.application.DeploymentActivity; +import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; +import com.yahoo.vespa.hosted.controller.application.QuotaUsage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock; -import org.junit.Test; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; +import java.util.OptionalDouble; +import java.util.stream.Stream; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.productionUsWest1; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.stagingTest; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.systemTest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author andreer @@ -33,14 +46,14 @@ public class EndpointCertificateMaintainerTest { private final EndpointCertificateMetadata exampleMetadata = new EndpointCertificateMetadata("keyName", "certName", 0, 0, "root-request-uuid", Optional.of("leaf-request-uuid"), List.of(), "issuer", Optional.empty(), Optional.empty()); @Test - public void old_and_unused_cert_is_deleted() { + void old_and_unused_cert_is_deleted() { tester.curator().writeEndpointCertificateMetadata(ApplicationId.defaultId(), exampleMetadata); assertEquals(1.0, maintainer.maintain(), 0.0000001); assertTrue(tester.curator().readEndpointCertificateMetadata(ApplicationId.defaultId()).isEmpty()); } @Test - public void unused_but_recently_used_cert_is_not_deleted() { + void unused_but_recently_used_cert_is_not_deleted() { EndpointCertificateMetadata recentlyRequestedCert = exampleMetadata.withLastRequested(tester.clock().instant().minusSeconds(3600).getEpochSecond()); tester.curator().writeEndpointCertificateMetadata(ApplicationId.defaultId(), recentlyRequestedCert); assertEquals(1.0, maintainer.maintain(), 0.0000001); @@ -48,7 +61,7 @@ public class EndpointCertificateMaintainerTest { } @Test - public void refreshed_certificate_is_updated() { + void refreshed_certificate_is_updated() { EndpointCertificateMetadata recentlyRequestedCert = exampleMetadata.withLastRequested(tester.clock().instant().minusSeconds(3600).getEpochSecond()); tester.curator().writeEndpointCertificateMetadata(ApplicationId.defaultId(), recentlyRequestedCert); @@ -63,7 +76,7 @@ public class EndpointCertificateMaintainerTest { } @Test - public void certificate_in_use_is_not_deleted() { + void certificate_in_use_is_not_deleted() { var appId = ApplicationId.from("tenant", "application", "default"); DeploymentTester deploymentTester = new DeploymentTester(tester); @@ -82,7 +95,7 @@ public class EndpointCertificateMaintainerTest { } @Test - public void refreshed_certificate_is_discovered_and_after_four_days_deployed() { + void refreshed_certificate_is_discovered_and_after_four_days_deployed() { var appId = ApplicationId.from("tenant", "application", "default"); DeploymentTester deploymentTester = new DeploymentTester(tester); @@ -111,7 +124,7 @@ public class EndpointCertificateMaintainerTest { deploymentContext.assertNotRunning(productionUsWest1); var updatedMetadata = tester.curator().readEndpointCertificateMetadata(appId).orElseThrow(); assertNotEquals(originalMetadata.leafRequestId().orElseThrow(), updatedMetadata.leafRequestId().orElseThrow()); - assertEquals(updatedMetadata.version(), originalMetadata.version()+1); + assertEquals(updatedMetadata.version(), originalMetadata.version() + 1); // after another 4 days, we should force trigger deployment if it hasn't already happened tester.clock().advance(Duration.ofDays(4).plusSeconds(1)); @@ -121,7 +134,22 @@ public class EndpointCertificateMaintainerTest { } @Test - public void unmaintained_cert_is_deleted() { + void testEligibleSorting() { + EndpointCertificateMaintainer.EligibleJob oldestDeployment = makeDeploymentAtAge(5); + assertEquals( + oldestDeployment, + Stream.of(makeDeploymentAtAge(2), oldestDeployment, makeDeploymentAtAge(4)).min(maintainer.oldestFirst).get()); + } + + @NotNull + private EndpointCertificateMaintainer.EligibleJob makeDeploymentAtAge(int ageInDays) { + var deployment = new Deployment(ZoneId.defaultId(), RevisionId.forProduction(1), Version.emptyVersion, + Instant.now().minus(ageInDays, ChronoUnit.DAYS), DeploymentMetrics.none, DeploymentActivity.none, QuotaUsage.none, OptionalDouble.empty()); + return new EndpointCertificateMaintainer.EligibleJob(deployment, ApplicationId.defaultId(), JobType.prod("somewhere")); + } + + @Test + void unmaintained_cert_is_deleted() { EndpointCertificateMock endpointCertificateProvider = (EndpointCertificateMock) tester.controller().serviceRegistry().endpointCertificateProvider(); ApplicationId unknown = ApplicationId.fromSerializedForm("applicationid:is:unknown"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdaterTest.java index 890f0b41098..1ef32b8e347 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdaterTest.java @@ -8,15 +8,15 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter; import com.yahoo.vespa.hosted.controller.api.integration.entity.NodeEntity; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -25,7 +25,7 @@ import static org.junit.Assert.assertTrue; public class HostInfoUpdaterTest { @Test - public void maintain() { + void maintain() { ControllerTester tester = new ControllerTester(); tester.serviceRegistry().configServer().nodeRepository().allowPatching(true); addNodeEntities(tester); @@ -36,8 +36,8 @@ public class HostInfoUpdaterTest { List<Node> nodes = allNodes(tester); assertFalse(nodes.isEmpty()); for (var node : nodes) { - assertEquals("Node " + node.hostname().value() + (node.type().isHost() ? " has" : " does not have") - + " switch hostname", node.type().isHost(), node.switchHostname().isPresent()); + assertEquals(node.type().isHost(), node.switchHostname().isPresent(), "Node " + node.hostname().value() + (node.type().isHost() ? " has" : " does not have") + + " switch hostname"); if (node.type().isHost()) { assertEquals("tor-" + node.hostname().value(), node.switchHostname().get()); } @@ -85,18 +85,18 @@ public class HostInfoUpdaterTest { ZoneId zone = tester.zoneRegistry().zones().controllerUpgraded().all().ids().get(0); String hostnameSuffix = ".prod." + zone.value(); Node configNode = Node.builder().hostname(HostName.of("cfg3" + hostnameSuffix)) - .type(NodeType.config) - .build(); + .type(NodeType.config) + .build(); Node configHost = Node.builder().hostname(HostName.of("cfghost3" + hostnameSuffix)) - .type(NodeType.confighost) - .build(); + .type(NodeType.confighost) + .build(); tester.serviceRegistry().configServer().nodeRepository().putNodes(zone, List.of(configNode, configHost)); String switchHostname = switchHostname(configHost); NodeEntity configNodeEntity = new NodeEntity("cfg3" + hostnameSuffix, "RD350G", "Lenovo", switchHostname); tester.serviceRegistry().entityService().addNodeEntity(configNodeEntity); maintainer.maintain(); assertEquals(switchHostname, getNode(configHost.hostname(), tester).switchHostname().get()); - assertTrue("Switch hostname is not set for non-host", getNode(configNode.hostname(), tester).switchHostname().isEmpty()); + assertTrue(getNode(configNode.hostname(), tester).switchHostname().isEmpty(), "Switch hostname is not set for non-host"); } private static Node getNode(HostName hostname, ControllerTester tester) { 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 b9ced334e5a..e7e542d2b82 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 @@ -21,7 +21,7 @@ import com.yahoo.vespa.hosted.controller.deployment.StepRunner; import com.yahoo.vespa.hosted.controller.deployment.Submission; import com.yahoo.vespa.hosted.controller.deployment.Versions; import com.yahoo.vespa.hosted.controller.integration.MetricsMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Collections; @@ -63,12 +63,12 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.installReal; 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.startTests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author jonmv @@ -82,10 +82,10 @@ public class JobRunnerTest { Optional.empty()); @Test - public void multiThreadedExecutionFinishes() { + void multiThreadedExecutionFinishes() { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); - StepRunner stepRunner = (step, id) -> id.type().equals(stagingTest) && step.get() == startTests? Optional.of(error) : Optional.of(running); + StepRunner stepRunner = (step, id) -> id.type().equals(stagingTest) && step.get() == startTests ? Optional.of(error) : Optional.of(running); Phaser phaser = new Phaser(1); JobRunner runner = new JobRunner(tester.controller(), Duration.ofDays(1), phasedExecutor(phaser), stepRunner); @@ -98,7 +98,8 @@ public class JobRunnerTest { start(jobs, id, systemTest); fail("Job is already running, so this should not be allowed!"); } - catch (IllegalArgumentException ignored) { } + catch (IllegalArgumentException ignored) { + } start(jobs, id, stagingTest); assertTrue(jobs.last(id, systemTest).get().stepStatuses().values().stream().allMatch(unfinished::equals)); @@ -118,7 +119,7 @@ public class JobRunnerTest { } @Test - public void stepLogic() { + void stepLogic() { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); Map<Step, RunStatus> outcomes = new EnumMap<>(Step.class); @@ -225,7 +226,7 @@ public class JobRunnerTest { } @Test - public void locksAndGarbage() throws InterruptedException, BrokenBarrierException { + void locksAndGarbage() throws InterruptedException, BrokenBarrierException { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); // Hang during tester deployment, until notified. @@ -242,10 +243,12 @@ public class JobRunnerTest { runner.maintain(); barrier.await(); try { - jobs.locked(id, systemTest, deactivateTester, step -> { }); + jobs.locked(id, systemTest, deactivateTester, step -> { + }); fail("deployTester step should still be locked!"); } - catch (TimeoutException ignored) { } + catch (TimeoutException ignored) { + } // Thread is still trying to deploy tester -- delete application, and see all data is garbage collected. assertEquals(Collections.singletonList(runId), jobs.active().stream().map(run -> run.id()).collect(Collectors.toList())); @@ -264,7 +267,7 @@ public class JobRunnerTest { } @Test - public void historyPruning() { + void historyPruning() { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); JobRunner runner = new JobRunner(tester.controller(), Duration.ofDays(1), inThreadExecutor(), (id, step) -> Optional.of(running)); @@ -341,7 +344,7 @@ public class JobRunnerTest { } @Test - public void onlySuccessfulRunExpiresThenAnotherFails() { + void onlySuccessfulRunExpiresThenAnotherFails() { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); var app = tester.newDeploymentContext().submit(); @@ -360,7 +363,7 @@ public class JobRunnerTest { } @Test - public void timeout() { + void timeout() { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); Map<Step, RunStatus> outcomes = new EnumMap<>(Step.class); @@ -378,7 +381,7 @@ public class JobRunnerTest { } @Test - public void jobMetrics() throws TimeoutException { + void jobMetrics() throws TimeoutException { DeploymentTester tester = new DeploymentTester(); JobController jobs = tester.controller().jobController(); Map<Step, RunStatus> outcomes = new EnumMap<>(Step.class); @@ -401,10 +404,10 @@ public class JobRunnerTest { } Map<String, String> context = Map.of("applicationId", "tenant.real.default", - "tenantName", "tenant", - "app", "real.default", - "test", "true", - "zone", "test.us-east-1"); + "tenantName", "tenant", + "app", "real.default", + "test", "true", + "zone", "test.us-east-1"); MetricsMock metric = ((MetricsMock) tester.controller().metric()); assertEquals(RunStatus.values().length - 2, metric.getMetric(context::equals, JobMetrics.start).get().intValue()); assertEquals(1, metric.getMetric(context::equals, JobMetrics.abort).get().intValue()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainerTest.java index 225b7cb1d5e..65dab67663e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/MeteringMonitorMaintainerTest.java @@ -8,15 +8,15 @@ import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistryMoc import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceDatabaseClientMock; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.MetricsMock; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; import java.util.Map; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author olaa @@ -31,7 +31,7 @@ public class MeteringMonitorMaintainerTest { private final ApplicationId applicationId = ApplicationId.from("foo", "bar", "default"); private final ZoneId zone = ZoneId.from("prod.aws-us-east-1c"); - @Before + @BeforeEach public void setup() { tester = new ControllerTester(SystemName.Public); deploymentTester = new DeploymentTester(tester); @@ -39,8 +39,9 @@ public class MeteringMonitorMaintainerTest { database = new ResourceDatabaseClientMock(new PlanRegistryMock()); maintainer = new MeteringMonitorMaintainer(tester.controller(), Duration.ofMinutes(5), database, metrics); } + @Test - public void finds_stale_data() { + void finds_stale_data() { deploymentTester.newDeploymentContext(applicationId).submit().deploy(); maintainer.maintain(); var now = tester.clock().instant().getEpochSecond(); 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 215c03030a9..b41c17fcd33 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 @@ -28,7 +28,7 @@ import com.yahoo.vespa.hosted.controller.integration.MetricsMock; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.tenant.Tenant; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -43,9 +43,9 @@ import java.util.stream.Stream; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.productionUsWest1; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.stagingTest; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.systemTest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mortent @@ -56,7 +56,7 @@ public class MetricsReporterTest { private final ZmsClientMock zmsClient = new ZmsClientMock(new AthenzDbMock(), AthenzIdentities.from("mock.identity")); @Test - public void audit_log_metric() { + void audit_log_metric() { var tester = new ControllerTester(); MetricsReporter metricsReporter = createReporter(tester.controller()); @@ -114,7 +114,7 @@ public class MetricsReporterTest { } @Test - public void deployment_fail_ratio() { + void deployment_fail_ratio() { var tester = new DeploymentTester(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -147,7 +147,7 @@ public class MetricsReporterTest { } @Test - public void deployment_average_duration() { + void deployment_average_duration() { var tester = new DeploymentTester(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -156,8 +156,8 @@ public class MetricsReporterTest { MetricsReporter reporter = createReporter(tester.controller()); var context = tester.newDeploymentContext() - .submit(applicationPackage) - .deploy(); + .submit(applicationPackage) + .deploy(); reporter.maintain(); assertEquals(Duration.ZERO, getAverageDeploymentDuration(context.instanceId())); // An exceptionally fast deployment :-) @@ -180,19 +180,19 @@ public class MetricsReporterTest { // Another deployment starts and stalls for 12 hours context.submit(applicationPackage) - .triggerJobs(); + .triggerJobs(); tester.clock().advance(Duration.ofHours(12)); reporter.maintain(); assertEquals(Duration.ofHours(12) // hanging system-test - .plus(Duration.ofHours(12)) // hanging staging-test - .plus(Duration.ofMinutes(90)) // previous production job - .dividedBy(3), // Total number of orchestrated jobs - getAverageDeploymentDuration(context.instanceId())); + .plus(Duration.ofHours(12)) // hanging staging-test + .plus(Duration.ofMinutes(90)) // previous production job + .dividedBy(3), // Total number of orchestrated jobs + getAverageDeploymentDuration(context.instanceId())); } @Test - public void deployments_failing_upgrade() { + void deployments_failing_upgrade() { var tester = new DeploymentTester(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -208,40 +208,40 @@ public class MetricsReporterTest { // Failing application change is not counted context.submit(applicationPackage) - .triggerJobs() - .failDeployment(systemTest); + .triggerJobs() + .failDeployment(systemTest); reporter.maintain(); assertEquals(0, getDeploymentsFailingUpgrade(context.instanceId())); // Application change completes context.deploy(); - assertFalse("Change deployed", context.instance().change().hasTargets()); + assertFalse(context.instance().change().hasTargets(), "Change deployed"); // New versions is released and upgrade fails in test environments Version version = Version.fromString("7.1"); tester.controllerTester().upgradeSystem(version); tester.upgrader().maintain(); context.failDeployment(systemTest) - .failDeployment(stagingTest); + .failDeployment(stagingTest); reporter.maintain(); assertEquals(2, getDeploymentsFailingUpgrade(context.instanceId())); // Test and staging pass and upgrade fails in production context.runJob(systemTest) - .runJob(stagingTest) - .failDeployment(productionUsWest1); + .runJob(stagingTest) + .failDeployment(productionUsWest1); reporter.maintain(); assertEquals(1, getDeploymentsFailingUpgrade(context.instanceId())); // Upgrade eventually succeeds context.runJob(productionUsWest1); - assertFalse("Upgrade deployed", context.instance().change().hasTargets()); + assertFalse(context.instance().change().hasTargets(), "Upgrade deployed"); reporter.maintain(); assertEquals(0, getDeploymentsFailingUpgrade(context.instanceId())); } @Test - public void deployment_warnings_metric() { + void deployment_warnings_metric() { var tester = new DeploymentTester(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") @@ -257,22 +257,22 @@ public class MetricsReporterTest { } @Test - public void build_time_reporting() { + void build_time_reporting() { var tester = new DeploymentTester(); var applicationPackage = new ApplicationPackageBuilder().region("us-west-1").build(); var context = tester.newDeploymentContext() - .submit(applicationPackage) - .deploy(); + .submit(applicationPackage) + .deploy(); assertEquals(1000, context.application().revisions().get(context.lastSubmission().get()).buildTime().get().toEpochMilli()); MetricsReporter reporter = createReporter(tester.controller()); reporter.maintain(); assertEquals(tester.clock().instant().getEpochSecond() - 1, - getMetric(MetricsReporter.DEPLOYMENT_BUILD_AGE_SECONDS, context.instanceId())); + getMetric(MetricsReporter.DEPLOYMENT_BUILD_AGE_SECONDS, context.instanceId())); } @Test - public void name_service_queue_size_metric() { + void name_service_queue_size_metric() { var tester = new DeploymentTester(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .globalServiceId("default") @@ -281,21 +281,21 @@ public class MetricsReporterTest { .build(); MetricsReporter reporter = createReporter(tester.controller()); var context = tester.newDeploymentContext() - .deferDnsUpdates(); + .deferDnsUpdates(); reporter.maintain(); - assertEquals("Queue is empty initially", 0, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue()); + assertEquals(0, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue(), "Queue is empty initially"); context.submit(applicationPackage).deploy(); reporter.maintain(); - assertEquals("Deployment queues name services requests", 2, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue()); + assertEquals(2, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue(), "Deployment queues name services requests"); context.flushDnsUpdates(); reporter.maintain(); - assertEquals("Queue consumed", 0, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue()); + assertEquals(0, metrics.getMetric(MetricsReporter.NAME_SERVICE_REQUESTS_QUEUED).intValue(), "Queue consumed"); } @Test - public void platform_change_duration() { + void platform_change_duration() { var tester = new ControllerTester(); var reporter = createReporter(tester.controller()); var zone = ZoneId.from("prod.eu-west-1"); @@ -326,11 +326,12 @@ public class MetricsReporterTest { assertPlatformChangeDuration(Duration.ZERO, hosts); // 1/3 nodes upgrade within timeout - assertEquals("Wanted version is raised for all nodes", version, - getNodes(zone, hosts, tester).stream() - .map(Node::wantedVersion) - .min(Comparator.naturalOrder()) - .get()); + assertEquals(version, + getNodes(zone, hosts, tester).stream() + .map(Node::wantedVersion) + .min(Comparator.naturalOrder()) + .get(), + "Wanted version is raised for all nodes"); suspend(hosts, zone, tester); var firstHost = hosts.get(0); upgradeTo(version, List.of(firstHost), zone, tester); @@ -351,7 +352,7 @@ public class MetricsReporterTest { } @Test - public void os_change_duration() { + void os_change_duration() { var tester = new ControllerTester(); var reporter = createReporter(tester.controller()); var zone = ZoneId.from("prod.eu-west-1"); @@ -389,12 +390,13 @@ public class MetricsReporterTest { assertOsChangeDuration(Duration.ZERO, hosts); // Nodes are told to upgrade, but do not suspend yet - assertEquals("Wanted OS version is raised for all nodes", nextVersion, - tester.configServer().nodeRepository().list(zone, NodeFilter.all().applications(SystemApplication.tenantHost.id())).stream() - .map(Node::wantedOsVersion).min(Comparator.naturalOrder()).get()); - assertTrue("No nodes are suspended", tester.controller().serviceRegistry().configServer() - .nodeRepository().list(zone, NodeFilter.all()).stream() - .noneMatch(node -> node.serviceState() == Node.ServiceState.allowedDown)); + assertEquals(nextVersion, + tester.configServer().nodeRepository().list(zone, NodeFilter.all().applications(SystemApplication.tenantHost.id())).stream() + .map(Node::wantedOsVersion).min(Comparator.naturalOrder()).get(), + "Wanted OS version is raised for all nodes"); + assertTrue(tester.controller().serviceRegistry().configServer() + .nodeRepository().list(zone, NodeFilter.all()).stream() + .noneMatch(node -> node.serviceState() == Node.ServiceState.allowedDown), "No nodes are suspended"); // Another 30 minutes pass tester.clock().advance(Duration.ofMinutes(30)); @@ -440,19 +442,19 @@ public class MetricsReporterTest { // Dimensions used for node count metric are only known OS versions Set<Version> versionDimensions = metrics.getMetrics((dimensions) -> true) - .entrySet() - .stream() - .filter(kv -> kv.getValue().containsKey(MetricsReporter.OS_NODE_COUNT)) - .map(kv -> kv.getKey().getDimensions()) - .map(dimensions -> dimensions.get("currentVersion")) - .map(Version::fromString) - .collect(Collectors.toSet()); - assertTrue("Reports only OS versions", allVersions.containsAll(versionDimensions)); + .entrySet() + .stream() + .filter(kv -> kv.getValue().containsKey(MetricsReporter.OS_NODE_COUNT)) + .map(kv -> kv.getKey().getDimensions()) + .map(dimensions -> dimensions.get("currentVersion")) + .map(Version::fromString) + .collect(Collectors.toSet()); + assertTrue(allVersions.containsAll(versionDimensions), "Reports only OS versions"); } } @Test - public void broken_system_version() { + void broken_system_version() { var tester = new DeploymentTester().atMondayMorning(); var ctx = tester.newDeploymentContext(); var applicationPackage = new ApplicationPackageBuilder().upgradePolicy("canary").region("us-west-1").build(); @@ -485,7 +487,7 @@ public class MetricsReporterTest { } @Test - public void tenant_counter() { + void tenant_counter() { var tester = new ControllerTester(SystemName.Public); tester.createTenant("foo", Tenant.Type.cloud); tester.createTenant("bar", Tenant.Type.cloud); @@ -501,17 +503,17 @@ public class MetricsReporterTest { } @Test - public void overdue_upgrade_metric() { + void overdue_upgrade_metric() { ApplicationPackage pkg = new ApplicationPackageBuilder().region("us-west-1") - // window 1 - .blockChange(false, true, "mon-tue", "2-9", "CET") - // window 2 - .blockChange(false, true, "mon-tue", "1-8,11-12", "CET") - // window 3 - .blockChange(false, true, "wed-thu", "0-23", "CET") - // window 4 (does not apply to upgrade) - .blockChange(true, false, "mon-sun", "0-7", "CET") - .build(); + // window 1 + .blockChange(false, true, "mon-tue", "2-9", "CET") + // window 2 + .blockChange(false, true, "mon-tue", "1-8,11-12", "CET") + // window 3 + .blockChange(false, true, "wed-thu", "0-23", "CET") + // window 4 (does not apply to upgrade) + .blockChange(true, false, "mon-sun", "0-7", "CET") + .build(); Instant mondayNight = Instant.parse("2021-12-13T23:30:00.00Z"); DeploymentTester tester = new DeploymentTester().at(mondayNight); @@ -519,8 +521,8 @@ public class MetricsReporterTest { DeploymentContext context = tester.newDeploymentContext(); Supplier<Duration> metric = () -> { reporter.maintain(); - return Duration.ofSeconds(metrics.getMetric(context.instanceId(),MetricsReporter.DEPLOYMENT_OVERDUE_UPGRADE) - .get().longValue()); + return Duration.ofSeconds(metrics.getMetric(context.instanceId(), MetricsReporter.DEPLOYMENT_OVERDUE_UPGRADE) + .get().longValue()); }; // Deploy completely once @@ -532,33 +534,33 @@ public class MetricsReporterTest { // Start production job for upgrade, without completing it context.runJob(systemTest) - .runJob(stagingTest) - .triggerJobs() - .assertRunning(productionUsWest1); - assertEquals("Upgrade is not overdue yet", Duration.ZERO, metric.get()); + .runJob(stagingTest) + .triggerJobs() + .assertRunning(productionUsWest1); + assertEquals(Duration.ZERO, metric.get(), "Upgrade is not overdue yet"); // Upgrade continues into block window tester.clock().advance(Duration.ofHours(1)); // Tuesday at 00:30 (01:30 CET) - assertEquals("Upgrade is overdue measured relative to window 2", Duration.ofHours(0).plusMinutes(30), metric.get()); + assertEquals(Duration.ofHours(0).plusMinutes(30), metric.get(), "Upgrade is overdue measured relative to window 2"); tester.clock().advance(Duration.ofHours(1)); // Tuesday at 01:30 (02:30 CET) - assertEquals("Upgrade is overdue measured relative to window 2", Duration.ofHours(1).plusMinutes(30), metric.get()); + assertEquals(Duration.ofHours(1).plusMinutes(30), metric.get(), "Upgrade is overdue measured relative to window 2"); tester.clock().advance(Duration.ofHours(1)); // Tuesday at 02:30 (03:30 CET) - assertEquals("Upgrade is overdue measured relative to window 2", Duration.ofHours(2).plusMinutes(30), metric.get()); + assertEquals(Duration.ofHours(2).plusMinutes(30), metric.get(), "Upgrade is overdue measured relative to window 2"); tester.clock().advance(Duration.ofHours(6)); // Tuesday at 08:30 (09:30 CET) - assertEquals("Upgrade is overdue measured relative to window 1", Duration.ofHours(8).plusMinutes(30), metric.get()); + assertEquals(Duration.ofHours(8).plusMinutes(30), metric.get(), "Upgrade is overdue measured relative to window 1"); tester.clock().advance(Duration.ofHours(1)); // Tuesday at 09:30 (10:30 CET) - assertEquals("Upgrade is no longer overdue", Duration.ZERO, metric.get()); + assertEquals(Duration.ZERO, metric.get(), "Upgrade is no longer overdue"); tester.clock().advance(Duration.ofDays(2)); // Thursday at 10:30 (11:30 CET) - assertEquals("Upgrade is overdue measure relative to window 3", Duration.ofHours(34).plusMinutes(30), metric.get()); + assertEquals(Duration.ofHours(34).plusMinutes(30), metric.get(), "Upgrade is overdue measure relative to window 3"); } @Test - public void zms_quota_metrics() { + void zms_quota_metrics() { var tester = new ControllerTester(); var reporter = createReporter(tester.controller()); reporter.maintain(); @@ -576,7 +578,7 @@ public class MetricsReporterTest { .map(Number::longValue) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Expected to find metric for version " + version)); - assertEquals("Expected number of nodes are on " + version.toFullString(), n, nodeCount); + assertEquals(n, nodeCount, "Expected number of nodes are on " + version.toFullString()); } private void assertPlatformNodeCount(int n, Version version) { @@ -647,15 +649,13 @@ public class MetricsReporterTest { private void assertPlatformChangeDuration(Duration duration, List<Node> nodes) { for (var node : nodes) { - assertEquals("Platform change duration of " + node.hostname(), - duration, getChangeDuration(MetricsReporter.PLATFORM_CHANGE_DURATION, node.hostname())); + assertEquals(duration, getChangeDuration(MetricsReporter.PLATFORM_CHANGE_DURATION, node.hostname()), "Platform change duration of " + node.hostname()); } } private void assertOsChangeDuration(Duration duration, List<Node> nodes) { for (var node : nodes) { - assertEquals("OS change duration of " + node.hostname(), - duration, getChangeDuration(MetricsReporter.OS_CHANGE_DURATION, node.hostname())); + assertEquals(duration, getChangeDuration(MetricsReporter.OS_CHANGE_DURATION, node.hostname()), "OS change duration of " + node.hostname()); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java index 300aa86b5ea..5ed441398fd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeSchedulerTest.java @@ -9,14 +9,20 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.deployment.OsRelease; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; +import java.util.Optional; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -24,10 +30,10 @@ import static org.junit.Assert.assertTrue; public class OsUpgradeSchedulerTest { @Test - public void schedule_calendar_versioned_release() { + void schedule_calendar_versioned_release() { ControllerTester tester = new ControllerTester(); OsUpgradeScheduler scheduler = new OsUpgradeScheduler(tester.controller(), Duration.ofDays(1)); - Instant t0 = Instant.parse("2021-01-23T00:00:00.00Z"); // Outside trigger period + Instant t0 = Instant.parse("2022-01-16T00:00:00.00Z"); // Outside trigger period tester.clock().setInstant(t0); CloudName cloud = CloudName.from("cloud"); @@ -36,10 +42,10 @@ public class OsUpgradeSchedulerTest { // Initial run does nothing as the cloud does not have a target scheduler.maintain(); - assertTrue("No target set", tester.controller().osVersionTarget(cloud).isEmpty()); + assertTrue(tester.controller().osVersionTarget(cloud).isEmpty(), "No target set"); - // Target is set - Version version0 = Version.fromString("7.0.0.20210123190005"); + // Target is set manually + Version version0 = Version.fromString("7.0.0.20220101"); tester.controller().upgradeOsIn(cloud, version0, Duration.ofDays(1), false); // Target remains unchanged as it hasn't expired yet @@ -49,27 +55,40 @@ public class OsUpgradeSchedulerTest { assertEquals(version0, tester.controller().osVersionTarget(cloud).get().osVersion().version()); } - // Just over 45 days pass, and a new target replaces the expired one - Version version1 = Version.fromString("7.0.0.20210302"); - tester.clock().advance(Duration.ofDays(15).plus(Duration.ofSeconds(1))); + // Enough days pass that the next release is triggered + Version version1 = Version.fromString("7.0.0.20220228"); + tester.clock().advance(Duration.ofDays(30)); scheduler.maintain(); - assertEquals("Target is unchanged because we're outside trigger period", version0, - tester.controller().osVersionTarget(cloud).get().osVersion().version()); - tester.clock().advance(Duration.ofHours(7)); // Put us inside the trigger period + assertEquals(version0, + tester.controller().osVersionTarget(cloud).get().osVersion().version(), + "Target is unchanged because we're outside trigger period"); + tester.clock().advance(Duration.ofHours(9).plusMinutes(5)); // Put us inside the trigger period + assertEquals("2022-03-17T09:05:00", formatInstant(tester.clock().instant())); + Optional<OsUpgradeScheduler.Change> change = scheduler.changeIn(cloud); + assertTrue(change.isPresent()); + assertEquals("2022-03-17T07:00:00", formatInstant(change.get().scheduleAt())); scheduler.maintain(); - assertEquals("New target set", version1, - tester.controller().osVersionTarget(cloud).get().osVersion().version()); + assertEquals(version1, + tester.controller().osVersionTarget(cloud).get().osVersion().version(), + "New target set"); - // A few days pass and target remains unchanged + // A few more days pass and target remains unchanged tester.clock().advance(Duration.ofDays(2)); scheduler.maintain(); assertEquals(version1, tester.controller().osVersionTarget(cloud).get().osVersion().version()); + + // Estimate next change + Optional<OsUpgradeScheduler.Change> nextChange = scheduler.changeIn(cloud); + assertTrue(nextChange.isPresent()); + assertEquals("7.0.0.20220425", nextChange.get().version().toFullString()); + assertEquals("2022-05-02T07:00:00", formatInstant(nextChange.get().scheduleAt())); } @Test - public void schedule_stable_release() { + void schedule_stable_release() { ControllerTester tester = new ControllerTester(); - Instant t0 = Instant.parse("2021-06-21T07:00:00.00Z"); // Inside trigger period + OsUpgradeScheduler scheduler = new OsUpgradeScheduler(tester.controller(), Duration.ofDays(1)); + Instant t0 = Instant.parse("2021-06-21T06:00:00.00Z"); // Outside trigger period tester.clock().setInstant(t0); // Set initial target @@ -77,23 +96,27 @@ public class OsUpgradeSchedulerTest { Version version0 = Version.fromString("8.0"); tester.controller().upgradeOsIn(cloud, version0, Duration.ZERO, false); - // Stable release is scheduled immediately + // Stable release is scheduled once trigger period opens Version version1 = Version.fromString("8.1"); tester.serviceRegistry().artifactRepository().addRelease(new OsRelease(version1, OsRelease.Tag.stable, - tester.clock().instant())); - scheduleUpgradeAfter(Duration.ZERO, version1, tester); + tester.clock().instant())); + scheduleUpgradeAfter(Duration.ZERO, version0, scheduler, tester); + assertEquals(version1, scheduler.changeIn(cloud).get().version(), "Change available"); + scheduleUpgradeAfter(Duration.ofHours(1), version1, scheduler, tester); // Inside trigger period // A newer version is triggered manually Version version3 = Version.fromString("8.3"); tester.controller().upgradeOsIn(cloud, version3, Duration.ZERO, false); // Nothing happens in next iteration as tagged release is older than manually triggered version - scheduleUpgradeAfter(Duration.ofDays(7), version3, tester); + scheduleUpgradeAfter(Duration.ofDays(7), version3, scheduler, tester); + assertTrue(scheduler.changeIn(cloud).isEmpty()); } @Test - public void schedule_latest_release_in_cd() { + void schedule_latest_release_in_cd() { ControllerTester tester = new ControllerTester(SystemName.cd); + OsUpgradeScheduler scheduler = new OsUpgradeScheduler(tester.controller(), Duration.ofDays(1)); Instant t0 = Instant.parse("2021-06-21T07:00:00.00Z"); // Inside trigger period tester.clock().setInstant(t0); @@ -105,25 +128,47 @@ public class OsUpgradeSchedulerTest { // Latest release is not scheduled immediately Version version1 = Version.fromString("8.1"); tester.serviceRegistry().artifactRepository().addRelease(new OsRelease(version1, OsRelease.Tag.latest, - tester.clock().instant())); - scheduleUpgradeAfter(Duration.ZERO, version0, tester); + tester.clock().instant())); + assertEquals(version1, scheduler.changeIn(cloud).get().version(), "Change available"); + scheduleUpgradeAfter(Duration.ZERO, version0, scheduler, tester); // Cooldown period passes and latest release is scheduled - scheduleUpgradeAfter(Duration.ofDays(1), version1, tester); + scheduleUpgradeAfter(Duration.ofDays(1), version1, scheduler, tester); + } + + @Test + void schedule_of_calender_versioned_releases() { + Map<String, String> tests = Map.of("2022-01-01", "2021-12-27", + "2022-03-01", "2021-12-27", + "2022-03-02", "2022-02-28", + "2022-04-30", "2022-02-28", + "2022-05-01", "2022-04-25", + "2022-06-29", "2022-04-25", + "2022-07-01", "2022-06-27", + "2022-08-28", "2022-06-27", + "2022-08-29", "2022-08-29"); + tests.forEach((now, expectedVersion) -> { + Instant instant = LocalDate.parse(now).atStartOfDay().toInstant(ZoneOffset.UTC); + LocalDate dateOfWantedVersion = OsUpgradeScheduler.CalendarVersionedRelease.dateOfWantedVersion(instant); + assertEquals(LocalDate.parse(expectedVersion), dateOfWantedVersion, "scheduled wanted version at " + now); + }); } - private void scheduleUpgradeAfter(Duration duration, Version version, ControllerTester tester) { + private void scheduleUpgradeAfter(Duration duration, Version version, OsUpgradeScheduler scheduler, ControllerTester tester) { tester.clock().advance(duration); - new OsUpgradeScheduler(tester.controller(), Duration.ofDays(1)).maintain(); + scheduler.maintain(); CloudName cloud = tester.controller().clouds().iterator().next(); OsVersionTarget target = tester.controller().osVersionTarget(cloud).get(); assertEquals(version, target.osVersion().version()); - assertEquals("No budget when scheduling a tagged release", - Duration.ZERO, target.upgradeBudget()); + assertEquals(Duration.ZERO, target.upgradeBudget(), "No budget when scheduling a tagged release"); } private static ZoneApi zone(String id, CloudName cloud) { return ZoneApiMock.newBuilder().withId(id).with(cloud).build(); } + private static String formatInstant(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneOffset.UTC).format(DateTimeFormatter.ISO_DATE_TIME); + } + } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java index 3c3f0053e91..1c8a7ac641d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgraderTest.java @@ -3,7 +3,9 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; +import com.yahoo.config.provision.zone.NodeSlice; import com.yahoo.config.provision.zone.UpgradePolicy; +import com.yahoo.config.provision.zone.UpgradePolicy.Step; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.ControllerTester; @@ -13,16 +15,18 @@ import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.versions.NodeVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.function.Function; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; +import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -33,7 +37,7 @@ public class OsUpgraderTest { private final OsVersionStatusUpdater statusUpdater = new OsVersionStatusUpdater(tester.controller(), Duration.ofDays(1)); @Test - public void upgrade_os() { + void upgrade_os() { CloudName cloud1 = CloudName.from("c1"); CloudName cloud2 = CloudName.from("c2"); ZoneApi zone0 = zone("prod.us-north-42", "prod.controller", cloud1); @@ -45,25 +49,26 @@ public class OsUpgraderTest { UpgradePolicy upgradePolicy = UpgradePolicy.builder() .upgrade(zone0) .upgrade(zone1) - .upgradeInParallel(zone2, zone3) + .upgrade(Step.of(zone2, zone3).require(NodeSlice.minCount(1))) .upgrade(zone5) // Belongs to a different cloud and is ignored by this upgrader .upgrade(zone4) .build(); OsUpgrader osUpgrader = osUpgrader(upgradePolicy, cloud1, false); // Bootstrap system - List<ZoneId> nonControllerZones = List.of(zone1, zone2, zone3, zone4, zone5).stream() - .map(ZoneApi::getVirtualId) - .collect(Collectors.toList()); + List<ZoneId> nonControllerZones = Stream.of(zone1, zone2, zone3, zone4, zone5) + .map(ZoneApi::getVirtualId) + .collect(Collectors.toList()); tester.configServer().bootstrap(nonControllerZones, List.of(SystemApplication.tenantHost)); tester.configServer().addNodes(List.of(zone0.getVirtualId()), List.of(SystemApplication.controllerHost)); // Add system application that exists in a real system, but isn't eligible for OS upgrades tester.configServer().addNodes(nonControllerZones, List.of(SystemApplication.configServer)); - // Fail a few nodes. Failed nodes should not affect versions + // Change state of a few nodes. These should not affect convergence failNodeIn(zone1, SystemApplication.tenantHost); failNodeIn(zone3, SystemApplication.tenantHost); + Node nodeDeferringOsUpgrade = deferOsUpgradeIn(zone2, SystemApplication.tenantHost); // New OS version released Version version1 = Version.fromString("7.1"); @@ -100,8 +105,14 @@ public class OsUpgraderTest { // zone 4: still on previous version assertWanted(Version.emptyVersion, SystemApplication.tenantHost, zone4); - // zone 2 and 3: completes upgrade - completeUpgrade(version1, SystemApplication.tenantHost, zone2, zone3); + // zone 2 and 3: enough nodes upgrade to satisfy node slice of this step + completeUpgrade(1, version1, SystemApplication.tenantHost, zone2); + completeUpgrade(1, version1, SystemApplication.tenantHost, zone3); + assertEquals(Version.emptyVersion, + nodeRepository().list(zone2.getVirtualId(), NodeFilter.all().hostnames(nodeDeferringOsUpgrade.hostname())) + .get(0) + .currentOsVersion(), + "Current version is unchanged for node deferring OS upgrade"); // zone 4: begins upgrading osUpgrader.maintain(); @@ -110,16 +121,21 @@ public class OsUpgraderTest { // zone 4: completes upgrade completeUpgrade(version1, SystemApplication.tenantHost, zone4); + // zone 2 and 3: stragglers complete upgrade + completeUpgrade(version1, SystemApplication.tenantHost, zone2, zone3); + // Next run does nothing as all zones are upgraded osUpgrader.maintain(); assertWanted(version1, SystemApplication.tenantHost, zone1, zone2, zone3, zone4); statusUpdater.maintain(); - assertTrue("All nodes on target version", tester.controller().osVersionStatus().nodesIn(cloud1).stream() - .allMatch(node -> node.currentVersion().equals(version1))); + assertTrue(tester.controller().osVersionStatus().nodesIn(cloud1).stream() + .filter(node -> !node.hostname().equals(nodeDeferringOsUpgrade.hostname())) + .allMatch(node -> node.currentVersion().equals(version1)), + "All non-deferring nodes are on target version"); } @Test - public void upgrade_os_with_budget() { + void upgrade_os_with_budget() { CloudName cloud = CloudName.from("cloud"); ZoneApi zone0 = zone("prod.us-north-42", "prod.controller", cloud); ZoneApi zone1 = zone("dev.us-east-1", cloud); @@ -127,17 +143,17 @@ public class OsUpgraderTest { ZoneApi zone3 = zone("prod.us-central-1", cloud); ZoneApi zone4 = zone("prod.eu-west-1", cloud); UpgradePolicy upgradePolicy = UpgradePolicy.builder() - .upgrade(zone0) - .upgrade(zone1) - .upgradeInParallel(zone2, zone3) - .upgrade(zone4) - .build(); + .upgrade(zone0) + .upgrade(zone1) + .upgradeInParallel(zone2, zone3) + .upgrade(zone4) + .build(); OsUpgrader osUpgrader = osUpgrader(upgradePolicy, cloud, true); // Bootstrap system List<SystemApplication> nodeTypes = List.of(SystemApplication.configServerHost, SystemApplication.tenantHost); tester.configServer().bootstrap(List.of(zone1.getId(), zone2.getId(), zone3.getId(), zone4.getId()), - nodeTypes); + nodeTypes); tester.configServer().addNodes(List.of(zone0.getVirtualId()), List.of(SystemApplication.controllerHost)); // Upgrade with budget @@ -150,7 +166,7 @@ public class OsUpgraderTest { // Controllers upgrade first osUpgrader.maintain(); assertWanted(version, SystemApplication.controllerHost, zone0); - assertEquals("Controller zone gets a zero budget", Duration.ZERO, upgradeBudget(zone0, SystemApplication.controllerHost, version)); + assertEquals(Duration.ZERO, upgradeBudget(zone0, SystemApplication.controllerHost, version), "Controller zone gets a zero budget"); completeUpgrade(version, SystemApplication.controllerHost, zone0); statusUpdater.maintain(); assertEquals(3, nodesOn(version).size()); @@ -158,16 +174,17 @@ public class OsUpgraderTest { // First zone upgrades osUpgrader.maintain(); for (var nodeType : nodeTypes) { - assertEquals("Dev zone gets a zero budget", Duration.ZERO, upgradeBudget(zone1, nodeType, version)); + assertEquals(Duration.ofHours(4), upgradeBudget(zone1, nodeType, version)); completeUpgrade(version, nodeType, zone1); } // Next set of zones upgrade osUpgrader.maintain(); - for (var zone : List.of(zone2, zone3)) { + for (var zone : List.of(zone1, zone2, zone3)) { for (var nodeType : nodeTypes) { - assertEquals("Parallel prod zones share the budget of a single zone", Duration.ofHours(6), - upgradeBudget(zone, nodeType, version)); + assertEquals(Duration.ofHours(4), + upgradeBudget(zone, nodeType, version), + "Parallel prod zones share the budget of a single zone"); completeUpgrade(version, nodeType, zone); } } @@ -175,31 +192,32 @@ public class OsUpgraderTest { // Last zone upgrades osUpgrader.maintain(); for (var nodeType : nodeTypes) { - assertEquals(nodeType + " in last prod zone gets the budget of a single zone", Duration.ofHours(6), - upgradeBudget(zone4, nodeType, version)); + assertEquals(Duration.ofHours(4), + upgradeBudget(zone4, nodeType, version), + nodeType + " in last prod zone gets the budget of a single zone"); completeUpgrade(version, nodeType, zone4); } // All host applications upgraded statusUpdater.maintain(); - assertTrue("All nodes on target version", tester.controller().osVersionStatus().nodesIn(cloud).stream() - .allMatch(node -> node.currentVersion().equals(version))); + assertTrue(tester.controller().osVersionStatus().nodesIn(cloud).stream() + .allMatch(node -> node.currentVersion().equals(version)), "All nodes on target version"); } @Test - public void upgrade_os_nodes_choose_newer_version() { + void upgrade_os_nodes_choose_newer_version() { CloudName cloud = CloudName.from("cloud"); ZoneApi zone1 = zone("dev.us-east-1", cloud); ZoneApi zone2 = zone("prod.us-west-1", cloud); UpgradePolicy upgradePolicy = UpgradePolicy.builder() - .upgrade(zone1) - .upgrade(zone2) - .build(); + .upgrade(zone1) + .upgrade(zone2) + .build(); OsUpgrader osUpgrader = osUpgrader(upgradePolicy, cloud, false); // Bootstrap system tester.configServer().bootstrap(List.of(zone1.getId(), zone2.getId()), - List.of(SystemApplication.tenantHost)); + List.of(SystemApplication.tenantHost)); // New OS version released Version version = Version.fromString("7.1"); @@ -211,28 +229,28 @@ public class OsUpgraderTest { osUpgrader.maintain(); assertWanted(version, SystemApplication.tenantHost, zone1); Version chosenVersion = Version.fromString("7.1.1"); // Upgrade mechanism chooses a slightly newer version - completeUpgrade(version, chosenVersion, SystemApplication.tenantHost, zone1); + completeUpgrade(Integer.MAX_VALUE, version, chosenVersion, SystemApplication.tenantHost, zone1); statusUpdater.maintain(); assertEquals(3, nodesOn(chosenVersion).size()); // zone 2 upgrades osUpgrader.maintain(); assertWanted(version, SystemApplication.tenantHost, zone2); - completeUpgrade(version, chosenVersion, SystemApplication.tenantHost, zone2); + completeUpgrade(Integer.MAX_VALUE, version, chosenVersion, SystemApplication.tenantHost, zone2); statusUpdater.maintain(); assertEquals(6, nodesOn(chosenVersion).size()); // No more upgrades osUpgrader.maintain(); assertWanted(version, SystemApplication.tenantHost, zone1, zone2); - assertTrue("All nodes on target version or newer", tester.controller().osVersionStatus().nodesIn(cloud).stream() - .noneMatch(node -> node.currentVersion().isBefore(version))); + assertTrue(tester.controller().osVersionStatus().nodesIn(cloud).stream() + .noneMatch(node -> node.currentVersion().isBefore(version)), "All nodes on target version or newer"); } private Duration upgradeBudget(ZoneApi zone, SystemApplication application, Version version) { var upgradeBudget = tester.configServer().nodeRepository().osUpgradeBudget(zone.getVirtualId(), application.nodeType(), version); - assertTrue("Expected budget for upgrade to " + version + " of " + application.id() + " in " + zone.getVirtualId(), - upgradeBudget.isPresent()); + assertTrue(upgradeBudget.isPresent(), + "Expected budget for upgrade to " + version + " of " + application.id() + " in " + zone.getVirtualId()); return upgradeBudget.get(); } @@ -249,9 +267,10 @@ public class OsUpgraderTest { private void assertWanted(Version version, SystemApplication application, ZoneApi... zones) { for (var zone : zones) { - assertEquals("Target version set for " + application + " in " + zone.getVirtualId(), version, + assertEquals(version, nodeRepository().targetVersionsOf(zone.getVirtualId()).osVersion(application.nodeType()) - .orElse(Version.emptyVersion)); + .orElse(Version.emptyVersion), + "Target version set for " + application + " in " + zone.getVirtualId()); } } @@ -259,7 +278,7 @@ public class OsUpgraderTest { ZoneApi... zones) { for (ZoneApi zone : zones) { for (Node node : nodesRequiredToUpgrade(zone, application)) { - assertEquals(application + " version in " + zone, version, versionField.apply(node)); + assertEquals(version, versionField.apply(node), application + " version in " + zone.getId()); } } } @@ -267,33 +286,55 @@ public class OsUpgraderTest { private List<Node> nodesRequiredToUpgrade(ZoneApi zone, SystemApplication application) { return nodeRepository().list(zone.getVirtualId(), NodeFilter.all().applications(application.id())) .stream() - .filter(OsUpgrader::canUpgrade) + .filter(node -> OsUpgrader.canUpgrade(node, false)) .collect(Collectors.toList()); } - private void failNodeIn(ZoneApi zone, SystemApplication application) { + private Node failNodeIn(ZoneApi zone, SystemApplication application) { + return patchOneNodeIn(zone, application, (node) -> Node.builder(node).state(Node.State.failed).build()); + } + + private Node deferOsUpgradeIn(ZoneApi zone, SystemApplication application) { + return patchOneNodeIn(zone, application, (node) -> Node.builder(node).deferOsUpgrade(true).build()); + } + + private Node patchOneNodeIn(ZoneApi zone, SystemApplication application, UnaryOperator<Node> patcher) { List<Node> nodes = nodeRepository().list(zone.getVirtualId(), NodeFilter.all().applications(application.id())); if (nodes.isEmpty()) { throw new IllegalArgumentException("No nodes allocated to " + application.id()); } Node node = nodes.get(0); - nodeRepository().putNodes(zone.getVirtualId(), Node.builder(node).state(Node.State.failed).build()); + Node newNode = patcher.apply(node); + nodeRepository().putNodes(zone.getVirtualId(), newNode); + return newNode; } /** Simulate OS upgrade of nodes allocated to application. In a real system this is done by the node itself */ private void completeUpgrade(Version version, SystemApplication application, ZoneApi... zones) { - completeUpgrade(version, version, application, zones); + completeUpgrade(-1, version, application, zones); } - private void completeUpgrade(Version wantedVersion, Version version, SystemApplication application, ZoneApi... zones) { + private void completeUpgrade(int nodeCount, Version version, SystemApplication application, ZoneApi... zones) { + completeUpgrade(nodeCount, version, version, application, zones); + } + + private void completeUpgrade(int nodeCount, Version wantedVersion, Version version, SystemApplication application, ZoneApi... zones) { assertWanted(wantedVersion, application, zones); for (ZoneApi zone : zones) { - for (Node node : nodesRequiredToUpgrade(zone, application)) { + int nodesUpgraded = 0; + List<Node> nodes = nodesRequiredToUpgrade(zone, application); + for (Node node : nodes) { + if (node.currentVersion().equals(wantedVersion)) continue; nodeRepository().putNodes(zone.getVirtualId(), Node.builder(node).wantedOsVersion(version) .currentOsVersion(version) .build()); + if (++nodesUpgraded == nodeCount) { + break; + } + } + if (nodesUpgraded == nodes.size()) { + assertCurrent(version, application, zone); } - assertCurrent(version, application, zone); } } @@ -302,7 +343,7 @@ public class OsUpgraderTest { } private OsUpgrader osUpgrader(UpgradePolicy upgradePolicy, CloudName cloud, boolean reprovisionToUpgradeOs) { - var zones = upgradePolicy.steps().stream().flatMap(Collection::stream).collect(Collectors.toList()); + var zones = upgradePolicy.steps().stream().map(Step::zones).flatMap(Collection::stream).collect(Collectors.toList()); tester.zoneRegistry() .setZones(zones) .setOsUpgradePolicy(cloud, upgradePolicy); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdaterTest.java index f2d738bd2e1..0bd3810e9a4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdaterTest.java @@ -8,14 +8,14 @@ import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.versions.OsVersion; import com.yahoo.vespa.hosted.controller.versions.OsVersionStatus; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue; public class OsVersionStatusUpdaterTest { @Test - public void test_update() { + void test_update() { ControllerTester tester = new ControllerTester(); OsVersionStatusUpdater statusUpdater = new OsVersionStatusUpdater(tester.controller(), Duration.ofDays(1) ); @@ -45,8 +45,8 @@ public class OsVersionStatusUpdaterTest { var osVersions = tester.controller().osVersionStatus().versions(); assertEquals(2, osVersions.size()); - assertFalse("All nodes on unknown version", osVersions.get(new OsVersion(Version.emptyVersion, cloud)).isEmpty()); - assertTrue("No nodes on current target", osVersions.get(new OsVersion(version1, cloud)).isEmpty()); + assertFalse(osVersions.get(new OsVersion(Version.emptyVersion, cloud)).isEmpty(), "All nodes on unknown version"); + assertTrue(osVersions.get(new OsVersion(version1, cloud)).isEmpty(), "No nodes on current target"); } } 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 e989486c595..9b2a1607e76 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 @@ -8,13 +8,13 @@ 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; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Optional; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author bratseth @@ -22,7 +22,7 @@ import static org.junit.Assert.assertTrue; public class OutstandingChangeDeployerTest { @Test - public void testChangeDeployer() { + void testChangeDeployer() { DeploymentTester tester = new DeploymentTester(); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-west-1") diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggererTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggererTest.java index 53e7dd7ca58..b0601dcd880 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggererTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggererTest.java @@ -6,7 +6,7 @@ import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ApplicationReindexing; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ApplicationReindexing.Cluster; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ApplicationReindexing.Status; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -22,13 +22,13 @@ import static java.time.DayOfWeek.MONDAY; import static java.time.DayOfWeek.THURSDAY; import static java.time.DayOfWeek.TUESDAY; import static java.time.DayOfWeek.WEDNESDAY; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ReindexingTriggererTest { @Test - public void testWindowOfOpportunity() { + void testWindowOfOpportunity() { Duration interval = Duration.ofHours(1); Instant now = Instant.now(); Instant doom = now.plus(ReindexingTriggerer.reindexingPeriod); @@ -43,42 +43,42 @@ public class ReindexingTriggererTest { now = now.plus(interval); } // Summer/winter time :'( - assertTrue("Should be in window of opportunity three to five times each period", 3 <= triggered && triggered <= 5); + assertTrue(3 <= triggered && triggered <= 5, "Should be in window of opportunity three to five times each period"); } @Test - public void testReindexingIsReady() { + void testReindexingIsReady() { Instant then = Instant.now(); ApplicationReindexing reindexing = new ApplicationReindexing(true, - Map.of("c", new Cluster(Map.of(), Map.of("d", new Status(then))))); + Map.of("c", new Cluster(Map.of(), Map.of("d", new Status(then))))); Instant now = then; - assertFalse("Should not be ready less than one half-period after last triggering", - reindexingIsReady(reindexing, now)); + assertFalse(reindexingIsReady(reindexing, now), + "Should not be ready less than one half-period after last triggering"); now = now.plus(reindexingPeriod.dividedBy(2)); - assertFalse("Should not be ready one half-period after last triggering", - reindexingIsReady(reindexing, now)); + assertFalse(reindexingIsReady(reindexing, now), + "Should not be ready one half-period after last triggering"); now = now.plusMillis(1); - assertTrue("Should be ready more than one half-period after last triggering", - reindexingIsReady(reindexing, now)); + assertTrue(reindexingIsReady(reindexing, now), + "Should be ready more than one half-period after last triggering"); reindexing = new ApplicationReindexing(true, - Map.of("cluster", - new Cluster(Map.of(), - Map.of("type", - new Status(then, then, null, null, null, null, 1.0))))); - assertFalse("Should not be ready when reindexing is already running", - reindexingIsReady(reindexing, now)); + Map.of("cluster", + new Cluster(Map.of(), + Map.of("type", + new Status(then, then, null, null, null, null, 1.0))))); + assertFalse(reindexingIsReady(reindexing, now), + "Should not be ready when reindexing is already running"); reindexing = new ApplicationReindexing(true, - Map.of("cluster", - new Cluster(Map.of("type", 123L), - Map.of("type", - new Status(then, then, now, null, null, null, 1.0))))); - assertTrue("Should be ready when reindexing is no longer running", - reindexingIsReady(reindexing, now)); + Map.of("cluster", + new Cluster(Map.of("type", 123L), + Map.of("type", + new Status(then, then, now, null, null, null, 1.0))))); + assertTrue(reindexingIsReady(reindexing, now), + "Should be ready when reindexing is no longer running"); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java index a8d39438654..1f92c7f6e41 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java @@ -18,7 +18,7 @@ import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.MetricsMock; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -29,9 +29,9 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author olaa @@ -45,7 +45,7 @@ public class ResourceMeterMaintainerTest { new ResourceMeterMaintainer(tester.controller(), Duration.ofMinutes(5), metrics, resourceClient); @Test - public void updates_deployment_costs() { + void updates_deployment_costs() { ApplicationId app1 = ApplicationId.from("t1", "a1", "default"); ApplicationId app2 = ApplicationId.from("t2", "a1", "default"); ZoneId z1 = ZoneId.from("prod.aws-us-east-1c"); @@ -79,13 +79,13 @@ public class ResourceMeterMaintainerTest { assertEquals(1.72, (Double) metrics.getMetric(context -> z1.value().equals(context.get("zoneId")) && - app1.tenant().value().equals(context.get("tenant")), + app1.tenant().value().equals(context.get("tenant")), "metering.cost.hourly").get(), Double.MIN_VALUE); } @Test - public void testMaintainer() { + void testMaintainer() { setUpZones(); long lastRefreshTime = tester.clock().millis(); tester.curator().writeMeteringRefreshTime(lastRefreshTime); @@ -105,7 +105,7 @@ public class ResourceMeterMaintainerTest { assertEquals(24, app2.getMemoryGb(), Double.MIN_VALUE); assertEquals(500, app2.getDiskGb(), Double.MIN_VALUE); - assertEquals(tester.clock().millis()/1000, metrics.getMetric("metering_last_reported")); + assertEquals(tester.clock().millis() / 1000, metrics.getMetric("metering_last_reported")); assertEquals(2224.0d, (Double) metrics.getMetric("metering_total_reported"), Double.MIN_VALUE); assertEquals(24d, (Double) metrics.getMetric(context -> "tenant1".equals(context.get("tenant")), "metering.vcpu").get(), Double.MIN_VALUE); assertEquals(40d, (Double) metrics.getMetric(context -> "tenant2".equals(context.get("tenant")), "metering.vcpu").get(), Double.MIN_VALUE); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainerTest.java index 1a6976034c5..0068f15ed46 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainerTest.java @@ -10,14 +10,14 @@ import com.yahoo.vespa.hosted.controller.api.integration.aws.MockResourceTagger; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; import java.util.Map; import static com.yahoo.vespa.hosted.controller.maintenance.ResourceTagMaintainer.SHARED_HOST_APPLICATION; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author olaa @@ -27,12 +27,12 @@ public class ResourceTagMaintainerTest { private final ControllerTester tester = new ControllerTester(); @Test - public void maintain() { + void maintain() { setUpZones(); MockResourceTagger mockResourceTagger = new MockResourceTagger(); ResourceTagMaintainer resourceTagMaintainer = new ResourceTagMaintainer(tester.controller(), - Duration.ofMinutes(5), - mockResourceTagger); + Duration.ofMinutes(5), + mockResourceTagger); resourceTagMaintainer.maintain(); assertEquals(2, mockResourceTagger.getValues().size()); Map<HostName, ApplicationId> applicationForHost = mockResourceTagger.getValues().get(ZoneId.from("prod.region-2")); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainerTest.java index bebecf8b52b..69301ea91ef 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainerTest.java @@ -9,13 +9,13 @@ import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.deployment.RetriggerEntry; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.time.Duration; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mortent @@ -25,7 +25,7 @@ public class RetriggerMaintainerTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void processes_queue() throws IOException { + void processes_queue() throws IOException { RetriggerMaintainer maintainer = new RetriggerMaintainer(tester.controller(), Duration.ofDays(1)); ApplicationId applicationId = ApplicationId.from("tenant", "app", "default"); var devApp = tester.newDeploymentContext(applicationId); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java index c09d3ec3a92..7426e0dd23b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/SystemUpgraderTest.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; @@ -19,9 +19,9 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author mpolden @@ -36,19 +36,19 @@ public class SystemUpgraderTest { private final ControllerTester tester = new ControllerTester(); @Test - public void upgrade_system() { + void upgrade_system() { SystemUpgrader systemUpgrader = systemUpgrader( UpgradePolicy.builder() - .upgrade(zone1) - .upgradeInParallel(zone2, zone3) - .upgrade(zone4) - .build() + .upgrade(zone1) + .upgradeInParallel(zone2, zone3) + .upgrade(zone4) + .build() ); Version version1 = Version.fromString("6.5"); // Bootstrap a system without host applications tester.configServer().bootstrap(List.of(zone1.getId(), zone2.getId(), zone3.getId(), zone4.getId()), - SystemApplication.configServer, SystemApplication.proxy); + SystemApplication.configServer, SystemApplication.proxy); // Fail a few nodes. Failed nodes should not affect versions failNodeIn(zone1, SystemApplication.configServer); failNodeIn(zone3, SystemApplication.proxy); @@ -68,58 +68,63 @@ public class SystemUpgraderTest { assertWantedVersion(SystemApplication.configServer, version2, zone1); // Other zones remain on previous version assertWantedVersion(SystemApplication.configServer, version1, zone2, zone3, zone4); - // Zone application is not upgraded yet + // Proxy application is not upgraded yet assertWantedVersion(SystemApplication.proxy, version1, zone1, zone2, zone3, zone4); - // zone1: zone-config-server upgrades + // zone1: config server upgrades and proxy application completeUpgrade(SystemApplication.configServer, version2, zone1); + systemUpgrader.maintain(); - // zone 1: proxy-application upgrades + // zone 1: proxy application upgrades systemUpgrader.maintain(); assertWantedVersion(SystemApplication.proxy, version2, zone1); completeUpgrade(SystemApplication.proxy, version2, zone1); - assertTrue("Deployed proxy application", - tester.configServer().application(SystemApplication.proxy.id(), zone1.getId()).isPresent()); + assertTrue(tester.configServer().application(SystemApplication.proxy.id(), zone1.getId()).isPresent(), + "Deployed proxy application"); // zone 2, 3 and 4: still targets old version assertWantedVersion(SystemApplication.configServer, version1, zone2, zone3, zone4); assertWantedVersion(SystemApplication.proxy, version1, zone2, zone3, zone4); - // zone 2 and 3: upgrade does not start until zone 1 zone-application config converges + // zone 2 and 3: upgrade does not start until zone 1 proxy application config converges systemUpgrader.maintain(); assertWantedVersion(SystemApplication.configServer, version1, zone2, zone3); convergeServices(SystemApplication.proxy, zone1); - // zone 2 and 3: zone-config-server upgrades, first in zone 2, then in zone 3 + // zone 2 and 3: config server upgrades, first in zone 2, then in zone 3 systemUpgrader.maintain(); assertWantedVersion(SystemApplication.configServer, version2, zone2, zone3); assertWantedVersion(SystemApplication.configServer, version1, zone4); assertWantedVersion(SystemApplication.proxy, version1, zone2, zone3, zone4); completeUpgrade(SystemApplication.configServer, version2, zone2); - // zone-application starts upgrading in zone 2, while zone-config-server completes upgrade in zone 3 + // proxy application starts upgrading in zone 2, while config server completes upgrade in zone 3 systemUpgrader.maintain(); assertWantedVersion(SystemApplication.proxy, version2, zone2); assertWantedVersion(SystemApplication.proxy, version1, zone3); completeUpgrade(SystemApplication.configServer, version2, zone3); - // zone 2 and 3: proxy-application upgrades in parallel + // zone 2 and 3: proxy application upgrades in parallel systemUpgrader.maintain(); assertWantedVersion(SystemApplication.proxy, version2, zone2, zone3); completeUpgrade(SystemApplication.proxy, version2, zone2, zone3); convergeServices(SystemApplication.proxy, zone2, zone3); - // zone 4: zone-config-server upgrades + // zone 4: config server upgrades systemUpgrader.maintain(); assertWantedVersion(SystemApplication.configServer, version2, zone4); assertWantedVersion(SystemApplication.proxy, version1, zone4); - completeUpgrade(SystemApplication.configServer, version2, zone4); + // zone 4: proxy application does not upgrade until all config servers are done + completeUpgrade(2, SystemApplication.configServer, version2, zone4); + systemUpgrader.maintain(); + assertWantedVersion(SystemApplication.proxy, version1, zone4); + completeUpgrade(1, SystemApplication.configServer, version2, zone4); // System version remains unchanged until final application upgrades tester.computeVersionStatus(); assertSystemVersion(version1); - // zone 4: proxy-application upgrades + // zone 4: proxy application upgrades systemUpgrader.maintain(); assertWantedVersion(SystemApplication.proxy, version2, zone4); completeUpgrade(SystemApplication.proxy, version2, zone4); @@ -138,12 +143,12 @@ public class SystemUpgraderTest { } @Test - public void upgrade_controller_with_non_converging_application() { + void upgrade_controller_with_non_converging_application() { SystemUpgrader systemUpgrader = systemUpgrader(UpgradePolicy.builder().upgrade(zone1).build()); // Bootstrap system tester.configServer().bootstrap(List.of(zone1.getId()), SystemApplication.configServer, - SystemApplication.proxy); + SystemApplication.proxy); Version version1 = Version.fromString("6.5"); tester.upgradeSystem(version1); @@ -157,7 +162,7 @@ public class SystemUpgraderTest { systemUpgrader.maintain(); completeUpgrade(SystemApplication.proxy, version2, zone1); tester.computeVersionStatus(); - assertSystemVersion(version1); // Unchanged until proxy-application converges + assertSystemVersion(version1); // Unchanged until proxy application converges // Controller upgrades again Version version3 = Version.fromString("6.7"); @@ -165,7 +170,7 @@ public class SystemUpgraderTest { assertSystemVersion(version1); assertControllerVersion(version3); - // zone 1: proxy-application converges and system version changes + // zone 1: proxy application converges and system version changes convergeServices(SystemApplication.proxy, zone1); tester.computeVersionStatus(); assertSystemVersion(version2); @@ -173,13 +178,13 @@ public class SystemUpgraderTest { } @Test - public void upgrade_system_containing_host_applications() { + void upgrade_system_containing_host_applications() { SystemUpgrader systemUpgrader = systemUpgrader( UpgradePolicy.builder() - .upgrade(zone1) - .upgradeInParallel(zone2, zone3) - .upgrade(zone4) - .build() + .upgrade(zone1) + .upgradeInParallel(zone2, zone3) + .upgrade(zone4) + .build() ); Version version1 = Version.fromString("6.5"); @@ -196,9 +201,9 @@ public class SystemUpgraderTest { // System upgrades in zone 1: systemUpgrader.maintain(); List<SystemApplication> allExceptZone = List.of(SystemApplication.configServerHost, - SystemApplication.configServer, - SystemApplication.proxyHost, - SystemApplication.tenantHost); + SystemApplication.configServer, + SystemApplication.proxyHost, + SystemApplication.tenantHost); completeUpgrade(allExceptZone, version2, zone1); systemUpgrader.maintain(); completeUpgrade(SystemApplication.proxy, version2, zone1); @@ -225,7 +230,7 @@ public class SystemUpgraderTest { } @Test - public void downgrading_controller_never_downgrades_system() { + void downgrading_controller_never_downgrades_system() { SystemUpgrader systemUpgrader = systemUpgrader(UpgradePolicy.builder().upgrade(zone1).build()); Version version = Version.fromString("6.5"); @@ -244,7 +249,7 @@ public class SystemUpgraderTest { } @Test - public void upgrade_halts_on_broken_version() { + void upgrade_halts_on_broken_version() { SystemUpgrader systemUpgrader = systemUpgrader(UpgradePolicy.builder().upgrade(zone1).upgrade(zone2).build()); // Initial system version @@ -252,11 +257,11 @@ public class SystemUpgraderTest { tester.upgradeSystem(version1); systemUpgrader.maintain(); assertCurrentVersion(List.of(SystemApplication.configServerHost, SystemApplication.proxyHost, - SystemApplication.configServer, SystemApplication.proxy), - version1, zone1); + SystemApplication.configServer, SystemApplication.proxy), + version1, zone1); assertCurrentVersion(List.of(SystemApplication.configServerHost, SystemApplication.proxyHost, - SystemApplication.configServer, SystemApplication.proxy), - version1, zone2); + SystemApplication.configServer, SystemApplication.proxy), + version1, zone2); // System starts upgrading to next version Version version2 = Version.fromString("6.6"); @@ -273,13 +278,13 @@ public class SystemUpgraderTest { overrideConfidence(version2, VespaVersion.Confidence.broken); systemUpgrader.maintain(); assertWantedVersion(List.of(SystemApplication.configServerHost, SystemApplication.proxyHost, - SystemApplication.configServer, SystemApplication.proxy), version1, zone2); + SystemApplication.configServer, SystemApplication.proxy), version1, zone2); } @Test - public void does_not_deploy_proxy_app_in_zone_without_shared_routing() { + void does_not_deploy_proxy_app_in_zone_without_shared_routing() { var applications = List.of(SystemApplication.configServerHost, SystemApplication.configServer, - SystemApplication.tenantHost); + SystemApplication.tenantHost); tester.configServer().bootstrap(List.of(zone1.getId()), applications); tester.configServer().disallowConvergenceCheck(SystemApplication.proxy.id()); tester.zoneRegistry().exclusiveRoutingIn(zone1); @@ -301,7 +306,7 @@ public class SystemUpgraderTest { } @Test - public void downgrade_from_aborted_version() { + void downgrade_from_aborted_version() { SystemUpgrader systemUpgrader = systemUpgrader(UpgradePolicy.builder().upgrade(zone1).upgrade(zone2).upgrade(zone3).build()); Version version1 = Version.fromString("6.5"); @@ -319,9 +324,9 @@ public class SystemUpgraderTest { for (var zone : List.of(zone1, zone2)) { systemUpgrader.maintain(); completeUpgrade(List.of(SystemApplication.tenantHost, - SystemApplication.proxyHost, - SystemApplication.configServerHost), - version2, zone); + SystemApplication.proxyHost, + SystemApplication.configServerHost), + version2, zone); completeUpgrade(SystemApplication.configServer, version2, zone); systemUpgrader.maintain(); completeUpgrade(SystemApplication.proxy, version2, zone); @@ -336,12 +341,12 @@ public class SystemUpgraderTest { for (var zone : List.of(zone2, zone1)) { systemUpgrader.maintain(); completeUpgrade(List.of(SystemApplication.tenantHost, - SystemApplication.configServerHost, - SystemApplication.proxy), - version1, zone); + SystemApplication.configServerHost, + SystemApplication.proxy), + version1, zone); convergeServices(SystemApplication.proxy, zone); List<SystemApplication> lastToDowngrade = List.of(SystemApplication.configServer, - SystemApplication.proxyHost); + SystemApplication.proxyHost); assertWantedVersion(lastToDowngrade, version2, zone); // ... and then configserver and proxyhost @@ -359,7 +364,8 @@ public class SystemUpgraderTest { try { overrideConfidence(version3, VespaVersion.Confidence.aborted); fail("Expected exception"); - } catch (IllegalArgumentException ignored) {} + } catch (IllegalArgumentException ignored) { + } systemUpgrader.maintain(); assertWantedVersion(SystemApplication.notController(), version3, zone1, zone2, zone3); } @@ -369,16 +375,28 @@ public class SystemUpgraderTest { tester.computeVersionStatus(); } - /** Simulate upgrade of nodes allocated to given application. In a real system this is done by the node itself */ private void completeUpgrade(SystemApplication application, Version version, ZoneApi first, ZoneApi... rest) { + completeUpgrade(-1, application, version, first, rest); + } + + /** Simulate upgrade of nodes allocated to given application. In a real system this is done by the node itself */ + private void completeUpgrade(int nodeCount, SystemApplication application, Version version, ZoneApi first, ZoneApi... rest) { assertWantedVersion(application, version, first, rest); Stream.concat(Stream.of(first), Stream.of(rest)).forEach(zone -> { - for (Node node : listNodes(zone, application)) { + int nodesUpgraded = 0; + List<Node> nodes = listNodes(zone, application); + for (Node node : nodes) { + if (node.currentVersion().equals(node.wantedVersion())) continue; nodeRepository().putNodes( zone.getId(), Node.builder(node).currentVersion(node.wantedVersion()).build()); + if (++nodesUpgraded == nodeCount) { + break; + } + } + if (nodesUpgraded == nodes.size()) { + assertCurrentVersion(application, version, zone); } - assertCurrentVersion(application, version, zone); }); } @@ -414,8 +432,9 @@ public class SystemUpgraderTest { private void assertWantedVersion(SystemApplication application, Version version, ZoneApi first, ZoneApi... rest) { Stream.concat(Stream.of(first), Stream.of(rest)).forEach(zone -> { if (!application.hasApplicationPackage()) { - assertEquals("Target version set for " + application + " in " + zone.getId(), version, - nodeRepository().targetVersionsOf(zone.getId()).vespaVersion(application.nodeType()).orElse(Version.emptyVersion)); + assertEquals(version, + nodeRepository().targetVersionsOf(zone.getId()).vespaVersion(application.nodeType()).orElse(Version.emptyVersion), + "Target version set for " + application + " in " + zone.getId()); } assertVersion(application, version, Node::wantedVersion, zone); }); @@ -437,7 +456,7 @@ public class SystemUpgraderTest { ZoneApi first, ZoneApi... rest) { Stream.concat(Stream.of(first), Stream.of(rest)).forEach(zone -> { for (Node node : listNodes(zone, application)) { - assertEquals("Version of " + application.id() + " in " + zone.getId(), version, versionField.apply(node)); + assertEquals(version, versionField.apply(node), "Version of " + application.id() + " in " + zone.getId()); } }); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainerTest.java index 7026d975010..97656583d04 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainerTest.java @@ -9,13 +9,13 @@ import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mortent @@ -25,11 +25,11 @@ public class TenantRoleMaintainerTest { private final DeploymentTester tester = new DeploymentTester(); @Test - public void maintains_iam_roles_for_tenants_in_production() { + void maintains_iam_roles_for_tenants_in_production() { var devAppTenant1 = tester.newDeploymentContext("tenant1", "app1", "default"); var prodAppTenant2 = tester.newDeploymentContext("tenant2", "app2", "default"); - var devAppTenant2 = tester.newDeploymentContext("tenant2","app3","default"); - var perfAppTenant1 = tester.newDeploymentContext("tenant3","app1","default"); + var devAppTenant2 = tester.newDeploymentContext("tenant2", "app3", "default"); + var perfAppTenant1 = tester.newDeploymentContext("tenant3", "app1", "default"); ApplicationPackage appPackage = new ApplicationPackageBuilder() .region("us-west-1") .build(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TrafficShareUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TrafficShareUpdaterTest.java index c59155cb162..07ea04b4c84 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TrafficShareUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/TrafficShareUpdaterTest.java @@ -10,12 +10,12 @@ import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Map; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests the traffic fraction updater. This also tests its dependency on DeploymentMetricsMaintainer. @@ -25,7 +25,7 @@ import static org.junit.Assert.assertEquals; public class TrafficShareUpdaterTest { @Test - public void testTrafficUpdater() { + void testTrafficUpdater() { DeploymentTester tester = new DeploymentTester(); var application = tester.newDeploymentContext(); var deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(tester.controller(), Duration.ofDays(1)); @@ -70,7 +70,7 @@ public class TrafficShareUpdaterTest { assertTrafficFraction(0.47, 0.50, application.instanceId(), prod2, tester); assertTrafficFraction(0.00, 0.50, application.instanceId(), prod3, tester); // - all hot - setQpsMetric( 50.0, application.application().id().defaultInstance(), prod1, tester); + setQpsMetric(50.0, application.application().id().defaultInstance(), prod1, tester); setQpsMetric(25.0, application.application().id().defaultInstance(), prod2, tester); setQpsMetric(25.0, application.application().id().defaultInstance(), prod3, tester); deploymentMetricsMaintainer.maintain(); 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 3b21d3a017e..b24b6c8e99b 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 @@ -13,9 +13,10 @@ import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; +import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger; import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -37,9 +38,9 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.sys import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.ALL; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PIN; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel.PLATFORM; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author bratseth @@ -49,13 +50,13 @@ public class UpgraderTest { private final DeploymentTester tester = new DeploymentTester().atMondayMorning(); @Test - public void testUpgrading() { + void testUpgrading() { // --- Setup Version version0 = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version0); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("No applications: Nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "No applications: Nothing to do"); // Setup applications var canary0 = createAndDeploy("canary0", "canary"); @@ -67,7 +68,7 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("All already on the right version: Nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "All already on the right version: Nothing to do"); // --- Next version released - everything goes smoothly Version version1 = Version.fromString("6.3"); @@ -76,14 +77,14 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("New system version: Should upgrade Canaries", 4, tester.jobs().active().size()); + assertEquals(4, tester.jobs().active().size(), "New system version: Should upgrade Canaries"); canary0.deployPlatform(version1); assertEquals(version1, tester.configServer().lastPrepareVersion().get()); tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("One canary pending; nothing else", 2, tester.jobs().active().size()); + assertEquals(2, tester.jobs().active().size(), "One canary pending; nothing else"); canary1.deployPlatform(version1); @@ -91,7 +92,7 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.normal, tester.controller().readVersionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Canaries done: Should upgrade defaults", 6, tester.jobs().active().size()); + assertEquals(6, tester.jobs().active().size(), "Canaries done: Should upgrade defaults"); default0.deployPlatform(version1); default1.deployPlatform(version1); @@ -101,13 +102,13 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.high, tester.controller().readVersionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Normals done: Should upgrade conservatives", 2, tester.jobs().active().size()); + assertEquals(2, tester.jobs().active().size(), "Normals done: Should upgrade conservatives"); conservative0.deployPlatform(version1); tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "Nothing to do"); // --- Next version released - which fails a Canary Version version2 = Version.fromString("6.4"); @@ -116,7 +117,7 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("New system version: Should upgrade Canaries", 4, tester.jobs().active().size()); + assertEquals(4, tester.jobs().active().size(), "New system version: Should upgrade Canaries"); canary0.runJob(systemTest); canary0.failDeployment(stagingTest); @@ -124,7 +125,7 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.broken, tester.controller().readVersionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Version broken, but Canaries should keep trying", 3, tester.jobs().active().size()); + assertEquals(3, tester.jobs().active().size(), "Version broken, but Canaries should keep trying"); // --- Next version released - which repairs the Canary app and fails a default Version version3 = Version.fromString("6.5"); @@ -136,14 +137,14 @@ public class UpgraderTest { canary1.abortJob(stagingTest); tester.triggerJobs(); - assertEquals("New system version: Should upgrade Canaries", 4, tester.jobs().active().size()); + assertEquals(4, tester.jobs().active().size(), "New system version: Should upgrade Canaries"); canary0.deployPlatform(version3); assertEquals(version3, tester.configServer().lastPrepareVersion().get()); tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("One canary pending; nothing else", 2, tester.jobs().active().size()); + assertEquals(2, tester.jobs().active().size(), "One canary pending; nothing else"); canary1.deployPlatform(version3); @@ -152,7 +153,7 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Canaries done: Should upgrade defaults", 6, tester.jobs().active().size()); + assertEquals(6, tester.jobs().active().size(), "Canaries done: Should upgrade defaults"); default0.runJob(systemTest); default0.failDeployment(stagingTest); @@ -160,11 +161,10 @@ public class UpgraderTest { default2.deployPlatform(version3); tester.controllerTester().computeVersionStatus(); - assertEquals("Not enough evidence to mark this as neither broken nor high", - VespaVersion.Confidence.normal, tester.controller().readVersionStatus().systemVersion().get().confidence()); + assertEquals(VespaVersion.Confidence.normal, tester.controller().readVersionStatus().systemVersion().get().confidence(), "Not enough evidence to mark this as neither broken nor high"); tester.triggerJobs(); - assertEquals("Upgrade with error should retry", 1, tester.jobs().active().size()); + assertEquals(1, tester.jobs().active().size(), "Upgrade with error should retry"); // --- Failing application is repaired by changing the application, causing confidence to move above 'high' threshold // Deploy application change @@ -175,13 +175,13 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.high, tester.controller().readVersionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Normals done: Should upgrade conservatives", 2, tester.jobs().active().size()); + assertEquals(2, tester.jobs().active().size(), "Normals done: Should upgrade conservatives"); conservative0.deployPlatform(version3); tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Applications are on " + version3 + " - nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "Applications are on " + version3 + " - nothing to do"); // --- Starting upgrading to a new version which breaks, causing upgrades to commence on the previous version var default3 = createAndDeploy("default3", "default"); @@ -197,7 +197,7 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Upgrade of defaults are scheduled", 10, tester.jobs().active().size()); + assertEquals(10, tester.jobs().active().size(), "Upgrade of defaults are scheduled"); for (var context : List.of(default0, default1, default2, default3, default4)) assertEquals(version4, context.instance().change().platform().get()); @@ -215,7 +215,7 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Upgrade of defaults are scheduled", 10, tester.jobs().active().size()); + assertEquals(10, tester.jobs().active().size(), "Upgrade of defaults are scheduled"); assertEquals(version5, default0.instance().change().platform().get()); for (var context : List.of(default1, default2, default3, default4)) assertEquals(version4, context.instance().change().platform().get()); @@ -253,17 +253,17 @@ public class UpgraderTest { } @Test - public void testUpgradingToVersionWhichBreaksSomeNonCanaries() { + void testUpgradingToVersionWhichBreaksSomeNonCanaries() { // --- Setup tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("No system version: Nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "No system version: Nothing to do"); Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("No applications: Nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "No applications: Nothing to do"); // Setup applications var canary0 = createAndDeploy("canary0", "canary"); @@ -281,7 +281,7 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("All already on the right version: Nothing to do", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "All already on the right version: Nothing to do"); // --- A new version is released version = Version.fromString("6.3"); @@ -290,14 +290,14 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("New system version: Should upgrade Canaries", 4, tester.jobs().active().size()); + assertEquals(4, tester.jobs().active().size(), "New system version: Should upgrade Canaries"); canary0.deployPlatform(version); assertEquals(version, tester.configServer().lastPrepareVersion().get()); tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("One canary pending; nothing else", 2, tester.jobs().active().size()); + assertEquals(2, tester.jobs().active().size(), "One canary pending; nothing else"); canary1.deployPlatform(version); @@ -305,7 +305,7 @@ public class UpgraderTest { assertEquals(VespaVersion.Confidence.normal, tester.controller().readVersionStatus().systemVersion().get().confidence()); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Canaries done: Should upgrade defaults", 20, tester.jobs().active().size()); + assertEquals(20, tester.jobs().active().size(), "Canaries done: Should upgrade defaults"); default0.deployPlatform(version); for (var context : List.of(default1, default2, default3, default4)) @@ -317,7 +317,7 @@ public class UpgraderTest { tester.abortAll(); tester.triggerJobs(); assertEquals(VespaVersion.Confidence.broken, tester.controller().readVersionStatus().systemVersion().get().confidence()); - assertEquals("Upgrades are cancelled", 0, tester.jobs().active().size()); + assertEquals(0, tester.jobs().active().size(), "Upgrades are cancelled"); } // Scenario: @@ -327,7 +327,7 @@ public class UpgraderTest { // V2 is marked as broken and upgrade of A to V2 is cancelled. // Upgrade of A to V1 is scheduled: Should skip the zone on V2 but upgrade the next zone to V1 @Test - public void testVersionIsBrokenAfterAZoneIsLive() { + void testVersionIsBrokenAfterAZoneIsLive() { Version v0 = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(v0); @@ -382,8 +382,8 @@ public class UpgraderTest { // Applications with default policy start upgrading to V2 tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Upgrade scheduled for remaining apps", 10, tester.jobs().active().size()); - assertEquals("default4 is still upgrading to 6.3", v1, default4.instance().change().platform().get()); + assertEquals(10, tester.jobs().active().size(), "Upgrade scheduled for remaining apps"); + assertEquals(v1, default4.instance().change().platform().get(), "default4 is still upgrading to 6.3"); // 4/5 applications fail (in the last prod zone) and lowers confidence default0.runJob(systemTest).runJob(stagingTest).runJob(productionUsWest1).failDeployment(productionUsEast3); @@ -399,20 +399,19 @@ public class UpgraderTest { tester.abortAll(); tester.triggerJobs(); - assertEquals("Upgrade to 5.1 scheduled for apps not completely on 5.1 or 5.2", 10, tester.jobs().active().size()); + assertEquals(10, tester.jobs().active().size(), "Upgrade to 5.1 scheduled for apps not completely on 5.1 or 5.2"); // prod zone on 5.2 (usWest1) is skipped, but we still trigger the next zone from triggerReadyJobs: default0.runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3); // Resulting state: assertEquals(v2, default0.deployment(ZoneId.from("prod.us-west-1")).version()); - assertEquals("Last zone is upgraded to v1", - v1, default0.deployment(ZoneId.from("prod.us-east-3")).version()); + assertEquals(v1, default0.deployment(ZoneId.from("prod.us-east-3")).version(), "Last zone is upgraded to v1"); assertFalse(default0.instance().change().hasTargets()); } @Test - public void testConfidenceIgnoresFailingApplicationChanges() { + void testConfidenceIgnoresFailingApplicationChanges() { Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); @@ -458,7 +457,7 @@ public class UpgraderTest { } @Test - public void testBlockVersionChange() { + void testBlockVersionChange() { // Tuesday, 18:00 tester.at(Instant.parse("2017-09-26T18:00:00.00Z")); Version version = Version.fromString("6.2"); @@ -480,25 +479,25 @@ public class UpgraderTest { // Application is not upgraded at this time tester.upgrader().maintain(); tester.triggerJobs(); - assertTrue("No jobs scheduled", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "No jobs scheduled"); // One hour passes, time is 19:00, still no upgrade tester.clock().advance(Duration.ofHours(1)); tester.upgrader().maintain(); tester.triggerJobs(); - assertTrue("No jobs scheduled", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "No jobs scheduled"); // Two hours pass in total, time is 20:00 and application upgrades tester.clock().advance(Duration.ofHours(1)); tester.upgrader().maintain(); tester.triggerJobs(); - assertFalse("Job is scheduled", tester.jobs().active().isEmpty()); + assertFalse(tester.jobs().active().isEmpty(), "Job is scheduled"); app.deployPlatform(version); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); } @Test - public void testBlockVersionChangeHalfwayThough() { + void testBlockVersionChangeHalfwayThough() { // Tuesday, 17:00 tester.at(Instant.parse("2017-09-26T17:00:00.00Z")); @@ -531,11 +530,11 @@ public class UpgraderTest { assertEquals(1, tester.jobs().active().size()); // Next job triggered because upgrade is already rolling out. app.runJob(productionUsCentral1).runJob(productionUsEast3); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); } @Test - public void testBlockVersionChangeHalfwayThroughThenNewRevision() { + void testBlockVersionChangeHalfwayThroughThenNewRevision() { // Friday, 16:00 tester.at(Instant.parse("2017-09-29T16:00:00.00Z")); @@ -599,12 +598,12 @@ public class UpgraderTest { tester.upgrader().maintain(); tester.triggerJobs(); app.runJob(systemTest) - .runJob(stagingTest) - .runJob(productionUsWest1) - .runJob(productionUsCentral1) - .runJob(stagingTest) - .runJob(productionUsEast3); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + .runJob(stagingTest) + .runJob(productionUsWest1) + .runJob(productionUsCentral1) + .runJob(stagingTest) + .runJob(productionUsEast3); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); // App is completely upgraded to the latest version for (Deployment deployment : app.instance().deployments().values()) @@ -612,7 +611,7 @@ public class UpgraderTest { } @Test - public void testThrottlesUpgrades() { + void testThrottlesUpgrades() { Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); @@ -631,7 +630,7 @@ public class UpgraderTest { // Dev deployment which should be ignored var dev0 = tester.newDeploymentContext("tenant1", "dev0", "default") - .runJob(devUsEast1, DeploymentContext.applicationPackage()); + .runJob(devUsEast1, DeploymentContext.applicationPackage()); // New version is released and canaries upgrade version = Version.fromString("6.3"); @@ -660,13 +659,13 @@ public class UpgraderTest { } @Test - public void testPinningMajorVersionInDeploymentXml() { + void testPinningMajorVersionInDeploymentXml() { Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); ApplicationPackage version6ApplicationPackage = new ApplicationPackageBuilder().majorVersion(6) - .region("us-west-1") - .build(); + .region("us-west-1") + .build(); // Setup applications var canary0 = createAndDeploy("canary0", "canary"); @@ -692,7 +691,7 @@ public class UpgraderTest { } @Test - public void testPinningMajorVersionInApplication() { + void testPinningMajorVersionInApplication() { Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); @@ -700,7 +699,7 @@ public class UpgraderTest { var canary0 = createAndDeploy("canary", "canary"); var default0 = tester.newDeploymentContext().submit().deploy(); tester.applications().lockApplicationOrThrow(default0.application().id(), - a -> tester.applications().store(a.withMajorVersion(6))); + a -> tester.applications().store(a.withMajorVersion(6))); assertEquals(OptionalInt.of(6), default0.application().majorVersion()); // New major version is released @@ -723,20 +722,20 @@ public class UpgraderTest { } @Test - public void testPinningMajorVersionInUpgrader() { + void testPinningMajorVersionInUpgrader() { Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); ApplicationPackage version7CanaryApplicationPackage = new ApplicationPackageBuilder() - .majorVersion(7) - .upgradePolicy("canary") - .region("us-west-1") - .build(); + .majorVersion(7) + .upgradePolicy("canary") + .region("us-west-1") + .build(); ApplicationPackage version7DefaultApplicationPackage = new ApplicationPackageBuilder() - .majorVersion(7) - .upgradePolicy("default") - .region("us-west-1") - .build(); + .majorVersion(7) + .upgradePolicy("default") + .region("us-west-1") + .build(); // Setup applications var canary0 = tester.newDeploymentContext("tenant1", "canary0", "default").submit(version7CanaryApplicationPackage).deploy(); @@ -744,7 +743,7 @@ public class UpgraderTest { var default1 = tester.newDeploymentContext("tenant1", "default1", "default").submit(DeploymentContext.applicationPackage()).deploy(); // New major version is released, but we don't want to upgrade to it yet - tester.upgrader().setTargetMajorVersion(Optional.of(6)); + tester.upgrader().setTargetMajorVersion(OptionalInt.of(6)); version = Version.fromString("7.0"); tester.controllerTester().upgradeSystem(version); assertEquals(version, tester.controller().readVersionStatus().systemVersion().get().versionNumber()); @@ -769,7 +768,7 @@ public class UpgraderTest { assertEquals(0, tester.jobs().active().size()); // Now we want to upgrade the latest application - tester.upgrader().setTargetMajorVersion(Optional.empty()); + tester.upgrader().setTargetMajorVersion(OptionalInt.empty()); tester.upgrader().maintain(); tester.triggerJobs(); assertEquals(2, tester.jobs().active().size()); @@ -777,7 +776,7 @@ public class UpgraderTest { } @Test - public void testAllowApplicationChangeDuringFailingUpgrade() { + void testAllowApplicationChangeDuringFailingUpgrade() { Version version = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version); @@ -796,15 +795,15 @@ public class UpgraderTest { 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().revision().get().equals(revision)); + assertTrue(app.instance().change().platform().get().equals(version) && + app.instance().change().revision().get().equals(revision), + "Change contains both upgrade and application change"); // Deployment completes app.runJob(systemTest).runJob(stagingTest) - .runJob(productionUsWest1) - .runJob(productionUsEast3); - assertEquals("All jobs consumed", List.of(), tester.jobs().active()); + .runJob(productionUsWest1) + .runJob(productionUsEast3); + assertEquals(List.of(), tester.jobs().active(), "All jobs consumed"); for (Deployment deployment : app.instance().deployments().values()) { assertEquals(version, deployment.version()); @@ -813,7 +812,7 @@ public class UpgraderTest { } @Test - public void testBlockRevisionChangeHalfwayThoughThenUpgrade() { + void testBlockRevisionChangeHalfwayThoughThenUpgrade() { // Tuesday, 17:00. tester.at(Instant.parse("2017-09-26T17:00:00.00Z")); @@ -851,11 +850,11 @@ public class UpgraderTest { // Upgrade may start, now that revision is rolled out. tester.upgrader().maintain(); app.deployPlatform(version); - assertTrue("All jobs consumed", tester.jobs().active().isEmpty()); + assertTrue(tester.jobs().active().isEmpty(), "All jobs consumed"); } @Test - public void testBlockRevisionChangeHalfwayThoughThenNewRevision() { + void testBlockRevisionChangeHalfwayThoughThenNewRevision() { // Tuesday, 17:00. tester.at(Instant.parse("2017-09-26T17:00:00.00Z")); @@ -900,7 +899,7 @@ public class UpgraderTest { } @Test - public void testPinning() { + void testPinning() { Version version0 = Version.fromString("6.2"); tester.controllerTester().upgradeSystem(version0); @@ -938,7 +937,7 @@ public class UpgraderTest { // Application fails upgrade after one zone is complete, and is pinned again to the old version. context.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1) - .timeOutUpgrade(productionUsWest1); + .timeOutUpgrade(productionUsWest1); tester.deploymentTrigger().cancelChange(context.instanceId(), ALL); tester.deploymentTrigger().forceChange(context.instanceId(), Change.of(version0).withPin()); assertEquals(version0, context.instance().change().platform().get()); @@ -952,12 +951,12 @@ public class UpgraderTest { } @Test - public void upgradesToLatestAllowedMajor() { + void upgradesToLatestAllowedMajor() { Version version0 = Version.fromString("6.1"); tester.controllerTester().upgradeSystem(version0); // Apps target 6 by default - tester.upgrader().setTargetMajorVersion(Optional.of(6)); + tester.upgrader().setTargetMajorVersion(OptionalInt.of(6)); // All applications deploy on current version var app1 = createAndDeploy("app1", "default"); @@ -966,7 +965,7 @@ public class UpgraderTest { // Keep app 1 on current version tester.controller().applications().lockApplicationIfPresent(app1.application().id(), app -> tester.controller().applications().store(app.with(app1.instance().name(), - instance -> instance.withChange(instance.change().withPin())))); + instance -> instance.withChange(instance.change().withPin())))); // New version is released Version version1 = Version.fromString("6.2"); @@ -988,14 +987,69 @@ public class UpgraderTest { // App 1 is unpinned and upgrades to latest 6 tester.controller().applications().lockApplicationIfPresent(app1.application().id(), app -> tester.controller().applications().store(app.with(app1.instance().name(), - instance -> instance.withChange(instance.change().withoutPin())))); + instance -> instance.withChange(instance.change().withoutPin())))); tester.upgrader().maintain(); - assertEquals("Application upgrades to latest allowed major", version1, - app1.instance().change().platform().orElseThrow()); + assertEquals(version1, + app1.instance().change().platform().orElseThrow(), + "Application upgrades to latest allowed major"); + } + + @Test + void testSettingFailingRevisionAside() { + DeploymentContext app = tester.newDeploymentContext().submit().deploy(); + + // New revision fails. + app.submit(); + Optional<RevisionId> revision1 = app.lastSubmission(); + app.failDeployment(systemTest); + + // New version is not targeted. + Version version1 = new Version("7"); + tester.controllerTester().upgradeSystem(version1); + assertEquals(Change.of(revision1.get()), app.instance().change()); + + tester.upgrader().run(); + assertEquals(Change.of(revision1.get()), app.instance().change()); + + // Broken revision is replaced by a new attempt, which also fails, and cancellation is not yet triggered. + tester.clock().advance(DeploymentTrigger.maxFailingRevisionTime.plusSeconds(1)); + app.submit(); + Optional<RevisionId> revision2 = app.lastSubmission(); + app.failDeployment(systemTest); + tester.upgrader().run(); + assertEquals(Change.of(revision2.get()), app.instance().change()); + + // Broken revision is cancelled, and new version targeted, after some time. + tester.clock().advance(DeploymentTrigger.maxFailingRevisionTime.plusSeconds(1)); + tester.upgrader().run(); + assertEquals(Change.of(version1), app.instance().change()); + + // Broken revision is not targeted again. + app.triggerJobs(); + tester.upgrader().run(); + tester.outstandingChangeDeployer().run(); + assertEquals(Change.of(version1), app.instance().change()); + + app.failDeployment(systemTest); + tester.upgrader().run(); + tester.outstandingChangeDeployer().run(); + assertEquals(Change.of(version1), app.instance().change()); + + // Revision gets a second change when upgrade fixes the failing job. + tester.clock().advance(Duration.ofDays(12)); // Time for retries. + app.runJob(systemTest).jobAborted(stagingTest).runJob(stagingTest).runJob(productionUsCentral1); + tester.upgrader().run(); + tester.outstandingChangeDeployer().run(); + + assertEquals(Change.of(version1).with(revision2.get()), app.instance().change()); + app.runJob(systemTest).runJob(stagingTest).runJob(productionUsCentral1); // Revision rolls. + app.runJob(productionUsEast3).runJob(productionUsWest1); // Upgrade completes. + app.runJob(productionUsEast3).runJob(productionUsWest1); // Revision completes. + assertEquals(Change.empty(), app.instance().change()); } @Test - public void testsEachUpgradeCombinationWithFailingDeployments() { + void testsEachUpgradeCombinationWithFailingDeployments() { Version v1 = Version.fromString("6.1"); tester.controllerTester().upgradeSystem(v1); @@ -1050,11 +1104,11 @@ public class UpgraderTest { // Upgrade completes application.runJob(productionUsEast3); - assertTrue("Upgrade complete", application.instance().change().isEmpty()); + assertTrue(application.instance().change().isEmpty(), "Upgrade complete"); } @Test - public void testUpgradesPerMinute() { + void testUpgradesPerMinute() { assertEquals(0, Upgrader.numberOfApplicationsToUpgrade(10, 0, 0)); for (long now = 0; now < 60_000; now++) @@ -1075,17 +1129,17 @@ public class UpgraderTest { } @Test - public void testUpgradeShuffling() { + void testUpgradeShuffling() { // Deploy applications on initial version var default0 = createAndDeploy("default0", "default"); var default1 = createAndDeploy("default1", "default"); var default2 = createAndDeploy("default2", "default"); var applications = Map.of(default0.instanceId(), default0, - default1.instanceId(), default1, - default2.instanceId(), default2); + default1.instanceId(), default1, + default2.instanceId(), default2); // Throttle upgrades per run - ((ManualClock) tester.controller().clock()).setInstant(Instant.ofEpochMilli(1589787109000L)); // Fixed random seed + ((ManualClock) tester.controller().clock()).setInstant(Instant.ofEpochMilli(1589787107000L)); // Fixed random seed Upgrader upgrader = new Upgrader(tester.controller(), Duration.ofMinutes(10)); upgrader.setUpgradesPerMinute(0.1); @@ -1102,10 +1156,10 @@ public class UpgraderTest { upgrader.maintain(); tester.triggerJobs(); Set<ApplicationId> triggered = tester.jobs().active().stream() - .map(Run::id) - .map(RunId::application) - .collect(Collectors.toSet()); - assertEquals("Expected number of applications is triggered", 1, triggered.size()); + .map(Run::id) + .map(RunId::application) + .collect(Collectors.toSet()); + assertEquals(1, triggered.size(), "Expected number of applications is triggered"); ApplicationId application = triggered.iterator().next(); upgraderOrder.add(application); applications.get(application).completeRollout(); @@ -1113,7 +1167,7 @@ public class UpgraderTest { } upgradeOrders.add(upgraderOrder); } - assertEquals("Upgrade orders are distinct", versions.size(), upgradeOrders.size()); + assertEquals(versions.size(), upgradeOrders.size(), "Upgrade orders are distinct"); } private static final ApplicationPackage canaryApplicationPackage = diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainerTest.java index c1a9559ac46..4fe04b25577 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainerTest.java @@ -3,11 +3,11 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.hosted.controller.ControllerTester; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author olaa @@ -19,7 +19,7 @@ public class UserManagementMaintainerTest { private final String APP_NAME = "some-app"; @Test - public void deletes_tenant_when_not_public() { + void deletes_tenant_when_not_public() { var tester = createTester(SystemName.main); var maintainer = new UserManagementMaintainer(tester.controller(), Duration.ofMinutes(5), tester.serviceRegistry().roleMaintainer()); maintainer.maintain(); @@ -32,7 +32,7 @@ public class UserManagementMaintainerTest { } @Test - public void no_tenant_deletion_in_public() { + void no_tenant_deletion_in_public() { var tester = createTester(SystemName.Public); var maintainer = new UserManagementMaintainer(tester.controller(), Duration.ofMinutes(5), tester.serviceRegistry().roleMaintainer()); maintainer.maintain(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainerTest.java index bfbd3836ce7..52bd8e9c618 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainerTest.java @@ -14,9 +14,10 @@ import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction.State; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VcmrReport; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest.Status; +import com.yahoo.vespa.hosted.controller.integration.MetricsMock; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.DayOfWeek; import java.time.Duration; @@ -25,9 +26,10 @@ import java.time.ZonedDateTime; import java.time.temporal.TemporalAdjusters; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static com.yahoo.vespa.hosted.controller.maintenance.VcmrMaintainer.TRACKED_CMRS_METRIC; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author olaa @@ -37,6 +39,7 @@ public class VcmrMaintainerTest { private ControllerTester tester; private VcmrMaintainer maintainer; private NodeRepositoryMock nodeRepo; + private MetricsMock metrics; private final ZoneId zoneId = ZoneId.from("prod.us-east-3"); private final ZoneId zone2 = ZoneId.from("prod.us-west-1"); private final HostName host1 = HostName.of("host1"); @@ -44,23 +47,24 @@ public class VcmrMaintainerTest { private final HostName host3 = HostName.of("host3"); private final String changeRequestId = "id123"; - @Before + @BeforeEach public void setup() { tester = new ControllerTester(); - maintainer = new VcmrMaintainer(tester.controller(), Duration.ofMinutes(1)); + metrics = new MetricsMock(); + maintainer = new VcmrMaintainer(tester.controller(), Duration.ofMinutes(1), metrics); nodeRepo = tester.serviceRegistry().configServer().nodeRepository().allowPatching(true); } @Test - public void recycle_hosts_after_completion() { + void recycle_hosts_after_completion() { var vcmrReport = new VcmrReport(); vcmrReport.addVcmr("id123", ZonedDateTime.now(), ZonedDateTime.now()); var parkedNode = createNode(host1, NodeType.host, Node.State.parked, true); var failedNode = createNode(host2, NodeType.host, Node.State.failed, false); var reports = vcmrReport.toNodeReports(); parkedNode = Node.builder(parkedNode) - .reports(reports) - .build(); + .reports(reports) + .build(); nodeRepo.putNodes(zoneId, List.of(parkedNode, failedNode)); @@ -79,7 +83,7 @@ public class VcmrMaintainerTest { } @Test - public void infrastructure_hosts_require_maunal_intervention() { + void infrastructure_hosts_require_maunal_intervention() { var configNode = createNode(host1, NodeType.config, Node.State.active, false); var activeNode = createNode(host2, NodeType.host, Node.State.active, false); nodeRepo.putNodes(zoneId, List.of(configNode, activeNode)); @@ -99,7 +103,7 @@ public class VcmrMaintainerTest { } @Test - public void retires_hosts_when_near_vcmr() { + void retires_hosts_when_near_vcmr() { var activeNode = createNode(host1, NodeType.host, Node.State.active, false); var failedNode = createNode(host2, NodeType.host, Node.State.failed, false); nodeRepo.putNodes(zoneId, List.of(activeNode, failedNode)); @@ -121,7 +125,7 @@ public class VcmrMaintainerTest { } @Test - public void no_spare_capacity_requires_operator_action() { + void no_spare_capacity_requires_operator_action() { var activeNode = createNode(host1, NodeType.host, Node.State.active, false); var failedNode = createNode(host2, NodeType.host, Node.State.failed, false); nodeRepo.putNodes(zoneId, List.of(activeNode, failedNode)); @@ -143,7 +147,7 @@ public class VcmrMaintainerTest { } @Test - public void updates_status_when_retiring_host_is_parked() { + void updates_status_when_retiring_host_is_parked() { var parkedNode = createNode(host1, NodeType.host, Node.State.parked, true); nodeRepo.putNodes(zoneId, parkedNode); nodeRepo.hasSpareCapacity(true); @@ -158,7 +162,7 @@ public class VcmrMaintainerTest { } @Test - public void pending_retirement_when_vcmr_is_far_ahead() { + void pending_retirement_when_vcmr_is_far_ahead() { var activeNode = createNode(host2, NodeType.host, Node.State.active, false); nodeRepo.putNodes(zoneId, List.of(activeNode)); nodeRepo.hasSpareCapacity(true); @@ -177,13 +181,13 @@ public class VcmrMaintainerTest { activeNode = nodeRepo.list(zoneId, NodeFilter.all().hostnames(host2)).get(0); var report = VcmrReport.fromReports(activeNode.reports()); var reportAdded = report.getVcmrs().stream() - .filter(vcmr -> vcmr.getId().equals(changeRequestId)) - .count() == 1; + .filter(vcmr -> vcmr.getId().equals(changeRequestId)) + .count() == 1; assertTrue(reportAdded); } @Test - public void recycles_nodes_if_vcmr_is_postponed() { + void recycles_nodes_if_vcmr_is_postponed() { var parkedNode = createNode(host1, NodeType.host, Node.State.parked, false); var retiringNode = createNode(host2, NodeType.host, Node.State.active, true); nodeRepo.putNodes(zoneId, List.of(parkedNode, retiringNode)); @@ -206,7 +210,7 @@ public class VcmrMaintainerTest { } @Test - public void handle_multizone_vcmr() { + void handle_multizone_vcmr() { var configNode = createNode(host1, NodeType.config, Node.State.active, false); var tenantNode1 = createNode(host2, NodeType.host, Node.State.active, false); var tenantNode2 = createNode(host3, NodeType.host, Node.State.active, false); @@ -230,7 +234,7 @@ public class VcmrMaintainerTest { } @Test - public void out_of_sync_when_manual_reactivation() { + void out_of_sync_when_manual_reactivation() { var nonRetiringNode = createNode(host1, NodeType.host, Node.State.active, false); nodeRepo.putNodes(zoneId, nonRetiringNode); @@ -244,10 +248,12 @@ public class VcmrMaintainerTest { assertEquals(State.OUT_OF_SYNC, action.getState()); assertEquals(Status.OUT_OF_SYNC, writtenChangeRequest.getStatus()); + assertEquals(1, metrics.getMetric(context -> "OUT_OF_SYNC".equals(context.get("status")), TRACKED_CMRS_METRIC).get()); + assertEquals(0, metrics.getMetric(context -> "REQUIRES_OPERATOR_ACTION".equals(context.get("status")), TRACKED_CMRS_METRIC).get()); } @Test - public void retirement_start_time_ignores_weekends() { + void retirement_start_time_ignores_weekends() { var plannedStartTime = ZonedDateTime.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY)); var retirementStartTime = maintainer.getRetirementStartTime(plannedStartTime); assertEquals(plannedStartTime.minusDays(2), retirementStartTime); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdaterTest.java index d85ec868d56..e6f46e0630d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdaterTest.java @@ -5,15 +5,15 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.organization.SystemMonitor; import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Collections; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author bratseth @@ -23,7 +23,7 @@ public class VersionStatusUpdaterTest { /** Test that this job updates the status. Test of the content of the update is in * {@link com.yahoo.vespa.hosted.controller.versions.VersionStatusTest} */ @Test - public void testVersionUpdating() { + void testVersionUpdating() { ControllerTester tester = new ControllerTester(); tester.controller().updateVersionStatus(new VersionStatus(Collections.emptyList())); assertFalse(tester.controller().readVersionStatus().systemVersion().isPresent()); @@ -35,7 +35,7 @@ public class VersionStatusUpdaterTest { } @Test - public void testConfidenceConversion() { + void testConfidenceConversion() { List.of(VespaVersion.Confidence.values()).forEach(VersionStatusUpdater::convert); assertEquals(SystemMonitor.Confidence.broken, VersionStatusUpdater.convert(VespaVersion.Confidence.broken)); assertEquals(SystemMonitor.Confidence.low, VersionStatusUpdater.convert(VespaVersion.Confidence.low)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java index c643a612f00..164df0a27f5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java @@ -13,12 +13,12 @@ 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.application.TenantAndApplicationId; import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author enygaard @@ -35,7 +35,7 @@ public class NotificationFormatterTest { private final NotificationFormatter formatter = new NotificationFormatter(zoneRegistry); @Test - public void applicationPackage() { + void applicationPackage() { var notification = new Notification(Instant.now(), Notification.Type.applicationPackage, Notification.Level.warning, NotificationSource.from(applicationId), List.of("1", "2")); var content = formatter.format(notification); assertEquals("Application package", content.prettyType()); @@ -44,7 +44,7 @@ public class NotificationFormatterTest { } @Test - public void deployment() { + void deployment() { var runId = new RunId(applicationId, JobType.prod(RegionName.defaultName()), 1001); var notification = new Notification(Instant.now(), Notification.Type.deployment, Notification.Level.warning, NotificationSource.from(runId), List.of("1")); var content = formatter.format(notification); @@ -54,7 +54,7 @@ public class NotificationFormatterTest { } @Test - public void deploymentError() { + void deploymentError() { var runId = new RunId(applicationId, JobType.prod(RegionName.defaultName()), 1001); var notification = new Notification(Instant.now(), Notification.Type.deployment, Notification.Level.error, NotificationSource.from(runId), List.of("1")); var content = formatter.format(notification); @@ -64,7 +64,7 @@ public class NotificationFormatterTest { } @Test - public void testPackage() { + void testPackage() { var notification = new Notification(Instant.now(), Notification.Type.testPackage, Notification.Level.warning, NotificationSource.from(TenantAndApplicationId.from(applicationId)), List.of("1")); var content = formatter.format(notification); assertEquals("Test package", content.prettyType()); @@ -73,7 +73,7 @@ public class NotificationFormatterTest { } @Test - public void reindex() { + void reindex() { var notification = new Notification(Instant.now(), Notification.Type.reindex, Notification.Level.info, NotificationSource.from(deploymentId, cluster), List.of("1")); var content = formatter.format(notification); assertEquals("Reindex", content.prettyType()); @@ -82,7 +82,7 @@ public class NotificationFormatterTest { } @Test - public void feedBlock() { + void feedBlock() { var notification = new Notification(Instant.now(), Notification.Type.feedBlock, Notification.Level.warning, NotificationSource.from(deploymentId, cluster), List.of("1")); var content = formatter.format(notification); assertEquals("Nearly feed blocked", content.prettyType()); 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 5666f8bafd8..4c1344650f8 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 @@ -25,8 +25,8 @@ import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo; import com.yahoo.vespa.hosted.controller.tenant.TenantContacts; import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.ArrayList; @@ -39,9 +39,9 @@ import java.util.stream.Collectors; import static com.yahoo.vespa.hosted.controller.notification.Notification.Level; import static com.yahoo.vespa.hosted.controller.notification.Notification.Type; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author freva @@ -61,7 +61,8 @@ public class NotificationsDbTest { List.of(TenantContacts.Audience.NOTIFICATIONS), email)))), List.of(), - new ArchiveAccess()); + new ArchiveAccess(), + Optional.empty()); private static final List<Notification> notifications = List.of( notification(1001, Type.deployment, Level.error, NotificationSource.from(tenant), "tenant msg"), notification(1101, Type.applicationPackage, Level.warning, NotificationSource.from(TenantAndApplicationId.from(tenant.value(), "app1")), "app msg"), @@ -77,7 +78,7 @@ public class NotificationsDbTest { private final NotificationsDb notificationsDb = new NotificationsDb(clock, curatorDb, new Notifier(curatorDb, new ZoneRegistryMock(SystemName.cd), mailer, flagSource)); @Test - public void list_test() { + void list_test() { assertEquals(notifications, notificationsDb.listNotifications(NotificationSource.from(tenant), false)); assertEquals(notificationIndices(0, 1, 2, 3), notificationsDb.listNotifications(NotificationSource.from(tenant), true)); assertEquals(notificationIndices(2, 3), notificationsDb.listNotifications(NotificationSource.from(TenantAndApplicationId.from(tenant.value(), "app2")), false)); @@ -87,7 +88,7 @@ public class NotificationsDbTest { } @Test - public void add_test() { + void add_test() { Notification notification1 = notification(12345, Type.deployment, Level.warning, NotificationSource.from(ApplicationId.from(tenant.value(), "app2", "instance2")), "instance msg #2"); Notification notification2 = notification(12345, Type.deployment, Level.error, NotificationSource.from(ApplicationId.from(tenant.value(), "app3", "instance2")), "instance msg #3"); @@ -103,11 +104,11 @@ public class NotificationsDbTest { } @Test - public void notifier_test() { + void notifier_test() { Notification notification1 = notification(12345, Type.deployment, Level.warning, NotificationSource.from(ApplicationId.from(tenant.value(), "app2", "instance2")), "instance msg #2"); Notification notification2 = notification(12345, Type.applicationPackage, Level.error, NotificationSource.from(ApplicationId.from(tenant.value(), "app3", "instance2")), "instance msg #3"); Notification notification3 = notification(12345, Type.reindex, Level.warning, NotificationSource.from(new DeploymentId(ApplicationId.from(tenant.value(), "app2", "instance2"), ZoneId.defaultId()), new ClusterSpec.Id("content")), "instance msg #2"); -; + ; var a = notifications.get(0); notificationsDb.setNotification(a.source(), a.type(), a.level(), a.messages()); assertEquals(0, mailer.inbox(email).size()); @@ -126,7 +127,7 @@ public class NotificationsDbTest { } @Test - public void remove_single_test() { + void remove_single_test() { // Remove the 3rd notification notificationsDb.removeNotification(NotificationSource.from(ApplicationId.from(tenant.value(), "app2", "instance2")), Type.deployment); @@ -137,7 +138,7 @@ public class NotificationsDbTest { } @Test - public void remove_multiple_test() { + void remove_multiple_test() { // Remove the 3rd notification notificationsDb.removeNotifications(NotificationSource.from(ApplicationId.from(tenant.value(), "app1", "instance1"))); assertEquals(notificationIndices(0, 1, 2, 3), curatorDb.readNotifications(tenant)); @@ -149,7 +150,7 @@ public class NotificationsDbTest { } @Test - public void deployment_metrics_notify_test() { + void deployment_metrics_notify_test() { DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenant.value(), "app1", "instance1"), ZoneId.from("prod", "us-south-3")); NotificationSource sourceCluster1 = NotificationSource.from(deploymentId, ClusterSpec.Id.from("cluster1")); List<Notification> expected = new ArrayList<>(notifications); @@ -172,7 +173,7 @@ public class NotificationsDbTest { } @Test - public void feed_blocked_single_cluster_test() { + void feed_blocked_single_cluster_test() { DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenant.value(), "app1", "instance1"), ZoneId.from("prod", "us-south-3")); NotificationSource sourceCluster1 = NotificationSource.from(deploymentId, ClusterSpec.Id.from("cluster1")); List<Notification> expected = new ArrayList<>(notifications); @@ -207,7 +208,7 @@ public class NotificationsDbTest { } @Test - public void deployment_metrics_multiple_cluster_test() { + void deployment_metrics_multiple_cluster_test() { DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenant.value(), "app1", "instance1"), ZoneId.from("prod", "us-south-3")); NotificationSource sourceCluster1 = NotificationSource.from(deploymentId, ClusterSpec.Id.from("cluster1")); NotificationSource sourceCluster2 = NotificationSource.from(deploymentId, ClusterSpec.Id.from("cluster2")); @@ -231,7 +232,7 @@ public class NotificationsDbTest { assertEquals(expected, curatorDb.readNotifications(tenant)); } - @Before + @BeforeEach public void init() { curatorDb.writeNotifications(tenant, notifications); curatorDb.writeTenant(cloudTenant); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java index 8bf0e584892..7c07192d633 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java @@ -16,17 +16,15 @@ import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo; import com.yahoo.vespa.hosted.controller.tenant.TenantContacts; import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.List; import java.util.Map; import java.util.Optional; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class NotifierTest { private static final TenantName tenant = TenantName.from("tenant1"); @@ -43,18 +41,19 @@ public class NotifierTest { List.of(TenantContacts.Audience.NOTIFICATIONS), email)))), List.of(), - new ArchiveAccess()); + new ArchiveAccess(), + Optional.empty()); MockCuratorDb curatorDb = new MockCuratorDb(SystemName.Public); - @Before + @BeforeEach public void init() { curatorDb.writeTenant(cloudTenant); } @Test - public void dispatch() { + void dispatch() { var mailer = new MockMailer(); var flagSource = new InMemoryFlagSource().withBooleanFlag(Flags.NOTIFICATION_DISPATCH_FLAG.id(), true); var notifier = new Notifier(curatorDb, new ZoneRegistryMock(SystemName.cd), mailer, flagSource); @@ -68,20 +67,28 @@ public class NotifierTest { var mail = mailer.inbox(email).get(0); assertEquals("[WARNING] Test package Vespa Notification for tenant1.default.default", mail.subject()); - assertEquals("There are problems with tests for default.default<br>\n" + - "<ul>\n" + - "<li>test package has production tests, but no production tests are declared in deployment.xml</li><br>\n" + - "<li>see <a href=\"https://docs.vespa.ai/en/testing.html\">https://docs.vespa.ai/en/testing.html</a> for details on how to write system tests for Vespa</li></ul>\n" + - "<br>\n" + - "<a href=\"https://dashboard.tld/tenant1/default\">Vespa Console</a>", + assertEquals(""" + <div style="background: #00598c; height: 55px; width: 100%"> + <img + src="https://vespa.ai/assets/vespa-logo.png" + style="width: auto; height: 34px; margin: 10px" + /> + </div> + <br> + There are problems with tests for default.default<br> + <ul> + <li>test package has production tests, but no production tests are declared in deployment.xml</li><br> + <li>see <a href="https://docs.vespa.ai/en/testing.html">https://docs.vespa.ai/en/testing.html</a> for details on how to write system tests for Vespa</li></ul> + <br> + <a href="https://dashboard.tld/tenant1/default">Vespa Console</a>""", mail.htmlMessage().get()); } @Test - public void linkify() { + void linkify() { var data = Map.of( "Hello. https://example.com/foo/bar.html is a nice place.", "Hello. <a href=\"https://example.com/foo/bar.html\">https://example.com/foo/bar.html</a> is a nice place.", "No url.", "No url."); data.forEach((input, expected) -> assertEquals(expected, Notifier.linkify(input))); } -}
\ No newline at end of file +} 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 e60325a140a..88da698b3f2 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 @@ -30,7 +30,7 @@ import com.yahoo.vespa.hosted.controller.metric.ApplicationMetrics; import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId; import com.yahoo.vespa.hosted.controller.routing.rotation.RotationState; import com.yahoo.vespa.hosted.controller.routing.rotation.RotationStatus; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.file.Files; import java.nio.file.Path; @@ -47,7 +47,7 @@ import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author bratseth @@ -70,18 +70,18 @@ public class ApplicationSerializerTest { @Test - public void testSerialization() throws Exception { + void testSerialization() throws Exception { DeploymentSpec deploymentSpec = DeploymentSpec.fromXml("<deployment version='1.0'>\n" + - " <staging/>\n" + - " <instance id=\"i1\">\n" + - " <prod global-service-id=\"default\">\n" + - " <region active=\"true\">us-west-1</region>\n" + - " </prod>\n" + - " </instance>\n" + - "</deployment>"); + " <staging/>\n" + + " <instance id=\"i1\">\n" + + " <prod global-service-id=\"default\">\n" + + " <region active=\"true\">us-west-1</region>\n" + + " </prod>\n" + + " </instance>\n" + + "</deployment>"); ValidationOverrides validationOverrides = ValidationOverrides.fromXml("<validation-overrides version='1.0'>" + - " <allow until='2017-06-15'>deployment-removal</allow>" + - "</validation-overrides>"); + " <allow until='2017-06-15'>deployment-removal</allow>" + + "</validation-overrides>"); OptionalLong projectId = OptionalLong.of(123L); @@ -89,69 +89,69 @@ public class ApplicationSerializerTest { ApplicationId id3 = ApplicationId.from("t1", "a1", "i3"); List<Deployment> deployments = new ArrayList<>(); 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); + 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(RevisionId.forDevelopment(31, new JobId(id1, DeploymentContext.productionUsEast3)), - new SourceRevision("repo1", "branch1", "commit1"), "a@b", - Version.fromString("6.3.1"), - Instant.ofEpochMilli(496)); + 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.id(), Version.fromString("1.2.3"), Instant.ofEpochMilli(3), - DeploymentMetrics.none, DeploymentActivity.none, QuotaUsage.none, OptionalDouble.empty())); + DeploymentMetrics.none, DeploymentActivity.none, QuotaUsage.none, OptionalDouble.empty())); 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)), - DeploymentActivity.create(Optional.of(activityAt), Optional.of(activityAt), - OptionalDouble.of(200), OptionalDouble.of(10)), - QuotaUsage.create(OptionalDouble.of(23.5)), - OptionalDouble.of(12.3))); + new DeploymentMetrics(2, 3, 4, 5, 6, + Optional.of(Instant.now().truncatedTo(ChronoUnit.MILLIS)), + Map.of(DeploymentMetrics.Warning.all, 3)), + DeploymentActivity.create(Optional.of(activityAt), Optional.of(activityAt), + OptionalDouble.of(200), OptionalDouble.of(10)), + QuotaUsage.create(OptionalDouble.of(23.5)), + OptionalDouble.of(12.3))); var rotationStatus = RotationStatus.from(Map.of(new RotationId("my-rotation"), - new RotationStatus.Targets( - Map.of(ZoneId.from("prod", "us-west-1"), RotationState.in, - ZoneId.from("prod", "us-east-3"), RotationState.out), - Instant.ofEpochMilli(42)))); + new RotationStatus.Targets( + Map.of(ZoneId.from("prod", "us-west-1"), RotationState.in, + ZoneId.from("prod", "us-east-3"), RotationState.out), + Instant.ofEpochMilli(42)))); RevisionHistory revisions = RevisionHistory.ofRevisions(List.of(applicationVersion1), - Map.of(new JobId(id1, DeploymentContext.productionUsEast3), List.of(applicationVersion2))); + Map.of(new JobId(id1, DeploymentContext.productionUsEast3), List.of(applicationVersion2))); List<Instance> instances = List.of(new Instance(id1, - deployments, - Map.of(DeploymentContext.systemTest, Instant.ofEpochMilli(333)), - List.of(AssignedRotation.fromStrings("foo", "default", "my-rotation", Set.of("us-west-1"))), - rotationStatus, - Change.of(new Version("6.1"))), - new Instance(id3, - List.of(), - Map.of(), - List.of(), - RotationStatus.EMPTY, - Change.of(Version.fromString("6.7")).withPin())); + deployments, + Map.of(DeploymentContext.systemTest, Instant.ofEpochMilli(333)), + List.of(AssignedRotation.fromStrings("foo", "default", "my-rotation", Set.of("us-west-1"))), + rotationStatus, + Change.of(new Version("6.1"))), + new Instance(id3, + List.of(), + Map.of(), + List.of(), + RotationStatus.EMPTY, + Change.of(Version.fromString("6.7")).withPin())); Application original = new Application(TenantAndApplicationId.from(id1), - Instant.now().truncatedTo(ChronoUnit.MILLIS), - deploymentSpec, - validationOverrides, - Optional.of(IssueId.from("4321")), - Optional.of(IssueId.from("1234")), - Optional.of(User.from("by-username")), - OptionalInt.of(7), - new ApplicationMetrics(0.5, 0.9), - Set.of(publicKey, otherPublicKey), - projectId, - revisions, - instances + Instant.now().truncatedTo(ChronoUnit.MILLIS), + deploymentSpec, + validationOverrides, + Optional.of(IssueId.from("4321")), + Optional.of(IssueId.from("1234")), + Optional.of(User.from("by-username")), + OptionalInt.of(7), + new ApplicationMetrics(0.5, 0.9), + Set.of(publicKey, otherPublicKey), + projectId, + revisions, + instances ); Application serialized = APPLICATION_SERIALIZER.fromSlime(SlimeUtils.toJsonBytes(APPLICATION_SERIALIZER.toSlime(original))); @@ -189,9 +189,9 @@ public class ApplicationSerializerTest { assertEquals(original.require(id1.instance()).deployments().get(zone2), serialized.require(id1.instance()).deployments().get(zone2)); assertEquals(original.require(id1.instance()).jobPause(DeploymentContext.systemTest), - serialized.require(id1.instance()).jobPause(DeploymentContext.systemTest)); + serialized.require(id1.instance()).jobPause(DeploymentContext.systemTest)); assertEquals(original.require(id1.instance()).jobPause(DeploymentContext.stagingTest), - serialized.require(id1.instance()).jobPause(DeploymentContext.stagingTest)); + serialized.require(id1.instance()).jobPause(DeploymentContext.stagingTest)); assertEquals(original.ownershipIssueId(), serialized.ownershipIssueId()); assertEquals(original.owner(), serialized.owner()); @@ -220,7 +220,7 @@ public class ApplicationSerializerTest { } @Test - public void testCompleteApplicationDeserialization() throws Exception { + void testCompleteApplicationDeserialization() throws Exception { byte[] applicationJson = Files.readAllBytes(testData.resolve("complete-application.json")); APPLICATION_SERIALIZER.fromSlime(applicationJson); // ok if no error diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ArchiveBucketsSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ArchiveBucketsSerializerTest.java index a3e4f6e6f89..82c5a6fc0c1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ArchiveBucketsSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ArchiveBucketsSerializerTest.java @@ -3,16 +3,16 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.config.provision.TenantName; import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveBucket; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.LinkedHashSet; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ArchiveBucketsSerializerTest { @Test - public void serdes() { + void serdes() { var testTenants = new LinkedHashSet<TenantName>(); testTenants.add(TenantName.from("tenant1")); testTenants.add(TenantName.from("tenant2")); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializerTest.java index c047f31e171..4508372738f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/AuditLogSerializerTest.java @@ -2,7 +2,7 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.vespa.hosted.controller.auditlog.AuditLog; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -10,8 +10,8 @@ import java.time.Instant; import java.util.List; import static java.time.temporal.ChronoUnit.MILLIS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue; public class AuditLogSerializerTest { @Test - public void test_serialization() { + void test_serialization() { Instant i1 = Instant.now(); Instant i2 = i1.minus(Duration.ofHours(1)); Instant i3 = i1.minus(Duration.ofHours(2)); @@ -27,20 +27,20 @@ public class AuditLogSerializerTest { AuditLog log = new AuditLog(List.of( new AuditLog.Entry(i1, "bar", AuditLog.Entry.Method.POST, - "/bar/baz/", - "0".repeat(2048).getBytes(StandardCharsets.UTF_8)), + "/bar/baz/", + "0".repeat(2048).getBytes(StandardCharsets.UTF_8)), new AuditLog.Entry(i2, "foo", AuditLog.Entry.Method.POST, - "/foo/bar/", - "{\"foo\":\"bar\"}".getBytes(StandardCharsets.UTF_8)), + "/foo/bar/", + "{\"foo\":\"bar\"}".getBytes(StandardCharsets.UTF_8)), new AuditLog.Entry(i3, "baz", AuditLog.Entry.Method.POST, - "/foo/baz/", - new byte[0]), + "/foo/baz/", + new byte[0]), new AuditLog.Entry(i4, "baz", AuditLog.Entry.Method.POST, - "/foo/baz/", - "000\ufdff\ufeff\uffff000".getBytes(StandardCharsets.UTF_8)), // non-ascii + "/foo/baz/", + "000\ufdff\ufeff\uffff000".getBytes(StandardCharsets.UTF_8)), // non-ascii new AuditLog.Entry(i4, "quux", AuditLog.Entry.Method.POST, - "/foo/quux/", - new byte[]{(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}) // garbage + "/foo/quux/", + new byte[]{(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}) // garbage )); AuditLogSerializer serializer = new AuditLogSerializer(); 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 0f7f97d333a..eb5422f5cff 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 @@ -10,7 +10,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockRunDataStore; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.RunLog; import com.yahoo.vespa.hosted.controller.deployment.Step; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.ArrayList; @@ -20,21 +20,21 @@ import java.util.Optional; import java.util.stream.IntStream; import static java.util.stream.Collectors.toUnmodifiableList; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BufferedLogStoreTest { @Test - public void chunkingAndFlush() { + void chunkingAndFlush() { int chunkSize = 1 << 10; int maxChunks = 1 << 5; 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"), - DeploymentContext.productionUsWest1, - 123); + DeploymentContext.productionUsWest1, + 123); byte[] manyBytes = new byte[chunkSize / 2 + 1]; // One fits, and two (over-)fills. Arrays.fill(manyBytes, (byte) 'O'); @@ -50,29 +50,29 @@ public class BufferedLogStoreTest { assertEquals(Optional.empty(), logs.readFinished(id, -1)); assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), -1)); - logs.append(id.application(), id.type(), Step.deployReal, List.of(entry)); + logs.append(id.application(), id.type(), Step.deployReal, List.of(entry), false); assertEquals(List.of(entry0), - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 0)); - logs.append(id.application(), id.type(), Step.deployReal, List.of(entry)); + logs.append(id.application(), id.type(), Step.deployReal, List.of(entry), false); assertEquals(List.of(entry0, entry1), - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); assertEquals(List.of(entry1), - logs.readActive(id.application(), id.type(), 0).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), 0).get(Step.deployReal)); assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 1)); - logs.append(id.application(), id.type(), Step.deployReal, List.of(entry, entry, entry)); + logs.append(id.application(), id.type(), Step.deployReal, List.of(entry, entry, entry), false); assertEquals(List.of(entry0, entry1, entry2, entry3, entry4), - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); assertEquals(List.of(entry1, entry2, entry3, entry4), - logs.readActive(id.application(), id.type(), 0).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), 0).get(Step.deployReal)); assertEquals(List.of(entry2, entry3, entry4), - logs.readActive(id.application(), id.type(), 1).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), 1).get(Step.deployReal)); assertEquals(List.of(entry3, entry4), - logs.readActive(id.application(), id.type(), 2).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), 2).get(Step.deployReal)); assertEquals(List.of(entry4), - logs.readActive(id.application(), id.type(), 3).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), 3).get(Step.deployReal)); assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), 4)); // We should now have three chunks, with two, two, and one entries. @@ -86,40 +86,51 @@ public class BufferedLogStoreTest { assertEquals(RunLog.empty(), logs.readActive(id.application(), id.type(), -1)); assertEquals(List.of(entry0, entry1, entry2, entry3, entry4), - logs.readFinished(id, -1).get().get(Step.deployReal)); + logs.readFinished(id, -1).get().get(Step.deployReal)); assertEquals(List.of(entry1, entry2, entry3, entry4), - logs.readFinished(id, 0).get().get(Step.deployReal)); + logs.readFinished(id, 0).get().get(Step.deployReal)); assertEquals(List.of(entry2, entry3, entry4), - logs.readFinished(id, 1).get().get(Step.deployReal)); + logs.readFinished(id, 1).get().get(Step.deployReal)); assertEquals(List.of(entry3, entry4), - logs.readFinished(id, 2).get().get(Step.deployReal)); + logs.readFinished(id, 2).get().get(Step.deployReal)); assertEquals(List.of(entry4), - logs.readFinished(id, 3).get().get(Step.deployReal)); + logs.readFinished(id, 3).get().get(Step.deployReal)); assertEquals(List.of(), logs.readFinished(id, 4).get().get(Step.deployReal)); // Logging a large entry enough times to reach the maximum size causes no further entries to be stored. List<LogEntry> monsterLog = IntStream.range(0, 2 * maxChunks + 3) - .mapToObj(i -> new LogEntry(i, entry.at(), entry.type(), entry.message())) - .collect(toUnmodifiableList()); + .mapToObj(i -> new LogEntry(i, entry.at(), entry.type(), entry.message())) + .collect(toUnmodifiableList()); List<LogEntry> logged = new ArrayList<>(monsterLog); logged.remove(logged.size() - 1); logged.remove(logged.size() - 1); logged.remove(logged.size() - 1); - logged.add(new LogEntry(2 * maxChunks, entry.at(), LogEntry.Type.warning, "Max log size of " + ((chunkSize * maxChunks) >> 20) + "Mb exceeded; further entries are discarded.")); + logged.add(new LogEntry(2 * maxChunks, entry.at(), LogEntry.Type.warning, "Max log size of " + ((chunkSize * maxChunks) >> 20) + "Mb exceeded; further user entries are discarded.")); - logs.append(id.application(), id.type(), Step.deployReal, monsterLog); + logs.append(id.application(), id.type(), Step.deployReal, monsterLog, false); assertEquals(logged.size(), - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size()); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size()); assertEquals(logged, - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + // An additional, forced entry is appended. + LogEntry forced = new LogEntry(logged.size(), entry.at(), entry.type(), entry.message()); + logs.append(id.application(), id.type(), Step.deployReal, List.of(forced), true); + logged.add(forced); + assertEquals(logged.size(), + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size()); + assertEquals(logged, + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + logged.remove(logged.size() - 1); + + // Flushing the buffer clears it again, and makes it ready for reuse. logs.flush(id); for (int i = 0; i < 2 * maxChunks + 3; i++) - logs.append(id.application(), id.type(), Step.deployReal, List.of(entry)); + logs.append(id.application(), id.type(), Step.deployReal, List.of(entry), false); assertEquals(logged.size(), - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size()); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal).size()); assertEquals(logged, - logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); + logs.readActive(id.application(), id.type(), -1).get(Step.deployReal)); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializerTest.java index a52a6b81eb9..77686467336 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ChangeRequestSerializerTest.java @@ -6,13 +6,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequest; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestSource; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.time.ZonedDateTime; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author olaa @@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals; public class ChangeRequestSerializerTest { @Test - public void reserialization_equality() { + void reserialization_equality() { var source = new ChangeRequestSource("aws", "id321", "url", ChangeRequestSource.Status.STARTED, ZonedDateTime.now(), ZonedDateTime.now()); var actionPlan = List.of( new HostAction("host1", HostAction.State.RETIRING, Instant.now()), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ControllerVersionSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ControllerVersionSerializerTest.java index 20cc82f0143..a7188cfa93e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ControllerVersionSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ControllerVersionSerializerTest.java @@ -3,11 +3,11 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.component.Version; import com.yahoo.vespa.hosted.controller.api.identifiers.ControllerVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -17,7 +17,7 @@ public class ControllerVersionSerializerTest { private final ControllerVersionSerializer serializer = new ControllerVersionSerializer(); @Test - public void serialization() { + void serialization() { var version = new ControllerVersion(Version.fromString("7.42.1"), "badc0ffee", Instant.ofEpochSecond(1565876112)); var serialized = serializer.fromSlime(serializer.toSlime(version)); assertEquals(version.version(), serialized.version()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java index c4aa2b5a18b..819d9282618 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/EndpointCertificateMetadataSerializerTest.java @@ -2,12 +2,12 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.Optional; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EndpointCertificateMetadataSerializerTest { @@ -18,21 +18,21 @@ public class EndpointCertificateMetadataSerializerTest { new EndpointCertificateMetadata("keyName", "certName", 1, 0, "rootRequestId", Optional.empty(), List.of("SAN1", "SAN2"), "issuer", Optional.empty(), Optional.empty()); @Test - public void serialize_with_optional_fields() { + void serialize_with_optional_fields() { assertEquals( "{\"keyName\":\"keyName\",\"certName\":\"certName\",\"version\":1,\"lastRequested\":0,\"requestId\":\"rootRequestId\",\"leafRequestId\":\"leafRequestId\",\"requestedDnsSans\":[\"SAN1\",\"SAN2\"],\"issuer\":\"issuer\",\"expiry\":1628000000,\"lastRefreshed\":1612000000}", EndpointCertificateMetadataSerializer.toSlime(sampleWithOptionalFieldsSet).toString()); } @Test - public void serialize_without_optional_fields() { + void serialize_without_optional_fields() { assertEquals( "{\"keyName\":\"keyName\",\"certName\":\"certName\",\"version\":1,\"lastRequested\":0,\"requestId\":\"rootRequestId\",\"requestedDnsSans\":[\"SAN1\",\"SAN2\"],\"issuer\":\"issuer\"}", EndpointCertificateMetadataSerializer.toSlime(sampleWithoutOptionalFieldsSet).toString()); } @Test - public void deserialize_from_json_with_optional_fields() { + void deserialize_from_json_with_optional_fields() { assertEquals( sampleWithOptionalFieldsSet, EndpointCertificateMetadataSerializer.fromJsonString( @@ -40,7 +40,7 @@ public class EndpointCertificateMetadataSerializerTest { } @Test - public void deserialize_from_json_without_optional_fields() { + void deserialize_from_json_without_optional_fields() { assertEquals( sampleWithoutOptionalFieldsSet, EndpointCertificateMetadataSerializer.fromJsonString( diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LogSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LogSerializerTest.java index 1e63b87e7c6..d03a17edde2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LogSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/LogSerializerTest.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.vespa.hosted.controller.api.integration.LogEntry; import com.yahoo.vespa.hosted.controller.deployment.Step; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; @@ -17,7 +17,7 @@ import java.util.Map; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -28,7 +28,7 @@ public class LogSerializerTest { private static final Path logsFile = Paths.get("src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/logs.json"); @Test - public void testSerialization() throws IOException { + void testSerialization() throws IOException { for (LogEntry.Type type : LogEntry.Type.values()) assertEquals(type, LogSerializer.typeOf(LogSerializer.valueOf(type))); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java index 5ecc22ffc5e..6f0a36690ed 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java @@ -11,11 +11,11 @@ import com.yahoo.vespa.hosted.controller.dns.CreateRecord; import com.yahoo.vespa.hosted.controller.dns.CreateRecords; import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue; import com.yahoo.vespa.hosted.controller.dns.RemoveRecords; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -25,20 +25,20 @@ public class NameServiceQueueSerializerTest { private final NameServiceQueueSerializer serializer = new NameServiceQueueSerializer(); @Test - public void test_serialization() { + void test_serialization() { var record1 = new Record(Record.Type.CNAME, RecordName.from("cname.example.com"), RecordData.from("example.com")); var record2 = new Record(Record.Type.TXT, RecordName.from("txt.example.com"), RecordData.from("text")); var requests = List.of( new CreateRecord(record1), new CreateRecords(List.of(record2)), new CreateRecords(List.of(new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"), - new LatencyAliasTarget(HostName.of("alias1"), - "dns-zone-01", - ZoneId.from("prod", "us-north-1")).pack()), - new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"), - new LatencyAliasTarget(HostName.of("alias2"), - "dns-zone-02", - ZoneId.from("prod", "us-north-2")).pack())) + new LatencyAliasTarget(HostName.of("alias1"), + "dns-zone-01", + ZoneId.from("prod", "us-north-1")).pack()), + new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"), + new LatencyAliasTarget(HostName.of("alias2"), + "dns-zone-02", + ZoneId.from("prod", "us-north-2")).pack())) ), new RemoveRecords(record1.type(), record1.name()), new RemoveRecords(record2.type(), record2.data()) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NotificationsSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NotificationsSerializerTest.java index 370b1cbe02c..c9755672232 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NotificationsSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NotificationsSerializerTest.java @@ -10,13 +10,13 @@ import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.notification.Notification; import com.yahoo.vespa.hosted.controller.notification.NotificationSource; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.time.Instant; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author freva @@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals; public class NotificationsSerializerTest { @Test - public void serialization_test() throws IOException { + void serialization_test() throws IOException { NotificationsSerializer serializer = new NotificationsSerializer(); TenantName tenantName = TenantName.from("tenant1"); List<Notification> notifications = List.of( @@ -42,20 +42,20 @@ public class NotificationsSerializerTest { Slime serialized = serializer.toSlime(notifications); assertEquals("{\"notifications\":[" + "{" + - "\"at\":1234000," + - "\"type\":\"applicationPackage\"," + - "\"level\":\"warning\"," + - "\"messages\":[\"Something something deprecated...\"]," + - "\"application\":\"app1\"" + + "\"at\":1234000," + + "\"type\":\"applicationPackage\"," + + "\"level\":\"warning\"," + + "\"messages\":[\"Something something deprecated...\"]," + + "\"application\":\"app1\"" + "},{" + - "\"at\":2345000," + - "\"type\":\"deployment\"," + - "\"level\":\"error\"," + - "\"messages\":[\"Failed to deploy: Node allocation failure\"]," + - "\"application\":\"app1\"," + - "\"instance\":\"instance1\"," + - "\"jobId\":\"test.us-east-1\"," + - "\"runNumber\":12" + + "\"at\":2345000," + + "\"type\":\"deployment\"," + + "\"level\":\"error\"," + + "\"messages\":[\"Failed to deploy: Node allocation failure\"]," + + "\"application\":\"app1\"," + + "\"instance\":\"instance1\"," + + "\"jobId\":\"test.us-east-1\"," + + "\"runNumber\":12" + "}]}", new String(SlimeUtils.toJsonBytes(serialized))); List<Notification> deserialized = serializer.fromSlime(tenantName, serialized); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionSerializerTest.java index c722b3bbb96..568b17817a1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionSerializerTest.java @@ -5,11 +5,11 @@ import com.google.common.collect.ImmutableSet; import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; import com.yahoo.vespa.hosted.controller.versions.OsVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -17,7 +17,7 @@ import static org.junit.Assert.assertEquals; public class OsVersionSerializerTest { @Test - public void test_serialization() { + void test_serialization() { OsVersionSerializer serializer = new OsVersionSerializer(); Set<OsVersion> osVersions = ImmutableSet.of( new OsVersion(Version.fromString("7.1"), CloudName.defaultName()), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionStatusSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionStatusSerializerTest.java index d50c071d98e..7461bf6516c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionStatusSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionStatusSerializerTest.java @@ -8,7 +8,7 @@ import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.versions.NodeVersion; import com.yahoo.vespa.hosted.controller.versions.OsVersion; import com.yahoo.vespa.hosted.controller.versions.OsVersionStatus; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.LinkedHashMap; @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals; public class OsVersionStatusSerializerTest { @Test - public void serialization() { + void serialization() { Version version1 = Version.fromString("7.1"); Version version2 = Version.fromString("7.2"); Map<OsVersion, List<NodeVersion>> versions = new LinkedHashMap<>(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionTargetSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionTargetSerializerTest.java index 1af4465528d..654703b36c0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionTargetSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/OsVersionTargetSerializerTest.java @@ -6,13 +6,13 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; import com.yahoo.vespa.hosted.controller.versions.OsVersion; import com.yahoo.vespa.hosted.controller.versions.OsVersionTarget; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals; public class OsVersionTargetSerializerTest { @Test - public void serialization() { + void serialization() { OsVersionTargetSerializer serializer = new OsVersionTargetSerializer(new OsVersionSerializer()); Set<OsVersionTarget> targets = ImmutableSet.of( new OsVersionTarget(new OsVersion(Version.fromString("7.1"), CloudName.defaultName()), Duration.ZERO, Instant.ofEpochMilli(123)), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java index 2e8ba84d4ff..3df459513d7 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java @@ -9,7 +9,7 @@ import com.yahoo.vespa.hosted.controller.application.EndpointId; import com.yahoo.vespa.hosted.controller.routing.RoutingPolicy; import com.yahoo.vespa.hosted.controller.routing.RoutingPolicyId; import com.yahoo.vespa.hosted.controller.routing.RoutingStatus; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.Iterator; @@ -17,7 +17,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mortent @@ -27,34 +27,34 @@ public class RoutingPolicySerializerTest { private final RoutingPolicySerializer serializer = new RoutingPolicySerializer(); @Test - public void serialization() { + void serialization() { var owner = ApplicationId.defaultId(); var instanceEndpoints = Set.of(EndpointId.of("r1"), EndpointId.of("r2")); var applicationEndpoints = Set.of(EndpointId.of("a1")); var id1 = new RoutingPolicyId(owner, - ClusterSpec.Id.from("my-cluster1"), - ZoneId.from("prod", "us-north-1")); + ClusterSpec.Id.from("my-cluster1"), + ZoneId.from("prod", "us-north-1")); var id2 = new RoutingPolicyId(owner, - ClusterSpec.Id.from("my-cluster2"), - ZoneId.from("prod", "us-north-2")); + ClusterSpec.Id.from("my-cluster2"), + ZoneId.from("prod", "us-north-2")); var policies = List.of(new RoutingPolicy(id1, - HostName.of("long-and-ugly-name"), - Optional.of("zone1"), - instanceEndpoints, - applicationEndpoints, - new RoutingPolicy.Status(true, RoutingStatus.DEFAULT)), - new RoutingPolicy(id2, - HostName.of("long-and-ugly-name-2"), - Optional.empty(), - instanceEndpoints, - Set.of(), - new RoutingPolicy.Status(false, - new RoutingStatus(RoutingStatus.Value.out, - RoutingStatus.Agent.tenant, - Instant.ofEpochSecond(123))))); + HostName.of("long-and-ugly-name"), + Optional.of("zone1"), + instanceEndpoints, + applicationEndpoints, + new RoutingPolicy.Status(true, RoutingStatus.DEFAULT)), + new RoutingPolicy(id2, + HostName.of("long-and-ugly-name-2"), + Optional.empty(), + instanceEndpoints, + Set.of(), + new RoutingPolicy.Status(false, + new RoutingStatus(RoutingStatus.Value.out, + RoutingStatus.Agent.tenant, + Instant.ofEpochSecond(123))))); var serialized = serializer.fromSlime(owner, serializer.toSlime(policies)); assertEquals(policies.size(), serialized.size()); - for (Iterator<RoutingPolicy> it1 = policies.iterator(), it2 = serialized.iterator(); it1.hasNext();) { + for (Iterator<RoutingPolicy> it1 = policies.iterator(), it2 = serialized.iterator(); it1.hasNext(); ) { var expected = it1.next(); var actual = it2.next(); assertEquals(expected.id(), actual.id()); 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 6a01a70eb98..644f26e888a 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 @@ -15,7 +15,7 @@ import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.deployment.RunStatus; import com.yahoo.vespa.hosted.controller.deployment.Step; import com.yahoo.vespa.hosted.controller.deployment.StepInfo; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; @@ -46,9 +46,9 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.startStagingSetu 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; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class RunSerializerTest { @@ -60,7 +60,7 @@ public class RunSerializerTest { private static final Instant start = Instant.parse("2007-12-03T10:15:30.00Z"); @Test - public void testSerialization() throws IOException { + void testSerialization() throws IOException { for (Step step : Step.values()) assertEquals(step, RunSerializer.stepOf(RunSerializer.valueOf(step))); @@ -89,16 +89,16 @@ public class RunSerializerTest { assertEquals(new Version(1, 2, 2), run.versions().sourcePlatform().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()); + run.convergenceSummary()); assertEquals(X509CertificateUtils.fromPem("-----BEGIN CERTIFICATE-----\n" + - "MIIBEzCBu6ADAgECAgEBMAoGCCqGSM49BAMEMBQxEjAQBgNVBAMTCW15c2Vydmlj\n" + - "ZTAeFw0xOTA5MDYwNzM3MDZaFw0xOTA5MDcwNzM3MDZaMBQxEjAQBgNVBAMTCW15\n" + - "c2VydmljZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM0JhD8fV2DlAkjQOGX3\n" + - "Y50ryMBr3g2+v/uFiRoxJ1muuSOWYrW7HCQIGuzc04fa0QwtaX/voAZKCV51t6jF\n" + - "0fwwCgYIKoZIzj0EAwQDRwAwRAIgVbQ3Co1H4X0gmRrtXSyTU0HgBQu9PXHMmX20\n" + - "5MyyPSoCIBltOcmaPfdN03L3zqbqZ6PgUBWsvAHgiBzL3hrtJ+iy\n" + - "-----END CERTIFICATE-----"), - run.testerCertificate().get()); + "MIIBEzCBu6ADAgECAgEBMAoGCCqGSM49BAMEMBQxEjAQBgNVBAMTCW15c2Vydmlj\n" + + "ZTAeFw0xOTA5MDYwNzM3MDZaFw0xOTA5MDcwNzM3MDZaMBQxEjAQBgNVBAMTCW15\n" + + "c2VydmljZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABM0JhD8fV2DlAkjQOGX3\n" + + "Y50ryMBr3g2+v/uFiRoxJ1muuSOWYrW7HCQIGuzc04fa0QwtaX/voAZKCV51t6jF\n" + + "0fwwCgYIKoZIzj0EAwQDRwAwRAIgVbQ3Co1H4X0gmRrtXSyTU0HgBQu9PXHMmX20\n" + + "5MyyPSoCIBltOcmaPfdN03L3zqbqZ6PgUBWsvAHgiBzL3hrtJ+iy\n" + + "-----END CERTIFICATE-----"), + run.testerCertificate().get()); assertEquals(ImmutableMap.<Step, StepInfo>builder() .put(deployInitialReal, new StepInfo(deployInitialReal, unfinished, Optional.empty())) .put(installInitialReal, new StepInfo(installInitialReal, failed, Optional.of(Instant.ofEpochMilli(1196676940000L)))) @@ -118,10 +118,10 @@ public class RunSerializerTest { run.steps()); run = run.with(1L << 50) - .with(Instant.now().truncatedTo(MILLIS)) - .noNodesDownSince(Instant.now().truncatedTo(MILLIS)) - .aborted() - .finished(Instant.now().truncatedTo(MILLIS)); + .with(Instant.now().truncatedTo(MILLIS)) + .noNodesDownSince(Instant.now().truncatedTo(MILLIS)) + .aborted() + .finished(Instant.now().truncatedTo(MILLIS)); assertEquals(aborted, run.status()); assertTrue(run.hasEnded()); @@ -140,7 +140,7 @@ public class RunSerializerTest { assertEquals(run.reason(), phoenix.reason()); assertEquals(new String(SlimeUtils.toJsonBytes(serializer.toSlime(run).get(), false), UTF_8), - new String(SlimeUtils.toJsonBytes(serializer.toSlime(phoenix).get(), false), UTF_8)); + new String(SlimeUtils.toJsonBytes(serializer.toSlime(phoenix).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))); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java index 8b2c291f751..8aa53fb5cd4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java @@ -11,7 +11,7 @@ import com.yahoo.slime.Slime; import com.yahoo.vespa.hosted.controller.support.access.SupportAccess; import com.yahoo.vespa.hosted.controller.support.access.SupportAccessGrant; import org.intellij.lang.annotations.Language; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.security.auth.x500.X500Principal; import java.io.ByteArrayOutputStream; @@ -22,7 +22,7 @@ import java.security.cert.X509Certificate; import java.time.Instant; import java.time.temporal.ChronoUnit; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SupportAccessSerializerTest { @@ -78,70 +78,70 @@ public class SupportAccessSerializerTest { } @Test - public void serialize_default() { + void serialize_default() { var slime = SupportAccessSerializer.serializeCurrentState(SupportAccess.DISALLOWED_NO_HISTORY, Instant.EPOCH); assertSerialized(slime, "{\n" + - " \"state\": {\n" + - " \"supportAccess\": \"NOT_ALLOWED\"\n" + - " },\n" + - " \"history\": [ ],\n" + - " \"grants\": [ ]\n" + - "}\n"); + " \"state\": {\n" + + " \"supportAccess\": \"NOT_ALLOWED\"\n" + + " },\n" + + " \"history\": [ ],\n" + + " \"grants\": [ ]\n" + + "}\n"); } @Test - public void serialize_with_certificates() { + void serialize_with_certificates() { var slime = SupportAccessSerializer.toSlime(supportAccessExample); assertSerialized(slime, expectedWithCertificates); } @Test - public void serialize_with_status() { + void serialize_with_status() { var slime = SupportAccessSerializer.serializeCurrentState(supportAccessExample, hour(12)); assertSerialized(slime, - "{\n" - + " \"state\": {\n" - + " \"supportAccess\": \"ALLOWED\",\n" - + " \"until\": \"1970-01-02T12:00:00Z\",\n" - + " \"by\": \"andreer\"\n" - + " },\n" - + " \"history\": [\n" - + " {\n" - + " \"state\": \"allowed\",\n" - + " \"at\": \"1970-01-02T06:00:00Z\",\n" - + " \"until\": \"1970-01-02T12:00:00Z\",\n" - + " \"by\": \"andreer\"\n" - + " },\n" - + " {\n" - + " \"state\": \"disallowed\",\n" - + " \"at\": \"1970-01-01T22:00:00Z\",\n" - + " \"by\": \"andreer\"\n" - + " },\n" - + " {\n" - + " \"state\": \"allowed\",\n" - + " \"at\": \"1970-01-01T02:00:00Z\",\n" - + " \"until\": \"1970-01-02T00:00:00Z\",\n" - + " \"by\": \"andreer\"\n" - + " },\n" - + " {\n" - + " \"state\": \"grant\",\n" - + " \"at\": \"1970-01-01T03:00:00Z\",\n" - + " \"until\": \"1970-01-01T04:00:00Z\",\n" - + " \"by\": \"mortent\"\n" - + " }\n" - + " ],\n" - + " \"grants\": [\n" - + " {\n" - + " \"requestor\": \"mortent\",\n" - + " \"notBefore\": \"1970-01-01T07:00:00Z\",\n" - + " \"notAfter\": \"1970-01-01T19:00:00Z\"\n" - + " }\n" - + " ]\n" - + "}\n"); + "{\n" + + " \"state\": {\n" + + " \"supportAccess\": \"ALLOWED\",\n" + + " \"until\": \"1970-01-02T12:00:00Z\",\n" + + " \"by\": \"andreer\"\n" + + " },\n" + + " \"history\": [\n" + + " {\n" + + " \"state\": \"allowed\",\n" + + " \"at\": \"1970-01-02T06:00:00Z\",\n" + + " \"until\": \"1970-01-02T12:00:00Z\",\n" + + " \"by\": \"andreer\"\n" + + " },\n" + + " {\n" + + " \"state\": \"disallowed\",\n" + + " \"at\": \"1970-01-01T22:00:00Z\",\n" + + " \"by\": \"andreer\"\n" + + " },\n" + + " {\n" + + " \"state\": \"allowed\",\n" + + " \"at\": \"1970-01-01T02:00:00Z\",\n" + + " \"until\": \"1970-01-02T00:00:00Z\",\n" + + " \"by\": \"andreer\"\n" + + " },\n" + + " {\n" + + " \"state\": \"grant\",\n" + + " \"at\": \"1970-01-01T03:00:00Z\",\n" + + " \"until\": \"1970-01-01T04:00:00Z\",\n" + + " \"by\": \"mortent\"\n" + + " }\n" + + " ],\n" + + " \"grants\": [\n" + + " {\n" + + " \"requestor\": \"mortent\",\n" + + " \"notBefore\": \"1970-01-01T07:00:00Z\",\n" + + " \"notAfter\": \"1970-01-01T19:00:00Z\"\n" + + " }\n" + + " ]\n" + + "}\n"); } @Test - public void deserialize() { + void deserialize() { var slime = SupportAccessSerializer.toSlime(supportAccessExample); assertSerialized(slime, expectedWithCertificates); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java index a9e633a78d6..636620acf07 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java @@ -23,7 +23,7 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantBilling; import com.yahoo.vespa.hosted.controller.tenant.TenantContact; import com.yahoo.vespa.hosted.controller.tenant.TenantContacts; import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URI; import java.security.PublicKey; @@ -33,9 +33,9 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -53,12 +53,12 @@ public class TenantSerializerTest { "-----END PUBLIC KEY-----\n"); @Test - public void athenz_tenant() { + void athenz_tenant() { AthenzTenant tenant = AthenzTenant.create(TenantName.from("athenz-tenant"), - new AthenzDomain("domain1"), - new Property("property1"), - Optional.of(new PropertyId("1")), - Instant.ofEpochMilli(1234L)); + new AthenzDomain("domain1"), + new Property("property1"), + Optional.of(new PropertyId("1")), + Instant.ofEpochMilli(1234L)); AthenzTenant serialized = (AthenzTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(tenant.name(), serialized.name()); assertEquals(tenant.domain(), serialized.domain()); @@ -69,42 +69,42 @@ public class TenantSerializerTest { } @Test - public void athenz_tenant_without_property_id() { + void athenz_tenant_without_property_id() { AthenzTenant tenant = AthenzTenant.create(TenantName.from("athenz-tenant"), - new AthenzDomain("domain1"), - new Property("property1"), - Optional.empty(), - Instant.EPOCH); + new AthenzDomain("domain1"), + new Property("property1"), + Optional.empty(), + Instant.EPOCH); AthenzTenant serialized = (AthenzTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertFalse(serialized.propertyId().isPresent()); assertEquals(tenant.propertyId(), serialized.propertyId()); } @Test - public void athenz_tenant_with_contact() { + void athenz_tenant_with_contact() { AthenzTenant tenant = new AthenzTenant(TenantName.from("athenz-tenant"), - new AthenzDomain("domain1"), - new Property("property1"), - Optional.of(new PropertyId("1")), - Optional.of(contact()), - Instant.EPOCH, - lastLoginInfo(321L, 654L, 987L)); + new AthenzDomain("domain1"), + new Property("property1"), + Optional.of(new PropertyId("1")), + Optional.of(contact()), + Instant.EPOCH, + lastLoginInfo(321L, 654L, 987L)); AthenzTenant serialized = (AthenzTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(tenant.contact(), serialized.contact()); } @Test - public void cloud_tenant() { + void cloud_tenant() { CloudTenant tenant = new CloudTenant(TenantName.from("elderly-lady"), - Instant.ofEpochMilli(1234L), - lastLoginInfo(123L, 456L, null), - Optional.of(new SimplePrincipal("foobar-user")), - ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"), - otherPublicKey, new SimplePrincipal("jane")), - TenantInfo.empty(), - List.of(), - new ArchiveAccess() - ); + Instant.ofEpochMilli(1234L), + lastLoginInfo(123L, 456L, null), + Optional.of(new SimplePrincipal("foobar-user")), + ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"), + otherPublicKey, new SimplePrincipal("jane")), + TenantInfo.empty(), + List.of(), + new ArchiveAccess(), + Optional.empty()); CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(tenant.name(), serialized.name()); assertEquals(tenant.creator(), serialized.creator()); @@ -113,7 +113,7 @@ public class TenantSerializerTest { } @Test - public void cloud_tenant_with_info() { + void cloud_tenant_with_info() { CloudTenant tenant = new CloudTenant(TenantName.from("elderly-lady"), Instant.EPOCH, lastLoginInfo(null, 789L, 654L), @@ -125,15 +125,16 @@ public class TenantSerializerTest { new TenantSecretStore("ss1", "123", "role1"), new TenantSecretStore("ss2", "124", "role2") ), - new ArchiveAccess().withAWSRole("arn:aws:iam::123456789012:role/my-role") - ); + new ArchiveAccess().withAWSRole("arn:aws:iam::123456789012:role/my-role"), + Optional.of(Instant.ofEpochMilli(1234567))); CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(tenant.info(), serialized.info()); assertEquals(tenant.tenantSecretStores(), serialized.tenantSecretStores()); + assertEquals(tenant.invalidateUserSessionsBefore(), serialized.invalidateUserSessionsBefore()); } @Test - public void cloud_tenant_with_old_archive_access_serialization() { + void cloud_tenant_with_old_archive_access_serialization() { var json = "{\n" + " \"name\": \"elderly-lady\",\n" + " \"type\": \"cloud\",\n" + @@ -165,7 +166,7 @@ public class TenantSerializerTest { } @Test - public void cloud_tenant_with_archive_access() { + void cloud_tenant_with_archive_access() { CloudTenant tenant = new CloudTenant(TenantName.from("elderly-lady"), Instant.ofEpochMilli(1234L), lastLoginInfo(123L, 456L, null), @@ -174,15 +175,15 @@ public class TenantSerializerTest { otherPublicKey, new SimplePrincipal("jane")), TenantInfo.empty(), List.of(), - new ArchiveAccess().withAWSRole("arn:aws:iam::123456789012:role/my-role").withGCPMember("user:foo@example.com") - ); + new ArchiveAccess().withAWSRole("arn:aws:iam::123456789012:role/my-role").withGCPMember("user:foo@example.com"), + Optional.empty()); CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(serialized.archiveAccess().awsRole().get(), "arn:aws:iam::123456789012:role/my-role"); assertEquals(serialized.archiveAccess().gcpMember().get(), "user:foo@example.com"); } @Test - public void cloud_tenant_with_tenant_info_partial() { + void cloud_tenant_with_tenant_info_partial() { TenantInfo partialInfo = TenantInfo.empty() .withAddress(TenantAddress.empty().withCity("Hønefoss")); @@ -193,7 +194,7 @@ public class TenantSerializerTest { } @Test - public void cloud_tenant_with_tenant_info_full() { + void cloud_tenant_with_tenant_info_full() { TenantInfo fullInfo = TenantInfo.empty() .withName("My Company") .withEmail("email@mycomp.any") @@ -222,7 +223,7 @@ public class TenantSerializerTest { } @Test - public void cloud_tenant_with_tenant_info_contacts() { + void cloud_tenant_with_tenant_info_contacts() { TenantInfo tenantInfo = TenantInfo.empty() .withContacts(new TenantContacts(List.of( new TenantContacts.EmailContact(List.of(TenantContacts.Audience.TENANT), "email1@email.com"), @@ -235,7 +236,7 @@ public class TenantSerializerTest { } @Test - public void deleted_tenant() { + void deleted_tenant() { DeletedTenant tenant = new DeletedTenant( TenantName.from("tenant1"), Instant.ofEpochMilli(1234L), Instant.ofEpochMilli(2345L)); DeletedTenant serialized = (DeletedTenant) serializer.tenantFrom(serializer.toSlime(tenant)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java index 73cfc6ad2f3..618c33835bd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/VersionStatusSerializerTest.java @@ -7,7 +7,7 @@ import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.versions.NodeVersion; import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.ArrayList; @@ -15,7 +15,7 @@ import java.util.List; import java.util.Optional; import static java.time.temporal.ChronoUnit.MILLIS; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -23,15 +23,15 @@ import static org.junit.Assert.assertEquals; public class VersionStatusSerializerTest { @Test - public void testSerialization() { + void testSerialization() { List<VespaVersion> vespaVersions = new ArrayList<>(); Version version = Version.fromString("5.0"); vespaVersions.add(new VespaVersion(version, "dead", Instant.now(), false, false, - true, nodeVersions(Version.fromString("5.0"), Version.fromString("5.1"), - "cfg1", "cfg2", "cfg3"), VespaVersion.Confidence.normal)); + true, nodeVersions(Version.fromString("5.0"), Version.fromString("5.1"), + "cfg1", "cfg2", "cfg3"), VespaVersion.Confidence.normal)); vespaVersions.add(new VespaVersion(version, "cafe", Instant.now(), true, true, - false, nodeVersions(Version.fromString("5.0"), Version.fromString("5.1"), - "cfg1", "cfg2", "cfg3"), VespaVersion.Confidence.normal)); + false, nodeVersions(Version.fromString("5.0"), Version.fromString("5.1"), + "cfg1", "cfg2", "cfg3"), VespaVersion.Confidence.normal)); VersionStatus status = new VersionStatus(vespaVersions); VersionStatusSerializer serializer = new VersionStatusSerializer(new NodeVersionSerializer()); VersionStatus deserialized = serializer.fromSlime(serializer.toSlime(status)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ZoneRoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ZoneRoutingPolicySerializerTest.java index 234de233571..f524f4a2d4f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ZoneRoutingPolicySerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ZoneRoutingPolicySerializerTest.java @@ -4,11 +4,11 @@ package com.yahoo.vespa.hosted.controller.persistence; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.routing.RoutingStatus; import com.yahoo.vespa.hosted.controller.routing.ZoneRoutingPolicy; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -16,12 +16,12 @@ import static org.junit.Assert.assertEquals; public class ZoneRoutingPolicySerializerTest { @Test - public void serialization() { + void serialization() { var serializer = new ZoneRoutingPolicySerializer(new RoutingPolicySerializer()); var zone = ZoneId.from("prod", "us-north-1"); var policy = new ZoneRoutingPolicy(zone, - RoutingStatus.create(RoutingStatus.Value.out, RoutingStatus.Agent.operator, - Instant.ofEpochMilli(123))); + RoutingStatus.create(RoutingStatus.Value.out, RoutingStatus.Agent.operator, + Instant.ofEpochMilli(123))); var serialized = serializer.fromSlime(zone, serializer.toSlime(policy)); assertEquals(policy, serialized); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImplTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImplTest.java index f5926e799af..5214ded0904 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImplTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ConfigServerRestExecutorImplTest.java @@ -2,7 +2,7 @@ package com.yahoo.vespa.hosted.controller.proxy; import ai.vespa.http.HttpURL.Path; -import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.github.tomakehurst.wiremock.stubbing.Scenario; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; @@ -10,8 +10,8 @@ import com.yahoo.yolean.concurrent.Sleeper; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpCoreContext; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import javax.net.ssl.SSLContext; import java.io.ByteArrayOutputStream; @@ -25,21 +25,21 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden */ public class ConfigServerRestExecutorImplTest { - @Rule - public final WireMockRule wireMock = new WireMockRule(options().dynamicPort(), true); + @RegisterExtension + public final WireMockExtension wireMock = WireMockExtension.newInstance().options(options().dynamicPort()).failOnUnmatchedRequests(true).build(); @Test - public void proxy_with_retries() throws Exception { + void proxy_with_retries() throws Exception { var connectionReuseStrategy = new CountingConnectionReuseStrategy(Set.of("127.0.0.1")); var proxy = new ConfigServerRestExecutorImpl(new SSLConnectionSocketFactory(SSLContext.getDefault()), - Sleeper.NOOP, connectionReuseStrategy); + Sleeper.NOOP, connectionReuseStrategy); URI url = url(); String path = url.getPath(); @@ -61,10 +61,10 @@ public class ConfigServerRestExecutorImplTest { } @Test - public void proxy_without_connection_reuse() throws Exception { + void proxy_without_connection_reuse() throws Exception { var connectionReuseStrategy = new CountingConnectionReuseStrategy(Set.of()); var proxy = new ConfigServerRestExecutorImpl(new SSLConnectionSocketFactory(SSLContext.getDefault()), - Sleeper.NOOP, connectionReuseStrategy); + Sleeper.NOOP, connectionReuseStrategy); URI url = url(); String path = url.getPath(); @@ -79,7 +79,7 @@ public class ConfigServerRestExecutorImplTest { } private URI url() { - return URI.create("http://127.0.0.1:" + wireMock.port() + "/"); + return URI.create("http://127.0.0.1:" + wireMock.getPort() + "/"); } private void stubRequests(String path) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequestTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequestTest.java index e6de60c859b..d851eb56890 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequestTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequestTest.java @@ -3,14 +3,14 @@ package com.yahoo.vespa.hosted.controller.proxy; import ai.vespa.http.HttpURL.Path; import com.yahoo.jdisc.http.HttpRequest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URI; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @author Haakon Dybdahl @@ -18,14 +18,14 @@ import static org.junit.Assert.assertThrows; public class ProxyRequestTest { @Test - public void testBadUri() { + void testBadUri() { assertEquals("Request path '/path' does not end with proxy path '/zone/v2/'", - assertThrows(IllegalArgumentException.class, - () -> testRequest("http://domain.tld/path", "/zone/v2/")).getMessage()); + assertThrows(IllegalArgumentException.class, + () -> testRequest("http://domain.tld/path", "/zone/v2/")).getMessage()); } @Test - public void testUris() { + void testUris() { { // Root request ProxyRequest request = testRequest("http://controller.domain.tld/my/path", ""); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponseTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponseTest.java index 845d007c154..08164de6a8e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponseTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponseTest.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.proxy; import ai.vespa.http.HttpURL.Path; import com.yahoo.jdisc.http.HttpRequest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.net.URI; @@ -11,7 +11,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Haakon Dybdahl @@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals; public class ProxyResponseTest { @Test - public void testRewriteUrl() throws Exception { + void testRewriteUrl() throws Exception { ProxyRequest request = new ProxyRequest(HttpRequest.Method.GET, URI.create("http://domain.tld/zone/v2/dev/us-north-1/configserver"), Map.of(), null, List.of(URI.create("http://example.com")), Path.parse("configserver")); ProxyResponse proxyResponse = new ProxyResponse( @@ -37,9 +37,9 @@ public class ProxyResponseTest { } @Test - public void testRewriteSecureUrl() throws Exception { + void testRewriteSecureUrl() throws Exception { ProxyRequest request = new ProxyRequest(HttpRequest.Method.GET, URI.create("https://domain.tld/zone/v2/prod/eu-south-3/configserver"), - Map.of(), null, List.of(URI.create("http://example.com")), Path.parse("configserver")); + Map.of(), null, List.of(URI.create("http://example.com")), Path.parse("configserver")); ProxyResponse proxyResponse = new ProxyResponse( request, "response link is http://configserver:4443/bla/bla/", 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..539d6cff06d 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 @@ -28,8 +28,8 @@ import java.util.Optional; import java.util.function.Supplier; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * Provides testing of JSON container responses @@ -123,7 +123,7 @@ public class ContainerTester { catch (IOException e) { fail("failed writing JSON: " + e); } - assertEquals("Status code", expectedStatusCode, response.getStatus()); + assertEquals(expectedStatusCode, response.getStatus(), "Status code"); } public void assertResponse(Supplier<Request> request, String expectedResponse) { @@ -154,7 +154,7 @@ public class ContainerTester { } catch (Exception e) { throw new RuntimeException(e); } - assertEquals("Status code", expectedStatusCode, response.getStatus()); + assertEquals(expectedStatusCode, response.getStatus(), "Status code"); } // Hack to run request filters as part of the request processing chain. diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java index 87fe8dd17fe..404dcca87c0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -9,8 +9,8 @@ import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzUser; import com.yahoo.vespa.athenz.api.OAuthCredentials; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactoryMock; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.IDENTITY_HEADER_NAME; import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.OKTA_ACCESS_TOKEN_HEADER_NAME; @@ -33,7 +33,7 @@ public class ControllerContainerTest { protected JDisc container; - @Before + @BeforeEach public void startContainer() { container = JDisc.fromServicesXml(controllerServicesXml(), networking()); addUserToHostedOperatorRole(hostedOperator); @@ -41,7 +41,7 @@ public class ControllerContainerTest { protected Networking networking() { return Networking.disable; } - @After + @AfterEach public void stopContainer() { container.close(); } private String controllerServicesXml() { 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 26dd6335ab8..674424fbdd9 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 @@ -27,8 +27,8 @@ import com.yahoo.vespa.hosted.controller.security.Auth0Credentials; import com.yahoo.vespa.hosted.controller.security.CloudTenantSpec; import com.yahoo.vespa.hosted.controller.security.Credentials; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.Collections; @@ -40,10 +40,10 @@ import static com.yahoo.application.container.handler.Request.Method.GET; import static com.yahoo.application.container.handler.Request.Method.POST; import static com.yahoo.application.container.handler.Request.Method.PUT; import static com.yahoo.vespa.hosted.controller.restapi.application.ApplicationApiTest.createApplicationSubmissionData; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author oyving @@ -57,7 +57,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { private static final TenantName tenantName = TenantName.from("scoober"); private static final ApplicationName applicationName = ApplicationName.from("albums"); - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); ((InMemoryFlagSource) tester.controller().flagSource()) @@ -66,7 +66,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void test_missing_security_clients_pem() { + void test_missing_security_clients_pem() { var application = prodBuilder().build(); var deployRequest = request("/application/v4/tenant/scoober/application/albums/submit", POST) @@ -80,7 +80,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void tenant_info_profile() { + void tenant_info_profile() { var request = request("/application/v4/tenant/scoober/info/profile", GET) .roles(Set.of(Role.reader(tenantName))); tester.assertResponse(request, "{}", 200); @@ -94,7 +94,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void tenant_info_billing() { + void tenant_info_billing() { var request = request("/application/v4/tenant/scoober/info/billing", GET) .roles(Set.of(Role.reader(tenantName))); tester.assertResponse(request, "{}", 200); @@ -111,7 +111,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void tenant_info_contacts() { + void tenant_info_contacts() { var request = request("/application/v4/tenant/scoober/info/contacts", GET) .roles(Set.of(Role.reader(tenantName))); tester.assertResponse(request, "{\"contacts\":[]}", 200); @@ -126,10 +126,10 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void tenant_info_workflow() { + void tenant_info_workflow() { var infoRequest = request("/application/v4/tenant/scoober/info", GET) - .roles(Set.of(Role.reader(tenantName))); + .roles(Set.of(Role.reader(tenantName))); tester.assertResponse(infoRequest, "{}", 200); String partialInfo = "{\"contactName\":\"newName\", \"contactEmail\": \"foo@example.com\", \"billingContact\":{\"name\":\"billingName\"}}"; @@ -166,7 +166,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void tenant_info_missing_fields() { + void tenant_info_missing_fields() { // tenants can be created with empty tenant info - they're not part of the POST to v4/tenant var infoRequest = request("/application/v4/tenant/scoober/info", GET) @@ -255,7 +255,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void trial_tenant_limit_reached() { + void trial_tenant_limit_reached() { ((InMemoryFlagSource) tester.controller().flagSource()).withIntFlag(PermanentFlags.MAX_TRIAL_TENANTS.id(), 1); tester.controller().serviceRegistry().billingController().setPlan(tenantName, PlanId.from("pay-as-you-go"), false, false); @@ -273,7 +273,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void test_secret_store_configuration() { + void test_secret_store_configuration() { var secretStoreRequest = request("/application/v4/tenant/scoober/secret-store/some-name", PUT) .data("{" + @@ -303,7 +303,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void validate_secret_store() { + void validate_secret_store() { deployApplication(); var secretStoreRequest = request("/application/v4/tenant/scoober/secret-store/secret-foo/validate?aws-region=us-west-1¶meter-name=foo&application-id=scoober.albums.default&zone=prod.aws-us-east-1c", GET) @@ -326,7 +326,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void delete_secret_store() { + void delete_secret_store() { var deleteRequest = request("/application/v4/tenant/scoober/secret-store/secret-foo", DELETE) .roles(Set.of(Role.developer(tenantName))); @@ -347,18 +347,18 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void archive_uri_test() { + void archive_uri_test() { ControllerTester wrapped = new ControllerTester(tester); wrapped.upgradeSystem(Version.fromString("7.1")); new DeploymentTester(wrapped).newDeploymentContext(ApplicationId.from(tenantName, applicationName, InstanceName.defaultName())) - .submit() - .deploy(); + .submit() + .deploy(); tester.assertResponse(request("/application/v4/tenant/scoober", GET).roles(Role.reader(tenantName)), (response) -> assertFalse(response.getBodyAsString().contains("archiveAccessRole")), 200); tester.assertResponse(request("/application/v4/tenant/scoober/archive-access", PUT) - .data("{\"role\":\"dummy\"}").roles(Role.administrator(tenantName)), + .data("{\"role\":\"dummy\"}").roles(Role.administrator(tenantName)), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid archive access role 'dummy': Must match expected pattern: 'arn:aws:iam::\\\\d{12}:.+'\"}", 400); tester.assertResponse(request("/application/v4/tenant/scoober/archive-access/aws", PUT) @@ -411,7 +411,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { } @Test - public void create_application_on_deploy() { + void create_application_on_deploy() { var application = ApplicationName.from("unique"); var applicationPackage = new ApplicationPackageBuilder().withoutAthenzIdentity().build(); @@ -420,15 +420,15 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { tester.assertResponse( request("/application/v4/tenant/scoober/application/unique/instance/default/deploy/dev-aws-us-east-1c", POST) - .data(createApplicationDeployData(Optional.of(applicationPackage), Optional.empty(), true)) - .roles(Set.of(Role.developer(tenantName))), + .data(createApplicationDeployData(Optional.of(applicationPackage), Optional.empty(), true)) + .roles(Set.of(Role.developer(tenantName))), "{\"message\":\"Deployment started in run 1 of dev-aws-us-east-1c for scoober.unique. This may take about 15 minutes the first time.\",\"run\":1}"); assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isPresent()); } @Test - public void create_application_on_submit() { + void create_application_on_submit() { var application = ApplicationName.from("unique"); var applicationPackage = new ApplicationPackageBuilder() .trustDefaultCertificate() 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 2c0ab97c00e..e3292700432 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 @@ -70,8 +70,8 @@ import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; import com.yahoo.yolean.Exceptions; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import javax.security.auth.x500.X500Principal; import java.io.File; @@ -100,9 +100,9 @@ import static com.yahoo.application.container.handler.Request.Method.PUT; import static java.net.URLEncoder.encode; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.joining; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author bratseth @@ -153,7 +153,7 @@ public class ApplicationApiTest extends ControllerContainerTest { private ContainerTester tester; private DeploymentTester deploymentTester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); deploymentTester = new DeploymentTester(new ControllerTester(tester)); @@ -161,24 +161,24 @@ public class ApplicationApiTest extends ControllerContainerTest { } @Test - public void testApplicationApi() { + void testApplicationApi() { createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); // (Necessary but not provided in this API) // GET API root tester.assertResponse(request("/application/v4/", GET).userIdentity(USER_ID), - new File("root.json")); + new File("root.json")); // POST (add) a tenant without property ID tester.assertResponse(request("/application/v4/tenant/tenant1", POST) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS), - new File("tenant-without-applications.json")); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS), + new File("tenant-without-applications.json")); // PUT (modify) a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), - new File("tenant-without-applications.json")); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), + new File("tenant-without-applications.json")); // Add another Athens domain, so we can try to create more tenants createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN_2, USER_ID); // New domain to test tenant w/property ID @@ -187,59 +187,59 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (add) a tenant with property ID tester.assertResponse(request("/application/v4/tenant/tenant2", POST) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS) - .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), - new File("tenant-without-applications-with-id.json")); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS) + .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), + new File("tenant-without-applications-with-id.json")); // PUT (modify) a tenant with property ID tester.assertResponse(request("/application/v4/tenant/tenant2", PUT) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS) - .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), - new File("tenant-without-applications-with-id.json")); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS) + .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), + new File("tenant-without-applications-with-id.json")); // GET a tenant with property ID and contact information updateContactInformation(); tester.controller().tenants().updateLastLogin(TenantName.from("tenant2"), List.of(LastLoginInfo.UserLevel.user, LastLoginInfo.UserLevel.administrator), Instant.ofEpochMilli(1234)); tester.assertResponse(request("/application/v4/tenant/tenant2", GET).userIdentity(USER_ID), - new File("tenant2.json")); + new File("tenant2.json")); // POST (create) an application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("instance-reference.json")); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("instance-reference.json")); // GET a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID), - new File("tenant-with-application.json")); + new File("tenant-with-application.json")); tester.assertResponse(request("/application/v4/tenant/tenant1", GET) - .userIdentity(USER_ID) - .properties(Map.of("activeInstances", "true")), - new File("tenant-without-applications.json")); + .userIdentity(USER_ID) + .properties(Map.of("activeInstances", "true")), + new File("tenant-without-applications.json")); // GET tenant applications tester.assertResponse(request("/application/v4/tenant/tenant1/application/", GET).userIdentity(USER_ID), - new File("application-list.json")); + new File("application-list.json")); // GET tenant application instances for application that does not exist tester.assertResponse(request("/application/v4/tenant/tenant1/application/fake-app/instance/", GET).userIdentity(USER_ID), "{\"error-code\":\"NOT_FOUND\",\"message\":\"Application 'fake-app' does not exist\"}", 404); // GET tenant applications (instances of "application1" only) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/", GET).userIdentity(USER_ID), - new File("application-list.json")); + new File("application-list.json")); // GET at a tenant, with "&recursive=true&production=true", recurses over no instances yet, as they are not in deployment spec. tester.assertResponse(request("/application/v4/tenant/tenant1/", GET) - .userIdentity(USER_ID) - .properties(Map.of("recursive", "true", - "production", "true")), - new File("tenant-with-empty-application.json")); + .userIdentity(USER_ID) + .properties(Map.of("recursive", "true", + "production", "true")), + new File("tenant-with-empty-application.json")); // GET at an application, with "&recursive=true&production=true", recurses over no instances yet, as they are not in deployment spec. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", GET) - .userIdentity(USER_ID) - .properties(Map.of("recursive", "true", - "production", "true")), - new File("application-without-instances.json")); + .userIdentity(USER_ID) + .properties(Map.of("recursive", "true", + "production", "true")), + new File("application-without-instances.json")); addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR)); @@ -249,87 +249,87 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (deploy) an application to start a manual deployment in prod is not allowed MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/production-us-east-3/", POST) - .data(entity) - .userIdentity(USER_ID), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Direct deployments are only allowed to manually deployed environments.\"}", 400); + .data(entity) + .userIdentity(USER_ID), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Direct deployments are only allowed to manually deployed environments.\"}", 400); // POST (deploy) an application to start a manual deployment in prod is allowed for operators tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/production-us-east-3/", POST) - .data(entity) - .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"message\":\"Deployment started in run 1 of production-us-east-3 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}"); + .data(entity) + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"message\":\"Deployment started in run 1 of production-us-east-3 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}"); app1.runJob(DeploymentContext.productionUsEast3); tester.controller().applications().deactivate(app1.instanceId(), ZoneId.from("prod", "us-east-3")); // POST (deploy) an application to start a manual deployment to dev tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/dev-us-east-1/", POST) - .data(entity) - .userIdentity(USER_ID), - "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}"); + .data(entity) + .userIdentity(USER_ID), + "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}"); app1.runJob(DeploymentContext.devUsEast1); // POST (deploy) a job to restart a manual deployment to dev tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/dev-us-east-1", POST) - .userIdentity(USER_ID), - "{\"message\":\"Triggered dev-us-east-1 for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Triggered dev-us-east-1 for tenant1.application1.instance1\"}"); app1.runJob(DeploymentContext.devUsEast1); // GET dev application package tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/dev-us-east-1/package", GET) - .userIdentity(USER_ID), - (response) -> { - assertEquals("attachment; filename=\"tenant1.application1.instance1.dev.us-east-1.zip\"", response.getHeaders().getFirst("Content-Disposition")); - assertArrayEquals(applicationPackageInstance1.zippedContent(), response.getBody()); - }, - 200); + .userIdentity(USER_ID), + (response) -> { + assertEquals("attachment; filename=\"tenant1.application1.instance1.dev.us-east-1.zip\"", response.getHeaders().getFirst("Content-Disposition")); + assertArrayEquals(applicationPackageInstance1.zippedContent(), response.getBody()); + }, + 200); // POST an application package is not generally allowed under user instance tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/otheruser/deploy/dev-us-east-1", POST) - .userIdentity(OTHER_USER_ID) - .data(createApplicationDeployData(applicationPackageInstance1)), - accessDenied, - 403); + .userIdentity(OTHER_USER_ID) + .data(createApplicationDeployData(applicationPackageInstance1)), + accessDenied, + 403); // DELETE a dev deployment is not generally allowed under user instance tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/otheruser/environment/dev/region/us-east-1", DELETE) - .userIdentity(OTHER_USER_ID), - accessDenied, - 403); + .userIdentity(OTHER_USER_ID), + accessDenied, + 403); // When the user is a tenant admin, user instances are allowed. // POST an application package is not allowed under user instance for tenant admins tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser/deploy/dev-us-east-1", POST) - .userIdentity(USER_ID) - .data(createApplicationDeployData(applicationPackageInstance1)), - new File("deployment-job-accepted-2.json")); + .userIdentity(USER_ID) + .data(createApplicationDeployData(applicationPackageInstance1)), + new File("deployment-job-accepted-2.json")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser/job/dev-us-east-1/diff/1", GET).userIdentity(HOSTED_VESPA_OPERATOR), - (response) -> assertTrue(response.getBodyAsString(), - response.getBodyAsString().contains("--- schemas/test.sd\n" + + (response) -> assertTrue(response.getBodyAsString().contains("--- schemas/test.sd\n" + "@@ -1,0 +1,1 @@\n" + - "+ search test { }\n")), + "+ search test { }\n"), + response.getBodyAsString()), 200); // DELETE a dev deployment is allowed under user instance for tenant admins tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser/environment/dev/region/us-east-1", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Deactivated tenant1.application1.myuser in dev.us-east-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Deactivated tenant1.application1.myuser in dev.us-east-1\"}"); // DELETE a user instance tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted instance tenant1.application1.myuser\"}"); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted instance tenant1.application1.myuser\"}"); addScrewdriverUserToDeployRole(SCREWDRIVER_ID, - ATHENZ_TENANT_DOMAIN, - id.application()); + ATHENZ_TENANT_DOMAIN, + id.application()); // POST an application package and a test jar, submitting a new application for production deployment. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .data(createApplicationSubmissionData(applicationPackageInstance1, 123)), - "{\"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\"}"); + .screwdriverIdentity(SCREWDRIVER_ID) + .data(createApplicationSubmissionData(applicationPackageInstance1, 123)), + "{\"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(DeploymentContext.systemTest).runJob(DeploymentContext.stagingTest).runJob(DeploymentContext.productionUsCentral1); @@ -344,102 +344,102 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (create) another application tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/default", POST) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("instance-reference-2.json")); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("instance-reference-2.json")); ApplicationId id2 = ApplicationId.from("tenant2", "application2", "instance1"); var app2 = deploymentTester.newDeploymentContext(id2); addScrewdriverUserToDeployRole(SCREWDRIVER_ID, - ATHENZ_TENANT_DOMAIN_2, - id2.application()); + ATHENZ_TENANT_DOMAIN_2, + id2.application()); // POST an application package and a test jar, submitting a new application for production deployment. tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .data(createApplicationSubmissionData(applicationPackage, 1000)), - "{\"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\"}"); + .screwdriverIdentity(SCREWDRIVER_ID) + .data(createApplicationSubmissionData(applicationPackage, 1000)), + "{\"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(); // POST a triggering to force a production job to start without successful tests tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/instance1/job/production-us-west-1", POST) - .data("{ \"skipTests\": true, \"skipRevision\": true, \"skipUpgrade\": true }") - .userIdentity(USER_ID), - "{\"message\":\"Triggered production-us-west-1 for tenant2.application2.instance1, without revision and platform upgrade\"}"); + .data("{ \"skipTests\": true, \"skipRevision\": true, \"skipUpgrade\": true }") + .userIdentity(USER_ID), + "{\"message\":\"Triggered production-us-west-1 for tenant2.application2.instance1, without revision and platform upgrade\"}"); app2.runJob(DeploymentContext.productionUsWest1); // POST a re-triggering to force a production job to start with previous parameters tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/instance1/job/production-us-west-1", POST) - .data("{\"reTrigger\":true}") - .userIdentity(USER_ID), - "{\"message\":\"Triggered production-us-west-1 for tenant2.application2.instance1\"}"); + .data("{\"reTrigger\":true}") + .userIdentity(USER_ID), + "{\"message\":\"Triggered production-us-west-1 for tenant2.application2.instance1\"}"); // DELETE manually deployed prod deployment again tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/instance1/environment/prod/region/us-west-1", DELETE) - .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"message\":\"Deactivated tenant2.application2.instance1 in prod.us-west-1\"}"); + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"message\":\"Deactivated tenant2.application2.instance1 in prod.us-west-1\"}"); // GET application having both change and outstanding change tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", GET) - .screwdriverIdentity(SCREWDRIVER_ID), - new File("application2.json")); + .screwdriverIdentity(SCREWDRIVER_ID), + new File("application2.json")); // PATCH in a major version override tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", PATCH) - .userIdentity(USER_ID) - .data("{\"majorVersion\":7}"), - "{\"message\":\"Set major version to 7\"}"); + .userIdentity(USER_ID) + .data("{\"majorVersion\":7}"), + "{\"message\":\"Set major version to 7\"}"); // POST a pem deploy key tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/key", POST) - .userIdentity(USER_ID) - .data("{\"key\":\"" + pemPublicKey + "\"}"), - "{\"keys\":[\"-----BEGIN PUBLIC KEY-----\\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9\\nz/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw==\\n-----END PUBLIC KEY-----\\n\"]}"); + .userIdentity(USER_ID) + .data("{\"key\":\"" + pemPublicKey + "\"}"), + "{\"keys\":[\"-----BEGIN PUBLIC KEY-----\\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9\\nz/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw==\\n-----END PUBLIC KEY-----\\n\"]}"); // PATCH in a pem deploy key at deprecated path tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/default", PATCH) - .userIdentity(USER_ID) - .data("{\"pemDeployKey\":\"" + pemPublicKey + "\"}"), - "{\"message\":\"Added deploy key " + quotedPemPublicKey + "\"}"); + .userIdentity(USER_ID) + .data("{\"pemDeployKey\":\"" + pemPublicKey + "\"}"), + "{\"message\":\"Added deploy key " + quotedPemPublicKey + "\"}"); // GET an application with a major version override tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", GET) - .userIdentity(USER_ID), - new File("application2-with-patches.json")); + .userIdentity(USER_ID), + new File("application2-with-patches.json")); // PATCH in removal of the application major version override removal tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", PATCH) - .userIdentity(USER_ID) - .data("{\"majorVersion\":null}"), - "{\"message\":\"Set major version to empty\"}"); + .userIdentity(USER_ID) + .data("{\"majorVersion\":null}"), + "{\"message\":\"Set major version to empty\"}"); // GET compile version for an application tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/compile-version", GET) - .userIdentity(USER_ID), - "{\"compileVersion\":\"6.1.0\"}"); + .userIdentity(USER_ID), + "{\"compileVersion\":\"6.1.0\"}"); // DELETE the pem deploy key tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/key", DELETE) - .userIdentity(USER_ID) - .data("{\"key\":\"" + pemPublicKey + "\"}"), - "{\"keys\":[]}"); + .userIdentity(USER_ID) + .data("{\"key\":\"" + pemPublicKey + "\"}"), + "{\"keys\":[]}"); tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", GET) - .userIdentity(USER_ID), - new File("application2.json")); + .userIdentity(USER_ID), + new File("application2.json")); // DELETE instance 1 of 2 tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/default", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted instance tenant2.application2.default\"}"); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted instance tenant2.application2.default\"}"); // DELETE application with only one instance left tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted application tenant2.application2\"}"); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted application tenant2.application2\"}"); // Set version 6.1 to broken to change compile version for. deploymentTester.upgrader().overrideConfidence(Version.fromString("6.1"), VespaVersion.Confidence.broken); @@ -448,44 +448,44 @@ public class ApplicationApiTest extends ControllerContainerTest { // GET tenant application deployments tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", GET) - .userIdentity(USER_ID), - new File("instance.json")); + .userIdentity(USER_ID), + new File("instance.json")); // GET an application deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1", GET) - .userIdentity(USER_ID), - new File("deployment.json")); + .userIdentity(USER_ID), + new File("deployment.json")); addIssues(deploymentTester, TenantAndApplicationId.from("tenant1", "application1")); // GET at root, with "&recursive=deployment", returns info about all tenants, their applications and their deployments tester.assertResponse(request("/application/v4/", GET) - .userIdentity(USER_ID) - .recursive("deployment"), - new File("recursive-root.json")); + .userIdentity(USER_ID) + .recursive("deployment"), + new File("recursive-root.json")); // GET at root, with "&recursive=tenant", returns info about all tenants, with limited info about their applications. tester.assertResponse(request("/application/v4/", GET) - .userIdentity(USER_ID) - .recursive("tenant"), - new File("recursive-until-tenant-root.json")); + .userIdentity(USER_ID) + .recursive("tenant"), + new File("recursive-until-tenant-root.json")); // GET at a tenant, with "&recursive=true", returns full info about their applications and their deployments tester.assertResponse(request("/application/v4/tenant/tenant1/", GET) - .userIdentity(USER_ID) - .recursive("true"), - new File("tenant1-recursive.json")); + .userIdentity(USER_ID) + .recursive("true"), + new File("tenant1-recursive.json")); // GET at an application, with "&recursive=true", returns full info about its deployments tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", GET) - .userIdentity(USER_ID) - .recursive("true"), - new File("instance1-recursive.json")); + .userIdentity(USER_ID) + .recursive("true"), + new File("instance1-recursive.json")); // GET nodes tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/nodes", GET) - .userIdentity(USER_ID), - new File("application-nodes.json")); + .userIdentity(USER_ID), + new File("application-nodes.json")); // GET clusters tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/clusters", GET) - .userIdentity(USER_ID), - new File("application-clusters.json")); + .userIdentity(USER_ID), + new File("application-clusters.json")); // GET logs tester.assertResponse(request("/application/v4/tenant/tenant2/application/application1/environment/dev/region/us-east-1/instance/default/logs?from=1233&to=3214", GET) @@ -494,12 +494,12 @@ public class ApplicationApiTest extends ControllerContainerTest { // GET controller logs tester.assertResponse(request("/application/v4/tenant/tenant2/application/application1/environment/prod/region/controller/instance/default/logs?from=1233&to=3214", GET) - .userIdentity(USER_ID), - "INFO - All good"); + .userIdentity(USER_ID), + "INFO - All good"); // Get content/../foo tester.assertResponse(request("/application/v4/tenant/tenant2/application/application1/instance/default/environment/dev/region/us-east-1/content/%2E%2E%2Ffoo", GET).userIdentity(USER_ID), - accessDenied, 403); + accessDenied, 403); // Get content - root tester.assertResponse(request("/application/v4/tenant/tenant2/application/application1/instance/default/environment/dev/region/us-east-1/content/", GET).userIdentity(USER_ID), "{\"path\":\"/\"}"); @@ -513,121 +513,121 @@ public class ApplicationApiTest extends ControllerContainerTest { // GET metrics tester.assertJsonResponse(request("/application/v4/tenant/tenant2/application/application1/environment/dev/region/us-east-1/instance/default/metrics", GET) .userIdentity(USER_ID), - new File("proton-metrics.json")); + new File("proton-metrics.json")); // 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 revision change to build 1 for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"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 revision change to build 1 for tenant1.application1.instance1\"}"); + .data("{ \"build\": 1 }") + .userIdentity(USER_ID), + "{\"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 'revision change to build 1' to 'no change' for tenant1.application1.instance1\"}"); + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"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) - .userIdentity(USER_ID) - .data("{\"cancel\":\"all\"}"), - "{\"message\":\"No deployment in progress for tenant1.application1.instance1 at this time\"}"); + .userIdentity(USER_ID) + .data("{\"cancel\":\"all\"}"), + "{\"message\":\"No deployment in progress for tenant1.application1.instance1 at this time\"}"); // POST pinning to a given version to an application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin", POST) - .userIdentity(USER_ID) - .data("6.1.0"), - "{\"message\":\"Triggered pin to 6.1 for tenant1.application1.instance1\"}"); - assertTrue("Action is logged to audit log", - tester.controller().auditLogger().readLog().entries().stream() - .anyMatch(entry -> entry.resource().equals("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin?"))); + .userIdentity(USER_ID) + .data("6.1.0"), + "{\"message\":\"Triggered pin to 6.1 for tenant1.application1.instance1\"}"); + assertTrue(tester.controller().auditLogger().readLog().entries().stream() + .anyMatch(entry -> entry.resource().equals("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin?")), + "Action is logged to audit log"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", GET) - .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":true}"); + .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":true}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin", GET) - .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":true}"); + .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":true}"); // DELETE only the pin to a given version tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Changed deployment from 'pin to 6.1' to 'upgrade to 6.1' for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Changed deployment from 'pin to 6.1' to 'upgrade to 6.1' for tenant1.application1.instance1\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", GET) - .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":false}"); + .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":false}"); // POST pinning again tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin", POST) - .userIdentity(USER_ID) - .data("6.1"), - "{\"message\":\"Triggered pin to 6.1 for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID) + .data("6.1"), + "{\"message\":\"Triggered pin to 6.1 for tenant1.application1.instance1\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", GET) - .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":true}"); + .userIdentity(USER_ID), "{\"platform\":\"6.1\",\"pinned\":true}"); // DELETE only the version, but leave the pin tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/platform", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Changed deployment from 'pin to 6.1' to 'pin to current platform' for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Changed deployment from 'pin to 6.1' to 'pin to current platform' for tenant1.application1.instance1\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", GET) - .userIdentity(USER_ID), "{\"pinned\":true}"); + .userIdentity(USER_ID), "{\"pinned\":true}"); // DELETE also the pin to a given version tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying/pin", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Changed deployment from 'pin to current platform' to 'no change' for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Changed deployment from 'pin to current platform' to 'no change' for tenant1.application1.instance1\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploying", GET) - .userIdentity(USER_ID), "{}"); + .userIdentity(USER_ID), "{}"); // POST a pause to a production job tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-west-1/pause", POST) - .userIdentity(USER_ID), - "{\"message\":\"production-us-west-1 for tenant1.application1.instance1 paused for " + DeploymentTrigger.maxPause + "\"}"); + .userIdentity(USER_ID), + "{\"message\":\"production-us-west-1 for tenant1.application1.instance1 paused for " + DeploymentTrigger.maxPause + "\"}"); // DELETE a pause of a production job tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-west-1/pause", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"production-us-west-1 for tenant1.application1.instance1 resumed\"}"); + .userIdentity(USER_ID), + "{\"message\":\"production-us-west-1 for tenant1.application1.instance1 resumed\"}"); // POST a triggering to the same production job tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-west-1", POST) - .userIdentity(USER_ID), - "{\"message\":\"Triggered production-us-west-1 for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Triggered production-us-west-1 for tenant1.application1.instance1\"}"); // POST a 'reindex application' command tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindex", POST) - .properties(Map.of("indexedOnly", "true", - "speed", "10")) - .userIdentity(USER_ID), - "{\"message\":\"Requested reindexing of tenant1.application1.instance1 in prod.us-central-1, for indexed types, with speed 10.0\"}"); + .properties(Map.of("indexedOnly", "true", + "speed", "10")) + .userIdentity(USER_ID), + "{\"message\":\"Requested reindexing of tenant1.application1.instance1 in prod.us-central-1, for indexed types, with speed 10.0\"}"); // POST a 'reindex application' command with cluster filter tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindex", POST) - .properties(Map.of("clusterId", "boo,moo")) - .userIdentity(USER_ID), - "{\"message\":\"Requested reindexing of tenant1.application1.instance1 in prod.us-central-1, on clusters boo, moo\"}"); + .properties(Map.of("clusterId", "boo,moo")) + .userIdentity(USER_ID), + "{\"message\":\"Requested reindexing of tenant1.application1.instance1 in prod.us-central-1, on clusters boo, moo\"}"); // POST a 'reindex application' command with cluster and document type filters tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindex", POST) - .properties(Map.of("clusterId", "boo,moo", - "documentType", "foo,boo")) - .userIdentity(USER_ID), - "{\"message\":\"Requested reindexing of tenant1.application1.instance1 in prod.us-central-1, on clusters boo, moo, for types foo, boo\"}"); + .properties(Map.of("clusterId", "boo,moo", + "documentType", "foo,boo")) + .userIdentity(USER_ID), + "{\"message\":\"Requested reindexing of tenant1.application1.instance1 in prod.us-central-1, on clusters boo, moo, for types foo, boo\"}"); // POST to enable reindexing tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindexing", POST) - .userIdentity(USER_ID), - "{\"message\":\"Enabled reindexing of tenant1.application1.instance1 in prod.us-central-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Enabled reindexing of tenant1.application1.instance1 in prod.us-central-1\"}"); // DELETE to disable reindexing tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindexing", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Disabled reindexing of tenant1.application1.instance1 in prod.us-central-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Disabled reindexing of tenant1.application1.instance1 in prod.us-central-1\"}"); // GET to get reindexing status tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/reindexing", GET) - .userIdentity(USER_ID), - "{\"enabled\":true,\"clusters\":[{\"name\":\"cluster\",\"pending\":[{\"type\":\"type\",\"requiredGeneration\":100}],\"ready\":[{\"type\":\"type\",\"readyAtMillis\":345,\"startedAtMillis\":456,\"endedAtMillis\":567,\"state\":\"failed\",\"message\":\"(#`д´)ノ\",\"progress\":0.1,\"speed\":1.0}]}]}"); + .userIdentity(USER_ID), + "{\"enabled\":true,\"clusters\":[{\"name\":\"cluster\",\"pending\":[{\"type\":\"type\",\"requiredGeneration\":100}],\"ready\":[{\"type\":\"type\",\"readyAtMillis\":345,\"startedAtMillis\":456,\"endedAtMillis\":567,\"state\":\"failed\",\"message\":\"(#`д´)ノ\",\"progress\":0.1,\"speed\":1.0}]}]}"); // POST to request a service dump tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/node/host-tenant1.application1.instance1-prod.us-central-1/service-dump", POST) @@ -643,112 +643,122 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST a 'restart application' command tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/restart", POST) - .userIdentity(USER_ID), - "{\"message\":\"Requested restart of tenant1.application1.instance1 in prod.us-central-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Requested restart of tenant1.application1.instance1 in prod.us-central-1\"}"); // POST a 'restart application' command tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/restart", POST) - .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"message\":\"Requested restart of tenant1.application1.instance1 in prod.us-central-1\"}"); + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"message\":\"Requested restart of tenant1.application1.instance1 in prod.us-central-1\"}"); addUserToHostedOperatorRole(HostedAthenzIdentities.from(SCREWDRIVER_ID)); // POST a 'restart application' in staging environment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/instance1/restart", POST) - .screwdriverIdentity(SCREWDRIVER_ID), - "{\"message\":\"Requested restart of tenant1.application1.instance1 in staging.us-east-3\"}"); + .screwdriverIdentity(SCREWDRIVER_ID), + "{\"message\":\"Requested restart of tenant1.application1.instance1 in staging.us-east-3\"}"); // POST a 'restart application' in test environment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/instance1/restart", POST) - .screwdriverIdentity(SCREWDRIVER_ID), - "{\"message\":\"Requested restart of tenant1.application1.instance1 in test.us-east-1\"}"); + .screwdriverIdentity(SCREWDRIVER_ID), + "{\"message\":\"Requested restart of tenant1.application1.instance1 in test.us-east-1\"}"); // POST a 'restart application' in dev environment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/dev/region/us-east-1/instance/instance1/restart", POST) - .userIdentity(USER_ID), - "{\"message\":\"Requested restart of tenant1.application1.instance1 in dev.us-east-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Requested restart of tenant1.application1.instance1 in dev.us-east-1\"}"); // POST a 'restart application' command with a host filter (other filters not supported yet) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/restart", POST) - .properties(Map.of("hostname", "node-1-tenant-host-prod.us-central-1")) - .screwdriverIdentity(SCREWDRIVER_ID), - "{\"message\":\"Requested restart of tenant1.application1.instance1 in prod.us-central-1\"}", 200); + .properties(Map.of("hostname", "node-1-tenant-host-prod.us-central-1")) + .screwdriverIdentity(SCREWDRIVER_ID), + "{\"message\":\"Requested restart of tenant1.application1.instance1 in prod.us-central-1\"}", 200); // POST a 'suspend application' in dev environment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/dev/region/us-east-1/suspend", POST) - .userIdentity(USER_ID), - "{\"message\":\"Suspended orchestration of tenant1.application1.instance1 in dev.us-east-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Suspended orchestration of tenant1.application1.instance1 in dev.us-east-1\"}"); // POST a 'resume application' in dev environment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/dev/region/us-east-1/suspend", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Resumed orchestration of tenant1.application1.instance1 in dev.us-east-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Resumed orchestration of tenant1.application1.instance1 in dev.us-east-1\"}"); // POST a 'suspend application' in prod environment fails tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-east-3/suspend", POST) - .userIdentity(USER_ID), - accessDenied, 403); + .userIdentity(USER_ID), + accessDenied, 403); // GET suspended tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/suspended", GET) - .userIdentity(USER_ID), - new File("suspended.json")); + .userIdentity(USER_ID), + new File("suspended.json")); // GET service/state/v1 tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/service/storagenode/host.com/state/v1/?foo=bar", GET) - .userIdentity(USER_ID), - new File("service")); + .userIdentity(USER_ID), + new File("service")); // GET orchestrator tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/orchestrator", GET) - .userIdentity(USER_ID), - "{\"json\":\"thank you very much\"}"); + .userIdentity(USER_ID), + "{\"json\":\"thank you very much\"}"); + + // GET application package which has been deployed to production + tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET) + .properties(Map.of("build", "latestDeployed")) + .userIdentity(HOSTED_VESPA_OPERATOR), + (response) -> { + assertEquals("attachment; filename=\"tenant1.application1-build1.zip\"", response.getHeaders().getFirst("Content-Disposition")); + assertArrayEquals(applicationPackageInstance1.zippedContent(), response.getBody()); + }, + 200); // DELETE application with active deployments fails tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("delete-with-active-deployments.json"), 400); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("delete-with-active-deployments.json"), 400); // DELETE (deactivate) a deployment - dev tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/dev/region/us-east-1/instance/instance1", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Deactivated tenant1.application1.instance1 in dev.us-east-1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Deactivated tenant1.application1.instance1 in dev.us-east-1\"}"); // DELETE (deactivate) a deployment - prod tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1", DELETE) - .screwdriverIdentity(SCREWDRIVER_ID), - "{\"message\":\"Deactivated tenant1.application1.instance1 in prod.us-central-1\"}"); + .screwdriverIdentity(SCREWDRIVER_ID), + "{\"message\":\"Deactivated tenant1.application1.instance1 in prod.us-central-1\"}"); // DELETE (deactivate) a deployment is idempotent tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1", DELETE) - .screwdriverIdentity(SCREWDRIVER_ID), - "{\"message\":\"Deactivated tenant1.application1.instance1 in prod.us-central-1\"}"); + .screwdriverIdentity(SCREWDRIVER_ID), + "{\"message\":\"Deactivated tenant1.application1.instance1 in prod.us-central-1\"}"); // Setup for test config tests tester.controller().jobController().deploy(ApplicationId.from("tenant1", "application1", "default"), - DeploymentContext.productionUsCentral1, - Optional.empty(), - applicationPackageDefault); + DeploymentContext.productionUsCentral1, + Optional.empty(), + applicationPackageDefault); tester.controller().jobController().deploy(ApplicationId.from("tenant1", "application1", "my-user"), - DeploymentContext.devUsEast1, - Optional.empty(), - applicationPackageDefault); + DeploymentContext.devUsEast1, + Optional.empty(), + applicationPackageDefault); // GET test-config for local tests against a dev deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/my-user/job/dev-us-east-1/test-config", GET) - .userIdentity(USER_ID), - new File("test-config-dev.json")); + .userIdentity(USER_ID), + new File("test-config-dev.json")); // GET test-config for local tests against a prod deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/my-user/job/production-us-central-1/test-config", GET) - .userIdentity(USER_ID), - new File("test-config.json")); + .userIdentity(USER_ID), + new File("test-config.json")); tester.controller().applications().deactivate(ApplicationId.from("tenant1", "application1", "default"), - ZoneId.from("prod", "us-central-1")); + ZoneId.from("prod", "us-central-1")); tester.controller().applications().deactivate(ApplicationId.from("tenant1", "application1", "my-user"), - ZoneId.from("dev", "us-east-1")); + ZoneId.from("dev", "us-east-1")); // teardown for test config tests // Second attempt has a service under a different domain than the tenant of the application, and fails. @@ -759,9 +769,9 @@ public class ApplicationApiTest extends ControllerContainerTest { .build(); allowLaunchOfService(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN_2, "service")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .data(createApplicationSubmissionData(packageWithServiceForWrongDomain, 123)), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz domain in deployment.xml: [domain2] must match tenant domain: [domain1]\"}", 400); + .screwdriverIdentity(SCREWDRIVER_ID) + .data(createApplicationSubmissionData(packageWithServiceForWrongDomain, 123)), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz domain in deployment.xml: [domain2] must match tenant domain: [domain1]\"}", 400); // Third attempt has a service under the domain of the tenant, and also succeeds. ApplicationPackage packageWithService = new ApplicationPackageBuilder() @@ -773,54 +783,54 @@ public class ApplicationApiTest extends ControllerContainerTest { .build(); allowLaunchOfService(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .data(createApplicationSubmissionData(packageWithService, 123)), - "{\"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\"}"); + .screwdriverIdentity(SCREWDRIVER_ID) + .data(createApplicationSubmissionData(packageWithService, 123)), + "{\"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(), - response.getBodyAsString().contains("+ <deployment version='1.0' athenz-domain='domain1' athenz-service='service'>\n" + - "- <deployment version='1.0' >\n")), - 200); + (response) -> assertTrue(response.getBodyAsString().contains("+ <deployment version='1.0' athenz-domain='domain1' athenz-service='service'>\n" + + "- <deployment version='1.0' >\n"), + response.getBodyAsString()), + 200); // GET last submitted application package tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET).userIdentity(HOSTED_VESPA_OPERATOR), - (response) -> { - assertEquals("attachment; filename=\"tenant1.application1-build2.zip\"", response.getHeaders().getFirst("Content-Disposition")); - assertArrayEquals(packageWithService.zippedContent(), response.getBody()); - }, - 200); + (response) -> { + assertEquals("attachment; filename=\"tenant1.application1-build2.zip\"", response.getHeaders().getFirst("Content-Disposition")); + assertArrayEquals(packageWithService.zippedContent(), response.getBody()); + }, + 200); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET).userIdentity(HOSTED_VESPA_OPERATOR).properties(Map.of("tests", "true")), - (response) -> { - assertEquals("attachment; filename=\"tenant1.application1-tests2.zip\"", response.getHeaders().getFirst("Content-Disposition")); - assertArrayEquals("content".getBytes(UTF_8), response.getBody()); - }, - 200); + (response) -> { + assertEquals("attachment; filename=\"tenant1.application1-tests2.zip\"", response.getHeaders().getFirst("Content-Disposition")); + assertArrayEquals("content".getBytes(UTF_8), response.getBody()); + }, + 200); // GET application package for specific build tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/package", GET) - .properties(Map.of("build", "2")) - .userIdentity(HOSTED_VESPA_OPERATOR), - (response) -> { - assertEquals("attachment; filename=\"tenant1.application1-build2.zip\"", response.getHeaders().getFirst("Content-Disposition")); - assertArrayEquals(packageWithService.zippedContent(), response.getBody()); - }, - 200); + .properties(Map.of("build", "2")) + .userIdentity(HOSTED_VESPA_OPERATOR), + (response) -> { + assertEquals("attachment; filename=\"tenant1.application1-build2.zip\"", response.getHeaders().getFirst("Content-Disposition")); + assertArrayEquals(packageWithService.zippedContent(), response.getBody()); + }, + 200); // Fourth attempt has a wrong content hash in a header, and fails. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .header("X-Content-Hash", "not/the/right/hash") - .data(createApplicationSubmissionData(packageWithService, 123)), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Value of X-Content-Hash header does not match computed content hash\"}", 400); + .screwdriverIdentity(SCREWDRIVER_ID) + .header("X-Content-Hash", "not/the/right/hash") + .data(createApplicationSubmissionData(packageWithService, 123)), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Value of X-Content-Hash header does not match computed content hash\"}", 400); // Fifth attempt has the right content hash in a header, and succeeds. MultiPartStreamer streamer = createApplicationSubmissionData(packageWithService, 123); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .header("X-Content-Hash", Base64.getEncoder().encodeToString(Signatures.sha256Digest(streamer::data))) - .data(streamer), - "{\"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\"}"); + .screwdriverIdentity(SCREWDRIVER_ID) + .header("X-Content-Hash", Base64.getEncoder().encodeToString(Signatures.sha256Digest(streamer::data))) + .data(streamer), + "{\"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,53 +841,53 @@ public class ApplicationApiTest extends ControllerContainerTest { .endpoint("default", "foo", "us-central-1", "us-west-1", "us-east-3") .build(); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST) - .screwdriverIdentity(SCREWDRIVER_ID) - .data(createApplicationSubmissionData(multiInstanceSpec, 123)), - "{\"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\"}"); + .screwdriverIdentity(SCREWDRIVER_ID) + .data(createApplicationSubmissionData(multiInstanceSpec, 123)), + "{\"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 tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit/2", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Marked build '2' as non-deployable\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Marked build '2' as non-deployable\"}"); // GET deployment job overview, after triggering system and staging test jobs. assertEquals(2, tester.controller().applications().deploymentTrigger().triggerReadyJobs().triggered()); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job", GET) - .userIdentity(USER_ID), - new File("jobs.json")); + .userIdentity(USER_ID), + new File("jobs.json")); // GET deployment job overview for whole application. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/deployment", GET) - .userIdentity(USER_ID), - new File("deployment-overview.json")); + .userIdentity(USER_ID), + new File("deployment-overview.json")); // GET system test job overview. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/system-test", GET) - .userIdentity(USER_ID), - new File("system-test-job.json")); + .userIdentity(USER_ID), + new File("system-test-job.json")); // GET system test run 1 details. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/system-test/run/1", GET) - .userIdentity(USER_ID), - new File("system-test-details.json")); + .userIdentity(USER_ID), + new File("system-test-details.json")); // DELETE a running job to have it aborted. tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/job/staging-test", DELETE) - .userIdentity(USER_ID), - "{\"message\":\"Aborting run 2 of staging-test for tenant1.application1.instance1\"}"); + .userIdentity(USER_ID), + "{\"message\":\"Aborting run 2 of staging-test for tenant1.application1.instance1\"}"); // GET compile version for specific major deploymentTester.controllerTester().upgradeSystem(Version.fromString("7.0")); deploymentTester.controllerTester().flagSource().withListFlag(PermanentFlags.INCOMPATIBLE_VERSIONS.id(), List.of("*"), String.class); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/compile-version", GET) - .userIdentity(USER_ID).properties(Map.of("allowMajor", "7")), - "{\"compileVersion\":\"7.0.0\"}"); + .userIdentity(USER_ID).properties(Map.of("allowMajor", "7")), + "{\"compileVersion\":\"7.0.0\"}"); // OPTIONS return 200 OK tester.assertResponse(request("/application/v4/", Request.Method.OPTIONS) - .userIdentity(USER_ID), - ""); + .userIdentity(USER_ID), + ""); addNotifications(TenantName.from("tenant1")); addNotifications(TenantName.from("tenant2")); @@ -892,14 +902,14 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE the application which no longer has any deployments tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted application tenant1.application1\"}"); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted application tenant1.application1\"}"); // DELETE an empty tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted tenant tenant1\"}"); + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted tenant tenant1\"}"); // The tenant is not found tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID) @@ -937,7 +947,7 @@ public class ApplicationApiTest extends ControllerContainerTest { } @Test - public void testRotationOverride() { + void testRotationOverride() { // Setup createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); var westZone = ZoneId.from("prod", "us-west-1"); @@ -955,60 +965,60 @@ public class ApplicationApiTest extends ControllerContainerTest { // Invalid application fails tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/environment/prod/region/us-west-1/instance/default/global-rotation", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"tenant2.application2 not found\"}", - 400); + .userIdentity(USER_ID), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"tenant2.application2 not found\"}", + 400); // Invalid deployment fails tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/global-rotation", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"application 'tenant1.application1.instance1' has no deployment in prod.us-central-1\"}", - 404); + .userIdentity(USER_ID), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"application 'tenant1.application1.instance1' has no deployment in prod.us-central-1\"}", + 404); // Change status of non-existing deployment fails tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/global-rotation/override", PUT) - .userIdentity(USER_ID) - .data("{\"reason\":\"unit-test\"}"), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"application 'tenant1.application1.instance1' has no deployment in prod.us-central-1\"}", - 404); + .userIdentity(USER_ID) + .data("{\"reason\":\"unit-test\"}"), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"application 'tenant1.application1.instance1' has no deployment in prod.us-central-1\"}", + 404); // GET global rotation status tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation", GET) - .userIdentity(USER_ID), - new File("global-rotation.json")); + .userIdentity(USER_ID), + new File("global-rotation.json")); // GET global rotation override status tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation/override", GET) - .userIdentity(USER_ID), - new File("global-rotation-get.json")); + .userIdentity(USER_ID), + new File("global-rotation-get.json")); // SET global rotation override status tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation/override", PUT) - .userIdentity(USER_ID) - .data("{\"reason\":\"unit-test\"}"), - new File("global-rotation-put.json")); + .userIdentity(USER_ID) + .data("{\"reason\":\"unit-test\"}"), + new File("global-rotation-put.json")); // Status of routing policy is changed assertGlobalRouting(app.deploymentIdIn(westZone), RoutingStatus.Value.out, RoutingStatus.Agent.tenant); // DELETE global rotation override status tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation/override", DELETE) - .userIdentity(USER_ID) - .data("{\"reason\":\"unit-test\"}"), - new File("global-rotation-delete.json")); + .userIdentity(USER_ID) + .data("{\"reason\":\"unit-test\"}"), + new File("global-rotation-delete.json")); assertGlobalRouting(app.deploymentIdIn(westZone), RoutingStatus.Value.in, RoutingStatus.Agent.tenant); // SET global rotation override status by operator addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR)); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation/override", PUT) - .userIdentity(HOSTED_VESPA_OPERATOR) - .data("{\"reason\":\"unit-test\"}"), - new File("global-rotation-put.json")); + .userIdentity(HOSTED_VESPA_OPERATOR) + .data("{\"reason\":\"unit-test\"}"), + new File("global-rotation-put.json")); assertGlobalRouting(app.deploymentIdIn(westZone), RoutingStatus.Value.out, RoutingStatus.Agent.operator); } @Test - public void multiple_endpoints() { + void multiple_endpoints() { // Setup createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() @@ -1026,34 +1036,34 @@ public class ApplicationApiTest extends ControllerContainerTest { // GET global rotation status without specifying endpointId fails tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"application 'tenant1.application1.instance1' has multiple rotations. Query parameter 'endpointId' must be given\"}", - 400); + .userIdentity(USER_ID), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"application 'tenant1.application1.instance1' has multiple rotations. Query parameter 'endpointId' must be given\"}", + 400); // GET global rotation status for us-west-1 in default endpoint tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation", GET) - .properties(Map.of("endpointId", "default")) - .userIdentity(USER_ID), - "{\"bcpStatus\":{\"rotationStatus\":\"UNKNOWN\"}}", - 200); + .properties(Map.of("endpointId", "default")) + .userIdentity(USER_ID), + "{\"bcpStatus\":{\"rotationStatus\":\"UNKNOWN\"}}", + 200); // GET global rotation status for us-west-1 in eu endpoint tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/global-rotation", GET) - .properties(Map.of("endpointId", "eu")) - .userIdentity(USER_ID), - "{\"bcpStatus\":{\"rotationStatus\":\"UNKNOWN\"}}", - 200); + .properties(Map.of("endpointId", "eu")) + .userIdentity(USER_ID), + "{\"bcpStatus\":{\"rotationStatus\":\"UNKNOWN\"}}", + 200); // GET global rotation status for eu-west-1 in eu endpoint tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/eu-west-1/global-rotation", GET) - .properties(Map.of("endpointId", "eu")) - .userIdentity(USER_ID), - "{\"bcpStatus\":{\"rotationStatus\":\"UNKNOWN\"}}", - 200); + .properties(Map.of("endpointId", "eu")) + .userIdentity(USER_ID), + "{\"bcpStatus\":{\"rotationStatus\":\"UNKNOWN\"}}", + 200); } @Test - public void testDeployWithApplicationPackage() { + void testDeployWithApplicationPackage() { // Setup addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR)); deploymentTester.controllerTester().upgradeController(new Version("6.2")); @@ -1061,22 +1071,22 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (deploy) a system application with an application package MultiPartStreamer noAppEntity = createApplicationDeployData(Optional.empty()); tester.assertResponse(request("/application/v4/tenant/hosted-vespa/application/routing/environment/prod/region/us-central-1/instance/default/deploy", POST) - .data(noAppEntity) - .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Deployment of system applications during a system upgrade is not allowed\"}", - 400); + .data(noAppEntity) + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Deployment of system applications during a system upgrade is not allowed\"}", + 400); deploymentTester.controllerTester() - .upgradeSystem(deploymentTester.controller().readVersionStatus().controllerVersion().get() - .versionNumber()); + .upgradeSystem(deploymentTester.controller().readVersionStatus().controllerVersion().get() + .versionNumber()); tester.assertResponse(request("/application/v4/tenant/hosted-vespa/application/routing/environment/prod/region/us-central-1/instance/default/deploy", POST) - .data(noAppEntity) - .userIdentity(HOSTED_VESPA_OPERATOR), - new File("deploy-result.json")); + .data(noAppEntity) + .userIdentity(HOSTED_VESPA_OPERATOR), + new File("deploy-result.json")); } @Test - public void testRemovingAllDeployments() { + void testRemovingAllDeployments() { createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .instances("instance1") @@ -1106,100 +1116,100 @@ public class ApplicationApiTest extends ControllerContainerTest { } @Test - public void testErrorResponses() { + void testErrorResponses() { createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); // PUT (update) non-existing tenant returns 403 as tenant access cannot be determined when the tenant does not exist tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), - accessDenied, - 403); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), + accessDenied, + 403); // GET non-existing tenant tester.assertResponse(request("/application/v4/tenant/tenant1", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'tenant1' does not exist\"}", - 404); + .userIdentity(USER_ID), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'tenant1' does not exist\"}", + 404); // GET non-existing tenant's applications tester.assertResponse(request("/application/v4/tenant/tenant1/application", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'tenant1' does not exist\"}", - 404); + .userIdentity(USER_ID), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'tenant1' does not exist\"}", + 404); // GET non-existing application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"tenant1.application1 not found\"}", - 404); + .userIdentity(USER_ID), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"tenant1.application1 not found\"}", + 404); // GET non-existing deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-east/instance/default", GET) - .userIdentity(USER_ID), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"tenant1.application1 not found\"}", - 404); + .userIdentity(USER_ID), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"tenant1.application1 not found\"}", + 404); // POST (add) a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", POST) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS), - new File("tenant-without-applications.json")); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS), + new File("tenant-without-applications.json")); // POST (add) another tenant under the same domain tester.assertResponse(request("/application/v4/tenant/tenant2", POST) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create tenant 'tenant2': The Athens domain 'domain1' is already connected to tenant 'tenant1'\"}", - 400); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create tenant 'tenant2': The Athens domain 'domain1' is already connected to tenant 'tenant1'\"}", + 400); // Add the same tenant again tester.assertResponse(request("/application/v4/tenant/tenant1", POST) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'tenant1' already exists\"}", - 400); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'tenant1' already exists\"}", + 400); // POST (add) an Athenz tenant with underscore in name tester.assertResponse(request("/application/v4/tenant/my_tenant_2", POST) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"New tenant or application names must start with a letter, may contain no more than 20 characters, and may only contain lowercase letters, digits or dashes, but no double-dashes.\"}", - 400); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"New tenant or application names must start with a letter, may contain no more than 20 characters, and may only contain lowercase letters, digits or dashes, but no double-dashes.\"}", + 400); // POST (add) an Athenz tenant with a reserved name tester.assertResponse(request("/application/v4/tenant/hosted-vespa", POST) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'hosted-vespa' already exists\"}", - 400); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'hosted-vespa' already exists\"}", + 400); // POST (create) an (empty) application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("instance-reference.json")); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("instance-reference.json")); // Create the same application again tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST) - .oAuthCredentials(OKTA_CREDENTIALS) - .userIdentity(USER_ID), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create 'tenant1.application1.instance1': Instance already exists\"}", - 400); + .oAuthCredentials(OKTA_CREDENTIALS) + .userIdentity(USER_ID), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create 'tenant1.application1.instance1': Instance already exists\"}", + 400); ConfigServerMock configServer = tester.serviceRegistry().configServerMock(); configServer.throwOnNextPrepare(new ConfigServerException(ConfigServerException.ErrorCode.INVALID_APPLICATION_PACKAGE, "Failed to prepare application", "Invalid application package")); // 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\"}", - 404); + "{\"error-code\":\"NOT_FOUND\",\"message\":\"no application package has been submitted for tenant1.application1\"}", + 404); // GET non-existent application package of specific build addScrewdriverUserToDeployRole(SCREWDRIVER_ID, ATHENZ_TENANT_DOMAIN, ApplicationName.from("application1")); @@ -1208,63 +1218,63 @@ public class ApplicationApiTest extends ControllerContainerTest { .data(createApplicationSubmissionData(applicationPackageInstance1, 1000)), "{\"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 build 42 found for tenant1.application1\"}", - 404); + .properties(Map.of("build", "42")) + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"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\"}"); // GET non-existent application package of invalid build 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 value for request parameter 'build': For input string: \\\"foobar\\\"\"}", - 400); - + .properties(Map.of("build", "foobar")) + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"invalid value for request parameter 'build': For input string: \\\"foobar\\\"\"}", + 400); + // POST (deploy) an application to legacy deploy path MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/dev/region/us-east-1/instance/instance1/deploy", POST) - .data(entity) - .userIdentity(USER_ID), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Deployment of tenant1.application1.instance1 is not supported through this API\"}", 400); + .data(entity) + .userIdentity(USER_ID), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Deployment of tenant1.application1.instance1 is not supported through this API\"}", 400); // DELETE tenant which has an application tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not delete tenant 'tenant1': This tenant has active applications\"}", - 400); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not delete tenant 'tenant1': This tenant has active applications\"}", + 400); // DELETE application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted instance tenant1.application1.instance1\"}"); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted instance tenant1.application1.instance1\"}"); // DELETE application again - should produce 404 tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE) - .oAuthCredentials(OKTA_CREDENTIALS) - .userIdentity(USER_ID), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"Could not delete instance 'tenant1.application1.instance1': Instance not found\"}", - 404); + .oAuthCredentials(OKTA_CREDENTIALS) + .userIdentity(USER_ID), + "{\"error-code\":\"NOT_FOUND\",\"message\":\"Could not delete instance 'tenant1.application1.instance1': Instance not found\"}", + 404); // DELETE and forget an application as non-operator tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).properties(Map.of("forget", "true")) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"error-code\":\"FORBIDDEN\",\"message\":\"Only operators can forget a tenant\"}", - 403); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"error-code\":\"FORBIDDEN\",\"message\":\"Only operators can forget a tenant\"}", + 403); // DELETE tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) - .userIdentity(USER_ID) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted tenant tenant1\"}"); + .userIdentity(USER_ID) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted tenant tenant1\"}"); // DELETE tenant again returns 403 as tenant access cannot be determined when the tenant does not exist tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) - .userIdentity(USER_ID), - accessDenied, - 403); + .userIdentity(USER_ID), + accessDenied, + 403); // Create legacy tenant name containing underscores tester.controller().curator().writeTenant(new AthenzTenant(TenantName.from("my_tenant"), ATHENZ_TENANT_DOMAIN, @@ -1272,116 +1282,116 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (add) a Athenz tenant with dashes duplicates existing one with underscores tester.assertResponse(request("/application/v4/tenant/my-tenant", POST) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'my-tenant' already exists\"}", - 400); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'my-tenant' already exists\"}", + 400); } @Test - public void testAuthorization() { + void testAuthorization() { UserId authorizedUser = USER_ID; UserId unauthorizedUser = new UserId("othertenant"); - + // Mutation without an user is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1", POST) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), - "{\n \"message\" : \"Not authenticated\"\n}", - 401); + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), + "{\n \"message\" : \"Not authenticated\"\n}", + 401); // ... but read methods are allowed for authenticated user tester.assertResponse(request("/application/v4/tenant/", GET) - .userIdentity(USER_ID) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), - "[]", - 200); + .userIdentity(USER_ID) + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), + "[]", + 200); createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); // Creating a tenant for an Athens domain the user is not admin for is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1", POST) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oAuthCredentials(OKTA_CREDENTIALS) - .userIdentity(unauthorizedUser), - "{\"error-code\":\"FORBIDDEN\",\"message\":\"The user 'user.othertenant' is not admin in Athenz domain 'domain1'\"}", - 403); + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .oAuthCredentials(OKTA_CREDENTIALS) + .userIdentity(unauthorizedUser), + "{\"error-code\":\"FORBIDDEN\",\"message\":\"The user 'user.othertenant' is not admin in Athenz domain 'domain1'\"}", + 403); // (Create it with the right tenant id) tester.assertResponse(request("/application/v4/tenant/tenant1", POST) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .userIdentity(authorizedUser) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("tenant-without-applications.json"), - 200); + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .userIdentity(authorizedUser) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("tenant-without-applications.json"), + 200); // Creating an application for an Athens domain the user is not admin for is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST) - .userIdentity(unauthorizedUser) - .oAuthCredentials(OKTA_CREDENTIALS), - accessDenied, - 403); + .userIdentity(unauthorizedUser) + .oAuthCredentials(OKTA_CREDENTIALS), + accessDenied, + 403); // (Create it with the right tenant id) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST) - .userIdentity(authorizedUser) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("instance-reference.json"), - 200); + .userIdentity(authorizedUser) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("instance-reference.json"), + 200); // Deploy to an authorized zone by a user tenant is disallowed MultiPartStreamer entity = createApplicationDeployData(applicationPackageDefault); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/deploy", POST) - .data(entity) - .userIdentity(USER_ID), - accessDenied, - 403); + .data(entity) + .userIdentity(USER_ID), + accessDenied, + 403); // Deleting an application for an Athens domain the user is not admin for is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) - .userIdentity(unauthorizedUser), - accessDenied, - 403); + .userIdentity(unauthorizedUser), + accessDenied, + 403); // Create another instance under the application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/default", POST) - .userIdentity(authorizedUser) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("instance-reference-default.json"), - 200); + .userIdentity(authorizedUser) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("instance-reference-default.json"), + 200); // (Deleting the application with the right tenant id) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) - .userIdentity(authorizedUser) - .oAuthCredentials(OKTA_CREDENTIALS), - "{\"message\":\"Deleted application tenant1.application1\"}", - 200); + .userIdentity(authorizedUser) + .oAuthCredentials(OKTA_CREDENTIALS), + "{\"message\":\"Deleted application tenant1.application1\"}", + 200); // Updating a tenant for an Athens domain the user is not admin for is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) - .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .userIdentity(unauthorizedUser), - accessDenied, - 403); - + .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") + .userIdentity(unauthorizedUser), + accessDenied, + 403); + // Change Athens domain createAthenzDomainWithAdmin(new AthenzDomain("domain2"), USER_ID); tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) - .data("{\"athensDomain\":\"domain2\", \"property\":\"property1\"}") - .userIdentity(authorizedUser) - .oAuthCredentials(OKTA_CREDENTIALS), - new File("tenant1.json"), - 200); + .data("{\"athensDomain\":\"domain2\", \"property\":\"property1\"}") + .userIdentity(authorizedUser) + .oAuthCredentials(OKTA_CREDENTIALS), + new File("tenant1.json"), + 200); // Deleting a tenant for an Athens domain the user is not admin for is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) - .userIdentity(unauthorizedUser), - accessDenied, - 403); + .userIdentity(unauthorizedUser), + accessDenied, + 403); } @Test - public void athenz_service_must_be_allowed_to_launch_and_be_under_tenant_domain() { + void athenz_service_must_be_allowed_to_launch_and_be_under_tenant_domain() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .upgradePolicy("default") .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("another.domain"), com.yahoo.config.provision.AthenzService.from("service")) @@ -1397,10 +1407,10 @@ public class ApplicationApiTest extends ControllerContainerTest { allowLaunchOfService(new com.yahoo.vespa.athenz.api.AthenzService(new AthenzDomain("another.domain"), "service")); // Submit a package with a service under a different Athenz domain from that of the tenant tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit/", POST) - .data(createApplicationSubmissionData(applicationPackage, 123)) - .screwdriverIdentity(screwdriverId), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz domain in deployment.xml: [another.domain] must match tenant domain: [domain1]\"}", - 400); + .data(createApplicationSubmissionData(applicationPackage, 123)) + .screwdriverIdentity(screwdriverId), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz domain in deployment.xml: [another.domain] must match tenant domain: [domain1]\"}", + 400); // Set the correct domain in the application package, but do not yet allow Vespa to launch the service. applicationPackage = new ApplicationPackageBuilder() @@ -1410,22 +1420,22 @@ public class ApplicationApiTest extends ControllerContainerTest { .build(); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit", POST) - .data(createApplicationSubmissionData(applicationPackage, 123)) - .screwdriverIdentity(screwdriverId), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Not allowed to launch Athenz service domain1.service\"}", - 400); + .data(createApplicationSubmissionData(applicationPackage, 123)) + .screwdriverIdentity(screwdriverId), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Not allowed to launch Athenz service domain1.service\"}", + 400); // Allow Vespa to launch the Athenz service. allowLaunchOfService(new com.yahoo.vespa.athenz.api.AthenzService(ATHENZ_TENANT_DOMAIN, "service")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/submit/", POST) - .data(createApplicationSubmissionData(applicationPackage, 123)) - .screwdriverIdentity(screwdriverId), - "{\"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\"}"); + .data(createApplicationSubmissionData(applicationPackage, 123)) + .screwdriverIdentity(screwdriverId), + "{\"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 - public void personal_deployment_with_athenz_service_requires_user_is_admin() { + void personal_deployment_with_athenz_service_requires_user_is_admin() { // Setup UserId tenantAdmin = new UserId("tenant-admin"); UserId userId = new UserId("new-user"); @@ -1440,26 +1450,26 @@ public class ApplicationApiTest extends ControllerContainerTest { MultiPartStreamer entity = createApplicationDeployData(applicationPackage); // POST (deploy) an application to dev through a deployment job, with user instance and a proper tenant tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/new-user/deploy/dev-us-east-1", POST) - .data(entity) - .userIdentity(userId), - accessDenied, - 403); + .data(entity) + .userIdentity(userId), + accessDenied, + 403); // Add "new-user" to the admin role, to allow service launches. tester.athenzClientFactory().getSetup() .domains.get(ATHENZ_TENANT_DOMAIN) - .admin(HostedAthenzIdentities.from(userId)); + .admin(HostedAthenzIdentities.from(userId)); // POST (deploy) an application to dev through a deployment job tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/new-user/deploy/dev-us-east-1", POST) - .data(entity) - .userIdentity(userId), - "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.new-user. This may take about 15 minutes the first time.\",\"run\":1}"); + .data(entity) + .userIdentity(userId), + "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.new-user. This may take about 15 minutes the first time.\",\"run\":1}"); } - @Test // Deploy to sandbox tenant launching a service from another domain. - public void developers_can_deploy_when_privileged() { + @Test + void developers_can_deploy_when_privileged() { // Create an athenz domain where the developer is not yet authorized UserId tenantAdmin = new UserId("tenant-admin"); createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, tenantAdmin); @@ -1493,7 +1503,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // Allow developer launch privilege to domain1.service. Deployment now completes. AthenzDbMock.Domain domainMock = tester.athenzClientFactory().getSetup().getOrCreateDomain(ATHENZ_TENANT_DOMAIN); - domainMock.withPolicy("launch-" +developer.id(), "user." + developer.id(), "launch", "service.service"); + domainMock.withPolicy("launch-" + developer.id(), "user." + developer.id(), "launch", "service.service"); tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST) @@ -1516,25 +1526,25 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (deploy) an application package as content type application/zip — not multipart tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST) - .data(applicationPackageInstance1.zippedContent()) - .contentType("application/zip") - .userIdentity(developer2), - "{\"message\":\"Deployment started in run 3 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":3}"); + .data(applicationPackageInstance1.zippedContent()) + .contentType("application/zip") + .userIdentity(developer2), + "{\"message\":\"Deployment started in run 3 of dev-us-east-1 for sandbox.myapp. This may take about 15 minutes the first time.\",\"run\":3}"); // POST (deploy) an application package not as content type application/zip — not multipart — is disallowed tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST) - .data(applicationPackageInstance1.zippedContent()) - .contentType("application/gzip") - .userIdentity(developer2), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Expected a multipart or application/zip message, but got Content-Type: application/gzip\"}", 400); + .data(applicationPackageInstance1.zippedContent()) + .contentType("application/gzip") + .userIdentity(developer2), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Expected a multipart or application/zip message, but got Content-Type: application/gzip\"}", 400); } @Test - public void applicationWithRoutingPolicy() { + void applicationWithRoutingPolicy() { var app = deploymentTester.newDeploymentContext(createTenantAndApplication()); var zone = ZoneId.from(Environment.prod, RegionName.from("us-west-1")); deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone), - RoutingMethod.exclusive); + RoutingMethod.exclusive); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("domain"), AthenzService.from("service")) .instances("instance1") @@ -1545,22 +1555,22 @@ public class ApplicationApiTest extends ControllerContainerTest { // GET application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", GET) - .userIdentity(USER_ID), - new File("instance-with-routing-policy.json")); + .userIdentity(USER_ID), + new File("instance-with-routing-policy.json")); // GET deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/instance1", GET) - .userIdentity(USER_ID), - new File("deployment-with-routing-policy.json")); + .userIdentity(USER_ID), + new File("deployment-with-routing-policy.json")); // GET deployment tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/instance1", GET) - .userIdentity(USER_ID), - new File("deployment-without-shared-endpoints.json")); + .userIdentity(USER_ID), + new File("deployment-without-shared-endpoints.json")); } @Test - public void support_access() { + void support_access() { var app = deploymentTester.newDeploymentContext(createTenantAndApplication()); var zone = ZoneId.from(Environment.prod, RegionName.from("us-west-1")); deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.exclusive); @@ -1591,15 +1601,15 @@ public class ApplicationApiTest extends ControllerContainerTest { // Grant access to support user X509Certificate support_cert = grantCertificate(now, now.plusSeconds(3600)); - String grantPayload= "{\n" + - " \"applicationId\": \"tenant1:application1:instance1\",\n" + - " \"zone\": \"prod.us-west-1\",\n" + - " \"certificate\":\""+X509CertificateUtils.toPem(support_cert)+ "\"\n" + - "}"; - tester.assertResponse(request("/controller/v1/access/grants/"+HOSTED_VESPA_OPERATOR.id(), POST) - .data(grantPayload) - .userIdentity(HOSTED_VESPA_OPERATOR), - "{\"message\":\"Operator user.johnoperator granted access and job production-us-west-1 triggered\"}"); + String grantPayload = "{\n" + + " \"applicationId\": \"tenant1:application1:instance1\",\n" + + " \"zone\": \"prod.us-west-1\",\n" + + " \"certificate\":\"" + X509CertificateUtils.toPem(support_cert) + "\"\n" + + "}"; + tester.assertResponse(request("/controller/v1/access/grants/" + HOSTED_VESPA_OPERATOR.id(), POST) + .data(grantPayload) + .userIdentity(HOSTED_VESPA_OPERATOR), + "{\"message\":\"Operator user.johnoperator granted access and job production-us-west-1 triggered\"}"); // GET shows grant String grantResponse = allowedResponse.replaceAll("\"grants\":\\[]", @@ -1621,7 +1631,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE removes access String disallowedResponse = grantResponse .replaceAll("ALLOWED\".*?}", "NOT_ALLOWED\"}") - .replace("history\":[", "history\":[{\"state\":\"disallowed\",\"at\":\""+ serializeInstant(now) +"\",\"by\":\"user.myuser\"},"); + .replace("history\":[", "history\":[{\"state\":\"disallowed\",\"at\":\"" + serializeInstant(now) + "\",\"by\":\"user.myuser\"},"); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/access/support", DELETE) .userIdentity(USER_ID), disallowedResponse, 200 @@ -1636,7 +1646,7 @@ public class ApplicationApiTest extends ControllerContainerTest { } @Test - public void create_application_on_deploy() { + void create_application_on_deploy() { // Setup createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID); addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR)); @@ -1658,7 +1668,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/dev-us-east-1/", POST) .data(entity) .oAuthCredentials(OKTA_CREDENTIALS) - + .userIdentity(USER_ID), "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/CliApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/CliApiHandlerTest.java index 5a4d47e0db5..4166ce8b81e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/CliApiHandlerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/CliApiHandlerTest.java @@ -3,8 +3,8 @@ package com.yahoo.vespa.hosted.controller.restapi.application; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * @author mpolden @@ -13,13 +13,13 @@ public class CliApiHandlerTest extends ControllerContainerTest { private ContainerTester tester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, "src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/"); } @Test - public void root() { + void root() { tester.assertResponse(authenticatedRequest("http://localhost:8080/cli/v1/"), "{\"minVersion\":\"7.547.18\"}"); } 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 d2698afdc48..233b460333e 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 @@ -15,7 +15,7 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.notification.Notification.Type; import com.yahoo.vespa.hosted.controller.notification.NotificationSource; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.net.URI; @@ -40,7 +40,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.deploymentF import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.installationFailed; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -49,7 +49,7 @@ import static org.junit.Assert.assertEquals; public class JobControllerApiHandlerHelperTest { @Test - public void testResponses() { + void testResponses() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .stagingTest() .blockChange(true, true, "mon,tue", "7-13", "UTC") @@ -100,7 +100,7 @@ public class JobControllerApiHandlerHelperTest { // Revision 3 starts. app.submit(applicationPackage) - .runJob(systemTest).runJob(stagingTest); + .runJob(systemTest).runJob(stagingTest); tester.triggerJobs(); // Starts runs for us-central-1 and a new staging test run. tester.runner().run(); assertEquals(running, tester.jobs().last(app.instanceId(), productionUsCentral1).get().status()); @@ -147,7 +147,7 @@ public class JobControllerApiHandlerHelperTest { } @Test - public void testDevResponses() { + void testDevResponses() { DeploymentTester tester = new DeploymentTester(); var app = tester.newDeploymentContext(); tester.clock().setInstant(Instant.EPOCH); @@ -167,7 +167,7 @@ public class JobControllerApiHandlerHelperTest { } @Test - public void testResponsesWithDirectDeployment() { + void testResponsesWithDirectDeployment() { var tester = new DeploymentTester(); var app = tester.newDeploymentContext(); tester.clock().setInstant(Instant.EPOCH); @@ -176,11 +176,11 @@ public class JobControllerApiHandlerHelperTest { // Deploy directly to production zone, like integration tests. tester.controller().jobController().deploy(tester.instance().id(), productionUsWest1, Optional.empty(), applicationPackage); assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.controller(), app.instanceId(), URI.create("https://some.url:43/root/")), - "jobs-direct-deployment.json"); + "jobs-direct-deployment.json"); } @Test - public void testResponsesWithDryRunDeployment() { + void testResponsesWithDryRunDeployment() { var tester = new DeploymentTester(); var app = tester.newDeploymentContext(); tester.clock().setInstant(Instant.EPOCH); @@ -189,7 +189,7 @@ public class JobControllerApiHandlerHelperTest { // Deploy directly to production zone, like integration tests, with dryRun. tester.controller().jobController().deploy(tester.instance().id(), productionUsWest1, Optional.empty(), applicationPackage, true); assertResponse(JobControllerApiHandlerHelper.jobTypeResponse(tester.controller(), app.instanceId(), URI.create("https://some.url:43/root/")), - "jobs-direct-deployment.json"); + "jobs-direct-deployment.json"); } private void assertResponse(HttpResponse response, String fileName) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java index 12a0a00713c..37f4c19e27a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/MultipartParserTest.java @@ -7,16 +7,16 @@ import com.yahoo.jdisc.Request; import com.yahoo.jdisc.ResourceReference; import com.yahoo.jdisc.handler.RequestHandler; import com.yahoo.jdisc.service.CurrentContainer; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author bratseth @@ -24,25 +24,25 @@ import static org.junit.Assert.fail; public class MultipartParserTest { @Test - public void parser() { + void parser() { String data = "Content-Type: multipart/form-data; boundary=AaB03x\r\n" + - "\r\n" + - "--AaB03x\r\n" + - "Content-Disposition: form-data; name=\"submit-name\"\r\n" + - "\r\n" + - "Larry\r\n" + - "--AaB03x\r\n" + - "Content-Disposition: form-data; name=\"submit-address\"\r\n" + - "Content-Type: text/plain\r\n" + - "\r\n" + - "House 1\r\n" + - "--AaB03x\r\n" + - "Content-Disposition: form-data; name=\"files\"; filename=\"file1.txt\"\r\n" + - "Content-Type: text/plain\r\n" + - "\r\n" + - "... contents of file1.txt ...\r\n" + - "--AaB03x--\r\n"; + "\r\n" + + "--AaB03x\r\n" + + "Content-Disposition: form-data; name=\"submit-name\"\r\n" + + "\r\n" + + "Larry\r\n" + + "--AaB03x\r\n" + + "Content-Disposition: form-data; name=\"submit-address\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "House 1\r\n" + + "--AaB03x\r\n" + + "Content-Disposition: form-data; name=\"files\"; filename=\"file1.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "... contents of file1.txt ...\r\n" + + "--AaB03x--\r\n"; Map<String, byte[]> parts = parse(data, Long.MAX_VALUE); assertEquals(3, parts.size()); assertTrue(parts.containsKey("submit-name")); @@ -53,22 +53,22 @@ public class MultipartParserTest { } @Test - public void max_length() { + void max_length() { String part1 = "Larry"; String part2 = "House 1"; String data = "Content-Type: multipart/form-data; boundary=AaB03x\r\n" + - "\r\n" + - "--AaB03x\r\n" + - "Content-Disposition: form-data; name=\"submit-name\"\r\n" + - "\r\n" + - part1 + "\r\n" + - "--AaB03x\r\n" + - "Content-Disposition: form-data; name=\"submit-address\"\r\n" + - "Content-Type: text/plain\r\n" + - "\r\n" + - part2 + "\r\n" + - "--AaB03x--\r\n"; + "\r\n" + + "--AaB03x\r\n" + + "Content-Disposition: form-data; name=\"submit-name\"\r\n" + + "\r\n" + + part1 + "\r\n" + + "--AaB03x\r\n" + + "Content-Disposition: form-data; name=\"submit-address\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + part2 + "\r\n" + + "--AaB03x--\r\n"; parse(data, part1.length() + part2.length()); try { parse(data, part1.length() + part2.length() - 1); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json index 481997102d2..a7e2d1913cf 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview-2.json @@ -158,7 +158,6 @@ "jobName": "system-test", "url": "https://some.url:43/instance/default/job/system-test", "environment": "test", - "region": "test.us-east-1", "toRun": [ ], "runs": [ { @@ -348,7 +347,6 @@ "jobName": "staging-test", "url": "https://some.url:43/instance/default/job/staging-test", "environment": "staging", - "region": "staging.us-east-3", "toRun": [ { "versions": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json index f39aab26d75..38e9d8c823e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-overview.json @@ -62,7 +62,6 @@ "jobName": "system-test", "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/system-test", "environment": "test", - "region": "test.us-east-1", "toRun": [ ], "runs": [ { @@ -191,7 +190,6 @@ "jobName": "staging-test", "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/staging-test", "environment": "staging", - "region": "staging.us-east-3", "toRun": [ ], "runs": [ { @@ -430,6 +428,7 @@ "url": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/job/production-us-west-1/run/1", "start": 1600000000000, "status": "running", + "reason": "triggered by user.myuser", "versions": { "targetPlatform": "6.1.0", "targetApplication": { 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 63869ecfba8..9b391196d55 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 @@ -11,6 +11,11 @@ { "at": 0, "type": "info", + "message": "Using CA signed certificate version 0" + }, + { + "at": 0, + "type": "info", "message": "Deployment successful." }, { @@ -49,7 +54,7 @@ } ] }, - "lastId": 7, + "lastId": 8, "steps": { "deployReal": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json index 175c45eb2cd..4ffac2bf738 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json @@ -6,6 +6,11 @@ { "at": 0, "type": "info", + "message": "Found endpoints:" + }, + { + "at": 0, + "type": "info", "message": "- dev.us-east-1" }, { @@ -20,7 +25,7 @@ } ] }, - "lastId": 11, + "lastId": 12, "steps": { "deployReal": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json deleted file mode 100644 index 2ca520c0122..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "tenant": "tenant1", - "application": "application1", - "instance": "instance1", - "environment": "dev", - "region": "us-east-1", - "endpoints": [ - { - "cluster": "default", - "tls": true, - "url": "https://instance1.application1.tenant1.us-east-1.dev.vespa.oath.cloud/", - "scope": "zone", - "routingMethod": "sharedLayer4", - "legacy": false - } - ], - "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/dev/region/us-east-1/clusters", - "nodes": "http://localhost:8080/zone/v2/dev/us-east-1/nodes/v2/node/?recursive=true&application=tenant1.application1.instance1", - "yamasUrl": "http://monitoring-system.test/?environment=dev®ion=us-east-1&application=tenant1.application1.instance1", - "version": "(ignore)", - "revision": "(ignore)", - "deployTimeEpochMs": "(ignore)", - "screwdriverId": "123", - "status": "complete", - "quota": "(ignore)", - "activity": { - "lastQueried": 1527848130000, - "lastWritten": 1527848130000, - "lastQueriesPerSecond": 1.0, - "lastWritesPerSecond": 2.0 - }, - "metrics": { - "queriesPerSecond": 1.0, - "writesPerSecond": 2.0, - "documentCount": 3.0, - "queryLatencyMillis": 4.0, - "writeLatencyMillis": 5.0, - "lastUpdated": 123123 - } -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-metering.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-metering.json deleted file mode 100644 index 2e07a7e8a96..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-metering.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "currentrate": { - "cpu": 1.0, - "mem": 2.0, - "disk": 3.0 - }, - "thismonth": { - "cpu": 12.0, - "mem": 24.0, - "disk": 1000.0 - }, - "lastmonth": { - "cpu": 24.0, - "mem": 48.0, - "disk": 2000.0 - }, - "details": { - "cpu": { - "default": { - "data": [ - {"unixms":123,"value":1.0}, - {"unixms":246,"value":1.0}, - {"unixms":492,"value":1.0} - ] - } - }, - "mem": { - "default": { - "data": [ - {"unixms":123,"value":2.0}, - {"unixms":246,"value":2.0}, - {"unixms":492,"value":2.0} - ] - } - }, - "disk": { - "default": { - "data": [ - {"unixms":123,"value":3.0}, - {"unixms":246,"value":3.0}, - {"unixms":492,"value":3.0} - ] - } - } - } -}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json index 12430b67539..2477e8df56e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json @@ -9,6 +9,7 @@ "start": 1600000000000, "end": 1600000000000, "status": "success", + "reason": "triggered by user.myuser", "versions": { "targetPlatform": "6.1.0", "targetApplication": { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json deleted file mode 100644 index 2daa7f54cf6..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "bcpStatus": { - "rotationStatus": "UNKNOWN" - }, - "tenant": "tenant1", - "application": "application1", - "instance": "instance1", - "environment": "prod", - "region": "us-central-1", - "endpoints": [ - { - "cluster": "default", - "tls": true, - "url": "https://instance1.application1.tenant1.us-central-1.vespa.oath.cloud/", - "scope": "zone", - "routingMethod": "sharedLayer4", - "legacy": false - }, - { - "cluster": "foo", - "tls": true, - "url": "https://instance1.application1.tenant1.global.vespa.oath.cloud/", - "scope": "global", - "routingMethod": "sharedLayer4", - "legacy": false - }, - { - "cluster": "foo", - "tls": true, - "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/", - "scope": "application", - "routingMethod": "sharedLayer4", - "legacy": false - } - ], - "clusters": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/clusters", - "nodes": "http://localhost:8080/zone/v2/prod/us-central-1/nodes/v2/node/?recursive=true&application=tenant1.application1.instance1", - "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-central-1&application=tenant1.application1.instance1", - "version": "(ignore)", - "revision": "(ignore)", - "deployTimeEpochMs": "(ignore)", - "screwdriverId": "123", - "endpointStatus": [ - { - "endpointId": "default", - "rotationId": "rotation-id-1", - "clusterId": "foo", - "status": "UNKNOWN", - "lastUpdated": "(ignore)" - } - ], - "applicationVersion": { - "build": 1, - "compileVersion": "6.1.0", - "sourceUrl": "repository1/tree/commit1", - "commit": "commit1" - }, - "status": "complete", - "quota": "(ignore)", - "activity": { - "lastQueried": 1527848130000, - "lastWritten": 1527848130000, - "lastQueriesPerSecond": 1.0, - "lastWritesPerSecond": 2.0 - }, - "metrics": { - "queriesPerSecond": 1.0, - "writesPerSecond": 2.0, - "documentCount": 3.0, - "queryLatencyMillis": 4.0, - "writeLatencyMillis": 5.0, - "lastUpdated": 123123 - } -} 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 ba65b962a73..a2f62621f5b 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 @@ -85,6 +85,11 @@ { "at": 14503000, "type": "info", + "message": "Using CA signed certificate version 0" + }, + { + "at": 14503000, + "type": "info", "message": "Deployment successful." }, { @@ -160,7 +165,7 @@ } ] }, - "lastId": 29, + "lastId": 30, "steps": { "deployTester": { "status": "succeeded", 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 3b505bc11fd..a691762c40b 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 @@ -105,6 +105,11 @@ { "at": 1600000000000, "type": "info", + "message": "Using CA signed certificate version 1" + }, + { + "at": 1600000000000, + "type": "info", "message": "Deployment successful." }, { @@ -354,7 +359,7 @@ } ] }, - "lastId": 66, + "lastId": 67, "steps": { "deployTester": { "status": "succeeded", 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 5bf6822baff..4e8737d5f67 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 @@ -100,6 +100,11 @@ { "at": 0, "type": "info", + "message": "Using CA signed certificate version 0" + }, + { + "at": 0, + "type": "info", "message": "Deployment successful." }, { @@ -349,7 +354,7 @@ } ] }, - "lastId": 66, + "lastId": 67, "steps": { "deployTester": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application-with-metadata.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application-with-metadata.json deleted file mode 100644 index 194f2cdd494..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application-with-metadata.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "tenant": "tenant1", - "type": "ATHENS", - "athensDomain": "domain1", - "property": "property1", - "applications": [ - { - "tenant": "tenant1", - "application":"application1", - "instance":"instance1", - "url":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1" - } - ], - "metaData": { - "createdAtMillis": "(ignore)", - "lastDeploymentToDevMillis":"(ignore)", - "lastSubmissionToProdMillis":1000 - } -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiTest.java index de3968727d6..3a539987443 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/athenz/AthenzApiTest.java @@ -7,7 +7,7 @@ import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; @@ -19,7 +19,7 @@ public class AthenzApiTest extends ControllerContainerTest { private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/athenz/responses/"; @Test - public void testAthenzApi() { + void testAthenzApi() { ContainerTester tester = new ContainerTester(container, responseFiles); ControllerTester controllerTester = new ControllerTester(tester); @@ -38,19 +38,19 @@ public class AthenzApiTest extends ControllerContainerTest { // GET root tester.assertResponse(authenticatedRequest("http://localhost:8080/athenz/v1/"), - new File("root.json")); + new File("root.json")); // GET Athenz domains tester.assertResponse(authenticatedRequest("http://localhost:8080/athenz/v1/domains"), - new File("athensDomain-list.json")); + new File("athensDomain-list.json")); // GET properties tester.assertResponse(authenticatedRequest("http://localhost:8080/athenz/v1/properties/"), - new File("property-list.json")); + new File("property-list.json")); // POST user signup tester.assertResponse(authenticatedRequest("http://localhost:8080/athenz/v1/user", "", Request.Method.POST), - "{\"message\":\"User 'bob' added to admin role of 'vespa.vespa.tenants.sandbox'\"}"); + "{\"message\":\"User 'bob' added to admin role of 'vespa.vespa.tenants.sandbox'\"}"); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java index 38ebe030e8e..aa9cb57f541 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java @@ -12,9 +12,8 @@ import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; import com.yahoo.vespa.hosted.controller.security.Auth0Credentials; import com.yahoo.vespa.hosted.controller.security.CloudTenantSpec; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.math.BigDecimal; @@ -30,8 +29,8 @@ import static com.yahoo.application.container.handler.Request.Method.DELETE; import static com.yahoo.application.container.handler.Request.Method.GET; import static com.yahoo.application.container.handler.Request.Method.PATCH; import static com.yahoo.application.container.handler.Request.Method.POST; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author olaa @@ -47,7 +46,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { private ContainerTester tester; - @Before + @BeforeEach public void setup() { tester = new ContainerTester(container, responseFiles); billingController = (MockBillingController) tester.serviceRegistry().billingController(); @@ -79,32 +78,32 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void list_plans() { + void list_plans() { var listPlansRequest = request("/billing/v1/plans", GET) .roles(Role.hostedAccountant()); tester.assertResponse(listPlansRequest, "{\"plans\":[{\"id\":\"trial\",\"name\":\"Free Trial - for testing purposes\"},{\"id\":\"paid\",\"name\":\"Paid Plan - for testing purposes\"},{\"id\":\"none\",\"name\":\"None Plan - for testing purposes\"}]}"); } @Test - public void setting_and_deleting_instrument() { + void setting_and_deleting_instrument() { assertTrue(billingController.getDefaultInstrument(tenant).isEmpty()); var instrumentRequest = request("/billing/v1/tenant/tenant1/instrument", PATCH) .data("{\"active\": \"id-1\"}") .roles(tenantRole); - tester.assertResponse(instrumentRequest,"OK"); + tester.assertResponse(instrumentRequest, "OK"); assertEquals("id-1", billingController.getDefaultInstrument(tenant).get().getId()); var deleteInstrumentRequest = request("/billing/v1/tenant/tenant1/instrument/id-1", DELETE) .roles(tenantRole); - tester.assertResponse(deleteInstrumentRequest,"OK"); + tester.assertResponse(deleteInstrumentRequest, "OK"); assertTrue(billingController.getDefaultInstrument(tenant).isEmpty()); } @Test - public void response_list_bills() { + void response_list_bills() { var bill = createBill(); billingController.addBill(tenant, bill, true); @@ -117,7 +116,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void test_bill_creation() { + void test_bill_creation() { var bills = billingController.getBillsForTenant(tenant); assertEquals(0, bills.size()); @@ -141,7 +140,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void adding_and_listing_line_item() { + void adding_and_listing_line_item() { var requestBody = "{" + "\"description\":\"some description\"," + @@ -155,7 +154,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { tester.assertResponse(request, "{\"message\":\"Added line item for tenant tenant1\"}"); var lineItems = billingController.getUnusedLineItems(tenant); - Assert.assertEquals(1, lineItems.size()); + assertEquals(1, lineItems.size()); Bill.LineItem lineItem = lineItems.get(0); assertEquals("some description", lineItem.description()); assertEquals(new BigDecimal("123.45"), lineItem.amount()); @@ -167,7 +166,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void adding_new_status() { + void adding_new_status() { billingController.addBill(tenant, createBill(), true); var requestBody = "{\"status\":\"DONE\"}"; @@ -181,7 +180,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void list_all_unbilled_items() { + void list_all_unbilled_items() { tester.controller().tenants().create(new CloudTenantSpec(tenant, ""), new Auth0Credentials(() -> "foo", Set.of(Role.hostedOperator()))); tester.controller().tenants().create(new CloudTenantSpec(tenant2, ""), new Auth0Credentials(() -> "foo", Set.of(Role.hostedOperator()))); @@ -198,7 +197,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void setting_plans() { + void setting_plans() { var planRequest = request("/billing/v1/tenant/tenant1/plan", PATCH) .data("{\"plan\": \"new-plan\"}") .roles(tenantRole); @@ -207,7 +206,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void csv_export() { + void csv_export() { var bill = createBill(); billingController.addBill(tenant, bill, true); var csvRequest = request("/billing/v1/invoice/export", GET).roles(financeAdmin); @@ -215,7 +214,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest { } @Test - public void patch_collection_method() { + void patch_collection_method() { test_patch_collection_with_field_name("collectionMethod"); test_patch_collection_with_field_name("collection"); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java index 58701732515..c62a9f1399f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java @@ -10,8 +10,8 @@ import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; import com.yahoo.vespa.hosted.controller.security.Auth0Credentials; import com.yahoo.vespa.hosted.controller.security.CloudTenantSpec; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.Set; @@ -37,7 +37,7 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { private MockBillingController billingController; private ContainerTester tester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); tester.controller().tenants().create(new CloudTenantSpec(tenant, ""), new Auth0Credentials(() -> "foo", Set.of(Role.hostedOperator()))); @@ -68,13 +68,13 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { } @Test - public void require_tenant_info() { + void require_tenant_info() { var request = request("/billing/v2/tenant/" + tenant.value()).roles(tenantReader); tester.assertResponse(request, "{\"tenant\":\"tenant1\",\"plan\":{\"id\":\"trial\",\"name\":\"Free Trial - for testing purposes\"},\"collection\":\"AUTO\"}"); } @Test - public void require_admin_for_update_plan() { + void require_admin_for_update_plan() { var request = request("/billing/v2/tenant/" + tenant.value(), Request.Method.PATCH) .data("{\"plan\": \"paid\"}"); @@ -86,7 +86,7 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { } @Test - public void require_accountant_for_update_collection() { + void require_accountant_for_update_collection() { var request = request("/billing/v2/tenant/" + tenant.value(), Request.Method.PATCH) .data("{\"collection\": \"INVOICE\"}"); @@ -99,13 +99,13 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { } @Test - public void require_tenant_usage() { + void require_tenant_usage() { var request = request("/billing/v2/tenant/" + tenant + "/usage").roles(tenantReader); tester.assertResponse(request, "{\"from\":\"2021-04-13\",\"to\":\"2021-04-13\",\"total\":\"0.00\",\"items\":[]}"); } @Test - public void require_tenant_invoice() { + void require_tenant_invoice() { var listRequest = request("/billing/v2/tenant/" + tenant + "/bill").roles(tenantReader); tester.assertResponse(listRequest, "{\"invoices\":[{\"id\":\"id-1\",\"from\":\"2020-05-23\",\"to\":\"2020-05-28\",\"total\":\"123.00\",\"status\":\"OPEN\"}]}"); @@ -115,7 +115,7 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { } @Test - public void require_accountant_summary() { + void require_accountant_summary() { var tenantRequest = request("/billing/v2/accountant").roles(tenantReader); tester.assertResponse(tenantRequest, "{\n" + " \"code\" : 403,\n" + @@ -124,17 +124,17 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { var accountantRequest = request("/billing/v2/accountant").roles(Role.hostedAccountant()); tester.assertResponse(accountantRequest, """ - {"tenants":[{"tenant":"tenant1","plan":{"id":"trial","name":"Free Trial - for testing purposes"},"collection":"AUTO","lastBill":null,"unbilled":"0.00"}]}"""); + {"tenants":[{"tenant":"tenant1","plan":{"id":"trial","name":"Free Trial - for testing purposes"},"quota":{"budget":-1.0},"collection":"AUTO","lastBill":null,"unbilled":"0.00"}]}"""); } @Test - public void require_accountant_tenant_preview() { + void require_accountant_tenant_preview() { var accountantRequest = request("/billing/v2/accountant/preview/tenant/tenant1").roles(Role.hostedAccountant()); tester.assertResponse(accountantRequest, "{\"id\":\"empty\",\"from\":\"2021-04-13\",\"to\":\"2021-04-12\",\"total\":\"0.00\",\"status\":\"OPEN\",\"statusHistory\":[{\"at\":\"2021-04-13T00:00:00Z\",\"status\":\"OPEN\"}],\"items\":[]}"); } @Test - public void require_accountant_tenant_bill() { + void require_accountant_tenant_bill() { var accountantRequest = request("/billing/v2/accountant/preview/tenant/tenant1", Request.Method.POST) .roles(Role.hostedAccountant()) .data("{\"from\": \"2020-05-01\",\"to\": \"2020-06-01\"}"); @@ -142,7 +142,7 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest { } @Test - public void require_list_of_all_plans() { + void require_list_of_all_plans() { var accountantRequest = request("/billing/v2/accountant/plans") .roles(Role.hostedAccountant()); tester.assertResponse(accountantRequest, "{\"plans\":[{\"id\":\"trial\",\"name\":\"Free Trial - for testing purposes\"},{\"id\":\"paid\",\"name\":\"Paid Plan - for testing purposes\"},{\"id\":\"none\",\"name\":\"None Plan - for testing purposes\"}]}"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/changemanagement/ChangeManagementApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/changemanagement/ChangeManagementApiHandlerTest.java index ea2c303d3f6..de2bf70997a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/changemanagement/ChangeManagementApiHandlerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/changemanagement/ChangeManagementApiHandlerTest.java @@ -13,8 +13,8 @@ import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.time.Instant; @@ -22,7 +22,7 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ChangeManagementApiHandlerTest extends ControllerContainerTest { @@ -32,7 +32,7 @@ public class ChangeManagementApiHandlerTest extends ControllerContainerTest { private ContainerTester tester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responses); addUserToHostedOperatorRole(operator); @@ -42,26 +42,26 @@ public class ChangeManagementApiHandlerTest extends ControllerContainerTest { } @Test - public void test_api() { + void test_api() { assertFile(new Request("http://localhost:8080/changemanagement/v1/assessment", "{\"zone\":\"prod.us-east-3\", \"hosts\": [\"host1\"]}", Request.Method.POST), "initial.json"); assertFile(new Request("http://localhost:8080/changemanagement/v1/assessment", "{\"zone\":\"prod.us-east-3\", \"switches\": [\"switch1\"]}", Request.Method.POST), "initial.json"); assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr"), "vcmrs.json"); } @Test - public void deletes_vcmr() { + void deletes_vcmr() { assertEquals(1, tester.controller().curator().readChangeRequests().size()); assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr/" + changeRequestId, "", Request.Method.DELETE), "vcmr.json"); assertEquals(0, tester.controller().curator().readChangeRequests().size()); } @Test - public void get_vcmr() { + void get_vcmr() { assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr/" + changeRequestId, "", Request.Method.GET), "vcmr.json"); } @Test - public void patch_vcmr() { + void patch_vcmr() { var payload = "{" + "\"approval\": \"REJECTED\"," + "\"status\": \"COMPLETED\"," + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java index ce6f713a13d..8bda8ba0d59 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java @@ -10,15 +10,15 @@ import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.proxy.ProxyRequest; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.net.URI; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author freva @@ -35,7 +35,7 @@ public class ConfigServerApiHandlerTest extends ControllerContainerTest { private ContainerTester tester; private ConfigServerProxyMock proxy; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); tester.serviceRegistry().zoneRegistry() @@ -45,14 +45,14 @@ public class ConfigServerApiHandlerTest extends ControllerContainerTest { } @Test - public void test_requests() { + void test_requests() { // GET /configserver/v1 tester.assertResponse(operatorRequest("http://localhost:8080/configserver/v1"), new File("root.json")); // GET /configserver/v1/nodes/v2 tester.assertResponse(operatorRequest("http://localhost:8080/configserver/v1/prod/us-north-1/nodes/v2"), - "ok"); + "ok"); assertLastRequest("https://cfg.prod.us-north-1.test.vip:4443/", "GET"); // GET /configserver/v1/nodes/v2/node/?recursive=true @@ -82,11 +82,11 @@ public class ConfigServerApiHandlerTest extends ControllerContainerTest { assertLastRequest("https://cfg.dev.aws-us-north-2.test.vip:4443/", "PATCH"); assertEquals("{\"currentRestartGeneration\": 1}", proxy.lastRequestBody().get()); - assertFalse("Actions are logged to audit log", tester.controller().auditLogger().readLog().entries().isEmpty()); + assertFalse(tester.controller().auditLogger().readLog().entries().isEmpty(), "Actions are logged to audit log"); } @Test - public void test_allowed_apis() { + void test_allowed_apis() { // GET /configserver/v1/prod/us-north-1 tester.assertResponse(() -> operatorRequest("http://localhost:8080/configserver/v1/prod/us-north-1/"), "{\"error-code\":\"FORBIDDEN\",\"message\":\"Cannot access path '/' through /configserver/v1, following APIs are permitted: /flags/v1/, /nodes/v2/, /orchestrator/v1/\"}", @@ -98,33 +98,33 @@ public class ConfigServerApiHandlerTest extends ControllerContainerTest { } @Test - public void test_invalid_requests() { + void test_invalid_requests() { // POST /configserver/v1/prod/us-north-34/nodes/v2 tester.assertResponse(() -> operatorRequest("http://localhost:8080/configserver/v1/prod/us-north-42/nodes/v2", - "", Request.Method.POST), + "", Request.Method.POST), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such zone: prod.us-north-42\"}", 400); assertFalse(proxy.lastReceived().isPresent()); } @Test - public void non_operators_are_forbidden() { + void non_operators_are_forbidden() { // Read request tester.assertResponse(() -> authenticatedRequest("http://localhost:8080/configserver/v1/prod/us-north-1/nodes/v2/node"), "{\n" + - " \"code\" : 403,\n" + - " \"message\" : \"Access denied\"\n" + - "}", 403); + " \"code\" : 403,\n" + + " \"message\" : \"Access denied\"\n" + + "}", 403); // Write request tester.assertResponse(() -> authenticatedRequest("http://localhost:8080/configserver/v1/prod/us-north-1/nodes/v2/node", "", Request.Method.POST), "{\n" + - " \"code\" : 403,\n" + - " \"message\" : \"Access denied\"\n" + - "}", 403); + " \"code\" : 403,\n" + + " \"message\" : \"Access denied\"\n" + + "}", 403); } @Test - public void unauthenticated_request_are_unauthorized() { + void unauthenticated_request_are_unauthorized() { { // Read request Request request = new Request("http://localhost:8080/configserver/v1/prod/us-north-1/nodes/v2/node", "", Request.Method.GET); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java index 5936c135af9..fa652af18f3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java @@ -17,8 +17,8 @@ import com.yahoo.vespa.hosted.controller.auditlog.AuditLogger; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.File; @@ -27,7 +27,7 @@ import java.time.Duration; import java.time.Instant; import java.util.List; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author bratseth @@ -38,42 +38,42 @@ public class ControllerApiTest extends ControllerContainerTest { private ContainerTester tester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); } @Test - public void testControllerApi() { + void testControllerApi() { tester.assertResponse(authenticatedRequest("http://localhost:8080/controller/v1/", "", Request.Method.GET), new File("root.json")); ((InMemoryFlagSource) tester.controller().flagSource()).withListFlag(PermanentFlags.INACTIVE_MAINTENANCE_JOBS.id(), List.of("DeploymentExpirer"), String.class); // GET a list of all maintenance jobs tester.assertResponse(authenticatedRequest("http://localhost:8080/controller/v1/maintenance/", "", Request.Method.GET), - new File("maintenance.json")); + new File("maintenance.json")); } @Test - public void testStats() { - var mock = (NodeRepositoryMock)tester.controller().serviceRegistry().configServer().nodeRepository(); + void testStats() { + var mock = (NodeRepositoryMock) tester.controller().serviceRegistry().configServer().nodeRepository(); mock.putApplication(ZoneId.from("prod", "us-west-1"), - new Application(ApplicationId.fromFullString("t1.a1.i1"), List.of())); + new Application(ApplicationId.fromFullString("t1.a1.i1"), List.of())); mock.putApplication(ZoneId.from("prod", "us-west-1"), - new Application(ApplicationId.fromFullString("t2.a2.i2"), List.of())); + new Application(ApplicationId.fromFullString("t2.a2.i2"), List.of())); mock.putApplication(ZoneId.from("prod", "us-east-3"), - new Application(ApplicationId.fromFullString("t1.a1.i1"), List.of())); + new Application(ApplicationId.fromFullString("t1.a1.i1"), List.of())); tester.assertResponse(authenticatedRequest("http://localhost:8080/controller/v1/stats", "", Request.Method.GET), - new File("stats.json")); + new File("stats.json")); } @Test - public void testUpgraderApi() { + void testUpgraderApi() { // Get current configuration tester.assertResponse(authenticatedRequest("http://localhost:8080/controller/v1/jobs/upgrader", "", Request.Method.GET), - "{\"upgradesPerMinute\":0.125,\"confidenceOverrides\":[]}", - 200); + "{\"upgradesPerMinute\":0.125,\"confidenceOverrides\":[]}", + 200); // Set invalid configuration tester.assertResponse( @@ -123,11 +123,11 @@ public class ControllerApiTest extends ControllerContainerTest { "{\"upgradesPerMinute\":42.0,\"confidenceOverrides\":[{\"6.43\":\"broken\"}]}", 200); - assertFalse("Actions are logged to audit log", tester.controller().auditLogger().readLog().entries().isEmpty()); + assertFalse(tester.controller().auditLogger().readLog().entries().isEmpty(), "Actions are logged to audit log"); } @Test - public void testAuditLogApi() { + void testAuditLogApi() { ManualClock clock = new ManualClock(Instant.parse("2019-03-01T12:13:14.00Z")); AuditLogger logger = new AuditLogger(tester.controller().curator(), clock); @@ -153,13 +153,13 @@ public class ControllerApiTest extends ControllerContainerTest { } @Test - public void testMeteringApi() { + void testMeteringApi() { ApplicationId applicationId = ApplicationId.from("tenant", "app", "instance"); Instant timestamp = Instant.ofEpochMilli(123456789); ZoneId zoneId = ZoneId.defaultId(); List<ResourceSnapshot> snapshots = List.of( - new ResourceSnapshot(applicationId, 12,48,1200, NodeResources.Architecture.arm64, timestamp, zoneId), - new ResourceSnapshot(applicationId, 24, 96,2400, NodeResources.Architecture.x86_64, timestamp, zoneId) + new ResourceSnapshot(applicationId, 12, 48, 1200, NodeResources.Architecture.arm64, timestamp, zoneId), + new ResourceSnapshot(applicationId, 24, 96, 2400, NodeResources.Architecture.x86_64, timestamp, zoneId) ); tester.controller().serviceRegistry().resourceDatabase().writeResourceSnapshots(snapshots); tester.assertResponse( @@ -169,24 +169,24 @@ public class ControllerApiTest extends ControllerContainerTest { } @Test - public void testApproveMembership() { + void testApproveMembership() { ApplicationId applicationId = ApplicationId.from("tenant", "app", "instance"); DeploymentId deployment = new DeploymentId(applicationId, ZoneId.defaultId()); String requestBody = "{\n" + - " \"applicationId\": \"" + deployment.applicationId().serializedForm() + "\",\n" + - " \"zone\": \"" + deployment.zoneId().value() + "\"\n" + - "}"; + " \"applicationId\": \"" + deployment.applicationId().serializedForm() + "\",\n" + + " \"zone\": \"" + deployment.zoneId().value() + "\"\n" + + "}"; MockAccessControlService accessControlService = (MockAccessControlService) tester.serviceRegistry().accessControlService(); - tester.assertResponse(operatorRequest("http://localhost:8080/controller/v1/access/requests/"+hostedOperator.getName(), requestBody, Request.Method.POST), - "{\"message\":\"Unable to approve membership request\"}", 400); + tester.assertResponse(operatorRequest("http://localhost:8080/controller/v1/access/requests/" + hostedOperator.getName(), requestBody, Request.Method.POST), + "{\"message\":\"Unable to approve membership request\"}", 400); accessControlService.addPendingMember(hostedOperator); - tester.assertResponse(operatorRequest("http://localhost:8080/controller/v1/access/requests/"+hostedOperator.getName(), requestBody, Request.Method.POST), - "{\"message\":\"Unable to approve membership request\"}", 400); + tester.assertResponse(operatorRequest("http://localhost:8080/controller/v1/access/requests/" + hostedOperator.getName(), requestBody, Request.Method.POST), + "{\"message\":\"Unable to approve membership request\"}", 400); tester.controller().supportAccess().allow(deployment, tester.controller().clock().instant().plus(Duration.ofHours(1)), "tenantx"); - tester.assertResponse(operatorRequest("http://localhost:8080/controller/v1/access/requests/"+hostedOperator.getName(), requestBody, Request.Method.POST), - "{\"members\":[\"user.alice\"]}"); + tester.assertResponse(operatorRequest("http://localhost:8080/controller/v1/access/requests/" + hostedOperator.getName(), requestBody, Request.Method.POST), + "{\"members\":[\"user.alice\"]}"); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json index 673767c13a0..44b52e5be2c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json @@ -2,6 +2,8 @@ "zones": [ { "id": "prod.us-east-3", + "totalCost": 0.0, + "totalAllocatedCost": 0.0, "load": { "cpu": 0.0, "memory": 0.0, @@ -27,6 +29,8 @@ }, { "id": "prod.us-west-1", + "totalCost": 0.0, + "totalAllocatedCost": 0.0, "load": { "cpu": 0.0, "memory": 0.0, diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java index d99239e8f23..5dd57b09af4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java @@ -9,7 +9,7 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; @@ -24,30 +24,30 @@ public class BadgeApiTest extends ControllerContainerTest { private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/"; @Test - public void testBadgeApi() throws IOException { + void testBadgeApi() throws IOException { ContainerTester tester = new ContainerTester(container, responseFiles); DeploymentTester deploymentTester = new DeploymentTester(new ControllerTester(tester)); deploymentTester.controllerTester().upgradeSystem(Version.fromString("6.1")); var application = deploymentTester.newDeploymentContext("tenant", "application", "default"); ApplicationPackage applicationPackage = new ApplicationPackageBuilder().parallel("us-west-1", "aws-us-east-1a") - .test("us-west-1") - .region("ap-southeast-1") - .test("ap-southeast-1") - .region("eu-west-1") - .test("eu-west-1") - .build(); + .test("us-west-1") + .region("ap-southeast-1") + .test("ap-southeast-1") + .region("eu-west-1") + .test("eu-west-1") + .build(); application.submit(applicationPackage).deploy(); application.submit(applicationPackage) - .runJob(DeploymentContext.systemTest) - .runJob(DeploymentContext.stagingTest) - .runJob(DeploymentContext.productionUsWest1) - .runJob(DeploymentContext.productionAwsUsEast1a) - .runJob(DeploymentContext.testUsWest1) - .runJob(DeploymentContext.productionApSoutheast1) - .failDeployment(DeploymentContext.testApSoutheast1); + .runJob(DeploymentContext.systemTest) + .runJob(DeploymentContext.stagingTest) + .runJob(DeploymentContext.productionUsWest1) + .runJob(DeploymentContext.productionAwsUsEast1a) + .runJob(DeploymentContext.testUsWest1) + .runJob(DeploymentContext.productionApSoutheast1) + .failDeployment(DeploymentContext.testApSoutheast1); application.submit(applicationPackage) - .failTests(DeploymentContext.systemTest, true) - .runJob(DeploymentContext.stagingTest); + .failTests(DeploymentContext.systemTest, true) + .runJob(DeploymentContext.stagingTest); for (int i = 0; i < 31; i++) if ((i & 1) == 0) application.failDeployment(DeploymentContext.productionUsWest1); @@ -58,27 +58,27 @@ public class BadgeApiTest extends ControllerContainerTest { tester.controller().applications().deploymentTrigger().reTrigger(application.instanceId(), DeploymentContext.testEuWest1, "reason"); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default"), - Files.readString(Paths.get(responseFiles + "overview.svg")), 200); + Files.readString(Paths.get(responseFiles + "overview.svg")), 200); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=0"), - Files.readString(Paths.get(responseFiles + "single-running.svg")), 200); + Files.readString(Paths.get(responseFiles + "single-running.svg")), 200); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/system-test"), - Files.readString(Paths.get(responseFiles + "running-test.svg")), 200); + Files.readString(Paths.get(responseFiles + "running-test.svg")), 200); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=32"), - Files.readString(Paths.get(responseFiles + "history.svg")), 200); + Files.readString(Paths.get(responseFiles + "history.svg")), 200); // New change not reflected before cache entry expires. tester.serviceRegistry().clock().advance(Duration.ofSeconds(59)); application.runJob(DeploymentContext.productionUsWest1); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=32"), - Files.readString(Paths.get(responseFiles + "history.svg")), 200); + Files.readString(Paths.get(responseFiles + "history.svg")), 200); // Cached entry refreshed after a minute. tester.serviceRegistry().clock().advance(Duration.ofSeconds(1)); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=32"), - Files.readString(Paths.get(responseFiles + "history2.svg")), 200); + Files.readString(Paths.get(responseFiles + "history2.svg")), 200); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default/production-us-west-1?historyLength=0"), - Files.readString(Paths.get(responseFiles + "single-done.svg")), 200); + Files.readString(Paths.get(responseFiles + "single-done.svg")), 200); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java index d837b9e8264..7ee5f6db9b9 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiTest.java @@ -15,7 +15,7 @@ import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; import com.yahoo.vespa.hosted.controller.versions.NodeVersion; import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.ArrayList; @@ -30,7 +30,7 @@ public class DeploymentApiTest extends ControllerContainerTest { private final static String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/"; @Test - public void testDeploymentApi() { + void testDeploymentApi() { ContainerTester tester = new ContainerTester(container, responseFiles); DeploymentTester deploymentTester = new DeploymentTester(new ControllerTester(tester)); Version version = Version.fromString("4.9"); @@ -43,8 +43,8 @@ public class DeploymentApiTest extends ControllerContainerTest { .region("us-west-1") .build(); ApplicationPackage emptyPackage = new ApplicationPackageBuilder().instances("default") - .allow(ValidationId.deploymentRemoval) - .build(); + .allow(ValidationId.deploymentRemoval) + .build(); // Deploy application without any declared jobs on the oldest version. var oldAppWithoutDeployment = deploymentTester.newDeploymentContext("tenant4", "application4", "default"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java index 577aab0ade6..877ca4a99d2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java @@ -15,13 +15,13 @@ import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFact import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzDbMock; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.net.URI; import java.util.Set; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -48,7 +48,7 @@ public class AthenzRoleFilterTest { private AthenzRoleFilter filter; - @Before + @BeforeEach public void setup() { ControllerTester tester = new ControllerTester(); filter = new AthenzRoleFilter(new AthenzClientFactoryMock(tester.athenzDb()), @@ -67,70 +67,70 @@ public class AthenzRoleFilterTest { } @Test - public void testTranslations() throws Exception { + void testTranslations() throws Exception { // Hosted operators are always members of the hostedOperator role. assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedAccountant(), Role.hostedSupporter()), - filter.roles(HOSTED_OPERATOR, NO_CONTEXT_PATH)); + filter.roles(HOSTED_OPERATOR, NO_CONTEXT_PATH)); assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedAccountant(), Role.hostedSupporter()), - filter.roles(HOSTED_OPERATOR, TENANT_CONTEXT_PATH)); + filter.roles(HOSTED_OPERATOR, TENANT_CONTEXT_PATH)); assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.paymentProcessor(), Role.hostedAccountant(), Role.hostedSupporter()), - filter.roles(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH)); + filter.roles(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH)); // Tenant admins are members of the athenzTenantAdmin role within their tenant subtree. assertEquals(Set.of(Role.everyone()), - filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH)); + filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH)); assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)), - filter.roles(TENANT_ADMIN, TENANT_CONTEXT_PATH)); + filter.roles(TENANT_ADMIN, TENANT_CONTEXT_PATH)); assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)), - filter.roles(TENANT_ADMIN, APPLICATION_CONTEXT_PATH)); + filter.roles(TENANT_ADMIN, APPLICATION_CONTEXT_PATH)); assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)), - filter.roles(TENANT_ADMIN, TENANT2_CONTEXT_PATH)); + filter.roles(TENANT_ADMIN, TENANT2_CONTEXT_PATH)); assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)), - filter.roles(TENANT_ADMIN, APPLICATION2_CONTEXT_PATH)); + filter.roles(TENANT_ADMIN, APPLICATION2_CONTEXT_PATH)); // Build services are members of the buildService role within their application subtree. assertEquals(Set.of(Role.everyone()), - filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH)); + filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH)); assertEquals(Set.of(Role.everyone()), - filter.roles(TENANT_PIPELINE, TENANT_CONTEXT_PATH)); + filter.roles(TENANT_PIPELINE, TENANT_CONTEXT_PATH)); assertEquals(Set.of(Role.buildService(TENANT, APPLICATION)), - filter.roles(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH)); + filter.roles(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH)); assertEquals(Set.of(Role.everyone()), - filter.roles(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH)); + filter.roles(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH)); // Principals member of both tenantPipeline and tenantAdmin roles get correct roles assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)), - filter.roles(TENANT_ADMIN_AND_PIPELINE, TENANT_CONTEXT_PATH)); + filter.roles(TENANT_ADMIN_AND_PIPELINE, TENANT_CONTEXT_PATH)); assertEquals(Set.of(Role.athenzTenantAdmin(TENANT), Role.buildService(TENANT, APPLICATION)), - filter.roles(TENANT_ADMIN_AND_PIPELINE, APPLICATION_CONTEXT_PATH)); + filter.roles(TENANT_ADMIN_AND_PIPELINE, APPLICATION_CONTEXT_PATH)); // Users have nothing special under their instance assertEquals(Set.of(Role.everyone()), - filter.roles(USER, INSTANCE_CONTEXT_PATH)); + filter.roles(USER, INSTANCE_CONTEXT_PATH)); // Unprivileged users are just members of the everyone role. assertEquals(Set.of(Role.everyone()), - filter.roles(USER, NO_CONTEXT_PATH)); + filter.roles(USER, NO_CONTEXT_PATH)); assertEquals(Set.of(Role.everyone()), - filter.roles(USER, TENANT_CONTEXT_PATH)); + filter.roles(USER, TENANT_CONTEXT_PATH)); assertEquals(Set.of(Role.everyone()), - filter.roles(USER, APPLICATION_CONTEXT_PATH)); + filter.roles(USER, APPLICATION_CONTEXT_PATH)); assertEquals(Set.of(Role.everyone()), - filter.roles(USER, INSTANCE2_CONTEXT_PATH)); + filter.roles(USER, INSTANCE2_CONTEXT_PATH)); } } 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 be3a50c38b0..64dce08e735 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 @@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.UncheckedIOException; @@ -20,9 +20,9 @@ import java.util.Set; import static com.yahoo.container.jdisc.RequestHandlerTestDriver.MockResponseHandler; import static com.yahoo.jdisc.http.HttpResponse.Status.FORBIDDEN; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author bjorncs @@ -33,7 +33,7 @@ public class ControllerAuthorizationFilterTest { private static final ObjectMapper mapper = new ObjectMapper(); @Test - public void operator() { + void operator() { ControllerTester tester = new ControllerTester(); SecurityContext securityContext = new SecurityContext(() -> "operator", Set.of(Role.hostedOperator())); ControllerAuthorizationFilter filter = createFilter(tester); @@ -44,7 +44,7 @@ public class ControllerAuthorizationFilterTest { } @Test - public void supporter() { + void supporter() { ControllerTester tester = new ControllerTester(); SecurityContext securityContext = new SecurityContext(() -> "operator", Set.of(Role.hostedSupporter())); ControllerAuthorizationFilter filter = createFilter(tester); @@ -54,7 +54,7 @@ public class ControllerAuthorizationFilterTest { } @Test - public void unprivileged() { + void unprivileged() { ControllerTester tester = new ControllerTester(); SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.everyone())); ControllerAuthorizationFilter filter = createFilter(tester); @@ -65,7 +65,7 @@ public class ControllerAuthorizationFilterTest { } @Test - public void unprivilegedInPublic() { + void unprivilegedInPublic() { ControllerTester tester = new ControllerTester(SystemName.Public); SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.everyone())); @@ -76,7 +76,7 @@ public class ControllerAuthorizationFilterTest { } @Test - public void hostedDeveloper() { + void hostedDeveloper() { ControllerTester tester = new ControllerTester(); TenantName tenantName = TenantName.defaultName(); SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.hostedDeveloper(tenantName))); @@ -88,14 +88,14 @@ public class ControllerAuthorizationFilterTest { } private static void assertIsAllowed(Optional<AuthorizationResponse> response) { - assertFalse("Expected no response from filter, but got \"" + - response.map(r -> r.message + "\" (" + r.statusCode + ")").orElse(""), - response.isPresent()); + assertFalse(response.isPresent(), + "Expected no response from filter, but got \"" + + response.map(r -> r.message + "\" (" + r.statusCode + ")").orElse("")); } private static void assertIsForbidden(Optional<AuthorizationResponse> response) { - assertTrue("Expected a response from filter", response.isPresent()); - assertEquals("Invalid status code", FORBIDDEN, response.get().statusCode); + assertTrue(response.isPresent(), "Expected a response from filter"); + assertEquals(FORBIDDEN, response.get().statusCode, "Invalid status code"); } private static ControllerAuthorizationFilter createFilter(ControllerTester tester) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/LastLoginUpdateFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/LastLoginUpdateFilterTest.java index c7071cfb68c..b85c93a5d90 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/LastLoginUpdateFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/LastLoginUpdateFilterTest.java @@ -8,7 +8,7 @@ import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper; import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.Set; @@ -16,7 +16,7 @@ import java.util.Set; import static com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo.UserLevel.administrator; import static com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo.UserLevel.developer; import static com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo.UserLevel.user; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LastLoginUpdateFilterTest { @@ -27,7 +27,7 @@ public class LastLoginUpdateFilterTest { private final LastLoginUpdateFilter filter = new LastLoginUpdateFilter(tester.controller()); @Test - public void updateLastLoginTimeTest() { + void updateLastLoginTimeTest() { tester.createTenant(tenant1.value()); tester.createTenant(tenant2.value()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java index 9024d7c8e7e..ec9be1f04c3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java @@ -20,8 +20,8 @@ import com.yahoo.vespa.hosted.controller.tenant.ArchiveAccess; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo; import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -34,8 +34,8 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SignatureFilterTest { @@ -63,7 +63,7 @@ public class SignatureFilterTest { private SignatureFilter filter; private RequestSigner signer; - @Before + @BeforeEach public void setup() { tester = new ControllerTester(); applications = tester.controller().applications(); @@ -77,17 +77,18 @@ public class SignatureFilterTest { ImmutableBiMap.of(), TenantInfo.empty(), List.of(), - new ArchiveAccess())); + new ArchiveAccess(), + Optional.empty())); tester.curator().writeApplication(new Application(appId, tester.clock().instant())); } @Test - public void testFilter() { + void testFilter() { // Unsigned request gets no role. HttpRequest.Builder request = HttpRequest.newBuilder(URI.create("https://host:123/path/./..//..%2F?query=empty&%3F=%26")); byte[] emptyBody = new byte[0]; verifySecurityContext(requestOf(request.copy().method("GET", HttpRequest.BodyPublishers.ofByteArray(emptyBody)).build(), emptyBody), - null); + null); // Signed request gets no role when no key is stored for the application. verifySecurityContext(requestOf(signer.signed(request.copy(), Method.GET, InputStream::nullInputStream), emptyBody), @@ -97,42 +98,43 @@ public class SignatureFilterTest { applications.lockApplicationOrThrow(appId, application -> applications.store(application.withDeployKey(otherPublicKey))); // Signed request gets no role when no key is stored for the application. verifySecurityContext(requestOf(signer.signed(request.copy(), Method.GET, InputStream::nullInputStream), emptyBody), - null); + null); // Signed request gets a headless role when a matching key is stored for the application. applications.lockApplicationOrThrow(appId, application -> applications.store(application.withDeployKey(publicKey))); verifySecurityContext(requestOf(signer.signed(request.copy(), Method.GET, InputStream::nullInputStream), emptyBody), - new SecurityContext(new SimplePrincipal("headless@my-tenant.my-app"), - Set.of(Role.reader(id.tenant()), - Role.headless(id.tenant(), id.application())), - tester.clock().instant())); + new SecurityContext(new SimplePrincipal("headless@my-tenant.my-app"), + Set.of(Role.reader(id.tenant()), + Role.headless(id.tenant(), id.application())), + tester.clock().instant())); // Signed POST request with X-Key header gets a headless role. byte[] hiBytes = new byte[]{0x48, 0x69}; verifySecurityContext(requestOf(signer.signed(request.copy(), Method.POST, () -> new ByteArrayInputStream(hiBytes)), hiBytes), - new SecurityContext(new SimplePrincipal("headless@my-tenant.my-app"), - Set.of(Role.reader(id.tenant()), - Role.headless(id.tenant(), id.application())), - tester.clock().instant())); + new SecurityContext(new SimplePrincipal("headless@my-tenant.my-app"), + Set.of(Role.reader(id.tenant()), + Role.headless(id.tenant(), id.application())), + tester.clock().instant())); // Signed request gets a developer role when a matching developer key is stored for the tenant. tester.curator().writeTenant(new CloudTenant(appId.tenant(), - Instant.EPOCH, - LastLoginInfo.EMPTY, - Optional.empty(), - ImmutableBiMap.of(publicKey, () -> "user"), - TenantInfo.empty(), - List.of(), - new ArchiveAccess())); + Instant.EPOCH, + LastLoginInfo.EMPTY, + Optional.empty(), + ImmutableBiMap.of(publicKey, () -> "user"), + TenantInfo.empty(), + List.of(), + new ArchiveAccess(), + Optional.empty())); verifySecurityContext(requestOf(signer.signed(request.copy(), Method.POST, () -> new ByteArrayInputStream(hiBytes)), hiBytes), - new SecurityContext(new SimplePrincipal("user"), - Set.of(Role.reader(id.tenant()), - Role.developer(id.tenant())), - tester.clock().instant())); + new SecurityContext(new SimplePrincipal("user"), + Set.of(Role.reader(id.tenant()), + Role.developer(id.tenant())), + tester.clock().instant())); // Unsigned requests still get no roles. verifySecurityContext(requestOf(request.copy().method("GET", HttpRequest.BodyPublishers.ofByteArray(emptyBody)).build(), emptyBody), - null); + null); } private void verifySecurityContext(DiscFilterRequest request, SecurityContext securityContext) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java index 6d8826f6e48..ee89f506f17 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/flags/AuditedFlagsApiTest.java @@ -7,10 +7,10 @@ import com.yahoo.vespa.athenz.api.AthenzUser; import com.yahoo.vespa.hosted.controller.auditlog.AuditLog; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author mpolden @@ -22,24 +22,24 @@ public class AuditedFlagsApiTest extends ControllerContainerTest { private ContainerTester tester; - @Before + @BeforeEach public void before() { addUserToHostedOperatorRole(operator); tester = new ContainerTester(container, responses); } @Test - public void test_audit_logging() { + void test_audit_logging() { var body = "{\n" + - " \"id\": \"id1\",\n" + - " \"rules\": [\n" + - " {\n" + - " \"value\": true\n" + - " }\n" + - " ]\n" + - "}"; + " \"id\": \"id1\",\n" + + " \"rules\": [\n" + + " {\n" + + " \"value\": true\n" + + " }\n" + + " ]\n" + + "}"; assertResponse(new Request("http://localhost:8080/flags/v1/data/id1?force=true", body, Request.Method.PUT), - "", 200); + "", 200); var log = tester.controller().auditLogger().readLog(); assertEquals(1, log.entries().size()); var entry = log.entries().get(0); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/HorizonApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/HorizonApiTest.java index 96b08006790..fabae508057 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/HorizonApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/HorizonApiTest.java @@ -8,7 +8,7 @@ import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; @@ -18,7 +18,7 @@ import java.util.Set; public class HorizonApiTest extends ControllerContainerCloudTest { @Test - public void only_operators_and_flag_enabled_tenants_allowed() { + void only_operators_and_flag_enabled_tenants_allowed() { ContainerTester tester = new ContainerTester(container, ""); TenantName tenantName = TenantName.defaultName(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/TsdbQueryRewriterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/TsdbQueryRewriterTest.java index 62ec8f4222c..d0d169720d0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/TsdbQueryRewriterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/horizon/TsdbQueryRewriterTest.java @@ -5,7 +5,7 @@ import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; import com.yahoo.slime.JsonFormat; import com.yahoo.slime.SlimeUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -16,7 +16,7 @@ import java.util.Set; import static com.yahoo.slime.SlimeUtils.jsonToSlimeOrThrow; import static com.yahoo.slime.SlimeUtils.toJsonBytes; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author valerijf @@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals; public class TsdbQueryRewriterTest { @Test - public void rewrites_query() throws IOException { + void rewrites_query() throws IOException { assertRewrite("filters-complex.json", "filters-complex.expected.json", Set.of(TenantName.from("tenant2")), false); assertRewrite("filter-in-execution-graph.json", 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 15e7a804143..15f0100ade8 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 @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.restapi.os; import com.yahoo.application.container.handler.Request; +import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.UpgradePolicy; @@ -11,25 +12,25 @@ import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzUser; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.OsRelease; import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; -import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; import com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintainer; import com.yahoo.vespa.hosted.controller.maintenance.OsUpgrader; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; import com.yahoo.vespa.hosted.controller.versions.OsVersionStatus; import org.intellij.lang.annotations.Language; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.time.Duration; import java.time.Instant; import java.util.List; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author mpolden @@ -52,15 +53,18 @@ public class OsApiTest extends ControllerContainerTest { return SystemName.cd; } - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responses); tester.serviceRegistry().clock().setInstant(Instant.ofEpochMilli(1234)); addUserToHostedOperatorRole(operator); - zoneRegistryMock().setZones(zone1, zone2, zone3) - .reprovisionToUpgradeOsIn(zone3) - .setOsUpgradePolicy(cloud1, UpgradePolicy.builder().upgrade(zone1).upgrade(zone2).build()) - .setOsUpgradePolicy(cloud2, UpgradePolicy.builder().upgrade(zone3).build()); + tester.serviceRegistry().zoneRegistry().setZones(zone1, zone2, zone3) + .reprovisionToUpgradeOsIn(zone3) + .setOsUpgradePolicy(cloud1, UpgradePolicy.builder().upgrade(zone1).upgrade(zone2).build()) + .setOsUpgradePolicy(cloud2, UpgradePolicy.builder().upgrade(zone3).build()); + tester.serviceRegistry().artifactRepository().addRelease(new OsRelease(Version.fromString("7.0"), + OsRelease.Tag.latest, + Instant.EPOCH)); osUpgraders = List.of( new OsUpgrader(tester.controller(), Duration.ofDays(1), cloud1), @@ -69,7 +73,7 @@ public class OsApiTest extends ControllerContainerTest { } @Test - public void test_api() { + void test_api() { // No versions available yet assertResponse(new Request("http://localhost:8080/os/v1/"), "{\"versions\":[]}", 200); @@ -77,9 +81,9 @@ public class OsApiTest extends ControllerContainerTest { upgradeAndUpdateStatus(); // Upgrade OS to a different version in each cloud assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"7.5.2\", \"cloud\": \"cloud1\", \"upgradeBudget\": \"PT0S\"}", Request.Method.PATCH), - "{\"message\":\"Set target OS version for cloud 'cloud1' to 7.5.2 with upgrade budget PT0S\"}", 200); + "{\"message\":\"Set target OS version for cloud 'cloud1' to 7.5.2 with upgrade budget PT0S\"}", 200); assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"8.2.1\", \"cloud\": \"cloud2\", \"upgradeBudget\": \"PT24H\"}", Request.Method.PATCH), - "{\"message\":\"Set target OS version for cloud 'cloud2' to 8.2.1 with upgrade budget PT24H\"}", 200); + "{\"message\":\"Set target OS version for cloud 'cloud2' to 8.2.1 with upgrade budget PT24H\"}", 200); // Status is updated after some zones are upgraded upgradeAndUpdateStatus(); @@ -93,49 +97,49 @@ public class OsApiTest extends ControllerContainerTest { // Downgrade with force is permitted assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"7.5.1\", \"cloud\": \"cloud1\", \"force\": true, \"upgradeBudget\": \"PT0S\"}", Request.Method.PATCH), - "{\"message\":\"Set target OS version for cloud 'cloud1' to 7.5.1 with upgrade budget PT0S\"}", 200); + "{\"message\":\"Set target OS version for cloud 'cloud1' to 7.5.1 with upgrade budget PT0S\"}", 200); // Clear target for a given cloud assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": null, \"cloud\": \"cloud2\"}", Request.Method.PATCH), - "{\"message\":\"Cleared target OS version for cloud 'cloud2'\"}", 200); + "{\"message\":\"Cleared target OS version for cloud 'cloud2'\"}", 200); // Error: Missing fields assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"7.6\"}", Request.Method.PATCH), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Field 'cloud' is required\"}", 400); + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Field 'cloud' is required\"}", 400); assertResponse(new Request("http://localhost:8080/os/v1/", "{\"cloud\": \"cloud1\"}", Request.Method.PATCH), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Field 'version' is required\"}", 400); + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Field 'version' is required\"}", 400); // Error: Invalid versions assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"0.0.0\", \"cloud\": \"cloud1\", \"upgradeBudget\": \"PT0S\"}", Request.Method.PATCH), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid version '0.0.0'\"}", 400); + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid version '0.0.0'\"}", 400); assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"foo\", \"cloud\": \"cloud1\", \"upgradeBudget\": \"PT0S\"}", Request.Method.PATCH), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid version 'foo': For input string: \\\"foo\\\"\"}", 400); + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid version 'foo': For input string: \\\"foo\\\"\"}", 400); // Error: Invalid cloud assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"7.6\", \"cloud\": \"foo\", \"upgradeBudget\": \"PT0S\"}", Request.Method.PATCH), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cloud 'foo' does not exist in this system\"}", 400); + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cloud 'foo' does not exist in this system\"}", 400); // Error: Downgrade OS assertResponse(new Request("http://localhost:8080/os/v1/", "{\"version\": \"7.4.1\", \"cloud\": \"cloud1\", \"upgradeBudget\": \"PT0S\"}", Request.Method.PATCH), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cannot downgrade cloud 'cloud1' to version 7.4.1\"}", 400); + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cannot downgrade cloud 'cloud1' to version 7.4.1\"}", 400); // Request firmware checks in all zones. assertResponse(new Request("http://localhost:8080/os/v1/firmware/", "", Request.Method.POST), - "{\"message\":\"Requested firmware checks in prod.us-east-3, prod.us-west-1, prod.eu-west-1.\"}", 200); + "{\"message\":\"Requested firmware checks in prod.us-east-3, prod.us-west-1, prod.eu-west-1.\"}", 200); // Cancel firmware checks in all prod zones. assertResponse(new Request("http://localhost:8080/os/v1/firmware/prod/", "", Request.Method.DELETE), - "{\"message\":\"Cancelled firmware checks in prod.us-east-3, prod.us-west-1, prod.eu-west-1.\"}", 200); + "{\"message\":\"Cancelled firmware checks in prod.us-east-3, prod.us-west-1, prod.eu-west-1.\"}", 200); // Request firmware checks in prod.us-east-3. assertResponse(new Request("http://localhost:8080/os/v1/firmware/prod/us-east-3", "", Request.Method.POST), - "{\"message\":\"Requested firmware checks in prod.us-east-3.\"}", 200); + "{\"message\":\"Requested firmware checks in prod.us-east-3.\"}", 200); // Error: Cancel firmware checks in an empty set of zones. assertResponse(new Request("http://localhost:8080/os/v1/firmware/dev/", "", Request.Method.DELETE), - "{\"error-code\":\"NOT_FOUND\",\"message\":\"No zones at path '/os/v1/firmware/dev/'\"}", 404); + "{\"error-code\":\"NOT_FOUND\",\"message\":\"No zones at path '/os/v1/firmware/dev/'\"}", 404); - assertFalse("Actions are logged to audit log", tester.controller().auditLogger().readLog().entries().isEmpty()); + assertFalse(tester.controller().auditLogger().readLog().entries().isEmpty(), "Actions are logged to audit log"); } private void upgradeAndUpdateStatus() { @@ -160,10 +164,6 @@ public class OsApiTest extends ControllerContainerTest { updateVersionStatus(); } - private ZoneRegistryMock zoneRegistryMock() { - return tester.serviceRegistry().zoneRegistry(); - } - private NodeRepositoryMock nodeRepository() { return tester.serviceRegistry().configServerMock().nodeRepository(); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/responses/versions-all-upgraded.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/responses/versions-all-upgraded.json index a5af4f45370..be94b85f113 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/responses/versions-all-upgraded.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/os/responses/versions-all-upgraded.json @@ -104,6 +104,8 @@ "targetVersion": true, "upgradeBudget": "PT24H", "scheduledAt": 1234, + "nextVersion": "8.2.1.20211227", + "nextScheduledAt": 7200000, "cloud": "cloud2", "nodes": [ { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java index ea7ec7c1589..cb402d700e2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java @@ -14,13 +14,13 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.List; -import static org.junit.Assert.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; /** * @author mpolden @@ -32,7 +32,7 @@ public class RoutingApiTest extends ControllerContainerTest { private ContainerTester tester; private DeploymentTester deploymentTester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); deploymentTester = new DeploymentTester(new ControllerTester(tester)); @@ -40,7 +40,7 @@ public class RoutingApiTest extends ControllerContainerTest { } @Test - public void discovery() { + void discovery() { // Deploy var context = deploymentTester.newDeploymentContext("t1", "a1", "default"); var westZone = ZoneId.from("prod", "us-west-1"); @@ -54,34 +54,34 @@ public class RoutingApiTest extends ControllerContainerTest { // GET root tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/", "", - Request.Method.GET), - new File("discovery/root.json")); + Request.Method.GET), + new File("discovery/root.json")); // GET tenant tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1", "", - Request.Method.GET), - new File("discovery/tenant.json")); + Request.Method.GET), + new File("discovery/tenant.json")); // GET application tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1/", - "", - Request.Method.GET), - new File("discovery/application.json")); + "", + Request.Method.GET), + new File("discovery/application.json")); // GET instance tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1/instance/default/", - "", - Request.Method.GET), - new File("discovery/instance.json")); + "", + Request.Method.GET), + new File("discovery/instance.json")); // GET environment tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/", "", - Request.Method.GET), - new File("discovery/environment.json")); + Request.Method.GET), + new File("discovery/environment.json")); } @Test - public void recursion() { + void recursion() { var context1 = deploymentTester.newDeploymentContext("t1", "a1", "default"); var westZone = ZoneId.from("prod", "us-west-1"); var eastZone = ZoneId.from("prod", "us-east-3"); @@ -102,33 +102,33 @@ public class RoutingApiTest extends ControllerContainerTest { // GET tenant recursively tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1?recursive=true", "", - Request.Method.GET), - new File("recursion/tenant.json")); + Request.Method.GET), + new File("recursion/tenant.json")); // GET application recursively tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1?recursive=true", "", - Request.Method.GET), - new File("recursion/application.json")); + Request.Method.GET), + new File("recursion/application.json")); // GET instance recursively tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1/instance/default?recursive=true", "", - Request.Method.GET), - new File("recursion/application.json")); + Request.Method.GET), + new File("recursion/application.json")); // GET environment recursively tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment?recursive=true", "", - Request.Method.GET), - new File("recursion/environment.json")); + Request.Method.GET), + new File("recursion/environment.json")); } @Test - public void exclusive_routing() { + void exclusive_routing() { var context = deploymentTester.newDeploymentContext(); // Zones support direct routing var westZone = ZoneId.from("prod", "us-west-1"); var eastZone = ZoneId.from("prod", "us-east-3"); deploymentTester.controllerTester().zoneRegistry().exclusiveRoutingIn(ZoneApiMock.from(westZone), - ZoneApiMock.from(eastZone)); + ZoneApiMock.from(eastZone)); // Deploy application var applicationPackage = new ApplicationPackageBuilder() .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service")) @@ -140,45 +140,45 @@ public class RoutingApiTest extends ControllerContainerTest { // GET initial deployment status tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("policy/deployment-status-initial.json")); + "", Request.Method.GET), + new File("policy/deployment-status-initial.json")); // POST sets deployment out tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.POST), - "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to OUT\"}"); + "", Request.Method.POST), + "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to OUT\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("policy/deployment-status-out.json")); + "", Request.Method.GET), + new File("policy/deployment-status-out.json")); // DELETE sets deployment in tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.DELETE), - "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to IN\"}"); + "", Request.Method.DELETE), + "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to IN\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("policy/deployment-status-in.json")); + "", Request.Method.GET), + new File("policy/deployment-status-in.json")); // GET initial zone status tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("policy/zone-status-initial.json")); + "", Request.Method.GET), + new File("policy/zone-status-initial.json")); // POST sets zone out tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/environment/prod/region/us-west-1", - "", Request.Method.POST), - "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to OUT\"}"); + "", Request.Method.POST), + "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to OUT\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("policy/zone-status-out.json")); + "", Request.Method.GET), + new File("policy/zone-status-out.json")); // DELETE sets zone in tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/environment/prod/region/us-west-1", - "", Request.Method.DELETE), - "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to IN\"}"); + "", Request.Method.DELETE), + "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to IN\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("policy/zone-status-in.json")); + "", Request.Method.GET), + new File("policy/zone-status-in.json")); // Endpoint is removed applicationPackage = new ApplicationPackageBuilder() @@ -191,12 +191,12 @@ public class RoutingApiTest extends ControllerContainerTest { // GET deployment status. Now empty as no routing policies have global endpoints tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - "{\"deployments\":[]}"); + "", Request.Method.GET), + "{\"deployments\":[]}"); } @Test - public void shared_routing() { + void shared_routing() { // Deploy application var context = deploymentTester.newDeploymentContext(); var westZone = ZoneId.from("prod", "us-west-1"); @@ -208,61 +208,61 @@ public class RoutingApiTest extends ControllerContainerTest { .build(); context.submit(applicationPackage).deploy(); - assertNotEquals("Rotation is assigned", List.of(), context.instance().rotations()); + assertNotEquals(List.of(), context.instance().rotations(), "Rotation is assigned"); // GET initial deployment status tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/deployment-status-initial.json")); + "", Request.Method.GET), + new File("rotation/deployment-status-initial.json")); // POST sets deployment out tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.POST), - "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to OUT\"}"); + "", Request.Method.POST), + "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to OUT\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/deployment-status-out.json")); + "", Request.Method.GET), + new File("rotation/deployment-status-out.json")); // DELETE sets deployment in tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.DELETE), - "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to IN\"}"); + "", Request.Method.DELETE), + "{\"message\":\"Set global routing status for tenant.application in prod.us-west-1 to IN\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/deployment-status-in.json")); + "", Request.Method.GET), + new File("rotation/deployment-status-in.json")); // GET initial zone status tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/zone-status-initial.json")); + "", Request.Method.GET), + new File("rotation/zone-status-initial.json")); // POST sets zone out tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/environment/prod/region/us-west-1", - "", Request.Method.POST), - "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to OUT\"}"); + "", Request.Method.POST), + "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to OUT\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/zone-status-out.json")); + "", Request.Method.GET), + new File("rotation/zone-status-out.json")); // DELETE sets zone in tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/environment/prod/region/us-west-1", - "", Request.Method.DELETE), - "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to IN\"}"); + "", Request.Method.DELETE), + "{\"message\":\"Set global routing status for deployments in prod.us-west-1 to IN\"}"); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/zone-status-in.json")); + "", Request.Method.GET), + new File("rotation/zone-status-in.json")); } @Test - public void mixed_routing_multiple_zones() { + void mixed_routing_multiple_zones() { var westZone = ZoneId.from("prod", "us-west-1"); var eastZone = ZoneId.from("prod", "us-east-3"); // One shared and one exclusive zone deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(westZone), - RoutingMethod.sharedLayer4); + RoutingMethod.sharedLayer4); deploymentTester.controllerTester().zoneRegistry().setRoutingMethod(ZoneApiMock.from(eastZone), - RoutingMethod.exclusive); + RoutingMethod.exclusive); // Deploy application var context = deploymentTester.newDeploymentContext(); @@ -277,23 +277,23 @@ public class RoutingApiTest extends ControllerContainerTest { // GET status for zone using sharedLayer4 routing tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - new File("rotation/deployment-status-initial.json")); + "", Request.Method.GET), + new File("rotation/deployment-status-initial.json")); // GET status for zone using exclusive routing tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-east-3", - "", Request.Method.GET), - "{\"deployments\":[{\"routingMethod\":\"exclusive\",\"instance\":\"tenant:application:default\"," + - "\"environment\":\"prod\",\"region\":\"us-east-3\",\"status\":\"in\",\"agent\":\"system\",\"changedAt\":0}]}"); + "", Request.Method.GET), + "{\"deployments\":[{\"routingMethod\":\"exclusive\",\"instance\":\"tenant:application:default\"," + + "\"environment\":\"prod\",\"region\":\"us-east-3\",\"status\":\"in\",\"agent\":\"system\",\"changedAt\":0}]}"); } @Test - public void invalid_requests() { + void invalid_requests() { // GET non-existent application tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"t1.a1 not found\"}", - 400); + "", Request.Method.GET), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"t1.a1 not found\"}", + 400); // GET, DELETE non-existent deployment var context = deploymentTester.newDeploymentContext(); @@ -303,23 +303,23 @@ public class RoutingApiTest extends ControllerContainerTest { .build(); context.submit(applicationPackage).deploy(); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.GET), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such deployment: tenant.application in prod.us-west-1\"}", - 400); + "", Request.Method.GET), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such deployment: tenant.application in prod.us-west-1\"}", + 400); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/inactive/tenant/tenant/application/application/instance/default/environment/prod/region/us-west-1", - "", Request.Method.DELETE), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such deployment: tenant.application in prod.us-west-1\"}", - 400); + "", Request.Method.DELETE), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such deployment: tenant.application in prod.us-west-1\"}", + 400); // GET non-existent zone tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/environment/prod/region/us-north-1", - "", Request.Method.GET), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such zone: prod.us-north-1\"}", - 400); + "", Request.Method.GET), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No such zone: prod.us-north-1\"}", + 400); } @Test - public void endpoints_list() { + void endpoints_list() { var context = deploymentTester.newDeploymentContext("t1", "a1", "default"); var westZone = ZoneId.from("prod", "us-west-1"); var eastZone = ZoneId.from("prod", "us-east-3"); @@ -331,7 +331,7 @@ public class RoutingApiTest extends ControllerContainerTest { context.submit(applicationPackage).deploy(); tester.assertResponse(operatorRequest("http://localhost:8080/routing/v1/status/tenant/t1/application/a1/instance/default/endpoint", "", Request.Method.GET), - new File("endpoint/endpoints.json")); + new File("endpoint/endpoints.json")); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResultTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResultTest.java index c157cb3516f..36679e0dd91 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResultTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployResultTest.java @@ -7,7 +7,7 @@ import com.yahoo.vespa.hosted.controller.api.systemflags.v1.FlagsTarget; import com.yahoo.vespa.hosted.controller.api.systemflags.v1.wire.WireSystemFlagsDeployResult; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; @@ -21,7 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class SystemFlagsDeployResultTest { @Test - public void changes_and_errors_are_present_in_wire_format() { + void changes_and_errors_are_present_in_wire_format() { FlagsTarget controllerTarget = FlagsTarget.forController(SystemName.cd); FlagId flagOne = new FlagId("flagone"); FlagId flagTwo = new FlagId("flagtwo"); @@ -40,7 +40,7 @@ public class SystemFlagsDeployResultTest { } @Test - public void identical_errors_and_changes_from_multiple_targets_are_merged() { + void identical_errors_and_changes_from_multiple_targets_are_merged() { ZoneApiMock prodUsWest1Zone = ZoneApiMock.fromId("prod.us-west-1"); ZoneRegistryMock registry = new ZoneRegistryMock(SystemName.cd).setZones(prodUsWest1Zone); FlagsTarget prodUsWest1Target = FlagsTarget.forConfigServer(registry, prodUsWest1Zone.getId()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java index d92b0be0eb9..50354639f6f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/systemflags/SystemFlagsDeployerTest.java @@ -8,7 +8,7 @@ import com.yahoo.vespa.hosted.controller.api.systemflags.v1.FlagsTarget; import com.yahoo.vespa.hosted.controller.api.systemflags.v1.SystemFlagsDataArchive; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.UncheckedIOException; @@ -43,7 +43,7 @@ public class SystemFlagsDeployerTest { private final FlagsTarget prodUsEast3Target = FlagsTarget.forConfigServer(registry, prodUsEast3Zone.getId()); @Test - public void deploys_flag_data_to_targets() throws IOException { + void deploys_flag_data_to_targets() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(controllerTarget)).thenReturn(List.of()); when(flagsClient.listFlagData(prodUsWest1Target)).thenReturn(List.of(flagData("existing-prod.us-west-1.json"))); @@ -74,7 +74,7 @@ public class SystemFlagsDeployerTest { } @Test - public void dryrun_should_not_change_flags() throws IOException { + void dryrun_should_not_change_flags() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(controllerTarget)).thenReturn(List.of()); when(flagsClient.listDefinedFlags(controllerTarget)).thenReturn(List.of(new FlagId("my-flag"))); @@ -97,7 +97,7 @@ public class SystemFlagsDeployerTest { } @Test - public void creates_error_entries_in_result_if_flag_data_operations_fail() throws IOException { + void creates_error_entries_in_result_if_flag_data_operations_fail() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); UncheckedIOException exception = new UncheckedIOException(new IOException("I/O error message")); when(flagsClient.listFlagData(prodUsWest1Target)).thenThrow(exception); @@ -120,7 +120,7 @@ public class SystemFlagsDeployerTest { } @Test - public void creates_error_entry_for_invalid_flag_archive() throws IOException { + void creates_error_entry_for_invalid_flag_archive() throws IOException { FlagsClient flagsClient = mock(FlagsClient.class); FlagData defaultData = flagData("flags/my-flag/main.json"); SystemFlagsDataArchive archive = new SystemFlagsDataArchive.Builder() @@ -135,7 +135,7 @@ public class SystemFlagsDeployerTest { } @Test - public void creates_error_entry_for_flag_data_of_undefined_flag() throws IOException { + void creates_error_entry_for_flag_data_of_undefined_flag() throws IOException { FlagData prodUsEast3Data = flagData("flags/my-flag/main.prod.us-east-3.json"); FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(prodUsEast3Target)) @@ -154,7 +154,7 @@ public class SystemFlagsDeployerTest { } @Test - public void creates_warning_entry_for_existing_flag_data_for_undefined_flag() throws IOException { + void creates_warning_entry_for_existing_flag_data_for_undefined_flag() throws IOException { FlagData prodUsEast3Data = flagData("flags/my-flag/main.prod.us-east-3.json"); FlagsClient flagsClient = mock(FlagsClient.class); when(flagsClient.listFlagData(prodUsEast3Target)) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiOnPremTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiOnPremTest.java index 1cd4b5e5541..f7e270b3c68 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiOnPremTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiOnPremTest.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.jdisc.http.filter.security.misc.User; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.HashMap; @@ -26,7 +26,7 @@ public class UserApiOnPremTest extends ControllerContainerTest { private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/"; @Test - public void userMetadataOnPremTest() { + void userMetadataOnPremTest() { try (Flags.Replacer ignored = Flags.clearFlagsForTesting(PermanentFlags.MAX_TRIAL_TENANTS.id(), PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.id())) { ContainerTester tester = new ContainerTester(container, responseFiles); ControllerTester controller = new ControllerTester(tester); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java index 1bb409b9906..1344b106bbe 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java @@ -14,7 +14,7 @@ import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; import com.yahoo.vespa.hosted.controller.tenant.Tenant; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.Set; @@ -22,7 +22,7 @@ import java.util.Set; import static com.yahoo.application.container.handler.Request.Method.DELETE; import static com.yahoo.application.container.handler.Request.Method.POST; import static com.yahoo.application.container.handler.Request.Method.PUT; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -42,7 +42,7 @@ public class UserApiTest extends ControllerContainerCloudTest { @Test - public void testUserManagement() { + void testUserManagement() { ContainerTester tester = new ContainerTester(container, responseFiles); assertEquals(SystemName.Public, tester.controller().system()); Set<Role> operator = Set.of(Role.hostedOperator()); @@ -51,25 +51,25 @@ public class UserApiTest extends ControllerContainerCloudTest { // GET at application/v4 root fails as it's not public read. tester.assertResponse(request("/application/v4/"), - accessDenied, 403); + accessDenied, 403); // GET at application/v4/tenant succeeds for operators. tester.assertResponse(request("/application/v4/tenant") - .roles(operator), - "[]"); + .roles(operator), + "[]"); // POST a tenant is not available to everyone. tester.assertResponse(request("/application/v4/tenant/my-tenant", POST) - .data("{\"token\":\"hello\"}"), - "{\"error-code\":\"FORBIDDEN\",\"message\":\"You are not currently permitted to create tenants. Please contact the Vespa team to request access.\"}", 403); + .data("{\"token\":\"hello\"}"), + "{\"error-code\":\"FORBIDDEN\",\"message\":\"You are not currently permitted to create tenants. Please contact the Vespa team to request access.\"}", 403); // POST a tenant is available to operators. tester.assertResponse(request("/application/v4/tenant/my-tenant", POST) - .roles(operator) - .principal("administrator@tenant") - .user(new User("administrator@tenant", "administrator", "admin", "picture")) - .data("{\"token\":\"hello\"}"), - new File("tenant-without-applications.json")); + .roles(operator) + .principal("administrator@tenant") + .user(new User("administrator@tenant", "administrator", "admin", "picture")) + .data("{\"token\":\"hello\"}"), + new File("tenant-without-applications.json")); // GET at tenant/info with contact information. tester.assertResponse(request("/application/v4/tenant/my-tenant/info") @@ -79,97 +79,80 @@ public class UserApiTest extends ControllerContainerCloudTest { // GET at user/v1 root fails as no access control is defined there. tester.assertResponse(request("/user/v1/"), - accessDenied, 403); + accessDenied, 403); // POST a hosted operator role is not allowed. tester.assertResponse(request("/user/v1/tenant/my-tenant", POST) - .roles(Set.of(Role.administrator(id.tenant()))) - .data("{\"user\":\"evil@evil\",\"roleName\":\"hostedOperator\"}"), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'hostedOperator'.\"}", 400); + .roles(Set.of(Role.administrator(id.tenant()))) + .data("{\"user\":\"evil@evil\",\"roles\":[\"hostedOperator\"]}"), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'hostedOperator'.\"}", 400); // POST a tenant developer is available to the tenant owner. tester.assertResponse(request("/user/v1/tenant/my-tenant", POST) - .roles(Set.of(Role.administrator(id.tenant()))) - .data("{\"user\":\"developer@tenant\",\"roles\":[\"developer\",\"reader\"]}"), - "{\"message\":\"user 'developer@tenant' is now a member of role 'developer' of 'my-tenant', role 'reader' of 'my-tenant'\"}"); + .roles(Set.of(Role.administrator(id.tenant()))) + .data("{\"user\":\"developer@tenant\",\"roles\":[\"developer\",\"reader\"]}"), + "{\"message\":\"user 'developer@tenant' is now a member of role 'developer' of 'my-tenant', role 'reader' of 'my-tenant'\"}"); // POST a tenant admin is not available to a tenant developer. tester.assertResponse(request("/user/v1/tenant/my-tenant", POST) - .roles(Set.of(Role.developer(id.tenant()))) - .data("{\"user\":\"developer@tenant\",\"roleName\":\"administrator\"}"), - accessDenied, 403); - - // POST a headless for a non-existent application fails. - tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST) - .roles(Set.of(Role.administrator(TenantName.from("my-tenant")))) - .data("{\"user\":\"headless@app\",\"roleName\":\"headless\"}"), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"role 'headless' of 'my-app' owned by 'my-tenant' not found\"}", 400); + .roles(Set.of(Role.developer(id.tenant()))) + .data("{\"user\":\"developer@tenant\",\"roles\":[\"administrator\"]}"), + accessDenied, 403); // POST an application is allowed for a tenant developer. tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app", POST) - .principal("developer@tenant") - .roles(Set.of(Role.developer(id.tenant()))), - new File("application-created.json")); + .principal("developer@tenant") + .roles(Set.of(Role.developer(id.tenant()))), + new File("application-created.json")); // POST an application is not allowed under a different tenant. tester.assertResponse(request("/application/v4/tenant/other-tenant/application/my-app", POST) - .roles(Set.of(Role.administrator(id.tenant()))), - accessDenied, 403); - - // POST a tenant role is not allowed to an application. - tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST) - .roles(Set.of(Role.hostedOperator())) - .data("{\"user\":\"developer@app\",\"roleName\":\"developer\"}"), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'developer'.\"}", 400); + .roles(Set.of(Role.administrator(id.tenant()))), + accessDenied, 403); // GET tenant role information is available to readers. tester.assertResponse(request("/user/v1/tenant/my-tenant") - .roles(Set.of(Role.reader(id.tenant()))), - new File("tenant-roles.json")); - - // GET application role information is available to tenant administrators. - tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app") - .roles(Set.of(Role.administrator(id.tenant()))), - new File("application-roles.json")); + .roles(Set.of(Role.reader(id.tenant()))), + new File("tenant-roles.json")); // POST a pem deploy key tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app/key", POST) - .roles(Set.of(Role.developer(id.tenant()))) - .data("{\"key\":\"" + pemPublicKey + "\"}"), - new File("first-deploy-key.json")); + .roles(Set.of(Role.developer(id.tenant()))) + .data("{\"key\":\"" + pemPublicKey + "\"}"), + new File("first-deploy-key.json")); // POST a pem developer key tester.assertResponse(request("/application/v4/tenant/my-tenant/key", POST) - .principal("joe@dev") - .roles(Set.of(Role.developer(id.tenant()))) - .data("{\"key\":\"" + pemPublicKey + "\"}"), - new File("first-developer-key.json")); + .principal("joe@dev") + .roles(Set.of(Role.developer(id.tenant()))) + .data("{\"key\":\"" + pemPublicKey + "\"}"), + new File("first-developer-key.json")); // POST the same pem developer key for a different user is forbidden tester.assertResponse(request("/application/v4/tenant/my-tenant/key", POST) - .principal("operator@tenant") - .roles(Set.of(Role.developer(id.tenant()))) - .data("{\"key\":\"" + pemPublicKey + "\"}"), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Key "+ quotedPemPublicKey + " is already owned by joe@dev\"}", - 400); + .principal("operator@tenant") + .roles(Set.of(Role.developer(id.tenant()))) + .data("{\"key\":\"" + pemPublicKey + "\"}"), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Key " + quotedPemPublicKey + " is already owned by joe@dev\"}", + 400); // POST in a different pem developer key tester.assertResponse(request("/application/v4/tenant/my-tenant/key", POST) - .principal("developer@tenant") - .roles(Set.of(Role.developer(id.tenant()))) - .data("{\"key\":\"" + otherPemPublicKey + "\"}"), - new File("both-developer-keys.json")); + .principal("developer@tenant") + .roles(Set.of(Role.developer(id.tenant()))) + .data("{\"key\":\"" + otherPemPublicKey + "\"}"), + new File("both-developer-keys.json")); // GET tenant information with keys tester.assertResponse(request("/application/v4/tenant/my-tenant/") - .roles(Set.of(Role.reader(id.tenant()))), - new File("tenant-with-keys.json")); + .roles(Set.of(Role.reader(id.tenant()))), + new File("tenant-with-keys.json")); // DELETE a pem developer key tester.assertResponse(request("/application/v4/tenant/my-tenant/key", DELETE) - .roles(Set.of(Role.developer(id.tenant()))) - .data("{\"key\":\"" + pemPublicKey + "\"}"), - new File("second-developer-key.json")); + .roles(Set.of(Role.developer(id.tenant()))) + .data("{\"key\":\"" + pemPublicKey + "\"}"), + new File("second-developer-key.json")); // PUT in a new secret store for the tenant tester.assertResponse(request("/application/v4/tenant/my-tenant/secret-store/secret-foo", PUT) @@ -187,33 +170,33 @@ public class UserApiTest extends ControllerContainerCloudTest { // DELETE an application is available to developers. tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app", DELETE) - .roles(Set.of(Role.developer(id.tenant()))), - "{\"message\":\"Deleted application my-tenant.my-app\"}"); + .roles(Set.of(Role.developer(id.tenant()))), + "{\"message\":\"Deleted application my-tenant.my-app\"}"); // DELETE a tenant role is available to tenant admins. // DELETE the developer role clears any developer key. tester.assertResponse(request("/user/v1/tenant/my-tenant", DELETE) - .roles(Set.of(Role.administrator(id.tenant()))) - .data("{\"user\":\"developer@tenant\",\"roles\":[\"developer\",\"reader\"]}"), - "{\"message\":\"user 'developer@tenant' is no longer a member of role 'developer' of 'my-tenant', role 'reader' of 'my-tenant'\"}"); + .roles(Set.of(Role.administrator(id.tenant()))) + .data("{\"user\":\"developer@tenant\",\"roles\":[\"developer\",\"reader\"]}"), + "{\"message\":\"user 'developer@tenant' is no longer a member of role 'developer' of 'my-tenant', role 'reader' of 'my-tenant'\"}"); // DELETE the last tenant owner is not allowed. tester.assertResponse(request("/user/v1/tenant/my-tenant", DELETE) - .roles(operator) - .data("{\"user\":\"administrator@tenant\",\"roleName\":\"administrator\"}"), - "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Can't remove the last administrator of a tenant.\"}", 400); + .roles(operator) + .data("{\"user\":\"administrator@tenant\",\"roles\":[\"administrator\"]}"), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Can't remove the last administrator of a tenant.\"}", 400); // DELETE the tenant is not allowed tester.assertResponse(request("/application/v4/tenant/my-tenant", DELETE) - .roles(Set.of(Role.developer(id.tenant()))), - "{\n" + - " \"code\" : 403,\n" + - " \"message\" : \"Access denied\"\n" + - "}", 403); + .roles(Set.of(Role.developer(id.tenant()))), + "{\n" + + " \"code\" : 403,\n" + + " \"message\" : \"Access denied\"\n" + + "}", 403); } @Test - public void userMetadataTest() { + void userMetadataTest() { try (Flags.Replacer ignored = Flags.clearFlagsForTesting(PermanentFlags.MAX_TRIAL_TENANTS.id(), PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.id())) { ContainerTester tester = new ContainerTester(container, responseFiles); ((InMemoryFlagSource) tester.controller().flagSource()) @@ -262,7 +245,7 @@ public class UserApiTest extends ControllerContainerCloudTest { } @Test - public void maxTrialTenants() { + void maxTrialTenants() { try (Flags.Replacer ignored = Flags.clearFlagsForTesting(PermanentFlags.MAX_TRIAL_TENANTS.id(), PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.id())) { ContainerTester tester = new ContainerTester(container, responseFiles); ((InMemoryFlagSource) tester.controller().flagSource()) @@ -280,7 +263,7 @@ public class UserApiTest extends ControllerContainerCloudTest { } @Test - public void supportTenant() { + void supportTenant() { try (Flags.Replacer ignored = Flags.clearFlagsForTesting(PermanentFlags.MAX_TRIAL_TENANTS.id(), PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.id())) { ContainerTester tester = new ContainerTester(container, responseFiles); ((InMemoryFlagSource) tester.controller().flagSource()) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserFlagsSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserFlagsSerializerTest.java index 8625628b74e..ed7d02d0047 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserFlagsSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserFlagsSerializerTest.java @@ -14,7 +14,7 @@ import com.yahoo.vespa.flags.JsonNodeRawFlag; import com.yahoo.vespa.flags.json.Condition; import com.yahoo.vespa.flags.json.FlagData; import com.yahoo.vespa.flags.json.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -36,7 +36,7 @@ import static com.yahoo.vespa.flags.FetchVector.Dimension.TENANT_ID; public class UserFlagsSerializerTest { @Test - public void user_flag_test() throws IOException { + void user_flag_test() throws IOException { String email1 = "alice@domain.tld"; String email2 = "bob@domain.tld"; @@ -56,33 +56,33 @@ public class UserFlagsSerializerTest { rule("[\"value2\"]", condition(CONSOLE_USER_EMAIL, Condition.Type.WHITELIST, email2)), rule("[\"value1\",\"value3\"]", condition(APPLICATION_ID, Condition.Type.BLACKLIST, "tenant1:video:default", "tenant1:video:default", "tenant2:music:default"))), flagData("jackson-id", rule("{\"integer\":456,\"string\":\"xyz\"}", condition(CONSOLE_USER_EMAIL, Condition.Type.WHITELIST, email1), condition(TENANT_ID, Condition.Type.WHITELIST, "tenant1", "tenant3"))) - ).collect(Collectors.toMap(FlagData::id, fd -> fd)); + ).collect(Collectors.toMap(FlagData::id, fd -> fd)); // double-id is not here as it does not have CONSOLE_USER_EMAIL dimension assertUserFlags("{\"flags\":[" + - "{\"id\":\"int-id\",\"rules\":[{\"value\":456}]}," + // Default from DB - "{\"id\":\"jackson-id\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"tenant\"}],\"value\":{\"integer\":456,\"string\":\"xyz\"}},{\"value\":{\"integer\":123,\"string\":\"abc\"}}]}," + // Resolved for email - // Resolved for email, but conditions are empty since this user is not authorized for any tenants - "{\"id\":\"list-id\",\"rules\":[{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\"}],\"value\":[\"value1\"]},{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\"}],\"value\":[\"value1\",\"value3\"]},{\"value\":[\"a\"]}]}," + - "{\"id\":\"string-id\",\"rules\":[{\"value\":\"value1\"}]}]}", // resolved for email + "{\"id\":\"int-id\",\"rules\":[{\"value\":456}]}," + // Default from DB + "{\"id\":\"jackson-id\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"tenant\"}],\"value\":{\"integer\":456,\"string\":\"xyz\"}},{\"value\":{\"integer\":123,\"string\":\"abc\"}}]}," + // Resolved for email + // Resolved for email, but conditions are empty since this user is not authorized for any tenants + "{\"id\":\"list-id\",\"rules\":[{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\"}],\"value\":[\"value1\"]},{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\"}],\"value\":[\"value1\",\"value3\"]},{\"value\":[\"a\"]}]}," + + "{\"id\":\"string-id\",\"rules\":[{\"value\":\"value1\"}]}]}", // resolved for email flagData, Set.of(), false, email1); // Same as the first one, but user is authorized for tenant1 assertUserFlags("{\"flags\":[" + - "{\"id\":\"int-id\",\"rules\":[{\"value\":456}]}," + // Default from DB - "{\"id\":\"jackson-id\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"tenant\",\"values\":[\"tenant1\"]}],\"value\":{\"integer\":456,\"string\":\"xyz\"}},{\"value\":{\"integer\":123,\"string\":\"abc\"}}]}," + // Resolved for email - // Resolved for email, but conditions have filtered out tenant2 - "{\"id\":\"list-id\",\"rules\":[{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"tenant1:video:default\",\"tenant1:video:default\"]}],\"value\":[\"value1\"]},{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"tenant1:video:default\",\"tenant1:video:default\"]}],\"value\":[\"value1\",\"value3\"]},{\"value\":[\"a\"]}]}," + - "{\"id\":\"string-id\",\"rules\":[{\"value\":\"value1\"}]}]}", // resolved for email + "{\"id\":\"int-id\",\"rules\":[{\"value\":456}]}," + // Default from DB + "{\"id\":\"jackson-id\",\"rules\":[{\"conditions\":[{\"type\":\"whitelist\",\"dimension\":\"tenant\",\"values\":[\"tenant1\"]}],\"value\":{\"integer\":456,\"string\":\"xyz\"}},{\"value\":{\"integer\":123,\"string\":\"abc\"}}]}," + // Resolved for email + // Resolved for email, but conditions have filtered out tenant2 + "{\"id\":\"list-id\",\"rules\":[{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"tenant1:video:default\",\"tenant1:video:default\"]}],\"value\":[\"value1\"]},{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"tenant1:video:default\",\"tenant1:video:default\"]}],\"value\":[\"value1\",\"value3\"]},{\"value\":[\"a\"]}]}," + + "{\"id\":\"string-id\",\"rules\":[{\"value\":\"value1\"}]}]}", // resolved for email flagData, Set.of("tenant1"), false, email1); // As operator no conditions are filtered, but the email precondition is applied assertUserFlags("{\"flags\":[" + - "{\"id\":\"int-id\",\"rules\":[{\"value\":456}]}," + // Default from DB - "{\"id\":\"jackson-id\",\"rules\":[{\"value\":{\"integer\":123,\"string\":\"abc\"}}]}," + // Default from code, no DB values match - // Includes last value from DB which is not conditioned on email and the default from code - "{\"id\":\"list-id\",\"rules\":[{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"tenant1:video:default\",\"tenant1:video:default\",\"tenant2:music:default\"]}],\"value\":[\"value1\",\"value3\"]},{\"value\":[\"a\"]}]}," + - "{\"id\":\"string-id\",\"rules\":[{\"value\":\"default value\"}]}]}", // Default from code + "{\"id\":\"int-id\",\"rules\":[{\"value\":456}]}," + // Default from DB + "{\"id\":\"jackson-id\",\"rules\":[{\"value\":{\"integer\":123,\"string\":\"abc\"}}]}," + // Default from code, no DB values match + // Includes last value from DB which is not conditioned on email and the default from code + "{\"id\":\"list-id\",\"rules\":[{\"conditions\":[{\"type\":\"blacklist\",\"dimension\":\"application\",\"values\":[\"tenant1:video:default\",\"tenant1:video:default\",\"tenant2:music:default\"]}],\"value\":[\"value1\",\"value3\"]},{\"value\":[\"a\"]}]}," + + "{\"id\":\"string-id\",\"rules\":[{\"value\":\"default value\"}]}]}", // Default from code flagData, Set.of(), true, "operator@domain.tld"); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json deleted file mode 100644 index 8497358fe40..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "tenant": "my-tenant", - "application": "my-app", - "roleNames": [ ], - "users": [ - { - "name": "administrator@tenant", - "email": "administrator@tenant", - "verified": false, - "roles": { } - }, - { - "name": "developer@tenant", - "email": "developer@tenant", - "verified": false, - "roles": { } - } - ] -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json index f980f9231f3..6b78bfda6c4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-keys.json @@ -20,9 +20,7 @@ } }, "quota": { - "budget": null, - "budgetUsed": 0.0, - "clusterSize": 5 + "budgetUsed": 0.0 }, "archiveAccess": { }, "applications": [ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json index 1152033791b..8c3bcc041d6 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-with-secrets.json @@ -28,9 +28,7 @@ } }, "quota": { - "budget": null, - "budgetUsed": 0.0, - "clusterSize": 5 + "budgetUsed": 0.0 }, "archiveAccess": { }, "applications": [ diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json index 631346181a1..694e886a876 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json @@ -11,9 +11,7 @@ } }, "quota": { - "budget": null, - "budgetUsed": 0.0, - "clusterSize": 5 + "budgetUsed": 0.0 }, "archiveAccess": { }, "applications": [ ], diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java index fcf8402d23c..5d5e310503d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java @@ -8,8 +8,8 @@ import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.List; @@ -31,7 +31,7 @@ public class ZoneApiTest extends ControllerContainerCloudTest { private ContainerTester tester; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); tester.serviceRegistry().zoneRegistry() @@ -40,30 +40,30 @@ public class ZoneApiTest extends ControllerContainerCloudTest { } @Test - public void test_requests() { + void test_requests() { // GET /zone/v1 tester.assertResponse(request("/zone/v1") - .roles(everyone), - new File("root.json")); + .roles(everyone), + new File("root.json")); // GET /zone/v1/environment/prod tester.assertResponse(request("/zone/v1/environment/prod") - .roles(everyone), - new File("prod.json")); + .roles(everyone), + new File("prod.json")); // GET /zone/v1/environment/dev/default tester.assertResponse(request("/zone/v1/environment/dev/default") - .roles(everyone), - new File("default-for-region.json")); + .roles(everyone), + new File("default-for-region.json")); } @Test - public void test_invalid_requests() { + void test_invalid_requests() { // GET /zone/v1/environment/prod/default: No default region tester.assertResponse(request("/zone/v1/environment/prod/default") - .roles(everyone), - new File("no-default-region.json"), - 400); + .roles(everyone), + new File("no-default-region.json"), + 400); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java index fd39c13707b..81484f05d1e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java @@ -11,15 +11,15 @@ import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.proxy.ProxyRequest; import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mpolden @@ -36,7 +36,7 @@ public class ZoneApiTest extends ControllerContainerTest { private ContainerTester tester; private ConfigServerProxyMock proxy; - @Before + @BeforeEach public void before() { tester = new ContainerTester(container, responseFiles); tester.serviceRegistry().zoneRegistry() @@ -46,53 +46,53 @@ public class ZoneApiTest extends ControllerContainerTest { } @Test - public void test_requests() { + void test_requests() { // GET /zone/v2 tester.assertResponse(authenticatedRequest("http://localhost:8080/zone/v2"), - new File("root.json")); + new File("root.json")); // GET /zone/v2/prod/us-north-1 tester.assertResponse(authenticatedRequest("http://localhost:8080/zone/v2/prod/us-north-1"), - "ok"); + "ok"); assertLastRequest(ZoneId.from("prod", "us-north-1"), 1, "GET"); // GET /zone/v2/nodes/v2/node/?recursive=true tester.assertResponse(authenticatedRequest("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/node/?recursive=true"), - "ok"); + "ok"); assertLastRequest(ZoneId.from("prod", "us-north-1"), 1, "GET"); // POST /zone/v2/dev/us-north-2/nodes/v2/command/restart?hostname=node1 tester.assertResponse(operatorRequest("http://localhost:8080/zone/v2/dev/aws-us-north-2/nodes/v2/command/restart?hostname=node1", - "", Method.POST), - "ok"); + "", Method.POST), + "ok"); // PUT /zone/v2/prod/us-north-1/nodes/v2/state/dirty/node1 tester.assertResponse(operatorRequest("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/state/dirty/node1", - "", Method.PUT), "ok"); + "", Method.PUT), "ok"); assertLastRequest(ZoneId.from("prod", "us-north-1"), 1, "PUT"); // DELETE /zone/v2/prod/us-north-1/nodes/v2/node/node1 tester.assertResponse(operatorRequest("http://localhost:8080/zone/v2/prod/us-north-1/nodes/v2/node/node1", - "", Method.DELETE), "ok"); + "", Method.DELETE), "ok"); assertLastRequest(ZoneId.from("prod", "us-north-1"), 1, "DELETE"); // PATCH /zone/v2/prod/us-north-1/nodes/v2/node/node1 tester.assertResponse(operatorRequest("http://localhost:8080/zone/v2/dev/aws-us-north-2/nodes/v2/node/node1", - "{\"currentRestartGeneration\": 1}", - Method.PATCH), "ok"); + "{\"currentRestartGeneration\": 1}", + Method.PATCH), "ok"); assertLastRequest(ZoneId.from("dev", "aws-us-north-2"), 1, "PATCH"); assertEquals("{\"currentRestartGeneration\": 1}", proxy.lastRequestBody().get()); - assertFalse("Actions are logged to audit log", tester.controller().auditLogger().readLog().entries().isEmpty()); + assertFalse(tester.controller().auditLogger().readLog().entries().isEmpty(), "Actions are logged to audit log"); } @Test - public void test_invalid_requests() { + void test_invalid_requests() { // POST /zone/v2/prod/us-north-34/nodes/v2 tester.assertResponse(operatorRequest("http://localhost:8080/zone/v2/prod/us-north-42/nodes/v2", - "", Method.POST), - new File("unknown-zone.json"), 400); + "", Method.POST), + new File("unknown-zone.json"), 400); assertFalse(proxy.lastReceived().isPresent()); } 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 c0fb9b3d8c7..c5d74e0b01d 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 @@ -32,7 +32,7 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.rotation.config.RotationsConfig; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; @@ -47,9 +47,9 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author mortent @@ -67,7 +67,7 @@ public class RoutingPoliciesTest { .build(); @Test - public void global_routing_policies() { + void global_routing_policies() { var tester = new RoutingPoliciesTester(); var context1 = tester.newDeploymentContext("tenant1", "app1", "default"); var context2 = tester.newDeploymentContext("tenant1", "app2", "default"); @@ -87,9 +87,9 @@ public class RoutingPoliciesTest { tester.assertTargets(context1.instanceId(), EndpointId.of("r0"), 0, zone1, zone2); tester.assertTargets(context1.instanceId(), EndpointId.of("r1"), 0, zone1); tester.assertTargets(context1.instanceId(), EndpointId.of("r2"), 1, zone1, zone2); - assertEquals("Routing policy count is equal to cluster count", - numberOfDeployments * clustersPerZone, - tester.policiesOf(context1.instance().id()).size()); + assertEquals(numberOfDeployments * clustersPerZone, + tester.policiesOf(context1.instance().id()).size(), + "Routing policy count is equal to cluster count"); // Applications gains a new deployment ApplicationPackage applicationPackage2 = applicationPackageBuilder() @@ -144,13 +144,13 @@ public class RoutingPoliciesTest { tester.assertTargets(context1.instanceId(), EndpointId.of("r2"), 0); var policies = tester.policiesOf(context1.instanceId()); assertEquals(clustersPerZone * numberOfDeployments, policies.size()); - assertTrue("Rotation membership is removed from all policies", - policies.asList().stream().allMatch(policy -> policy.instanceEndpoints().isEmpty())); - assertEquals("Rotations for " + context2.application() + " are not removed", 1, tester.aliasDataOf(endpoint4).size()); + assertTrue(policies.asList().stream().allMatch(policy -> policy.instanceEndpoints().isEmpty()), + "Rotation membership is removed from all policies"); + assertEquals(1, tester.aliasDataOf(endpoint4).size(), "Rotations for " + context2.application() + " are not removed"); } @Test - public void global_routing_policies_with_duplicate_region() { + void global_routing_policies_with_duplicate_region() { var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); int clustersPerZone = 2; @@ -168,40 +168,40 @@ public class RoutingPoliciesTest { context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, zone1, zone3, zone4); tester.assertTargets(context.instanceId(), EndpointId.of("r1"), 1, zone1, zone3, zone4); - assertEquals("Routing policy count is equal to cluster count", - numberOfDeployments * clustersPerZone, - tester.policiesOf(context.instance().id()).size()); + assertEquals(numberOfDeployments * clustersPerZone, + tester.policiesOf(context.instance().id()).size(), + "Routing policy count is equal to cluster count"); // A zone in shared region is set out tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone4), RoutingStatus.Value.out, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); // Weight of inactive zone is set to zero tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, ImmutableMap.of(zone1, 1L, - zone3, 1L, - zone4, 0L)); + zone3, 1L, + zone4, 0L)); // Other zone in shared region is set out. Entire record group for the region is removed as all zones in the // region are out (weight sum = 0) tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone3), RoutingStatus.Value.out, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, ImmutableMap.of(zone1, 1L)); // Everything is set back in tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone3), RoutingStatus.Value.in, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone4), RoutingStatus.Value.in, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, ImmutableMap.of(zone1, 1L, - zone3, 1L, - zone4, 1L)); + zone3, 1L, + zone4, 1L)); } @Test - public void global_routing_policies_legacy_global_service_id() { + void global_routing_policies_legacy_global_service_id() { var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); int clustersPerZone = 2; @@ -216,13 +216,13 @@ public class RoutingPoliciesTest { // Creates alias records context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); tester.assertTargets(context.instanceId(), EndpointId.defaultId(), 0, zone1, zone2); - assertEquals("Routing policy count is equal to cluster count", - numberOfDeployments * clustersPerZone, - tester.policiesOf(context.instance().id()).size()); + assertEquals(numberOfDeployments * clustersPerZone, + tester.policiesOf(context.instance().id()).size(), + "Routing policy count is equal to cluster count"); } @Test - public void zone_routing_policies() { + void zone_routing_policies() { zone_routing_policies(false); zone_routing_policies(true); } @@ -313,12 +313,12 @@ public class RoutingPoliciesTest { "c1.app1.tenant1.us-central-1.vespa.oath.cloud" ); assertEquals(expectedRecords, tester.recordNames()); - assertTrue("Removes stale routing policies " + context2.application(), tester.routingPolicies().read(context2.instanceId()).isEmpty()); - assertEquals("Keeps routing policies for " + context1.application(), 4, tester.routingPolicies().read(context1.instanceId()).size()); + assertTrue(tester.routingPolicies().read(context2.instanceId()).isEmpty(), "Removes stale routing policies " + context2.application()); + assertEquals(4, tester.routingPolicies().read(context1.instanceId()).size(), "Keeps routing policies for " + context1.application()); } @Test - public void zone_routing_policies_without_dns_update() { + void zone_routing_policies_without_dns_update() { var tester = new RoutingPoliciesTester(new DeploymentTester(), false); var context = tester.newDeploymentContext("tenant1", "app1", "default"); tester.provisionLoadBalancers(1, context.instanceId(), true, zone1, zone2); @@ -329,7 +329,7 @@ public class RoutingPoliciesTest { } @Test - public void global_routing_policies_in_rotationless_system() { + void global_routing_policies_in_rotationless_system() { var tester = new RoutingPoliciesTester(SystemName.Public); var context = tester.newDeploymentContext("tenant1", "app1", "default"); List<ZoneId> prodZones = tester.controllerTester().controller().zoneRegistry().zones().all().in(Environment.prod).ids(); @@ -345,13 +345,13 @@ public class RoutingPoliciesTest { context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, zone1); - assertTrue("No rotations assigned", context.application().instances().values().stream() - .map(Instance::rotations) - .allMatch(List::isEmpty)); + assertTrue(context.application().instances().values().stream() + .map(Instance::rotations) + .allMatch(List::isEmpty), "No rotations assigned"); } @Test - public void global_routing_policies_in_public() { + void global_routing_policies_in_public() { var tester = new RoutingPoliciesTester(SystemName.Public); var context = tester.newDeploymentContext("tenant1", "app1", "default"); List<ZoneId> prodZones = tester.controllerTester().controller().zoneRegistry().zones().all().in(Environment.prod).ids(); @@ -367,19 +367,19 @@ public class RoutingPoliciesTest { context.submit(applicationPackage).deploy(); tester.assertTargets(context.instanceId(), EndpointId.defaultId(), - ClusterSpec.Id.from("default"), 0, - Map.of(zone1, 1L, zone2, 1L)); - assertEquals("Registers expected DNS names", - Set.of("app1.tenant1.aws-eu-west-1.w.vespa-app.cloud", - "app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud", - "app1.tenant1.aws-us-east-1.w.vespa-app.cloud", - "app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", - "app1.tenant1.g.vespa-app.cloud"), - tester.recordNames()); + ClusterSpec.Id.from("default"), 0, + Map.of(zone1, 1L, zone2, 1L)); + assertEquals(Set.of("app1.tenant1.aws-eu-west-1.w.vespa-app.cloud", + "app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud", + "app1.tenant1.aws-us-east-1.w.vespa-app.cloud", + "app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", + "app1.tenant1.g.vespa-app.cloud"), + tester.recordNames(), + "Registers expected DNS names"); } @Test - public void manual_deployment_creates_routing_policy() { + void manual_deployment_creates_routing_policy() { // Empty application package is valid in manually deployed environments var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -387,12 +387,12 @@ public class RoutingPoliciesTest { var zone = ZoneId.from("dev", "us-east-1"); var zoneApi = ZoneApiMock.from(zone.environment(), zone.region()); tester.controllerTester().serviceRegistry().zoneRegistry() - .setZones(zoneApi) - .exclusiveRoutingIn(zoneApi); + .setZones(zoneApi) + .exclusiveRoutingIn(zoneApi); // Deploy to dev context.runJob(zone, emptyApplicationPackage); - assertEquals("DeploymentSpec is not persisted", DeploymentSpec.empty, context.application().deploymentSpec()); + assertEquals(DeploymentSpec.empty, context.application().deploymentSpec(), "DeploymentSpec is not persisted"); context.flushDnsUpdates(); // Routing policy is created and DNS is updated @@ -401,7 +401,7 @@ public class RoutingPoliciesTest { } @Test - public void manual_deployment_creates_routing_policy_with_non_empty_spec() { + void manual_deployment_creates_routing_policy_with_non_empty_spec() { // Initial deployment var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -415,7 +415,7 @@ public class RoutingPoliciesTest { var devContext = tester.newDeploymentContext(context.application().id().instance("user")); devContext.runJob(zone, applicationPackage); - assertEquals("DeploymentSpec is persisted", applicationPackage.deploymentSpec(), context.application().deploymentSpec()); + assertEquals(applicationPackage.deploymentSpec(), context.application().deploymentSpec(), "DeploymentSpec is persisted"); context.flushDnsUpdates(); // Routing policy is created and DNS is updated @@ -424,7 +424,7 @@ public class RoutingPoliciesTest { } @Test - public void reprovisioning_load_balancer_preserves_cname_record() { + void reprovisioning_load_balancer_preserves_cname_record() { var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -449,23 +449,24 @@ public class RoutingPoliciesTest { // Load balancer for the same application is provisioned again, but with a different hostname var newHostname = HostName.of("new-hostname"); var loadBalancer = new LoadBalancer("LB-0-Z-" + zone1.value(), - context.instanceId(), - ClusterSpec.Id.from("c0"), - Optional.of(newHostname), - LoadBalancer.State.active, - Optional.of("dns-zone-1")); + context.instanceId(), + ClusterSpec.Id.from("c0"), + Optional.of(newHostname), + LoadBalancer.State.active, + Optional.of("dns-zone-1")); tester.controllerTester().configServer().putLoadBalancers(zone1, List.of(loadBalancer)); // Application redeployment preserves DNS record context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); assertEquals(expectedRecords, tester.recordNames()); assertEquals(1, tester.policiesOf(context.instanceId()).size()); - assertEquals("CNAME points to current load blancer", newHostname.value() + ".", - tester.cnameDataOf(expectedRecords.iterator().next()).get(0)); + assertEquals(newHostname.value() + ".", + tester.cnameDataOf(expectedRecords.iterator().next()).get(0), + "CNAME points to current load blancer"); } @Test - public void set_global_endpoint_status() { + void set_global_endpoint_status() { var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -486,7 +487,7 @@ public class RoutingPoliciesTest { // Global routing status is overridden in one zone var changedAt = tester.controllerTester().clock().instant(); tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone1), RoutingStatus.Value.out, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); // Inactive zone is removed from global DNS record @@ -548,7 +549,7 @@ public class RoutingPoliciesTest { } @Test - public void set_zone_global_endpoint_status() { + void set_zone_global_endpoint_status() { var tester = new RoutingPoliciesTester(); var context1 = tester.newDeploymentContext("tenant1", "app1", "default"); var context2 = tester.newDeploymentContext("tenant2", "app2", "default"); @@ -573,12 +574,12 @@ public class RoutingPoliciesTest { tester.assertTargets(context2.instanceId(), EndpointId.defaultId(), 0, zone1); for (var context : contexts) { var policies = tester.routingPolicies().read(context.instanceId()); - assertTrue("Global routing status for policy remains " + RoutingStatus.Value.in, - policies.asList().stream() - .map(RoutingPolicy::status) - .map(RoutingPolicy.Status::routingStatus) - .map(RoutingStatus::value) - .allMatch(status -> status == RoutingStatus.Value.in)); + assertTrue(policies.asList().stream() + .map(RoutingPolicy::status) + .map(RoutingPolicy.Status::routingStatus) + .map(RoutingStatus::value) + .allMatch(status -> status == RoutingStatus.Value.in), + "Global routing status for policy remains " + RoutingStatus.Value.in); } var changedAt = tester.controllerTester().clock().instant(); var zonePolicy = tester.controllerTester().controller().curator().readZoneRoutingPolicy(zone2); @@ -604,7 +605,7 @@ public class RoutingPoliciesTest { } @Test - public void non_production_deployment_is_not_registered_in_global_endpoint() { + void non_production_deployment_is_not_registered_in_global_endpoint() { var tester = new RoutingPoliciesTester(SystemName.Public); // Configure the system to use the same region for test, staging and prod @@ -634,7 +635,7 @@ public class RoutingPoliciesTest { } @Test - public void changing_global_routing_status_never_removes_all_members() { + void changing_global_routing_status_never_removes_all_members() { var tester = new RoutingPoliciesTester(); var context = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -652,20 +653,20 @@ public class RoutingPoliciesTest { // Global routing status is overridden for one deployment tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone1), RoutingStatus.Value.out, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, zone2); // Setting other deployment out implicitly sets all deployments in. Weight is set to zero, but that has no // impact on routing decisions when the weight sum is zero tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone2), RoutingStatus.Value.out, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, ImmutableMap.of(zone1, 0L, zone2, 0L)); // One inactive deployment is put back in. Global DNS record now points to the only active deployment tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone1), RoutingStatus.Value.in, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); tester.assertTargets(context.instanceId(), EndpointId.of("r0"), 0, zone1); @@ -682,7 +683,7 @@ public class RoutingPoliciesTest { // Inactive deployment is set in tester.routingPolicies().setRoutingStatus(context.deploymentIdIn(zone2), RoutingStatus.Value.in, - RoutingStatus.Agent.tenant); + RoutingStatus.Agent.tenant); context.flushDnsUpdates(); for (var policy : tester.routingPolicies().read(context.instanceId())) { assertSame(RoutingStatus.Value.in, policy.status().routingStatus().value()); @@ -691,7 +692,7 @@ public class RoutingPoliciesTest { } @Test - public void application_endpoint_routing_policy() { + void application_endpoint_routing_policy() { RoutingPoliciesTester tester = new RoutingPoliciesTester(); TenantAndApplicationId application = TenantAndApplicationId.from("tenant1", "app1"); ApplicationId betaInstance = application.instance("beta"); @@ -704,11 +705,11 @@ public class RoutingPoliciesTest { .region(zone1.region()) .region(zone2.region()) .applicationEndpoint("a0", "c0", "us-west-1", - Map.of(betaInstance.instance(), 2, - mainInstance.instance(), 8)) + Map.of(betaInstance.instance(), 2, + mainInstance.instance(), 8)) .applicationEndpoint("a1", "c1", "us-central-1", - Map.of(betaInstance.instance(), 4, - mainInstance.instance(), 6)) + Map.of(betaInstance.instance(), 4, + mainInstance.instance(), 6)) .build(); for (var zone : List.of(zone1, zone2)) { tester.provisionLoadBalancers(2, betaInstance, zone); @@ -724,11 +725,11 @@ public class RoutingPoliciesTest { DeploymentId betaZone2 = betaContext.deploymentIdIn(zone2); DeploymentId mainZone2 = mainContext.deploymentIdIn(zone2); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 2, - mainZone1, 8)); + Map.of(betaZone1, 2, + mainZone1, 8)); tester.assertTargets(application, EndpointId.of("a1"), ClusterSpec.Id.from("c1"), 1, - Map.of(betaZone2, 4, - mainZone2, 6)); + Map.of(betaZone2, 4, + mainZone2, 6)); // Weights are updated applicationPackage = applicationPackageBuilder() @@ -736,19 +737,19 @@ public class RoutingPoliciesTest { .region(zone1.region()) .region(zone2.region()) .applicationEndpoint("a0", "c0", "us-west-1", - Map.of(betaInstance.instance(), 3, - mainInstance.instance(), 7)) + Map.of(betaInstance.instance(), 3, + mainInstance.instance(), 7)) .applicationEndpoint("a1", "c1", "us-central-1", - Map.of(betaInstance.instance(), 1, - mainInstance.instance(), 9)) + Map.of(betaInstance.instance(), 1, + mainInstance.instance(), 9)) .build(); betaContext.submit(applicationPackage).deploy(); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 3, - mainZone1, 7)); + Map.of(betaZone1, 3, + mainZone1, 7)); tester.assertTargets(application, EndpointId.of("a1"), ClusterSpec.Id.from("c1"), 1, - Map.of(betaZone2, 1, - mainZone2, 9)); + Map.of(betaZone2, 1, + mainZone2, 9)); // An endpoint is removed applicationPackage = applicationPackageBuilder() @@ -756,21 +757,21 @@ public class RoutingPoliciesTest { .region(zone1.region()) .region(zone2.region()) .applicationEndpoint("a0", "c0", "us-west-1", - Map.of(betaInstance.instance(), 1)) + Map.of(betaInstance.instance(), 1)) .build(); betaContext.submit(applicationPackage).deploy(); // Application endpoints now point to a single instance tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 1)); - assertTrue("Endpoint removed", - tester.controllerTester().controller().routing() - .readDeclaredEndpointsOf(application) - .named(EndpointId.of("a1")).isEmpty()); + Map.of(betaZone1, 1)); + assertTrue(tester.controllerTester().controller().routing() + .readDeclaredEndpointsOf(application) + .named(EndpointId.of("a1")).isEmpty(), + "Endpoint removed"); } @Test - public void application_endpoint_routing_status() { + void application_endpoint_routing_status() { RoutingPoliciesTester tester = new RoutingPoliciesTester(); TenantAndApplicationId application = TenantAndApplicationId.from("tenant1", "app1"); ApplicationId betaInstance = application.instance("beta"); @@ -782,8 +783,8 @@ public class RoutingPoliciesTest { .instances("beta,main") .region(zone1.region()) .applicationEndpoint("a0", "c0", "us-west-1", - Map.of(betaInstance.instance(), 2, - mainInstance.instance(), 8)) + Map.of(betaInstance.instance(), 2, + mainInstance.instance(), 8)) .build(); tester.provisionLoadBalancers(1, betaInstance, zone1); tester.provisionLoadBalancers(1, mainInstance, zone1); @@ -795,35 +796,35 @@ public class RoutingPoliciesTest { DeploymentId betaZone1 = betaContext.deploymentIdIn(zone1); DeploymentId mainZone1 = mainContext.deploymentIdIn(zone1); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 2, - mainZone1, 8)); + Map.of(betaZone1, 2, + mainZone1, 8)); // Changing routing status removes deployment from DNS tester.routingPolicies().setRoutingStatus(mainZone1, RoutingStatus.Value.out, RoutingStatus.Agent.tenant); betaContext.flushDnsUpdates(); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 2)); + Map.of(betaZone1, 2)); // Changing routing status for remaining deployment adds back all deployments, because removing all deployments // puts all IN tester.routingPolicies().setRoutingStatus(betaZone1, RoutingStatus.Value.out, RoutingStatus.Agent.tenant); betaContext.flushDnsUpdates(); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 2, - mainZone1, 8)); + Map.of(betaZone1, 2, + mainZone1, 8)); // Activating main deployment allows us to deactivate the beta deployment tester.routingPolicies().setRoutingStatus(mainZone1, RoutingStatus.Value.in, RoutingStatus.Agent.tenant); betaContext.flushDnsUpdates(); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(mainZone1, 8)); + Map.of(mainZone1, 8)); // Activate all deployments again tester.routingPolicies().setRoutingStatus(betaZone1, RoutingStatus.Value.in, RoutingStatus.Agent.tenant); betaContext.flushDnsUpdates(); tester.assertTargets(application, EndpointId.of("a0"), ClusterSpec.Id.from("c0"), 0, - Map.of(betaZone1, 2, - mainZone1, 8)); + Map.of(betaZone1, 2, + mainZone1, 8)); } /** Returns an application package builder that satisfies requirements for a directly routed endpoint */ @@ -951,20 +952,21 @@ public class RoutingPoliciesTest { .named(endpointId) .targets(deployment) .cluster(cluster); - assertEquals("Expected a single endpoint with ID '" + endpointId + "'", 1, - applicationEndpoints.size()); + assertEquals(1, + applicationEndpoints.size(), + "Expected a single endpoint with ID '" + endpointId + "'"); String dnsName = applicationEndpoints.asList().get(0).dnsName(); deploymentsByDnsName.computeIfAbsent(dnsName, (k) -> new ArrayList<>()) .add(deployment); } - assertEquals("Found " + endpointId + " for " + application, 1, deploymentsByDnsName.size()); + assertEquals(1, deploymentsByDnsName.size(), "Found " + endpointId + " for " + application); deploymentsByDnsName.forEach((dnsName, deployments) -> { Set<String> weightedTargets = deployments.stream() .map(d -> "weighted/lb-" + loadBalancerId + "--" + d.applicationId().toFullString() + "--" + d.zoneId().value() + "/dns-zone-1/" + d.zoneId().value() + "/" + deploymentWeights.get(d)) .collect(Collectors.toSet()); - assertEquals(dnsName + " has expected targets", weightedTargets, aliasDataOf(dnsName)); + assertEquals(weightedTargets, aliasDataOf(dnsName), dnsName + " has expected targets"); }); } @@ -988,9 +990,9 @@ public class RoutingPoliciesTest { instance.toFullString() + "--" + z.value() + "/dns-zone-1/" + z.value() + "/" + zoneWeights.get(z)) .collect(Collectors.toSet()); - assertEquals("Region endpoint " + regionEndpoint + " points to load balancer", - weightedTargets, - aliasDataOf(regionEndpoint)); + assertEquals(weightedTargets, + aliasDataOf(regionEndpoint), + "Region endpoint " + regionEndpoint + " points to load balancer"); ZoneId zone = zonesInRegion.get(0); String latencyTarget = "latency/" + regionEndpoint + "/dns-zone-1/" + zone.value(); latencyTargets.add(latencyTarget); @@ -1002,8 +1004,7 @@ public class RoutingPoliciesTest { .primary() .map(Endpoint::dnsName) .orElse("<none>"); - assertEquals("Global endpoint " + globalEndpoint + " points to expected latency targets", - latencyTargets, Set.copyOf(aliasDataOf(globalEndpoint))); + assertEquals(latencyTargets, Set.copyOf(aliasDataOf(globalEndpoint)), "Global endpoint " + globalEndpoint + " points to expected latency targets"); } 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 277c8e1ef85..7477b94e3f4 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 @@ -13,7 +13,7 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.rotation.config.RotationsConfig; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URI; import java.util.List; @@ -21,8 +21,8 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Oyvind Gronnesby @@ -53,21 +53,21 @@ public class RotationRepositoryTest { private final DeploymentContext application = tester.newDeploymentContext("tenant1", "app1", "default"); @Test - public void assigns_and_reuses_rotation() { + void assigns_and_reuses_rotation() { // Deploying assigns a rotation application.submit(applicationPackage).deploy(); Rotation expected = new Rotation(new RotationId("foo-1"), "foo-1.com"); assertEquals(List.of(expected.id()), rotationIds(application.instance().rotations())); assertEquals(URI.create("https://app1.tenant1.global.vespa.oath.cloud/"), - tester.controller().routing().readDeclaredEndpointsOf(application.instanceId()).direct().first().get().url()); + tester.controller().routing().readDeclaredEndpointsOf(application.instanceId()).direct().first().get().url()); try (RotationLock lock = repository.lock()) { List<AssignedRotation> rotations = repository.getOrAssignRotations(application.application().deploymentSpec(), - application.instance(), - lock); + application.instance(), + lock); assertSingleRotation(expected, rotations, repository); assertEquals(Set.of(RegionName.from("us-west-1"), RegionName.from("us-east-3")), - application.instance().rotations().get(0).regions()); + application.instance().rotations().get(0).regions()); } // Submitting once more assigns same rotation @@ -83,12 +83,12 @@ public class RotationRepositoryTest { .build(); application.submit(applicationPackage).deploy(); assertEquals(Set.of(RegionName.from("us-west-1"), RegionName.from("us-east-3"), - RegionName.from("us-central-1")), - application.instance().rotations().get(0).regions()); + RegionName.from("us-central-1")), + application.instance().rotations().get(0).regions()); } - + @Test - public void strips_whitespace_in_rotation_fqdn() { + void strips_whitespace_in_rotation_fqdn() { var tester = new DeploymentTester(new ControllerTester(rotationsConfigWhitespaces, SystemName.main)); RotationRepository repository = tester.controller().routing().rotations(); var application2 = tester.newDeploymentContext("tenant1", "app2", "default"); @@ -103,7 +103,7 @@ public class RotationRepositoryTest { } @Test - public void out_of_rotations() { + void out_of_rotations() { // Assigns 1 rotation application.submit(applicationPackage).deploy(); @@ -114,11 +114,11 @@ public class RotationRepositoryTest { // We're now out of rotations and next deployment fails var application3 = tester.newDeploymentContext("tenant3", "app3", "default"); application3.submit(applicationPackage) - .runJobExpectingFailure(DeploymentContext.systemTest, Optional.of("out of rotations")); + .runJobExpectingFailure(DeploymentContext.systemTest, Optional.of("out of rotations")); } @Test - public void too_few_zones() { + void too_few_zones() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .globalServiceId("foo") .region("us-east-3") @@ -127,7 +127,7 @@ public class RotationRepositoryTest { } @Test - public void no_rotation_assigned_for_application_without_service_id() { + void no_rotation_assigned_for_application_without_service_id() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .region("us-east-3") .region("us-west-1") @@ -137,7 +137,7 @@ public class RotationRepositoryTest { } @Test - public void prefixes_system_when_not_main() { + void prefixes_system_when_not_main() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .globalServiceId("foo") .region("cd-us-east-1") @@ -150,18 +150,18 @@ public class RotationRepositoryTest { 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); + .setZones(zones) + .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(); assertEquals(List.of(new RotationId("foo-1")), rotationIds(application2.instance().rotations())); assertEquals("https://cd.app2.tenant2.global.vespa.oath.cloud/", - tester.controller().routing().readDeclaredEndpointsOf(application2.instanceId()).primary().get().url().toString()); + tester.controller().routing().readDeclaredEndpointsOf(application2.instanceId()).primary().get().url().toString()); } @Test - public void multiple_instances_with_similar_global_service_id() { + void multiple_instances_with_similar_global_service_id() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .instances("instance1,instance2") .region("us-central-1") @@ -169,19 +169,19 @@ public class RotationRepositoryTest { .globalServiceId("global") .build(); var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1") - .submit(applicationPackage) - .deploy(); + .submit(applicationPackage) + .deploy(); var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2"); assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations())); assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations())); assertEquals(URI.create("https://instance1.application1.tenant1.global.vespa.oath.cloud/"), - tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).direct().first().get().url()); + tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).direct().first().get().url()); assertEquals(URI.create("https://instance2.application1.tenant1.global.vespa.oath.cloud/"), - tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).direct().first().get().url()); + tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).direct().first().get().url()); } @Test - public void multiple_instances_with_similar_endpoints() { + void multiple_instances_with_similar_endpoints() { ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .instances("instance1,instance2") .region("us-central-1") @@ -189,17 +189,17 @@ public class RotationRepositoryTest { .endpoint("default", "foo", "us-central-1", "us-west-1") .build(); var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1") - .submit(applicationPackage) - .deploy(); + .submit(applicationPackage) + .deploy(); var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2"); assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations())); assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations())); assertEquals(URI.create("https://instance1.application1.tenant1.global.vespa.oath.cloud/"), - tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).direct().first().get().url()); + tester.controller().routing().readDeclaredEndpointsOf(instance1.instanceId()).direct().first().get().url()); assertEquals(URI.create("https://instance2.application1.tenant1.global.vespa.oath.cloud/"), - tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).direct().first().get().url()); + tester.controller().routing().readDeclaredEndpointsOf(instance2.instanceId()).direct().first().get().url()); } private void assertSingleRotation(Rotation expected, List<AssignedRotation> assignedRotations, RotationRepository repository) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java new file mode 100644 index 00000000000..710e75fb235 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java @@ -0,0 +1,64 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.security; + +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.flags.InMemoryFlagSource; +import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.vespa.hosted.controller.LockedTenant; +import com.yahoo.vespa.hosted.controller.api.role.Role; +import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; +import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal; +import com.yahoo.vespa.hosted.controller.api.role.TenantRole; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author freva + */ +class CloudUserSessionManagerTest { + + private final ControllerTester tester = new ControllerTester(SystemName.Public); + private final CloudUserSessionManager userSessionManager = new CloudUserSessionManager(tester.controller()); + + @Test + void test() { + createTenant("tenant1", null); + createTenant("tenant2", 1234); + createTenant("tenant3", 1543); + createTenant("tenant4", 2313); + + assertShouldExpire(false, 123); + assertShouldExpire(false, 123, "tenant1"); + assertShouldExpire(true, 123, "tenant2"); + assertShouldExpire(false, 2123, "tenant2"); + assertShouldExpire(true, 123, "tenant1", "tenant2"); + + ((InMemoryFlagSource) tester.controller().flagSource()).withLongFlag(PermanentFlags.INVALIDATE_CONSOLE_SESSIONS.id(), 150); + assertShouldExpire(true, 123); + assertShouldExpire(true, 123, "tenant1"); + } + + private void assertShouldExpire(boolean expected, long issuedAtSeconds, String... tenantNames) { + Set<Role> roles = Stream.of(tenantNames).map(name -> TenantRole.developer(TenantName.from(name))).collect(Collectors.toSet()); + SecurityContext context = new SecurityContext(new SimplePrincipal("dev"), roles, Instant.ofEpochSecond(issuedAtSeconds)); + assertEquals(expected, userSessionManager.shouldExpireSessionFor(context)); + } + + private void createTenant(String tenantName, Integer invalidateAfterSeconds) { + tester.createTenant(tenantName); + Optional.ofNullable(invalidateAfterSeconds) + .map(Instant::ofEpochSecond) + .ifPresent(instant -> + tester.controller().tenants().lockOrThrow(TenantName.from(tenantName), LockedTenant.Cloud.class, tenant -> + tester.controller().tenants().store(tenant.withInvalidateUserSessionsBefore(instant)))); + } +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/tls/SecureContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/tls/SecureContainerTest.java index de268f34093..05a0aedb18b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/tls/SecureContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/tls/SecureContainerTest.java @@ -9,19 +9,19 @@ import com.yahoo.component.ComponentId; import com.yahoo.security.KeyStoreBuilder; import com.yahoo.security.KeyStoreType; import com.yahoo.security.KeyStoreUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; import java.security.KeyStore; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * @author mpolden @@ -30,28 +30,28 @@ public class SecureContainerTest { private JDisc container; - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + @TempDir + public File folder; - @Before + @BeforeEach public void startContainer() { container = JDisc.fromServicesXml(servicesXml(writeKeyStore()), Networking.enable); } - @After + @AfterEach public void stopContainer() { container.close(); } @Test - public void test_https_request() { - assertNotNull("SslContextFactoryProvider is created", sslContextFactoryProvider()); + void test_https_request() { + assertNotNull(sslContextFactoryProvider(), "SslContextFactoryProvider is created"); assertResponse(Request.Method.GET, "/", 200); } private void assertResponse(Request.Method method, String path, int expectedStatusCode) { Response response = container.handleRequest(new Request("https://localhost:9999" + path, new byte[0], method)); - assertEquals("Status code", expectedStatusCode, response.getStatus()); + assertEquals(expectedStatusCode, response.getStatus(), "Status code"); } private ControllerSslContextFactoryProvider sslContextFactoryProvider() { @@ -83,7 +83,7 @@ public class SecureContainerTest { Keys.keyPair.getPrivate(), new char[0], Keys.certificate) .build(); try { - Path path = folder.newFile().toPath(); + Path path = File.createTempFile("junit", null, folder).toPath(); KeyStoreUtils.writeKeyStoreToFile(keyStore, path); return path; } catch (IOException e) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/MavenRepositoryClientTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/MavenRepositoryClientTest.java index 25dd72ae622..33b1792accb 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/MavenRepositoryClientTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/MavenRepositoryClientTest.java @@ -2,11 +2,11 @@ package com.yahoo.vespa.hosted.controller.versions; import com.yahoo.vespa.hosted.controller.api.integration.maven.ArtifactId; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.URI; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jonmv @@ -14,10 +14,10 @@ import static org.junit.Assert.assertEquals; public class MavenRepositoryClientTest { @Test - public void testUri() { + void testUri() { assertEquals(URI.create("https://domain:123/base/group/id/artifact-id/maven-metadata.xml"), - MavenRepositoryClient.withArtifactPath(URI.create("https://domain:123/base/"), - new ArtifactId("group.id", "artifact-id"))); + MavenRepositoryClient.withArtifactPath(URI.create("https://domain:123/base/"), + new ArtifactId("group.id", "artifact-id"))); } } 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 7a137d4e410..18776be2dce 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 @@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter; 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.application.Change; import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -21,12 +22,13 @@ import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb; import com.yahoo.vespa.hosted.controller.versions.VespaVersion.Confidence; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.Instant; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -35,11 +37,11 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.pro import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.stagingTest; import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.systemTest; import static java.util.stream.Collectors.toSet; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test computing of version status @@ -98,8 +100,8 @@ public class VersionStatusTest { writeControllerVersion(controller3, Version.fromString("6.2"), db); VersionStatus versionStatus = VersionStatus.compute(tester.controller()); - assertEquals("Controller version is oldest version", Version.fromString("6.1"), - versionStatus.controllerVersion().get().versionNumber()); + assertEquals(Version.fromString("6.1"), + versionStatus.controllerVersion().get().versionNumber(), "Controller version is oldest version"); // Last controller upgrades writeControllerVersion(controller2, Version.fromString("6.2"), db); @@ -160,7 +162,7 @@ public class VersionStatusTest { tester.triggerJobs(); tester.controllerTester().computeVersionStatus(); List<VespaVersion> versions = tester.controller().readVersionStatus().versions(); - assertEquals("The two versions above exist", 2, versions.size()); + assertEquals(2, versions.size(), "The two versions above exist"); VespaVersion v1 = versions.get(0); assertEquals(version1, v1.versionNumber()); @@ -176,9 +178,9 @@ public class VersionStatusTest { context2.instanceId(), List.of(productionUsEast3), context3.instanceId(), List.of(productionUsWest1, productionUsEast3)), statistics1.productionSuccesses()); - assertEquals("No applications have active deployment jobs on version1.", + assertEquals( List.of(), - statistics1.runningUpgrade()); + statistics1.runningUpgrade(), "No applications have active deployment jobs on version1."); VespaVersion v2 = versions.get(1); assertEquals(version2, v2.versionNumber()); @@ -201,13 +203,13 @@ public class VersionStatusTest { } private static void assertJobsRun(String assertion, Map<ApplicationId, List<JobType>> jobs, List<Run> runs) { - assertEquals(assertion, - jobs.entrySet().stream() + assertEquals(jobs.entrySet().stream() .flatMap(entry -> entry.getValue().stream().map(type -> new JobId(entry.getKey(), type))) .collect(toSet()), runs.stream() .map(run -> run.id().job()) - .collect(toSet())); + .collect(toSet()), + assertion); } @Test @@ -218,7 +220,7 @@ public class VersionStatusTest { tester.upgrader().maintain(); // Setup applications - all running on version0 - ApplicationPackage canaryPolicy = applicationPackage("canary"); + ApplicationPackage canaryPolicy = applicationPackage("canary", 7); var canary0 = tester.newDeploymentContext("tenant1", "canary0", "default") .submit(canaryPolicy) .deploy(); @@ -231,7 +233,7 @@ public class VersionStatusTest { ApplicationPackage defaultPolicy = applicationPackage("default"); var default0 = tester.newDeploymentContext("tenant1", "default0", "default") - .submit(defaultPolicy) + .submit(applicationPackage("default", 7)) .deploy(); var default1 = tester.newDeploymentContext("tenant1", "default1", "default") .submit(defaultPolicy) @@ -271,8 +273,8 @@ public class VersionStatusTest { // Application without deployment var ignored0 = tester.newDeploymentContext("tenant1", "ignored0", "default"); - assertEquals("All applications running on this version: High", - Confidence.high, confidence(tester.controller(), version0)); + assertEquals( + Confidence.high, confidence(tester.controller(), version0), "All applications running on this version: High"); // New version is released Version version1 = new Version("6.3"); @@ -286,25 +288,25 @@ public class VersionStatusTest { .runJob(stagingTest) .failDeployment(productionUsWest1); tester.controllerTester().computeVersionStatus(); - assertEquals("One canary failed: Broken", - Confidence.broken, confidence(tester.controller(), version1)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version1), "One canary failed: Broken"); // New version is released Version version2 = new Version("6.4"); tester.controllerTester().upgradeSystem(version2); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Confidence defaults to low for version with no applications", - Confidence.low, confidence(tester.controller(), version2)); + assertEquals( + Confidence.low, confidence(tester.controller(), version2), "Confidence defaults to low for version with no applications"); // All canaries upgrade successfully canary0.deployPlatform(version2); canary1.deployPlatform(version2); - assertEquals("Confidence for remains unchanged for version1: Broken", - Confidence.broken, confidence(tester.controller(), version1)); - assertEquals("Nothing has failed but not all canaries have upgraded: Low", - Confidence.low, confidence(tester.controller(), version2)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version1), "Confidence for remains unchanged for version1: Broken"); + assertEquals( + Confidence.low, confidence(tester.controller(), version2), "Nothing has failed but not all canaries have upgraded: Low"); // Remaining canary upgrades to version2 which raises confidence to normal and more apps upgrade canary2.triggerJobs().jobAborted(systemTest).jobAborted(stagingTest); @@ -313,8 +315,8 @@ public class VersionStatusTest { tester.controllerTester().computeVersionStatus(); tester.upgrader().maintain(); tester.triggerJobs(); - assertEquals("Canaries have upgraded: Normal", - Confidence.normal, confidence(tester.controller(), version2)); + assertEquals( + Confidence.normal, confidence(tester.controller(), version2), "Canaries have upgraded: Normal"); default0.deployPlatform(version2); default1.deployPlatform(version2); default2.deployPlatform(version2); @@ -328,23 +330,23 @@ public class VersionStatusTest { // Remember confidence across restart tester.controllerTester().createNewController(); - assertEquals("Confidence remains unchanged for version0: High", - Confidence.high, confidence(tester.controller(), version0)); - assertEquals("All canaries deployed + < 90% of defaults: Normal", - Confidence.normal, confidence(tester.controller(), version2)); - assertTrue("Status for version without applications is removed", - tester.controller().readVersionStatus().versions().stream() - .noneMatch(vespaVersion -> vespaVersion.versionNumber().equals(version1))); + assertEquals( + Confidence.high, confidence(tester.controller(), version0), "Confidence remains unchanged for version0: High"); + assertEquals( + Confidence.normal, confidence(tester.controller(), version2), "All canaries deployed + < 90% of defaults: Normal"); + assertTrue(tester.controller().readVersionStatus().versions().stream() + .noneMatch(vespaVersion -> vespaVersion.versionNumber().equals(version1)), + "Status for version without applications is removed"); // Another default application upgrades, raising confidence to high default8.deployPlatform(version2); default9.deployPlatform(version2); tester.controllerTester().computeVersionStatus(); - assertEquals("Confidence remains unchanged for version0: High", - Confidence.high, confidence(tester.controller(), version0)); - assertEquals("90% of defaults deployed successfully: High", - VespaVersion.Confidence.high, confidence(tester.controller(), version2)); + assertEquals( + Confidence.high, confidence(tester.controller(), version0), "Confidence remains unchanged for version0: High"); + assertEquals( + VespaVersion.Confidence.high, confidence(tester.controller(), version2), "90% of defaults deployed successfully: High"); // A new version is released, all canaries upgrade successfully, but enough "default" apps fail to mark version // as broken @@ -364,12 +366,12 @@ public class VersionStatusTest { default3.failDeployment(stagingTest); tester.controllerTester().computeVersionStatus(); - assertEquals("Confidence remains unchanged for version0: High", - Confidence.high, confidence(tester.controller(), version0)); - assertEquals("Confidence remains unchanged for version2: High", - Confidence.high, confidence(tester.controller(), version2)); - assertEquals("40% of defaults failed: Broken", - VespaVersion.Confidence.broken, confidence(tester.controller(), version3)); + assertEquals( + Confidence.high, confidence(tester.controller(), version0), "Confidence remains unchanged for version0: High"); + assertEquals( + Confidence.high, confidence(tester.controller(), version2), "Confidence remains unchanged for version2: High"); + assertEquals( + VespaVersion.Confidence.broken, confidence(tester.controller(), version3), "40% of defaults failed: Broken"); // Test version order List<VespaVersion> versions = tester.controller().readVersionStatus().versions(); @@ -379,6 +381,28 @@ public class VersionStatusTest { assertTrue(versions.get(0).isReleased()); assertFalse(versions.get(1).isReleased()); // tesst quirk: maven repo lost during controller recreation; useful to test status though assertTrue(versions.get(2).isReleased()); + + // A new major version is released and all canaries upgrade + Version version4 = new Version("7.1"); + tester.controller().applications().setTargetMajorVersion(OptionalInt.of(version3.getMajor())); // Previous remains the default + tester.controllerTester().upgradeSystem(version4); + tester.upgrader().maintain(); + tester.triggerJobs(); + canary0.deployPlatform(version4); + canary1.deployPlatform(version4); + canary2.deployPlatform(version4); + tester.controllerTester().computeVersionStatus(); + assertEquals(Confidence.normal, confidence(tester.controller(), version4)); + + // The single application allowing this major upgrades and confidence becomes 'high' + tester.upgrader().maintain(); + tester.triggerJobs(); + assertEquals(Change.of(version4), default0.instance().change()); + default0.jobAborted(systemTest) + .jobAborted(stagingTest) + .deployPlatform(version4); + tester.controllerTester().computeVersionStatus(); + assertEquals(Confidence.high, confidence(tester.controller(), version4)); } @Test @@ -393,8 +417,8 @@ public class VersionStatusTest { .submit(appPackage) .deploy(); - assertEquals("All applications running on this version: High", - Confidence.high, confidence(tester.controller(), version0)); + assertEquals( + Confidence.high, confidence(tester.controller(), version0), "All applications running on this version: High"); // New version is released Version version1 = new Version("6.3"); @@ -406,59 +430,61 @@ public class VersionStatusTest { canary0.failDeployment(systemTest); canary0.abortJob(stagingTest); tester.controllerTester().computeVersionStatus(); - assertEquals("One canary failed: Broken", - Confidence.broken, confidence(tester.controller(), version1)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version1), "One canary failed: Broken"); // New version is released Version version2 = new Version("6.4"); tester.controllerTester().upgradeSystem(version2); tester.upgrader().maintain(); - assertEquals("Confidence remains unchanged for version1 until app overrides old tests: Broken", - Confidence.broken, confidence(tester.controller(), version1)); - assertEquals("Confidence defaults to low for version with no applications", - Confidence.low, confidence(tester.controller(), version2)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version1), "Confidence remains unchanged for version1 until app overrides old tests: Broken"); + assertEquals( + Confidence.low, confidence(tester.controller(), version2), "Confidence defaults to low for version with no applications"); assertEquals(version2, canary0.instance().change().platform().orElseThrow()); canary0.failDeployment(systemTest); canary0.abortJob(stagingTest); tester.controllerTester().computeVersionStatus(); - assertFalse("Previous version should be forgotten, as canary only had test jobs run on it", - tester.controller().readVersionStatus().versions().stream().anyMatch(version -> version.versionNumber().equals(version1))); + assertFalse( + tester.controller().readVersionStatus().versions().stream().anyMatch(version -> version.versionNumber().equals(version1)), + "Previous version should be forgotten, as canary only had test jobs run on it"); // App succeeds with tests, but fails production deployment canary0.runJob(systemTest) .runJob(stagingTest) .failDeployment(productionUsWest1); - assertEquals("One canary failed: Broken", - Confidence.broken, confidence(tester.controller(), version2)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version2), "One canary failed: Broken"); // A new version is released, and the app again fails production deployment. Version version3 = new Version("6.5"); tester.controllerTester().upgradeSystem(version3); tester.upgrader().maintain(); - assertEquals("Confidence remains unchanged for version2: Broken", - Confidence.broken, confidence(tester.controller(), version2)); - assertEquals("Confidence defaults to low for version with no applications", - Confidence.low, confidence(tester.controller(), version3)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version2), "Confidence remains unchanged for version2: Broken"); + assertEquals( + Confidence.low, confidence(tester.controller(), version3), "Confidence defaults to low for version with no applications"); assertEquals(version3, canary0.instance().change().platform().orElseThrow()); canary0.runJob(systemTest) .runJob(stagingTest) .failDeployment(productionUsWest1); tester.controllerTester().computeVersionStatus(); - assertEquals("Confidence remains unchanged for version2: Broken", - Confidence.broken, confidence(tester.controller(), version2)); - assertEquals("Canary broken, so confidence for version3: Broken", - Confidence.broken, confidence(tester.controller(), version3)); + assertEquals( + Confidence.broken, confidence(tester.controller(), version2), "Confidence remains unchanged for version2: Broken"); + assertEquals( + Confidence.broken, confidence(tester.controller(), version3), "Canary broken, so confidence for version3: Broken"); // App succeeds production deployment, clearing failure on version2 canary0.runJob(productionUsWest1); tester.controllerTester().computeVersionStatus(); - assertFalse("Previous version should be forgotten, as canary only had test jobs run on it", - tester.controller().readVersionStatus().versions().stream().anyMatch(version -> version.versionNumber().equals(version2))); - assertEquals("Canary OK, but not done upgrading, so confidence for version3: Low", - Confidence.low, confidence(tester.controller(), version3)); + assertFalse( + tester.controller().readVersionStatus().versions().stream().anyMatch(version -> version.versionNumber().equals(version2)), + "Previous version should be forgotten, as canary only had test jobs run on it"); + assertEquals( + Confidence.low, confidence(tester.controller(), version3), "Canary OK, but not done upgrading, so confidence for version3: Low"); } @Test @@ -488,8 +514,9 @@ public class VersionStatusTest { assertEquals(Confidence.high, confidence(tester.controller(), version1)); // Stale override was removed - assertFalse("Stale override removed", tester.controller().curator().readConfidenceOverrides() - .containsKey(version0)); + assertFalse( tester.controller().curator().readConfidenceOverrides() + .containsKey(version0), + "Stale override removed"); } @Test @@ -668,11 +695,11 @@ public class VersionStatusTest { private void assertOnVersion(Version version, ApplicationId instance, DeploymentTester tester) { var vespaVersion = tester.controller().readVersionStatus().version(version); - assertNotNull("Statistics for version " + version + " exist", vespaVersion); + assertNotNull(vespaVersion, "Statistics for version " + version + " exist"); var statistics = DeploymentStatistics.compute(List.of(version), tester.deploymentStatuses()).get(0); - assertTrue("Application is on version " + version, + assertTrue( Stream.of(statistics.productionSuccesses(), statistics.failingUpgrades(), statistics.runningUpgrade()) - .anyMatch(runs -> runs.stream().anyMatch(run -> run.id().application().equals(instance)))); + .anyMatch(runs -> runs.stream().anyMatch(run -> run.id().application().equals(instance))), "Application is on version " + version); } private static void writeControllerVersion(HostName hostname, Version version, CuratorDb db) { @@ -687,6 +714,14 @@ public class VersionStatusTest { .orElseThrow(() -> new IllegalArgumentException("Expected to find version: " + version)); } + private static ApplicationPackage applicationPackage(String upgradePolicy, int majorVersion) { + return new ApplicationPackageBuilder().upgradePolicy(upgradePolicy) + .region("us-west-1") + .region("us-east-3") + .majorVersion(majorVersion) + .build(); + } + private static final ApplicationPackage canaryApplicationPackage = new ApplicationPackageBuilder().upgradePolicy("canary") .region("us-west-1") @@ -707,12 +742,12 @@ public class VersionStatusTest { /** Returns empty prebuilt applications for efficiency */ private ApplicationPackage applicationPackage(String upgradePolicy) { - switch (upgradePolicy) { - case "canary" : return canaryApplicationPackage; - case "default" : return defaultApplicationPackage; - case "conservative" : return conservativeApplicationPackage; - default : throw new IllegalArgumentException("No upgrade policy '" + upgradePolicy + "'"); - } + return switch (upgradePolicy) { + case "canary" -> canaryApplicationPackage; + case "default" -> defaultApplicationPackage; + case "conservative" -> conservativeApplicationPackage; + default -> throw new IllegalArgumentException("No upgrade policy '" + upgradePolicy + "'"); + }; } } diff --git a/controller-server/src/test/resources/horizon/filters-complex.expected.json b/controller-server/src/test/resources/horizon/filters-complex.expected.json index b3416f8a410..30ee6a28d7e 100644 --- a/controller-server/src/test/resources/horizon/filters-complex.expected.json +++ b/controller-server/src/test/resources/horizon/filters-complex.expected.json @@ -7,7 +7,7 @@ "type": "TimeSeriesDataSource", "metric": { "type": "MetricLiteral", - "metric": "Vespa.vespa.qrserver.documents_covered.count" + "metric": "Vespa.vespa.container.documents_covered.count" }, "sourceId": null, "fetchLast": false, diff --git a/controller-server/src/test/resources/horizon/filters-complex.json b/controller-server/src/test/resources/horizon/filters-complex.json index 3acc7fe5044..e21fa61128a 100644 --- a/controller-server/src/test/resources/horizon/filters-complex.json +++ b/controller-server/src/test/resources/horizon/filters-complex.json @@ -7,7 +7,7 @@ "type": "TimeSeriesDataSource", "metric": { "type": "MetricLiteral", - "metric": "Vespa.vespa.qrserver.documents_covered.count" + "metric": "Vespa.vespa.container.documents_covered.count" }, "sourceId": null, "fetchLast": false, |