From 8c55343f66e0aac23466a1696b6de653143870a1 Mon Sep 17 00:00:00 2001 From: Valerij Fredriksen Date: Tue, 6 Jul 2021 18:38:13 +0200 Subject: Expose status page for more distributor and storagenode under /application/v2 --- .../vespa/config/server/ApplicationRepository.java | 20 ++++++++++++--- .../config/server/http/v2/ApplicationHandler.java | 8 +++--- .../server/http/v2/ApplicationHandlerTest.java | 29 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) (limited to 'configserver') diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index 6249e3bfff8..56fd6a64305 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -547,15 +547,27 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye } } - public HttpResponse clusterControllerStatusPage(ApplicationId applicationId, String hostName, String pathSuffix) { + public HttpResponse serviceStatusPage(ApplicationId applicationId, String hostName, String serviceName, String pathSuffix) { // WARNING: pathSuffix may be given by the external user. Make sure no security issues arise... // We should be OK here, because at most, pathSuffix may change the parent path, but cannot otherwise // change the hostname and port. Exposing other paths on the cluster controller should be fine. // TODO: It would be nice to have a simple check to verify pathSuffix doesn't contain /../ components. - String relativePath = "clustercontroller-status/" + pathSuffix; + String pathPrefix; + switch (serviceName) { + case "container-clustercontroller": { + pathPrefix = "clustercontroller-status/v1/"; + break; + } + case "distributor": + case "storagenode": { + pathPrefix = ""; + break; + } + default: + throw new NotFoundException("No status page for service: " + serviceName); + } - return httpProxy.get(getApplication(applicationId), hostName, - CLUSTERCONTROLLER_CONTAINER.serviceName, relativePath); + return httpProxy.get(getApplication(applicationId), hostName, serviceName, pathPrefix + pathSuffix); } public Map getClusterReindexingStatus(ApplicationId applicationId) { diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java index e56fb0b1bcc..7691748a4cf 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java @@ -5,6 +5,7 @@ import com.google.inject.Inject; import com.yahoo.component.Version; import com.yahoo.config.application.api.ApplicationFile; import com.yahoo.config.model.api.Model; +import com.yahoo.config.model.api.container.ContainerServiceType; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.InstanceName; @@ -68,13 +69,14 @@ public class ApplicationHandler extends HttpHandler { if (path.matches("/application/v2/tenant/{tenant}/application/{application}")) return getApplicationResponse(ApplicationId.from(path.get("tenant"), path.get("application"), InstanceName.defaultName().value())); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}")) return getApplicationResponse(applicationId(path)); - if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/clustercontroller/{hostname}/status/{*}")) return clusterControllerStatusPage(applicationId(path), path.get("hostname"), path.getRest()); + if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/clustercontroller/{hostname}/status/v1/{*}")) return serviceStatusPage(applicationId(path), ContainerServiceType.CLUSTERCONTROLLER_CONTAINER.serviceName, path.get("hostname"), path.getRest()); // TODO (freva): Remove August 2021 if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/content/{*}")) return content(applicationId(path), path.getRest(), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/filedistributionstatus")) return filedistributionStatus(applicationId(path), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/logs")) return logs(applicationId(path), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/metrics/deployment")) return deploymentMetrics(applicationId(path)); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/metrics/proton")) return protonMetrics(applicationId(path)); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/reindexing")) return getReindexingStatus(applicationId(path)); + if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/service/{service}/{hostname}/status/{*}")) return serviceStatusPage(applicationId(path), path.get("service"), path.get("hostname"), path.getRest()); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/serviceconverge")) return listServiceConverge(applicationId(path), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/serviceconverge/{hostAndPort}")) return checkServiceConverge(applicationId(path), path.get("hostAndPort"), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/suspended")) return isSuspended(applicationId(path)); @@ -115,8 +117,8 @@ public class ApplicationHandler extends HttpHandler { getTimeoutFromRequest(request), getVespaVersionFromRequest(request)); } - private HttpResponse clusterControllerStatusPage(ApplicationId applicationId, String hostname, String pathSuffix) { - return applicationRepository.clusterControllerStatusPage(applicationId, hostname, pathSuffix); + private HttpResponse serviceStatusPage(ApplicationId applicationId, String service, String hostname, String pathSuffix) { + return applicationRepository.serviceStatusPage(applicationId, hostname, service, pathSuffix); } private HttpResponse content(ApplicationId applicationId, String contentPath, HttpRequest request) { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java index dd7604264f8..acc39223396 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java @@ -73,6 +73,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -363,6 +364,34 @@ public class ApplicationHandlerTest { assertHttpStatusCodeAndMessage(response, 200, "text/html", "..."); } + @Test + public void testServiceStatus() throws Exception { + applicationRepository.deploy(testApp, prepareParams(applicationId)); + String host = "foo.yahoo.com"; + HttpProxy mockHttpProxy = mock(HttpProxy.class); + ApplicationRepository applicationRepository = new ApplicationRepository.Builder() + .withTenantRepository(tenantRepository) + .withHostProvisionerProvider(HostProvisionerProvider.empty()) + .withOrchestrator(orchestrator) + .withTesterClient(testerClient) + .withHttpProxy(mockHttpProxy) + .build(); + ApplicationHandler mockHandler = createApplicationHandler(applicationRepository); + doAnswer(invoc -> new StaticResponse(200, "text/html", "" + + "host=" + invoc.getArgument(1, String.class) + "," + + "service=" + invoc.getArgument(2, String.class) + "," + + "path=" + invoc.getArgument(3, String.class) + "")).when(mockHttpProxy).get(any(), any(), any(), any()); + + HttpResponse response = mockHandler.handle(createTestRequest(toUrlPath(applicationId, Zone.defaultZone(), true) + "/service/container-clustercontroller/" + host + "/status/some/path/clusterName1", GET)); + assertHttpStatusCodeAndMessage(response, 200, "text/html", "host=foo.yahoo.com,service=container-clustercontroller,path=clustercontroller-status/v1/some/path/clusterName1"); + + response = mockHandler.handle(createTestRequest(toUrlPath(applicationId, Zone.defaultZone(), true) + "/service/distributor/" + host + "/status/something", GET)); + assertHttpStatusCodeAndMessage(response, 200, "text/html", "host=foo.yahoo.com,service=distributor,path=something"); + + response = mockHandler.handle(createTestRequest(toUrlPath(applicationId, Zone.defaultZone(), true) + "/service/fake-service/" + host + "/status/something", GET)); + assertHttpStatusCodeAndMessage(response, 404, "{\"error-code\":\"NOT_FOUND\",\"message\":\"No status page for service: fake-service\"}"); + } + @Test public void testPutIsIllegal() throws IOException { assertNotAllowed(Method.PUT); -- cgit v1.2.3