Merge branch 'stable-2.15' into stable-2.16

* stable-2.15:
  Add option to exclude metrics

Change-Id: I54c39fbff22495155aa48daeb4e518c6f817aea8
diff --git a/BUILD b/BUILD
index 89cb6c3..ee7c4fb 100644
--- a/BUILD
+++ b/BUILD
@@ -5,8 +5,8 @@
     srcs = glob(["src/main/java/**/*.java"]),
     manifest_entries = [
         "Gerrit-PluginName: metrics-reporter-prometheus",
-        "Gerrit-Module: com.googlesource.gerrit.plugins.metricsreporters.GerritPrometheusModule",
-        "Gerrit-HttpModule: com.googlesource.gerrit.plugins.metricsreporters.GerritPrometheusHttpModule",
+        "Gerrit-Module: com.googlesource.gerrit.plugins.metricsreporterprometheus.GerritPrometheusModule",
+        "Gerrit-HttpModule: com.googlesource.gerrit.plugins.metricsreporterprometheus.GerritPrometheusHttpModule",
     ],
     resources = glob(["src/main/resources/**/*"]),
     deps = [
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/CapabilityChecker.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/CapabilityChecker.java
similarity index 94%
rename from src/main/java/com/googlesource/gerrit/plugins/metricsreporters/CapabilityChecker.java
rename to src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/CapabilityChecker.java
index ae7bc93..f1b936e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/CapabilityChecker.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/CapabilityChecker.java
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.googlesource.gerrit.plugins.metricsreporters;
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.annotations.PluginName;
@@ -44,7 +44,7 @@
   public boolean canViewMetrics() {
     try {
       permissionBackend
-          .user(userProvider)
+          .user(userProvider.get())
           .checkAny(
               ImmutableSet.of(
                   GlobalPermission.ADMINISTRATE_SERVER,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/FilteredMetricRegistry.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/FilteredMetricRegistry.java
new file mode 100644
index 0000000..87cdcbd
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/FilteredMetricRegistry.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import java.util.SortedMap;
+import java.util.function.Predicate;
+
+public class FilteredMetricRegistry extends MetricRegistry {
+  private final MetricRegistry registry;
+  private final MetricFilter nonExcluded;
+
+  FilteredMetricRegistry(MetricRegistry registry, Predicate<String> exclusionFilter) {
+    this.registry = registry;
+    this.nonExcluded = (n, m) -> !exclusionFilter.test(n);
+  }
+
+  @Override
+  @SuppressWarnings("rawtypes")
+  public SortedMap<String, Gauge> getGauges() {
+    return registry.getGauges(nonExcluded);
+  }
+
+  @Override
+  public SortedMap<String, Counter> getCounters() {
+    return registry.getCounters(nonExcluded);
+  }
+
+  @Override
+  public SortedMap<String, Histogram> getHistograms() {
+    return registry.getHistograms(nonExcluded);
+  }
+
+  @Override
+  public SortedMap<String, Timer> getTimers() {
+    return registry.getTimers(nonExcluded);
+  }
+
+  @Override
+  public SortedMap<String, Meter> getMeters() {
+    return registry.getMeters(nonExcluded);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritBuildInformationMetric.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritBuildInformationMetric.java
new file mode 100644
index 0000000..0f567b5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritBuildInformationMetric.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
+
+import com.google.gerrit.common.Version;
+import io.prometheus.client.Counter;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+
+public class GerritBuildInformationMetric {
+  static final Counter requests =
+      Counter.build()
+          .name("gerrit_build_info")
+          .help("Gerrit build information.")
+          .labelNames("javaversion", "version", "revision")
+          .register();
+
+  public void compute() {
+    GerritVersionInfo versionInfo = extractVersionComponents();
+    requests.labels(getJavaVersion(), versionInfo.version, versionInfo.revision);
+  }
+
+  private static String getJavaVersion() {
+    RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
+    return String.format("%s(%s)", mxBean.getSpecVersion(), mxBean.getVmVersion());
+  }
+
+  private static GerritVersionInfo extractVersionComponents() {
+    GerritVersionInfo versionInfo = new GerritVersionInfo();
+    String fullVersion = Version.getVersion();
+
+    if (fullVersion == null) {
+      return versionInfo;
+    }
+
+    String[] versionComponents = fullVersion.split("-");
+
+    versionInfo.version = versionComponents[0];
+
+    if (versionComponents.length > 2) {
+      versionInfo.revision = versionComponents.length > 2 ? versionComponents[2].substring(1) : "";
+    } else {
+      versionInfo.revision = "";
+    }
+
+    return versionInfo;
+  }
+
+  private static class GerritVersionInfo {
+    String revision = "unknown";
+    String version = "unknown";
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusExporter.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusExporter.java
new file mode 100644
index 0000000..0dba67a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusExporter.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
+
+import static java.util.stream.Collectors.toList;
+
+import com.codahale.metrics.MetricRegistry;
+import com.google.common.base.Strings;
+import com.google.common.net.HttpHeaders;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.dropwizard.DropwizardExports;
+import io.prometheus.client.exporter.MetricsServlet;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@SuppressWarnings("serial")
+@Singleton
+public class GerritPrometheusExporter extends MetricsServlet {
+  private static final String PROMETHEUS_BEARER_TOKEN = "prometheusBearerToken";
+  private static final String EXCLUDE_KEY = "excludeMetrics";
+
+  private final GerritBuildInformationMetric gerritBuildInfoMetric;
+  private final CapabilityChecker capabilityChecker;
+  private final String prometheusBearerToken;
+
+  @Inject
+  public GerritPrometheusExporter(
+      MetricRegistry registry,
+      CapabilityChecker capabilityChecker,
+      PluginConfigFactory cfgFactory,
+      @PluginName String pluginName) {
+    this.capabilityChecker = capabilityChecker;
+    this.prometheusBearerToken =
+        cfgFactory.getFromGerritConfig(pluginName).getString(PROMETHEUS_BEARER_TOKEN);
+    this.gerritBuildInfoMetric = new GerritBuildInformationMetric();
+
+    List<Pattern> excludes =
+        Arrays.stream(cfgFactory.getFromGerritConfig(pluginName).getStringList(EXCLUDE_KEY))
+            .map(Pattern::compile)
+            .collect(toList());
+
+    FilteredMetricRegistry filteredRegistry =
+        new FilteredMetricRegistry(
+            registry, s -> excludes.stream().anyMatch(e -> e.matcher(s).matches()));
+
+    // Hook the Dropwizard registry into the Prometheus registry
+    // via the DropwizardExports collector.
+    CollectorRegistry.defaultRegistry.register(new DropwizardExports(filteredRegistry));
+  }
+
+  @Override
+  public void service(ServletRequest req, ServletResponse res)
+      throws ServletException, IOException {
+    if (capabilityChecker.canViewMetrics() || canExportUsingPrometheusBearerToken(req)) {
+      gerritBuildInfoMetric.compute();
+      super.service(req, res);
+    } else {
+      HttpServletResponse httpResponse = (HttpServletResponse) res;
+      httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
+    }
+  }
+
+  private boolean canExportUsingPrometheusBearerToken(ServletRequest req) {
+    if (Strings.isNullOrEmpty(prometheusBearerToken)) {
+      return false;
+    }
+
+    HttpServletRequest httpRequest = (HttpServletRequest) req;
+    return Optional.ofNullable(httpRequest.getHeader(HttpHeaders.AUTHORIZATION))
+        .map(h -> h.equals("Bearer " + prometheusBearerToken))
+        .orElse(false);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusHttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusHttpModule.java
similarity index 92%
rename from src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusHttpModule.java
rename to src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusHttpModule.java
index 5764c38..0a2a625 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusHttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusHttpModule.java
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.googlesource.gerrit.plugins.metricsreporters;
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
 
 import com.google.inject.servlet.ServletModule;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusModule.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusModule.java
similarity index 93%
rename from src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusModule.java
rename to src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusModule.java
index 36c8663..591e81e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/GerritPrometheusModule.java
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.googlesource.gerrit.plugins.metricsreporters;
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
 
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.config.CapabilityDefinition;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/ViewMetricsCapability.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/ViewMetricsCapability.java
similarity index 92%
rename from src/main/java/com/googlesource/gerrit/plugins/metricsreporters/ViewMetricsCapability.java
rename to src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/ViewMetricsCapability.java
index c3d22e0..8bb5157 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/ViewMetricsCapability.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/metricsreporterprometheus/ViewMetricsCapability.java
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.googlesource.gerrit.plugins.metricsreporters;
+package com.googlesource.gerrit.plugins.metricsreporterprometheus;
 
 import com.google.gerrit.extensions.config.CapabilityDefinition;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusExporter.java b/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusExporter.java
deleted file mode 100644
index 217badb..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/metricsreporters/GerritPrometheusExporter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.googlesource.gerrit.plugins.metricsreporters;
-
-import com.codahale.metrics.Metric;
-import com.codahale.metrics.MetricFilter;
-import com.codahale.metrics.MetricRegistry;
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.PluginConfigFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import io.prometheus.client.CollectorRegistry;
-import io.prometheus.client.dropwizard.DropwizardExports;
-import io.prometheus.client.exporter.MetricsServlet;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletResponse;
-
-@SuppressWarnings("serial")
-@Singleton
-public class GerritPrometheusExporter extends MetricsServlet {
-  CapabilityChecker capabilityChecker;
-  private static final String EXCLUDE_KEY = "excludeMetrics";
-
-  @Inject
-  public GerritPrometheusExporter(
-      MetricRegistry registry,
-      CapabilityChecker capabilityChecker,
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName) {
-    this.capabilityChecker = capabilityChecker;
-
-    /* Copy the registry to avoid filtering the global one */
-    MetricRegistry filteredRegistry = new MetricRegistry();
-    filteredRegistry.registerAll(registry);
-
-    Set<String> excludedMetrics = new HashSet<>();
-    excludedMetrics.addAll(
-        Arrays.asList(cfgFactory.getFromGerritConfig(pluginName).getStringList(EXCLUDE_KEY)));
-
-    excludedMetrics.forEach(
-        exclude -> {
-          filteredRegistry.removeMatching(
-              new MetricFilter() {
-                @Override
-                public boolean matches(String name, Metric metric) {
-                  return name.matches(exclude);
-                }
-              });
-        });
-
-    // Hook the Dropwizard registry into the Prometheus registry
-    // via the DropwizardExports collector.
-    CollectorRegistry.defaultRegistry.register(new DropwizardExports(filteredRegistry));
-  }
-
-  @Override
-  public void service(ServletRequest req, ServletResponse res)
-      throws ServletException, IOException {
-    if (capabilityChecker.canViewMetrics()) {
-      super.service(req, res);
-    } else {
-      HttpServletResponse httpResponse = (HttpServletResponse) res;
-      httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
-    }
-  }
-};
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 0092de0..96f347e 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -4,4 +4,5 @@
 
 To access the monitoring URL, a user must be a member of a group that is granted
 the ‘View Metrics’ capability (provided by this plugin) or the ‘Administrate
-Server’ capability.
\ No newline at end of file
+Server’ capability. Alternatively, authentication using prometheus bearer token
+is also supported.
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index df13ebc..3550c50 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -26,7 +26,7 @@
 The output is created in
 
 ```
-  bazel-genfiles/plugins/@PLUGIN@/@PLUGIN@.jar
+  bazel-bin/plugins/@PLUGIN@/@PLUGIN@.jar
 ```
 
 This project can be imported into the Eclipse IDE.
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 265a6c7..070151f 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -1,9 +1,13 @@
 Configuration
 =============
 
+The configuration of the @PLUGIN@ plugin is done in the `gerrit.config`
+file.
+
 To access the monitoring URL, a user must be a member of a group that is granted
 the ‘View Metrics’ capability (provided by this plugin) or the ‘Administrate
-Server’ capability.This plugin requires no configuration.
+Server’ capability. Alternatively, authentication using prometheus bearer token
+is also supported.
 
 This capability can be configured in the 'Global Capabilities' section of the
 ['All-Projects'](@URL@#/admin/projects/All-Projects,access) access right.
@@ -11,12 +15,22 @@
 It is possible to allow anonymous access to the metrics by giving the capability
 to the 'Anonymous Users' group.
 
+plugin.@PLUGIN@.prometheusBearerToken
+:	Bearer token for allowing Prometheus to query Gerrit metrics through its scraper.
+	Defaults to undefine.
+
+When defined, access to the plugins/@PLUGIN@/metrics URL does not require any
+authentication and do not check any ACL related to the ‘View Metrics’ global capability.
+See [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration)
+for how to configure the integration with Prometheus.
+
 plugin.@PLUGIN@.excludeMetrics
-:   String used to exclude metrics from the report. It can be specified multiple times.
-    Parsed as regular expression. Note, ^ and $ are automatically added around the string.
+:   Regex pattern used to exclude metrics from the report. It can be specified multiple times.
+    Note that pattern matching is done on the whole metric name, not only on a part of it.
     By default no metric is excluded.
     For example, to exclude all cache metrics, use: `excludeMetrics = cache.*`
 
+
 [Back to @PLUGIN@ documentation index][index]
 
-[index]: index.html
\ No newline at end of file
+[index]: index.html