From 32b6e3fb6ad8d549096526a42040fc164eeb3c9c Mon Sep 17 00:00:00 2001 From: Michael Schechter <61873300+mschechter-bellese@users.noreply.github.com> Date: Fri, 30 Apr 2021 09:57:54 -0400 Subject: [PATCH] Add per-path configurability (#1891) Allows URIs served by the `AdminServlet` to be individually enabled/disabled. --- .../metrics/servlets/AdminServlet.java | 116 ++++++++++++++---- .../servlets/AdminServletExclusionTest.java | 60 +++++++++ .../metrics/servlets/AdminServletUriTest.java | 66 ++++++++++ 3 files changed, 216 insertions(+), 26 deletions(-) create mode 100755 metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletExclusionTest.java create mode 100755 metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletUriTest.java diff --git a/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java index 3fe3e89621..342c72c788 100755 --- a/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java +++ b/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java @@ -1,6 +1,7 @@ package com.codahale.metrics.servlets; import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -16,14 +17,19 @@ public class AdminServlet extends HttpServlet { public static final String DEFAULT_THREADS_URI = "/threads"; public static final String DEFAULT_CPU_PROFILE_URI = "/pprof"; + public static final String METRICS_ENABLED_PARAM_KEY = "metrics-enabled"; public static final String METRICS_URI_PARAM_KEY = "metrics-uri"; + public static final String PING_ENABLED_PARAM_KEY = "ping-enabled"; public static final String PING_URI_PARAM_KEY = "ping-uri"; + public static final String THREADS_ENABLED_PARAM_KEY = "threads-enabled"; public static final String THREADS_URI_PARAM_KEY = "threads-uri"; + public static final String HEALTHCHECK_ENABLED_PARAM_KEY = "healthcheck-enabled"; public static final String HEALTHCHECK_URI_PARAM_KEY = "healthcheck-uri"; public static final String SERVICE_NAME_PARAM_KEY = "service-name"; + public static final String CPU_PROFILE_ENABLED_PARAM_KEY = "cpu-profile-enabled"; public static final String CPU_PROFILE_URI_PARAM_KEY = "cpu-profile-uri"; - private static final String TEMPLATE = String.format( + private static final String BASE_TEMPLATE = "%n" + "%n" + @@ -33,16 +39,18 @@ public class AdminServlet extends HttpServlet { "%n" + "

Operational Menu{10}

%n" + " %n" + "%n" + - "" - ); + ""; + private static final String METRICS_LINK = "
  • Metrics
  • %n"; + private static final String PING_LINK = "
  • Ping
  • %n" ; + private static final String THREADS_LINK = "
  • Threads
  • %n" ; + private static final String HEALTHCHECK_LINK = "
  • Healthcheck
  • %n" ; + private static final String CPU_PROFILE_LINK = "
  • CPU Profile
  • %n" + + "
  • CPU Contention
  • %n"; + + private static final String CONTENT_TYPE = "text/html"; private static final long serialVersionUID = -2850794040708785318L; @@ -51,38 +59,74 @@ public class AdminServlet extends HttpServlet { private transient PingServlet pingServlet; private transient ThreadDumpServlet threadDumpServlet; private transient CpuProfileServlet cpuProfileServlet; + private transient boolean metricsEnabled; private transient String metricsUri; + private transient boolean pingEnabled; private transient String pingUri; + private transient boolean threadsEnabled; private transient String threadsUri; + private transient boolean healthcheckEnabled; private transient String healthcheckUri; - private transient String cpuprofileUri; + private transient boolean cpuProfileEnabled; + private transient String cpuProfileUri; private transient String serviceName; + private transient String pageContentTemplate; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - this.healthCheckServlet = new HealthCheckServlet(); - healthCheckServlet.init(config); + final ServletContext context = config.getServletContext(); + final StringBuilder servletLinks = new StringBuilder(); + this.metricsEnabled = + Boolean.parseBoolean(getParam(context.getInitParameter(METRICS_ENABLED_PARAM_KEY), "true")); + if (this.metricsEnabled) { + servletLinks.append(METRICS_LINK); + } this.metricsServlet = new MetricsServlet(); metricsServlet.init(config); + this.pingEnabled = + Boolean.parseBoolean(getParam(context.getInitParameter(PING_ENABLED_PARAM_KEY), "true")); + if (this.pingEnabled) { + servletLinks.append(PING_LINK); + } this.pingServlet = new PingServlet(); pingServlet.init(config); + this.threadsEnabled = + Boolean.parseBoolean(getParam(context.getInitParameter(THREADS_ENABLED_PARAM_KEY), "true")); + if (this.threadsEnabled) { + servletLinks.append(THREADS_LINK); + } this.threadDumpServlet = new ThreadDumpServlet(); threadDumpServlet.init(config); + this.healthcheckEnabled = + Boolean.parseBoolean(getParam(context.getInitParameter(HEALTHCHECK_ENABLED_PARAM_KEY), "true")); + if (this.healthcheckEnabled) { + servletLinks.append(HEALTHCHECK_LINK); + } + this.healthCheckServlet = new HealthCheckServlet(); + healthCheckServlet.init(config); + + this.cpuProfileEnabled = + Boolean.parseBoolean(getParam(context.getInitParameter(CPU_PROFILE_ENABLED_PARAM_KEY), "true")); + if (this.cpuProfileEnabled) { + servletLinks.append(CPU_PROFILE_LINK); + } this.cpuProfileServlet = new CpuProfileServlet(); cpuProfileServlet.init(config); - this.metricsUri = getParam(config.getInitParameter(METRICS_URI_PARAM_KEY), DEFAULT_METRICS_URI); - this.pingUri = getParam(config.getInitParameter(PING_URI_PARAM_KEY), DEFAULT_PING_URI); - this.threadsUri = getParam(config.getInitParameter(THREADS_URI_PARAM_KEY), DEFAULT_THREADS_URI); - this.healthcheckUri = getParam(config.getInitParameter(HEALTHCHECK_URI_PARAM_KEY), DEFAULT_HEALTHCHECK_URI); - this.cpuprofileUri = getParam(config.getInitParameter(CPU_PROFILE_URI_PARAM_KEY), DEFAULT_CPU_PROFILE_URI); - this.serviceName = getParam(config.getInitParameter(SERVICE_NAME_PARAM_KEY), null); + pageContentTemplate = String.format(BASE_TEMPLATE, String.format(servletLinks.toString())); + + this.metricsUri = getParam(context.getInitParameter(METRICS_URI_PARAM_KEY), DEFAULT_METRICS_URI); + this.pingUri = getParam(context.getInitParameter(PING_URI_PARAM_KEY), DEFAULT_PING_URI); + this.threadsUri = getParam(context.getInitParameter(THREADS_URI_PARAM_KEY), DEFAULT_THREADS_URI); + this.healthcheckUri = getParam(context.getInitParameter(HEALTHCHECK_URI_PARAM_KEY), DEFAULT_HEALTHCHECK_URI); + this.cpuProfileUri = getParam(context.getInitParameter(CPU_PROFILE_URI_PARAM_KEY), DEFAULT_CPU_PROFILE_URI); + this.serviceName = getParam(context.getInitParameter(SERVICE_NAME_PARAM_KEY), null); } @Override @@ -93,8 +137,8 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); resp.setContentType(CONTENT_TYPE); try (PrintWriter writer = resp.getWriter()) { - writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, - threadsUri, path, healthcheckUri, path, cpuprofileUri, + writer.println(MessageFormat.format(pageContentTemplate, path, metricsUri, path, pingUri, path, + threadsUri, path, healthcheckUri, path, cpuProfileUri, serviceName == null ? "" : " (" + serviceName + ")")); } } @@ -105,15 +149,35 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws if (uri == null || uri.equals("/")) { super.service(req, resp); } else if (uri.equals(healthcheckUri)) { - healthCheckServlet.service(req, resp); + if (healthcheckEnabled) { + healthCheckServlet.service(req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } } else if (uri.startsWith(metricsUri)) { - metricsServlet.service(req, resp); + if (metricsEnabled) { + metricsServlet.service(req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } } else if (uri.equals(pingUri)) { - pingServlet.service(req, resp); + if (pingEnabled) { + pingServlet.service(req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } } else if (uri.equals(threadsUri)) { - threadDumpServlet.service(req, resp); - } else if (uri.equals(cpuprofileUri)) { - cpuProfileServlet.service(req, resp); + if (threadsEnabled) { + threadDumpServlet.service(req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } else if (uri.equals(cpuProfileUri)) { + if (cpuProfileEnabled) { + cpuProfileServlet.service(req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } } else { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } diff --git a/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletExclusionTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletExclusionTest.java new file mode 100755 index 0000000000..5fafab8cc9 --- /dev/null +++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletExclusionTest.java @@ -0,0 +1,60 @@ +package com.codahale.metrics.servlets; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.health.HealthCheckRegistry; +import static org.assertj.core.api.Assertions.assertThat; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.servlet.ServletTester; +import org.junit.Before; +import org.junit.Test; + +public class AdminServletExclusionTest extends AbstractServletTest { + private final MetricRegistry registry = new MetricRegistry(); + private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + + @Override + protected void setUp(ServletTester tester) { + tester.setContextPath("/context"); + + tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry); + tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry); + tester.setInitParameter("threads-enabled", "false"); + tester.setInitParameter("cpu-profile-enabled", "false"); + tester.addServlet(AdminServlet.class, "/admin"); + } + + @Before + public void setUp() { + request.setMethod("GET"); + request.setURI("/context/admin"); + request.setVersion("HTTP/1.0"); + } + + @Test + public void returnsA200() throws Exception { + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo(String.format( + "%n" + + "%n" + + "%n" + + " Metrics%n" + + "%n" + + "%n" + + "

    Operational Menu

    %n" + + " %n" + + "%n" + + "%n" + )); + assertThat(response.get(HttpHeader.CONTENT_TYPE)) + .isEqualTo("text/html;charset=UTF-8"); + } +} diff --git a/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletUriTest.java b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletUriTest.java new file mode 100755 index 0000000000..b97530e237 --- /dev/null +++ b/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletUriTest.java @@ -0,0 +1,66 @@ +package com.codahale.metrics.servlets; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.health.HealthCheckRegistry; +import static org.assertj.core.api.Assertions.assertThat; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.servlet.ServletTester; +import org.junit.Before; +import org.junit.Test; + +public class AdminServletUriTest extends AbstractServletTest { + private final MetricRegistry registry = new MetricRegistry(); + private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + + @Override + protected void setUp(ServletTester tester) { + tester.setContextPath("/context"); + + tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry); + tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry); + tester.setInitParameter("metrics-uri", "/metrics-test"); + tester.setInitParameter("ping-uri", "/ping-test"); + tester.setInitParameter("threads-uri", "/threads-test"); + tester.setInitParameter("healthcheck-uri", "/healthcheck-test"); + tester.setInitParameter("cpu-profile-uri", "/pprof-test"); + tester.addServlet(AdminServlet.class, "/admin"); + } + + @Before + public void setUp() { + request.setMethod("GET"); + request.setURI("/context/admin"); + request.setVersion("HTTP/1.0"); + } + + @Test + public void returnsA200() throws Exception { + processRequest(); + + assertThat(response.getStatus()) + .isEqualTo(200); + assertThat(response.getContent()) + .isEqualTo(String.format( + "%n" + + "%n" + + "%n" + + " Metrics%n" + + "%n" + + "%n" + + "

    Operational Menu

    %n" + + " %n" + + "%n" + + "%n" + )); + assertThat(response.get(HttpHeader.CONTENT_TYPE)) + .isEqualTo("text/html;charset=UTF-8"); + } +}