aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2020-04-23 21:51:07 +0200
committerJon Bratseth <bratseth@gmail.com>2020-04-23 22:08:11 +0200
commit6094d7c082d642530f1878f568be3e18e52b56a5 (patch)
treee44daf9d05913133bbf7cbda075b0372404ffdee
parentafdeb2a86df8a471166252da1f178e5c528631b9 (diff)
nodes/v2/application responses WIP
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/ApplicationSerializer.java48
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeResourcesSerializer.java57
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializer.java45
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java62
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java13
5 files changed, 163 insertions, 62 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/ApplicationSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/ApplicationSerializer.java
new file mode 100644
index 00000000000..54f60651795
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/ApplicationSerializer.java
@@ -0,0 +1,48 @@
+// Copyright Verizon Media. 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.config.provision.ClusterResources;
+import com.yahoo.config.provision.NodeResources;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.provision.applications.Application;
+import com.yahoo.vespa.hosted.provision.applications.Cluster;
+
+import java.net.URI;
+import java.util.Collection;
+
+/**
+ * Serializes application information for nodes/v2/application responses
+ */
+public class ApplicationSerializer {
+
+ public static Slime toSlime(Application application, URI applicationUri) {
+ Slime slime = new Slime();
+ toSlime(application, slime.setObject(), applicationUri);
+ return slime;
+ }
+
+ private static void toSlime(Application application, Cursor object, URI applicationUri) {
+ object.setString("url", applicationUri.toString());
+ object.setString("id", application.id().toFullString());
+ clustersToSlime(application.clusters().values(), object.setObject("clusters"));
+ }
+
+ private static void clustersToSlime(Collection<Cluster> clusters, Cursor clustersObject) {
+ clusters.forEach(cluster -> toSlime(cluster, clustersObject.setObject(cluster.id().value())));
+ }
+
+ private static void toSlime(Cluster cluster, Cursor clusterObject) {
+ toSlime(cluster.minResources(), clusterObject.setObject("min"));
+ toSlime(cluster.maxResources(), clusterObject.setObject("max"));
+ cluster.suggestedResources().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject("suggested")));
+ cluster.targetResources().ifPresent(target -> toSlime(target, clusterObject.setObject("target")));
+ }
+
+ private static void toSlime(ClusterResources resources, Cursor clusterResourcesObject) {
+ clusterResourcesObject.setLong("nodes", resources.nodes());
+ clusterResourcesObject.setLong("groups", resources.groups());
+ NodeResourcesSerializer.toSlime(resources.nodeResources(), clusterResourcesObject.setObject("resources"));
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeResourcesSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeResourcesSerializer.java
new file mode 100644
index 00000000000..10b3d6a4db8
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeResourcesSerializer.java
@@ -0,0 +1,57 @@
+// Copyright Verizon Media. 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.config.provision.NodeResources;
+import com.yahoo.slime.Cursor;
+
+/**
+ * @author bratseth
+ */
+public class NodeResourcesSerializer {
+
+ static void toSlime(NodeResources resources, Cursor object) {
+ object.setDouble("vcpu", resources.vcpu());
+ object.setDouble("memoryGb", resources.memoryGb());
+ object.setDouble("diskGb", resources.diskGb());
+ object.setDouble("bandwidthGbps", resources.bandwidthGbps());
+ object.setString("diskSpeed", toString(resources.diskSpeed()));
+ object.setString("storageType", toString(resources.storageType()));
+ }
+
+ public NodeResources.DiskSpeed diskSpeedFrom(String diskSpeed) {
+ switch (diskSpeed) {
+ case "fast": return NodeResources.DiskSpeed.fast;
+ case "slow": return NodeResources.DiskSpeed.slow;
+ case "any" : return NodeResources.DiskSpeed.any;
+ default: throw new IllegalArgumentException("Unknown disk speed '" + diskSpeed + "'");
+ }
+ }
+
+ private static String toString(NodeResources.DiskSpeed diskSpeed) {
+ switch (diskSpeed) {
+ case fast : return "fast";
+ case slow : return "slow";
+ case any : return "any";
+ default: throw new IllegalArgumentException("Unknown disk speed '" + diskSpeed.name() + "'");
+ }
+ }
+
+ public NodeResources.StorageType storageTypeFrom(String storageType) {
+ switch (storageType) {
+ case "local" : return NodeResources.StorageType.local;
+ case "remote": return NodeResources.StorageType.remote;
+ case "any" : return NodeResources.StorageType.any;
+ default: throw new IllegalArgumentException("Unknown storage type '" + storageType + "'");
+ }
+ }
+
+ private static String toString(NodeResources.StorageType storageType) {
+ switch (storageType) {
+ case remote : return "remote";
+ case local : return "local";
+ case any : return "any";
+ default: throw new IllegalArgumentException("Unknown storage type '" + storageType.name() + "'");
+ }
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializer.java
index 492994c2f0e..67f3b293de8 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializer.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.restapi.v2;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
+import com.yahoo.slime.Cursor;
import com.yahoo.vespa.hosted.provision.Node;
/**
@@ -12,7 +13,7 @@ import com.yahoo.vespa.hosted.provision.Node;
*/
public class NodeSerializer {
- public Node.State stateFrom(String state) {
+ public static Node.State stateFrom(String state) {
switch (state) {
case "active": return Node.State.active;
case "dirty": return Node.State.dirty;
@@ -27,7 +28,7 @@ public class NodeSerializer {
}
}
- public String toString(Node.State state) {
+ public static String toString(Node.State state) {
switch (state) {
case active: return "active";
case dirty: return "dirty";
@@ -42,7 +43,7 @@ public class NodeSerializer {
}
}
- public NodeType typeFrom(String nodeType) {
+ public static NodeType typeFrom(String nodeType) {
switch (nodeType) {
case "tenant": return NodeType.tenant;
case "host": return NodeType.host;
@@ -57,7 +58,7 @@ public class NodeSerializer {
}
}
- public String toString(NodeType type) {
+ public static String toString(NodeType type) {
switch (type) {
case tenant: return "tenant";
case host: return "host";
@@ -72,40 +73,4 @@ public class NodeSerializer {
}
}
- public NodeResources.DiskSpeed diskSpeedFrom(String diskSpeed) {
- switch (diskSpeed) {
- case "fast": return NodeResources.DiskSpeed.fast;
- case "slow": return NodeResources.DiskSpeed.slow;
- case "any" : return NodeResources.DiskSpeed.any;
- default: throw new IllegalArgumentException("Unknown disk speed '" + diskSpeed + "'");
- }
- }
-
- public String toString(NodeResources.DiskSpeed diskSpeed) {
- switch (diskSpeed) {
- case fast : return "fast";
- case slow : return "slow";
- case any : return "any";
- default: throw new IllegalArgumentException("Unknown disk speed '" + diskSpeed.name() + "'");
- }
- }
-
- public NodeResources.StorageType storageTypeFrom(String storageType) {
- switch (storageType) {
- case "local" : return NodeResources.StorageType.local;
- case "remote": return NodeResources.StorageType.remote;
- case "any" : return NodeResources.StorageType.any;
- default: throw new IllegalArgumentException("Unknown storage type '" + storageType + "'");
- }
- }
-
- public String toString(NodeResources.StorageType storageType) {
- switch (storageType) {
- case remote : return "remote";
- case local : return "local";
- case any : return "any";
- default: throw new IllegalArgumentException("Unknown storage type '" + storageType.name() + "'");
- }
- }
-
}
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 809e4200e7e..9c1b1e03b11 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.restapi.v2;
import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostFilter;
@@ -17,7 +18,9 @@ import com.yahoo.restapi.ErrorResponse;
import com.yahoo.restapi.MessageResponse;
import com.yahoo.restapi.Path;
import com.yahoo.restapi.ResourceResponse;
+import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
@@ -25,6 +28,7 @@ import com.yahoo.slime.Type;
import com.yahoo.vespa.hosted.provision.NoSuchNodeException;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.applications.Application;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter;
@@ -42,6 +46,8 @@ import javax.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -100,17 +106,20 @@ public class NodesApiHandler extends LoggingRequestHandler {
}
private HttpResponse handleGET(HttpRequest request) {
- String path = request.getUri().getPath();
- if (path.equals( "/nodes/v2/")) return new ResourceResponse(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);
- if (path.startsWith("/nodes/v2/state/")) return new NodesResponse(ResponseType.nodesInStateList, request, orchestrator, nodeRepository);
- if (path.startsWith("/nodes/v2/acl/")) return new NodeAclResponse(request, nodeRepository);
- if (path.equals( "/nodes/v2/command/")) return new ResourceResponse(request.getUri(), "restart", "reboot");
- if (path.equals( "/nodes/v2/maintenance/")) return new JobsResponse(nodeRepository.jobControl());
- if (path.equals( "/nodes/v2/upgrade/")) return new UpgradeResponse(nodeRepository.infrastructureVersions(), nodeRepository.osVersions(), nodeRepository.dockerImages());
- if (path.startsWith("/nodes/v2/capacity")) return new HostCapacityResponse(nodeRepository, request);
+ Path path = new Path(request.getUri());
+ String pathS = request.getUri().toString();
+ if (pathS.equals( "/nodes/v2/")) return new ResourceResponse(request.getUri(), "state", "node", "command", "maintenance", "upgrade");
+ if (pathS.equals( "/nodes/v2/node/")) return new NodesResponse(ResponseType.nodeList, request, orchestrator, nodeRepository);
+ if (pathS.startsWith("/nodes/v2/node/")) return new NodesResponse(ResponseType.singleNode, request, orchestrator, nodeRepository);
+ if (pathS.equals( "/nodes/v2/state/")) return new NodesResponse(ResponseType.stateList, request, orchestrator, nodeRepository);
+ if (pathS.startsWith("/nodes/v2/state/")) return new NodesResponse(ResponseType.nodesInStateList, request, orchestrator, nodeRepository);
+ if (pathS.startsWith("/nodes/v2/acl/")) return new NodeAclResponse(request, nodeRepository);
+ if (pathS.equals( "/nodes/v2/command/")) return new ResourceResponse(request.getUri(), "restart", "reboot");
+ if (pathS.equals( "/nodes/v2/maintenance/")) return new JobsResponse(nodeRepository.jobControl());
+ if (pathS.equals( "/nodes/v2/upgrade/")) return new UpgradeResponse(nodeRepository.infrastructureVersions(), nodeRepository.osVersions(), nodeRepository.dockerImages());
+ if (pathS.startsWith("/nodes/v2/capacity")) return new HostCapacityResponse(nodeRepository, request);
+ if (path.matches("/nodes/v2/application")) return applicationList(request.getUri());
+ if (path.matches("/nodes/v2/application/{applicationId}")) return application(path.get("applicationId"), request.getUri());
throw new NotFoundException("Nothing at path '" + path + "'");
}
@@ -394,4 +403,35 @@ public class NodesApiHandler extends LoggingRequestHandler {
return nodes.stream().map(Node::hostname).sorted().collect(Collectors.joining(", "));
}
+ private HttpResponse applicationList(URI uri) {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ Cursor applications = root.setArray("applications");
+ for (ApplicationId id : nodeRepository.applications().ids()) {
+ Cursor application = applications.addObject();
+ application.setString("url", withPath("nodes/v2/applications/" + id.toFullString(), uri).toString());
+ application.setString("id", id.toFullString());
+ }
+ return new SlimeJsonResponse(slime);
+ }
+
+ private HttpResponse application(String id, URI uri) {
+ Optional<Application> application = nodeRepository.applications().get(ApplicationId.fromFullString(id));
+ if (application.isEmpty())
+ return ErrorResponse.notFoundError("No application '" + id + "'");
+ Slime slime = ApplicationSerializer.toSlime(application.get(),
+ withPath("nodes/v2/applications/" + id, uri));
+ return new SlimeJsonResponse(slime);
+ }
+
+ /** Returns a copy of the given URI with the host and port from the given URI and the path set to the given path */
+ private URI withPath(String newPath, URI uri) {
+ try {
+ return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), newPath, null, null);
+ }
+ catch (URISyntaxException e) {
+ throw new RuntimeException("Will not happen", e);
+ }
+ }
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java
index a4412a502aa..fbb9b207c32 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java
@@ -148,7 +148,7 @@ class NodesResponse extends HttpResponse {
node.reservedTo().ifPresent(reservedTo -> object.setString("reservedTo", reservedTo.value()));
if (node.flavor().isConfigured())
object.setDouble("cpuCores", node.flavor().getMinCpuCores());
- toSlime(node.flavor().resources(), object.setObject("resources"));
+ NodeResourcesSerializer.toSlime(node.flavor().resources(), object.setObject("resources"));
if (node.flavor().cost() > 0)
object.setLong("cost", node.flavor().cost());
object.setString("environment", node.flavor().getType().name());
@@ -160,7 +160,7 @@ class NodesResponse extends HttpResponse {
object.setString("wantedDockerImage", allocation.membership().cluster().dockerImage()
.orElseGet(() -> nodeRepository.dockerImage(node).withTag(allocation.membership().cluster().vespaVersion()).asString()));
object.setString("wantedVespaVersion", allocation.membership().cluster().vespaVersion().toFullString());
- toSlime(allocation.requestedResources(), object.setObject("requestedResources"));
+ NodeResourcesSerializer.toSlime(allocation.requestedResources(), object.setObject("requestedResources"));
allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray("networkPorts")));
orchestrator.apply(new HostName(node.hostname()))
.ifPresent(info -> {
@@ -210,15 +210,6 @@ class NodesResponse extends HttpResponse {
}
}
- private void toSlime(NodeResources resources, Cursor object) {
- object.setDouble("vcpu", resources.vcpu());
- object.setDouble("memoryGb", resources.memoryGb());
- object.setDouble("diskGb", resources.diskGb());
- object.setDouble("bandwidthGbps", resources.bandwidthGbps());
- object.setString("diskSpeed", serializer.toString(resources.diskSpeed()));
- object.setString("storageType", serializer.toString(resources.storageType()));
- }
-
// Hack: For non-docker nodes, return current docker image as default prefix + current Vespa version
// TODO: Remove current + wanted docker image from response for non-docker types
private Optional<DockerImage> currentDockerImage(Node node) {