aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Marius Venstad <jonmv@users.noreply.github.com>2021-05-02 19:27:57 +0200
committerGitHub <noreply@github.com>2021-05-02 19:27:57 +0200
commitedd0efdce13c9e4e264062a5c8e8163975164dc5 (patch)
tree05eb70684174321cf6ad3fda1578d0fce6be55c2
parent2daa854096154742885d8ef2d7ca5d0745abb9d2 (diff)
parentb947b3750ce77bbb28addf1a86b7cfaf22d8243d (diff)
Merge pull request #17697 from vespa-engine/jonmv/reapply-rest-apis-without-trailing-slash-requirement
Jonmv/reapply rest apis without trailing slash requirement
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java113
-rw-r--r--configserver/src/main/resources/configserver-app/services.xml3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java108
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/RestApiException.java4
-rw-r--r--container-core/src/main/java/com/yahoo/restapi/RestApiImpl.java5
-rw-r--r--jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java4
-rw-r--r--node-repository/src/main/config/node-repository.xml1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java42
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java1
11 files changed, 116 insertions, 171 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
index 3961fe12357..bd4231972e7 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/HttpErrorResponse.java
@@ -110,7 +110,7 @@ public class HttpErrorResponse extends HttpResponse {
new JsonFormat(true).encode(stream, slime);
}
- //@Override
+ @Override
public String getContentType() {
return HttpConfigResponse.JSON_CONTENT_TYPE;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java
index 6aff3b8a361..bb94f8d442a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/TenantHandler.java
@@ -3,112 +3,77 @@ package com.yahoo.vespa.config.server.http.v2;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
-
import com.yahoo.config.provision.TenantName;
-import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.jdisc.application.BindingMatch;
+import com.yahoo.restapi.ErrorResponse;
+import com.yahoo.restapi.RestApi;
+import com.yahoo.restapi.RestApiException;
+import com.yahoo.restapi.RestApiRequestHandler;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.yolean.Exceptions;
-import com.yahoo.vespa.config.server.http.BadRequestException;
-import com.yahoo.vespa.config.server.http.HttpHandler;
-import com.yahoo.vespa.config.server.http.InternalServerException;
-import com.yahoo.vespa.config.server.http.Utils;
/**
* Handler to create, get and delete a tenant, and listing of tenants.
*
- * @author Vegard Havdal
+ * @author jonmv
*/
-public class TenantHandler extends HttpHandler {
+public class TenantHandler extends RestApiRequestHandler<TenantHandler> {
private static final String TENANT_NAME_REGEXP = "[\\w-]+";
private final TenantRepository tenantRepository;
private final ApplicationRepository applicationRepository;
- // instantiated by dependency injection
@Inject
public TenantHandler(Context ctx, ApplicationRepository applicationRepository) {
- super(ctx);
+ super(ctx, TenantHandler::defineApi);
this.tenantRepository = applicationRepository.tenantRepository();
this.applicationRepository = applicationRepository;
}
- @Override
- protected HttpResponse handlePUT(HttpRequest request) {
- TenantName tenantName = getAndValidateTenantFromRequest(request);
- try {
- tenantRepository.addTenant(tenantName);
- } catch (Exception e) {
- throw new InternalServerException(Exceptions.toMessageString(e));
- }
- return new TenantCreateResponse(tenantName);
- }
-
- @Override
- protected HttpResponse handleGET(HttpRequest request) {
- if (isGetTenantRequest(request)) {
- final TenantName tenantName = getTenantNameFromRequest(request);
- Utils.checkThatTenantExists(tenantRepository, tenantName);
- return new TenantGetResponse(tenantName);
- } else if (isListTenantsRequest(request)) {
- return new ListTenantsResponse(ImmutableSet.copyOf(tenantRepository.getAllTenantNames()));
- } else {
- throw new BadRequestException(request.getUri().toString());
- }
- }
-
- @Override
- protected HttpResponse handleDELETE(HttpRequest request) {
- final TenantName tenantName = getTenantNameFromRequest(request);
- Utils.checkThatTenantExists(tenantRepository, tenantName);
- applicationRepository.deleteTenant(tenantName);
- return new TenantDeleteResponse(tenantName);
+ private RestApi defineApi() {
+ return RestApi.builder()
+ .addRoute(RestApi.route("/application/v2/tenant")
+ .get(this::getTenants))
+ .addRoute(RestApi.route("/application/v2/tenant/{tenant}")
+ .get(this::getTenant)
+ .put(this::putTenant)
+ .delete(this::deleteTenant))
+ .addExceptionMapper(IllegalArgumentException.class, (c, e) -> ErrorResponse.badRequest(Exceptions.toMessageString(e)))
+ .addExceptionMapper(RuntimeException.class, (c, e) -> ErrorResponse.internalServerError(Exceptions.toMessageString(e)))
+ .build();
}
- /**
- * Gets the tenant name from the request, throws if it exists already and validates its name
- *
- * @param request an {@link com.yahoo.container.jdisc.HttpRequest}
- * @return tenant name
- */
- private TenantName getAndValidateTenantFromRequest(HttpRequest request) {
- final TenantName tenantName = getTenantNameFromRequest(request);
- checkThatTenantDoesNotExist(tenantName);
- validateTenantName(tenantName);
- return tenantName;
+ private HttpResponse getTenants(RestApi.RequestContext context) {
+ return new ListTenantsResponse(ImmutableSet.copyOf(tenantRepository.getAllTenantNames()));
}
- private void validateTenantName(TenantName tenant) {
- if (!tenant.value().matches(TENANT_NAME_REGEXP)) {
- throw new BadRequestException("Illegal tenant name: " + tenant);
- }
- }
+ private HttpResponse getTenant(RestApi.RequestContext context) {
+ TenantName name = TenantName.from(context.pathParameters().getStringOrThrow("tenant"));
+ if ( ! tenantRepository.checkThatTenantExists(name))
+ throw new RestApiException.NotFound("Tenant '" + name + "' was not found.");
- private void checkThatTenantDoesNotExist(TenantName tenantName) {
- if (tenantRepository.checkThatTenantExists(tenantName))
- throw new BadRequestException("There already exists a tenant '" + tenantName + "'");
+ return new TenantGetResponse(name);
}
- private static BindingMatch<?> getBindingMatch(HttpRequest request) {
- return HttpConfigRequests.getBindingMatch(request,
- "http://*/application/v2/tenant/",
- "http://*/application/v2/tenant/*");
- }
+ private HttpResponse putTenant(RestApi.RequestContext context) {
+ TenantName name = TenantName.from(context.pathParameters().getStringOrThrow("tenant"));
+ if (tenantRepository.checkThatTenantExists(name))
+ throw new RestApiException.BadRequest("There already exists a tenant '" + name + "'");
+ if ( ! name.value().matches(TENANT_NAME_REGEXP))
+ throw new RestApiException.BadRequest("Illegal tenant name: " + name);
- private static boolean isGetTenantRequest(HttpRequest request) {
- return getBindingMatch(request).groupCount() == 3;
+ tenantRepository.addTenant(name);
+ return new TenantCreateResponse(name);
}
- private static boolean isListTenantsRequest(HttpRequest request) {
- return getBindingMatch(request).groupCount() == 2 &&
- request.getUri().getPath().endsWith("/tenant/");
- }
+ private HttpResponse deleteTenant(RestApi.RequestContext context) {
+ TenantName name = TenantName.from(context.pathParameters().getStringOrThrow("tenant"));
+ if ( ! tenantRepository.checkThatTenantExists(name))
+ throw new RestApiException.NotFound("Tenant '" + name + "' was not found.");
- private static TenantName getTenantNameFromRequest(HttpRequest request) {
- BindingMatch<?> bm = getBindingMatch(request);
- return TenantName.from(bm.group(2));
+ applicationRepository.deleteTenant(name);
+ return new TenantDeleteResponse(name);
}
}
diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml
index fd8bda8f305..73aa5db98e4 100644
--- a/configserver/src/main/resources/configserver-app/services.xml
+++ b/configserver/src/main/resources/configserver-app/services.xml
@@ -102,8 +102,7 @@
<binding>http://*/status</binding>
</handler>
<handler id='com.yahoo.vespa.config.server.http.v2.TenantHandler' bundle='configserver'>
- <binding>http://*/application/v2/tenant/</binding>
- <binding>http://*/application/v2/tenant/*</binding>
+ <binding>http://*/application/v2/tenant*</binding>
</handler>
<handler id='com.yahoo.vespa.config.server.http.v2.SessionCreateHandler' bundle='configserver'>
<binding>http://*/application/v2/tenant/*/session</binding>
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java
index b0b01ea24b4..a6cf6d24c88 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/TenantHandlerTest.java
@@ -6,16 +6,13 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
-import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.HttpRequestBuilder;
import com.yahoo.jdisc.http.HttpRequest.Method;
+import com.yahoo.restapi.RestApiTestDriver;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.MockProvisioner;
import com.yahoo.vespa.config.server.application.OrchestratorMock;
-import com.yahoo.vespa.config.server.http.BadRequestException;
-import com.yahoo.vespa.config.server.http.NotFoundException;
import com.yahoo.vespa.config.server.session.PrepareParams;
-import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.config.server.tenant.TestTenantRepository;
import org.junit.After;
@@ -29,11 +26,11 @@ import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import static org.hamcrest.CoreMatchers.is;
+import static com.yahoo.jdisc.http.HttpRequest.Method.DELETE;
+import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
+import static com.yahoo.jdisc.http.HttpRequest.Method.PUT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
public class TenantHandlerTest {
@@ -44,6 +41,8 @@ public class TenantHandlerTest {
private TenantHandler handler;
private final TenantName a = TenantName.from("a");
+ private RestApiTestDriver testDriver;
+
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -63,7 +62,8 @@ public class TenantHandlerTest {
.withOrchestrator(new OrchestratorMock())
.withConfigserverConfig(configserverConfig)
.build();
- handler = new TenantHandler(TenantHandler.testOnlyContext(), applicationRepository);
+ handler = new TenantHandler(RestApiTestDriver.createHandlerTestContext(), applicationRepository);
+ testDriver = RestApiTestDriver.newBuilder(handler).build();
}
@After
@@ -74,91 +74,81 @@ public class TenantHandlerTest {
@Test
public void testTenantCreate() throws Exception {
assertNull(tenantRepository.getTenant(a));
- TenantCreateResponse response = putSync(
- HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/a", Method.PUT));
- assertResponseEquals(response, "{\"message\":\"Tenant a created.\"}");
+ assertResponse(PUT, "/application/v2/tenant/a",
+ "{\"message\":\"Tenant a created.\"}");
+ assertEquals(a, tenantRepository.getTenant(a).getName());
}
@Test
public void testTenantCreateWithAllPossibleCharactersInName() throws Exception {
TenantName tenantName = TenantName.from("aB-9999_foo");
assertNull(tenantRepository.getTenant(tenantName));
- TenantCreateResponse response = putSync(
- HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/" + tenantName, Method.PUT));
- assertResponseEquals(response, "{\"message\":\"Tenant " + tenantName + " created.\"}");
+ assertResponse(PUT, "/application/v2/tenant/aB-9999_foo",
+ "{\"message\":\"Tenant " + tenantName + " created.\"}");
}
- @Test(expected=NotFoundException.class)
- public void testGetNonExisting() {
- handler.handleGET(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/x", Method.GET));
+ @Test
+ public void testGetNonExisting() throws IOException {
+ assertResponse(GET, "/application/v2/tenant/x",
+ "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'x' was not found.\"}");
}
-
+
@Test
public void testGetAndList() throws Exception {
tenantRepository.addTenant(a);
- assertResponseEquals((TenantGetResponse) handler.handleGET(
- HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/a", Method.GET)),
- "{\"message\":\"Tenant 'a' exists.\"}");
- assertResponseEquals((ListTenantsResponse) handler.handleGET(
- HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/", Method.GET)),
- "{\"tenants\":[\"default\",\"a\"]}");
+ assertResponse(GET, "/application/v2/tenant/a",
+ "{\"message\":\"Tenant 'a' exists.\"}");
+ assertResponse(GET, "/application/v2/tenant/",
+ "{\"tenants\":[\"default\",\"a\"]}");
+ assertResponse(GET, "/application/v2/tenant",
+ "{\"tenants\":[\"default\",\"a\"]}");
}
- @Test(expected=BadRequestException.class)
+ @Test
public void testCreateExisting() throws Exception {
assertNull(tenantRepository.getTenant(a));
- TenantCreateResponse response = putSync(HttpRequest.createTestRequest(
- "http://deploy.example.yahoo.com:80/application/v2/tenant/a", Method.PUT));
- assertResponseEquals(response, "{\"message\":\"Tenant a created.\"}");
+ assertResponse(PUT, "/application/v2/tenant/a",
+ "{\"message\":\"Tenant a created.\"}");
assertEquals(tenantRepository.getTenant(a).getName(), a);
- handler.handlePUT(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/a", Method.PUT));
+ assertResponse(PUT, "/application/v2/tenant/a",
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"There already exists a tenant 'a'\"}");
}
@Test
public void testDelete() throws IOException {
- putSync(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/a", Method.PUT));
- assertEquals(tenantRepository.getTenant(a).getName(), a);
- TenantDeleteResponse delResp = (TenantDeleteResponse) handler.handleDELETE(
- HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/a", Method.DELETE));
- assertResponseEquals(delResp, "{\"message\":\"Tenant a deleted.\"}");
+ tenantRepository.addTenant(a);
+ assertResponse(DELETE, "/application/v2/tenant/a",
+ "{\"message\":\"Tenant a deleted.\"}");
assertNull(tenantRepository.getTenant(a));
}
@Test
- public void testDeleteTenantWithActiveApplications() {
- putSync(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/" + a, Method.PUT));
- Tenant tenant = tenantRepository.getTenant(a);
- assertEquals(a, tenant.getName());
-
+ public void testDeleteTenantWithActiveApplications() throws IOException {
+ tenantRepository.addTenant(a);
ApplicationId applicationId = ApplicationId.from(a, ApplicationName.from("foo"), InstanceName.defaultName());
applicationRepository.deploy(testApp, new PrepareParams.Builder().applicationId(applicationId).build());
- try {
- handler.handleDELETE(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/" + a, Method.DELETE));
- fail();
- } catch (IllegalArgumentException e) {
- assertThat(e.getMessage(), is("Cannot delete tenant 'a', it has active applications: [a.foo]"));
- }
+ assertResponse(DELETE, "/application/v2/tenant/a",
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Cannot delete tenant 'a', it has active applications: [a.foo]\"}");
}
- @Test(expected=NotFoundException.class)
- public void testDeleteNonExisting() {
- handler.handleDELETE(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/x", Method.DELETE));
- }
-
- @Test(expected=BadRequestException.class)
- public void testIllegalNameSlashes() {
- putSync(HttpRequest.createTestRequest("http://deploy.example.yahoo.com:80/application/v2/tenant/a/b", Method.PUT));
+ @Test
+ public void testDeleteNonExisting() throws IOException {
+ assertResponse(DELETE, "/application/v2/tenant/a",
+ "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'a' was not found.\"}");
}
- private TenantCreateResponse putSync(HttpRequest testRequest) {
- return (TenantCreateResponse) handler.handlePUT(testRequest);
+ @Test
+ public void testIllegalNameSlashes() throws IOException {
+ assertResponse(PUT, "/application/v2/tenant/a/b",
+ "{\"error-code\":\"NOT_FOUND\",\"message\":\"Nothing at '/application/v2/tenant/a/b'\"}");
}
- private void assertResponseEquals(HttpResponse response, String payload) throws IOException {
+ private void assertResponse(Method method, String path, String payload) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- response.render(baos);
- assertEquals(baos.toString(StandardCharsets.UTF_8), payload);
+ testDriver.executeRequest(HttpRequestBuilder.create(method, path).build())
+ .render(baos);
+ assertEquals(payload, baos.toString(StandardCharsets.UTF_8));
}
}
diff --git a/container-core/src/main/java/com/yahoo/restapi/RestApiException.java b/container-core/src/main/java/com/yahoo/restapi/RestApiException.java
index d9da320499f..68e46a3a9b8 100644
--- a/container-core/src/main/java/com/yahoo/restapi/RestApiException.java
+++ b/container-core/src/main/java/com/yahoo/restapi/RestApiException.java
@@ -42,6 +42,7 @@ public class RestApiException extends RuntimeException {
public static class NotFound extends RestApiException {
public NotFound() { this(null, null); }
+ public NotFound(HttpRequest request) { this("Nothing at '" + request.getUri().getRawPath() + "'", null); }
public NotFound(Throwable cause) { this(cause.getMessage(), cause); }
public NotFound(String message) { this(message, null); }
public NotFound(String message, Throwable cause) { super(ErrorResponse::notFoundError, message, cause); }
@@ -50,7 +51,8 @@ public class RestApiException extends RuntimeException {
public static class MethodNotAllowed extends RestApiException {
public MethodNotAllowed() { super(ErrorResponse::methodNotAllowed, "Method not allowed", null); }
public MethodNotAllowed(HttpRequest request) {
- super(ErrorResponse::methodNotAllowed, "Method '" + request.getMethod().name() + "' is not allowed", null);
+ super(ErrorResponse::methodNotAllowed, "Method '" + request.getMethod().name() + "' is not allowed at '" +
+ request.getUri().getRawPath() + "'", null);
}
}
diff --git a/container-core/src/main/java/com/yahoo/restapi/RestApiImpl.java b/container-core/src/main/java/com/yahoo/restapi/RestApiImpl.java
index 8ba94f9aca9..d63add5ed1d 100644
--- a/container-core/src/main/java/com/yahoo/restapi/RestApiImpl.java
+++ b/container-core/src/main/java/com/yahoo/restapi/RestApiImpl.java
@@ -14,6 +14,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
@@ -144,7 +145,7 @@ class RestApiImpl implements RestApi {
private static Route createDefaultRoute() {
RouteBuilder routeBuilder = new RouteBuilderImpl("{*}")
.defaultHandler(context -> {
- throw new RestApiException.NotFound();
+ throw new RestApiException.NotFound(context.request());
});
return ((RouteBuilderImpl)routeBuilder).build();
}
@@ -155,6 +156,8 @@ class RestApiImpl implements RestApi {
if (!disableDefaultMappers){
exceptionMappers.add(new ExceptionMapperHolder<>(RestApiException.class, (context, exception) -> exception.response()));
}
+ // Topologically sort children before superclasses, so most the specific match is found by iterating through mappers in order.
+ exceptionMappers.sort((a, b) -> (a.type.isAssignableFrom(b.type) ? 1 : 0) + (b.type.isAssignableFrom(a.type) ? -1 : 0));
return exceptionMappers;
}
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java
index 028d0d69df6..64bcaf15a69 100644
--- a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java
+++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java
@@ -249,7 +249,7 @@ public class BindingSetTestCase {
handlers.put(new UriPattern("http://*/config/v1/*/"), foo1);
handlers.put(new UriPattern("http://*/config/v1/*/*"), foo2);
handlers.put(new UriPattern("http://*/config/v1/*/*/"), foo3);
- handlers.put(new UriPattern("http://*/application/v2/tenant/"), foo4);
+ handlers.put(new UriPattern("http://*/application/v2/tenant*"), foo4);
handlers.put(new UriPattern("http://*/application/v2/tenant/*"), foo5);
handlers.put(new UriPattern("http://*/application/v2/tenant/*/session"), foo6);
handlers.put(new UriPattern("http://*/application/v2/tenant/*/session/*/prepared"), foo7);
@@ -276,7 +276,7 @@ public class BindingSetTestCase {
assertSame(foo3, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" +
"/config/v1/cloud.config.log.logd/admin/")));
assertSame(foo4, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" +
- "/application/v2/tenant/")));
+ "/application/v2/tenant")));
assertSame(foo5, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" +
"/application/v2/tenant/b")));
assertSame(foo6, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" +
diff --git a/node-repository/src/main/config/node-repository.xml b/node-repository/src/main/config/node-repository.xml
index a12e2a8b11c..8a6e466fdf3 100644
--- a/node-repository/src/main/config/node-repository.xml
+++ b/node-repository/src/main/config/node-repository.xml
@@ -14,6 +14,7 @@
</handler>
<handler id="com.yahoo.vespa.hosted.provision.restapi.LoadBalancersV1ApiHandler" bundle="node-repository">
+ <binding>http://*/loadbalancers/v1</binding>
<binding>http://*/loadbalancers/v1/*</binding>
</handler>
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java
index f81e3240397..e73f97304c1 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiHandler.java
@@ -1,53 +1,37 @@
// 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;
-import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.LoggingRequestHandler;
-import com.yahoo.restapi.ErrorResponse;
-import com.yahoo.vespa.hosted.provision.NoSuchNodeException;
+import com.yahoo.restapi.RestApi;
+import com.yahoo.restapi.RestApiRequestHandler;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.yolean.Exceptions;
import javax.inject.Inject;
-import java.util.logging.Level;
/**
* @author mpolden
+ * @author jonmv
*/
-public class LoadBalancersV1ApiHandler extends LoggingRequestHandler {
+public class LoadBalancersV1ApiHandler extends RestApiRequestHandler<LoadBalancersV1ApiHandler> {
private final NodeRepository nodeRepository;
@Inject
public LoadBalancersV1ApiHandler(LoggingRequestHandler.Context parentCtx, NodeRepository nodeRepository) {
- super(parentCtx);
+ super(parentCtx, LoadBalancersV1ApiHandler::createRestApiDefinition);
this.nodeRepository = nodeRepository;
}
- @Override
- public HttpResponse handle(HttpRequest request) {
- try {
- switch (request.getMethod()) {
- case GET: return handleGET(request);
- default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
- }
- }
- catch (NotFoundException | NoSuchNodeException e) {
- return ErrorResponse.notFoundError(Exceptions.toMessageString(e));
- }
- catch (IllegalArgumentException e) {
- return ErrorResponse.badRequest(Exceptions.toMessageString(e));
- }
- catch (RuntimeException e) {
- log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e);
- return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
- }
+ private static RestApi createRestApiDefinition(LoadBalancersV1ApiHandler self) {
+ return RestApi.builder()
+ .addRoute(RestApi.route("/loadbalancers/v1")
+ .get(self::getLoadBalancers))
+ .build();
}
- private HttpResponse handleGET(HttpRequest request) {
- String path = request.getUri().getPath();
- if (path.equals("/loadbalancers/v1/")) return new LoadBalancersResponse(request, nodeRepository);
- throw new NotFoundException("Nothing at path '" + path + "'");
+ private HttpResponse getLoadBalancers(RestApi.RequestContext context) {
+ return new LoadBalancersResponse(context.request(), nodeRepository);
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
index 5e40c0bd9ff..ebaf4d47887 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java
@@ -33,10 +33,10 @@ public class ContainerConfig {
" <component id='com.yahoo.vespa.flags.InMemoryFlagSource'/>\n" +
" <component id='com.yahoo.config.provision.Zone'/>\n" +
" <handler id='com.yahoo.vespa.hosted.provision.restapi.NodesV2ApiHandler'>\n" +
- " <binding>http://*/nodes/v2/*</binding>\n" +
+ " <binding>http://*/nodes/v2*</binding>\n" +
" </handler>\n" +
" <handler id='com.yahoo.vespa.hosted.provision.restapi.LoadBalancersV1ApiHandler'>\n" +
- " <binding>http://*/loadbalancers/v1/*</binding>\n" +
+ " <binding>http://*/loadbalancers/v1*</binding>\n" +
" </handler>\n" +
" <http>\n" +
" <server id='myServer' port='" + port + "'/>\n" +
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java
index e9811985b7d..1ca552bca94 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersV1ApiTest.java
@@ -22,6 +22,7 @@ public class LoadBalancersV1ApiTest {
@Test
public void test_load_balancers() throws Exception {
+ tester.assertFile(new Request("http://localhost:8080/loadbalancers/v1"), "load-balancers.json");
tester.assertFile(new Request("http://localhost:8080/loadbalancers/v1/"), "load-balancers.json");
tester.assertFile(new Request("http://localhost:8080/loadbalancers/v1/?application=tenant4.application4.instance4"), "load-balancers-single.json");
tester.assertResponse(new Request("http://localhost:8080/loadbalancers/v1/?application=tenant.nonexistent.default"), "{\"loadBalancers\":[]}");