aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/main
diff options
context:
space:
mode:
authorJon Marius Venstad <jonmv@gmail.com>2022-04-06 19:35:30 +0200
committerJon Marius Venstad <jonmv@gmail.com>2022-04-06 19:35:30 +0200
commit039589faf5f989d80b9fec2b28ed955ac6fd86f6 (patch)
tree45c314cc9ede2d5c26a5d6b4f030ad3db2246a91 /controller-server/src/main
parentec92b5f8882e400f94b851dffcf0b3511373e890 (diff)
Use HttpURL.Path for Path.getRest()
Diffstat (limited to 'controller-server/src/main')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequest.java40
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponse.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java45
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java26
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java3
6 files changed, 69 insertions, 83 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequest.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequest.java
index 2a1b8a19475..7e1c9c8884f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequest.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyRequest.java
@@ -3,6 +3,9 @@ package com.yahoo.vespa.hosted.controller.proxy;
import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.restapi.HttpURL;
+import com.yahoo.restapi.HttpURL.Path;
+import com.yahoo.restapi.HttpURL.Query;
import com.yahoo.text.Text;
import java.io.InputStream;
import java.net.URI;
@@ -22,28 +25,27 @@ import static com.yahoo.jdisc.http.HttpRequest.Method;
public class ProxyRequest {
private final Method method;
- private final URI requestUri;
+ private final HttpURL requestUri;
private final Map<String, List<String>> headers;
private final InputStream requestData;
private final List<URI> targets;
- private final String targetPath;
+ private final Path targetPath;
- ProxyRequest(Method method, URI url, Map<String, List<String>> headers, InputStream body, List<URI> targets,
- String path) {
- Objects.requireNonNull(url);
- if (!url.getPath().endsWith(path)) {
- throw new IllegalArgumentException(Text.format("Request path '%s' does not end with proxy path '%s'", url.getPath(), path));
+ ProxyRequest(Method method, URI uri, Map<String, List<String>> headers, InputStream body, List<URI> targets, Path path) {
+ this.requestUri = HttpURL.from(uri);
+ if ( requestUri.path().segments().size() < path.segments().size()
+ || ! requestUri.path().tail(path.segments().size()).equals(path)) {
+ throw new IllegalArgumentException(Text.format("Request %s does not end with proxy %s", requestUri.path(), path));
}
if (targets.isEmpty()) {
throw new IllegalArgumentException("targets must be non-empty");
}
this.method = Objects.requireNonNull(method);
- this.requestUri = Objects.requireNonNull(url);
this.headers = Objects.requireNonNull(headers);
this.requestData = body;
this.targets = List.copyOf(targets);
- this.targetPath = path.startsWith("/") ? path : "/" + path;
+ this.targetPath = path;
}
@@ -64,24 +66,12 @@ public class ProxyRequest {
}
public URI createConfigServerRequestUri(URI baseURI) {
- try {
- return new URI(baseURI.getScheme(), baseURI.getUserInfo(), baseURI.getHost(),
- baseURI.getPort(), targetPath, requestUri.getQuery(), requestUri.getFragment());
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
+ return HttpURL.from(baseURI).withPath(targetPath).withQuery(requestUri.query()).asURI();
}
public URI getControllerPrefixUri() {
- String prefixPath = targetPath.equals("/") && !requestUri.getPath().endsWith("/") ?
- requestUri.getPath() + targetPath :
- requestUri.getPath().substring(0, requestUri.getPath().length() - targetPath.length() + 1);
- try {
- return new URI(requestUri.getScheme(), requestUri.getUserInfo(), requestUri.getHost(),
- requestUri.getPort(), prefixPath, null, null);
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
+ Path prefixPath = requestUri.path().cut(targetPath.segments().size()).withTrailingSlash();
+ return requestUri.withPath(prefixPath).withQuery(Query.empty()).asURI();
}
@Override
@@ -90,7 +80,7 @@ public class ProxyRequest {
}
/** Create a proxy request that repeatedly tries a single target */
- public static ProxyRequest tryOne(URI target, String path, HttpRequest request) {
+ public static ProxyRequest tryOne(URI target, Path path, HttpRequest request) {
return new ProxyRequest(request.getMethod(), request.getUri(), request.getJDiscRequest().headers(),
request.getData(), List.of(target), path);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponse.java
index 0b629a577d0..9ac30898f8b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/proxy/ProxyResponse.java
@@ -2,6 +2,8 @@
package com.yahoo.vespa.hosted.controller.proxy;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.restapi.HttpURL;
+import com.yahoo.restapi.HttpURL.Path;
import org.apache.http.client.utils.URIBuilder;
import java.io.IOException;
@@ -29,19 +31,8 @@ public class ProxyResponse extends HttpResponse {
super(statusResponse);
this.contentType = contentType;
- final String configServerPrefix;
- final String controllerRequestPrefix;
- try {
- configServerPrefix = new URIBuilder()
- .setScheme(configServer.getScheme())
- .setHost(configServer.getHost())
- .setPort(configServer.getPort())
- .setPath("/")
- .build().toString();
- controllerRequestPrefix = controllerRequest.getControllerPrefixUri().toString();
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
+ String configServerPrefix = HttpURL.from(configServer).withPath(Path.empty()).asURI().toString();
+ String controllerRequestPrefix = controllerRequest.getControllerPrefixUri().toString();
bodyResponseRewritten = bodyResponse.replace(configServerPrefix, controllerRequestPrefix);
}
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 d9a38a5b578..c2c4049bcc3 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.restapi.application;
import ai.vespa.hosted.api.Signatures;
+import ai.vespa.validation.Validation;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
@@ -25,8 +26,10 @@ import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
import com.yahoo.io.IOUtils;
+import com.yahoo.net.DomainName;
import com.yahoo.restapi.ByteArrayResponse;
import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.HttpURL;
import com.yahoo.restapi.MessageResponse;
import com.yahoo.restapi.Path;
import com.yahoo.restapi.ResourceResponse;
@@ -155,6 +158,7 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static ai.vespa.validation.Validation.requireAtLeast;
import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
import static com.yahoo.jdisc.Response.Status.CONFLICT;
import static com.yahoo.yolean.Exceptions.uncheck;
@@ -270,7 +274,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/reindexing")) return getReindexing(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}/suspended")) return suspended(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")) 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}/service/{service}/state/v1/{*}")) 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}/service/{service}/{host}/status/{*}")) return status(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.get("service"), path.get("host"), 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}/content/{*}")) return content(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.getRest(), request);
@@ -284,7 +289,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}")) return deployment(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}/suspended")) return suspended(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")) 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}/service/{service}/state/v1/{*}")) 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}/service/{service}/{host}/status/{*}")) return status(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), path.get("service"), path.get("host"), 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());
@@ -1857,40 +1863,29 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
return response;
}
- private HttpResponse service(String tenantName, String applicationName, String instanceName, String environment, String region, String serviceName, String restPath, HttpRequest request) {
+ private HttpResponse status(String tenantName, String applicationName, String instanceName, String environment, String region, String serviceName, String host, HttpURL.Path restPath, HttpRequest request) {
DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region));
+ String result = controller.serviceRegistry().configServer().getServiceStatusPage(deploymentId,
+ serviceName,
+ DomainName.of(host),
+ restPath); // TODO: add query
+ return new HtmlResponse(result);
+ }
- if (restPath.contains("/status/")) {
- String[] parts = restPath.split("/status/", 2);
- String result = controller.serviceRegistry().configServer().getServiceStatusPage(deploymentId, serviceName, parts[0], parts[1]);
- return new HtmlResponse(result);
- }
-
- String normalizedRestPath = URI.create(restPath).normalize().toString();
- // Only state/v1 is allowed
- if (! normalizedRestPath.startsWith("state/v1/")) {
- return ErrorResponse.forbidden("Access denied");
- }
-
+ private HttpResponse service(String tenantName, String applicationName, String instanceName, String environment, String region, String serviceName, HttpURL.Path restPath, HttpRequest request) {
+ DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region));
Map<?,?> result = controller.serviceRegistry().configServer().getServiceApiResponse(deploymentId, serviceName, restPath);
ServiceApiResponse response = new ServiceApiResponse(deploymentId.zoneId(),
deploymentId.applicationId(),
List.of(controller.zoneRegistry().getConfigServerVipUri(deploymentId.zoneId())),
request.getUri());
- response.setResponse(result, serviceName, restPath);
+ response.setResponse(result, serviceName, HttpURL.Path.parse("/state/v1").append(restPath));
return response;
}
- private HttpResponse content(String tenantName, String applicationName, String instanceName, String environment, String region, String restPath, HttpRequest request) {
+ private HttpResponse content(String tenantName, String applicationName, String instanceName, String environment, String region, HttpURL.Path restPath, HttpRequest request) {
DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region));
-
- String normalizedRestPath = URI.create("content/" + restPath).normalize().toString();
- // Only content/ is allowed
- if ( ! normalizedRestPath.startsWith("content/")) {
- return ErrorResponse.forbidden("Access denied");
- }
-
- return controller.serviceRegistry().configServer().getApplicationPackageContent(deploymentId, "/" + restPath, request.getUri());
+ return controller.serviceRegistry().configServer().getApplicationPackageContent(deploymentId, restPath, request.getUri());
}
private HttpResponse updateTenant(String tenantName, HttpRequest request) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java
index 888c402b6ec..a21f93bdaea 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ServiceApiResponse.java
@@ -4,6 +4,9 @@ package com.yahoo.vespa.hosted.controller.restapi.application;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.restapi.HttpURL;
+import com.yahoo.restapi.HttpURL.Path;
+import com.yahoo.restapi.HttpURL.Query;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
@@ -34,11 +37,11 @@ class ServiceApiResponse extends HttpResponse {
private final ApplicationId application;
private final List<URI> configServerURIs;
private final Slime slime;
- private final UriBuilder requestUri;
+ private final HttpURL requestUri;
// Only set for one of the setResponse calls
private String serviceName = null;
- private String restPath = null;
+ private Path restPath = null;
public ServiceApiResponse(ZoneId zone, ApplicationId application, List<URI> configServerURIs, URI requestUri) {
super(200);
@@ -46,7 +49,7 @@ class ServiceApiResponse extends HttpResponse {
this.application = application;
this.configServerURIs = configServerURIs;
this.slime = new Slime();
- this.requestUri = new UriBuilder(requestUri).withoutParameters();
+ this.requestUri = HttpURL.from(requestUri).withQuery(Query.empty());
}
public void setResponse(ApplicationView applicationView) {
@@ -68,7 +71,7 @@ class ServiceApiResponse extends HttpResponse {
}
}
- public void setResponse(Map<?,?> responseData, String serviceName, String restPath) {
+ public void setResponse(Map<?,?> responseData, String serviceName, Path restPath) {
this.serviceName = serviceName;
this.restPath = restPath;
mapToSlime(responseData, slime.setObject());
@@ -138,7 +141,7 @@ class ServiceApiResponse extends HttpResponse {
mapToSlime((Map)entry, array.addObject());
}
- private String rewriteIfUrl(String urlOrAnyString, UriBuilder requestUri) {
+ private String rewriteIfUrl(String urlOrAnyString, HttpURL requestUri) {
if (urlOrAnyString == null) return null;
String hostPattern = "(" +
@@ -163,19 +166,18 @@ class ServiceApiResponse extends HttpResponse {
if (matcher.find()) {
String proxiedPath = urlOrAnyString.substring(matcher.group().length());
- return requestUri.append(proxiedPath).toString();
+ return requestUri.withPath(requestUri.path().append(Path.parse(proxiedPath))).asURI().toString();
} else {
return urlOrAnyString; // not a service url
}
}
- private UriBuilder generateLocalLinkPrefix(String identifier, String restPath) {
- String proxiedPath = identifier + "/" + restPath;
-
- if (this.requestUri.toString().endsWith(proxiedPath)) {
- return new UriBuilder(this.requestUri.toString().substring(0, this.requestUri.toString().length() - proxiedPath.length()));
+ private HttpURL generateLocalLinkPrefix(String identifier, Path restPath) {
+ Path proxiedPath = Path.parse(identifier).append(restPath);
+ if (requestUri.path().tail(proxiedPath.segments().size()).equals(proxiedPath)) {
+ return requestUri.withPath(requestUri.path().cut(proxiedPath.segments().size()));
} else {
- throw new IllegalStateException("Expected the resource path '" + this.requestUri + "' to end with '" + proxiedPath + "'");
+ throw new IllegalStateException("Expected the resource " + requestUri.path() + " to end with " + proxiedPath);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java
index 0e09825ec41..27a8cbeaf3e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java
@@ -6,6 +6,7 @@ import com.yahoo.config.provision.zone.ZoneList;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.HttpURL;
import com.yahoo.restapi.Path;
import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.Cursor;
@@ -21,8 +22,11 @@ import com.yahoo.yolean.Exceptions;
import java.net.URI;
import java.util.List;
import java.util.logging.Level;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static com.yahoo.restapi.HttpURL.Path.parse;
+
/**
* REST API for proxying operator APIs to config servers in a given zone.
*
@@ -32,7 +36,9 @@ import java.util.stream.Stream;
public class ConfigServerApiHandler extends AuditLoggingRequestHandler {
private static final URI CONTROLLER_URI = URI.create("https://localhost:4443/");
- private static final List<String> WHITELISTED_APIS = List.of("/flags/v1/", "/nodes/v2/", "/orchestrator/v1/");
+ private static final List<HttpURL.Path> WHITELISTED_APIS = List.of(parse("/flags/v1/"),
+ parse("/nodes/v2/"),
+ parse("/orchestrator/v1/"));
private final ZoneRegistry zoneRegistry;
private final ConfigServerRestExecutor proxy;
@@ -84,17 +90,18 @@ public class ConfigServerApiHandler extends AuditLoggingRequestHandler {
}
ZoneId zoneId = ZoneId.from(path.get("environment"), path.get("region"));
- if (! zoneRegistry.hasZone(zoneId) && ! controllerZone.equals(zoneId)) {
+ if ( ! zoneRegistry.hasZone(zoneId) && ! controllerZone.equals(zoneId)) {
throw new IllegalArgumentException("No such zone: " + zoneId.value());
}
- String cfgPath = "/" + path.getRest();
- if (WHITELISTED_APIS.stream().noneMatch(cfgPath::startsWith)) {
- return ErrorResponse.forbidden("Cannot access '" + cfgPath +
- "' through /configserver/v1, following APIs are permitted: " + String.join(", ", WHITELISTED_APIS));
+ if (path.getRest().segments().size() < 2 || ! WHITELISTED_APIS.contains(path.getRest().head(2).withTrailingSlash())) {
+ return ErrorResponse.forbidden("Cannot access " + path.getRest() +
+ " through /configserver/v1, following APIs are permitted: " + WHITELISTED_APIS.stream()
+ .map(p -> "/" + String.join("/", p.segments()) + "/")
+ .collect(Collectors.joining(", ")));
}
- return proxy.handle(ProxyRequest.tryOne(getEndpoint(zoneId), cfgPath, request));
+ return proxy.handle(ProxyRequest.tryOne(getEndpoint(zoneId), path.getRest(), request));
}
private HttpResponse root(HttpRequest request) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
index a7416cfa0ed..40a402f209b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiHandler.java
@@ -7,6 +7,7 @@ import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.HttpURL;
import com.yahoo.restapi.Path;
import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.Cursor;
@@ -107,7 +108,7 @@ public class ZoneApiHandler extends AuditLoggingRequestHandler {
return ErrorResponse.notFoundError("Nothing at " + path);
}
- private ProxyRequest proxyRequest(ZoneId zoneId, String path, HttpRequest request) {
+ private ProxyRequest proxyRequest(ZoneId zoneId, HttpURL.Path path, HttpRequest request) {
return ProxyRequest.tryOne(zoneRegistry.getConfigServerVipUri(zoneId), path, request);
}