diff options
author | gjoranv <gv@verizonmedia.com> | 2019-07-08 10:21:43 +0200 |
---|---|---|
committer | gjoranv <gv@verizonmedia.com> | 2019-07-09 11:22:56 +0200 |
commit | 447357bd91208cb3d672850412e11d7bfc8ba009 (patch) | |
tree | e305a638516606de1b0e501c98546391314681ba | |
parent | 262be1a425e21e4f5c149f1d3162f4fdaed372e7 (diff) |
Implement the /metrics/v1 rest api.
- Json errors are now handled in ErrorResponse instead of
JsonRenderingException
11 files changed, 281 insertions, 159 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java index f290911c6bd..1b30faa3e21 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java @@ -9,7 +9,7 @@ import ai.vespa.metricsproxy.core.MetricsConsumers; import ai.vespa.metricsproxy.core.MetricsManager; import ai.vespa.metricsproxy.core.MonitoringConfig; import ai.vespa.metricsproxy.core.VespaMetrics; -import ai.vespa.metricsproxy.http.GenericMetricsHandler; +import ai.vespa.metricsproxy.http.MetricsHandler; import ai.vespa.metricsproxy.metric.ExternalMetrics; import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions; import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig; @@ -49,8 +49,8 @@ import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerClus import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.TENANT; import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.ZONE; import static com.yahoo.vespa.model.admin.monitoring.DefaultPublicConsumer.getDefaultPublicConsumer; -import static com.yahoo.vespa.model.admin.monitoring.VespaMetricsConsumer.getVespaMetricsConsumer; import static com.yahoo.vespa.model.admin.monitoring.MetricSet.emptyMetricSet; +import static com.yahoo.vespa.model.admin.monitoring.VespaMetricsConsumer.getVespaMetricsConsumer; import static com.yahoo.vespa.model.container.xml.BundleMapper.JarSuffix.JAR_WITH_DEPS; import static com.yahoo.vespa.model.container.xml.BundleMapper.absoluteBundlePath; @@ -71,7 +71,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC static final Path METRICS_PROXY_BUNDLE_FILE = absoluteBundlePath((Paths.get(METRICS_PROXY_NAME + JAR_WITH_DEPS.suffix))); static final String METRICS_PROXY_BUNDLE_NAME = "com.yahoo.vespa." + METRICS_PROXY_NAME; - private static final String METRICS_HANDLER_BINDING = "/metrics/v1/values"; + private static final String METRICS_HANDLER_BINDING = "/metrics/v1/*"; static final class AppDimensionNames { static final String ZONE = "zone"; @@ -113,7 +113,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC private void addGenericMetricsHandler() { Handler<AbstractConfigProducer<?>> metricsHandler = new Handler<>( - new ComponentModel(GenericMetricsHandler.class.getName(), null, METRICS_PROXY_BUNDLE_NAME, null)); + new ComponentModel(MetricsHandler.class.getName(), null, METRICS_PROXY_BUNDLE_NAME, null)); metricsHandler.addServerBindings("http://*" + METRICS_HANDLER_BINDING, "http://*" + METRICS_HANDLER_BINDING + "/*"); addComponent(metricsHandler); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java index fbd1c7455dd..9c75b80c2f4 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.model.admin.monitoring; -import ai.vespa.metricsproxy.http.GenericMetricsHandler; +import ai.vespa.metricsproxy.http.ValuesFetcher; import com.google.common.collect.ImmutableList; import static com.yahoo.vespa.model.admin.monitoring.DefaultPublicMetrics.defaultPublicMetricSet; @@ -17,7 +17,7 @@ import static java.util.Collections.emptyList; */ public class DefaultPublicConsumer { - public static final String DEFAULT_PUBLIC_CONSUMER_ID = GenericMetricsHandler.DEFAULT_PUBLIC_CONSUMER_ID.id; + public static final String DEFAULT_PUBLIC_CONSUMER_ID = ValuesFetcher.DEFAULT_PUBLIC_CONSUMER_ID.id; private static final MetricSet publicConsumerMetrics = new MetricSet("public-consumer-metrics", emptyList(), diff --git a/metrics-proxy/pom.xml b/metrics-proxy/pom.xml index 3cfe1d2f568..26caa3f66af 100644 --- a/metrics-proxy/pom.xml +++ b/metrics-proxy/pom.xml @@ -79,6 +79,12 @@ </dependency> <dependency> <groupId>com.yahoo.vespa</groupId> + <artifactId>jdisc_http_service</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> <artifactId>jrt</artifactId> <version>${project.version}</version> <scope>provided</scope> @@ -110,12 +116,6 @@ <version>${project.version}</version> </dependency> <dependency> - <groupId>com.yahoo.vespa</groupId> - <artifactId>jdisc_http_service</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ErrorResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ErrorResponse.java new file mode 100644 index 00000000000..679bae84f8e --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ErrorResponse.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.Map; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; + +/** + * @author gjoranv + */ +class ErrorResponse extends JsonResponse { + private static Logger log = Logger.getLogger(ErrorResponse.class.getName()); + + ErrorResponse(int code, String message) { + super(code, asErrorJson(message)); + } + + static String asErrorJson(String message) { + try { + return new ObjectMapper().writeValueAsString(Map.of("error", message)); + } catch (JsonProcessingException e) { + log.log(WARNING, "Could not encode error message to json:", e); + return "Could not encode error message to json, check the log for details."; + } + } +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java deleted file mode 100644 index f61a96917a9..00000000000 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - */ - -package ai.vespa.metricsproxy.http; - -import ai.vespa.metricsproxy.core.MetricsConsumers; -import ai.vespa.metricsproxy.core.MetricsManager; -import ai.vespa.metricsproxy.metric.model.ConsumerId; -import ai.vespa.metricsproxy.metric.model.MetricsPacket; -import ai.vespa.metricsproxy.metric.model.json.JsonRenderingException; -import ai.vespa.metricsproxy.service.VespaServices; -import com.google.inject.Inject; -import com.yahoo.container.jdisc.HttpRequest; -import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.time.Instant; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId; -import static ai.vespa.metricsproxy.metric.model.json.GenericJsonUtil.toGenericJsonModel; - -/** - * Http handler that exposes the generic metrics format. - * - * @author gjoranv - */ -public class GenericMetricsHandler extends ThreadedHttpRequestHandler { - private static final Logger log = Logger.getLogger(GenericMetricsHandler.class.getName()); - - public static final ConsumerId DEFAULT_PUBLIC_CONSUMER_ID = toConsumerId("default"); - - private final MetricsConsumers metricsConsumers; - private final MetricsManager metricsManager; - private final VespaServices vespaServices; - - @Inject - public GenericMetricsHandler(Executor executor, - MetricsManager metricsManager, - VespaServices vespaServices, - MetricsConsumers metricsConsumers) { - super(executor); - this.metricsConsumers = metricsConsumers; - this.metricsManager = metricsManager; - this.vespaServices = vespaServices; - } - - @Override - public HttpResponse handle(HttpRequest request) { - try { - ConsumerId consumer = getConsumerOrDefault(request.getProperty("consumer")); - - List<MetricsPacket> metrics = metricsManager.getMetrics(vespaServices.getVespaServices(), Instant.now()) - .stream() - .filter(metricsPacket -> metricsPacket.consumers().contains(consumer)) - .collect(Collectors.toList()); - return new Response(200, toGenericJsonModel(metrics).serialize()); - } catch (JsonRenderingException e) { - return new Response(500, e.getMessageAsJson()); - } - } - - private ConsumerId getConsumerOrDefault(String consumer) { - if (consumer == null) return DEFAULT_PUBLIC_CONSUMER_ID; - - ConsumerId consumerId = toConsumerId(consumer); - if (! metricsConsumers.getAllConsumers().contains(consumerId)) { - log.info("No consumer with id '" + consumer + "' - using the default consumer instead."); - return DEFAULT_PUBLIC_CONSUMER_ID; - } - return consumerId; - } - - private static class Response extends HttpResponse { - private final byte[] data; - - Response(int code, String data) { - super(code); - this.data = data.getBytes(Charset.forName(DEFAULT_CHARACTER_ENCODING)); - } - - @Override - public String getContentType() { - return "application/json"; - } - - @Override - public void render(OutputStream outputStream) throws IOException { - outputStream.write(data); - } - } - -} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/JsonResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/JsonResponse.java new file mode 100644 index 00000000000..758a043a823 --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/JsonResponse.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import com.yahoo.container.jdisc.HttpResponse; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; + +/** + * @author gjoranv + */ +class JsonResponse extends HttpResponse { + private final byte[] data; + + JsonResponse(int code, String data) { + super(code); + this.data = data.getBytes(Charset.forName(DEFAULT_CHARACTER_ENCODING)); + } + + @Override + public String getContentType() { + return "application/json"; + } + + @Override + public void render(OutputStream outputStream) throws IOException { + outputStream.write(data); + } +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsHandler.java new file mode 100644 index 00000000000..1d7206f177d --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import ai.vespa.metricsproxy.core.MetricsConsumers; +import ai.vespa.metricsproxy.core.MetricsManager; +import ai.vespa.metricsproxy.metric.model.json.JsonRenderingException; +import ai.vespa.metricsproxy.service.VespaServices; +import com.google.inject.Inject; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; +import com.yahoo.restapi.Path; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.URI; +import java.util.concurrent.Executor; + +import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR; +import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED; +import static com.yahoo.jdisc.Response.Status.NOT_FOUND; +import static com.yahoo.jdisc.Response.Status.OK; +import static com.yahoo.jdisc.http.HttpRequest.Method.GET; + +/** + * Http handler for the metrics/v1 rest api. + * + * @author gjoranv + */ +public class MetricsHandler extends ThreadedHttpRequestHandler { + + static final String V1_PATH = "/metrics/v1"; + static final String VALUES_PATH = V1_PATH + "/values"; + + private final ValuesFetcher valuesFetcher; + + @Inject + public MetricsHandler(Executor executor, + MetricsManager metricsManager, + VespaServices vespaServices, + MetricsConsumers metricsConsumers) { + super(executor); + valuesFetcher = new ValuesFetcher(metricsManager, vespaServices, metricsConsumers); + } + + @Override + public HttpResponse handle(HttpRequest request) { + if (request.getMethod() != GET) return new JsonResponse(METHOD_NOT_ALLOWED, "Only GET is supported"); + + Path path = new Path(request.getUri()); + + if (path.matches(V1_PATH)) return v1Response(request.getUri()); + if (path.matches(VALUES_PATH)) return valuesResponse(request); + + return new ErrorResponse(NOT_FOUND, "No content at given path"); + } + + private JsonResponse v1Response(URI requestUri) { + try { + return new JsonResponse(OK, v1Content(requestUri)); + } catch (JSONException e) { + log.warning("Bad JSON construction in " + V1_PATH + " response: " + e.getMessage()); + return new ErrorResponse(INTERNAL_SERVER_ERROR, + "An error occurred, please try path '" + VALUES_PATH + "'"); + } + } + + private JsonResponse valuesResponse(HttpRequest request) { + try { + return new JsonResponse(OK, valuesFetcher.fetch(request.getProperty("consumer"))); + } catch (JsonRenderingException e) { + return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + + // TODO: Use jackson with a "Resources" class instead of JSONObject + private String v1Content(URI requestUri) throws JSONException { + int port = requestUri.getPort(); + String host = requestUri.getHost(); + StringBuilder base = new StringBuilder("http://"); + base.append(host); + if (port >= 0) { + base.append(":").append(port); + } + String uriBase = base.toString(); + JSONArray linkList = new JSONArray(); + for (String api : new String[] {VALUES_PATH}) { + JSONObject resource = new JSONObject(); + resource.put("url", uriBase + api); + linkList.put(resource); + } + return new JSONObject().put("resources", linkList).toString(4); + } + +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ValuesFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ValuesFetcher.java new file mode 100644 index 00000000000..4686d9c1751 --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ValuesFetcher.java @@ -0,0 +1,65 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import ai.vespa.metricsproxy.core.MetricsConsumers; +import ai.vespa.metricsproxy.core.MetricsManager; +import ai.vespa.metricsproxy.metric.model.ConsumerId; +import ai.vespa.metricsproxy.metric.model.MetricsPacket; +import ai.vespa.metricsproxy.metric.model.json.JsonRenderingException; +import ai.vespa.metricsproxy.service.VespaServices; + +import java.time.Instant; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId; +import static ai.vespa.metricsproxy.metric.model.json.GenericJsonUtil.toGenericJsonModel; + +/** + * Generates metrics values in json format for the metrics/v1 rest api. + * + * @author gjoranv + */ +public class ValuesFetcher { + private static final Logger log = Logger.getLogger(ValuesFetcher.class.getName()); + + public static final ConsumerId DEFAULT_PUBLIC_CONSUMER_ID = toConsumerId("default"); + + private final MetricsManager metricsManager; + private final VespaServices vespaServices; + private final MetricsConsumers metricsConsumers; + + ValuesFetcher(MetricsManager metricsManager, + VespaServices vespaServices, + MetricsConsumers metricsConsumers) { + this.metricsManager = metricsManager; + this.vespaServices = vespaServices; + this.metricsConsumers = metricsConsumers; + } + + public String fetch(String requestedConsumer) throws JsonRenderingException { + ConsumerId consumer = getConsumerOrDefault(requestedConsumer); + + List<MetricsPacket> metrics = metricsManager.getMetrics(vespaServices.getVespaServices(), Instant.now()) + .stream() + .filter(metricsPacket -> metricsPacket.consumers().contains(consumer)) + .collect(Collectors.toList()); + return toGenericJsonModel(metrics).serialize(); + } + + private ConsumerId getConsumerOrDefault(String consumer) { + if (consumer == null) return DEFAULT_PUBLIC_CONSUMER_ID; + + ConsumerId consumerId = toConsumerId(consumer); + if (! metricsConsumers.getAllConsumers().contains(consumerId)) { + log.info("No consumer with id '" + consumer + "' - using the default consumer instead."); + return DEFAULT_PUBLIC_CONSUMER_ID; + } + return consumerId; + } + +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java index 4c1d42d75ee..02292cea164 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java @@ -4,43 +4,15 @@ package ai.vespa.metricsproxy.metric.model.json; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.util.Map; -import java.util.logging.Logger; - -import static java.util.logging.Level.WARNING; - /** - * An exception to be thrown upon errors in json rendering, and that can deliver its message wrapped - * in an "error" json object. + * An unchecked exception to be thrown upon errors in json rendering. * * @author gjoranv */ public class JsonRenderingException extends RuntimeException { - private static Logger log = Logger.getLogger(JsonRenderingException.class.getName()); - JsonRenderingException(String message) { super(message); } - /** - * Returns the message wrapped in an "error" json object. In the unlikely case that json rendering of the - * error message fails, a plain text string will be returned instead. - */ - public String getMessageAsJson() { - return wrap(getMessage()); - } - - private static String wrap(String message) { - try { - return new ObjectMapper().writeValueAsString(Map.of("error", message)); - } catch (JsonProcessingException e) { - log.log(WARNING, "Could not encode error message to json:", e); - return "Could not encode error message to json, check the log for details."; - } - } - } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingExceptionTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/ErrorResponseTest.java index 9b502d3f67a..ad2e06da19c 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingExceptionTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/ErrorResponseTest.java @@ -2,7 +2,7 @@ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. */ -package ai.vespa.metricsproxy.metric.model.json; +package ai.vespa.metricsproxy.http; import org.junit.Test; @@ -11,17 +11,18 @@ import static org.junit.Assert.assertEquals; /** * @author gjoranv */ -public class JsonRenderingExceptionTest { +public class ErrorResponseTest { @Test public void error_message_is_wrapped_in_json_object() { - var exception = new JsonRenderingException("bad"); - assertEquals("{\"error\":\"bad\"}", exception.getMessageAsJson()); + var json = ErrorResponse.asErrorJson("bad"); + assertEquals("{\"error\":\"bad\"}", json); } @Test public void quotes_are_escaped() { - var exception = new JsonRenderingException("Message \" with \" embedded quotes."); - assertEquals("{\"error\":\"Message \\\" with \\\" embedded quotes.\"}", exception.getMessageAsJson()); + var json = ErrorResponse.asErrorJson("Message \" with \" embedded quotes."); + assertEquals("{\"error\":\"Message \\\" with \\\" embedded quotes.\"}", json); } + } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/MetricsHandlerTest.java index dc89e5bb9f2..66220464e3e 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/MetricsHandlerTest.java @@ -26,6 +26,8 @@ import ai.vespa.metricsproxy.service.VespaServices; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.yahoo.container.jdisc.RequestHandlerTestDriver; +import org.json.JSONArray; +import org.json.JSONObject; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -36,9 +38,10 @@ import java.util.List; import java.util.concurrent.Executors; import static ai.vespa.metricsproxy.core.VespaMetrics.INSTANCE_DIMENSION_ID; -import static ai.vespa.metricsproxy.http.GenericMetricsHandler.DEFAULT_PUBLIC_CONSUMER_ID; +import static ai.vespa.metricsproxy.http.MetricsHandler.V1_PATH; +import static ai.vespa.metricsproxy.http.MetricsHandler.VALUES_PATH; +import static ai.vespa.metricsproxy.http.ValuesFetcher.DEFAULT_PUBLIC_CONSUMER_ID; import static ai.vespa.metricsproxy.metric.ExternalMetrics.VESPA_NODE_SERVICE_ID; -import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId; import static ai.vespa.metricsproxy.metric.model.StatusCode.DOWN; import static ai.vespa.metricsproxy.metric.model.json.JacksonUtil.createObjectMapper; import static ai.vespa.metricsproxy.service.DummyService.METRIC_1; @@ -46,13 +49,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author gjoranv */ @SuppressWarnings("UnstableApiUsage") -public class GenericMetricsHandlerTest { +public class MetricsHandlerTest { private static final List<VespaService> testServices = ImmutableList.of( new DummyService(0, ""), @@ -66,7 +70,9 @@ public class GenericMetricsHandlerTest { private static final String CPU_METRIC = "cpu"; - private static final String URI = "http://localhost/metrics/v1/values"; + private static final String URI_BASE = "http://localhost"; + private static final String V1_URI = URI_BASE + V1_PATH; + private static final String VALUES_URI = URI_BASE + VALUES_PATH; private static RequestHandlerTestDriver testDriver; @@ -78,12 +84,12 @@ public class GenericMetricsHandlerTest { new MetricsPacket.Builder(VESPA_NODE_SERVICE_ID) .timestamp(Instant.now().getEpochSecond()) .putMetrics(ImmutableList.of(new Metric(CPU_METRIC, 12.345))))); - GenericMetricsHandler handler = new GenericMetricsHandler(Executors.newSingleThreadExecutor(), metricsManager, vespaServices, getMetricsConsumers()); + MetricsHandler handler = new MetricsHandler(Executors.newSingleThreadExecutor(), metricsManager, vespaServices, getMetricsConsumers()); testDriver = new RequestHandlerTestDriver(handler); } private GenericJsonModel getResponseAsJsonModel(String consumer) { - String response = testDriver.sendRequest(URI + "?consumer=" + consumer).readAll(); + String response = testDriver.sendRequest(VALUES_URI + "?consumer=" + consumer).readAll(); try { return createObjectMapper().readValue(response, GenericJsonModel.class); } catch (IOException e) { @@ -92,10 +98,23 @@ public class GenericMetricsHandlerTest { } } + @Test + public void v1_response_contains_values_uri() throws Exception { + String response = testDriver.sendRequest(V1_URI).readAll(); + JSONObject root = new JSONObject(response); + assertTrue(root.has("resources")); + + JSONArray resources = root.getJSONArray("resources"); + assertEquals(1, resources.length()); + + JSONObject valuesUrl = resources.getJSONObject(0); + assertEquals(VALUES_URI, valuesUrl.getString("url")); + } + @Ignore @Test - public void visually_inspect_response() throws Exception{ - String response = testDriver.sendRequest(URI+ "?consumer=default").readAll(); + public void visually_inspect_values_response() throws Exception{ + String response = testDriver.sendRequest(VALUES_URI).readAll(); ObjectMapper mapper = createObjectMapper(); var jsonModel = mapper.readValue(response, GenericJsonModel.class); System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonModel)); @@ -103,15 +122,15 @@ public class GenericMetricsHandlerTest { @Test public void no_explicit_consumer_gives_the_default_consumer() { - String responseDefaultConsumer = testDriver.sendRequest(URI + "?consumer=default").readAll(); - String responseNoConsumer = testDriver.sendRequest(URI).readAll(); + String responseDefaultConsumer = testDriver.sendRequest(VALUES_URI + "?consumer=default").readAll(); + String responseNoConsumer = testDriver.sendRequest(VALUES_URI).readAll(); assertEqualsExceptTimestamps(responseDefaultConsumer, responseNoConsumer); } @Test public void unknown_consumer_gives_the_default_consumer() { - String response = testDriver.sendRequest(URI).readAll(); - String responseUnknownConsumer = testDriver.sendRequest(URI + "?consumer=not_defined").readAll(); + String response = testDriver.sendRequest(VALUES_URI).readAll(); + String responseUnknownConsumer = testDriver.sendRequest(VALUES_URI + "?consumer=not_defined").readAll(); assertEqualsExceptTimestamps(response, responseUnknownConsumer); } |