diff options
author | Valerij Fredriksen <freva@users.noreply.github.com> | 2020-04-24 13:50:14 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-24 13:50:14 +0200 |
commit | 43e1abc79588e593262796909d85f3419a78e1ac (patch) | |
tree | 3784e3e623d594e7d62ab5300a510ad6faaf0bbb | |
parent | b88b0df871042db8695b13f3435c0c2afb61a5b8 (diff) | |
parent | d8ec2c5c7dc4836dc76226bdddb71b3914fd4bad (diff) |
Merge pull request #13054 from vespa-engine/bratseth/expose-application-info-rebased
Bratseth/expose application info
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 |