summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorValerij Fredriksen <freva@users.noreply.github.com>2020-04-24 13:50:14 +0200
committerGitHub <noreply@github.com>2020-04-24 13:50:14 +0200
commit43e1abc79588e593262796909d85f3419a78e1ac (patch)
tree3784e3e623d594e7d62ab5300a510ad6faaf0bbb
parentb88b0df871042db8695b13f3435c0c2afb61a5b8 (diff)
parentd8ec2c5c7dc4836dc76226bdddb71b3914fd4bad (diff)
Merge pull request #13054 from vespa-engine/bratseth/expose-application-info-rebased
Bratseth/expose application info
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ApplicationId.java8
-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.java75
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java25
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java22
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/README.md2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializerTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java11
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application1.json56
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application2.json32
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/applications.json20
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/root.json3
14 files changed, 325 insertions, 85 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ApplicationId.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ApplicationId.java
index f175af3e2f1..692c941877b 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ApplicationId.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ApplicationId.java
@@ -47,6 +47,14 @@ public final class ApplicationId implements Comparable<ApplicationId> {
return new Builder().tenant(parts[0]).applicationName(parts[1]).instanceName(parts[2]).build();
}
+ public static ApplicationId fromFullString(String idString) {
+ String[] parts = idString.split("\\.");
+ if (parts.length < 3)
+ throw new IllegalArgumentException("Application ids must be on the form tenant.application.instance, but was " + idString);
+
+ return new Builder().tenant(parts[0]).applicationName(parts[1]).instanceName(parts[2]).build();
+ }
+
@Override
public int hashCode() { return stringValue.hashCode(); }
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..0d0c9305a92
--- /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 static 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 static 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..6799b68daae 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;
@@ -64,7 +70,6 @@ public class NodesApiHandler extends LoggingRequestHandler {
private final Orchestrator orchestrator;
private final NodeRepository nodeRepository;
private final NodeFlavors nodeFlavors;
- private final NodeSerializer serializer = new NodeSerializer();
@Inject
public NodesApiHandler(LoggingRequestHandler.Context parentCtx, Orchestrator orchestrator,
@@ -100,18 +105,21 @@ 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);
- throw new NotFoundException("Nothing at path '" + path + "'");
+ Path path = new Path(request.getUri());
+ String pathS = request.getUri().getPath();
+ if (pathS.equals( "/nodes/v2/")) return new ResourceResponse(request.getUri(), "state", "node", "command", "maintenance", "upgrade", "application");
+ 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);
}
private HttpResponse handlePUT(HttpRequest request) {
@@ -254,8 +262,8 @@ public class NodesApiHandler extends LoggingRequestHandler {
requiredField(resourcesInspector, "memoryGb", Inspector::asDouble),
requiredField(resourcesInspector, "diskGb", Inspector::asDouble),
requiredField(resourcesInspector, "bandwidthGbps", Inspector::asDouble),
- optionalString(resourcesInspector.field("diskSpeed")).map(serializer::diskSpeedFrom).orElse(NodeResources.DiskSpeed.getDefault()),
- optionalString(resourcesInspector.field("storageType")).map(serializer::storageTypeFrom).orElse(NodeResources.StorageType.getDefault())));
+ optionalString(resourcesInspector.field("diskSpeed")).map(NodeResourcesSerializer::diskSpeedFrom).orElse(NodeResources.DiskSpeed.getDefault()),
+ optionalString(resourcesInspector.field("storageType")).map(NodeResourcesSerializer::storageTypeFrom).orElse(NodeResources.StorageType.getDefault())));
}
Flavor flavor = nodeFlavors.getFlavorOrThrow(flavorInspector.asString());
@@ -269,9 +277,9 @@ public class NodesApiHandler extends LoggingRequestHandler {
if (resourcesInspector.field("bandwidthGbps").valid())
flavor = flavor.with(flavor.resources().withBandwidthGbps(resourcesInspector.field("bandwidthGbps").asDouble()));
if (resourcesInspector.field("diskSpeed").valid())
- flavor = flavor.with(flavor.resources().with(serializer.diskSpeedFrom(resourcesInspector.field("diskSpeed").asString())));
+ flavor = flavor.with(flavor.resources().with(NodeResourcesSerializer.diskSpeedFrom(resourcesInspector.field("diskSpeed").asString())));
if (resourcesInspector.field("storageType").valid())
- flavor = flavor.with(flavor.resources().with(serializer.storageTypeFrom(resourcesInspector.field("storageType").asString())));
+ flavor = flavor.with(flavor.resources().with(NodeResourcesSerializer.storageTypeFrom(resourcesInspector.field("storageType").asString())));
}
return flavor;
}
@@ -284,7 +292,7 @@ public class NodesApiHandler extends LoggingRequestHandler {
private NodeType nodeTypeFromSlime(Inspector object) {
if (! object.valid()) return NodeType.tenant; // default
- return serializer.typeFrom(object.asString());
+ return NodeSerializer.typeFrom(object.asString());
}
private Optional<TenantName> reservedToFromSlime(Inspector object) {
@@ -394,4 +402,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..c6b92d0a7f5 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
@@ -5,7 +5,6 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.Flavor;
-import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.serialization.NetworkPortsSerializer;
import com.yahoo.container.jdisc.HttpRequest;
@@ -48,7 +47,6 @@ class NodesResponse extends HttpResponse {
private final Function<HostName, Optional<HostInfo>> orchestrator;
private final NodeRepository nodeRepository;
private final Slime slime;
- private final NodeSerializer serializer = new NodeSerializer();
public NodesResponse(ResponseType responseType, HttpRequest request,
Orchestrator orchestrator, NodeRepository nodeRepository) {
@@ -65,7 +63,7 @@ class NodesResponse extends HttpResponse {
switch (responseType) {
case nodeList: nodesToSlime(root); break;
case stateList : statesToSlime(root); break;
- case nodesInStateList: nodesToSlime(serializer.stateFrom(lastElement(parentUrl)), root); break;
+ case nodesInStateList: nodesToSlime(NodeSerializer.stateFrom(lastElement(parentUrl)), root); break;
case singleNode : nodeToSlime(lastElement(parentUrl), root); break;
default: throw new IllegalArgumentException();
}
@@ -97,11 +95,11 @@ class NodesResponse extends HttpResponse {
private void statesToSlime(Cursor root) {
Cursor states = root.setObject("states");
for (Node.State state : Node.State.values())
- toSlime(state, states.setObject(serializer.toString(state)));
+ toSlime(state, states.setObject(NodeSerializer.toString(state)));
}
private void toSlime(Node.State state, Cursor object) {
- object.setString("url", parentUrl + serializer.toString(state));
+ object.setString("url", parentUrl + NodeSerializer.toString(state));
if (recursive)
nodesToSlime(state, object);
}
@@ -136,10 +134,10 @@ class NodesResponse extends HttpResponse {
object.setString("url", nodeParentUrl + node.hostname());
if ( ! allFields) return;
object.setString("id", node.hostname());
- object.setString("state", serializer.toString(node.state()));
+ object.setString("state", NodeSerializer.toString(node.state()));
object.setString("type", node.type().name());
object.setString("hostname", node.hostname());
- object.setString("type", serializer.toString(node.type()));
+ object.setString("type", NodeSerializer.toString(node.type()));
if (node.parentHostname().isPresent()) {
object.setString("parentHostname", node.parentHostname().get());
}
@@ -148,7 +146,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 +158,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 +208,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) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
index 8ff17a8e5a3..0cafe35ee52 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
@@ -16,11 +16,14 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
+import com.yahoo.transaction.Mutex;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.flags.InMemoryFlagSource;
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.applications.Cluster;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.Status;
@@ -142,9 +145,22 @@ public class MockNodeRepository extends NodeRepository {
ClusterSpec zoneCluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin")).vespaVersion("6.42").build();
activate(provisioner.prepare(zoneApp, zoneCluster, Capacity.fromRequiredNodeType(NodeType.host), null), zoneApp, provisioner);
- ApplicationId app1 = ApplicationId.from(TenantName.from("tenant1"), ApplicationName.from("application1"), InstanceName.from("instance1"));
- ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1")).vespaVersion("6.42").build();
- provisioner.prepare(app1, cluster1, Capacity.from(new ClusterResources(2, 1, new NodeResources(2, 8, 50, 1))), null);
+ ApplicationId app1Id = ApplicationId.from(TenantName.from("tenant1"), ApplicationName.from("application1"), InstanceName.from("instance1"));
+ ClusterSpec cluster1Id = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1")).vespaVersion("6.42").build();
+ provisioner.prepare(app1Id,
+ cluster1Id,
+ Capacity.from(new ClusterResources(2, 1, new NodeResources(2, 8, 50, 1)),
+ new ClusterResources(8, 2, new NodeResources(4, 16, 1000, 1)), false, true),
+ null);
+ Application app1 = applications().get(app1Id).get();
+ Cluster cluster1 = app1.cluster(cluster1Id.id()).get();
+ cluster1 = cluster1.withSuggested(new ClusterResources(6, 2,
+ new NodeResources(3, 20, 100, 1)));
+ cluster1 = cluster1.withTarget(new ClusterResources(4, 1,
+ new NodeResources(3, 16, 100, 1)));
+ try (Mutex lock = lock(app1Id)) {
+ applications().put(app1.with(cluster1), lock);
+ }
ApplicationId app2 = ApplicationId.from(TenantName.from("tenant2"), ApplicationName.from("application2"), InstanceName.from("instance2"));
ClusterSpec cluster2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id2")).vespaVersion("6.42").build();
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/README.md b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/README.md
index bbb2ec7080b..f7421bf07c9 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/README.md
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/README.md
@@ -1,5 +1,5 @@
<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-The test resources are used by both NoadAdmin and NodeRepository
+The test resources are used by both NodeAdmin and NodeRepository
tests to verify APIs. So when modifying this test data
remember to check tests for both NodeAdmin and NodeRepository.
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializerTest.java
index 05b23addddf..ed2aae64b5f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeSerializerTest.java
@@ -13,19 +13,17 @@ import static org.junit.Assert.assertEquals;
*/
public class NodeSerializerTest {
- private final NodeSerializer serializer = new NodeSerializer();
-
@Test
public void serialize_node_types() {
for (NodeType t : NodeType.values()) {
- assertEquals(t, serializer.typeFrom(serializer.toString(t)));
+ assertEquals(t, NodeSerializer.typeFrom(NodeSerializer.toString(t)));
}
}
@Test
public void serialize_node_states() {
for (Node.State s : Node.State.values()) {
- assertEquals(s, serializer.stateFrom(serializer.toString(s)));
+ assertEquals(s, NodeSerializer.stateFrom(NodeSerializer.toString(s)));
}
}
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 0001c344dd3..838a39ba01e 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
@@ -56,7 +56,7 @@ public class RestApiTest {
if (container != null) container.close();
}
- /** This test gives examples of all the requests that can be made to nodes/v2 */
+ /** This test gives examples of the node requests that can be made to nodes/v2 */
@Test
public void test_requests() throws Exception {
// GET
@@ -232,6 +232,15 @@ public class RestApiTest {
}
@Test
+ public void test_application_requests() throws Exception {
+ assertFile(new Request("http://localhost:8080/nodes/v2/application/"), "applications.json");
+ assertFile(new Request("http://localhost:8080/nodes/v2/application/tenant1.application1.instance1"),
+ "application1.json");
+ assertFile(new Request("http://localhost:8080/nodes/v2/application/tenant2.application2.instance2"),
+ "application2.json");
+ }
+
+ @Test
public void maintenance_requests() throws Exception {
// POST deactivation of a maintenance job
assertResponse(new Request("http://localhost:8080/nodes/v2/maintenance/inactive/NodeFailer",
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application1.json
new file mode 100644
index 00000000000..ce2309e4d6c
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application1.json
@@ -0,0 +1,56 @@
+{
+ "url" : "http://localhost:8080/nodes/v2/applications/tenant1.application1.instance1",
+ "id" : "tenant1.application1.instance1",
+ "clusters" : {
+ "id1" : {
+ "min" : {
+ "nodes" : 2,
+ "groups" : 1,
+ "resources" : {
+ "vcpu" : 2.0,
+ "memoryGb" : 8.0,
+ "diskGb" : 50.0,
+ "bandwidthGbps" : 1.0,
+ "diskSpeed" : "fast",
+ "storageType" : "any"
+ }
+ },
+ "max" : {
+ "nodes" : 8,
+ "groups" : 2,
+ "resources" : {
+ "vcpu" : 4.0,
+ "memoryGb" : 16.0,
+ "diskGb" : 1000.0,
+ "bandwidthGbps" : 1.0,
+ "diskSpeed" : "fast",
+ "storageType" : "any"
+ }
+ },
+ "suggested" : {
+ "nodes" : 6,
+ "groups" : 2,
+ "resources" : {
+ "vcpu" : 3.0,
+ "memoryGb" : 20.0,
+ "diskGb" : 100.0,
+ "bandwidthGbps" : 1.0,
+ "diskSpeed" : "fast",
+ "storageType" : "any"
+ }
+ },
+ "target" : {
+ "nodes" : 4,
+ "groups" : 1,
+ "resources" : {
+ "vcpu" : 3.0,
+ "memoryGb" : 16.0,
+ "diskGb" : 100.0,
+ "bandwidthGbps" : 1.0,
+ "diskSpeed" : "fast",
+ "storageType" : "any"
+ }
+ }
+ }
+ }
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application2.json
new file mode 100644
index 00000000000..75af5d4a328
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/application2.json
@@ -0,0 +1,32 @@
+{
+ "url": "http://localhost:8080/nodes/v2/applications/tenant2.application2.instance2",
+ "id": "tenant2.application2.instance2",
+ "clusters": {
+ "id2": {
+ "min": {
+ "nodes": 2,
+ "groups": 1,
+ "resources": {
+ "vcpu": 2.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "fast",
+ "storageType": "any"
+ }
+ },
+ "max": {
+ "nodes": 2,
+ "groups": 1,
+ "resources": {
+ "vcpu": 2.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "fast",
+ "storageType": "any"
+ }
+ }
+ }
+ }
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/applications.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/applications.json
new file mode 100644
index 00000000000..6d25b3e59f5
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/applications.json
@@ -0,0 +1,20 @@
+{
+ "applications" : [
+ {
+ "url" : "http://localhost:8080/nodes/v2/applications/tenant4.application4.instance4",
+ "id" : "tenant4.application4.instance4"
+ },
+ {
+ "url" : "http://localhost:8080/nodes/v2/applications/tenant2.application2.instance2",
+ "id" : "tenant2.application2.instance2"
+ },
+ {
+ "url" : "http://localhost:8080/nodes/v2/applications/tenant3.application3.instance3",
+ "id" : "tenant3.application3.instance3"
+ },
+ {
+ "url" : "http://localhost:8080/nodes/v2/applications/tenant1.application1.instance1",
+ "id" : "tenant1.application1.instance1"
+ }
+ ]
+} \ No newline at end of file
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 86becefb146..cc91314df84 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
@@ -14,6 +14,9 @@
},
{
"url":"http://localhost:8080/nodes/v2/upgrade/"
+ },
+ {
+ "url":"http://localhost:8080/nodes/v2/application/"
}
]
} \ No newline at end of file