aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java73
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java13
5 files changed, 82 insertions, 28 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
index 839e10ba096..fa265918c33 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
@@ -69,10 +69,14 @@ public class CapacityPolicies {
if (required || exclusive) return target; // Cannot downsize if resources are required, or exclusively allocated
if (target.isUnspecified()) return target; // Cannot be modified
- // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively
- if (zone.environment() == Environment.dev && zone.cloud().allowHostSharing())
+ if (zone.environment() == Environment.dev && zone.cloud().allowHostSharing()) {
+ // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively
target = target.withVcpu(0.1).withBandwidthGbps(0.1);
+ // Allocate without GPU in dev
+ target = target.with(NodeResources.GpuResources.zero());
+ }
+
// Allow slow storage in zones which are not performance sensitive
if (zone.system().isCd() || zone.environment() == Environment.dev || zone.environment() == Environment.test)
target = target.with(NodeResources.DiskSpeed.any).with(NodeResources.StorageType.any).withBandwidthGbps(0.1);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java
index 1ba686772c7..a23b1273fe3 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java
@@ -1,37 +1,86 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.restapi;
+import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
-import com.yahoo.restapi.RestApi;
-import com.yahoo.restapi.RestApiRequestHandler;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.MessageResponse;
+import com.yahoo.restapi.Path;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.lb.LoadBalancer;
+import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId;
+import com.yahoo.yolean.Exceptions;
import javax.inject.Inject;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.logging.Level;
/**
* @author mpolden
* @author jonmv
*/
-public class LoadBalancersV1ApiHandler extends RestApiRequestHandler<LoadBalancersV1ApiHandler> {
+public class LoadBalancersV1ApiHandler extends ThreadedHttpRequestHandler {
private final NodeRepository nodeRepository;
@Inject
public LoadBalancersV1ApiHandler(ThreadedHttpRequestHandler.Context parentCtx, NodeRepository nodeRepository) {
- super(parentCtx, LoadBalancersV1ApiHandler::createRestApiDefinition);
- this.nodeRepository = nodeRepository;
+ super(parentCtx);
+ this.nodeRepository = Objects.requireNonNull(nodeRepository);
}
- private static RestApi createRestApiDefinition(LoadBalancersV1ApiHandler self) {
- return RestApi.builder()
- .addRoute(RestApi.route("/loadbalancers/v1")
- .get(self::getLoadBalancers))
- .build();
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ try {
+ return switch (request.getMethod()) {
+ case GET -> get(request);
+ case PUT -> put(request);
+ default -> ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
+ };
+ } catch (NotFoundException e) {
+ return ErrorResponse.notFoundError(Exceptions.toMessageString(e));
+ } catch (IllegalArgumentException e) {
+ return ErrorResponse.badRequest(Exceptions.toMessageString(e));
+ } catch (RuntimeException e) {
+ log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
+ return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
+ }
}
- private HttpResponse getLoadBalancers(RestApi.RequestContext context) {
- return new LoadBalancersResponse(context.request(), nodeRepository);
+ private HttpResponse get(HttpRequest request) {
+ Path path = new Path(request.getUri());
+ if (path.matches("/loadbalancers/v1")) return new LoadBalancersResponse(request, nodeRepository);
+ throw new NotFoundException("Nothing at " + path);
+ }
+
+ private HttpResponse put(HttpRequest request) {
+ Path path = new Path(request.getUri());
+ if (path.matches("/loadbalancers/v1/state/{state}/{id}")) return setState(path.get("state"), path.get("id"));
+ throw new NotFoundException("Nothing at " + path);
+ }
+
+ private HttpResponse setState(String state, String id) {
+ LoadBalancer.State toState = stateFrom(state);
+ LoadBalancerId loadBalancerId = LoadBalancerId.fromSerializedForm(id);
+ try (var lock = nodeRepository.database().lock(loadBalancerId.application(), Duration.ofSeconds(1))) {
+ LoadBalancer loadBalancer = nodeRepository.database().readLoadBalancer(loadBalancerId)
+ .orElseThrow(() -> new NotFoundException(loadBalancerId + " does not exist"));
+ nodeRepository.database().writeLoadBalancer(loadBalancer.with(toState, nodeRepository.clock().instant()),
+ loadBalancer.state());
+ }
+ return new MessageResponse("Moved " + loadBalancerId + " to " + toState);
+ }
+
+ private LoadBalancer.State stateFrom(String state) {
+ return switch (state) {
+ case "reserved" -> LoadBalancer.State.reserved;
+ case "inactive" -> LoadBalancer.State.inactive;
+ case "active" -> LoadBalancer.State.active;
+ case "removable" -> LoadBalancer.State.removable;
+ default -> throw new IllegalArgumentException("Invalid state '" + state + "'");
+ };
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
index 1f0244c8e40..64f3ff9f5ba 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
@@ -14,9 +14,6 @@ import com.yahoo.slime.Inspector;
import com.yahoo.slime.ObjectTraverser;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.slime.Type;
-import com.yahoo.vespa.flags.BooleanFlag;
-import com.yahoo.vespa.flags.FlagSource;
-import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
@@ -68,13 +65,11 @@ public class NodePatcher {
private final NodeRepository nodeRepository;
private final NodeFlavors nodeFlavors;
private final Clock clock;
- private final BooleanFlag recursiveWantToDeprovision;
- public NodePatcher(NodeFlavors nodeFlavors, NodeRepository nodeRepository, FlagSource flagSource) {
+ public NodePatcher(NodeFlavors nodeFlavors, NodeRepository nodeRepository) {
this.nodeRepository = nodeRepository;
this.nodeFlavors = nodeFlavors;
this.clock = nodeRepository.clock();
- this.recursiveWantToDeprovision = Flags.RECURSIVE_WANT_TO_DEPROVISION.bindTo(flagSource);
}
/**
@@ -200,7 +195,6 @@ public class NodePatcher {
// These needs to be handled as one, because certain combinations are not allowed.
return node.withWantToRetire(asOptionalBoolean(root.field(WANT_TO_RETIRE)).orElseGet(node.status()::wantToRetire),
asOptionalBoolean(root.field(WANT_TO_DEPROVISION))
- .filter(want -> recursiveWantToDeprovision.value() || !applyingAsChild)
.orElseGet(node.status()::wantToDeprovision),
asOptionalBoolean(root.field(WANT_TO_REBUILD))
.filter(want -> !applyingAsChild)
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
index cde951d1bf1..b1a66e34c9e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
@@ -26,7 +26,6 @@ import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
-import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.hosted.provision.NoSuchNodeException;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
@@ -76,16 +75,13 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler {
private final Orchestrator orchestrator;
private final NodeRepository nodeRepository;
private final NodeFlavors nodeFlavors;
- private final FlagSource flagSource;
@Inject
- public NodesV2ApiHandler(ThreadedHttpRequestHandler.Context parentCtx, Orchestrator orchestrator,
- NodeRepository nodeRepository, NodeFlavors flavors, FlagSource flagSource) {
+ public NodesV2ApiHandler(Context parentCtx, Orchestrator orchestrator, NodeRepository nodeRepository, NodeFlavors flavors) {
super(parentCtx);
this.orchestrator = orchestrator;
this.nodeRepository = nodeRepository;
this.nodeFlavors = flavors;
- this.flagSource = flagSource;
}
@Override
@@ -172,7 +168,7 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler {
private HttpResponse handlePATCH(HttpRequest request) {
Path path = new Path(request.getUri());
if (path.matches("/nodes/v2/node/{hostname}")) {
- NodePatcher patcher = new NodePatcher(nodeFlavors, nodeRepository, flagSource);
+ NodePatcher patcher = new NodePatcher(nodeFlavors, nodeRepository);
String hostname = path.get("hostname");
if (isTenantPeer(request)) {
patcher.patchFromUntrustedTenantHost(hostname, request.getData());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java
index 3c20f6ddb09..240d0daf96f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java
@@ -7,6 +7,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+/**
+ * @author mpolden
+ */
public class LoadBalancersV1ApiTest {
private RestApiTester tester;
@@ -22,11 +25,19 @@ public class LoadBalancersV1ApiTest {
}
@Test
- public void test_load_balancers() throws Exception {
+ public void load_balancers() throws Exception {
tester.assertFile(new Request("http://localhost:8080/loadbalancers/v1"), "load-balancers.json");
tester.assertFile(new Request("http://localhost:8080/loadbalancers/v1/"), "load-balancers.json");
tester.assertFile(new Request("http://localhost:8080/loadbalancers/v1/?application=tenant4.application4.instance4"), "load-balancers-single.json");
tester.assertResponse(new Request("http://localhost:8080/loadbalancers/v1/?application=tenant.nonexistent.default"), "{\"loadBalancers\":[]}");
}
+ @Test
+ public void set_state() throws Exception {
+ tester.assertResponse(new Request("http://localhost:8080/loadbalancers/v1/state/removable/tenant42:application42:instance42:id42", "", Request.Method.PUT),
+ 404, "{\"error-code\":\"NOT_FOUND\",\"message\":\"load balancer tenant42:application42:instance42:id42 does not exist\"}");
+ tester.assertResponse(new Request("http://localhost:8080/loadbalancers/v1/state/removable/tenant4:application4:instance4:id4", "", Request.Method.PUT),
+ "{\"message\":\"Moved load balancer tenant4:application4:instance4:id4 to removable\"}");
+ }
+
}