diff options
Diffstat (limited to 'node-repository')
6 files changed, 120 insertions, 6 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java index 18af498a38d..7e8cd65d37f 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java @@ -48,10 +48,11 @@ public class NodeRepositoryMaintenance extends AbstractComponent { private final MetricsReporter metricsReporter; private final JobControl jobControl; + private final InfrastructureVersions infrastructureVersions; @Inject public NodeRepositoryMaintenance(NodeRepository nodeRepository, Deployer deployer, - HostLivenessTracker hostLivenessTracker, ServiceMonitor serviceMonitor, + HostLivenessTracker hostLivenessTracker, ServiceMonitor serviceMonitor, Zone zone, Orchestrator orchestrator, Metric metric, ConfigserverConfig configserverConfig) { this(nodeRepository, deployer, hostLivenessTracker, serviceMonitor, zone, Clock.systemUTC(), @@ -64,6 +65,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent { ConfigserverConfig configserverConfig) { DefaultTimes defaults = new DefaultTimes(zone.environment()); jobControl = new JobControl(nodeRepository.database()); + infrastructureVersions = new InfrastructureVersions(nodeRepository.database()); nodeFailer = new NodeFailer(deployer, hostLivenessTracker, serviceMonitor, nodeRepository, durationFromEnv("fail_grace").orElse(defaults.failGrace), clock, orchestrator, throttlePolicyFromEnv("throttle_policy").orElse(defaults.throttlePolicy), metric, jobControl, configserverConfig); periodicApplicationMaintainer = new PeriodicApplicationMaintainer(deployer, nodeRepository, durationFromEnv("periodic_redeploy_interval").orElse(defaults.periodicRedeployInterval), jobControl); @@ -101,6 +103,10 @@ public class NodeRepositoryMaintenance extends AbstractComponent { public JobControl jobControl() { return jobControl; } + public InfrastructureVersions infrastructureVersions() { + return infrastructureVersions; + } + private static Optional<Duration> durationFromEnv(String envVariable) { return Optional.ofNullable(System.getenv(envPrefix + envVariable)).map(Long::parseLong).map(Duration::ofSeconds); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java index 03777078251..54202a15971 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java @@ -1,6 +1,7 @@ // 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.provision.restapi.v2; +import com.yahoo.component.Version; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.NodeType; import com.yahoo.container.jdisc.HttpRequest; @@ -91,7 +92,7 @@ public class NodesApiHandler extends LoggingRequestHandler { private HttpResponse handleGET(HttpRequest request) { String path = request.getUri().getPath(); - if (path.equals( "/nodes/v2/")) return ResourcesResponse.fromStrings(request.getUri(), "state", "node", "command", "maintenance"); + if (path.equals( "/nodes/v2/")) return ResourcesResponse.fromStrings(request.getUri(), "state", "node", "command", "maintenance", "upgrade"); if (path.equals( "/nodes/v2/node/")) return new NodesResponse(ResponseType.nodeList, request, orchestrator, nodeRepository); if (path.startsWith("/nodes/v2/node/")) return new NodesResponse(ResponseType.singleNode, request, orchestrator, nodeRepository); if (path.equals( "/nodes/v2/state/")) return new NodesResponse(ResponseType.stateList, request, orchestrator, nodeRepository); @@ -99,6 +100,7 @@ public class NodesApiHandler extends LoggingRequestHandler { if (path.startsWith("/nodes/v2/acl/")) return new NodeAclResponse(request, nodeRepository); if (path.equals( "/nodes/v2/command/")) return ResourcesResponse.fromStrings(request.getUri(), "restart", "reboot"); if (path.equals( "/nodes/v2/maintenance/")) return new JobsResponse(maintenance.jobControl()); + if (path.equals( "/nodes/v2/upgrade/")) return new UpgradeResponse(maintenance.infrastructureVersions()); throw new NotFoundException("Nothing at path '" + path + "'"); } @@ -135,10 +137,16 @@ public class NodesApiHandler extends LoggingRequestHandler { private HttpResponse handlePATCH(HttpRequest request) { String path = request.getUri().getPath(); - if ( ! path.startsWith("/nodes/v2/node/")) throw new NotFoundException("Nothing at '" + path + "'"); - Node node = nodeFromRequest(request); - nodeRepository.write(new NodePatcher(nodeFlavors, request.getData(), node, nodeRepository).apply()); - return new MessageResponse("Updated " + node.hostname()); + if (path.startsWith("/nodes/v2/node/")) { + Node node = nodeFromRequest(request); + nodeRepository.write(new NodePatcher(nodeFlavors, request.getData(), node, nodeRepository).apply()); + return new MessageResponse("Updated " + node.hostname()); + } + else if (path.startsWith("/nodes/v2/upgrade/")) { + return setInfrastructureVersion(request); + } + + throw new NotFoundException("Nothing at '" + path + "'"); } private HttpResponse handlePOST(HttpRequest request) { @@ -276,4 +284,18 @@ public class NodesApiHandler extends LoggingRequestHandler { return new MessageResponse((active ? "Re-activated" : "Deactivated" ) + " job '" + jobName + "'"); } + private MessageResponse setInfrastructureVersion(HttpRequest request) { + NodeType nodeType = NodeType.valueOf(lastElement(request.getUri().getPath()).toLowerCase()); + Inspector inspector = toSlime(request.getData()).get(); + + Inspector versionField = inspector.field("version"); + if (!versionField.valid()) + throw new IllegalArgumentException("'version' is missing"); + Version version = Version.fromString(versionField.asString()); + boolean force = inspector.field("force").asBool(); + + maintenance.infrastructureVersions().setTargetVersion(nodeType, version, force); + + return new MessageResponse("Set version for " + nodeType + " to " + version.toFullString()); + } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java new file mode 100644 index 00000000000..12d4e935231 --- /dev/null +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/UpgradeResponse.java @@ -0,0 +1,42 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.provision.restapi.v2; + +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.hosted.provision.maintenance.InfrastructureVersions; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Comparator; +import java.util.Map; + +/** A response containing infrastructure versions */ +public class UpgradeResponse extends HttpResponse { + + private final InfrastructureVersions infrastructureVersions; + + public UpgradeResponse(InfrastructureVersions infrastructureVersions) { + super(200); + this.infrastructureVersions = infrastructureVersions; + } + + @Override + public void render(OutputStream stream) throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + + Cursor versionsObject = root.setObject("versions"); + infrastructureVersions.getTargetVersions().entrySet().stream() + .sorted(Comparator.comparing(Map.Entry::getKey)) // Sort for stable tests + .forEach(entry -> + versionsObject.setString(entry.getKey().name(), entry.getValue().toFullString())); + + new JsonFormat(true).encode(stream, slime); + } + + @Override + public String getContentType() { return "application/json"; } + +} diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java index 2e69867e9b1..5e58d0d89e7 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java @@ -495,6 +495,44 @@ public class RestApiTest { assertFile(new Request("http://localhost:8080/nodes/v2/node/host6.yahoo.com"), "node6.json"); } + @Test + public void test_upgrade() throws IOException { + // Initially, no versions are set + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/"), "{\"versions\":{}}"); + + // Set version for config and confighost + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/config", + Utf8.toBytes("{\"version\": \"6.123.456\"}"), + Request.Method.PATCH), + "{\"message\":\"Set version for config to 6.123.456\"}"); + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/confighost", + Utf8.toBytes("{\"version\": \"6.123.456\"}"), + Request.Method.PATCH), + "{\"message\":\"Set version for confighost to 6.123.456\"}"); + + // Verify versions are set + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/"), + "{\"versions\":{\"config\":\"6.123.456\",\"confighost\":\"6.123.456\"}}"); + + // Downgrade without force fails + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/confighost", + Utf8.toBytes("{\"version\": \"6.123.1\"}"), + Request.Method.PATCH), + 400, + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cannot downgrade version without setting 'force'. " + + "Current wanted version: 6.123.456, attempted to set wanted version: 6.123.1\"}"); + + // Downgrade with force is OK + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/confighost", + Utf8.toBytes("{\"version\": \"6.123.1\",\"force\":true}"), + Request.Method.PATCH), + "{\"message\":\"Set version for confighost to 6.123.1\"}"); + + // Verify version has been updated + assertResponse(new Request("http://localhost:8080/nodes/v2/upgrade/"), + "{\"versions\":{\"config\":\"6.123.456\",\"confighost\":\"6.123.1\"}}"); + } + /** Tests the rendering of each node separately to make it easier to find errors */ @Test public void test_single_node_rendering() throws Exception { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json index 28e28f9678e..99cb9fd91f5 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json @@ -34,6 +34,9 @@ "name":"MetricsReporter" }, { + "name":"InfrastructureProvisioner" + }, + { "name":"NodeFailer" } ], diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/root.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/root.json index 4224718ab06..86becefb146 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/root.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/root.json @@ -11,6 +11,9 @@ }, { "url": "http://localhost:8080/nodes/v2/maintenance/" + }, + { + "url":"http://localhost:8080/nodes/v2/upgrade/" } ] }
\ No newline at end of file |