summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorTorbjørn Smørgrav <smorgrav@users.noreply.github.com>2019-02-06 15:01:28 +0100
committerGitHub <noreply@github.com>2019-02-06 15:01:28 +0100
commite7fb6ba9cf93886e9cf54d51abd4f27c9a20bb8c (patch)
tree9c71aa2acad38e85e9f829bf88beeee24b13b4bf /controller-server
parent07437b3ea3525a877d4bd42cf0cde9891df352a9 (diff)
parentd0c0baba17829572ee5af428fcf6b068f013d55d (diff)
Merge pull request #8338 from vespa-engine/smorgrav/redeploy_zone_app
Add support for redeployment for the zone application
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java15
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java61
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java13
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deploy-no-deployment.json1
4 files changed, 85 insertions, 5 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index f9254e9c467..3efb0db8cce 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -378,14 +378,23 @@ public class ApplicationController {
/** Deploy a system application to given zone */
public void deploy(SystemApplication application, ZoneId zone, Version version) {
if (application.hasApplicationPackage()) {
+ deploySystemApplicationPackage(application, zone, version);
+ } else {
+ // Deploy by calling node repository directly
+ application.nodeTypes().forEach(nodeType -> configServer().nodeRepository().upgrade(zone, nodeType, version));
+ }
+ }
+
+ /** Deploy a system application to given zone */
+ public ActivateResult deploySystemApplicationPackage(SystemApplication application, ZoneId zone, Version version) {
+ if (application.hasApplicationPackage()) {
ApplicationPackage applicationPackage = new ApplicationPackage(
artifactRepository.getSystemApplicationPackage(application.id(), zone, version)
);
DeployOptions options = withVersion(version, DeployOptions.none());
- deploy(application.id(), applicationPackage, zone, options, Collections.emptySet(), Collections.emptySet());
+ return deploy(application.id(), applicationPackage, zone, options, Collections.emptySet(), Collections.emptySet());
} else {
- // Deploy by calling node repository directly
- application.nodeTypes().forEach(nodeType -> configServer().nodeRepository().upgrade(zone, nodeType, version));
+ throw new RuntimeException("This system application does not have an application package: " + application.id().toShortString());
}
}
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 c1b7f965135..19ff6df3ccb 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
@@ -49,6 +49,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFact
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Logs;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
@@ -65,9 +66,11 @@ 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.RotationStatus;
+import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.athenz.impl.ZmsClientFacade;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel;
+import com.yahoo.vespa.hosted.controller.maintenance.InfrastructureUpgrader;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse;
@@ -92,11 +95,13 @@ import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Scanner;
+import java.util.function.Function;
import java.util.logging.Level;
import static java.util.stream.Collectors.joining;
@@ -856,12 +861,40 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
ApplicationId applicationId = ApplicationId.from(tenantName, applicationName, instanceName);
ZoneId zone = ZoneId.from(environment, region);
+ // Get deployOptions
Map<String, byte[]> dataParts = new MultipartParser().parse(request);
if ( ! dataParts.containsKey("deployOptions"))
return ErrorResponse.badRequest("Missing required form part 'deployOptions'");
-
Inspector deployOptions = SlimeUtils.jsonToSlime(dataParts.get("deployOptions")).get();
+ /*
+ * Special handling of the zone application (the only system application with an application package)
+ * Setting any other deployOptions here is not supported for now (e.g. specifying version), but
+ * this might be handy later to handle emergency downgrades.
+ */
+ boolean isZoneApplication = SystemApplication.zone.id().equals(applicationId);
+ if (isZoneApplication) {
+ // Make it explicit that version is not yet supported here
+ String versionStr = deployOptions.field("vespaVersion").asString();
+ boolean versionPresent = !versionStr.isEmpty() && !versionStr.equals("null");
+ if (versionPresent) {
+ throw new RuntimeException("Version not supported for system applications");
+ }
+ // To avoid second guessing the orchestrated upgrades of system applications
+ // we don't allow to deploy these during an system upgrade (i.e when new vespa is being rolled out)
+ Version version = wantedSystemVersion(zone, SystemApplication.zone);
+ if (!controller.systemVersion().equals(version)) {
+ throw new RuntimeException("Deployment of system applications during a system upgrade is not allowed");
+ }
+ ActivateResult result = controller.applications()
+ .deploySystemApplicationPackage(SystemApplication.zone, zone, version);
+ return new SlimeJsonResponse(toSlime(result));
+ }
+
+ /*
+ * Normal applications from here
+ */
+
Optional<ApplicationPackage> applicationPackage = Optional.ofNullable(dataParts.get("applicationZip"))
.map(ApplicationPackage::new);
@@ -880,11 +913,15 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
applicationPackage = Optional.of(controller.applications().getApplicationPackage(controller.applications().require(applicationId), applicationVersion.get()));
}
-
boolean deployDirectly = deployOptions.field("deployDirectly").asBool();
Optional<Version> vespaVersion = optional("vespaVersion", deployOptions).map(Version::new);
+ /*
+ * Deploy direct is when we want to redeploy the current application - retrieve version
+ * info from the application package before deploying
+ */
if(deployDirectly && !applicationPackage.isPresent() && !applicationVersion.isPresent() && !vespaVersion.isPresent()) {
+
// Redeploy the existing deployment with the same versions.
Optional<Deployment> deployment = controller.applications().get(applicationId)
.map(Application::deployments)
@@ -907,15 +944,35 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
vespaVersion,
deployOptions.field("ignoreValidationErrors").asBool(),
deployOptions.field("deployCurrentVersion").asBool());
+
+
ActivateResult result = controller.applications().deploy(applicationId,
zone,
applicationPackage,
applicationVersion,
deployOptionsJsonClass,
Optional.of(getUserPrincipal(request).getIdentity()));
+
return new SlimeJsonResponse(toSlime(result));
}
+ /** Find the minimum value of a version field in a zone */
+ private Version wantedSystemVersion(ZoneId zone, SystemApplication application) {
+ try {
+ return controller.configServer()
+ .nodeRepository()
+ .list(zone, application.id())
+ .stream()
+ .filter(node -> node.state().equals(Node.State.active))
+ .map(Node::wantedVersion)
+ .min(Comparator.naturalOrder()).orElseThrow(
+ () -> new RuntimeException("System version not found in node repo"));
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Failed to get version for %s in %s: %s",
+ application.id(), zone, Exceptions.toMessageString(e)));
+ }
+ }
+
private HttpResponse deleteTenant(String tenantName, HttpRequest request) {
Optional<Tenant> tenant = controller.tenants().tenant(tenantName);
if ( ! tenant.isPresent()) return ErrorResponse.notFoundError("Could not delete tenant '" + tenantName + "': Tenant not found"); // NOTE: The Jersey implementation would silently ignore this
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 cd804e2ebfb..7697cf00b86 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
@@ -668,6 +668,19 @@ public class ApplicationApiTest extends ControllerContainerTest {
.data(entity)
.screwdriverIdentity(SCREWDRIVER_ID),
new File("deploy-result.json"));
+
+ // POST (deploy) a system application with an application package
+ HttpEntity noAppEntity = createApplicationDeployData(Optional.empty(), true);
+ tester.assertResponse(request("/application/v4/tenant/hosted-vespa/application/routing/environment/prod/region/us-central-1/instance/default/deploy", POST)
+ .data(noAppEntity)
+ .userIdentity(USER_ID),
+ new File("deploy-result.json"));
+
+ // POST (deploy) a system application without an application package
+ tester.assertResponse(request("/application/v4/tenant/hosted-vespa/application/proxy-host/environment/prod/region/us-central-1/instance/default/deploy", POST)
+ .data(noAppEntity)
+ .userIdentity(USER_ID),
+ new File("deploy-no-deployment.json"), 400);
}
// Tests deployment to config server when using just on API call
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deploy-no-deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deploy-no-deployment.json
new file mode 100644
index 00000000000..f90420f28d7
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deploy-no-deployment.json
@@ -0,0 +1 @@
+{"error-code":"BAD_REQUEST","message":"Can't redeploy application, no deployment currently exist"} \ No newline at end of file