From 02a376fea1b1f11e45fdabfde8e96cebe3b83316 Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Wed, 29 Jun 2022 17:56:58 +0200 Subject: Revert "Bjorncs/application status handler" --- .../vespa/model/container/ContainerCluster.java | 4 +- .../model/container/docproc/DocprocChains.java | 8 - .../model/container/search/ContainerSearch.java | 2 - .../observability/ApplicationStatusHandler.java | 288 ------------------ .../handler/observability/package-info.java | 8 - ....handler.observability.application-userdata.def | 8 - .../ApplicationStatusHandlerTest.java | 131 -------- .../observability/ApplicationStatusHandler.java | 334 +++++++++++++++++++++ .../handler/observability/package-info.java | 5 + ....handler.observability.application-userdata.def | 8 + .../ApplicationStatusHandlerTest.java | 132 ++++++++ container-search/pom.xml | 6 - .../observability/SearchStatusExtension.java | 32 -- .../search/handler/observability/package-info.java | 8 - docproc/pom.xml | 6 - .../observability/DocprocsStatusExtension.java | 51 ---- .../docproc/jdisc/observability/package-info.java | 8 - 17 files changed, 481 insertions(+), 558 deletions(-) delete mode 100644 container-disc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/handler/observability/package-info.java delete mode 100644 container-disc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def delete mode 100644 container-disc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java create mode 100644 container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java create mode 100644 container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/package-info.java create mode 100644 container-search-and-docproc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def create mode 100644 container-search-and-docproc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java delete mode 100644 container-search/src/main/java/com/yahoo/search/handler/observability/SearchStatusExtension.java delete mode 100644 container-search/src/main/java/com/yahoo/search/handler/observability/package-info.java delete mode 100644 docproc/src/main/java/com/yahoo/docproc/jdisc/observability/DocprocsStatusExtension.java delete mode 100644 docproc/src/main/java/com/yahoo/docproc/jdisc/observability/package-info.java diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java index f0b1af30c88..4755f674f69 100755 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java @@ -227,8 +227,8 @@ public abstract class ContainerCluster public void addApplicationStatusHandler() { Handler> statusHandler = new Handler<>( - new ComponentModel(BundleInstantiationSpecification.getFromStrings( - APPLICATION_STATUS_HANDLER_CLASS, null, null), null)); // null bundle, as the handler is in container-disc + new ComponentModel(BundleInstantiationSpecification.getInternalHandlerSpecificationFromStrings( + APPLICATION_STATUS_HANDLER_CLASS, null), null)); statusHandler.addServerBindings(SystemBindingPattern.fromHttpPath("/ApplicationStatus")); addComponent(statusHandler); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java index 31ba195775b..4b9897d0950 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/docproc/DocprocChains.java @@ -3,10 +3,7 @@ package com.yahoo.vespa.model.container.docproc; import com.yahoo.component.ComponentId; import com.yahoo.config.model.producer.AbstractConfigProducer; -import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.jdisc.config.SessionConfig; -import com.yahoo.docproc.jdisc.observability.DocprocsStatusExtension; -import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.Component; @@ -28,11 +25,6 @@ public class DocprocChains extends Chains { super(parent, subId); docprocHandler = new ProcessingHandler<>(this, "com.yahoo.docproc.jdisc.DocumentProcessingHandler"); addComponent(docprocHandler); - addComponent( - new SimpleComponent( - new ComponentModel( - BundleInstantiationSpecification.getInternalProcessingSpecificationFromStrings( - DocprocsStatusExtension.class.getName(), null), null))); if (! (getParent() instanceof ApplicationContainerCluster)) { // All application containers already have a DocumentTypeManager, diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java index 58151063956..97c3f85dced 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java @@ -5,7 +5,6 @@ import com.yahoo.container.QrSearchersConfig; import com.yahoo.prelude.semantics.SemanticRulesConfig; import com.yahoo.search.config.IndexInfoConfig; import com.yahoo.search.config.SchemaInfoConfig; -import com.yahoo.search.handler.observability.SearchStatusExtension; import com.yahoo.search.pagetemplates.PageTemplatesConfig; import com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry; import com.yahoo.search.query.profile.config.QueryProfilesConfig; @@ -58,7 +57,6 @@ public class ContainerSearch extends ContainerSubsystem owningCluster.addComponent(Component.fromClassAndBundle(QUERY_PROFILE_REGISTRY_CLASS, searchAndDocprocBundle)); owningCluster.addComponent(Component.fromClassAndBundle(com.yahoo.search.schema.SchemaInfo.class.getName(), searchAndDocprocBundle)); - owningCluster.addComponent(Component.fromClassAndBundle(SearchStatusExtension.class.getName(), searchAndDocprocBundle)); } public void connectSearchClusters(Map searchClusters) { diff --git a/container-disc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java b/container-disc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java deleted file mode 100644 index 67862533259..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.handler.observability; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.yahoo.component.Component; -import com.yahoo.component.ComponentId; -import com.yahoo.component.Vtag; -import com.yahoo.component.annotation.Inject; -import com.yahoo.component.chain.Chain; -import com.yahoo.component.provider.ComponentRegistry; -import com.yahoo.container.Container; -import com.yahoo.container.core.ApplicationMetadataConfig; -import com.yahoo.container.jdisc.JdiscBindingsConfig; -import com.yahoo.jdisc.handler.AbstractRequestHandler; -import com.yahoo.jdisc.handler.CompletionHandler; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.FastContentWriter; -import com.yahoo.jdisc.handler.RequestHandler; -import com.yahoo.jdisc.handler.ResponseDispatch; -import com.yahoo.jdisc.handler.ResponseHandler; -import com.yahoo.jdisc.http.filter.RequestFilterBase; -import com.yahoo.jdisc.http.filter.ResponseFilterBase; -import com.yahoo.jdisc.service.ClientProvider; -import com.yahoo.jdisc.service.ServerProvider; -import com.yahoo.processing.Processor; -import com.yahoo.processing.execution.chain.ChainRegistry; -import com.yahoo.processing.handler.ProcessingHandler; -import org.osgi.framework.Bundle; -import org.osgi.framework.FrameworkUtil; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; - -/** - * Handler that outputs meta-info about the deployed Vespa application, and status of components and chains. - * - * @author gjoranv - * @author Einar M R Rosenvinge - * @author bjorncs - */ -public class ApplicationStatusHandler extends AbstractRequestHandler { - - public interface Extension { - Map produceExtraFields(ApplicationStatusHandler handler); - } - - private static final ObjectMapper jsonMapper = new ObjectMapper(); - - private final JsonNode applicationJson; - private final JsonNode clientsJson; - private final JsonNode serversJson; - private final JsonNode requestFiltersJson; - private final JsonNode responseFiltersJson; - private final JdiscBindingsConfig bindingsConfig; - private final Collection extensions; - - @Inject - public ApplicationStatusHandler(ApplicationMetadataConfig metaConfig, - ApplicationUserdataConfig userConfig, - JdiscBindingsConfig bindingsConfig, - ComponentRegistry clientProviderRegistry, - ComponentRegistry serverProviderRegistry, - ComponentRegistry requestFilterRegistry, - ComponentRegistry responseFilterRegistry, - ComponentRegistry extensions) { - applicationJson = renderApplicationConfigs(metaConfig, userConfig); - clientsJson = renderRequestHandlers(bindingsConfig, clientProviderRegistry.allComponentsById()); - serversJson = renderObjectComponents(serverProviderRegistry.allComponentsById()); - requestFiltersJson = renderObjectComponents(requestFilterRegistry.allComponentsById()); - responseFiltersJson = renderObjectComponents(responseFilterRegistry.allComponentsById()); - - this.bindingsConfig = bindingsConfig; - this.extensions = extensions.allComponents(); - } - - @Override - public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { - FastContentWriter writer = new FastContentWriter(new ResponseDispatch() { - @Override - protected com.yahoo.jdisc.Response newResponse() { - com.yahoo.jdisc.Response response = new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK); - response.headers().add("Content-Type", List.of("application/json")); - return response; - } - }.connect(handler)); - - try { - writer.write(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(render())); - } catch (JsonProcessingException e) { - throw new RuntimeException("Invalid JSON: " + e.getMessage(), e); - } - writer.close(); - - return new IgnoredContent(); - } - - public ObjectMapper jsonMapper() { return jsonMapper; } - - public Collection requestHandlers() { return container().getRequestHandlerRegistry().allComponents(); } - - private Map requestHandlersById() { return container().getRequestHandlerRegistry().allComponentsById(); } - - private List components() { return container().getComponentRegistry().allComponents(); } - - private static Container container() { return Container.get(); } - - static JsonNode renderApplicationConfigs(ApplicationMetadataConfig metaConfig, - ApplicationUserdataConfig userConfig) { - ObjectNode vespa = jsonMapper.createObjectNode(); - vespa.put("version", Vtag.currentVersion.toString()); - - ObjectNode meta = jsonMapper.createObjectNode(); - meta.put("name", metaConfig.name()); - meta.put("user", metaConfig.user()); - meta.put("path", metaConfig.path()); - meta.put("generation", metaConfig.generation()); - meta.put("timestamp", metaConfig.timestamp()); - meta.put("date", new Date(metaConfig.timestamp()).toString()); - meta.put("checksum", metaConfig.checksum()); - - ObjectNode user = jsonMapper.createObjectNode(); - user.put("version", userConfig.version()); - - ObjectNode application = jsonMapper.createObjectNode(); - application.set("vespa", vespa); - application.set("meta", meta); - application.set("user", user); - return application; - } - - static JsonNode renderObjectComponents(Map componentsById) { - ArrayNode ret = jsonMapper.createArrayNode(); - - for (Map.Entry componentEntry : componentsById.entrySet()) { - JsonNode jc = renderComponent(componentEntry.getValue(), componentEntry.getKey()); - ret.add(jc); - } - return ret; - } - - static JsonNode renderRequestHandlers(JdiscBindingsConfig bindingsConfig, - Map handlersById) { - ArrayNode ret = jsonMapper.createArrayNode(); - - for (Map.Entry handlerEntry : handlersById.entrySet()) { - String id = handlerEntry.getKey().stringValue(); - RequestHandler handler = handlerEntry.getValue(); - - ObjectNode handlerJson = renderComponent(handler, handlerEntry.getKey()); - addBindings(bindingsConfig, id, handlerJson); - ret.add(handlerJson); - } - return ret; - } - - private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, ObjectNode handlerJson) { - List serverBindings = new ArrayList<>(); - List clientBindings = new ArrayList<>(); - - JdiscBindingsConfig.Handlers handlerConfig = bindingsConfig.handlers(id); - if (handlerConfig != null) { - serverBindings = handlerConfig.serverBindings(); - clientBindings = handlerConfig.clientBindings(); - } - handlerJson.set("serverBindings", renderBindings(serverBindings)); - handlerJson.set("clientBindings", renderBindings(clientBindings)); - } - - private static JsonNode renderBindings(List bindings) { - ArrayNode ret = jsonMapper.createArrayNode(); - - for (String binding : bindings) - ret.add(binding); - - return ret; - } - - private static JsonNode renderAbstractComponents(List components) { - ArrayNode ret = jsonMapper.createArrayNode(); - - for (Component c : components) { - JsonNode jc = renderComponent(c, c.getId()); - ret.add(jc); - } - return ret; - } - - public static ObjectNode renderComponent(Object component, ComponentId id) { - ObjectNode jc = jsonMapper.createObjectNode(); - jc.put("id", id.stringValue()); - addBundleInfo(jc, component); - return jc; - } - - private static void addBundleInfo(ObjectNode jsonObject, Object component) { - BundleInfo bundleInfo = bundleInfo(component); - jsonObject.put("class", bundleInfo.className); - jsonObject.put("bundle", bundleInfo.bundleName); - - } - - private static BundleInfo bundleInfo(Object component) { - try { - Bundle bundle = FrameworkUtil.getBundle(component.getClass()); - - String bundleName = bundle != null ? - bundle.getSymbolicName() + ":" + bundle.getVersion() : - "From classpath"; - return new BundleInfo(component.getClass().getName(), bundleName); - } catch (Exception | NoClassDefFoundError e) { - return new BundleInfo("Unavailable, reconfiguration in progress.", ""); - } - } - - static final class BundleInfo { - public final String className; - public final String bundleName; - BundleInfo(String className, String bundleName) { - this.className = className; - this.bundleName = bundleName; - } - } - - JsonNode render() { - ObjectNode root = jsonMapper.createObjectNode(); - - root.set("application", applicationJson); - root.set("abstractComponents", - renderAbstractComponents(components())); - - root.set("handlers", renderRequestHandlers(bindingsConfig, requestHandlersById())); - root.set("clients", clientsJson); - root.set("servers", serversJson); - root.set("httpRequestFilters", requestFiltersJson); - root.set("httpResponseFilters", responseFiltersJson); - - root.set("processingChains", renderProcessingChains()); - for (Extension extension : extensions) { - extension.produceExtraFields(this).forEach((field, json) -> { - if (root.has(field)) throw new IllegalArgumentException("Field '" + field + "' already defined"); - root.set(field, json); - }); - - } - return root; - } - - private JsonNode renderProcessingChains() { - JsonNode ret = jsonMapper.createObjectNode(); - for (RequestHandler h : requestHandlers()) { - if (h instanceof ProcessingHandler) { - ChainRegistry registry = ((ProcessingHandler) h).getChainRegistry(); - return renderChains(registry); - } - } - return ret; - } - - // Note the generic param here! The key to make this work is '? extends Chain', but why? - public static JsonNode renderChains(ComponentRegistry> chains) { - ObjectNode ret = jsonMapper.createObjectNode(); - for (Chain chain : chains.allComponents()) { - ret.set(chain.getId().stringValue(), renderAbstractComponents(chain.components())); - } - return ret; - } - - private class IgnoredContent implements ContentChannel { - @Override - public void write(ByteBuffer buf, CompletionHandler handler) { - handler.completed(); - } - - @Override - public void close(CompletionHandler handler) { - handler.completed(); - } - } - -} diff --git a/container-disc/src/main/java/com/yahoo/container/handler/observability/package-info.java b/container-disc/src/main/java/com/yahoo/container/handler/observability/package-info.java deleted file mode 100644 index 1a669b176d0..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/handler/observability/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @author bjorncs - */ -@ExportPackage -package com.yahoo.container.handler.observability; - -import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file diff --git a/container-disc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def b/container-disc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def deleted file mode 100644 index cde92df9ef4..00000000000 --- a/container-disc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -# Contains user generated info about one deployed application -# The values in this config are set by config overrides in vespa-services - -namespace=container.handler.observability - -# The user-defined version of this application. -version string default="" diff --git a/container-disc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java b/container-disc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java deleted file mode 100644 index 4c58a943199..00000000000 --- a/container-disc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.handler.observability; - -import com.yahoo.component.ComponentId; -import com.yahoo.component.chain.Chain; -import com.yahoo.container.core.ApplicationMetadataConfig; -import com.yahoo.container.jdisc.JdiscBindingsConfig; -import com.yahoo.jdisc.handler.RequestHandler; -import com.yahoo.jdisc.service.ClientProvider; -import com.yahoo.processing.Processor; -import com.yahoo.processing.Request; -import com.yahoo.processing.Response; -import com.yahoo.processing.execution.Execution; -import com.yahoo.processing.execution.chain.ChainRegistry; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.HashMap; - -import static com.yahoo.container.jdisc.JdiscBindingsConfig.Handlers; -import static org.junit.Assert.assertTrue; - -/** - * @author gjoranv - */ -public class ApplicationStatusHandlerTest { - - @Test - public void application_configs_are_rendered() { - ApplicationMetadataConfig metaConfig = new ApplicationMetadataConfig( - new ApplicationMetadataConfig.Builder() - .checksum("abc") - .name("app") - .path("/a/b/c") - .timestamp(3000) - .user("donald")); - - ApplicationUserdataConfig userConfig = new ApplicationUserdataConfig( - new ApplicationUserdataConfig.Builder() - .version("v1")); - - String json = ApplicationStatusHandler.renderApplicationConfigs(metaConfig, userConfig).toString(); - assertTrue(json.contains("version")); - assertTrue(json.contains("meta")); - assertTrue(json.contains("abc")); - assertTrue(json.contains("app")); - assertTrue(json.contains("/a/b/c")); - assertTrue(json.contains("3000")); - assertTrue(json.contains("donald")); - - assertTrue(json.contains("v1")); - } - - @Test - public void object_components_are_rendered() { - HashMap id2object = new HashMap<>(); - id2object.put(new ComponentId("myComponent"), new Object()); - - String json = ApplicationStatusHandler.renderObjectComponents(id2object).toString(); - assertTrue(json.contains("myComponent")); - } - - @Test - public void request_handlers_are_rendered() { - final String id = "myHandler"; - final String serverBinding1 = "http://*/serverBinding"; - final String serverBinding2 = "http://*/anotherServerBinding"; - final String clientBinding = "http://*/clientBinding"; - - HashMap handlersById = new HashMap<>(); - handlersById.put(new ComponentId(id), Mockito.mock(RequestHandler.class)); - - JdiscBindingsConfig bindingsConfig = new JdiscBindingsConfig(new JdiscBindingsConfig.Builder() - .handlers(id, new Handlers.Builder() - .serverBindings(serverBinding1) - .serverBindings(serverBinding2) - .clientBindings(clientBinding)) - ); - String json = ApplicationStatusHandler.renderRequestHandlers(bindingsConfig, handlersById).toString(); - assertTrue(json.contains("\"" + id + "\"")); - assertTrue(json.contains(serverBinding1)); - assertTrue(json.contains(serverBinding2)); - assertTrue(json.contains(clientBinding)); - } - - @Test - public void client_providers_are_rendered() { - final String id = "myClient"; - final String clientBinding = "http://*/clientBinding"; - final String clientBinding2 = "http://*/anotherClientBinding"; - final String serverBinding = "http://*/serverBinding"; - - HashMap clientsById = new HashMap<>(); - clientsById.put(new ComponentId(id), Mockito.mock(ClientProvider.class)); - - JdiscBindingsConfig bindingsConfig = new JdiscBindingsConfig(new JdiscBindingsConfig.Builder() - .handlers(id, new Handlers.Builder() - .clientBindings(clientBinding) - .clientBindings(clientBinding2) - .serverBindings(serverBinding)) - ); - String json = ApplicationStatusHandler.renderRequestHandlers(bindingsConfig, clientsById).toString(); - System.out.println(json); - assertTrue(json.contains("\"" + id + "\"")); - assertTrue(json.contains(clientBinding)); - assertTrue(json.contains(clientBinding2)); - assertTrue(json.contains(serverBinding)); - } - - @Test - public void chains_are_rendered() { - ChainRegistry chains = new ChainRegistry<>(); - Chain chain = new Chain<>("myChain", new VoidProcessor(new ComponentId("voidProcessor"))); - chains.register(new ComponentId("myChain"), chain); - - String json = ApplicationStatusHandler.renderChains(chains).toString(); - assertTrue(json.contains("myChain")); - assertTrue(json.contains("voidProcessor")); - } - - private static class VoidProcessor extends Processor { - private VoidProcessor(ComponentId id) { - super(); - initId(id); - } - @Override - public Response process(Request request, Execution processorExecution) { - return null; - } - } -} diff --git a/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java new file mode 100644 index 00000000000..09b5a6ff85e --- /dev/null +++ b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/ApplicationStatusHandler.java @@ -0,0 +1,334 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.handler.observability; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.yahoo.component.annotation.Inject; +import com.yahoo.component.AbstractComponent; +import com.yahoo.component.ComponentId; +import com.yahoo.component.Vtag; +import com.yahoo.component.chain.Chain; +import com.yahoo.component.provider.ComponentRegistry; +import com.yahoo.container.Container; +import com.yahoo.container.core.ApplicationMetadataConfig; +import com.yahoo.container.jdisc.JdiscBindingsConfig; +import com.yahoo.docproc.Call; +import com.yahoo.docproc.impl.DocprocService; +import com.yahoo.docproc.jdisc.DocumentProcessingHandler; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.FastContentWriter; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseDispatch; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.http.filter.RequestFilterBase; +import com.yahoo.jdisc.http.filter.ResponseFilterBase; +import com.yahoo.jdisc.service.ClientProvider; +import com.yahoo.jdisc.service.ServerProvider; +import com.yahoo.processing.Processor; +import com.yahoo.processing.execution.chain.ChainRegistry; +import com.yahoo.processing.handler.ProcessingHandler; +import com.yahoo.search.handler.SearchHandler; +import com.yahoo.search.searchchain.SearchChainRegistry; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Handler that outputs meta-info about the deployed Vespa application, and status of components and chains. + * + * @author gjoranv + * @author Einar M R Rosenvinge + */ +public class ApplicationStatusHandler extends AbstractRequestHandler { + + private static final ObjectMapper jsonMapper = new ObjectMapper(); + + private final JsonNode applicationJson; + private final JsonNode clientsJson; + private final JsonNode serversJson; + private final JsonNode requestFiltersJson; + private final JsonNode responseFiltersJson; + private final JdiscBindingsConfig bindingsConfig; + + @Inject + public ApplicationStatusHandler(ApplicationMetadataConfig metaConfig, + ApplicationUserdataConfig userConfig, + JdiscBindingsConfig bindingsConfig, + ComponentRegistry clientProviderRegistry, + ComponentRegistry serverProviderRegistry, + ComponentRegistry requestFilterRegistry, + ComponentRegistry responseFilterRegistry) { + + applicationJson = renderApplicationConfigs(metaConfig, userConfig); + clientsJson = renderRequestHandlers(bindingsConfig, clientProviderRegistry.allComponentsById()); + serversJson = renderObjectComponents(serverProviderRegistry.allComponentsById()); + requestFiltersJson = renderObjectComponents(requestFilterRegistry.allComponentsById()); + responseFiltersJson = renderObjectComponents(responseFilterRegistry.allComponentsById()); + + this.bindingsConfig = bindingsConfig; + } + + @Override + public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { + JsonNode json = new StatusResponse(applicationJson, clientsJson, serversJson, + requestFiltersJson, responseFiltersJson, bindingsConfig) + .render(); + + FastContentWriter writer = new FastContentWriter(new ResponseDispatch() { + @Override + protected com.yahoo.jdisc.Response newResponse() { + com.yahoo.jdisc.Response response = new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK); + response.headers().add("Content-Type", List.of("application/json")); + return response; + } + }.connect(handler)); + + try { + writer.write(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(json)); + } catch (JsonProcessingException e) { + throw new RuntimeException("Invalid JSON: " + e.getMessage(), e); + } + writer.close(); + + return new IgnoredContent(); + } + + static JsonNode renderApplicationConfigs(ApplicationMetadataConfig metaConfig, + ApplicationUserdataConfig userConfig) { + ObjectNode vespa = jsonMapper.createObjectNode(); + vespa.put("version", Vtag.currentVersion.toString()); + + ObjectNode meta = jsonMapper.createObjectNode(); + meta.put("name", metaConfig.name()); + meta.put("user", metaConfig.user()); + meta.put("path", metaConfig.path()); + meta.put("generation", metaConfig.generation()); + meta.put("timestamp", metaConfig.timestamp()); + meta.put("date", new Date(metaConfig.timestamp()).toString()); + meta.put("checksum", metaConfig.checksum()); + + ObjectNode user = jsonMapper.createObjectNode(); + user.put("version", userConfig.version()); + + ObjectNode application = jsonMapper.createObjectNode(); + application.set("vespa", vespa); + application.set("meta", meta); + application.set("user", user); + return application; + } + + static JsonNode renderObjectComponents(Map componentsById) { + ArrayNode ret = jsonMapper.createArrayNode(); + + for (Map.Entry componentEntry : componentsById.entrySet()) { + JsonNode jc = renderComponent(componentEntry.getValue(), componentEntry.getKey()); + ret.add(jc); + } + return ret; + } + + static JsonNode renderRequestHandlers(JdiscBindingsConfig bindingsConfig, + Map handlersById) { + ArrayNode ret = jsonMapper.createArrayNode(); + + for (Map.Entry handlerEntry : handlersById.entrySet()) { + String id = handlerEntry.getKey().stringValue(); + RequestHandler handler = handlerEntry.getValue(); + + ObjectNode handlerJson = renderComponent(handler, handlerEntry.getKey()); + addBindings(bindingsConfig, id, handlerJson); + ret.add(handlerJson); + } + return ret; + } + + private static void addBindings(JdiscBindingsConfig bindingsConfig, String id, ObjectNode handlerJson) { + List serverBindings = new ArrayList<>(); + List clientBindings = new ArrayList<>(); + + JdiscBindingsConfig.Handlers handlerConfig = bindingsConfig.handlers(id); + if (handlerConfig != null) { + serverBindings = handlerConfig.serverBindings(); + clientBindings = handlerConfig.clientBindings(); + } + handlerJson.set("serverBindings", renderBindings(serverBindings)); + handlerJson.set("clientBindings", renderBindings(clientBindings)); + } + + private static JsonNode renderBindings(List bindings) { + ArrayNode ret = jsonMapper.createArrayNode(); + + for (String binding : bindings) + ret.add(binding); + + return ret; + } + + private static JsonNode renderAbstractComponents(List components) { + ArrayNode ret = jsonMapper.createArrayNode(); + + for (AbstractComponent c : components) { + JsonNode jc = renderComponent(c, c.getId()); + ret.add(jc); + } + return ret; + } + + private static ObjectNode renderComponent(Object component, ComponentId id) { + ObjectNode jc = jsonMapper.createObjectNode(); + jc.put("id", id.stringValue()); + addBundleInfo(jc, component); + return jc; + } + + private static void addBundleInfo(ObjectNode jsonObject, Object component) { + BundleInfo bundleInfo = bundleInfo(component); + jsonObject.put("class", bundleInfo.className); + jsonObject.put("bundle", bundleInfo.bundleName); + + } + + private static BundleInfo bundleInfo(Object component) { + try { + Bundle bundle = FrameworkUtil.getBundle(component.getClass()); + + String bundleName = bundle != null ? + bundle.getSymbolicName() + ":" + bundle.getVersion() : + "From classpath"; + return new BundleInfo(component.getClass().getName(), bundleName); + } catch (Exception | NoClassDefFoundError e) { + return new BundleInfo("Unavailable, reconfiguration in progress.", ""); + } + } + + static final class BundleInfo { + public final String className; + public final String bundleName; + BundleInfo(String className, String bundleName) { + this.className = className; + this.bundleName = bundleName; + } + } + + static final class StatusResponse { + private final JsonNode applicationJson; + private final JsonNode clientsJson; + private final JsonNode serversJson; + private final JsonNode requestFiltersJson; + private final JsonNode responseFiltersJson; + private final JdiscBindingsConfig bindingsConfig; + + StatusResponse(JsonNode applicationJson, + JsonNode clientsJson, + JsonNode serversJson, + JsonNode requestFiltersJson, + JsonNode responseFiltersJson, + JdiscBindingsConfig bindingsConfig) { + this.applicationJson = applicationJson; + this.clientsJson = clientsJson; + this.serversJson = serversJson; + this.requestFiltersJson = requestFiltersJson; + this.responseFiltersJson = responseFiltersJson; + this.bindingsConfig = bindingsConfig; + } + + public JsonNode render() { + ObjectNode root = jsonMapper.createObjectNode(); + + root.set("application", applicationJson); + root.set("abstractComponents", + renderAbstractComponents(Container.get().getComponentRegistry().allComponents())); + + root.set("handlers", + renderRequestHandlers(bindingsConfig, Container.get().getRequestHandlerRegistry().allComponentsById())); + root.set("clients", clientsJson); + root.set("servers", serversJson); + root.set("httpRequestFilters", requestFiltersJson); + root.set("httpResponseFilters", responseFiltersJson); + + root.set("searchChains", renderSearchChains(Container.get())); + root.set("docprocChains", renderDocprocChains(Container.get())); + root.set("processingChains", renderProcessingChains(Container.get())); + + return root; + } + + private static JsonNode renderSearchChains(Container container) { + for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) { + if (h instanceof SearchHandler) { + SearchChainRegistry scReg = ((SearchHandler) h).getSearchChainRegistry(); + return renderChains(scReg); + } + } + return jsonMapper.createObjectNode(); + } + + private static JsonNode renderDocprocChains(Container container) { + ObjectNode ret = jsonMapper.createObjectNode(); + for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) { + if (h instanceof DocumentProcessingHandler) { + ComponentRegistry registry = ((DocumentProcessingHandler) h).getDocprocServiceRegistry(); + for (DocprocService service : registry.allComponents()) { + ret.set(service.getId().stringValue(), renderCalls(service.getCallStack().iterator())); + } + } + } + return ret; + } + + private static JsonNode renderProcessingChains(Container container) { + JsonNode ret = jsonMapper.createObjectNode(); + for (RequestHandler h : container.getRequestHandlerRegistry().allComponents()) { + if (h instanceof ProcessingHandler) { + ChainRegistry registry = ((ProcessingHandler) h).getChainRegistry(); + return renderChains(registry); + } + } + return ret; + } + + // Note the generic param here! The key to make this work is '? extends Chain', but why? + static JsonNode renderChains(ComponentRegistry> chains) { + ObjectNode ret = jsonMapper.createObjectNode(); + for (Chain chain : chains.allComponents()) { + ret.set(chain.getId().stringValue(), renderAbstractComponents(chain.components())); + } + return ret; + } + + private static JsonNode renderCalls(Iterator components) { + ArrayNode ret = jsonMapper.createArrayNode(); + while (components.hasNext()) { + Call c = components.next(); + JsonNode jc = renderComponent(c.getDocumentProcessor(), c.getDocumentProcessor().getId()); + ret.add(jc); + } + return ret; + } + + } + + private class IgnoredContent implements ContentChannel { + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + handler.completed(); + } + + @Override + public void close(CompletionHandler handler) { + handler.completed(); + } + } + +} diff --git a/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/package-info.java b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/package-info.java new file mode 100644 index 00000000000..7f9dc3d1cd9 --- /dev/null +++ b/container-search-and-docproc/src/main/java/com/yahoo/container/handler/observability/package-info.java @@ -0,0 +1,5 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.container.handler.observability; + +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/container-search-and-docproc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def b/container-search-and-docproc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def new file mode 100644 index 00000000000..cde92df9ef4 --- /dev/null +++ b/container-search-and-docproc/src/main/resources/configdefinitions/container.handler.observability.application-userdata.def @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Contains user generated info about one deployed application +# The values in this config are set by config overrides in vespa-services + +namespace=container.handler.observability + +# The user-defined version of this application. +version string default="" diff --git a/container-search-and-docproc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java b/container-search-and-docproc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java new file mode 100644 index 00000000000..317f5fc1329 --- /dev/null +++ b/container-search-and-docproc/src/test/java/com/yahoo/container/handler/observability/ApplicationStatusHandlerTest.java @@ -0,0 +1,132 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.handler.observability; + +import com.yahoo.component.ComponentId; +import com.yahoo.component.chain.Chain; +import com.yahoo.container.core.ApplicationMetadataConfig; +import com.yahoo.container.jdisc.JdiscBindingsConfig; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.service.ClientProvider; +import com.yahoo.processing.Processor; +import com.yahoo.processing.Request; +import com.yahoo.processing.Response; +import com.yahoo.processing.execution.Execution; +import com.yahoo.processing.execution.chain.ChainRegistry; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.HashMap; + +import static com.yahoo.container.jdisc.JdiscBindingsConfig.Handlers; +import static org.junit.Assert.assertTrue; + +/** + * @author gjoranv + * @since 5.1.10 + */ +public class ApplicationStatusHandlerTest { + + @Test + public void application_configs_are_rendered() { + ApplicationMetadataConfig metaConfig = new ApplicationMetadataConfig( + new ApplicationMetadataConfig.Builder() + .checksum("abc") + .name("app") + .path("/a/b/c") + .timestamp(3000) + .user("donald")); + + ApplicationUserdataConfig userConfig = new ApplicationUserdataConfig( + new ApplicationUserdataConfig.Builder() + .version("v1")); + + String json = ApplicationStatusHandler.renderApplicationConfigs(metaConfig, userConfig).toString(); + assertTrue(json.contains("version")); + assertTrue(json.contains("meta")); + assertTrue(json.contains("abc")); + assertTrue(json.contains("app")); + assertTrue(json.contains("/a/b/c")); + assertTrue(json.contains("3000")); + assertTrue(json.contains("donald")); + + assertTrue(json.contains("v1")); + } + + @Test + public void object_components_are_rendered() { + HashMap id2object = new HashMap<>(); + id2object.put(new ComponentId("myComponent"), new Object()); + + String json = ApplicationStatusHandler.renderObjectComponents(id2object).toString(); + assertTrue(json.contains("myComponent")); + } + + @Test + public void request_handlers_are_rendered() { + final String id = "myHandler"; + final String serverBinding1 = "http://*/serverBinding"; + final String serverBinding2 = "http://*/anotherServerBinding"; + final String clientBinding = "http://*/clientBinding"; + + HashMap handlersById = new HashMap<>(); + handlersById.put(new ComponentId(id), Mockito.mock(RequestHandler.class)); + + JdiscBindingsConfig bindingsConfig = new JdiscBindingsConfig(new JdiscBindingsConfig.Builder() + .handlers(id, new Handlers.Builder() + .serverBindings(serverBinding1) + .serverBindings(serverBinding2) + .clientBindings(clientBinding)) + ); + String json = ApplicationStatusHandler.renderRequestHandlers(bindingsConfig, handlersById).toString(); + assertTrue(json.contains("\"" + id + "\"")); + assertTrue(json.contains(serverBinding1)); + assertTrue(json.contains(serverBinding2)); + assertTrue(json.contains(clientBinding)); + } + + @Test + public void client_providers_are_rendered() { + final String id = "myClient"; + final String clientBinding = "http://*/clientBinding"; + final String clientBinding2 = "http://*/anotherClientBinding"; + final String serverBinding = "http://*/serverBinding"; + + HashMap clientsById = new HashMap<>(); + clientsById.put(new ComponentId(id), Mockito.mock(ClientProvider.class)); + + JdiscBindingsConfig bindingsConfig = new JdiscBindingsConfig(new JdiscBindingsConfig.Builder() + .handlers(id, new Handlers.Builder() + .clientBindings(clientBinding) + .clientBindings(clientBinding2) + .serverBindings(serverBinding)) + ); + String json = ApplicationStatusHandler.renderRequestHandlers(bindingsConfig, clientsById).toString(); + System.out.println(json); + assertTrue(json.contains("\"" + id + "\"")); + assertTrue(json.contains(clientBinding)); + assertTrue(json.contains(clientBinding2)); + assertTrue(json.contains(serverBinding)); + } + + @Test + public void chains_are_rendered() { + ChainRegistry chains = new ChainRegistry<>(); + Chain chain = new Chain<>("myChain", new VoidProcessor(new ComponentId("voidProcessor"))); + chains.register(new ComponentId("myChain"), chain); + + String json = ApplicationStatusHandler.StatusResponse.renderChains(chains).toString(); + assertTrue(json.contains("myChain")); + assertTrue(json.contains("voidProcessor")); + } + + private static class VoidProcessor extends Processor { + private VoidProcessor(ComponentId id) { + super(); + initId(id); + } + @Override + public Response process(Request request, Execution processorExecution) { + return null; + } + } +} diff --git a/container-search/pom.xml b/container-search/pom.xml index 031d25c7d02..ccec1330d68 100644 --- a/container-search/pom.xml +++ b/container-search/pom.xml @@ -72,12 +72,6 @@ ${project.version} provided - - com.yahoo.vespa - container-disc - ${project.version} - provided - xerces xercesImpl diff --git a/container-search/src/main/java/com/yahoo/search/handler/observability/SearchStatusExtension.java b/container-search/src/main/java/com/yahoo/search/handler/observability/SearchStatusExtension.java deleted file mode 100644 index 836bb1b8354..00000000000 --- a/container-search/src/main/java/com/yahoo/search/handler/observability/SearchStatusExtension.java +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.search.handler.observability; - -import com.fasterxml.jackson.databind.JsonNode; -import com.yahoo.container.handler.observability.ApplicationStatusHandler; -import com.yahoo.jdisc.handler.RequestHandler; -import com.yahoo.search.handler.SearchHandler; -import com.yahoo.search.searchchain.SearchChainRegistry; - -import java.util.Map; - -/** - * @author bjorncs - */ -public class SearchStatusExtension implements ApplicationStatusHandler.Extension { - - @Override - public Map produceExtraFields(ApplicationStatusHandler statusHandler) { - return Map.of("searchChains", renderSearchChains(statusHandler)); - } - - private static JsonNode renderSearchChains(ApplicationStatusHandler statusHandler) { - for (RequestHandler h : statusHandler.requestHandlers()) { - if (h instanceof SearchHandler) { - SearchChainRegistry scReg = ((SearchHandler) h).getSearchChainRegistry(); - return ApplicationStatusHandler.renderChains(scReg); - } - } - return statusHandler.jsonMapper().createObjectNode(); - } - -} diff --git a/container-search/src/main/java/com/yahoo/search/handler/observability/package-info.java b/container-search/src/main/java/com/yahoo/search/handler/observability/package-info.java deleted file mode 100644 index baf24b4a94d..00000000000 --- a/container-search/src/main/java/com/yahoo/search/handler/observability/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @author bjorncs - */ -@ExportPackage -package com.yahoo.search.handler.observability; - -import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file diff --git a/docproc/pom.xml b/docproc/pom.xml index d656559d5ec..e7d8d6e3a3e 100644 --- a/docproc/pom.xml +++ b/docproc/pom.xml @@ -46,12 +46,6 @@ ${project.version} provided - - com.yahoo.vespa - container-disc - ${project.version} - provided - com.yahoo.vespa container-messagebus diff --git a/docproc/src/main/java/com/yahoo/docproc/jdisc/observability/DocprocsStatusExtension.java b/docproc/src/main/java/com/yahoo/docproc/jdisc/observability/DocprocsStatusExtension.java deleted file mode 100644 index 15f6c3a5cd9..00000000000 --- a/docproc/src/main/java/com/yahoo/docproc/jdisc/observability/DocprocsStatusExtension.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.docproc.jdisc.observability; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.yahoo.component.provider.ComponentRegistry; -import com.yahoo.container.handler.observability.ApplicationStatusHandler; -import com.yahoo.docproc.Call; -import com.yahoo.docproc.impl.DocprocService; -import com.yahoo.docproc.jdisc.DocumentProcessingHandler; -import com.yahoo.jdisc.handler.RequestHandler; - -import java.util.Iterator; -import java.util.Map; - - -/** - * @author bjorncs - */ -public class DocprocsStatusExtension implements ApplicationStatusHandler.Extension { - - @Override - public Map produceExtraFields(ApplicationStatusHandler statusHandler) { - return Map.of("docprocChains", renderDocprocChains(statusHandler)); - } - - private static JsonNode renderDocprocChains(ApplicationStatusHandler statusHandler) { - ObjectNode ret = statusHandler.jsonMapper().createObjectNode(); - for (RequestHandler h : statusHandler.requestHandlers()) { - if (h instanceof DocumentProcessingHandler) { - ComponentRegistry registry = ((DocumentProcessingHandler) h).getDocprocServiceRegistry(); - for (DocprocService service : registry.allComponents()) { - ret.set(service.getId().stringValue(), renderCalls(statusHandler, service.getCallStack().iterator())); - } - } - } - return ret; - } - - private static JsonNode renderCalls(ApplicationStatusHandler statusHandler, Iterator components) { - ArrayNode ret = statusHandler.jsonMapper().createArrayNode(); - while (components.hasNext()) { - Call c = components.next(); - JsonNode jc = ApplicationStatusHandler.renderComponent(c.getDocumentProcessor(), c.getDocumentProcessor().getId()); - ret.add(jc); - } - return ret; - } - -} diff --git a/docproc/src/main/java/com/yahoo/docproc/jdisc/observability/package-info.java b/docproc/src/main/java/com/yahoo/docproc/jdisc/observability/package-info.java deleted file mode 100644 index 0040acf1fdc..00000000000 --- a/docproc/src/main/java/com/yahoo/docproc/jdisc/observability/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @author bjorncs - */ -@ExportPackage -package com.yahoo.docproc.jdisc.observability; - -import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file -- cgit v1.2.3