summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorJon Marius Venstad <jvenstad@yahoo-inc.com>2017-11-16 13:13:48 +0100
committerJon Marius Venstad <jvenstad@yahoo-inc.com>2017-11-16 13:13:48 +0100
commitdf72655a81bcebb1bfed3c61d773c29ef641f589 (patch)
treea8215e37a426d454bc06e0280ab3cf19bb3b7d92 /controller-server
parent015553855b172438d791c4e40ca0d7e1a65d8d65 (diff)
parentcb33c571fcc8a5b27b8a29aef2c236b837121c19 (diff)
Fixed conflict
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java134
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DelayedDeployer.java24
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployer.java35
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobControl.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReadyJobsTrigger.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BlockedChangeDeployer.java)6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ControllerCuratorDb.java69
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java161
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTester.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java34
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java16
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/UpgraderTest.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java99
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-without-change-multiple-deployments.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1.json159
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json31
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-id-without-applications.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-new-id-without-applications.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json68
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json34
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications-with-id.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1.json9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json26
30 files changed, 539 insertions, 430 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
index ddf5b989b07..b854ad3f771 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
@@ -156,8 +156,8 @@ public class Controller extends AbstractComponent {
public Clock clock() { return clock; }
- public URI getElkUri(Environment environment, RegionName region, DeploymentId deploymentId) {
- return elkUrl(zoneRegistry.getLogServerUri(environment, region), deploymentId);
+ public URI getElkUri(DeploymentId deploymentId) {
+ return elkUrl(zoneRegistry.getLogServerUri(deploymentId.zone().environment(), deploymentId.zone().region()), deploymentId);
}
public List<URI> getConfigServerUris(Environment environment, RegionName region) {
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 78dc01fef19..97ac317d15b 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
@@ -145,15 +145,24 @@ public class DeploymentTrigger {
List<JobType> jobs = order.jobsFrom(application.deploymentSpec());
// Should the first step be triggered?
- // TODO: How can the first job not be systemTest (second ccondition)?
- if ( ! jobs.isEmpty() && jobs.get(0).equals(JobType.systemTest) &&
- application.deploying().get() instanceof Change.VersionChange) {
- Version target = ((Change.VersionChange)application.deploying().get()).version();
- JobStatus jobStatus = application.deploymentJobs().jobStatus().get(JobType.systemTest);
- if (jobStatus == null || ! jobStatus.lastTriggered().isPresent()
- || ! jobStatus.lastTriggered().get().version().equals(target)) {
- application = trigger(JobType.systemTest, application, false, "Upgrade to " + target);
- controller.applications().store(application);
+ if ( ! jobs.isEmpty() && jobs.get(0).equals(JobType.systemTest) ) {
+ JobStatus systemTestStatus = application.deploymentJobs().jobStatus().get(JobType.systemTest);
+ if (application.deploying().get() instanceof Change.VersionChange) {
+ Version target = ((Change.VersionChange) application.deploying().get()).version();
+ if (systemTestStatus == null
+ || ! systemTestStatus.lastTriggered().isPresent()
+ || ! systemTestStatus.isSuccess()
+ || ! systemTestStatus.lastTriggered().get().version().equals(target)) {
+ application = trigger(JobType.systemTest, application, false, "Upgrade to " + target);
+ controller.applications().store(application);
+ }
+ }
+ else {
+ JobStatus componentStatus = application.deploymentJobs().jobStatus().get(JobType.component);
+ if (changesAvailable(application, componentStatus, systemTestStatus)) {
+ application = trigger(JobType.systemTest, application, false, "Available change in component");
+ controller.applications().store(application);
+ }
}
}
@@ -184,14 +193,13 @@ public class DeploymentTrigger {
if ( ! application.deploying().isPresent()) return false;
Change change = application.deploying().get();
- if ( ! previous.lastSuccess().isPresent() &&
- ! productionUpgradeHasSucceededFor(previous, change)) return false;
+ if ( ! previous.lastSuccess().isPresent()) return false;
if (change instanceof Change.VersionChange) {
Version targetVersion = ((Change.VersionChange)change).version();
if ( ! (targetVersion.equals(previous.lastSuccess().get().version())) )
return false; // version is outdated
- if (isOnNewerVersionInProductionThan(targetVersion, application, next.type()))
+ if (next != null && isOnNewerVersionInProductionThan(targetVersion, application, next.type()))
return false; // Don't downgrade
}
@@ -200,7 +208,7 @@ public class DeploymentTrigger {
JobStatus.JobRun previousSuccess = previous.lastSuccess().get();
JobStatus.JobRun nextSuccess = next.lastSuccess().get();
- if (previousSuccess.revision().isPresent() && ! previousSuccess.revision().get().equals(nextSuccess.revision().get()))
+ if (previousSuccess.revision().isPresent() && ! previousSuccess.revision().equals(nextSuccess.revision()))
return true;
if ( ! previousSuccess.version().equals(nextSuccess.version()))
return true;
@@ -208,62 +216,6 @@ public class DeploymentTrigger {
}
/**
- * Called periodically to cause triggering of jobs in the background
- */
- public void triggerFailing(ApplicationId applicationId) {
- try (Lock lock = applications().lock(applicationId)) {
- LockedApplication application = applications().require(applicationId, lock);
- if ( ! application.deploying().isPresent()) return; // No ongoing change, no need to retry
-
- // Retry first failing job
- for (JobType jobType : order.jobsFrom(application.deploymentSpec())) {
- JobStatus jobStatus = application.deploymentJobs().jobStatus().get(jobType);
- if (isFailing(application.deploying().get(), jobStatus)) {
- if (shouldRetryNow(jobStatus)) {
- application = trigger(jobType, application, false, "Retrying failing job");
- applications().store(application);
- }
- break;
- }
- }
-
- // Retry dead job
- Optional<JobStatus> firstDeadJob = firstDeadJob(application.deploymentJobs());
- if (firstDeadJob.isPresent()) {
- application = trigger(firstDeadJob.get().type(), application, false, "Retrying dead job");
- applications().store(application);
- }
- }
- }
-
- /** Triggers jobs that have been delayed according to deployment spec */
- public void triggerDelayed() {
- for (Application application : applications().asList()) {
- if ( ! application.deploying().isPresent() ) continue;
- if (application.deploymentJobs().hasFailures()) continue;
- if (application.deploymentJobs().isRunning(controller.applications().deploymentTrigger().jobTimeoutLimit())) continue;
- if (application.deploymentSpec().steps().stream().noneMatch(step -> step instanceof DeploymentSpec.Delay)) {
- continue; // Application does not have any delayed deployments
- }
-
- Optional<JobStatus> lastSuccessfulJob = application.deploymentJobs().jobStatus().values()
- .stream()
- .filter(j -> j.lastSuccess().isPresent())
- .sorted(Comparator.<JobStatus, Instant>comparing(j -> j.lastSuccess().get().at()).reversed())
- .findFirst();
- if ( ! lastSuccessfulJob.isPresent() ) continue;
-
- // Trigger next
- try (Lock lock = applications().lock(application.id())) {
- LockedApplication lockedApplication = applications().require(application.id(), lock);
- lockedApplication = trigger(order.nextAfter(lastSuccessfulJob.get().type(), lockedApplication),
- lockedApplication, "Resuming delayed deployment");
- applications().store(lockedApplication);
- }
- }
- }
-
- /**
* Triggers a change of this application
*
* @param applicationId the application to trigger
@@ -302,42 +254,10 @@ public class DeploymentTrigger {
private ApplicationController applications() { return controller.applications(); }
- /** Returns whether a job is failing for the current change in the given application */
- private boolean isFailing(Change change, JobStatus status) {
- return status != null
- && ! status.isSuccess()
- && status.lastCompleted().isPresent()
- && status.lastCompleted().get().lastCompletedWas(change);
- }
-
private boolean isCapacityConstrained(JobType jobType) {
return jobType == JobType.stagingTest || jobType == JobType.systemTest;
}
- /** Returns the first job that has been running for more than the given timeout */
- private Optional<JobStatus> firstDeadJob(DeploymentJobs jobs) {
- Optional<JobStatus> oldestRunningJob = jobs.jobStatus().values().stream()
- .filter(job -> job.isRunning(Instant.ofEpochMilli(0)))
- .sorted(Comparator.comparing(status -> status.lastTriggered().get().at()))
- .findFirst();
- return oldestRunningJob.filter(job -> job.lastTriggered().get().at().isBefore(jobTimeoutLimit()));
- }
-
- /** Decide whether the job should be triggered by the periodic trigger */
- private boolean shouldRetryNow(JobStatus job) {
- if (job.isSuccess()) return false;
- if (job.isRunning(jobTimeoutLimit())) return false;
-
- // Retry after 10% of the time since it started failing
- Duration aTenthOfFailTime = Duration.ofMillis( (clock.millis() - job.firstFailing().get().at().toEpochMilli()) / 10);
- if (job.lastCompleted().get().at().isBefore(clock.instant().minus(aTenthOfFailTime))) return true;
-
- // ... or retry anyway if we haven't tried in 4 hours
- if (job.lastCompleted().get().at().isBefore(clock.instant().minus(Duration.ofHours(4)))) return true;
-
- return false;
- }
-
/** Retry immediately only if this job just started failing. Otherwise retry periodically */
private boolean shouldRetryNow(Application application, JobType jobType) {
JobStatus jobStatus = application.deploymentJobs().jobStatus().get(jobType);
@@ -446,18 +366,6 @@ public class DeploymentTrigger {
}
/**
- * When upgrading it is ok to trigger the next job even if the previous failed if the previous has earlier succeeded
- * on the version we are currently upgrading to
- */
- private boolean productionUpgradeHasSucceededFor(JobStatus jobStatus, Change change) {
- if ( ! (change instanceof Change.VersionChange) ) return false;
- if ( ! isProduction(jobStatus.type())) return false;
- Optional<JobStatus.JobRun> lastSuccess = jobStatus.lastSuccess();
- if ( ! lastSuccess.isPresent()) return false;
- return lastSuccess.get().version().equals(((Change.VersionChange)change).version());
- }
-
- /**
* Returns whether the current deployed version in the zone given by the job
* is newer than the given version. This may be the case even if the production job
* in question failed, if the failure happens after deployment.
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 4acda712984..1969b41fd63 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
@@ -26,12 +26,10 @@ public class ControllerMaintenance extends AbstractComponent {
private final DeploymentExpirer deploymentExpirer;
private final DeploymentIssueReporter deploymentIssueReporter;
private final MetricsReporter metricsReporter;
- private final FailureRedeployer failureRedeployer;
private final OutstandingChangeDeployer outstandingChangeDeployer;
private final VersionStatusUpdater versionStatusUpdater;
private final Upgrader upgrader;
- private final DelayedDeployer delayedDeployer;
- private final BlockedChangeDeployer blockedChangeDeployer;
+ private final ReadyJobsTrigger readyJobsTrigger;
private final ClusterInfoMaintainer clusterInfoMaintainer;
private final ClusterUtilizationMaintainer clusterUtilizationMaintainer;
private final DeploymentMetricsMaintainer deploymentMetricsMaintainer;
@@ -46,12 +44,10 @@ public class ControllerMaintenance extends AbstractComponent {
deploymentExpirer = new DeploymentExpirer(controller, maintenanceInterval, jobControl);
deploymentIssueReporter = new DeploymentIssueReporter(controller, deploymentIssues, maintenanceInterval, jobControl);
metricsReporter = new MetricsReporter(controller, metric, chefClient, jobControl, controller.system());
- failureRedeployer = new FailureRedeployer(controller, maintenanceInterval, jobControl);
outstandingChangeDeployer = new OutstandingChangeDeployer(controller, maintenanceInterval, jobControl);
versionStatusUpdater = new VersionStatusUpdater(controller, Duration.ofMinutes(3), jobControl);
upgrader = new Upgrader(controller, maintenanceInterval, jobControl, curator);
- delayedDeployer = new DelayedDeployer(controller, maintenanceInterval, jobControl);
- blockedChangeDeployer = new BlockedChangeDeployer(controller, maintenanceInterval, jobControl);
+ readyJobsTrigger = new ReadyJobsTrigger(controller, maintenanceInterval, jobControl);
clusterInfoMaintainer = new ClusterInfoMaintainer(controller, Duration.ofHours(2), jobControl);
clusterUtilizationMaintainer = new ClusterUtilizationMaintainer(controller, Duration.ofHours(2), jobControl);
deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(controller, Duration.ofMinutes(10), jobControl);
@@ -68,12 +64,10 @@ public class ControllerMaintenance extends AbstractComponent {
deploymentExpirer.deconstruct();
deploymentIssueReporter.deconstruct();
metricsReporter.deconstruct();
- failureRedeployer.deconstruct();
outstandingChangeDeployer.deconstruct();
versionStatusUpdater.deconstruct();
upgrader.deconstruct();
- delayedDeployer.deconstruct();
- blockedChangeDeployer.deconstruct();
+ readyJobsTrigger.deconstruct();
clusterUtilizationMaintainer.deconstruct();
clusterInfoMaintainer.deconstruct();
deploymentMetricsMaintainer.deconstruct();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DelayedDeployer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DelayedDeployer.java
deleted file mode 100644
index cb09c41a034..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DelayedDeployer.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Yahoo Holdings. 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.vespa.hosted.controller.Controller;
-
-import java.time.Duration;
-
-/**
- * Maintenance job which triggers jobs that have been delayed according to the applications deployment spec.
- *
- * @author mpolden
- */
-public class DelayedDeployer extends Maintainer {
-
- public DelayedDeployer(Controller controller, Duration interval, JobControl jobControl) {
- super(controller, interval, jobControl);
- }
-
- @Override
- protected void maintain() {
- controller().applications().deploymentTrigger().triggerDelayed();
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployer.java
deleted file mode 100644
index 72f8faa5180..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 Yahoo Holdings. 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.vespa.hosted.controller.Application;
-import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.application.ApplicationList;
-
-import java.time.Duration;
-import java.util.List;
-
-/**
- * Attempts redeployment of failed jobs and deployments.
- *
- * @author bratseth
- * @author mpolden
- */
-public class FailureRedeployer extends Maintainer {
-
- public FailureRedeployer(Controller controller, Duration interval, JobControl jobControl) {
- super(controller, interval, jobControl);
- }
-
- @Override
- public void maintain() {
- List<Application> applications = ApplicationList.from(controller().applications().asList())
- .notPullRequest()
- .asList();
- applications.forEach(application -> triggerFailing(application));
- }
-
- private void triggerFailing(Application application) {
- controller().applications().deploymentTrigger().triggerFailing(application.id());
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobControl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobControl.java
index d7396cb2acb..6aa1b89c605 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobControl.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/JobControl.java
@@ -5,6 +5,7 @@ import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Logger;
@@ -40,7 +41,7 @@ public class JobControl {
* Returns a snapshot of the set of jobs started on this system (whether deactivated or not).
* Each job is represented by its simple (omitting package) class name.
*/
- public Set<String> jobs() { return new HashSet<>(startedJobs); }
+ public Set<String> jobs() { return new LinkedHashSet<>(startedJobs); }
/** Returns an unmodifiable set containing the currently inactive jobs in this */
public Set<String> inactiveJobs() { return curator.readInactiveJobs(); }
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java
index bbef7980273..ebab2054d4f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Maintainer.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.yahoo.component.AbstractComponent;
+import com.yahoo.component.ComponentId;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BlockedChangeDeployer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReadyJobsTrigger.java
index 4a68fd6cfab..f165b4e4ea3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BlockedChangeDeployer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReadyJobsTrigger.java
@@ -14,14 +14,14 @@ import java.time.Duration;
* @author bratseth
*/
@SuppressWarnings("unused")
-public class BlockedChangeDeployer extends Maintainer {
+public class ReadyJobsTrigger extends Maintainer {
- public BlockedChangeDeployer(Controller controller, Duration interval, JobControl jobControl) {
+ public ReadyJobsTrigger(Controller controller, Duration interval, JobControl jobControl) {
super(controller, interval, jobControl);
}
@Override
- protected void maintain() {
+ public void maintain() {
controller().applications().deploymentTrigger().triggerReadyJobs();
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ControllerCuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ControllerCuratorDb.java
deleted file mode 100644
index 53c152308d9..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ControllerCuratorDb.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 Yahoo Holdings. 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.google.inject.Inject;
-import com.yahoo.cloud.config.ClusterInfoConfig;
-import com.yahoo.cloud.config.ZookeeperServerConfig;
-import com.yahoo.net.HostName;
-import com.yahoo.vespa.curator.Curator;
-import com.yahoo.vespa.zookeeper.ZooKeeperServer;
-
-import java.util.stream.Collectors;
-
-/**
- * A CuratorDb that configures its own ZooKeeper cluster
- *
- * @author bratseth
- */
-// TODO: Remove when multi controller is enabled
-@Deprecated
-public class ControllerCuratorDb extends CuratorDb {
-
- /** Use a nonstandard zk port to avoid interfering with connection to the config server zk cluster */
- private static final int zooKeeperPort = 2281;
-
- @SuppressWarnings("unused") // This server is used (only) from the curator instance of this over the network */
- private final ZooKeeperServer zooKeeperServer;
-
- /** Create a curator db which also set up a ZooKeeper server (such that this instance is both client and server) */
- @Inject
- public ControllerCuratorDb(ClusterInfoConfig clusterInfo) {
- super(new Curator(toConnectionSpec(clusterInfo)));
- this.zooKeeperServer = new ZooKeeperServer(toZookeeperServerConfig(clusterInfo));
- }
-
- private static ZookeeperServerConfig toZookeeperServerConfig(ClusterInfoConfig clusterInfo) {
- ZookeeperServerConfig.Builder b = new ZookeeperServerConfig.Builder();
- b.zooKeeperConfigFile("conf/zookeeper/controller-zookeeper.cfg");
- b.dataDir("var/controller-zookeeper");
- b.clientPort(zooKeeperPort);
- b.myidFile("var/controller-zookeeper/myid");
- b.myid(myIndex(clusterInfo));
-
- for (ClusterInfoConfig.Services clusterMember : clusterInfo.services()) {
- ZookeeperServerConfig.Server.Builder server = new ZookeeperServerConfig.Server.Builder();
- server.id(clusterMember.index());
- server.hostname(clusterMember.hostname());
- server.quorumPort(zooKeeperPort + 1);
- server.electionPort(zooKeeperPort + 2);
- b.server(server);
- }
- return new ZookeeperServerConfig(b);
- }
-
- private static Integer myIndex(ClusterInfoConfig clusterInfo) {
- String hostname = HostName.getLocalhost();
- return clusterInfo.services().stream()
- .filter(service -> service.hostname().equals(hostname))
- .map(ClusterInfoConfig.Services::index)
- .findFirst()
- .orElseThrow(() -> new IllegalStateException("Unable to find index for this node by hostname '" +
- hostname + "'"));
- }
-
- private static String toConnectionSpec(ClusterInfoConfig clusterInfo) {
- return clusterInfo.services().stream()
- .map(member -> member.hostname() + ":" + zooKeeperPort)
- .collect(Collectors.joining(","));
- }
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java
index c8c027d91c9..9853320e482 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/Path.java
@@ -98,9 +98,6 @@ public class Path {
*/
public String getRest() { return rest; }
- /** Returns this path as a string */
- public String asString() { return pathString; }
-
@Override
public String toString() {
return "path '" + Arrays.stream(elements).collect(Collectors.joining("/")) + "'";
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 50b80e34788..1bb116809f7 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
@@ -62,7 +62,6 @@ import com.yahoo.vespa.hosted.controller.application.ClusterCost;
import com.yahoo.vespa.hosted.controller.application.ClusterUtilization;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentCost;
-import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.SourceRevision;
@@ -169,7 +168,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (path.matches("/application/v4/cookiefreshness")) return cookieFreshness(request);
if (path.matches("/application/v4/tenant/{tenant}")) return tenant(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/application")) return applications(path.get("tenant"), request);
- if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), path, request);
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}")) return deployment(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/converge")) return waitForConvergence(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service")) return services(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
@@ -222,10 +221,19 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
response.headers().put("Allow", "GET,PUT,POST,DELETE,OPTIONS");
return response;
}
-
+
+ private HttpResponse recursiveRoot(HttpRequest request) {
+ Slime slime = new Slime();
+ Cursor tenantArray = slime.setArray();
+ for (Tenant tenant : controller.tenants().asList())
+ toSlime(tenantArray.addObject(), tenant, request, true);
+ return new SlimeJsonResponse(slime);
+ }
+
private HttpResponse root(HttpRequest request) {
- return new ResourceResponse(request, "user", "tenant", "tenant-pipeline", "athensDomain",
- "property", "cookiefreshness");
+ return request.getBooleanProperty("recursive")
+ ? recursiveRoot(request)
+ : new ResourceResponse(request, "user", "tenant", "tenant-pipeline", "athensDomain", "property", "cookiefreshness");
}
private HttpResponse authenticatedUser(HttpRequest request) {
@@ -310,20 +318,9 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
private HttpResponse tenant(Tenant tenant, HttpRequest request, boolean listApplications) {
- Slime tenantSlime = toSlime(tenant, request, listApplications);
- tenant.getPropertyId().ifPresent(propertyId -> {
- try {
- toSlime(tenantSlime.get(),
- controller.organization().propertyUri(propertyId),
- controller.organization().contactsUri(propertyId),
- controller.organization().issueCreationUri(propertyId),
- controller.organization().contactsFor(propertyId));
- }
- catch (RuntimeException e) {
- log.log(Level.WARNING, "Error fetching property info for " + tenant + " with propertyId " + propertyId, e);
- }
- });
- return new SlimeJsonResponse(tenantSlime);
+ Slime slime = new Slime();
+ toSlime(slime.setObject(), tenant, request, listApplications);
+ return new SlimeJsonResponse(slime);
}
private HttpResponse applications(String tenantName, HttpRequest request) {
@@ -335,18 +332,23 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new SlimeJsonResponse(slime);
}
- private HttpResponse application(String tenantName, String applicationName, Path path, HttpRequest request) {
- Slime slime = new Slime();
- Cursor response = slime.setObject();
-
- com.yahoo.config.provision.ApplicationId applicationId = com.yahoo.config.provision.ApplicationId.from(tenantName, applicationName, "default");
+ private HttpResponse application(String tenantName, String applicationName, HttpRequest request) {
+ ApplicationId applicationId = ApplicationId.from(tenantName, applicationName, "default");
Application application =
controller.applications().get(applicationId)
.orElseThrow(() -> new NotExistsException(applicationId + " not found"));
-
+
+ Slime slime = new Slime();
+ toSlime(slime.setObject(), application, request);
+ return new SlimeJsonResponse(slime);
+ }
+
+ private void toSlime(Cursor object, Application application, HttpRequest request) {
+ object.setString("application", application.id().application().value());
+ object.setString("instance", application.id().instance().value());
// Currently deploying change
if (application.deploying().isPresent()) {
- Cursor deployingObject = response.setObject("deploying");
+ Cursor deployingObject = object.setObject("deploying");
if (application.deploying().get() instanceof Change.VersionChange)
deployingObject.setString("version", ((Change.VersionChange)application.deploying().get()).version().toString());
else if (((Change.ApplicationChange)application.deploying().get()).revision().isPresent())
@@ -358,7 +360,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
.deploymentOrder()
.sortBy(application.deploymentSpec(), application.deploymentJobs().jobStatus().values());
- Cursor deploymentsArray = response.setArray("deploymentJobs");
+ Cursor deploymentsArray = object.setArray("deploymentJobs");
for (JobStatus job : jobStatus) {
Cursor jobObject = deploymentsArray.addObject();
jobObject.setString("type", job.type().jobName());
@@ -371,11 +373,11 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
// Compile version. The version that should be used when building an application
- response.setString("compileVersion", application.compileVersion(controller).toFullString());
+ object.setString("compileVersion", application.compileVersion(controller).toFullString());
// Rotations
- Cursor globalRotationsArray = response.setArray("globalRotations");
- Set<URI> rotations = controller.getRotationUris(applicationId);
+ Cursor globalRotationsArray = object.setArray("globalRotations");
+ Set<URI> rotations = controller.getRotationUris(application.id());
Map<String, RotationStatus> rotationHealthStatus =
rotations.isEmpty() ? Collections.emptyMap() : controller.getHealthStatus(rotations.iterator().next().getHost());
for (URI rotation : rotations)
@@ -385,33 +387,36 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
List<Deployment> deployments = controller.applications().deploymentTrigger()
.deploymentOrder()
.sortBy(application.deploymentSpec().zones(), application.deployments().values());
- Cursor instancesArray = response.setArray("instances");
+ Cursor instancesArray = object.setArray("instances");
for (Deployment deployment : deployments) {
Cursor deploymentObject = instancesArray.addObject();
+
deploymentObject.setString("environment", deployment.zone().environment().value());
deploymentObject.setString("region", deployment.zone().region().value());
deploymentObject.setString("instance", application.id().instance().value()); // pointless
if ( ! rotations.isEmpty())
setRotationStatus(deployment, rotationHealthStatus, deploymentObject);
- deploymentObject.setString("url", withPath(path.asString() +
- "/environment/" + deployment.zone().environment().value() +
- "/region/" + deployment.zone().region().value() +
- "/instance/" + application.id().instance().value(),
- request.getUri()).toString());
+
+ if (request.getBooleanProperty("recursive")) // List full deployment information when recursive.
+ toSlime(deploymentObject, new DeploymentId(application.id(), deployment.zone()), deployment, request);
+ else
+ deploymentObject.setString("url", withPath(request.getUri().getPath() +
+ "/environment/" + deployment.zone().environment().value() +
+ "/region/" + deployment.zone().region().value() +
+ "/instance/" + application.id().instance().value(),
+ request.getUri()).toString());
}
// Metrics
try {
- MetricsService.ApplicationMetrics metrics = controller.metricsService().getApplicationMetrics(applicationId);
- Cursor metricsObject = response.setObject("metrics");
+ MetricsService.ApplicationMetrics metrics = controller.metricsService().getApplicationMetrics(application.id());
+ Cursor metricsObject = object.setObject("metrics");
metricsObject.setDouble("queryServiceQuality", metrics.queryServiceQuality());
metricsObject.setDouble("writeServiceQuality", metrics.writeServiceQuality());
}
catch (RuntimeException e) {
log.log(Level.WARNING, "Failed getting Yamas metrics", Exceptions.toMessageString(e));
}
-
- return new SlimeJsonResponse(slime);
}
private HttpResponse deployment(String tenantName, String applicationName, String instanceName, String environment, String region, HttpRequest request) {
@@ -426,21 +431,23 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (deployment == null)
throw new NotExistsException(application + " is not deployed in " + deploymentId.zone());
- Optional<InstanceEndpoints> deploymentEndpoints = controller.applications().getDeploymentEndpoints(deploymentId);
-
Slime slime = new Slime();
- Cursor response = slime.setObject();
+ toSlime(slime.setObject(), deploymentId, deployment, request);
+ return new SlimeJsonResponse(slime);
+ }
+
+ private void toSlime(Cursor response, DeploymentId deploymentId, Deployment deployment, HttpRequest request) {
+
+ Optional<InstanceEndpoints> deploymentEndpoints = controller.applications().getDeploymentEndpoints(deploymentId);
Cursor serviceUrlArray = response.setArray("serviceUrls");
if (deploymentEndpoints.isPresent()) {
for (URI uri : deploymentEndpoints.get().getContainerEndpoints())
serviceUrlArray.addString(uri.toString());
}
- response.setString("nodes", withPath("/zone/v2/" + environment + "/" + region + "/nodes/v2/node/?&recursive=true&application=" + tenantName + "." + applicationName + "." + instanceName, request.getUri()).toString());
+ response.setString("nodes", withPath("/zone/v2/" + deploymentId.zone().environment() + "/" + deploymentId.zone().region() + "/nodes/v2/node/?&recursive=true&application=" + deploymentId.applicationId().tenant() + "." + deploymentId.applicationId().application() + "." + deploymentId.applicationId().instance(), request.getUri()).toString());
- Environment env = Environment.from(environment);
- RegionName regionName = RegionName.from(region);
- URI elkUrl = controller.getElkUri(env, regionName, deploymentId);
+ URI elkUrl = controller.getElkUri(deploymentId);
if (elkUrl != null)
response.setString("elkUrl", elkUrl.toString());
@@ -448,10 +455,11 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
response.setString("version", deployment.version().toFullString());
response.setString("revision", deployment.revision().id());
response.setLong("deployTimeEpochMs", deployment.at().toEpochMilli());
- Optional<Duration> deploymentTimeToLive = controller.zoneRegistry().getDeploymentTimeToLive(Environment.from(environment), RegionName.from(region));
+ Optional<Duration> deploymentTimeToLive = controller.zoneRegistry().getDeploymentTimeToLive(deploymentId.zone().environment(), deploymentId.zone().region());
deploymentTimeToLive.ifPresent(duration -> response.setLong("expiryTimeEpochMs", deployment.at().plus(duration).toEpochMilli()));
- application.deploymentJobs().projectId().ifPresent(i -> response.setString("screwdriverId", String.valueOf(i)));
+ controller.applications().get(deploymentId.applicationId()).flatMap(application -> application.deploymentJobs().projectId())
+ .ifPresent(i -> response.setString("screwdriverId", String.valueOf(i)));
sourceRevisionToSlime(deployment.revision().source(), response);
// Cost
@@ -467,8 +475,6 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
metricsObject.setDouble("documentCount", metrics.documentCount());
metricsObject.setDouble("queryLatencyMillis", metrics.queryLatencyMillis());
metricsObject.setDouble("writeLatencyMillis", metrics.writeLatencyMillis());
-
- return new SlimeJsonResponse(slime);
}
private void toSlime(ApplicationRevision revision, Cursor object) {
@@ -575,7 +581,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private HttpResponse services(String tenantName, String applicationName, String instanceName, String environment, String region, HttpRequest request) {
ApplicationView applicationView = controller.getApplicationView(tenantName, applicationName, instanceName, environment, region);
ServiceApiResponse response = new ServiceApiResponse(new Zone(Environment.from(environment), RegionName.from(region)),
- new com.yahoo.config.provision.ApplicationId.Builder().tenant(tenantName).applicationName(applicationName).instanceName(instanceName).build(),
+ new ApplicationId.Builder().tenant(tenantName).applicationName(applicationName).instanceName(instanceName).build(),
controller.getConfigServerUris(Environment.from(environment), RegionName.from(region)),
request.getUri());
response.setResponse(applicationView);
@@ -585,7 +591,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private HttpResponse service(String tenantName, String applicationName, String instanceName, String environment, String region, String serviceName, String restPath, HttpRequest request) {
Map<?,?> result = controller.getServiceApiResponse(tenantName, applicationName, instanceName, environment, region, serviceName, restPath);
ServiceApiResponse response = new ServiceApiResponse(new Zone(Environment.from(environment), RegionName.from(region)),
- new com.yahoo.config.provision.ApplicationId.Builder().tenant(tenantName).applicationName(applicationName).instanceName(instanceName).build(),
+ new ApplicationId.Builder().tenant(tenantName).applicationName(applicationName).instanceName(instanceName).build(),
controller.getConfigServerUris(Environment.from(environment), RegionName.from(region)),
request.getUri());
response.setResponse(result, serviceName, restPath);
@@ -684,7 +690,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
authorizer.throwIfUnauthorized(new TenantId(tenantName), request);
Application application;
try {
- application = controller.applications().createApplication(com.yahoo.config.provision.ApplicationId.from(tenantName, applicationName, "default"), authorizer.getNToken(request));
+ application = controller.applications().createApplication(ApplicationId.from(tenantName, applicationName, "default"), authorizer.getNToken(request));
}
catch (ZmsException e) { // TODO: Push conversion down
if (e.getCode() == com.yahoo.jdisc.Response.Status.FORBIDDEN)
@@ -811,7 +817,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private HttpResponse deleteApplication(String tenantName, String applicationName, HttpRequest request) {
authorizer.throwIfUnauthorized(new TenantId(tenantName), request);
- com.yahoo.config.provision.ApplicationId id = com.yahoo.config.provision.ApplicationId.from(tenantName, applicationName, "default");
+ ApplicationId id = ApplicationId.from(tenantName, applicationName, "default");
Application deleted = controller.applications().deleteApplication(id, authorizer.getNToken(request));
if (deleted == null)
return ErrorResponse.notFoundError("Could not delete application '" + id + "': Application not found");
@@ -874,7 +880,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return authorizer.getPrincipalIfAny(request).map(Principal::getName);
}
- private void toSlime(Tenant tenant, Cursor object, HttpRequest request, boolean listApplications) {
+ private void toSlime(Cursor object, Tenant tenant, HttpRequest request, boolean listApplications) {
+ object.setString("tenant", tenant.getId().id());
object.setString("type", tenant.tenantType().name());
tenant.getAthensDomain().ifPresent(a -> object.setString("athensDomain", a.id()));
tenant.getProperty().ifPresent(p -> object.setString("property", p.id()));
@@ -883,10 +890,30 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
Cursor applicationArray = object.setArray("applications");
if (listApplications) { // This cludge is needed because we call this after deleting the tenant. As this call makes another tenant lookup it will fail. TODO is to support lookup on tenant
for (Application application : controller.applications().asList(TenantName.from(tenant.getId().id()))) {
- if (application.id().instance().isDefault()) // TODO: Skip non-default applications until supported properly
- toSlime(application, applicationArray.addObject(), request);
+ if (application.id().instance().isDefault()) {// TODO: Skip non-default applications until supported properly
+ if (request.getBooleanProperty("recursive"))
+ toSlime(applicationArray.addObject(), application, request);
+ else
+ toSlime(application, applicationArray.addObject(), request);
+ }
}
}
+ tenant.getPropertyId().ifPresent(propertyId -> {
+ try {
+ object.setString("propertyUrl", controller.organization().propertyUri(propertyId).toString());
+ object.setString("contactsUrl", controller.organization().contactsUri(propertyId).toString());
+ object.setString("issueCreationUrl", controller.organization().issueCreationUri(propertyId).toString());
+ Cursor lists = object.setArray("contacts");
+ for (List<? extends User> contactList : controller.organization().contactsFor(propertyId)) {
+ Cursor list = lists.addArray();
+ for (User contact : contactList)
+ list.addString(contact.displayName());
+ }
+ }
+ catch (RuntimeException e) {
+ log.log(Level.WARNING, "Error fetching property info for " + tenant + " with propertyId " + propertyId, e);
+ }
+ });
}
// A tenant has different content when in a list ... antipattern, but not solvable before application/v5
@@ -989,24 +1016,6 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return Joiner.on("/").join(elements);
}
- private Slime toSlime(Tenant tenant, HttpRequest request, boolean listApplications) {
- Slime slime = new Slime();
- toSlime(tenant, slime.setObject(), request, listApplications);
- return slime;
- }
-
- private void toSlime(Cursor root, URI propertyUri, URI contactsUri, URI issueCreationUri, List<? extends List<? extends User>> contacts) {
- root.setString("propertyUrl", propertyUri.toString());
- root.setString("contactsUrl", contactsUri.toString());
- root.setString("issueCreationUrl", issueCreationUri.toString());
- Cursor lists = root.setArray("contacts");
- for (List<? extends User> contactList : contacts) {
- Cursor list = lists.addArray();
- for (User contact : contactList)
- list.addString(contact.displayName());
- }
- }
-
private void toSlime(Application application, Cursor object, HttpRequest request) {
object.setString("application", application.id().application().value());
object.setString("instance", application.id().instance().value());
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 1574801b77b..5f4d40ed2d8 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
@@ -467,7 +467,7 @@ public class ControllerTest {
// back of the queue
tester.clock().advance(Duration.ofHours(3));
tester.clock().advance(Duration.ofMinutes(50));
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
List<BuildJob> nextJobs = buildSystem.takeJobsToRun();
assertEquals(2, nextJobs.size());
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 9f1a373f3dd..23033fbc4f8 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
@@ -16,7 +16,7 @@ import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
-import com.yahoo.vespa.hosted.controller.maintenance.FailureRedeployer;
+import com.yahoo.vespa.hosted.controller.maintenance.ReadyJobsTrigger;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
import com.yahoo.vespa.hosted.controller.maintenance.Upgrader;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
@@ -46,7 +46,7 @@ public class DeploymentTester {
private final ControllerTester tester;
private final Upgrader upgrader;
- private final FailureRedeployer failureRedeployer;
+ private final ReadyJobsTrigger readyJobTrigger;
public DeploymentTester() {
this(new ControllerTester());
@@ -57,13 +57,13 @@ public class DeploymentTester {
tester.curator().writeUpgradesPerMinute(100);
this.upgrader = new Upgrader(tester.controller(), maintenanceInterval, new JobControl(tester.curator()),
tester.curator());
- this.failureRedeployer = new FailureRedeployer(tester.controller(), maintenanceInterval,
- new JobControl(tester.curator()));
+ this.readyJobTrigger = new ReadyJobsTrigger(tester.controller(), maintenanceInterval,
+ new JobControl(tester.curator()));
}
public Upgrader upgrader() { return upgrader; }
- public FailureRedeployer failureRedeployer() { return failureRedeployer; }
+ public ReadyJobsTrigger readyJobTrigger() { return readyJobTrigger; }
public Controller controller() { return tester.controller(); }
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 3ca5e915ca9..10f8e80f318 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
@@ -13,7 +13,7 @@ import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs.JobType;
-import com.yahoo.vespa.hosted.controller.maintenance.BlockedChangeDeployer;
+import com.yahoo.vespa.hosted.controller.maintenance.ReadyJobsTrigger;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
import org.junit.Test;
@@ -63,7 +63,7 @@ public class DeploymentTriggerTest {
tester.deployAndNotify(app, applicationPackage, false, JobType.systemTest);
tester.clock().advance(Duration.ofHours(1));
assertEquals("Nothing scheduled", 0, tester.buildSystem().jobs().size());
- tester.failureRedeployer().maintain(); // Causes retry of systemTests
+ tester.readyJobTrigger().maintain(); // Causes retry of systemTests
assertEquals("Scheduled retry", 1, tester.buildSystem().jobs().size());
tester.deployAndNotify(app, applicationPackage, true, JobType.systemTest);
@@ -71,7 +71,7 @@ public class DeploymentTriggerTest {
// staging-test times out and is retried
tester.buildSystem().takeJobsToRun();
tester.clock().advance(Duration.ofHours(12).plus(Duration.ofSeconds(1)));
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertEquals("Retried dead job", 1, tester.buildSystem().jobs().size());
assertEquals(JobType.stagingTest.jobName(), tester.buildSystem().jobs().get(0).jobName());
}
@@ -128,7 +128,7 @@ public class DeploymentTriggerTest {
// 30 seconds pass, us-west-1 is triggered
tester.clock().advance(Duration.ofSeconds(30));
- tester.deploymentTrigger().triggerDelayed();
+ tester.deploymentTrigger().triggerReadyJobs();
// Consume us-west-1 job without reporting completion
assertEquals(1, buildSystem.jobs().size());
@@ -137,7 +137,7 @@ public class DeploymentTriggerTest {
// 3 minutes pass, delayed trigger does nothing as us-west-1 is still in progress
tester.clock().advance(Duration.ofMinutes(3));
- tester.deploymentTrigger().triggerDelayed();
+ tester.deploymentTrigger().triggerReadyJobs();
assertTrue("No more jobs triggered at this time", buildSystem.jobs().isEmpty());
// us-west-1 completes
@@ -145,18 +145,18 @@ public class DeploymentTriggerTest {
tester.notifyJobCompletion(JobType.productionUsWest1, application, true);
// Delayed trigger does nothing as not enough time has passed after us-west-1 completion
- tester.deploymentTrigger().triggerDelayed();
+ tester.deploymentTrigger().triggerReadyJobs();
assertTrue("No more jobs triggered at this time", buildSystem.jobs().isEmpty());
// 3 minutes pass, us-central-1 is triggered
tester.clock().advance(Duration.ofMinutes(3));
- tester.deploymentTrigger().triggerDelayed();
+ tester.deploymentTrigger().triggerReadyJobs();
tester.deployAndNotify(application, applicationPackage, true, JobType.productionUsCentral1);
assertTrue("All jobs consumed", buildSystem.jobs().isEmpty());
// Delayed trigger job runs again, with nothing to trigger
tester.clock().advance(Duration.ofMinutes(10));
- tester.deploymentTrigger().triggerDelayed();
+ tester.deploymentTrigger().triggerReadyJobs();
assertTrue("All jobs consumed", buildSystem.jobs().isEmpty());
}
@@ -270,9 +270,9 @@ public class DeploymentTriggerTest {
public void testBlockRevisionChange() {
ManualClock clock = new ManualClock(Instant.parse("2017-09-26T17:30:00.00Z")); // Tuesday, 17:30
DeploymentTester tester = new DeploymentTester(new ControllerTester(clock));
- BlockedChangeDeployer blockedChangeDeployer = new BlockedChangeDeployer(tester.controller(),
- Duration.ofHours(1),
- new JobControl(tester.controllerTester().curator()));
+ ReadyJobsTrigger readyJobsTrigger = new ReadyJobsTrigger(tester.controller(),
+ Duration.ofHours(1),
+ new JobControl(tester.controllerTester().curator()));
Version version = Version.fromString("5.0");
tester.updateVersionStatus(version);
@@ -291,7 +291,7 @@ public class DeploymentTriggerTest {
tester.clock().advance(Duration.ofHours(1)); // --------------- Enter block window: 18:30
- blockedChangeDeployer.run();
+ readyJobsTrigger.run();
assertEquals(0, tester.buildSystem().jobs().size());
String searchDefinition =
@@ -305,7 +305,7 @@ public class DeploymentTriggerTest {
tester.deployTestOnly(app, changedApplication);
- blockedChangeDeployer.run();
+ readyJobsTrigger.run();
assertEquals(0, tester.buildSystem().jobs().size());
tester.clock().advance(Duration.ofHours(2)); // ---------------- Exit block window: 20:30
@@ -318,14 +318,14 @@ public class DeploymentTriggerTest {
@Test
public void testUpgradingButNoJobStarted() {
DeploymentTester tester = new DeploymentTester();
- BlockedChangeDeployer blockedChangeDeployer = new BlockedChangeDeployer(tester.controller(),
- Duration.ofHours(1),
- new JobControl(tester.controllerTester().curator()));
+ ReadyJobsTrigger readyJobsTrigger = new ReadyJobsTrigger(tester.controller(),
+ Duration.ofHours(1),
+ new JobControl(tester.controllerTester().curator()));
LockedApplication app = (LockedApplication)tester.createAndDeploy("default0", 3, "default");
// Store that we are upgrading but don't start the system-tests job
tester.controller().applications().store(app.withDeploying(Optional.of(new Change.VersionChange(Version.fromString("6.2")))));
assertEquals(0, tester.buildSystem().jobs().size());
- blockedChangeDeployer.run();
+ readyJobsTrigger.run();
assertEquals(1, tester.buildSystem().jobs().size());
assertEquals("system-test", tester.buildSystem().jobs().get(0).jobName());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java
index d540db7c790..fd00123c697 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/FailureRedeployerTest.java
@@ -69,7 +69,7 @@ public class FailureRedeployerTest {
// Failure redeployer does not retry failing job for prod.us-east-3 as there's an ongoing deployment
tester.clock().advance(Duration.ofMinutes(1));
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertFalse("Job is not retried", tester.buildSystem().jobs().stream()
.anyMatch(j -> j.jobName().equals(DeploymentJobs.JobType.productionUsEast3.jobName())));
@@ -87,7 +87,7 @@ public class FailureRedeployerTest {
// Failure redeployer retries job
tester.clock().advance(Duration.ofMinutes(5));
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertEquals("Job is retried", 1, tester.buildSystem().jobs().size());
// Production job finally succeeds
@@ -111,12 +111,12 @@ public class FailureRedeployerTest {
// staging-test starts, but does not complete
assertEquals(DeploymentJobs.JobType.stagingTest.jobName(), tester.buildSystem().takeJobsToRun().get(0).jobName());
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertTrue("No jobs retried", tester.buildSystem().jobs().isEmpty());
// Just over 12 hours pass, job is retried
tester.clock().advance(Duration.ofHours(12).plus(Duration.ofSeconds(1)));
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertEquals(DeploymentJobs.JobType.stagingTest.jobName(), tester.buildSystem().takeJobsToRun().get(0).jobName());
// Deployment completes
@@ -169,7 +169,7 @@ public class FailureRedeployerTest {
// Failure re-deployer does not retry failing system-test job as it failed for an older change
tester.clock().advance(Duration.ofMinutes(5));
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertTrue("No jobs retried", tester.buildSystem().jobs().isEmpty());
}
@@ -217,7 +217,7 @@ public class FailureRedeployerTest {
tester.buildSystem().takeJobsToRun();
// Failure re-deployer runs
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertTrue("No jobs retried", tester.buildSystem().jobs().isEmpty());
// Deployment completes
@@ -242,7 +242,7 @@ public class FailureRedeployerTest {
Application application = tester.controllerTester().createApplication(slime);
// Failure redeployer does not restart deployment
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertTrue("No jobs scheduled", tester.buildSystem().jobs().isEmpty());
}
@@ -262,7 +262,7 @@ public class FailureRedeployerTest {
tester.controllerTester().createApplication(slime);
// Failure redeployer does not restart deployment
- tester.failureRedeployer().maintain();
+ tester.readyJobTrigger().maintain();
assertTrue("No jobs scheduled", tester.buildSystem().jobs().isEmpty());
}
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 e92d5400a3d..64082adc1c0 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
@@ -474,9 +474,9 @@ public class UpgraderTest {
public void testBlockVersionChangeHalfwayThough() {
ManualClock clock = new ManualClock(Instant.parse("2017-09-26T17:00:00.00Z")); // Tuesday, 17:00
DeploymentTester tester = new DeploymentTester(new ControllerTester(clock));
- BlockedChangeDeployer blockedChangeDeployer = new BlockedChangeDeployer(tester.controller(),
- Duration.ofHours(1),
- new JobControl(tester.controllerTester().curator()));
+ ReadyJobsTrigger readyJobsTrigger = new ReadyJobsTrigger(tester.controller(),
+ Duration.ofHours(1),
+ new JobControl(tester.controllerTester().curator()));
Version version = Version.fromString("5.0");
tester.updateVersionStatus(version);
@@ -506,12 +506,12 @@ public class UpgraderTest {
// One hour passes, time is 19:00, still no upgrade
tester.clock().advance(Duration.ofHours(1));
- blockedChangeDeployer.maintain();
+ readyJobsTrigger.maintain();
assertTrue("No jobs scheduled", tester.buildSystem().jobs().isEmpty());
// Another hour pass, time is 20:00 and application upgrades
tester.clock().advance(Duration.ofHours(1));
- blockedChangeDeployer.maintain();
+ readyJobsTrigger.maintain();
tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsCentral1);
tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.productionUsEast3);
assertTrue("All jobs consumed", tester.buildSystem().jobs().isEmpty());
@@ -528,9 +528,9 @@ public class UpgraderTest {
public void testBlockVersionChangeHalfwayThoughThenNewVersion() {
ManualClock clock = new ManualClock(Instant.parse("2017-09-29T16:00:00.00Z")); // Friday, 16:00
DeploymentTester tester = new DeploymentTester(new ControllerTester(clock));
- BlockedChangeDeployer blockedChangeDeployer = new BlockedChangeDeployer(tester.controller(),
- Duration.ofHours(1),
- new JobControl(tester.controllerTester().curator()));
+ ReadyJobsTrigger readyJobsTrigger = new ReadyJobsTrigger(tester.controller(),
+ Duration.ofHours(1),
+ new JobControl(tester.controllerTester().curator()));
Version version = Version.fromString("5.0");
tester.updateVersionStatus(version);
@@ -565,14 +565,14 @@ public class UpgraderTest {
version = Version.fromString("5.2");
tester.updateVersionStatus(version);
tester.upgrader().maintain();
- blockedChangeDeployer.maintain();
+ readyJobsTrigger.maintain();
assertTrue("Nothing is scheduled", tester.buildSystem().jobs().isEmpty());
// Monday morning: We are not blocked
tester.clock().advance(Duration.ofDays(1)); // Sunday, 17:00
tester.clock().advance(Duration.ofHours(17)); // Monday, 10:00
tester.upgrader().maintain();
- blockedChangeDeployer.maintain();
+ readyJobsTrigger.maintain();
// We proceed with the new version in the expected order, not starting with the previously blocked version:
// Test jobs are run with the new version, but not production as we are in the block window
tester.deployAndNotify(app, applicationPackage, true, DeploymentJobs.JobType.systemTest);
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 e3443d6c014..55742c5fec8 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
@@ -99,6 +99,38 @@ public class ApplicationApiTest extends ControllerContainerTest {
// GET all tenants
tester.assertResponse(request("/application/v4/tenant/", "", Request.Method.GET),
new File("tenant-list.json"));
+
+
+ // Add another Athens domain, so we can try to create more tenants
+ addTenantAthenzDomain("domain2", "mytenant"); // New domain to test tenant w/property ID
+ // Add property info for that property id, as well, in the mock organization.
+ addPropertyData((MockOrganization) controllerTester.controller().organization(), "1234");
+ // POST (add) a tenant with property ID
+ tester.assertResponse(request("/application/v4/tenant/tenant2",
+ "{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}",
+ Request.Method.POST),
+ new File("tenant-without-applications-with-id.json"));
+ // PUT (modify) a tenant with property ID
+ tester.assertResponse(request("/application/v4/tenant/tenant2",
+ "{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}",
+ Request.Method.PUT),
+ new File("tenant-without-applications-with-id.json"));
+ // GET a tenant with property ID
+ tester.assertResponse(request("/application/v4/tenant/tenant2", "", Request.Method.GET),
+ new File("tenant-without-applications-with-id.json"));
+
+ // Test legacy OpsDB tenants
+ // POST (add) an OpsDB tenant with property ID
+ tester.assertResponse(request("/application/v4/tenant/tenant3",
+ "{\"userGroup\":\"group1\",\"property\":\"property1\",\"propertyId\":\"1234\"}",
+ Request.Method.POST),
+ new File("opsdb-tenant-with-id-without-applications.json"));
+ // PUT (modify) the OpsDB tenant to set another property
+ tester.assertResponse(request("/application/v4/tenant/tenant3",
+ "{\"userGroup\":\"group1\",\"property\":\"property2\",\"propertyId\":\"4321\"}",
+ Request.Method.PUT),
+ new File("opsdb-tenant-with-new-id-without-applications.json"));
+
// POST (create) an application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1",
"",
@@ -183,6 +215,27 @@ public class ApplicationApiTest extends ControllerContainerTest {
setDeploymentMaintainedInfo(controllerTester);
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default", "", Request.Method.GET),
new File("deployment.json"));
+
+ // GET at root, with "&recursive=true", returns info about all tenants, their applications and their deployments
+ tester.assertResponse(request("/application/v4/",
+ "",
+ Request.Method.GET,
+ "domain1", "mytenant&recursive=true"),
+ new File("recursive-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/",
+ "",
+ Request.Method.GET,
+ "domain1", "mytenant&recursive=true"),
+ new File("tenant1.json"));
+ // GET at an application, with "&recursive=true", returns full info about its deployments
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/",
+ "",
+ Request.Method.GET,
+ "domain1", "mytenant&recursive=true"),
+ new File("application1.json"));
+
+
// POST a 'restart application' command
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/restart",
"",
@@ -241,36 +294,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/", "", Request.Method.OPTIONS),
"");
- // Add another Athens domain, so we can try to create more tenants
- addTenantAthenzDomain("domain2", "mytenant"); // New domain to test tenant w/property ID
- // Add property info for that property id, as well, in the mock organization.
- addPropertyData((MockOrganization) controllerTester.controller().organization(), "1234");
- // POST (add) a tenant with property ID
- tester.assertResponse(request("/application/v4/tenant/tenant2",
- "{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}",
- Request.Method.POST),
- new File("tenant-without-applications-with-id.json"));
- // PUT (modify) a tenant with property ID
- tester.assertResponse(request("/application/v4/tenant/tenant2",
- "{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}",
- Request.Method.PUT),
- new File("tenant-without-applications-with-id.json"));
- // GET a tenant with property ID
- tester.assertResponse(request("/application/v4/tenant/tenant2", "", Request.Method.GET),
- new File("tenant-without-applications-with-id.json"));
-
- // Test legacy OpsDB tenants
- // POST (add) an OpsDB tenant with property ID
- tester.assertResponse(request("/application/v4/tenant/tenant3",
- "{\"userGroup\":\"group1\",\"property\":\"property1\",\"propertyId\":\"1234\"}",
- Request.Method.POST),
- new File("opsdb-tenant-with-id-without-applications.json"));
- // PUT (modify) the OpsDB tenant to set another property
- tester.assertResponse(request("/application/v4/tenant/tenant3",
- "{\"userGroup\":\"group1\",\"property\":\"property2\",\"propertyId\":\"4321\"}",
- Request.Method.PUT),
- new File("opsdb-tenant-with-new-id-without-applications.json"));
-
// GET global rotation status
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/global-rotation", "", Request.Method.GET),
new File("global-rotation.json"));
@@ -295,13 +318,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
controllerTester.controller().deconstruct();
}
- private void addPropertyData(MockOrganization organization, String propertyIdValue) {
- PropertyId propertyId = new PropertyId(propertyIdValue);
- organization.addProperty(propertyId);
- organization.setContactsFor(propertyId, Arrays.asList(Collections.singletonList(User.from("alice")),
- Collections.singletonList(User.from("bob"))));
- }
-
@Test
public void testDeployDirectly() throws Exception {
// Setup
@@ -627,7 +643,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
"{\"athensDomain\":\"domain2\", \"property\":\"property1\"}",
Request.Method.PUT,
"domain1", authorizedUser),
- "{\"type\":\"ATHENS\",\"athensDomain\":\"domain2\",\"property\":\"property1\",\"applications\":[]}",
+ "{\"tenant\":\"tenant1\",\"type\":\"ATHENS\",\"athensDomain\":\"domain2\",\"property\":\"property1\",\"applications\":[]}",
200);
// Deleting a tenant for an Athens domain the user is not admin for is disallowed
@@ -789,4 +805,11 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
}
+ private void addPropertyData(MockOrganization organization, String propertyIdValue) {
+ PropertyId propertyId = new PropertyId(propertyIdValue);
+ organization.addProperty(propertyId);
+ organization.setContactsFor(propertyId, Arrays.asList(Collections.singletonList(User.from("alice")),
+ Collections.singletonList(User.from("bob"))));
+ }
+
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-without-change-multiple-deployments.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-without-change-multiple-deployments.json
index a1bcc3eeb67..6442ddf5c02 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-without-change-multiple-deployments.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-without-change-multiple-deployments.json
@@ -1,4 +1,6 @@
{
+ "application": "application1",
+ "instance": "default",
"deploymentJobs": [
{
"type": "component",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application.json
index f399d6c9188..fdd3dcc4d5c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application.json
@@ -1,4 +1,6 @@
{
+ "application": "application1",
+ "instance": "default",
"deploying": {
"version": "(ignore)"
},
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1.json
new file mode 100644
index 00000000000..3ebb870d75b
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1.json
@@ -0,0 +1,159 @@
+{
+ "application": "application1",
+ "instance": "default",
+ "deploying": {
+ "version": "6.1"
+ },
+ "deploymentJobs": [
+ {
+ "type": "system-test",
+ "success": true,
+ "lastTriggered": {
+ "id": -1,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "",
+ "at": "(ignore)"
+ },
+ "lastCompleted": {
+ "id": 42,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "",
+ "at": "(ignore)"
+ },
+ "lastSuccess": {
+ "id": 42,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "",
+ "at": "(ignore)"
+ }
+ },
+ {
+ "type": "staging-test",
+ "success": true,
+ "lastTriggered": {
+ "id": -1,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "system-test completed",
+ "at": "(ignore)"
+ },
+ "lastCompleted": {
+ "id": 42,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "system-test completed",
+ "at": "(ignore)"
+ },
+ "lastSuccess": {
+ "id": 42,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "system-test completed",
+ "at": "(ignore)"
+ }
+ },
+ {
+ "type": "production-corp-us-east-1",
+ "success": false,
+ "lastTriggered": {
+ "id": -1,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "Immediate retry on failure",
+ "at": "(ignore)"
+ },
+ "lastCompleted": {
+ "id": 42,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "staging-test completed",
+ "at": "(ignore)"
+ },
+ "firstFailing": {
+ "id": 42,
+ "version": "6.1.0",
+ "revision": {
+ "hash": "(ignore)",
+ "source": {
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1"
+ }
+ },
+ "reason": "staging-test completed",
+ "at": "(ignore)"
+ }
+ }
+ ],
+ "compileVersion": "6.1.0",
+ "globalRotations": [
+ "http://fake-global-rotation-tenant1.application1"
+ ],
+ "instances": [
+ @include(dev-us-west-1.json),
+ @include(prod-corp-us-east-1.json)
+ ],
+ "metrics": {
+ "queryServiceQuality": 0.5,
+ "writeServiceQuality": 0.7
+ }
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json
new file mode 100644
index 00000000000..de40861739a
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-west-1.json
@@ -0,0 +1,31 @@
+{
+ "environment": "dev",
+ "region": "us-west-1",
+ "instance": "default",
+ "serviceUrls": [
+ "http://old-endpoint.vespa.yahooapis.com:4080",
+ "http://qrs-endpoint.vespa.yahooapis.com:4080",
+ "http://feeding-endpoint.vespa.yahooapis.com:4080",
+ "http://global-endpoint.vespa.yahooapis.com:4080",
+ "http://alias-endpoint.vespa.yahooapis.com:4080"
+ ],
+ "nodes": "http://localhost:8080/zone/v2/dev/us-west-1/nodes/v2/node/%3F&recursive=true&application=tenant1.application1.default",
+ "yamasUrl": "http://monitoring-system.test/?environment=dev&region=us-west-1&application=tenant1.application1",
+ "version": "6.1.0",
+ "revision": "(ignore)",
+ "deployTimeEpochMs": "(ignore)",
+ "screwdriverId": "123",
+ "cost": {
+ "tco": 0,
+ "waste": 0,
+ "utilization": 0.0,
+ "cluster": {}
+ },
+ "metrics": {
+ "queriesPerSecond": 0.0,
+ "writesPerSecond": 0.0,
+ "documentCount": 0.0,
+ "queryLatencyMillis": 0.0,
+ "writeLatencyMillis": 0.0
+ }
+} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-id-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-id-without-applications.json
index 8acb4a045f3..a2e70d9c1eb 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-id-without-applications.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-id-without-applications.json
@@ -1,4 +1,5 @@
{
+ "tenant": "tenant3",
"type": "OPSDB",
"property": "property1",
"propertyId": "1234",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-new-id-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-new-id-without-applications.json
index 3f4b6017971..f9161ea49b1 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-new-id-without-applications.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/opsdb-tenant-with-new-id-without-applications.json
@@ -1,4 +1,5 @@
{
+ "tenant": "tenant3",
"type": "OPSDB",
"property": "property2",
"propertyId": "4321",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json
new file mode 100644
index 00000000000..75b257da0ed
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-corp-us-east-1.json
@@ -0,0 +1,68 @@
+{
+ "environment": "prod",
+ "region": "corp-us-east-1",
+ "instance": "default",
+ "bcpStatus": {
+ "rotationStatus": "UNKNOWN"
+ },
+ "serviceUrls": [
+ "http://old-endpoint.vespa.yahooapis.com:4080",
+ "http://qrs-endpoint.vespa.yahooapis.com:4080",
+ "http://feeding-endpoint.vespa.yahooapis.com:4080",
+ "http://global-endpoint.vespa.yahooapis.com:4080",
+ "http://alias-endpoint.vespa.yahooapis.com:4080"
+ ],
+ "nodes": "http://localhost:8080/zone/v2/prod/corp-us-east-1/nodes/v2/node/%3F&recursive=true&application=tenant1.application1.default",
+ "elkUrl": "http://log.prod.corp-us-east-1.test/#/discover?_g=()&_a=(columns:!(_source),index:'logstash-*',interval:auto,query:(query_string:(analyze_wildcard:!t,query:'HV-tenant:%22tenant1%22%20AND%20HV-application:%22application1%22%20AND%20HV-region:%22corp-us-east-1%22%20AND%20HV-instance:%22default%22%20AND%20HV-environment:%22prod%22')),sort:!('@timestamp',desc))",
+ "yamasUrl": "http://monitoring-system.test/?environment=prod&region=corp-us-east-1&application=tenant1.application1",
+ "version": "6.1.0",
+ "revision": "(ignore)",
+ "deployTimeEpochMs": "(ignore)",
+ "screwdriverId": "123",
+ "gitRepository": "repository1",
+ "gitBranch": "master",
+ "gitCommit": "commit1",
+ "cost": {
+ "tco": 74,
+ "waste": 0,
+ "utilization": 2.999999999999999,
+ "cluster": {
+ "cluster1": {
+ "count": 2,
+ "resource": "cpu",
+ "utilization": 2.999999999999999,
+ "tco": 74,
+ "waste": 0,
+ "flavor": "flavor1",
+ "flavorCost": 37.0,
+ "flavorCpu": 2.0,
+ "flavorMem": 4.0,
+ "flavorDisk": 50.0,
+ "type": "content",
+ "util": {
+ "cpu": 2.999999999999999,
+ "mem": 0.4285714285714286,
+ "disk": 0.5714285714285715,
+ "diskBusy": 1.0
+ },
+ "usage": {
+ "cpu": 0.6,
+ "mem": 0.3,
+ "disk": 0.4,
+ "diskBusy": 0.3
+ },
+ "hostnames": [
+ "host1",
+ "host2"
+ ]
+ }
+ }
+ },
+ "metrics": {
+ "queriesPerSecond": 1.0,
+ "writesPerSecond": 2.0,
+ "documentCount": 3.0,
+ "queryLatencyMillis": 4.0,
+ "writeLatencyMillis": 5.0
+ }
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json
new file mode 100644
index 00000000000..5126ad8fa69
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json
@@ -0,0 +1,34 @@
+[
+ {
+ "tenant": "tenant2",
+ "type": "ATHENS",
+ "athensDomain": "domain2",
+ "property": "property2",
+ "propertyId": "1234",
+ "applications": [],
+ "propertyUrl": "www.properties.tld/1234",
+ "contactsUrl": "www.contacts.tld/1234",
+ "issueCreationUrl": "www.issues.tld/1234",
+ "contacts": [
+ [
+ "alice"
+ ],
+ [
+ "bob"
+ ]
+ ]
+ },
+ {
+ "tenant": "tenant3",
+ "type": "OPSDB",
+ "property": "property2",
+ "propertyId": "4321",
+ "userGroup": "group1",
+ "applications": [],
+ "propertyUrl": "www.properties.tld/4321",
+ "contactsUrl": "www.contacts.tld/4321",
+ "issueCreationUrl": "www.issues.tld/4321",
+ "contacts": []
+ },
+ @include(tenant1.json)
+]
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application.json
index 87901218c2e..ad8e65692b4 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-with-application.json
@@ -1,4 +1,5 @@
{
+ "tenant": "tenant1",
"type": "ATHENS",
"athensDomain": "domain1",
"property": "property1",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications-with-id.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications-with-id.json
index ede2413218d..69949c47d8c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications-with-id.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications-with-id.json
@@ -1,4 +1,5 @@
{
+ "tenant": "tenant2",
"type": "ATHENS",
"athensDomain": "domain2",
"property": "property2",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications.json
index 69669b5dfb8..3ad5a307348 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant-without-applications.json
@@ -1,4 +1,5 @@
{
+ "tenant": "tenant1",
"type": "ATHENS",
"athensDomain": "domain1",
"property": "property1",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1.json
new file mode 100644
index 00000000000..9f9d7642867
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1.json
@@ -0,0 +1,9 @@
+{
+ "tenant": "tenant1",
+ "type": "ATHENS",
+ "athensDomain": "domain1",
+ "property": "property1",
+ "applications": [
+ @include(application1.json)
+ ]
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
index 31353e8a113..354bab4379c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json
@@ -1,22 +1,16 @@
{
"jobs": [
{
- "name": "DelayedDeployer"
- },
- {
- "name": "BlockedChangeDeployer"
- },
- {
- "name": "Upgrader"
+ "name": "ApplicationOwnershipConfirmer"
},
{
- "name": "FailureRedeployer"
+ "name": "ClusterInfoMaintainer"
},
{
- "name": "ApplicationOwnershipConfirmer"
+ "name": "ClusterUtilizationMaintainer"
},
{
- "name": "VersionStatusUpdater"
+ "name": "DeploymentExpirer"
},
{
"name": "DeploymentIssueReporter"
@@ -25,22 +19,22 @@
"name": "DeploymentMetricsMaintainer"
},
{
- "name": "OutstandingChangeDeployer"
+ "name": "MetricsReporter"
},
{
- "name": "ClusterUtilizationMaintainer"
+ "name": "OutstandingChangeDeployer"
},
{
- "name": "ClusterInfoMaintainer"
+ "name": "ReadyJobsTrigger"
},
{
- "name": "DeploymentExpirer"
+ "name": "Upgrader"
},
{
- "name": "MetricsReporter"
+ "name": "VersionStatusUpdater"
}
],
"inactive": [
"DeploymentExpirer"
]
-} \ No newline at end of file
+}