summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2020-05-06 13:24:09 +0200
committerGitHub <noreply@github.com>2020-05-06 13:24:09 +0200
commit8b107b8a4f56dce7d33371a423f26d2cf154a53d (patch)
tree38cec30614dab42779b1b7b7bd37190d4b0c01aa
parentee4fb54e2078961257a9c399f6e90622dc0b2298 (diff)
parentd9095a974d0fde9f65cf66f68aedc04c07d89362 (diff)
Merge pull request #13166 from vespa-engine/bratseth/propagate-cluster-info
Bratseth/propagate cluster info
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java10
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Application.java35
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java47
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ApplicationData.java30
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java39
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterResourcesData.java29
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeResources.java24
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ProvisionResource.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java63
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java22
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json58
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-nodes.json2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java12
-rw-r--r--vespa-http-client/src/main/java/com/yahoo/vespa/http/client/FeedClient.java2
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/vespa/http/server/FeederSettings.java1
20 files changed, 374 insertions, 41 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java
index bed36d0d5d2..18dc0b217a3 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeResources.java
@@ -10,6 +10,11 @@ import java.util.Objects;
*/
public class NodeResources {
+ // Standard unit cost in dollars per hour
+ private static final double cpuUnitCost = 12.0;
+ private static final double memoryUnitCost = 1.2;
+ private static final double diskUnitCost = 0.045;
+
public static final NodeResources unspecified = new NodeResources(0, 0, 0, 0);
public enum DiskSpeed {
@@ -113,6 +118,11 @@ public class NodeResources {
public DiskSpeed diskSpeed() { return diskSpeed; }
public StorageType storageType() { return storageType; }
+ /** Returns the standard cost of these resources, in dollars per hour */
+ public double cost() {
+ return vcpu * cpuUnitCost + memoryGb * memoryUnitCost + diskGb * diskUnitCost;
+ }
+
public NodeResources withVcpu(double vcpu) {
if (vcpu == this.vcpu) return this;
return new NodeResources(vcpu, memoryGb, diskGb, bandwidthGbps, diskSpeed, storageType);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Application.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Application.java
new file mode 100644
index 00000000000..1499d5c1b79
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Application.java
@@ -0,0 +1,35 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.configserver;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterSpec;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * The client-side version of the node repository's view of applications
+ *
+ * @author bratseth
+ */
+public class Application {
+
+ private ApplicationId id;
+ private Map<ClusterSpec.Id, Cluster> clusters;
+
+ public Application(ApplicationId id, Collection<Cluster> clusters) {
+ this.id = id;
+ this.clusters = clusters.stream().collect(Collectors.toUnmodifiableMap(c -> c.id(), c -> c));
+ }
+
+ public ApplicationId id() { return id; }
+
+ public Map<ClusterSpec.Id, Cluster> clusters() { return clusters; }
+
+ @Override
+ public String toString() {
+ return "application '" + id + "'";
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java
new file mode 100644
index 00000000000..fd339e3bb43
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Cluster.java
@@ -0,0 +1,47 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.configserver;
+
+import com.yahoo.config.provision.ClusterResources;
+import com.yahoo.config.provision.ClusterSpec;
+
+import java.util.Optional;
+
+/**
+ * @author bratseth
+ */
+public class Cluster {
+
+ private final ClusterSpec.Id id;
+ private final ClusterResources min;
+ private final ClusterResources max;
+ private final ClusterResources current;
+ private final Optional<ClusterResources> target;
+ private final Optional<ClusterResources> suggested;
+
+ public Cluster(ClusterSpec.Id id,
+ ClusterResources min,
+ ClusterResources max,
+ ClusterResources current,
+ Optional<ClusterResources> target,
+ Optional<ClusterResources> suggested) {
+ this.id = id;
+ this.min = min;
+ this.max = max;
+ this.current = current;
+ this.target = target;
+ this.suggested = suggested;
+ }
+
+ public ClusterSpec.Id id() { return id; }
+ public ClusterResources min() { return min; }
+ public ClusterResources max() { return max; }
+ public ClusterResources current() { return current; }
+ public Optional<ClusterResources> target() { return target; }
+ public Optional<ClusterResources> suggested() { return suggested; }
+
+ @Override
+ public String toString() {
+ return "cluster '" + id + "'";
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
index c5a016d53c8..b5d49df7e9c 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java
@@ -70,6 +70,8 @@ public interface NodeRepository {
.collect(Collectors.toList());
}
+ Application getApplication(ZoneId zone, ApplicationId application);
+
/** Upgrade all nodes of given type to a new version */
void upgrade(ZoneId zone, NodeType type, Version version);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ApplicationData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ApplicationData.java
new file mode 100644
index 00000000000..1fc6d7f898a
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ApplicationData.java
@@ -0,0 +1,30 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.noderepository;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Application;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author bratseth
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ApplicationData {
+
+ @JsonProperty
+ public String id;
+ @JsonProperty
+ public Map<String, ClusterData> clusters;
+
+ public Application toApplication() {
+ return new Application(ApplicationId.fromFullString(id),
+ clusters.entrySet().stream().map(e -> e.getValue().toCluster(e.getKey())).collect(Collectors.toList()));
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java
new file mode 100644
index 00000000000..298928a881d
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterData.java
@@ -0,0 +1,39 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.noderepository;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Cluster;
+
+import java.util.Optional;
+
+/**
+ * @author bratseth
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ClusterData {
+
+ @JsonProperty("min")
+ public ClusterResourcesData min;
+ @JsonProperty("max")
+ public ClusterResourcesData max;
+ @JsonProperty("current")
+ public ClusterResourcesData current;
+ @JsonProperty("suggested")
+ public ClusterResourcesData suggested;
+ @JsonProperty("target")
+ public ClusterResourcesData target;
+
+ public Cluster toCluster(String id) {
+ return new Cluster(ClusterSpec.Id.from(id),
+ min.toClusterResources(),
+ max.toClusterResources(),
+ current.toClusterResources(),
+ target == null ? Optional.empty() : Optional.of(target.toClusterResources()),
+ suggested == null ? Optional.empty() : Optional.of(suggested.toClusterResources()));
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterResourcesData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterResourcesData.java
new file mode 100644
index 00000000000..571af2a80c0
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ClusterResourcesData.java
@@ -0,0 +1,29 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.noderepository;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.config.provision.ClusterResources;
+
+import java.util.Optional;
+
+/**
+ * @author bratseth
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ClusterResourcesData {
+
+ @JsonProperty
+ public int nodes;
+ @JsonProperty
+ public int groups;
+ @JsonProperty
+ public NodeResources resources;
+
+ public ClusterResources toClusterResources() {
+ return new ClusterResources(nodes, groups, resources.toNodeResources());
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeResources.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeResources.java
index ded61c7a94e..b46412376c7 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeResources.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeResources.java
@@ -73,6 +73,30 @@ public class NodeResources {
this.storageType = storageType;
}
+ public com.yahoo.config.provision.NodeResources toNodeResources() {
+ return new com.yahoo.config.provision.NodeResources(vcpu, memoryGb, diskGb, bandwidthGbps,
+ toDiskSpeed(diskSpeed),
+ toStorageType(storageType));
+ }
+
+ private com.yahoo.config.provision.NodeResources.DiskSpeed toDiskSpeed(String diskSpeed) {
+ switch (diskSpeed) {
+ case "fast" : return com.yahoo.config.provision.NodeResources.DiskSpeed.fast;
+ case "slow" : return com.yahoo.config.provision.NodeResources.DiskSpeed.slow;
+ case "any" : return com.yahoo.config.provision.NodeResources.DiskSpeed.any;
+ default : throw new IllegalArgumentException("Unknown disk speed '" + diskSpeed + "'");
+ }
+ }
+
+ private com.yahoo.config.provision.NodeResources.StorageType toStorageType(String storageType) {
+ switch (storageType) {
+ case "remote" : return com.yahoo.config.provision.NodeResources.StorageType.remote;
+ case "local" : return com.yahoo.config.provision.NodeResources.StorageType.local;
+ case "any" : return com.yahoo.config.provision.NodeResources.StorageType.any;
+ default : throw new IllegalArgumentException("Unknown storage type '" + storageType + "'");
+ }
+ }
+
@Override
public String toString() {
return "NodeResources{" +
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ProvisionResource.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ProvisionResource.java
index 5eeb1f7401e..97720adbcc6 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ProvisionResource.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/ProvisionResource.java
@@ -53,11 +53,14 @@ public interface ProvisionResource {
NodeList listNodes(@QueryParam("recursive") boolean recursive,
@QueryParam("hostname") String hostnamesString);
- @GET
@Path("/node/")
NodeList listNodesWithParent(@QueryParam("recursive") boolean recursive,
@QueryParam("parentHost") String parentHostname);
+ @GET
+ @Path("/application/{application}")
+ ApplicationData getApplication(@PathParam("application") String applicationId);
+
@PUT
@Path("/state/{state}/{hostname}")
String setState(@PathParam("state") NodeState state, @PathParam("hostname") String hostname);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
index 80de7f802b6..2ed4c057dfd 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
@@ -107,11 +107,13 @@ enum PathGroup {
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/deploying/{*}",
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/job/{*}",
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/nodes",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/clusters",
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/logs",
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/suspended",
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/service/{*}",
"/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/global-rotation/{*}",
"/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/nodes",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/clusters",
"/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/logs",
"/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/suspended",
"/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/service/{*}",
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index c5da5755474..4eb2558f498 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -9,6 +9,7 @@ import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.NodeResources;
@@ -29,7 +30,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.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.LockedTenant;
@@ -44,9 +44,11 @@ import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbi
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Cluster;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Application;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
@@ -229,6 +231,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/service")) return services(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/service/{service}/{*}")) return service(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.get("service"), path.getRest(), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/nodes")) return nodes(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"));
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/clusters")) return clusters(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"));
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/logs")) return logs(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request.propertyMap());
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/global-rotation")) return rotationStatus(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), Optional.ofNullable(request.getProperty("endpointId")));
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/global-rotation/override")) return getGlobalRotationOverride(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"));
@@ -238,6 +241,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service")) return services(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service/{service}/{*}")) return service(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.get("service"), path.getRest(), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/nodes")) return nodes(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"));
+ if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/clusters")) return clusters(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"));
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/logs")) return logs(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request.propertyMap());
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation")) return rotationStatus(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), Optional.ofNullable(request.getProperty("endpointId")));
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/override")) return getGlobalRotationOverride(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"));
@@ -408,7 +412,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
Slime slime = new Slime();
Cursor applicationArray = slime.setArray();
- for (Application application : controller.applications().asList(tenant)) {
+ for (com.yahoo.vespa.hosted.controller.Application application : controller.applications().asList(tenant)) {
if (applicationName.map(application.id().application().value()::equals).orElse(true)) {
Cursor applicationObject = applicationArray.addObject();
applicationObject.setString("tenant", application.id().tenant().value());
@@ -587,7 +591,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new MessageResponse(messageBuilder.toString());
}
- private Application getApplication(String tenantName, String applicationName) {
+ private com.yahoo.vespa.hosted.controller.Application getApplication(String tenantName, String applicationName) {
TenantAndApplicationId applicationId = TenantAndApplicationId.from(tenantName, applicationName);
return controller.applications().getApplication(applicationId)
.orElseThrow(() -> new NotExistsException(applicationId + " not found"));
@@ -614,12 +618,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
nodeObject.setString("orchestration", valueOf(node.serviceState()));
nodeObject.setString("version", node.currentVersion().toString());
nodeObject.setString("flavor", node.flavor());
- nodeObject.setDouble("vcpu", node.resources().vcpu());
- nodeObject.setDouble("memoryGb", node.resources().memoryGb());
- nodeObject.setDouble("diskGb", node.resources().diskGb());
- nodeObject.setDouble("bandwidthGbps", node.resources().bandwidthGbps());
- nodeObject.setString("diskSpeed", valueOf(node.resources().diskSpeed()));
- nodeObject.setString("storageType", valueOf(node.resources().storageType()));
+ toSlime(node.resources(), nodeObject);
nodeObject.setBool("fastDisk", node.resources().diskSpeed() == NodeResources.DiskSpeed.fast); // TODO: Remove
nodeObject.setString("clusterId", node.clusterId());
nodeObject.setString("clusterType", valueOf(node.clusterType()));
@@ -627,6 +626,24 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new SlimeJsonResponse(slime);
}
+ private HttpResponse clusters(String tenantName, String applicationName, String instanceName, String environment, String region) {
+ ApplicationId id = ApplicationId.from(tenantName, applicationName, instanceName);
+ ZoneId zone = ZoneId.from(environment, region);
+ Application application = controller.serviceRegistry().configServer().nodeRepository().getApplication(zone, id);
+
+ Slime slime = new Slime();
+ Cursor clustersObject = slime.setObject().setObject("clusters");
+ for (Cluster cluster : application.clusters().values()) {
+ Cursor clusterObject = clustersObject.setObject(cluster.id().value());
+ toSlime(cluster.min(), clusterObject.setObject("min"));
+ toSlime(cluster.max(), clusterObject.setObject("max"));
+ toSlime(cluster.current(), clusterObject.setObject("current"));
+ cluster.target().ifPresent(target -> toSlime(target, clusterObject.setObject("target")));
+ cluster.suggested().ifPresent(suggested -> toSlime(suggested, clusterObject.setObject("suggested")));
+ }
+ return new SlimeJsonResponse(slime);
+ }
+
private static String valueOf(Node.State state) {
switch (state) {
case failed: return "failed";
@@ -716,7 +733,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new MessageResponse(type.jobName() + " for " + id + " resumed");
}
- private void toSlime(Cursor object, Application application, HttpRequest request) {
+ private void toSlime(Cursor object, com.yahoo.vespa.hosted.controller.Application application, HttpRequest request) {
object.setString("tenant", application.id().tenant().value());
object.setString("application", application.id().application().value());
object.setString("deployments", withPath("/application/v4" +
@@ -854,7 +871,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
private void toSlime(Cursor object, Instance instance, DeploymentStatus status, HttpRequest request) {
- Application application = status.application();
+ com.yahoo.vespa.hosted.controller.Application application = status.application();
object.setString("tenant", instance.id().tenant().value());
object.setString("application", instance.id().application().value());
object.setString("instance", instance.id().instance().value());
@@ -1349,7 +1366,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
Inspector requestObject = toSlime(request.getData()).get();
TenantAndApplicationId id = TenantAndApplicationId.from(tenantName, applicationName);
Credentials credentials = accessControlRequests.credentials(id.tenant(), requestObject, request.getJDiscRequest());
- Application application = controller.applications().createApplication(id, credentials);
+ com.yahoo.vespa.hosted.controller.Application application = controller.applications().createApplication(id, credentials);
Slime slime = new Slime();
toSlime(id, slime.setObject(), request);
return new SlimeJsonResponse(slime);
@@ -1513,7 +1530,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
Optional<ApplicationPackage> applicationPackage = Optional.ofNullable(dataParts.get("applicationZip"))
.map(ApplicationPackage::new);
- Optional<Application> application = controller.applications().getApplication(TenantAndApplicationId.from(applicationId));
+ Optional<com.yahoo.vespa.hosted.controller.Application> application = controller.applications().getApplication(TenantAndApplicationId.from(applicationId));
Inspector sourceRevision = deployOptions.field("sourceRevision");
Inspector buildNumber = deployOptions.field("buildNumber");
@@ -1656,7 +1673,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private void toSlime(Cursor object, Tenant tenant, HttpRequest request) {
object.setString("tenant", tenant.name().value());
object.setString("type", tenantType(tenant));
- List<Application> applications = controller.applications().asList(tenant.name());
+ List<com.yahoo.vespa.hosted.controller.Application> applications = controller.applications().asList(tenant.name());
switch (tenant.type()) {
case athenz:
AthenzTenant athenzTenant = (AthenzTenant) tenant;
@@ -1690,7 +1707,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
// TODO jonmv: This should list applications, not instances.
Cursor applicationArray = object.setArray("applications");
- for (Application application : applications) {
+ for (com.yahoo.vespa.hosted.controller.Application application : applications) {
DeploymentStatus status = controller.jobController().deploymentStatus(application);
for (Instance instance : showOnlyProductionInstances(request) ? application.productionInstances().values()
: application.instances().values())
@@ -1701,6 +1718,22 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
}
+ private void toSlime(ClusterResources resources, Cursor object) {
+ object.setLong("nodes", resources.nodes());
+ object.setLong("groups", resources.groups());
+ toSlime(resources.nodeResources(), object.setObject("nodeResources"));
+ object.setDouble("cost", resources.nodes() * resources.nodeResources().cost());
+ }
+
+ 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", valueOf(resources.diskSpeed()));
+ object.setString("storageType", valueOf(resources.storageType()));
+ }
+
// A tenant has different content when in a list ... antipattern, but not solvable before application/v5
private void tenantInTenantsListToSlime(Tenant tenant, URI requestURI, Cursor object) {
object.setString("tenant", tenant.name().value());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index 0fb2451ccba..fff63e1954e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
@@ -5,6 +5,7 @@ import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.Environment;
@@ -22,6 +23,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname;
import com.yahoo.vespa.hosted.controller.api.identifiers.Identifier;
import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId;
import com.yahoo.vespa.hosted.controller.api.integration.LogEntry;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Cluster;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
@@ -97,7 +99,17 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
}
/** Assigns a reserved tenant node to the given deployment, with initial versions. */
- public void provision(ZoneId zone, ApplicationId application) {
+ public void provision(ZoneId zone, ApplicationId application, ClusterSpec.Id clusterId) {
+ Cluster cluster = new Cluster(clusterId,
+ new ClusterResources(2, 1, new NodeResources(1, 4, 20, 1, slow, remote)),
+ new ClusterResources(2, 1, new NodeResources(4, 16, 90, 1, slow, remote)),
+ new ClusterResources(2, 1, new NodeResources(2, 8, 50, 1, slow, remote)),
+ Optional.of(new ClusterResources(2, 1, new NodeResources(3, 8, 50, 1, slow, remote))),
+ Optional.empty());
+ nodeRepository.putApplication(zone,
+ new com.yahoo.vespa.hosted.controller.api.integration.configserver.Application(application,
+ List.of(cluster)));
+
Node parent = nodeRepository().list(zone, SystemApplication.tenantHost.id()).stream().findAny()
.orElseThrow(() -> new IllegalStateException("No parent hosts in " + zone));
nodeRepository().putNodes(zone, new Node.Builder().hostname(hostFor(application, zone))
@@ -114,7 +126,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
.resources(new NodeResources(2, 8, 50, 1, slow, remote))
.serviceState(Node.ServiceState.unorchestrated)
.flavor("d-2-8-50")
- .clusterId("cluster")
+ .clusterId(clusterId.value())
.clusterType(Node.ClusterType.container)
.build());
}
@@ -326,10 +338,12 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
throw prepareException;
}
DeploymentId id = new DeploymentId(deployment.instance(), deployment.zone());
+
applications.put(id, new Application(id.applicationId(), lastPrepareVersion, new ApplicationPackage(deployment.applicationPackage())));
+ ClusterSpec.Id cluster = ClusterSpec.Id.from("default");
if (nodeRepository().list(id.zoneId(), id.applicationId()).isEmpty())
- provision(id.zoneId(), id.applicationId());
+ provision(id.zoneId(), id.applicationId(), cluster);
this.rotationNames.put(
id,
@@ -342,7 +356,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
if (!deferLoadBalancerProvisioning.contains(id.zoneId().environment())) {
putLoadBalancers(id.zoneId(), List.of(new LoadBalancer(UUID.randomUUID().toString(),
id.applicationId(),
- ClusterSpec.Id.from("default"),
+ cluster,
HostName.from("lb-0--" + id.applicationId().serializedForm() + "--" + id.zoneId().toString()),
LoadBalancer.State.active,
Optional.of("dns-zone-1"))));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
index 1e535e7e9de..76a30c289b8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java
@@ -8,6 +8,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Application;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.TargetVersions;
@@ -34,6 +35,7 @@ import java.util.stream.Collectors;
public class NodeRepositoryMock implements NodeRepository {
private final Map<ZoneId, Map<HostName, Node>> nodeRepository = new HashMap<>();
+ private final Map<ZoneId, Map<ApplicationId, Application>> applications = new HashMap<>();
private final Map<ZoneId, TargetVersions> targetVersions = new HashMap<>();
/** Add or update given nodes in zone */
@@ -43,6 +45,11 @@ public class NodeRepositoryMock implements NodeRepository {
Function.identity())));
}
+ public void putApplication(ZoneId zone, Application application) {
+ applications.putIfAbsent(zone, new HashMap<>());
+ applications.get(zone).put(application.id(), application);
+ }
+
/** Add or update given node in zone */
public void putNodes(ZoneId zone, Node node) {
putNodes(zone, Collections.singletonList(node));
@@ -162,6 +169,11 @@ public class NodeRepositoryMock implements NodeRepository {
}
@Override
+ public Application getApplication(ZoneId zone, ApplicationId applicationId) {
+ return applications.get(zone).get(applicationId);
+ }
+
+ @Override
public void upgrade(ZoneId zone, NodeType type, Version version) {
this.targetVersions.compute(zone, (ignored, targetVersions) -> {
if (targetVersions == null) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index a63597ad32b..7c703735dbf 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -494,9 +494,14 @@ public class ApplicationApiTest extends ControllerContainerTest {
// GET nodes
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/nodes", GET)
- .userIdentity(USER_ID),
+ .userIdentity(USER_ID),
new File("application-nodes.json"));
+ // GET clusters
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/clusters", GET)
+ .userIdentity(USER_ID),
+ new File("application-clusters.json"));
+
// GET logs
tester.assertResponse(request("/application/v4/tenant/tenant2/application/application1/environment/dev/region/us-central-1/instance/default/logs?from=1233&to=3214", GET)
.userIdentity(USER_ID),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json
new file mode 100644
index 00000000000..2752d69077b
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-clusters.json
@@ -0,0 +1,58 @@
+{
+ "clusters": {
+ "default": {
+ "min": {
+ "nodes": 2,
+ "groups": 1,
+ "nodeResources": {
+ "vcpu": 1.0,
+ "memoryGb": 4.0,
+ "diskGb": 20.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "slow",
+ "storageType": "remote"
+ },
+ "cost": 35.4
+ },
+ "max": {
+ "nodes": 2,
+ "groups": 1,
+ "nodeResources": {
+ "vcpu": 4.0,
+ "memoryGb": 16.0,
+ "diskGb": 90.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "slow",
+ "storageType": "remote"
+ },
+ "cost": 142.5
+ },
+ "current": {
+ "nodes": 2,
+ "groups": 1,
+ "nodeResources": {
+ "vcpu": 2.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "slow",
+ "storageType": "remote"
+ },
+ "cost": 71.7
+ },
+ "target": {
+ "nodes": 2,
+ "groups": 1,
+ "nodeResources": {
+ "vcpu": 3.0,
+ "memoryGb": 8.0,
+ "diskGb": 50.0,
+ "bandwidthGbps": 1.0,
+ "diskSpeed": "slow",
+ "storageType": "remote"
+ },
+ "cost": 95.7
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-nodes.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-nodes.json
index cee2fa7a6d2..0cfb457660c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-nodes.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-nodes.json
@@ -13,7 +13,7 @@
"diskSpeed": "slow",
"storageType": "remote",
"fastDisk": false,
- "clusterId": "cluster",
+ "clusterId": "default",
"clusterType": "container"
}
]
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java
index c6778af6efb..305b87f96e7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/AllocatableClusterResources.java
@@ -19,11 +19,6 @@ import java.util.Optional;
*/
public class AllocatableClusterResources {
- // We only depend on the ratios between these values
- private static final double cpuUnitCost = 12.0;
- private static final double memoryUnitCost = 1.2;
- private static final double diskUnitCost = 0.045;
-
/** The node count in the cluster */
private final int nodes;
@@ -107,7 +102,7 @@ public class AllocatableClusterResources {
public ClusterSpec.Type clusterType() { return clusterType; }
- public double cost() { return nodes * costOf(advertisedResources); }
+ public double cost() { return nodes * advertisedResources.cost(); }
/**
* Returns the fraction measuring how well the real resources fulfils the ideal: 1 means completely fulfilled,
@@ -116,12 +111,6 @@ public class AllocatableClusterResources {
*/
public double fulfilment() { return fulfilment; }
- private static double costOf(NodeResources resources) {
- return resources.vcpu() * cpuUnitCost +
- resources.memoryGb() * memoryUnitCost +
- resources.diskGb() * diskUnitCost;
- }
-
private static double fulfilment(NodeResources realResources, NodeResources idealResources) {
double vcpuFulfilment = Math.min(1, realResources.vcpu() / idealResources.vcpu());
double memoryGbFulfilment = Math.min(1, realResources.memoryGb() / idealResources.memoryGb());
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 75b1efb649e..8e342a985ca 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
@@ -62,7 +62,7 @@ import static com.yahoo.slime.SlimeUtils.optionalString;
/**
* The implementation of the /nodes/v2 API.
- * See RestApiTest for documentation.
+ * See NodesV2ApiTest for documentation.
*
* @author bratseth
*/
@@ -191,11 +191,6 @@ public class NodesV2ApiHandler extends LoggingRequestHandler {
throw new NotFoundException("Nothing at path '" + request.getUri().getPath() + "'");
}
- private HttpResponse runJob(String job) {
- nodeRepository.jobControl().run(job);
- return new MessageResponse("Executed job '" + job + "'");
- }
-
private HttpResponse handleDELETE(HttpRequest request) {
Path path = new Path(request.getUri());
if (path.matches("/nodes/v2/node/{hostname}")) return deleteNode(path.get("hostname"));
@@ -205,6 +200,11 @@ public class NodesV2ApiHandler extends LoggingRequestHandler {
throw new NotFoundException("Nothing at path '" + request.getUri().getPath() + "'");
}
+ private HttpResponse runJob(String job) {
+ nodeRepository.jobControl().run(job);
+ return new MessageResponse("Executed job '" + job + "'");
+ }
+
private HttpResponse deleteNode(String hostname) {
Optional<Node> node = nodeRepository.getNode(hostname);
if (node.isEmpty()) throw new NotFoundException("No node with hostname '" + hostname + "'");
diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/FeedClient.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/FeedClient.java
index 8cd22fe2e5c..8c2c1405fa0 100644
--- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/FeedClient.java
+++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/FeedClient.java
@@ -54,7 +54,7 @@ public interface FeedClient extends AutoCloseable {
/**
* Issues a document operation to the configured cluster(s).
* If the pipeline and buffers are full, this call will be blocking, ensuring that operations are not
- * produced faster than the can be handled. Transient failured are retried internally by this client.
+ * produced faster than the can be handled. Transient failures are retried internally by this client.
* Exactly one callback will always be received for each (completed) call to this.
*
* @param documentId the document id of the document
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/vespa/http/server/FeederSettings.java b/vespaclient-container-plugin/src/main/java/com/yahoo/vespa/http/server/FeederSettings.java
index 874509dd2f8..e6d8a88d10b 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/vespa/http/server/FeederSettings.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/vespa/http/server/FeederSettings.java
@@ -47,6 +47,7 @@ public class FeederSettings {
}
}
{
+ // TODO: Change default to JSON on Vespa 8
String tmpDataFormat = request.getHeader(Headers.DATA_FORMAT);
if (tmpDataFormat != null) {
dataFormat = DataFormat.valueOf(tmpDataFormat);