summaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java300
1 files changed, 0 insertions, 300 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
deleted file mode 100644
index edfa4d01d78..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/DeploymentApiHandler.java
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.restapi.deployment;
-
-import com.yahoo.component.Version;
-import com.yahoo.config.application.api.DeploymentInstanceSpec;
-import com.yahoo.config.application.api.DeploymentSpec;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.CloudAccount;
-import com.yahoo.container.jdisc.EmptyResponse;
-import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
-import com.yahoo.restapi.ErrorResponse;
-import com.yahoo.restapi.Path;
-import com.yahoo.restapi.SlimeJsonResponse;
-import com.yahoo.restapi.UriBuilder;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
-import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId;
-import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
-import com.yahoo.vespa.hosted.controller.application.ApplicationList;
-import com.yahoo.vespa.hosted.controller.application.Change;
-import com.yahoo.vespa.hosted.controller.application.Deployment;
-import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
-import com.yahoo.vespa.hosted.controller.deployment.DeploymentStatus;
-import com.yahoo.vespa.hosted.controller.deployment.DeploymentStatus.DelayCause;
-import com.yahoo.vespa.hosted.controller.deployment.DeploymentStatus.Readiness;
-import com.yahoo.vespa.hosted.controller.deployment.Run;
-import com.yahoo.vespa.hosted.controller.deployment.RunStatus;
-import com.yahoo.vespa.hosted.controller.deployment.Versions;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponses;
-import com.yahoo.vespa.hosted.controller.versions.DeploymentStatistics;
-import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
-import com.yahoo.yolean.Exceptions;
-
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.groupingBy;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
-import static java.util.stream.Collectors.toUnmodifiableMap;
-
-/**
- * This implements the deployment/v1 API which provides information about the status of Vespa platform and
- * application deployments.
- *
- * @author bratseth
- */
-@SuppressWarnings("unused") // Injected
-public class DeploymentApiHandler extends ThreadedHttpRequestHandler {
-
- private final Controller controller;
-
- public DeploymentApiHandler(ThreadedHttpRequestHandler.Context parentCtx, Controller controller) {
- super(parentCtx);
- this.controller = controller;
- }
-
- @Override
- public HttpResponse handle(HttpRequest request) {
- try {
- return switch (request.getMethod()) {
- case GET -> handleGET(request);
- case OPTIONS -> handleOPTIONS();
- default -> ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
- };
- }
- catch (IllegalArgumentException e) {
- return ErrorResponse.badRequest(Exceptions.toMessageString(e));
- }
- catch (RuntimeException e) {
- return ErrorResponses.logThrowing(request, log, e);
- }
- }
-
- private HttpResponse handleGET(HttpRequest request) {
- Path path = new Path(request.getUri());
- if (path.matches("/deployment/v1/")) return root(request);
- return ErrorResponse.notFoundError("Nothing at " + path);
- }
-
- private HttpResponse handleOPTIONS() {
- // We implement this to avoid redirect loops on OPTIONS requests from browsers, but do not really bother
- // spelling out the methods supported at each path, which we should
- EmptyResponse response = new EmptyResponse();
- response.headers().put("Allow", "GET,OPTIONS");
- return response;
- }
-
- private HttpResponse root(HttpRequest request) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- Cursor platformArray = root.setArray("versions");
- var versionStatus = controller.readVersionStatus();
- ApplicationList applications = ApplicationList.from(controller.applications().asList()).withJobs();
- var deploymentStatuses = controller.jobController().deploymentStatuses(applications, versionStatus);
- Map<Version, DeploymentStatistics> deploymentStatistics = DeploymentStatistics.compute(versionStatus.versions().stream().map(VespaVersion::versionNumber).toList(),
- deploymentStatuses)
- .stream().collect(toMap(DeploymentStatistics::version, identity()));
- for (VespaVersion version : versionStatus.versions()) {
- Cursor versionObject = platformArray.addObject();
- versionObject.setString("version", version.versionNumber().toString());
- versionObject.setString("confidence", version.confidence().name());
- versionObject.setString("commit", version.releaseCommit());
- versionObject.setLong("date", version.committedAt().toEpochMilli());
- versionObject.setBool("controllerVersion", version.isControllerVersion());
- versionObject.setBool("systemVersion", version.isSystemVersion());
-
- Cursor configServerArray = versionObject.setArray("configServers");
- for (var nodeVersion : version.nodeVersions()) {
- Cursor configServerObject = configServerArray.addObject();
- configServerObject.setString("hostname", nodeVersion.hostname().value());
- }
-
- DeploymentStatistics statistics = deploymentStatistics.get(version.versionNumber());
- Cursor failingArray = versionObject.setArray("failingApplications");
- for (Run run : statistics.failingUpgrades()) {
- Cursor applicationObject = failingArray.addObject();
- toSlime(applicationObject, run.id().application(), request);
- applicationObject.setString("failing", run.id().type().jobName());
- applicationObject.setString("status", nameOf(run.status()));
- }
-
- var statusByInstance = deploymentStatuses.asList().stream()
- .flatMap(status -> status.instanceJobs().keySet().stream()
- .map(instance -> Map.entry(instance, status)))
- .collect(toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
- var jobsByInstance = statusByInstance.entrySet().stream()
- .collect(toUnmodifiableMap(Map.Entry::getKey,
- entry -> entry.getValue().instanceJobs().get(entry.getKey())));
- Cursor productionArray = versionObject.setArray("productionApplications");
- statistics.productionSuccesses().stream()
- .collect(groupingBy(run -> run.id().application(), TreeMap::new, toList()))
- .forEach((id, runs) -> {
- Cursor applicationObject = productionArray.addObject();
- toSlime(applicationObject, id, request);
- applicationObject.setLong("productionJobs", jobsByInstance.get(id).production().size());
- applicationObject.setLong("productionSuccesses", runs.size());
- });
-
- Cursor runningArray = versionObject.setArray("deployingApplications");
- for (Run run : statistics.runningUpgrade()) {
- Cursor applicationObject = runningArray.addObject();
- toSlime(applicationObject, run.id().application(), request);
- applicationObject.setString("running", run.id().type().jobName());
- }
-
- Cursor instancesArray = versionObject.setArray("applications");
- Stream.of(statistics.failingUpgrades().stream().map(run -> new RunInfo(run, true)),
- statistics.otherFailing().stream().map(run -> new RunInfo(run, false)),
- statistics.runningUpgrade().stream().map(run -> new RunInfo(run, true)),
- statistics.otherRunning().stream().map(run -> new RunInfo(run, false)),
- statistics.productionSuccesses().stream().map(run -> new RunInfo(run, true)))
- .flatMap(identity())
- .collect(Collectors.groupingBy(run -> run.run.id().application(),
- LinkedHashMap::new, // Put apps with failing and running jobs first.
- groupingBy(run -> run.run.id().type(),
- LinkedHashMap::new,
- toList())))
- .forEach((instance, runs) -> {
- var status = statusByInstance.get(instance);
- var jobsToRun = status.jobsToRun();
- Cursor instanceObject = instancesArray.addObject();
- instanceObject.setString("tenant", instance.tenant().value());
- instanceObject.setString("application", instance.application().value());
- instanceObject.setString("instance", instance.instance().value());
- instanceObject.setBool("upgrading", status.application().require(instance.instance()).change().platform().equals(Optional.of(statistics.version())));
- instanceObject.setBool("pinned", status.application().require(instance.instance()).change().isPlatformPinned());
- instanceObject.setBool("platformPinned", status.application().require(instance.instance()).change().isPlatformPinned());
- instanceObject.setBool("revisionPinned", status.application().require(instance.instance()).change().isRevisionPinned());
- DeploymentStatus.StepStatus stepStatus = status.instanceSteps().get(instance.instance());
- if (stepStatus != null) { // Instance may not have any steps, i.e. an empty deployment spec has been submitted
- Readiness platformReadiness = stepStatus.blockedUntil(Change.of(statistics.version()));
- if (platformReadiness.cause() == DelayCause.changeBlocked)
- instanceObject.setLong("blockedUntil", platformReadiness.at().toEpochMilli());
- }
- instanceObject.setString("upgradePolicy", toString(status.application().deploymentSpec().instance(instance.instance())
- .map(DeploymentInstanceSpec::upgradePolicy)
- .orElse(DeploymentSpec.UpgradePolicy.defaultPolicy)));
- status.application().revisions().last().flatMap(ApplicationVersion::compileVersion)
- .ifPresent(compiled -> instanceObject.setString("compileVersion", compiled.toFullString()));
- Cursor jobsArray = instanceObject.setArray("jobs");
- status.jobSteps().forEach((job, jobStatus) -> {
- if ( ! job.application().equals(instance)) return;
- Cursor jobObject = jobsArray.addObject();
- jobObject.setString("name", job.type().jobName());
- if (jobsToRun.containsKey(job)) {
- Readiness readiness = jobsToRun.get(job).get(0).readiness();
- switch (readiness.cause()) {
- case paused -> jobObject.setLong("pausedUntil", readiness.at().toEpochMilli());
- case coolingDown -> jobObject.setLong("coolingDownUntil", readiness.at().toEpochMilli());
- }
- List<Versions> versionsOnThisPlatform = jobsToRun.get(job).stream()
- .map(DeploymentStatus.Job::versions)
- .filter(versions -> versions.targetPlatform().equals(statistics.version()))
- .toList();
- if ( ! versionsOnThisPlatform.isEmpty())
- jobObject.setString("pending", versionsOnThisPlatform.stream()
- .allMatch(versions -> versions.sourcePlatform()
- .map(statistics.version()::equals)
- .orElse(true))
- ? "application" : "platform");
- }
- });
- Cursor allRunsObject = instanceObject.setObject("allRuns");
- Cursor upgradeRunsObject = instanceObject.setObject("upgradeRuns");
- runs.forEach((type, rs) -> {
- Cursor runObject = allRunsObject.setObject(type.jobName());
- Cursor upgradeObject = upgradeRunsObject.setObject(type.jobName());
- CloudAccount cloudAccount = controller.applications().decideCloudAccountOf(new DeploymentId(instance, type.zone()),
- status.application().deploymentSpec())
- .orElse(null);
- for (RunInfo run : rs) {
- toSlime(runObject, run.run, cloudAccount);
- if (run.upgrade)
- toSlime(upgradeObject, run.run, cloudAccount);
- }
- });
- });
- }
- JobType.allIn(controller.zoneRegistry()).stream()
- .filter(job -> ! job.environment().isManuallyDeployed())
- .map(JobType::jobName).forEach(root.setArray("jobs")::addString);
- return new SlimeJsonResponse(slime);
- }
-
- private void toSlime(Cursor jobObject, Run run, CloudAccount cloudAccount) {
- String key = run.hasFailed() ? "failing" : run.hasEnded() ? "success" : "running";
- Cursor runObject = jobObject.setObject(key);
- runObject.setLong("number", run.id().number());
- runObject.setLong("start", run.start().toEpochMilli());
- run.end().ifPresent(end -> runObject.setLong("end", end.toEpochMilli()));
- runObject.setString("status", nameOf(run.status()));
- if (cloudAccount != null) runObject.setObject("enclave").setString("cloudAccount", cloudAccount.value());
- }
-
- private void toSlime(Cursor object, ApplicationId id, HttpRequest request) {
- object.setString("tenant", id.tenant().value());
- object.setString("application", id.application().value());
- object.setString("instance", id.instance().value());
- object.setString("url", new UriBuilder(request.getUri()).withPath("/application/v4/tenant/" +
- id.tenant().value() +
- "/application/" +
- id.application().value()).toString());
- object.setString("upgradePolicy", toString(controller.applications().requireApplication(TenantAndApplicationId.from(id))
- .deploymentSpec().instance(id.instance()).map(DeploymentInstanceSpec::upgradePolicy)
- .orElse(DeploymentSpec.UpgradePolicy.defaultPolicy)));
- }
-
- private static String toString(DeploymentSpec.UpgradePolicy upgradePolicy) {
- if (upgradePolicy == DeploymentSpec.UpgradePolicy.defaultPolicy) {
- return "default";
- }
- return upgradePolicy.name();
- }
-
- public static String nameOf(RunStatus status) {
- return switch (status) {
- case reset, running -> "running";
- case cancelled, aborted -> "aborted";
- case error -> "error";
- case testFailure -> "testFailure";
- case noTests -> "noTests";
- case endpointCertificateTimeout -> "endpointCertificateTimeout";
- case nodeAllocationFailure -> "nodeAllocationFailure";
- case installationFailed -> "installationFailed";
- case invalidApplication, deploymentFailed -> "deploymentFailed";
- case success -> "success";
- case quotaExceeded -> "quotaExceeded";
- };
- }
-
- private static class RunInfo {
- final Run run;
- final boolean upgrade;
-
- RunInfo(Run run, boolean upgrade) {
- this.run = run;
- this.upgrade = upgrade;
- }
-
- @Override
- public String toString() {
- return run.id().toString();
- }
-
- }
-
-
-}