From e4d838020277f37b2813d9fa26ab2b3b801e088d Mon Sep 17 00:00:00 2001 From: Paul Ambrose Date: Sun, 24 Dec 2017 08:37:03 -0800 Subject: [PATCH] Convert to Kotlin --- .gitignore | 94 +-- Makefile | 2 +- README.md | 18 +- bin/docker-agent.sh | 2 +- bin/docker-proxy.sh | 2 +- etc/compose/proxy.yml | 5 +- etc/config/config.conf | 4 +- etc/docker/agent.df | 2 +- pom.xml | 155 +++-- src/main/java/io/prometheus/Agent.java | 595 ------------------ src/main/java/io/prometheus/Agent.kt | 511 +++++++++++++++ src/main/java/io/prometheus/Proxy.java | 269 -------- src/main/java/io/prometheus/Proxy.kt | 234 +++++++ .../agent/AgentClientInterceptor.java | 75 --- .../agent/AgentClientInterceptor.kt | 58 ++ .../io/prometheus/agent/AgentMetrics.java | 56 -- .../java/io/prometheus/agent/AgentMetrics.kt | 61 ++ .../io/prometheus/agent/AgentOptions.java | 72 --- .../java/io/prometheus/agent/AgentOptions.kt | 61 ++ .../java/io/prometheus/agent/PathContext.java | 77 --- .../java/io/prometheus/agent/PathContext.kt | 58 ++ ...eption.java => RequestFailureException.kt} | 14 +- .../io/prometheus/common/AdminConfig.java | 60 -- .../java/io/prometheus/common/AdminConfig.kt | 45 ++ .../io/prometheus/common/AdminService.java | 93 --- .../java/io/prometheus/common/AdminService.kt | 69 ++ .../io/prometheus/common/BaseOptions.java | 151 ----- .../java/io/prometheus/common/BaseOptions.kt | 205 ++++++ .../java/io/prometheus/common/ConfigVals.java | 24 +- .../java/io/prometheus/common/EnvVars.java | 53 -- src/main/java/io/prometheus/common/EnvVars.kt | 47 ++ .../io/prometheus/common/GenericService.java | 221 ------- .../io/prometheus/common/GenericService.kt | 177 ++++++ .../common/GenericServiceListener.java | 63 -- .../common/GenericServiceListener.kt | 52 ++ .../common/InstrumentedThreadFactory.java | 93 --- .../common/InstrumentedThreadFactory.kt | 80 +++ .../io/prometheus/common/MetricsConfig.java | 72 --- .../io/prometheus/common/MetricsConfig.kt | 54 ++ .../io/prometheus/common/MetricsService.java | 78 --- .../io/prometheus/common/MetricsService.kt | 58 ++ .../io/prometheus/common/SamplerGauge.java | 49 -- .../java/io/prometheus/common/SamplerGauge.kt | 35 ++ .../io/prometheus/common/SystemMetrics.java | 55 -- .../io/prometheus/common/SystemMetrics.kt | 48 ++ src/main/java/io/prometheus/common/Utils.java | 238 ------- src/main/java/io/prometheus/common/Utils.kt | 143 +++++ .../io/prometheus/common/VersionServlet.java | 42 -- .../io/prometheus/common/VersionServlet.kt | 40 ++ .../io/prometheus/common/ZipkinConfig.java | 50 -- .../java/io/prometheus/common/ZipkinConfig.kt | 40 ++ .../common/ZipkinReporterService.java | 81 --- .../common/ZipkinReporterService.kt | 53 ++ src/main/java/io/prometheus/package-info.java | 2 +- .../io/prometheus/proxy/AgentContext.java | 100 --- .../java/io/prometheus/proxy/AgentContext.kt | 87 +++ .../proxy/AgentContextCleanupService.java | 67 -- .../proxy/AgentContextCleanupService.kt | 61 ++ .../io/prometheus/proxy/ProxyGrpcService.java | 124 ---- .../io/prometheus/proxy/ProxyGrpcService.kt | 117 ++++ .../io/prometheus/proxy/ProxyHttpService.java | 220 ------- .../io/prometheus/proxy/ProxyHttpService.kt | 190 ++++++ .../io/prometheus/proxy/ProxyInterceptor.java | 58 -- .../io/prometheus/proxy/ProxyInterceptor.kt | 48 ++ .../io/prometheus/proxy/ProxyMetrics.java | 77 --- .../java/io/prometheus/proxy/ProxyMetrics.kt | 83 +++ .../io/prometheus/proxy/ProxyOptions.java | 66 -- .../java/io/prometheus/proxy/ProxyOptions.kt | 52 ++ .../io/prometheus/proxy/ProxyServiceImpl.java | 223 ------- .../io/prometheus/proxy/ProxyServiceImpl.kt | 190 ++++++ .../proxy/ProxyTransportFilter.java | 75 --- .../prometheus/proxy/ProxyTransportFilter.kt | 56 ++ .../proxy/ScrapeRequestWrapper.java | 113 ---- .../prometheus/proxy/ScrapeRequestWrapper.kt | 89 +++ .../io/prometheus/AdminDefaultPathTest.java | 147 ----- .../io/prometheus/AdminDefaultPathTest.kt | 128 ++++ .../io/prometheus/AdminEmptyPathTest.java | 116 ---- .../java/io/prometheus/AdminEmptyPathTest.kt | 97 +++ .../prometheus/AdminNonDefaultPathTest.java | 119 ---- .../io/prometheus/AdminNonDefaultPathTest.kt | 107 ++++ .../java/io/prometheus/AutoValueTest.java | 109 ---- src/test/java/io/prometheus/DataClassTest.kt | 107 ++++ .../InProcessTestNoAdminMetricsTest.java | 101 --- .../InProcessTestNoAdminMetricsTest.kt | 87 +++ .../InProcessTestWithAdminMetricsTest.java | 95 --- .../InProcessTestWithAdminMetricsTest.kt | 83 +++ src/test/java/io/prometheus/MiscTests.kt | 240 +++++++ .../NettyTestNoAdminMetricsTest.java | 95 --- .../prometheus/NettyTestNoAdminMetricsTest.kt | 84 +++ .../NettyTestWithAdminMetricsTest.java | 99 --- .../NettyTestWithAdminMetricsTest.kt | 89 +++ src/test/java/io/prometheus/OptionsTest.java | 93 --- src/test/java/io/prometheus/OptionsTest.kt | 94 +++ .../java/io/prometheus/TestConstants.java | 36 -- src/test/java/io/prometheus/TestConstants.kt | 30 + src/test/java/io/prometheus/TestUtils.java | 72 --- src/test/java/io/prometheus/TestUtils.kt | 71 +++ src/test/java/io/prometheus/Tests.java | 265 -------- 98 files changed, 4448 insertions(+), 5208 deletions(-) delete mode 100644 src/main/java/io/prometheus/Agent.java create mode 100644 src/main/java/io/prometheus/Agent.kt delete mode 100644 src/main/java/io/prometheus/Proxy.java create mode 100644 src/main/java/io/prometheus/Proxy.kt delete mode 100644 src/main/java/io/prometheus/agent/AgentClientInterceptor.java create mode 100644 src/main/java/io/prometheus/agent/AgentClientInterceptor.kt delete mode 100644 src/main/java/io/prometheus/agent/AgentMetrics.java create mode 100644 src/main/java/io/prometheus/agent/AgentMetrics.kt delete mode 100644 src/main/java/io/prometheus/agent/AgentOptions.java create mode 100644 src/main/java/io/prometheus/agent/AgentOptions.kt delete mode 100644 src/main/java/io/prometheus/agent/PathContext.java create mode 100644 src/main/java/io/prometheus/agent/PathContext.kt rename src/main/java/io/prometheus/agent/{RequestFailureException.java => RequestFailureException.kt} (72%) delete mode 100644 src/main/java/io/prometheus/common/AdminConfig.java create mode 100644 src/main/java/io/prometheus/common/AdminConfig.kt delete mode 100644 src/main/java/io/prometheus/common/AdminService.java create mode 100644 src/main/java/io/prometheus/common/AdminService.kt delete mode 100644 src/main/java/io/prometheus/common/BaseOptions.java create mode 100644 src/main/java/io/prometheus/common/BaseOptions.kt delete mode 100644 src/main/java/io/prometheus/common/EnvVars.java create mode 100644 src/main/java/io/prometheus/common/EnvVars.kt delete mode 100644 src/main/java/io/prometheus/common/GenericService.java create mode 100644 src/main/java/io/prometheus/common/GenericService.kt delete mode 100644 src/main/java/io/prometheus/common/GenericServiceListener.java create mode 100644 src/main/java/io/prometheus/common/GenericServiceListener.kt delete mode 100644 src/main/java/io/prometheus/common/InstrumentedThreadFactory.java create mode 100644 src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt delete mode 100644 src/main/java/io/prometheus/common/MetricsConfig.java create mode 100644 src/main/java/io/prometheus/common/MetricsConfig.kt delete mode 100644 src/main/java/io/prometheus/common/MetricsService.java create mode 100644 src/main/java/io/prometheus/common/MetricsService.kt delete mode 100644 src/main/java/io/prometheus/common/SamplerGauge.java create mode 100644 src/main/java/io/prometheus/common/SamplerGauge.kt delete mode 100644 src/main/java/io/prometheus/common/SystemMetrics.java create mode 100644 src/main/java/io/prometheus/common/SystemMetrics.kt delete mode 100644 src/main/java/io/prometheus/common/Utils.java create mode 100644 src/main/java/io/prometheus/common/Utils.kt delete mode 100644 src/main/java/io/prometheus/common/VersionServlet.java create mode 100644 src/main/java/io/prometheus/common/VersionServlet.kt delete mode 100644 src/main/java/io/prometheus/common/ZipkinConfig.java create mode 100644 src/main/java/io/prometheus/common/ZipkinConfig.kt delete mode 100644 src/main/java/io/prometheus/common/ZipkinReporterService.java create mode 100644 src/main/java/io/prometheus/common/ZipkinReporterService.kt delete mode 100644 src/main/java/io/prometheus/proxy/AgentContext.java create mode 100644 src/main/java/io/prometheus/proxy/AgentContext.kt delete mode 100644 src/main/java/io/prometheus/proxy/AgentContextCleanupService.java create mode 100644 src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyGrpcService.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyGrpcService.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyHttpService.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyHttpService.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyInterceptor.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyInterceptor.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyMetrics.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyMetrics.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyOptions.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyOptions.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyServiceImpl.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt delete mode 100644 src/main/java/io/prometheus/proxy/ProxyTransportFilter.java create mode 100644 src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt delete mode 100644 src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.java create mode 100644 src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt delete mode 100644 src/test/java/io/prometheus/AdminDefaultPathTest.java create mode 100644 src/test/java/io/prometheus/AdminDefaultPathTest.kt delete mode 100644 src/test/java/io/prometheus/AdminEmptyPathTest.java create mode 100644 src/test/java/io/prometheus/AdminEmptyPathTest.kt delete mode 100644 src/test/java/io/prometheus/AdminNonDefaultPathTest.java create mode 100644 src/test/java/io/prometheus/AdminNonDefaultPathTest.kt delete mode 100644 src/test/java/io/prometheus/AutoValueTest.java create mode 100644 src/test/java/io/prometheus/DataClassTest.kt delete mode 100644 src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java create mode 100644 src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt delete mode 100644 src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java create mode 100644 src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt create mode 100644 src/test/java/io/prometheus/MiscTests.kt delete mode 100644 src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java create mode 100644 src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt delete mode 100644 src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java create mode 100644 src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt delete mode 100644 src/test/java/io/prometheus/OptionsTest.java create mode 100644 src/test/java/io/prometheus/OptionsTest.kt delete mode 100644 src/test/java/io/prometheus/TestConstants.java create mode 100644 src/test/java/io/prometheus/TestConstants.kt delete mode 100644 src/test/java/io/prometheus/TestUtils.java create mode 100644 src/test/java/io/prometheus/TestUtils.kt delete mode 100644 src/test/java/io/prometheus/Tests.java diff --git a/.gitignore b/.gitignore index 51f36bb8..cf9a0c05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,94 +1,6 @@ -.idea -prometheus-proxy.iml - -.wercker - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder +.idea/ +.wercker/ target/ -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject +*.iml -# Rope project settings -.ropeproject diff --git a/Makefile b/Makefile index 0c1cecfb..8093be80 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=1.2.5 +VERSION=1.3.0 default: build diff --git a/README.md b/README.md index 9200cc8b..e500a15c 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,8 @@ scrape_configs: The docker images are available via: ```bash -$ docker pull pambrose/prometheus-proxy:1.2.5 -$ docker pull pambrose/prometheus-agent:1.2.5 +$ docker pull pambrose/prometheus-proxy:1.3.0 +$ docker pull pambrose/prometheus-agent:1.3.0 ``` Start the proxy and an agent in separate shells on your local machine: @@ -94,14 +94,14 @@ Start the proxy and an agent in separate shells on your local machine: $ docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ -e HOSTNAME=${HOSTNAME} \ -e METRICS_ENABLED=true \ - pambrose/prometheus-proxy:1.2.5 + pambrose/prometheus-proxy:1.3.0 ``` ```bash $ docker run --rm -p 8083:8083 -p 8093:8093 \ -e HOSTNAME=${HOSTNAME} \ -e AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-agent:1.2.5 + pambrose/prometheus-agent:1.3.0 ``` Using the config file [simple.conf](https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf), @@ -180,4 +180,12 @@ The path names can be changed in the configuration file. To disable an admin ser * [Prometheus.io](http://prometheus.io) * [gRPC](http://grpc.io) -* [Typesafe Config](https://github.com/typesafehub/config) \ No newline at end of file +* [Typesafe Config](https://github.com/typesafehub/config) +* [Zipkin]() + +## Zipkin + +* Run a Zipkin server with: `docker run -d -p 9411:9411 openzipkin/zipkin` +* View Zipkin info at http://localhost:9411 + +Details on the Zipkin container are [here](https://github.com/openzipkin/docker-zipkin). \ No newline at end of file diff --git a/bin/docker-agent.sh b/bin/docker-agent.sh index 51e80d48..eb473628 100755 --- a/bin/docker-agent.sh +++ b/bin/docker-agent.sh @@ -3,4 +3,4 @@ docker run --rm -p 8083:8083 -p 8093:8093 \ -e HOSTNAME=${HOSTNAME} \ -e AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-agent:1.2.5 \ No newline at end of file + pambrose/prometheus-agent:1.3.0 \ No newline at end of file diff --git a/bin/docker-proxy.sh b/bin/docker-proxy.sh index c0557a15..c72e883b 100755 --- a/bin/docker-proxy.sh +++ b/bin/docker-proxy.sh @@ -3,4 +3,4 @@ docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ -e HOSTNAME=${HOSTNAME} \ -e PROXY_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-proxy:1.2.5 \ No newline at end of file + pambrose/prometheus-proxy:1.3.0 \ No newline at end of file diff --git a/etc/compose/proxy.yml b/etc/compose/proxy.yml index b19b3718..da5cc9f3 100644 --- a/etc/compose/proxy.yml +++ b/etc/compose/proxy.yml @@ -1,14 +1,13 @@ prometheus-proxy: autoredeploy: true - image: 'pambrose/prometheus-proxy:1.2.5' + image: 'pambrose/prometheus-proxy:1.3.0' ports: - '8080:8080' - '8082:8082' - '8092:8092' - '50051:50051' environment: - - PROXY_CONFIG=https://dl.dropboxusercontent.com/u/481551/prometheus/cloud-proxy.conf - + - PROXY_CONFIG=https://raw.githubusercontent.com/pambrose/config-data/master/prometheus-proxy/cloud-proxy.conf prometheus-test: autoredeploy: true diff --git a/etc/config/config.conf b/etc/config/config.conf index 9ff46b43..dfac8788 100644 --- a/etc/config/config.conf +++ b/etc/config/config.conf @@ -54,7 +54,7 @@ proxy { enabled = false hostname = "localhost" port = 9411 - path = "api/v1/spans" + path = "api/v2/spans" serviceName = "prometheus-proxy" grpcReportingEnabled = false } @@ -124,7 +124,7 @@ agent { enabled = false hostname = "localhost" port = 9411 - path = "api/v1/spans" + path = "api/v2/spans" serviceName = "prometheus-agent" grpcReportingEnabled = false } diff --git a/etc/docker/agent.df b/etc/docker/agent.df index 1461dc79..bf7d441c 100644 --- a/etc/docker/agent.df +++ b/etc/docker/agent.df @@ -9,6 +9,6 @@ RUN ./mvnw -DskipTests=true clean package && \ rm -rf /root/.m2 .mvn etc src target/*.jar target/protoc* target/maven* target/generated* target/archive* target/test-classes EXPOSE 8083 -expose 8093 +EXPOSE 8093 ENTRYPOINT ["/prometheus-proxy/target/bin/agent.sh"] diff --git a/pom.xml b/pom.xml index e238c37b..c6c60a6d 100644 --- a/pom.xml +++ b/pom.xml @@ -22,10 +22,10 @@ io.prometheus prometheus-proxy - 1.2.5-SNAPSHOT + 1.3.0-SNAPSHOT - + 1.2.10 0.1.0 1.72 1.3.2 @@ -37,11 +37,11 @@ 2.7.1 9.4.6.v20170531 3.9.1 - 1.5.2 0.3.0 - 4.0.0-alpha4 - 4.11.0 - 1.1.2 + 4.0.0-alpha5 + + 4.13.1 + 2.2.2 1.2.3 1.7.25 @@ -61,7 +61,7 @@ UTF-8 - **/grpc/*.java, **/AutoValue_* + **/grpc/*.java java @@ -179,20 +179,18 @@ - com.squareup.okhttp3 - okhttp - ${okhttp.version} + io.grpc + grpc-all + ${grpc.version} - - - io.grpc - grpc-all - ${grpc.version} - - - - - com.google.auto.value - auto-value - ${auto.version} - provided - - - - io.zipkin.brave - brave - ${brave.version} - - - - io.zipkin.brave - brave-sparkjava - ${brave.version} + io.zipkin.reporter2 + zipkin-sender-okhttp3 + ${zipkin-sender.version} - io.zipkin.brave - brave-okhttp + brave-instrumentation-grpc ${brave.version} - - io.zipkin.reporter - zipkin-sender-okhttp3 - ${zipkin-reporter.version} - - ch.qos.logback logback-core @@ -277,6 +247,17 @@ ${assertj.version} test + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin.version} + test + @@ -315,20 +296,11 @@ - - org.apache.maven.plugins - maven-compiler-plugin - ${maven.compiler.version} - - 1.8 - 1.8 - - org.apache.maven.plugins maven-enforcer-plugin - 1.4.1 + 3.0.0-M1 enforce-maven @@ -346,6 +318,72 @@ + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + src/main/java + target/generated-sources/protobuf/java + target/generated-sources/annotations + + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + + default-compile + none + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + 1.8 + 1.8 + + + maven-jar-plugin ${maven-jar-plugin.version} @@ -590,7 +628,6 @@ **/grpc/*.class **/ConfigVals*.class **/ZipkinReporter*.class - **/AutoValue_*.class diff --git a/src/main/java/io/prometheus/Agent.java b/src/main/java/io/prometheus/Agent.java deleted file mode 100644 index c8bdebf3..00000000 --- a/src/main/java/io/prometheus/Agent.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import com.google.common.base.MoreObjects; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.google.protobuf.Empty; -import io.grpc.ClientInterceptor; -import io.grpc.ManagedChannel; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.grpc.inprocess.InProcessChannelBuilder; -import io.grpc.netty.NettyChannelBuilder; -import io.grpc.stub.StreamObserver; -import io.prometheus.agent.AgentClientInterceptor; -import io.prometheus.agent.AgentMetrics; -import io.prometheus.agent.AgentOptions; -import io.prometheus.agent.PathContext; -import io.prometheus.agent.RequestFailureException; -import io.prometheus.client.Summary; -import io.prometheus.common.AdminConfig; -import io.prometheus.common.ConfigVals; -import io.prometheus.common.GenericService; -import io.prometheus.common.MetricsConfig; -import io.prometheus.common.Utils; -import io.prometheus.common.ZipkinConfig; -import io.prometheus.grpc.AgentInfo; -import io.prometheus.grpc.HeartBeatRequest; -import io.prometheus.grpc.HeartBeatResponse; -import io.prometheus.grpc.PathMapSizeRequest; -import io.prometheus.grpc.PathMapSizeResponse; -import io.prometheus.grpc.ProxyServiceGrpc.ProxyServiceBlockingStub; -import io.prometheus.grpc.ProxyServiceGrpc.ProxyServiceStub; -import io.prometheus.grpc.RegisterAgentRequest; -import io.prometheus.grpc.RegisterAgentResponse; -import io.prometheus.grpc.RegisterPathRequest; -import io.prometheus.grpc.RegisterPathResponse; -import io.prometheus.grpc.ScrapeRequest; -import io.prometheus.grpc.ScrapeResponse; -import io.prometheus.grpc.UnregisterPathRequest; -import io.prometheus.grpc.UnregisterPathResponse; -import okhttp3.OkHttpClient; -import okhttp3.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.net.HttpHeaders.CONTENT_TYPE; -import static io.grpc.ClientInterceptors.intercept; -import static io.prometheus.common.InstrumentedThreadFactory.newInstrumentedThreadFactory; -import static io.prometheus.common.Utils.queueHealthCheck; -import static io.prometheus.common.Utils.sleepForMillis; -import static io.prometheus.common.Utils.toMillis; -import static io.prometheus.grpc.ProxyServiceGrpc.newBlockingStub; -import static io.prometheus.grpc.ProxyServiceGrpc.newStub; -import static java.lang.String.format; -import static java.util.concurrent.Executors.newCachedThreadPool; - -public class Agent - extends GenericService { - - private static final Logger logger = LoggerFactory.getLogger(Agent.class); - - private final Map pathContextMap = Maps.newConcurrentMap(); // Map path to PathContext - private final AtomicReference agentIdRef = new AtomicReference<>(); - private final AtomicLong lastMsgSent = new AtomicLong(); - private final ExecutorService heartbeatService = Executors.newFixedThreadPool(1); - private final CountDownLatch initialConnectionLatch = new CountDownLatch(1); - private final OkHttpClient okHttpClient = new OkHttpClient(); - private final AtomicReference channelRef = new AtomicReference<>(); - private final AtomicReference blockingStubRef = new AtomicReference<>(); - private final AtomicReference asyncStubRef = new AtomicReference<>(); - - private final String inProcessServerName; - private final String agentName; - private final String hostname; - private final int port; - private final AgentMetrics metrics; - private final ExecutorService readRequestsExecutorService; - private final BlockingQueue scrapeResponseQueue; - private final RateLimiter reconnectLimiter; - private final List> pathConfigs; - - - public Agent(final AgentOptions options, final String inProcessServerName, final boolean testMode) { - super(options.getConfigVals(), - AdminConfig.create(options.isAdminEnabled(), - options.getAdminPort(), - options.getConfigVals().agent.admin), - MetricsConfig.create(options.isMetricsEnabled(), - options.getMetricsPort(), - options.getConfigVals().agent.metrics), - ZipkinConfig.create(options.getConfigVals().agent.internal.zipkin), - testMode); - - this.inProcessServerName = inProcessServerName; - this.agentName = isNullOrEmpty(options.getAgentName()) ? format("Unnamed-%s", Utils.getHostName()) - : options.getAgentName(); - final int queueSize = this.getConfigVals().internal.scrapeResponseQueueSize; - this.scrapeResponseQueue = new ArrayBlockingQueue<>(queueSize); - - this.metrics = this.isMetricsEnabled() ? new AgentMetrics(this) : null; - - this.readRequestsExecutorService = newCachedThreadPool(this.isMetricsEnabled() - ? newInstrumentedThreadFactory("agent_fetch", - "Agent fetch", - true) - : new ThreadFactoryBuilder().setNameFormat("agent_fetch-%d") - .setDaemon(true) - .build()); - - logger.info("Assigning proxy reconnect pause time to {} secs", this.getConfigVals().internal.reconectPauseSecs); - this.reconnectLimiter = RateLimiter.create(1.0 / this.getConfigVals().internal.reconectPauseSecs); - this.reconnectLimiter.acquire(); // Prime the limiter - - this.pathConfigs = this.getConfigVals().pathConfigs.stream() - .map(v -> ImmutableMap.of("name", v.name, - "path", v.path, - "url", v.url)) - .peek(v -> logger.info("Proxy path /{} will be assigned to {}", - v.get("path"), v.get("url"))) - .collect(Collectors.toList()); - - - if (options.getProxyHostname().contains(":")) { - String[] vals = options.getProxyHostname().split(":"); - this.hostname = vals[0]; - this.port = Integer.valueOf(vals[1]); - } - else { - this.hostname = options.getProxyHostname(); - this.port = 50051; - } - - this.resetGrpcStubs(); - this.init(); - } - - public static void main(final String[] argv) { - final AgentOptions options = new AgentOptions(argv, true); - - logger.info(Utils.getBanner("banners/agent.txt")); - logger.info(Utils.getVersionDesc(false)); - - final Agent agent = new Agent(options, null, false); - agent.startAsync(); - } - - @Override - protected void shutDown() - throws Exception { - if (this.getChannel() != null) - this.getChannel().shutdownNow(); - this.heartbeatService.shutdownNow(); - super.shutDown(); - } - - @Override - protected void run() { - while (this.isRunning()) { - try { - this.connectToProxy(); - } - catch (RequestFailureException e) { - logger.info("Disconnected from proxy at {} after invalid response {}", - this.getProxyHost(), e.getMessage()); - } - catch (StatusRuntimeException e) { - logger.info("Disconnected from proxy at {}", this.getProxyHost()); - } - catch (Exception e) { - // Catch anything else to avoid exiting retry loop - logger.info("Disconnected from proxy at {} - {} [{}]", - this.getProxyHost(), e.getClass().getSimpleName(), e.getMessage()); - } - finally { - final double secsWaiting = this.reconnectLimiter.acquire(); - logger.info("Waited {} secs to reconnect", secsWaiting); - } - } - } - - @Override - protected void registerHealthChecks() { - super.registerHealthChecks(); - this.getHealthCheckRegistry() - .register("scrape_response_queue_check", - queueHealthCheck(scrapeResponseQueue, - this.getConfigVals().internal.scrapeResponseQueueUnhealthySize)); - } - - @Override - protected String serviceName() { return format("%s %s", this.getClass().getSimpleName(), this.agentName); } - - private void connectToProxy() - throws RequestFailureException { - final AtomicBoolean disconnected = new AtomicBoolean(false); - - // Reset gRPC stubs if previous iteration had a successful connection, i.e., the agent id != null - if (this.getAgentId() != null) { - this.resetGrpcStubs(); - this.setAgentId(null); - } - - // Reset values for each connection attempt - this.pathContextMap.clear(); - this.scrapeResponseQueue.clear(); - this.lastMsgSent.set(0); - - if (this.connectAgent()) { - this.registerAgent(); - this.registerPaths(); - this.startHeartBeat(disconnected); - this.readRequestsFromProxy(disconnected); - this.writeResponsesToProxyUntilDisconnected(disconnected); - } - } - - private void startHeartBeat(final AtomicBoolean disconnected) { - if (this.getConfigVals().internal.heartbeatEnabled) { - final long threadPauseMillis = this.getConfigVals().internal.heartbeatCheckPauseMillis; - final int maxInactivitySecs = this.getConfigVals().internal.heartbeatMaxInactivitySecs; - logger.info("Heartbeat scheduled to fire after {} secs of inactivity", maxInactivitySecs); - this.heartbeatService.submit( - () -> { - while (isRunning() && !disconnected.get()) { - final long timeSinceLastWriteMillis = System.currentTimeMillis() - this.lastMsgSent.get(); - if (timeSinceLastWriteMillis > toMillis(maxInactivitySecs)) - this.sendHeartBeat(disconnected); - sleepForMillis(threadPauseMillis); - } - logger.info("Heartbeat completed"); - }); - } - else { - logger.info("Heartbeat disabled"); - } - } - - private void resetGrpcStubs() { - logger.info("Creating gRPC stubs"); - - if (this.getChannel() != null) - this.getChannel().shutdownNow(); - - this.channelRef.set(isNullOrEmpty(this.inProcessServerName) ? NettyChannelBuilder.forAddress(this.hostname, this.port) - .usePlaintext(true) - .build() - : InProcessChannelBuilder.forName(this.inProcessServerName) - .usePlaintext(true) - .build()); - final List interceptors = Lists.newArrayList(new AgentClientInterceptor(this)); - - /* - if (this.getConfigVals().metrics.grpc.metricsEnabled) - interceptors.add(MonitoringClientInterceptor.create(this.getConfigVals().grpc.allMetricsReported - ? Configuration.allMetrics() - : Configuration.cheapMetricsOnly())); - if (this.zipkinReporter != null && this.getConfigVals().grpc.zipkinReportingEnabled) - interceptors.add(BraveGrpcClientInterceptor.create(this.zipkinReporter.getBrave())); - */ - - this.blockingStubRef.set(newBlockingStub(intercept(this.getChannel(), interceptors))); - this.asyncStubRef.set(newStub(intercept(this.getChannel(), interceptors))); - } - - private void updateScrapeCounter(final String type) { - if (this.isMetricsEnabled()) - this.getMetrics().scrapeRequests.labels(type).inc(); - } - - private ScrapeResponse fetchUrl(final ScrapeRequest scrapeRequest) { - int statusCode = 404; - final String path = scrapeRequest.getPath(); - final ScrapeResponse.Builder scrapeResponse = ScrapeResponse.newBuilder() - .setAgentId(scrapeRequest.getAgentId()) - .setScrapeId(scrapeRequest.getScrapeId()); - final PathContext pathContext = this.pathContextMap.get(path); - if (pathContext == null) { - logger.warn("Invalid path in fetchUrl(): {}", path); - this.updateScrapeCounter("invalid_path"); - return scrapeResponse.setValid(false) - .setReason(format("Invalid path: %s", path)) - .setStatusCode(statusCode) - .setText("") - .setContentType("") - .build(); - } - - final Summary.Timer requestTimer = this.isMetricsEnabled() - ? this.getMetrics().scrapeRequestLatency.labels(this.agentName).startTimer() - : null; - String reason = "None"; - try { - try (final Response response = pathContext.fetchUrl(scrapeRequest)) { - statusCode = response.code(); - if (response.isSuccessful()) { - this.updateScrapeCounter("success"); - return scrapeResponse.setValid(true) - .setReason("") - .setStatusCode(statusCode) - .setText(response.body().string()) - .setContentType(response.header(CONTENT_TYPE)) - .build(); - } - else { - reason = format("Unsucessful response code %d", statusCode); - } - } - } - catch (IOException e) { - reason = format("%s - %s", e.getClass().getSimpleName(), e.getMessage()); - } - catch (Exception e) { - logger.warn("fetchUrl()", e); - reason = format("%s - %s", e.getClass().getSimpleName(), e.getMessage()); - } - finally { - if (requestTimer != null) - requestTimer.observeDuration(); - } - - this.updateScrapeCounter("unsuccessful"); - - return scrapeResponse.setValid(false) - .setReason(reason) - .setStatusCode(statusCode) - .setText("") - .setContentType("") - .build(); - } - - // If successful, this will create an agentContxt on the Proxy and an interceptor will - // add an agent_id to the headers` - private boolean connectAgent() { - try { - logger.info("Connecting to proxy at {}...", this.getProxyHost()); - this.getBlockingStub().connectAgent(Empty.getDefaultInstance()); - logger.info("Connected to proxy at {}", this.getProxyHost()); - if (this.isMetricsEnabled()) - this.getMetrics().connects.labels("success").inc(); - return true; - } - catch (StatusRuntimeException e) { - if (this.isMetricsEnabled()) - this.getMetrics().connects.labels("failure").inc(); - logger.info("Cannot connect to proxy at {} [{}]", this.getProxyHost(), e.getMessage()); - return false; - } - } - - private void registerAgent() - throws RequestFailureException { - final RegisterAgentRequest request = RegisterAgentRequest.newBuilder() - .setAgentId(this.getAgentId()) - .setAgentName(this.agentName) - .setHostname(Utils.getHostName()) - .build(); - final RegisterAgentResponse response = this.getBlockingStub().registerAgent(request); - this.markMsgSent(); - if (!response.getValid()) - throw new RequestFailureException(format("registerAgent() - %s", response.getReason())); - - this.initialConnectionLatch.countDown(); - } - - private void registerPaths() - throws RequestFailureException { - for (final Map agentConfig : this.pathConfigs) { - final String path = agentConfig.get("path"); - final String url = agentConfig.get("url"); - this.registerPath(path, url); - } - } - - public void registerPath(final String pathVal, final String url) - throws RequestFailureException { - final String path = checkNotNull(pathVal).startsWith("/") ? pathVal.substring(1) : pathVal; - final long pathId = this.registerPathOnProxy(path); - if (!this.isTestMode()) - logger.info("Registered {} as /{}", url, path); - this.pathContextMap.put(path, new PathContext(this.okHttpClient, pathId, path, url)); - } - - public void unregisterPath(final String pathVal) - throws RequestFailureException { - final String path = checkNotNull(pathVal).startsWith("/") ? pathVal.substring(1) : pathVal; - this.unregisterPathOnProxy(path); - final PathContext pathContext = this.pathContextMap.remove(path); - if (pathContext == null) - logger.info("No path value /{} found in pathContextMap", path); - else if (!this.isTestMode()) - logger.info("Unregistered /{} for {}", path, pathContext.getUrl()); - } - - public int pathMapSize() { - final PathMapSizeRequest request = PathMapSizeRequest.newBuilder() - .setAgentId(this.getAgentId()) - .build(); - final PathMapSizeResponse response = this.getBlockingStub().pathMapSize(request); - this.markMsgSent(); - return response.getPathCount(); - } - - private long registerPathOnProxy(final String path) - throws RequestFailureException { - final RegisterPathRequest request = RegisterPathRequest.newBuilder() - .setAgentId(this.getAgentId()) - .setPath(path) - .build(); - final RegisterPathResponse response = this.getBlockingStub().registerPath(request); - this.markMsgSent(); - if (!response.getValid()) - throw new RequestFailureException(format("registerPath() - %s", response.getReason())); - return response.getPathId(); - } - - private void unregisterPathOnProxy(final String path) - throws RequestFailureException { - final UnregisterPathRequest request = UnregisterPathRequest.newBuilder() - .setAgentId(this.getAgentId()) - .setPath(path) - .build(); - final UnregisterPathResponse response = this.getBlockingStub().unregisterPath(request); - this.markMsgSent(); - if (!response.getValid()) - throw new RequestFailureException(format("unregisterPath() - %s", response.getReason())); - } - - private Runnable readRequestAction(final ScrapeRequest request) { - return () -> { - final ScrapeResponse response = fetchUrl(request); - try { - scrapeResponseQueue.put(response); - } - catch (InterruptedException e) { - // Ignore - } - }; - } - - private void readRequestsFromProxy(final AtomicBoolean disconnected) { - final StreamObserver observer = - new StreamObserver() { - @Override - public void onNext(final ScrapeRequest request) { - readRequestsExecutorService.submit(readRequestAction(request)); - } - - @Override - public void onError(Throwable t) { - final Status status = Status.fromThrowable(t); - logger.info("Error in readRequestsFromProxy(): {}", status); - disconnected.set(true); - } - - @Override - public void onCompleted() { - disconnected.set(true); - } - }; - final AgentInfo agentInfo = AgentInfo.newBuilder().setAgentId(this.getAgentId()).build(); - this.getAsyncStub().readRequestsFromProxy(agentInfo, observer); - } - - private void writeResponsesToProxyUntilDisconnected(final AtomicBoolean disconnected) { - final long checkMillis = this.getConfigVals().internal.scrapeResponseQueueCheckMillis; - final StreamObserver observer = this.getAsyncStub().writeResponsesToProxy( - new StreamObserver() { - @Override - public void onNext(Empty empty) { - // Ignore Empty return value - } - - @Override - public void onError(Throwable t) { - final Status s = Status.fromThrowable(t); - logger.info("Error in writeResponsesToProxyUntilDisconnected(): {} {}", s.getCode(), s.getDescription()); - disconnected.set(true); - } - - @Override - public void onCompleted() { - disconnected.set(true); - } - }); - - while (!disconnected.get()) { - try { - // Set a short timeout to check if client has disconnected - final ScrapeResponse response = this.scrapeResponseQueue.poll(checkMillis, TimeUnit.MILLISECONDS); - if (response != null) { - observer.onNext(response); - this.markMsgSent(); - } - } - catch (InterruptedException e) { - // Ignore - } - } - - logger.info("Disconnected from proxy at {}", this.getProxyHost()); - observer.onCompleted(); - } - - private void markMsgSent() { - this.lastMsgSent.set(System.currentTimeMillis()); - } - - private void sendHeartBeat(final AtomicBoolean disconnected) { - final String agentId = this.getAgentId(); - if (agentId == null) - return; - try { - final HeartBeatRequest request = HeartBeatRequest.newBuilder().setAgentId(agentId).build(); - final HeartBeatResponse response = this.getBlockingStub().sendHeartBeat(request); - this.markMsgSent(); - if (!response.getValid()) { - logger.info("AgentId {} not found on proxy", agentId); - throw new StatusRuntimeException(Status.NOT_FOUND); - } - } - catch (StatusRuntimeException e) { - logger.info("Hearbeat failed {}", e.getStatus()); - disconnected.set(true); - } - } - - public boolean awaitInitialConnection(long timeout, TimeUnit unit) - throws InterruptedException { - return this.initialConnectionLatch.await(timeout, unit); - } - - private String getProxyHost() { return format("%s:%s", hostname, port); } - - public int getScrapeResponseQueueSize() { return this.scrapeResponseQueue.size(); } - - public AgentMetrics getMetrics() { return this.metrics; } - - public ManagedChannel getChannel() { return this.channelRef.get(); } - - private ProxyServiceBlockingStub getBlockingStub() { return this.blockingStubRef.get(); } - - private ProxyServiceStub getAsyncStub() { return this.asyncStubRef.get(); } - - public String getAgentId() { return this.agentIdRef.get(); } - - public void setAgentId(final String agentId) { this.agentIdRef.set(agentId); } - - public ConfigVals.Agent getConfigVals() { return this.getGenericConfigVals().agent; } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("agentId", this.getAgentId()) - .add("agentName", this.agentName) - .add("proxyHost", this.getProxyHost()) - .add("adminService", this.isAdminEnabled() ? this.getAdminService() : "Disabled") - .add("metricsService", this.isMetricsEnabled() ? this.getMetricsService() : "Disabled") - .toString(); - } -} diff --git a/src/main/java/io/prometheus/Agent.kt b/src/main/java/io/prometheus/Agent.kt new file mode 100644 index 00000000..c166e232 --- /dev/null +++ b/src/main/java/io/prometheus/Agent.kt @@ -0,0 +1,511 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import brave.Tracing +import brave.grpc.GrpcTracing +import com.google.common.base.MoreObjects +import com.google.common.base.Preconditions.checkNotNull +import com.google.common.collect.Maps +import com.google.common.net.HttpHeaders.CONTENT_TYPE +import com.google.common.util.concurrent.RateLimiter +import com.google.common.util.concurrent.ThreadFactoryBuilder +import com.google.protobuf.Empty +import io.grpc.ClientInterceptor +import io.grpc.ClientInterceptors.intercept +import io.grpc.ManagedChannel +import io.grpc.Status +import io.grpc.StatusRuntimeException +import io.grpc.inprocess.InProcessChannelBuilder +import io.grpc.netty.NettyChannelBuilder +import io.grpc.stub.StreamObserver +import io.prometheus.agent.* +import io.prometheus.common.* +import io.prometheus.grpc.* +import io.prometheus.grpc.ProxyServiceGrpc.* +import okhttp3.OkHttpClient +import org.slf4j.LoggerFactory +import java.io.IOException +import java.util.concurrent.* +import java.util.concurrent.Executors.newCachedThreadPool +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.atomic.AtomicReference + +class Agent(options: AgentOptions, + private val inProcessServerName: String?, + testMode: Boolean) : GenericService(options.configVals!!, + AdminConfig.create(options.adminEnabled, + options.adminPort!!, + options.configVals!!.agent.admin), + MetricsConfig.create(options.metricsEnabled, + options.metricsPort!!, + options.configVals!!.agent.metrics), + ZipkinConfig.create(options.configVals!!.agent.internal.zipkin), + testMode) { + + private val pathContextMap = Maps.newConcurrentMap() // Map path to PathContext + private val heartbeatService = Executors.newFixedThreadPool(1) + private val initialConnectionLatch = CountDownLatch(1) + private val okHttpClient = OkHttpClient() + private val lastMsgSentRef = AtomicLong() + private val agentIdRef = AtomicReference() + private val channelRef = AtomicReference() + private val blockingStubRef = AtomicReference() + private val asyncStubRef = AtomicReference() + private val scrapeResponseQueue = ArrayBlockingQueue(this.configVals.internal.scrapeResponseQueueSize) + private val agentName: String? = if (options.agentName.isNullOrBlank()) "Unnamed-$hostName" else options.agentName + private val metrics: AgentMetrics? = if (this.metricsEnabled) AgentMetrics(this) else null + private val readRequestsExecutorService: ExecutorService = + newCachedThreadPool(if (this.metricsEnabled) + InstrumentedThreadFactory.newInstrumentedThreadFactory("agent_fetch", + "Agent fetch", + true) + else + ThreadFactoryBuilder() + .setNameFormat("agent_fetch-%d") + .setDaemon(true) + .build()) + + private val _grpcTracing: Tracing? + private val grpcTracing: GrpcTracing? + private val hostname: String? + private val port: Int + private val reconnectLimiter: RateLimiter + private val pathConfigs: List> + + private val proxyHost: String + get() = "$hostname:$port" + + val scrapeResponseQueueSize: Int + get() = this.scrapeResponseQueue.size + + var channel: ManagedChannel? + get() = this.channelRef.get() + set(v) = this.channelRef.set(v) + + var agentId: String? + get() = this.agentIdRef.get() + set(v) = this.agentIdRef.set(v) + + val configVals: ConfigVals.Agent + get() = this.genericConfigVals.agent + + init { + logger.info("Assigning proxy reconnect pause time to ${this.configVals.internal.reconectPauseSecs} secs") + this.reconnectLimiter = RateLimiter.create(1.0 / this.configVals.internal.reconectPauseSecs) + this.reconnectLimiter.acquire() // Prime the limiter + + this.pathConfigs = + this.configVals.pathConfigs + .map { mapOf("name" to it.name, "path" to it.path, "url" to it.url) } + .onEach { logger.info("Proxy path /{} will be assigned to {}", it["path"], it["url"]) } + .toList() + + + if (options.proxyHostname!!.contains(":")) { + val vals = options.proxyHostname!!.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + this.hostname = vals[0] + this.port = Integer.valueOf(vals[1]) + } + else { + this.hostname = options.proxyHostname + this.port = 50051 + } + + if (zipkinEnabled) { + this._grpcTracing = this.zipkinReporterService!!.newTracing("grpc_client") + this.grpcTracing = GrpcTracing.create(this._grpcTracing) + } + else { + this._grpcTracing = null + this.grpcTracing = null + } + + this.resetGrpcStubs() + this.initService() + } + + override fun shutDown() { + this._grpcTracing?.close() + this.channel?.shutdownNow() + this.heartbeatService.shutdownNow() + super.shutDown() + } + + override fun run() { + while (this.isRunning) { + try { + this.connectToProxy() + } catch (e: RequestFailureException) { + logger.info("Disconnected from proxy at ${this.proxyHost} after invalid response ${e.message}") + } catch (e: StatusRuntimeException) { + logger.info("Disconnected from proxy at ${this.proxyHost}") + } catch (e: Exception) { + // Catch anything else to avoid exiting retry loop + } finally { + val secsWaiting = this.reconnectLimiter.acquire() + logger.info("Waited $secsWaiting secs to reconnect") + } + } + } + + override fun registerHealthChecks() { + super.registerHealthChecks() + this.healthCheckRegistry + .register("scrape_response_queue_check", + queueHealthCheck(scrapeResponseQueue, + this.configVals.internal.scrapeResponseQueueUnhealthySize)) + } + + override fun serviceName() = "${this.javaClass.simpleName} ${this.agentName}" + + @Throws(RequestFailureException::class) + private fun connectToProxy() { + val disconnected = AtomicBoolean(false) + + // Reset gRPC stubs if previous iteration had a successful connection, i.e., the agent id != null + if (this.agentId != null) { + this.resetGrpcStubs() + this.agentId = null + } + + // Reset values for each connection attempt + this.pathContextMap.clear() + this.scrapeResponseQueue.clear() + this.lastMsgSentRef.set(0) + + if (this.connectAgent()) { + this.registerAgent() + this.registerPaths() + this.startHeartBeat(disconnected) + this.readRequestsFromProxy(disconnected) + this.writeResponsesToProxyUntilDisconnected(disconnected) + } + } + + private fun startHeartBeat(disconnected: AtomicBoolean) { + if (this.configVals.internal.heartbeatEnabled) { + val threadPauseMillis = this.configVals.internal.heartbeatCheckPauseMillis.toLong() + val maxInactivitySecs = this.configVals.internal.heartbeatMaxInactivitySecs + logger.info("Heartbeat scheduled to fire after $maxInactivitySecs secs of inactivity") + this.heartbeatService.submit { + while (isRunning && !disconnected.get()) { + val timeSinceLastWriteMillis = System.currentTimeMillis() - this.lastMsgSentRef.get() + if (timeSinceLastWriteMillis > maxInactivitySecs.toLong().toMillis()) + this.sendHeartBeat(disconnected) + sleepForMillis(threadPauseMillis) + } + logger.info("Heartbeat completed") + } + } + else { + logger.info("Heartbeat disabled") + } + } + + private fun resetGrpcStubs() { + logger.info("Creating gRPC stubs") + + this.channel?.shutdownNow() + + val channelBuilder = + if (this.inProcessServerName.isNullOrBlank()) + NettyChannelBuilder.forAddress(this.hostname, this.port) + else + InProcessChannelBuilder.forName(this.inProcessServerName) + + if (this.zipkinEnabled) + channelBuilder.intercept(this.grpcTracing!!.newClientInterceptor()) + + this.channel = channelBuilder.usePlaintext(true).build() + val interceptors = listOf(AgentClientInterceptor(this)) + + this.blockingStubRef.set(newBlockingStub(intercept(this.channel, interceptors))) + this.asyncStubRef.set(newStub(intercept(this.channel, interceptors))) + } + + private fun updateScrapeCounter(type: String) = this.metrics?.scrapeRequests?.labels(type)?.inc() + + private fun fetchUrl(scrapeRequest: ScrapeRequest): ScrapeResponse { + var statusCode = 404 + val path = scrapeRequest.path + val scrapeResponse = + ScrapeResponse.newBuilder() + .setAgentId(scrapeRequest.agentId) + .setScrapeId(scrapeRequest.scrapeId) + val pathContext = this.pathContextMap[path] + if (pathContext == null) { + logger.warn("Invalid path in fetchUrl(): $path") + this.updateScrapeCounter("invalid_path") + return scrapeResponse + .setValid(false) + .setReason("Invalid path: $path") + .setStatusCode(statusCode) + .setText("") + .setContentType("") + .build() + } + + val requestTimer = this.metrics?.scrapeRequestLatency?.labels(this.agentName!!)?.startTimer() + var reason = "None" + try { + pathContext.fetchUrl(scrapeRequest).use { + statusCode = it.code() + if (it.isSuccessful) { + this.updateScrapeCounter("success") + return scrapeResponse + .setValid(true) + .setReason("") + .setStatusCode(statusCode) + .setText(it.body()!!.string()) + .setContentType(it.header(CONTENT_TYPE)) + .build() + } + else { + reason = "Unsucessful response code $statusCode" + } + } + } catch (e: IOException) { + reason = "${e.javaClass.simpleName} - ${e.message}" + } catch (e: Exception) { + logger.warn("fetchUrl()", e) + reason = "${e.javaClass.simpleName} - ${e.message}" + } finally { + requestTimer?.observeDuration() + } + + this.updateScrapeCounter("unsuccessful") + + return scrapeResponse + .setValid(false) + .setReason(reason) + .setStatusCode(statusCode) + .setText("") + .setContentType("") + .build() + } + + // If successful, this will create an agentContxt on the Proxy and an interceptor will + // add an agent_id to the headers` + private fun connectAgent(): Boolean { + return try { + logger.info("Connecting to proxy at ${this.proxyHost}...") + this.blockingStubRef.get().connectAgent(Empty.getDefaultInstance()) + logger.info("Connected to proxy at ${this.proxyHost}") + this.metrics?.connects?.labels("success")?.inc() + true + } catch (e: StatusRuntimeException) { + this.metrics?.connects?.labels("failure")?.inc() + logger.info("Cannot connect to proxy at ${this.proxyHost} [${e.message}]") + false + } + } + + @Throws(RequestFailureException::class) + private fun registerAgent() { + val request = + RegisterAgentRequest.newBuilder() + .setAgentId(this.agentId) + .setAgentName(this.agentName) + .setHostname(hostName) + .build() + val response = this.blockingStubRef.get().registerAgent(request) + this.markMsgSent() + if (!response.valid) + throw RequestFailureException("registerAgent() - ${response.reason}") + + this.initialConnectionLatch.countDown() + } + + @Throws(RequestFailureException::class) + private fun registerPaths() { + this.pathConfigs.forEach { + this.registerPath(it["path"]!!, it["url"]!!) + } + } + + @Throws(RequestFailureException::class) + fun registerPath(pathVal: String, url: String) { + val path = if (checkNotNull(pathVal).startsWith("/")) pathVal.substring(1) else pathVal + val pathId = this.registerPathOnProxy(path) + if (!this.isTestMode) + logger.info("Registered $url as /$path") + this.pathContextMap.put(path, PathContext(this.okHttpClient, pathId, path, url)) + } + + @Throws(RequestFailureException::class) + fun unregisterPath(pathVal: String) { + val path = if (checkNotNull(pathVal).startsWith("/")) pathVal.substring(1) else pathVal + this.unregisterPathOnProxy(path) + val pathContext = this.pathContextMap.remove(path) + when { + pathContext == null -> logger.info("No path value /$path found in pathContextMap") + !this.isTestMode -> logger.info("Unregistered /$path for ${pathContext.url}") + } + } + + fun pathMapSize(): Int { + val request = PathMapSizeRequest.newBuilder().setAgentId(this.agentId).build() + val response = this.blockingStubRef.get().pathMapSize(request) + this.markMsgSent() + return response.pathCount + } + + @Throws(RequestFailureException::class) + private fun registerPathOnProxy(path: String): Long { + val request = + RegisterPathRequest.newBuilder() + .setAgentId(this.agentId) + .setPath(path) + .build() + val response = this.blockingStubRef.get().registerPath(request) + this.markMsgSent() + if (!response.valid) + throw RequestFailureException("registerPath() - ${response.reason}") + return response.pathId + } + + @Throws(RequestFailureException::class) + private fun unregisterPathOnProxy(path: String) { + val request = + UnregisterPathRequest.newBuilder() + .setAgentId(this.agentId) + .setPath(path) + .build() + val response = this.blockingStubRef.get().unregisterPath(request) + this.markMsgSent() + if (!response.valid) + throw RequestFailureException("unregisterPath() - ${response.reason}") + } + + private fun readRequestAction(request: ScrapeRequest): Runnable { + return Runnable { + val response = fetchUrl(request) + try { + scrapeResponseQueue.put(response) + } catch (e: InterruptedException) { + // Ignore + } + } + } + + private fun readRequestsFromProxy(disconnected: AtomicBoolean) { + val observer = object : StreamObserver { + override fun onNext(request: ScrapeRequest) { + readRequestsExecutorService.submit(readRequestAction(request)) + } + + override fun onError(t: Throwable) { + val status = Status.fromThrowable(t) + logger.info("Error in readRequestsFromProxy(): $status") + disconnected.set(true) + } + + override fun onCompleted() { + disconnected.set(true) + } + } + val agentInfo = AgentInfo.newBuilder().setAgentId(this.agentId).build() + this.asyncStubRef.get().readRequestsFromProxy(agentInfo, observer) + } + + private fun writeResponsesToProxyUntilDisconnected(disconnected: AtomicBoolean) { + val checkMillis = this.configVals.internal.scrapeResponseQueueCheckMillis.toLong() + val observer = + this.asyncStubRef + .get() + .writeResponsesToProxy( + object : StreamObserver { + override fun onNext(empty: Empty) { + // Ignore Empty return value + } + + override fun onError(t: Throwable) { + val s = Status.fromThrowable(t) + logger.info("Error in writeResponsesToProxyUntilDisconnected(): ${s.code} ${s.description}") + disconnected.set(true) + } + + override fun onCompleted() = disconnected.set(true) + }) + + while (!disconnected.get()) { + try { + // Set a short timeout to check if client has disconnected + val response = this.scrapeResponseQueue.poll(checkMillis, TimeUnit.MILLISECONDS) + if (response != null) { + observer.onNext(response) + this.markMsgSent() + } + } catch (e: InterruptedException) { + // Ignore + } + + } + + logger.info("Disconnected from proxy at ${this.proxyHost}") + observer.onCompleted() + } + + private fun markMsgSent() = this.lastMsgSentRef.set(System.currentTimeMillis()) + + private fun sendHeartBeat(disconnected: AtomicBoolean) { + if (this.agentId == null) + return + + try { + val request = HeartBeatRequest.newBuilder().setAgentId(this.agentId).build() + val response = this.blockingStubRef.get().sendHeartBeat(request) + this.markMsgSent() + if (!response.valid) { + logger.info("AgentId ${this.agentId} not found on proxy") + throw StatusRuntimeException(Status.NOT_FOUND) + } + } catch (e: StatusRuntimeException) { + logger.info("Hearbeat failed ${e.status}") + disconnected.set(true) + } + } + + @Throws(InterruptedException::class) + fun awaitInitialConnection(timeout: Long, unit: TimeUnit) = this.initialConnectionLatch.await(timeout, unit) + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("agentId", this.agentId) + .add("agentName", this.agentName) + .add("proxyHost", this.proxyHost) + .add("adminService", this.adminService ?: "Disabled") + .add("metricsService", this.metricsService ?: "Disabled") + .toString() + + companion object { + private val logger = LoggerFactory.getLogger(Agent::class.java) + + @JvmStatic + fun main(argv: Array) { + val options = AgentOptions(argv, true) + + logger.info(getBanner("banners/agent.txt")) + logger.info(getVersionDesc(false)) + + val agent = Agent(options, null, false) + agent.startAsync() + } + } +} diff --git a/src/main/java/io/prometheus/Proxy.java b/src/main/java/io/prometheus/Proxy.java deleted file mode 100644 index 3d65188f..00000000 --- a/src/main/java/io/prometheus/Proxy.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import com.codahale.metrics.health.HealthCheck; -import com.google.common.base.Joiner; -import com.google.common.base.MoreObjects; -import com.google.common.collect.Maps; -import io.grpc.Attributes; -import io.prometheus.common.AdminConfig; -import io.prometheus.common.ConfigVals; -import io.prometheus.common.GenericService; -import io.prometheus.common.MetricsConfig; -import io.prometheus.common.Utils; -import io.prometheus.common.ZipkinConfig; -import io.prometheus.grpc.UnregisterPathResponse; -import io.prometheus.proxy.AgentContext; -import io.prometheus.proxy.AgentContextCleanupService; -import io.prometheus.proxy.ProxyGrpcService; -import io.prometheus.proxy.ProxyHttpService; -import io.prometheus.proxy.ProxyMetrics; -import io.prometheus.proxy.ProxyOptions; -import io.prometheus.proxy.ScrapeRequestWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static com.google.common.base.Strings.isNullOrEmpty; -import static io.prometheus.common.Utils.mapHealthCheck; -import static java.lang.String.format; - -public class Proxy - extends GenericService { - - private static final Logger logger = LoggerFactory.getLogger(Proxy.class); - - public static final String AGENT_ID = "agent-id"; - public static final Attributes.Key ATTRIB_AGENT_ID = Attributes.Key.of(AGENT_ID); - - private final Map agentContextMap = Maps.newConcurrentMap(); // Map agent_id to AgentContext - private final Map pathMap = Maps.newConcurrentMap(); // Map path to AgentContext - private final Map scrapeRequestMap = Maps.newConcurrentMap(); // Map scrape_id to agent_id - - private final ProxyMetrics metrics; - private final ProxyGrpcService grpcService; - private final ProxyHttpService httpService; - private final AgentContextCleanupService agentCleanupService; - - public Proxy(final ProxyOptions options, - final int proxyPort, - final String inProcessServerName, - final boolean testMode) { - super(options.getConfigVals(), - AdminConfig.create(options.isAdminEnabled(), - options.getAdminPort(), - options.getConfigVals().proxy.admin), - MetricsConfig.create(options.isMetricsEnabled(), - options.getMetricsPort(), - options.getConfigVals().proxy.metrics), - ZipkinConfig.create(options.getConfigVals().proxy.internal.zipkin), - testMode); - - this.metrics = this.isMetricsEnabled() ? new ProxyMetrics(this) : null; - this.grpcService = isNullOrEmpty(inProcessServerName) ? ProxyGrpcService.create(this, options.getAgentPort()) - : ProxyGrpcService.create(this, inProcessServerName); - this.httpService = new ProxyHttpService(this, proxyPort); - this.agentCleanupService = this.getConfigVals().internal.staleAgentCheckEnabled - ? new AgentContextCleanupService(this) : null; - - this.addServices(this.grpcService, this.httpService, this.agentCleanupService); - this.init(); - } - - public static void main(final String[] argv) { - final ProxyOptions options = new ProxyOptions(argv); - - logger.info(Utils.getBanner("banners/proxy.txt")); - logger.info(Utils.getVersionDesc(false)); - - final Proxy proxy = new Proxy(options, options.getProxyPort(), null, false); - proxy.startAsync(); - } - - @Override - protected void startUp() - throws Exception { - super.startUp(); - this.grpcService.startAsync(); - this.httpService.startAsync(); - - if (this.agentCleanupService != null) - this.agentCleanupService.startAsync(); - else - logger.info("Agent eviction thread not started"); - } - - @Override - protected void shutDown() - throws Exception { - this.grpcService.stopAsync(); - this.httpService.stopAsync(); - if (this.agentCleanupService != null) - this.agentCleanupService.stopAsync(); - super.shutDown(); - } - - @Override - protected void run() { - while (this.isRunning()) { - Utils.sleepForMillis(500); - } - } - - @Override - protected void registerHealthChecks() { - super.registerHealthChecks(); - this.getHealthCheckRegistry().register("grpc_service", this.grpcService.getHealthCheck()); - this.getHealthCheckRegistry() - .register("scrape_response_map_check", - mapHealthCheck(scrapeRequestMap, this.getConfigVals().internal.scrapeRequestMapUnhealthySize)); - this.getHealthCheckRegistry() - .register("agent_scrape_request_queue", - new HealthCheck() { - @Override - protected Result check() - throws Exception { - final int unhealthySize = getConfigVals().internal.scrapeRequestQueueUnhealthySize; - final List vals = getAgentContextMap().entrySet() - .stream() - .filter(kv -> kv.getValue().scrapeRequestQueueSize() >= unhealthySize) - .map(kv -> format("%s %d", - kv.getValue(), - kv.getValue().scrapeRequestQueueSize())) - .collect(Collectors.toList()); - return vals.isEmpty() ? Result.healthy() - : Result.unhealthy(format("Large scrapeRequestQueues: %s", - Joiner.on(", ").join(vals))); - } - }); - - } - - public void addAgentContext(final AgentContext agentContext) { - this.agentContextMap.put(agentContext.getAgentId(), agentContext); - } - - public AgentContext getAgentContext(String agentId) { return this.agentContextMap.get(agentId); } - - public AgentContext removeAgentContext(String agentId) { - final AgentContext agentContext = this.agentContextMap.remove(agentId); - if (agentContext != null) { - logger.info("Removed {}", agentContext); - agentContext.markInvalid(); - } - else - logger.error("Missing AgentContext for agentId: {}", agentId); - - return agentContext; - } - - public void addToScrapeRequestMap(final ScrapeRequestWrapper scrapeRequest) { - this.scrapeRequestMap.put(scrapeRequest.getScrapeId(), scrapeRequest); - } - - public ScrapeRequestWrapper getFromScrapeRequestMap(long scrapeId) { - return this.scrapeRequestMap.get(scrapeId); - } - - public ScrapeRequestWrapper removeFromScrapeRequestMap(long scrapeId) { - return this.scrapeRequestMap.remove(scrapeId); - } - - public AgentContext getAgentContextByPath(final String path) { return this.pathMap.get(path); } - - public boolean containsPath(final String path) { return this.pathMap.containsKey(path);} - - public void addPath(final String path, final AgentContext agentContext) { - synchronized (this.pathMap) { - this.pathMap.put(path, agentContext); - if (!this.isTestMode()) - logger.info("Added path /{} for {}", path, agentContext); - } - } - - public void removePath(final String path, final String agentId, - final UnregisterPathResponse.Builder responseBuilder) { - synchronized (this.pathMap) { - final AgentContext agentContext = this.pathMap.get(path); - if (agentContext == null) { - final String msg = format("Unable to remove path /%s - path not found", path); - logger.info(msg); - responseBuilder.setValid(false).setReason(msg); - } - else if (!agentContext.getAgentId().equals(agentId)) { - final String msg = format("Unable to remove path /%s - invalid agentId: %s (owner is %s)", - path, agentId, agentContext.getAgentId()); - logger.info(msg); - responseBuilder.setValid(false).setReason(msg); - } - else { - this.pathMap.remove(path); - if (!this.isTestMode()) - logger.info("Removed path /{} for {}", path, agentContext); - responseBuilder.setValid(true).setReason(""); - } - } - } - - public void removePathByAgentId(final String agentId) { - synchronized (this.pathMap) { - for (Map.Entry elem : this.pathMap.entrySet()) { - if (elem.getValue().getAgentId().equals(agentId)) { - final AgentContext agentContext = this.pathMap.remove(elem.getKey()); - if (agentContext != null) - logger.info("Removed path /{} for {}", elem.getKey(), agentContext); - else - logger.error("Missing path /{} for agentId: {}", elem.getKey(), agentId); - } - } - } - } - - public int pathMapSize() { return this.pathMap.size(); } - - public int getAgentContextSize() { return this.agentContextMap.size(); } - - public int getPathMapSize() { return this.pathMap.size(); } - - public int getScrapeMapSize() { return this.scrapeRequestMap.size(); } - - public ProxyMetrics getMetrics() { return this.metrics; } - - public ConfigVals.Proxy2 getConfigVals() { return this.getGenericConfigVals().proxy; } - - public Map getAgentContextMap() { return this.agentContextMap; } - - public int getTotalAgentRequestQueueSize() { - return this.agentContextMap.values() - .stream() - .mapToInt(AgentContext::scrapeRequestQueueSize) - .sum(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("proxyPort", this.httpService.getPort()) - .add("adminService", this.isAdminEnabled() ? this.getAdminService() : "Disabled") - .add("metricsService", this.isMetricsEnabled() ? this.getMetricsService() : "Disabled") - .toString(); - } -} diff --git a/src/main/java/io/prometheus/Proxy.kt b/src/main/java/io/prometheus/Proxy.kt new file mode 100644 index 00000000..07b9273b --- /dev/null +++ b/src/main/java/io/prometheus/Proxy.kt @@ -0,0 +1,234 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import com.codahale.metrics.health.HealthCheck +import com.google.common.base.Joiner +import com.google.common.base.MoreObjects +import com.google.common.collect.Maps +import io.grpc.Attributes +import io.prometheus.common.* +import io.prometheus.grpc.UnregisterPathResponse +import io.prometheus.proxy.* +import org.slf4j.LoggerFactory +import java.util.concurrent.ConcurrentMap + +class Proxy(options: ProxyOptions, + proxyPort: Int, + inProcessServerName: String?, + testMode: Boolean) : GenericService(options.configVals!!, + AdminConfig.create(options.adminEnabled, + options.adminPort!!, + options.configVals!!.proxy.admin), + MetricsConfig.create(options.metricsEnabled, + options.metricsPort!!, + options.configVals!!.proxy.metrics), + ZipkinConfig.create(options.configVals!!.proxy.internal.zipkin), + testMode) { + + private val pathMap = Maps.newConcurrentMap() // Map path to AgentContext + private val scrapeRequestMap = Maps.newConcurrentMap() // Map scrape_id to agent_id + + val agentContextMap: ConcurrentMap = Maps.newConcurrentMap() // Map agent_id to AgentContext + val metrics = if (this.metricsEnabled) ProxyMetrics(this) else null + + private val httpService = ProxyHttpService(this, proxyPort) + private val grpcService: ProxyGrpcService = + if (inProcessServerName.isNullOrBlank()) + ProxyGrpcService.create(this, options.agentPort!!) + else + ProxyGrpcService.create(this, inProcessServerName!!) + private val agentCleanupService = + if (this.configVals.internal.staleAgentCheckEnabled) + AgentContextCleanupService(this) + else + null + + val agentContextSize: Int + get() = this.agentContextMap.size + + val pathMapSize: Int + get() = this.pathMap.size + + val scrapeMapSize: Int + get() = this.scrapeRequestMap.size + + val configVals: ConfigVals.Proxy2 + get() = this.genericConfigVals.proxy + + val totalAgentRequestQueueSize: Int + get() = this.agentContextMap.values.map { it.scrapeRequestQueueSize() }.sum() + + init { + this.addServices(this.grpcService, this.httpService, this.agentCleanupService!!) + this.initService() + } + + override fun startUp() { + super.startUp() + this.grpcService.startAsync() + this.httpService.startAsync() + this.agentCleanupService?.startAsync() ?: logger.info("Agent eviction thread not started") + } + + override fun shutDown() { + this.grpcService.stopAsync() + this.httpService.stopAsync() + this.agentCleanupService?.stopAsync() + super.shutDown() + } + + override fun run() { + while (this.isRunning) + sleepForMillis(500) + } + + override fun registerHealthChecks() { + super.registerHealthChecks() + this.healthCheckRegistry.register("grpc_service", this.grpcService.healthCheck) + this.healthCheckRegistry.register("scrape_response_map_check", + mapHealthCheck(this.scrapeRequestMap, + this.configVals.internal.scrapeRequestMapUnhealthySize)) + this.healthCheckRegistry + .register("agent_scrape_request_queue", + object : HealthCheck() { + @Throws(Exception::class) + override fun check(): HealthCheck.Result { + val unhealthySize = configVals.internal.scrapeRequestQueueUnhealthySize + val vals = agentContextMap.entries + .filter { it.value.scrapeRequestQueueSize() >= unhealthySize } + .map { "${it.value} ${it.value.scrapeRequestQueueSize()}" } + .toList() + return if (vals.isEmpty()) + HealthCheck.Result.healthy() + else + HealthCheck.Result.unhealthy("Large scrapeRequestQueues: ${Joiner.on(", ").join(vals)}") + } + }) + } + + fun addAgentContext(agentContext: AgentContext) = this.agentContextMap.put(agentContext.agentId, agentContext) + + fun getAgentContext(agentId: String) = this.agentContextMap[agentId] + + fun removeAgentContext(agentId: String?): AgentContext? { + if (agentId == null) { + logger.error("Null agentId") + return null + } + + val agentContext = this.agentContextMap.remove(agentId) + if (agentContext != null) { + logger.info("Removed $agentContext") + agentContext.markInvalid() + } + else + logger.error("Missing AgentContext for agentId: $agentId") + + return agentContext + } + + fun addToScrapeRequestMap(scrapeRequest: ScrapeRequestWrapper) = this.scrapeRequestMap.put(scrapeRequest.scrapeId, + scrapeRequest) + + fun getFromScrapeRequestMap(scrapeId: Long) = this.scrapeRequestMap[scrapeId] + + fun removeFromScrapeRequestMap(scrapeId: Long) = this.scrapeRequestMap.remove(scrapeId) + + fun getAgentContextByPath(path: String) = this.pathMap[path] + + fun containsPath(path: String) = this.pathMap.containsKey(path) + + fun pathMapSize() = this.pathMap.size + + fun addPath(path: String, agentContext: AgentContext) { + synchronized(this.pathMap) { + this.pathMap.put(path, agentContext) + if (!this.isTestMode) + logger.info("Added path /$path for $agentContext") + } + } + + fun removePath(path: String, agentId: String, responseBuilder: UnregisterPathResponse.Builder) { + synchronized(this.pathMap) { + val agentContext = this.pathMap[path] + when { + agentContext == null -> { + val msg = "Unable to remove path /$path - path not found" + logger.info(msg) + responseBuilder.setValid(false).setReason(msg) + } + agentContext.agentId != agentId -> { + val msg = "Unable to remove path /$path - invalid agentId: $agentId (owner is ${agentContext.agentId})" + logger.info(msg) + responseBuilder.setValid(false).setReason(msg) + } + else + -> { + this.pathMap.remove(path) + if (!this.isTestMode) + logger.info("Removed path /$path for $agentContext") + responseBuilder.setValid(true).setReason("") + } + } + } + } + + fun removePathByAgentId(agentId: String?) { + if (agentId == null) { + logger.info("Null agentId") + return + } + + synchronized(this.pathMap) { + this.pathMap.forEach { k, v -> + if (v.agentId == agentId) { + val agentContext = this.pathMap.remove(k) + if (agentContext != null) + logger.info("Removed path /$k for $agentContext") + else + logger.error("Missing path /$k for agentId: $agentId") + } + } + } + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("proxyPort", this.httpService.port) + .add("adminService", this.adminService ?: "Disabled") + .add("metricsService", this.metricsService ?: "Disabled") + .toString() + + companion object { + private val logger = LoggerFactory.getLogger(Proxy::class.java) + + val AGENT_ID = "agent-id" + val ATTRIB_AGENT_ID: Attributes.Key = Attributes.Key.of(AGENT_ID) + + @JvmStatic + fun main(argv: Array) { + val options = ProxyOptions(argv) + + logger.info(getBanner("banners/proxy.txt")) + logger.info(getVersionDesc(false)) + + val proxy = Proxy(options, options.proxyPort!!, null, false) + proxy.startAsync() + } + } +} diff --git a/src/main/java/io/prometheus/agent/AgentClientInterceptor.java b/src/main/java/io/prometheus/agent/AgentClientInterceptor.java deleted file mode 100644 index 9bdeb3ec..00000000 --- a/src/main/java/io/prometheus/agent/AgentClientInterceptor.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.agent; - -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.ForwardingClientCall; -import io.grpc.ForwardingClientCallListener; -import io.grpc.Metadata; -import io.grpc.MethodDescriptor; -import io.prometheus.Agent; -import io.prometheus.Proxy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AgentClientInterceptor - implements ClientInterceptor { - - private static final Logger logger = LoggerFactory.getLogger(AgentClientInterceptor.class); - - private final Agent agent; - - public AgentClientInterceptor(Agent agent) { - this.agent = agent; - } - - @Override - public ClientCall interceptCall(final MethodDescriptor method, - final CallOptions callOptions, - final Channel next) { - // final String methodName = method.getFullMethodName(); - // logger.info("Intercepting {}", methodName); - return new ForwardingClientCall.SimpleForwardingClientCall(this.agent.getChannel().newCall(method, - callOptions)) { - @Override - public void start(final Listener responseListener, final Metadata headers) { - super.start( - new ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { - @Override - public void onHeaders(Metadata headers) { - // Grab agent_id from headers if not already assigned - if (agent.getAgentId() == null) { - final String agentId = headers.get(Metadata.Key.of(Proxy.AGENT_ID, Metadata.ASCII_STRING_MARSHALLER)); - if (agentId != null) { - agent.setAgentId(agentId); - logger.info("Assigned agentId to {}", agent); - } - else { - logger.error("Headers missing AGENT_ID key"); - } - } - super.onHeaders(headers); - } - }, - headers); - } - }; - } -} diff --git a/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt b/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt new file mode 100644 index 00000000..b9b4521c --- /dev/null +++ b/src/main/java/io/prometheus/agent/AgentClientInterceptor.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.agent + +import io.grpc.* +import io.prometheus.Agent +import io.prometheus.Proxy +import org.slf4j.LoggerFactory + +class AgentClientInterceptor(private val agent: Agent) : ClientInterceptor { + + override fun interceptCall(method: MethodDescriptor, + callOptions: CallOptions, + next: Channel): ClientCall = + // final String methodName = method.getFullMethodName(); + // logger.info("Intercepting {}", methodName); + object : ForwardingClientCall.SimpleForwardingClientCall(this.agent.channel!!.newCall(method, callOptions)) { + override fun start(responseListener: ClientCall.Listener, headers: Metadata) { + super.start( + object : ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { + override fun onHeaders(headers: Metadata?) { + // Grab agent_id from headers if not already assigned + if (agent.agentId == null) { + val agentId = headers!!.get(Metadata.Key.of(Proxy.AGENT_ID, + Metadata.ASCII_STRING_MARSHALLER)) + if (agentId != null) { + agent.agentId = agentId + logger.info("Assigned agentId to $agent") + } + else { + logger.error("Headers missing AGENT_ID key") + } + } + super.onHeaders(headers) + } + }, + headers) + } + } + + companion object { + private val logger = LoggerFactory.getLogger(AgentClientInterceptor::class.java) + } +} diff --git a/src/main/java/io/prometheus/agent/AgentMetrics.java b/src/main/java/io/prometheus/agent/AgentMetrics.java deleted file mode 100644 index 187669a9..00000000 --- a/src/main/java/io/prometheus/agent/AgentMetrics.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.agent; - -import io.prometheus.Agent; -import io.prometheus.client.Counter; -import io.prometheus.client.Gauge; -import io.prometheus.client.Summary; -import io.prometheus.common.SamplerGauge; - -public class AgentMetrics { - - public final Counter scrapeRequests = Counter.build() - .name("agent_scrape_requests") - .help("Agent scrape requests") - .labelNames("type") - .register(); - - public final Counter connects = Counter.build() - .name("agent_connect_count") - .help("Agent connect counts") - .labelNames("type") - .register(); - - public final Summary scrapeRequestLatency = Summary.build() - .name("agent_scrape_request_latency_seconds") - .help("Agent scrape request latency in seconds") - .labelNames("agent_name") - .register(); - - public AgentMetrics(final Agent agent) { - Gauge.build() - .name("agent_start_time_seconds") - .help("Agent start time in seconds") - .register() - .setToCurrentTime(); - - new SamplerGauge("agent_scrape_queue_size", - "Agent scrape response queue size", - agent::getScrapeResponseQueueSize).register(); - } -} diff --git a/src/main/java/io/prometheus/agent/AgentMetrics.kt b/src/main/java/io/prometheus/agent/AgentMetrics.kt new file mode 100644 index 00000000..1d5d32aa --- /dev/null +++ b/src/main/java/io/prometheus/agent/AgentMetrics.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.agent + +import io.prometheus.Agent +import io.prometheus.client.Collector +import io.prometheus.client.Counter +import io.prometheus.client.Gauge +import io.prometheus.client.Summary +import io.prometheus.common.SamplerGauge +import io.prometheus.common.SamplerGaugeData + +class AgentMetrics(agent: Agent) { + + val scrapeRequests: Counter = + Counter.build() + .name("agent_scrape_requests") + .help("Agent scrape requests") + .labelNames("type") + .register() + + val connects: Counter = + Counter.build() + .name("agent_connect_count") + .help("Agent connect counts") + .labelNames("type") + .register() + + val scrapeRequestLatency: Summary = + Summary.build() + .name("agent_scrape_request_latency_seconds") + .help("Agent scrape request latency in seconds") + .labelNames("agent_name") + .register() + + init { + Gauge.build() + .name("agent_start_time_seconds") + .help("Agent start time in seconds") + .register() + .setToCurrentTime() + + SamplerGauge("agent_scrape_queue_size", + "Agent scrape response queue size", + SamplerGaugeData { agent.scrapeResponseQueueSize.toDouble() }).register() + } +} diff --git a/src/main/java/io/prometheus/agent/AgentOptions.java b/src/main/java/io/prometheus/agent/AgentOptions.java deleted file mode 100644 index b4dd0203..00000000 --- a/src/main/java/io/prometheus/agent/AgentOptions.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.agent; - -import com.beust.jcommander.Parameter; -import com.google.common.collect.Iterables; -import io.prometheus.Agent; -import io.prometheus.common.BaseOptions; -import io.prometheus.common.ConfigVals; -import io.prometheus.common.EnvVars; - -import java.util.Collections; -import java.util.List; - -import static io.prometheus.common.EnvVars.AGENT_CONFIG; -import static io.prometheus.common.EnvVars.PROXY_HOSTNAME; -import static java.lang.String.format; - -public class AgentOptions - extends BaseOptions { - - @Parameter(names = {"-p", "--proxy"}, description = "Proxy hostname") - private String proxyHostname = null; - @Parameter(names = {"-n", "--name"}, description = "Agent name") - private String agentName = null; - - public AgentOptions(final List args, final boolean exitOnMissingConfig) { - this(Iterables.toArray(args != null ? args : Collections.emptyList(), String.class), exitOnMissingConfig); - } - - public AgentOptions(final String[] argv, final boolean exitOnMissingConfig) { - super(Agent.class.getName(), argv, AGENT_CONFIG.name(), exitOnMissingConfig); - this.assignConfigVals(this.getConfigVals()); - } - - @Override - protected void assignConfigVals(final ConfigVals configVals) { - if (this.proxyHostname == null) { - final String configHostname = configVals.agent.proxy.hostname; - this.proxyHostname = PROXY_HOSTNAME.getEnv(configHostname.contains(":") ? configHostname - : format("%s:%d", - configHostname, - configVals.agent.proxy.port)); - } - - if (this.agentName == null) - this.agentName = EnvVars.AGENT_NAME.getEnv(configVals.agent.name); - - this.assignAdminEnabled(configVals.agent.admin.enabled); - this.assignAdminPort(configVals.agent.admin.port); - this.assignMetricsEnabled(configVals.agent.metrics.enabled); - this.assignMetricsPort(configVals.agent.metrics.port); - } - - public String getProxyHostname() { return this.proxyHostname; } - - public String getAgentName() { return this.agentName; } -} \ No newline at end of file diff --git a/src/main/java/io/prometheus/agent/AgentOptions.kt b/src/main/java/io/prometheus/agent/AgentOptions.kt new file mode 100644 index 00000000..e4735e30 --- /dev/null +++ b/src/main/java/io/prometheus/agent/AgentOptions.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.agent + +import com.beust.jcommander.Parameter +import com.google.common.collect.Iterables +import io.prometheus.Agent +import io.prometheus.common.BaseOptions +import io.prometheus.common.EnvVars +import io.prometheus.common.EnvVars.AGENT_CONFIG +import io.prometheus.common.EnvVars.PROXY_HOSTNAME + +class AgentOptions(argv: Array, exitOnMissingConfig: Boolean) : BaseOptions(Agent::class.java.name, argv, AGENT_CONFIG.name, exitOnMissingConfig) { + + @Parameter(names = ["-p", "--proxy"], description = "Proxy hostname") + var proxyHostname: String? = null + private set + @Parameter(names = ["-n", "--name"], description = "Agent name") + var agentName: String? = null + private set + + constructor(args: List, exitOnMissingConfig: Boolean) : this(Iterables.toArray(args, + String::class.java), + exitOnMissingConfig) + + init { + this.parseOptions() + } + + override fun assignConfigVals() { + if (this.proxyHostname == null) { + val configHostname = this.configVals!!.agent.proxy.hostname + this.proxyHostname = PROXY_HOSTNAME.getEnv(if (configHostname.contains(":")) + configHostname + else + "$configHostname:${this.configVals!!.agent.proxy.port}") + } + + if (this.agentName == null) + this.agentName = EnvVars.AGENT_NAME.getEnv(this.configVals!!.agent.name) + + this.assignAdminEnabled(this.configVals!!.agent.admin.enabled) + this.assignAdminPort(this.configVals!!.agent.admin.port) + this.assignMetricsEnabled(this.configVals!!.agent.metrics.enabled) + this.assignMetricsPort(this.configVals!!.agent.metrics.port) + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/agent/PathContext.java b/src/main/java/io/prometheus/agent/PathContext.java deleted file mode 100644 index ac6d7412..00000000 --- a/src/main/java/io/prometheus/agent/PathContext.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.agent; - -import com.google.common.base.MoreObjects; -import io.prometheus.grpc.ScrapeRequest; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.net.HttpHeaders.ACCEPT; - -public class PathContext { - - private static final Logger logger = LoggerFactory.getLogger(PathContext.class); - - private final OkHttpClient okHttpClient; - private final long pathId; - private final String path; - private final String url; - private final Request.Builder request; - - public PathContext(final OkHttpClient okHttpClient, long pathId, String path, String url) { - this.okHttpClient = okHttpClient; - this.pathId = pathId; - this.path = path; - this.url = url; - this.request = new Request.Builder().url(url); - } - - public String getUrl() { - return this.url; - } - - public Response fetchUrl(final ScrapeRequest scrapeRequest) - throws IOException { - try { - logger.debug("Fetching {}", this); - final Request.Builder builder = !isNullOrEmpty(scrapeRequest.getAccept()) - ? this.request.header(ACCEPT, scrapeRequest.getAccept()) - : this.request; - return this.okHttpClient.newCall(builder.build()).execute(); - } - catch (IOException e) { - logger.info("Failed HTTP request: {} [{}: {}]", this.getUrl(), e.getClass().getSimpleName(), e.getMessage()); - throw e; - } - } - - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("path", "/" + path) - .add("url", url) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/agent/PathContext.kt b/src/main/java/io/prometheus/agent/PathContext.kt new file mode 100644 index 00000000..c75c1114 --- /dev/null +++ b/src/main/java/io/prometheus/agent/PathContext.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.agent + +import com.google.common.base.MoreObjects +import com.google.common.net.HttpHeaders.ACCEPT +import io.prometheus.grpc.ScrapeRequest +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import org.slf4j.LoggerFactory +import java.io.IOException + +class PathContext(private val okHttpClient: OkHttpClient, + private val pathId: Long, + private val path: String, + val url: String) { + private val request: Request.Builder = Request.Builder().url(this.url) + + @Throws(IOException::class) + fun fetchUrl(scrapeRequest: ScrapeRequest): Response = + try { + logger.debug("Fetching $this") + val builder = + if (!scrapeRequest.accept.isNullOrBlank()) + this.request.header(ACCEPT, scrapeRequest.accept) + else + this.request + this.okHttpClient.newCall(builder.build()).execute() + } catch (e: IOException) { + logger.info("Failed HTTP request: ${this.url} [${e.javaClass.simpleName}: ${e.message}]") + throw e + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("path", "/" + path) + .add("url", url) + .toString() + + companion object { + private val logger = LoggerFactory.getLogger(PathContext::class.java) + } +} diff --git a/src/main/java/io/prometheus/agent/RequestFailureException.java b/src/main/java/io/prometheus/agent/RequestFailureException.kt similarity index 72% rename from src/main/java/io/prometheus/agent/RequestFailureException.java rename to src/main/java/io/prometheus/agent/RequestFailureException.kt index 32d1f272..b386fafa 100644 --- a/src/main/java/io/prometheus/agent/RequestFailureException.java +++ b/src/main/java/io/prometheus/agent/RequestFailureException.kt @@ -14,14 +14,10 @@ * limitations under the License. */ -package io.prometheus.agent; +package io.prometheus.agent -public class RequestFailureException - extends Exception { - - private static final long serialVersionUID = 8748724180953791199L; - - public RequestFailureException(String message) { - super(message); - } +class RequestFailureException(message: String) : Exception(message) { + companion object { + private val serialVersionUID = 8748724180953791199L + } } diff --git a/src/main/java/io/prometheus/common/AdminConfig.java b/src/main/java/io/prometheus/common/AdminConfig.java deleted file mode 100644 index b5c73be9..00000000 --- a/src/main/java/io/prometheus/common/AdminConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.google.auto.value.AutoValue; - -@AutoValue -public abstract class AdminConfig { - public static AdminConfig create(final boolean enabled, - final int port, - final ConfigVals.Proxy2.Admin2 admin) { - - return new AutoValue_AdminConfig(enabled, - port, - admin.pingPath, - admin.versionPath, - admin.healthCheckPath, - admin.threadDumpPath); - } - - public static AdminConfig create(final boolean enabled, - final int port, - final ConfigVals.Agent.Admin admin) { - - return new AutoValue_AdminConfig(enabled, - port, - admin.pingPath, - admin.versionPath, - admin.healthCheckPath, - admin.threadDumpPath); - } - - public abstract boolean enabled(); - - public abstract int port(); - - public abstract String pingPath(); - - public abstract String versionPath(); - - public abstract String healthCheckPath(); - - public abstract String threadDumpPath(); -} - - diff --git a/src/main/java/io/prometheus/common/AdminConfig.kt b/src/main/java/io/prometheus/common/AdminConfig.kt new file mode 100644 index 00000000..422810bb --- /dev/null +++ b/src/main/java/io/prometheus/common/AdminConfig.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +data class AdminConfig(val enabled: Boolean, + val port: Int, + val pingPath: String, + val versionPath: String, + val healthCheckPath: String, + val threadDumpPath: String) { + + companion object { + fun create(enabled: Boolean, port: Int, admin: ConfigVals.Proxy2.Admin2) = + AdminConfig(enabled, + port, + admin.pingPath, + admin.versionPath, + admin.healthCheckPath, + admin.threadDumpPath) + + fun create(enabled: Boolean, port: Int, admin: ConfigVals.Agent.Admin) = + AdminConfig(enabled, + port, + admin.pingPath, + admin.versionPath, + admin.healthCheckPath, + admin.threadDumpPath) + } +} + + diff --git a/src/main/java/io/prometheus/common/AdminService.java b/src/main/java/io/prometheus/common/AdminService.java deleted file mode 100644 index 3db5d562..00000000 --- a/src/main/java/io/prometheus/common/AdminService.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.codahale.metrics.servlets.HealthCheckServlet; -import com.codahale.metrics.servlets.PingServlet; -import com.codahale.metrics.servlets.ThreadDumpServlet; -import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.AbstractIdleService; -import com.google.common.util.concurrent.MoreExecutors; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import static com.google.common.base.Strings.isNullOrEmpty; -import static java.lang.String.format; - -public class AdminService - extends AbstractIdleService { - - private final int port; - private final String pingPath; - private final String versionPath; - private final String healthCheckPath; - private final String threadDumpPath; - private final Server server; - - public AdminService(final GenericService service, - final int port, - final String pingPath, - final String versionPath, - final String healthCheckPath, - final String threadDumpPath) { - this.port = port; - this.pingPath = pingPath; - this.versionPath = versionPath; - this.healthCheckPath = healthCheckPath; - this.threadDumpPath = threadDumpPath; - this.server = new Server(this.port); - - final ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - this.server.setHandler(context); - - if (!isNullOrEmpty(this.pingPath)) - context.addServlet(new ServletHolder(new PingServlet()), "/" + this.pingPath); - if (!isNullOrEmpty(this.versionPath)) - context.addServlet(new ServletHolder(new VersionServlet()), "/" + this.versionPath); - if (!isNullOrEmpty(this.healthCheckPath)) - context.addServlet(new ServletHolder(new HealthCheckServlet(service.getHealthCheckRegistry())), - "/" + this.healthCheckPath); - if (!isNullOrEmpty(this.threadDumpPath)) - context.addServlet(new ServletHolder(new ThreadDumpServlet()), - "/" + this.threadDumpPath); - - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - @Override - protected void startUp() - throws Exception { - this.server.start(); - } - - @Override - protected void shutDown() - throws Exception { - this.server.stop(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("ping", format(":%d /%s", this.port, this.pingPath)) - .add("healthcheck", format(":%d /%s", this.port, this.healthCheckPath)) - .add("threaddump", format(":%d /%s", this.port, this.threadDumpPath)) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/common/AdminService.kt b/src/main/java/io/prometheus/common/AdminService.kt new file mode 100644 index 00000000..eb554a7f --- /dev/null +++ b/src/main/java/io/prometheus/common/AdminService.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import com.codahale.metrics.servlets.HealthCheckServlet +import com.codahale.metrics.servlets.PingServlet +import com.codahale.metrics.servlets.ThreadDumpServlet +import com.google.common.base.MoreObjects +import com.google.common.util.concurrent.AbstractIdleService +import com.google.common.util.concurrent.MoreExecutors +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.servlet.ServletContextHandler +import org.eclipse.jetty.servlet.ServletHolder + +class AdminService(service: GenericService, + private val port: Int, + private val pingPath: String, + private val versionPath: String, + private val healthCheckPath: String, + private val threadDumpPath: String) : AbstractIdleService() { + private val server: Server = Server(this.port) + + init { + val context = ServletContextHandler() + context.contextPath = "/" + this.server.handler = context + + if (this.pingPath.isNotBlank()) + context.addServlet(ServletHolder(PingServlet()), "/" + this.pingPath) + if (this.versionPath.isNotBlank()) + context.addServlet(ServletHolder(VersionServlet()), "/" + this.versionPath) + if (this.healthCheckPath.isNotBlank()) + context.addServlet(ServletHolder(HealthCheckServlet(service.healthCheckRegistry)), + "/" + this.healthCheckPath) + if (this.threadDumpPath.isNotBlank()) + context.addServlet(ServletHolder(ThreadDumpServlet()), "/" + this.threadDumpPath) + + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + override fun startUp() { + this.server.start() + } + + override fun shutDown() { + this.server.stop() + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("ping", ":${this.port}/${this.pingPath}") + .add("healthcheck", ":${this.port}/${this.healthCheckPath}") + .add("threaddump", ":${this.port}/${this.threadDumpPath}") + .toString() +} diff --git a/src/main/java/io/prometheus/common/BaseOptions.java b/src/main/java/io/prometheus/common/BaseOptions.java deleted file mode 100644 index 8d813fc4..00000000 --- a/src/main/java/io/prometheus/common/BaseOptions.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.beust.jcommander.DynamicParameter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import com.typesafe.config.ConfigParseOptions; -import com.typesafe.config.ConfigResolveOptions; -import com.typesafe.config.ConfigSyntax; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import static io.prometheus.common.EnvVars.ADMIN_ENABLED; -import static io.prometheus.common.EnvVars.ADMIN_PORT; -import static io.prometheus.common.EnvVars.METRICS_ENABLED; -import static io.prometheus.common.EnvVars.METRICS_PORT; -import static java.lang.String.format; - -public abstract class BaseOptions { - - private static final Logger logger = LoggerFactory.getLogger(BaseOptions.class); - private static final ConfigParseOptions PROPS = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES); - private static final String[] EMPTY_ARGV = {}; - - private final AtomicReference configRef = new AtomicReference<>(); - - private final String programName; - private final ConfigVals configVals; - - @Parameter(names = {"-c", "--conf", "--config"}, description = "Configuration file or url") - private String configName = null; - @Parameter(names = {"-r", "--admin"}, description = "Admin servlets enabled") - private Boolean adminEnabled = null; - @Parameter(names = {"-i", "--admin_port"}, description = "Admin servlets port") - private Integer adminPort = null; - @Parameter(names = {"-e", "--metrics"}, description = "Metrics enabled") - private Boolean metricsEnabled = null; - @Parameter(names = {"-m", "--metrics_port"}, description = "Metrics listen port") - private Integer metricsPort = null; - @Parameter(names = {"-v", "--version"}, description = "Print version info and exit", validateWith = Utils.VersionValidator.class) - private boolean version = false; - @Parameter(names = {"-u", "--usage"}, help = true) - private boolean usage = false; - @DynamicParameter(names = "-D", description = "Dynamic property assignment") - private Map dynamicParams = new HashMap<>(); - - protected BaseOptions(final String programName, - final String[] argv, - final String envConfig, - final boolean exitOnMissingConfig) { - this.programName = programName; - this.parseArgs(argv); - this.readConfig(envConfig, exitOnMissingConfig); - this.configVals = new ConfigVals(this.configRef.get()); - } - - public ConfigVals getConfigVals() { return this.configVals; } - - protected abstract void assignConfigVals(final ConfigVals configVals); - - private void parseArgs(final String[] argv) { - try { - final JCommander jcom = new JCommander(this); - jcom.setProgramName(this.programName); - jcom.setCaseSensitiveOptions(false); - jcom.parse(argv == null ? EMPTY_ARGV : argv); - - if (this.usage) { - jcom.usage(); - System.exit(0); - } - } - catch (ParameterException e) { - logger.error(e.getMessage(), e); - System.exit(1); - } - } - - protected void assignAdminEnabled(final boolean defaultVal) { - if (this.adminEnabled == null) - this.adminEnabled = ADMIN_ENABLED.getEnv(defaultVal); - } - - protected void assignAdminPort(final int defaultVal) { - if (this.adminPort == null) - this.adminPort = ADMIN_PORT.getEnv(defaultVal); - } - - protected void assignMetricsEnabled(final boolean defaultVal) { - if (this.metricsEnabled == null) - this.metricsEnabled = METRICS_ENABLED.getEnv(defaultVal); - } - - protected void assignMetricsPort(final int defaultVal) { - if (this.metricsPort == null) - this.metricsPort = METRICS_PORT.getEnv(defaultVal); - } - - private void readConfig(final String envConfig, final boolean exitOnMissingConfig) { - final Config config = Utils.readConfig(this.configName, - envConfig, - ConfigParseOptions.defaults().setAllowMissing(false), - ConfigFactory.load().resolve(), - exitOnMissingConfig) - .resolve(ConfigResolveOptions.defaults()); - this.configRef.set(config.resolve()); - - this.dynamicParams.forEach( - (key, value) -> { - // Strip quotes - final String prop = format("%s=%s", key, value.startsWith("\"") && value.endsWith("\"") - ? value.substring(1, value.length() - 1) - : value); - System.setProperty(key, prop); - final Config newConfig = ConfigFactory.parseString(prop, PROPS); - configRef.set(newConfig.withFallback(this.configRef.get()).resolve()); - }); - } - - public boolean isAdminEnabled() { return this.adminEnabled; } - - public int getAdminPort() { return this.adminPort; } - - public boolean isMetricsEnabled() { return this.metricsEnabled; } - - public int getMetricsPort() { return this.metricsPort; } - - public Map getDynamicParams() { return this.dynamicParams; } -} diff --git a/src/main/java/io/prometheus/common/BaseOptions.kt b/src/main/java/io/prometheus/common/BaseOptions.kt new file mode 100644 index 00000000..c52b20b3 --- /dev/null +++ b/src/main/java/io/prometheus/common/BaseOptions.kt @@ -0,0 +1,205 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import com.beust.jcommander.DynamicParameter +import com.beust.jcommander.JCommander +import com.beust.jcommander.Parameter +import com.beust.jcommander.ParameterException +import com.typesafe.config.* +import io.prometheus.common.EnvVars.* +import org.slf4j.LoggerFactory +import java.io.File +import java.io.FileNotFoundException +import java.net.URL + +abstract class BaseOptions protected constructor(private val progName: String, + private val argv: Array, + private val envConfig: String, + private val exitOnMissingConfig: Boolean) { + + @Parameter(names = ["-c", "--conf", "--config"], description = "Configuration file or url") + private var configName: String? = null + + @Parameter(names = ["-r", "--admin"], description = "Admin servlets enabled") + var adminEnabled: Boolean = false + private set + + @Parameter(names = ["-i", "--admin_port"], description = "Admin servlets port") + var adminPort: Int? = null + private set + + @Parameter(names = ["-e", "--metrics"], description = "Metrics enabled") + var metricsEnabled: Boolean = false + private set + + @Parameter(names = ["-m", "--metrics_port"], description = "Metrics listen port") + var metricsPort: Int? = null + private set + + @Parameter(names = ["-v", "--version"], + description = "Print version info and exit", + validateWith = [(VersionValidator::class)]) + private var version = false + + @Parameter(names = ["-u", "--usage"], help = true) + private var usage = false + + @DynamicParameter(names = ["-D"], description = "Dynamic property assignment") + var dynamicParams = mutableMapOf() + private set + + private var config: Config? = null + + var configVals: ConfigVals? = null + private set + + protected abstract fun assignConfigVals() + + protected fun parseOptions() { + this.parseArgs(this.argv) + this.readConfig(this.envConfig, this.exitOnMissingConfig) + this.configVals = ConfigVals(this.config) + this.assignConfigVals() + } + + private fun parseArgs(argv: Array?) { + try { + val jcom = + JCommander(this).apply { + programName = progName + setCaseSensitiveOptions(false) + parse(*argv ?: arrayOf()) + } + + if (this.usage) { + jcom.usage() + System.exit(0) + } + } catch (e: ParameterException) { + logger.error(e.message, e) + System.exit(1) + } + } + + protected fun assignAdminEnabled(defaultVal: Boolean) { + if (!this.adminEnabled) + this.adminEnabled = ADMIN_ENABLED.getEnv(defaultVal) + } + + protected fun assignAdminPort(defaultVal: Int) { + if (this.adminPort == null) + this.adminPort = ADMIN_PORT.getEnv(defaultVal) + } + + protected fun assignMetricsEnabled(defaultVal: Boolean) { + if (!this.metricsEnabled) + this.metricsEnabled = METRICS_ENABLED.getEnv(defaultVal) + } + + protected fun assignMetricsPort(defaultVal: Int) { + if (this.metricsPort == null) + this.metricsPort = METRICS_PORT.getEnv(defaultVal) + } + + private fun readConfig(envConfig: String, exitOnMissingConfig: Boolean) { + + this.config = readConfig(this.configName, + envConfig, + ConfigParseOptions.defaults().setAllowMissing(false), + ConfigFactory.load().resolve(), + exitOnMissingConfig) + .resolve(ConfigResolveOptions.defaults()) + .resolve() + + this.dynamicParams.forEach { k, v -> + // Strip quotes + val qval = if (v.startsWith("\"") && v.endsWith("\"")) v.substring(1, v.length - 1) else v + val prop = "$k=$qval" + System.setProperty(k, prop) + val newConfig = ConfigFactory.parseString(prop, PROPS) + this.config = newConfig.withFallback(this.config).resolve() + } + } + + private fun readConfig(cliConfig: String?, + envConfig: String, + configParseOptions: ConfigParseOptions, + fallback: Config, + exitOnMissingConfig: Boolean): Config { + + val configName = cliConfig ?: System.getenv(envConfig) + + when { + configName.isNullOrBlank() -> { + if (exitOnMissingConfig) { + logger.error("A configuration file or url must be specified with --getConfig or \$$envConfig") + System.exit(1) + } + return fallback + } + + configName.isUrlPrefix() -> { + try { + val configSyntax = getConfigSyntax(configName) + return ConfigFactory.parseURL(URL(configName), configParseOptions.setSyntax(configSyntax)) + .withFallback(fallback) + } catch (e: Exception) { + if (e.cause is FileNotFoundException) + logger.error("Invalid getConfig url: $configName") + else + logger.error("Exception: ${e.javaClass.simpleName} - ${e.message}", e) + } + + } + else -> { + try { + return ConfigFactory.parseFileAnySyntax(File(configName), configParseOptions).withFallback(fallback) + } catch (e: Exception) { + if (e.cause is FileNotFoundException) + logger.error("Invalid getConfig filename: $configName") + else + logger.error("Exception: ${e.javaClass.simpleName} - ${e.message}", e) + } + } + } + + System.exit(1) + return fallback // Never reached + } + + private fun getConfigSyntax(configName: String): ConfigSyntax = + when { + configName.isJsonSuffix() -> ConfigSyntax.JSON + configName.isPropertiesSuffix() -> ConfigSyntax.PROPERTIES + else -> ConfigSyntax.CONF + } + + private fun String.isUrlPrefix() = + this.toLowerCase().startsWith("http://") || this.toLowerCase().startsWith("https://") + + private fun String.isJsonSuffix() = + this.toLowerCase().endsWith(".json") || this.toLowerCase().endsWith(".jsn") + + private fun String.isPropertiesSuffix() = + this.toLowerCase().endsWith(".properties") || this.toLowerCase().endsWith(".props") + + companion object { + private val logger = LoggerFactory.getLogger(BaseOptions::class.java) + private val PROPS = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.PROPERTIES) + } +} diff --git a/src/main/java/io/prometheus/common/ConfigVals.java b/src/main/java/io/prometheus/common/ConfigVals.java index 878d6d1f..90887579 100644 --- a/src/main/java/io/prometheus/common/ConfigVals.java +++ b/src/main/java/io/prometheus/common/ConfigVals.java @@ -1,20 +1,4 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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. - */ - -// generated by tscfg 0.8.3 on Sat Nov 04 12:56:05 PDT 2017 +// generated by tscfg 0.8.3 on Sun Dec 24 08:21:31 PST 2017 // source: etc/config/config.conf package io.prometheus.common; @@ -101,7 +85,7 @@ public Zipkin(com.typesafe.config.Config c) { this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); this.grpcReportingEnabled = c.hasPathOrNull("grpcReportingEnabled") && c.getBoolean("grpcReportingEnabled"); this.hostname = c.hasPathOrNull("hostname") ? c.getString("hostname") : "localhost"; - this.path = c.hasPathOrNull("path") ? c.getString("path") : "api/v1/spans"; + this.path = c.hasPathOrNull("path") ? c.getString("path") : "api/v2/spans"; this.port = c.hasPathOrNull("port") ? c.getInt("port") : 9411; this.serviceName = c.hasPathOrNull("serviceName") ? c.getString("serviceName") : "prometheus-agent"; } @@ -119,7 +103,6 @@ public static class Metrics { public final boolean standardExportsEnabled; public final boolean threadExportsEnabled; public final boolean versionInfoExportsEnabled; - public static class Grpc { public final boolean allMetricsReported; public final boolean metricsEnabled; @@ -269,7 +252,7 @@ public Zipkin2(com.typesafe.config.Config c) { this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); this.grpcReportingEnabled = c.hasPathOrNull("grpcReportingEnabled") && c.getBoolean("grpcReportingEnabled"); this.hostname = c.hasPathOrNull("hostname") ? c.getString("hostname") : "localhost"; - this.path = c.hasPathOrNull("path") ? c.getString("path") : "api/v1/spans"; + this.path = c.hasPathOrNull("path") ? c.getString("path") : "api/v2/spans"; this.port = c.hasPathOrNull("port") ? c.getInt("port") : 9411; this.serviceName = c.hasPathOrNull("serviceName") ? c.getString("serviceName") : "prometheus-proxy"; } @@ -287,7 +270,6 @@ public static class Metrics2 { public final boolean standardExportsEnabled; public final boolean threadExportsEnabled; public final boolean versionInfoExportsEnabled; - public static class Grpc2 { public final boolean allMetricsReported; public final boolean metricsEnabled; diff --git a/src/main/java/io/prometheus/common/EnvVars.java b/src/main/java/io/prometheus/common/EnvVars.java deleted file mode 100644 index f9519f3b..00000000 --- a/src/main/java/io/prometheus/common/EnvVars.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import static java.lang.Boolean.parseBoolean; -import static java.lang.System.getenv; - -public enum EnvVars { - - // Proxy - PROXY_CONFIG, - PROXY_PORT, - AGENT_PORT, - - // Agent - AGENT_CONFIG, - PROXY_HOSTNAME, - AGENT_NAME, - - // Common - METRICS_ENABLED, - METRICS_PORT, - ADMIN_ENABLED, - ADMIN_PORT; - - private String getEnv() { return getenv(this.name()); } - - public String getEnv(final String defaultVal) { - return this.getEnv() != null ? this.getEnv() : defaultVal; - } - - public boolean getEnv(final boolean defaultVal) { - return this.getEnv() != null ? parseBoolean(this.getEnv()) : defaultVal; - } - - public int getEnv(final int defaultVal) { - return this.getEnv() != null ? Integer.parseInt(this.getEnv()) : defaultVal; - } -} diff --git a/src/main/java/io/prometheus/common/EnvVars.kt b/src/main/java/io/prometheus/common/EnvVars.kt new file mode 100644 index 00000000..c6921eea --- /dev/null +++ b/src/main/java/io/prometheus/common/EnvVars.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import java.lang.System.getenv + +enum class EnvVars { + + // Proxy + PROXY_CONFIG, + PROXY_PORT, + AGENT_PORT, + + // Agent + AGENT_CONFIG, + PROXY_HOSTNAME, + AGENT_NAME, + + // Common + METRICS_ENABLED, + METRICS_PORT, + ADMIN_ENABLED, + ADMIN_PORT; + + private val env: String? + get() = getenv(this.name) + + fun getEnv(defaultVal: String): String? = this.env ?: defaultVal + + fun getEnv(defaultVal: Boolean): Boolean = this.env?.toBoolean() ?: defaultVal + + fun getEnv(defaultVal: Int): Int = this.env?.toInt() ?: defaultVal +} diff --git a/src/main/java/io/prometheus/common/GenericService.java b/src/main/java/io/prometheus/common/GenericService.java deleted file mode 100644 index fb8b48e6..00000000 --- a/src/main/java/io/prometheus/common/GenericService.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.health.HealthCheck; -import com.codahale.metrics.health.HealthCheckRegistry; -import com.codahale.metrics.health.jvm.ThreadDeadlockHealthCheck; -import com.codahale.metrics.jmx.JmxReporter; -import com.github.kristofa.brave.Brave; -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.AbstractExecutionThreadService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.Service; -import com.google.common.util.concurrent.ServiceManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.Closeable; -import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; - -import static java.lang.String.format; - -public abstract class GenericService - extends AbstractExecutionThreadService - implements Closeable { - - private static final Logger logger = LoggerFactory.getLogger(GenericService.class); - - private final MetricRegistry metricRegistry = new MetricRegistry(); - private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); - private final List services = Lists.newArrayList(this); - - private final ConfigVals configVals; - private final boolean testMode; - private final JmxReporter jmxReporter; - private final MetricsService metricsService; - private final ZipkinReporterService zipkinReporterService; - private final AdminService adminService; - private ServiceManager serviceManager = null; - - protected GenericService(final ConfigVals configVals, - final AdminConfig adminConfig, - final MetricsConfig metricsConfig, - final ZipkinConfig zipkinConfig, - final boolean testMode) { - this.configVals = configVals; - this.testMode = testMode; - - this.jmxReporter = JmxReporter.forRegistry(this.metricRegistry).build(); - - if (adminConfig.enabled()) { - this.adminService = new AdminService(this, - adminConfig.port(), - adminConfig.pingPath(), - adminConfig.versionPath(), - adminConfig.healthCheckPath(), - adminConfig.threadDumpPath()); - this.addService(this.adminService); - } - else { - logger.info("Admin service disabled"); - this.adminService = null; - } - - if (metricsConfig.enabled()) { - final int port = metricsConfig.port(); - final String path = metricsConfig.path(); - this.metricsService = new MetricsService(port, path); - this.addService(this.metricsService); - SystemMetrics.initialize(metricsConfig.standardExportsEnabled(), - metricsConfig.memoryPoolsExportsEnabled(), - metricsConfig.garbageCollectorExportsEnabled(), - metricsConfig.threadExportsEnabled(), - metricsConfig.classLoadingExportsEnabled(), - metricsConfig.versionInfoExportsEnabled()); - } - else { - logger.info("Metrics service disabled"); - this.metricsService = null; - } - - if (zipkinConfig.enabled()) { - final String zipkinUrl = format("http://%s:%d/%s", - zipkinConfig.hostname(), zipkinConfig.port(), zipkinConfig.path()); - this.zipkinReporterService = new ZipkinReporterService(zipkinUrl, zipkinConfig.serviceName()); - this.addService(this.zipkinReporterService); - } - else { - logger.info("Zipkin reporter service disabled"); - this.zipkinReporterService = null; - } - - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - public void init() { - this.serviceManager = new ServiceManager(this.services); - this.serviceManager.addListener(this.newListener()); - this.registerHealthChecks(); - } - - @Override - protected void startUp() - throws Exception { - super.startUp(); - if (this.jmxReporter != null) - this.jmxReporter.start(); - if (this.isMetricsEnabled()) - this.metricsService.startAsync(); - if (this.isAdminEnabled()) - this.adminService.startAsync(); - Runtime.getRuntime().addShutdownHook(Utils.shutDownHookAction(this)); - } - - @Override - protected void shutDown() - throws Exception { - if (this.isAdminEnabled()) - this.adminService.shutDown(); - if (this.isMetricsEnabled()) - this.metricsService.stopAsync(); - if (this.isZipkinEnabled()) - this.zipkinReporterService.shutDown(); - if (this.jmxReporter != null) - this.jmxReporter.stop(); - super.shutDown(); - } - - @Override - public void close() - throws IOException { - this.stopAsync(); - } - - protected void addService(final Service service) { this.services.add(service); } - - protected void addServices(final Service service, final Service... services) { - this.services.addAll(Lists.asList(service, services)); - } - - protected void registerHealthChecks() { - this.getHealthCheckRegistry().register("thread_deadlock", new ThreadDeadlockHealthCheck()); - if (this.isMetricsEnabled()) - this.getHealthCheckRegistry().register("metrics_service", this.metricsService.getHealthCheck()); - this.getHealthCheckRegistry() - .register( - "all_services_healthy", - new HealthCheck() { - @Override - protected Result check() - throws Exception { - return serviceManager.isHealthy() - ? Result.healthy() - : Result.unhealthy(format("Incorrect state: %s", - Joiner.on(", ") - .join(serviceManager.servicesByState() - .entries() - .stream() - .filter(kv -> kv.getKey() != State.RUNNING) - .peek(kv -> logger.warn("Incorrect state - {}: {}", - kv.getKey(), kv.getValue())) - .map(kv -> format("%s: %s", kv.getKey(), kv.getValue())) - .collect(Collectors.toList())))); - } - }); - } - - protected ServiceManager.Listener newListener() { - final String serviceName = this.getClass().getSimpleName(); - return new ServiceManager.Listener() { - @Override - public void healthy() { logger.info("All {} services healthy", serviceName); } - - @Override - public void stopped() { logger.info("All {} services stopped", serviceName); } - - @Override - public void failure(final Service service) { logger.info("{} service failed: {}", serviceName, service); } - }; - } - - protected ConfigVals getGenericConfigVals() { return this.configVals; } - - public MetricRegistry getMetricRegistry() { return this.metricRegistry; } - - public HealthCheckRegistry getHealthCheckRegistry() { return this.healthCheckRegistry; } - - public boolean isTestMode() { return this.testMode; } - - public boolean isZipkinEnabled() { return this.zipkinReporterService != null; } - - public boolean isAdminEnabled() { return this.adminService != null; } - - public boolean isMetricsEnabled() { return this.metricsService != null; } - - protected AdminService getAdminService() { return this.adminService; } - - protected MetricsService getMetricsService() { return this.metricsService; } - - public ZipkinReporterService getZipkinReporterService() { return this.zipkinReporterService; } - - public Brave getBrave() { return this.getZipkinReporterService().getBrave(); } -} diff --git a/src/main/java/io/prometheus/common/GenericService.kt b/src/main/java/io/prometheus/common/GenericService.kt new file mode 100644 index 00000000..f280fb26 --- /dev/null +++ b/src/main/java/io/prometheus/common/GenericService.kt @@ -0,0 +1,177 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import brave.Tracing +import com.codahale.metrics.MetricRegistry +import com.codahale.metrics.health.HealthCheck +import com.codahale.metrics.health.HealthCheckRegistry +import com.codahale.metrics.health.jvm.ThreadDeadlockHealthCheck +import com.codahale.metrics.jmx.JmxReporter +import com.google.common.base.Joiner +import com.google.common.util.concurrent.AbstractExecutionThreadService +import com.google.common.util.concurrent.MoreExecutors +import com.google.common.util.concurrent.Service +import com.google.common.util.concurrent.ServiceManager +import org.slf4j.LoggerFactory +import java.io.Closeable + +abstract class GenericService protected constructor(protected val genericConfigVals: ConfigVals, + adminConfig: AdminConfig, + metricsConfig: MetricsConfig, + zipkinConfig: ZipkinConfig, + val isTestMode: Boolean) : AbstractExecutionThreadService(), Closeable { + + private val metricRegistry = MetricRegistry() + val healthCheckRegistry = HealthCheckRegistry() + + private val services = mutableListOf(this) + private val jmxReporter = JmxReporter.forRegistry(this.metricRegistry).build() + private var serviceManager: ServiceManager? = null + + protected val adminService: AdminService? + protected val metricsService: MetricsService? + var tracing: Tracing? = null + + val zipkinReporterService: ZipkinReporterService? + + val zipkinEnabled: Boolean + get() = this.zipkinReporterService != null + + val metricsEnabled: Boolean + get() = this.metricsService != null + + init { + if (adminConfig.enabled) { + this.adminService = AdminService(this, + adminConfig.port, + adminConfig.pingPath, + adminConfig.versionPath, + adminConfig.healthCheckPath, + adminConfig.threadDumpPath) + this.addService(this.adminService) + } + else { + logger.info("Admin service disabled") + this.adminService = null + } + + if (metricsConfig.enabled) { + this.metricsService = MetricsService(metricsConfig.port, metricsConfig.path) + this.addService(this.metricsService) + SystemMetrics.initialize(metricsConfig.standardExportsEnabled, + metricsConfig.memoryPoolsExportsEnabled, + metricsConfig.garbageCollectorExportsEnabled, + metricsConfig.threadExportsEnabled, + metricsConfig.classLoadingExportsEnabled, + metricsConfig.versionInfoExportsEnabled) + } + else { + logger.info("Metrics service disabled") + this.metricsService = null + } + + if (zipkinConfig.enabled) { + val url = "http://${zipkinConfig.hostname}:${zipkinConfig.port}/${zipkinConfig.path}" + this.zipkinReporterService = ZipkinReporterService(url) + this.tracing = this.zipkinReporterService.newTracing(zipkinConfig.serviceName) + this.addService(this.zipkinReporterService) + } + else { + logger.info("Zipkin reporter service disabled") + this.zipkinReporterService = null + } + + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + fun initService() { + this.serviceManager = ServiceManager(this.services) + this.serviceManager!!.addListener(this.newListener()) + this.registerHealthChecks() + } + + override fun startUp() { + super.startUp() + this.zipkinReporterService?.startAsync() + this.jmxReporter?.start() + this.metricsService?.startAsync() + this.adminService?.startAsync() + Runtime.getRuntime().addShutdownHook(shutDownHookAction(this)) + } + + override fun shutDown() { + this.tracing?.close() + this.adminService?.stopAsync() + this.metricsService?.stopAsync() + this.jmxReporter?.stop() + this.zipkinReporterService?.stopAsync() + super.shutDown() + } + + override fun close() { + this.stopAsync() + } + + private fun addService(service: Service) { + logger.info("Adding service $service") + this.services.add(service) + } + + protected fun addServices(service: Service, vararg services: Service) { + this.addService(service) + services.forEach { this.addService(it) } + } + + protected open fun registerHealthChecks() { + this.healthCheckRegistry.register("thread_deadlock", ThreadDeadlockHealthCheck()) + if (this.metricsEnabled) + this.healthCheckRegistry.register("metrics_service", this.metricsService!!.healthCheck) + this.healthCheckRegistry + .register( + "all_services_healthy", + object : HealthCheck() { + @Throws(Exception::class) + override fun check(): HealthCheck.Result { + return if (serviceManager!!.isHealthy) + HealthCheck.Result.healthy() + else { + val vals = serviceManager!!.servicesByState() + .entries() + .filter { it.key !== Service.State.RUNNING } + .onEach { logger.warn("Incorrect state - ${it.key}: ${it.value}") } + .map { "${it.key}: ${it.value}" } + .toList() + HealthCheck.Result.unhealthy("Incorrect state: ${Joiner.on(", ").join(vals)}") + } + } + }) + } + + private fun newListener(): ServiceManager.Listener { + val serviceName = this.javaClass.simpleName + return object : ServiceManager.Listener() { + override fun healthy() = logger.info("All $serviceName services healthy") + override fun stopped() = logger.info("All $serviceName services stopped") + override fun failure(service: Service?) = logger.info("$serviceName service failed: $service") + } + } + + companion object { + private val logger = LoggerFactory.getLogger(GenericService::class.java) + } +} diff --git a/src/main/java/io/prometheus/common/GenericServiceListener.java b/src/main/java/io/prometheus/common/GenericServiceListener.java deleted file mode 100644 index 787fd055..00000000 --- a/src/main/java/io/prometheus/common/GenericServiceListener.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.google.common.util.concurrent.Service; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GenericServiceListener - extends Service.Listener { - - private static final Logger logger = LoggerFactory.getLogger(GenericServiceListener.class); - - private final Service service; - - public GenericServiceListener(Service service) { - this.service = service; - } - - @Override - public void starting() { - super.starting(); - logger.info("Starting {}", this.service); - } - - @Override - public void running() { - super.running(); - logger.info("Running {}", this.service); - } - - @Override - public void stopping(Service.State from) { - super.stopping(from); - logger.info("Stopping {}", this.service); - } - - @Override - public void terminated(Service.State from) { - super.terminated(from); - logger.info("Terminated {}", this.service); - } - - @Override - public void failed(Service.State from, Throwable t) { - super.failed(from, t); - logger.info("Failed on {} {}", from, this.service, t); - } -} diff --git a/src/main/java/io/prometheus/common/GenericServiceListener.kt b/src/main/java/io/prometheus/common/GenericServiceListener.kt new file mode 100644 index 00000000..f6531a02 --- /dev/null +++ b/src/main/java/io/prometheus/common/GenericServiceListener.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import com.google.common.util.concurrent.Service +import org.slf4j.LoggerFactory + +class GenericServiceListener(private val service: Service) : Service.Listener() { + + override fun starting() { + super.starting() + logger.info("Starting ${this.service}") + } + + override fun running() { + super.running() + logger.info("Running ${this.service}") + } + + override fun stopping(from: Service.State) { + super.stopping(from) + logger.info("Stopping ${this.service}") + } + + override fun terminated(from: Service.State) { + super.terminated(from) + logger.info("Terminated ${this.service}") + } + + override fun failed(from: Service.State, t: Throwable) { + super.failed(from, t) + logger.info("Failed on $from ${this.service}", t) + } + + companion object { + private val logger = LoggerFactory.getLogger(GenericServiceListener::class.java) + } +} diff --git a/src/main/java/io/prometheus/common/InstrumentedThreadFactory.java b/src/main/java/io/prometheus/common/InstrumentedThreadFactory.java deleted file mode 100644 index 0a45f754..00000000 --- a/src/main/java/io/prometheus/common/InstrumentedThreadFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import io.prometheus.client.Counter; -import io.prometheus.client.Gauge; - -import java.util.concurrent.ThreadFactory; - -import static java.lang.String.format; - -public class InstrumentedThreadFactory - implements ThreadFactory { - - private final ThreadFactory delegate; - private final Counter created; - private final Gauge running; - private final Counter terminated; - - public InstrumentedThreadFactory(final ThreadFactory delegate, final String name, final String help) { - Preconditions.checkNotNull(name); - Preconditions.checkNotNull(help); - this.delegate = Preconditions.checkNotNull(delegate); - this.created = Counter.build() - .name(format("%s_threads_created", name)) - .help(format("%s threads created", help)) - .register(); - this.running = Gauge.build() - .name(format("%s_threads_running", name)) - .help(format("%s threads running", help)) - .register(); - this.terminated = Counter.build() - .name(format("%s_threads_terminated", name)) - .help(format("%s threads terminated", help)) - .register(); - } - - public static ThreadFactory newInstrumentedThreadFactory(final String name, - final String help, - final boolean daemon) { - final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(name + "-%d") - .setDaemon(daemon) - .build(); - return new InstrumentedThreadFactory(threadFactory, name, help); - } - - @Override - public Thread newThread(final Runnable runnable) { - final Runnable wrappedRunnable = new InstrumentedRunnable(runnable); - final Thread thread = this.delegate.newThread(wrappedRunnable); - this.created.inc(); - return thread; - } - - private class InstrumentedRunnable - implements Runnable { - - private final Runnable runnable; - - private InstrumentedRunnable(Runnable runnable) { - this.runnable = runnable; - } - - @Override - public void run() { - running.inc(); - try { - runnable.run(); - } - finally { - running.dec(); - terminated.inc(); - } - } - } - -} diff --git a/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt b/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt new file mode 100644 index 00000000..58c84459 --- /dev/null +++ b/src/main/java/io/prometheus/common/InstrumentedThreadFactory.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import com.google.common.base.Preconditions +import com.google.common.util.concurrent.ThreadFactoryBuilder +import io.prometheus.client.Counter +import io.prometheus.client.Gauge +import java.util.concurrent.ThreadFactory + +class InstrumentedThreadFactory(delegate: ThreadFactory, name: String, help: String) : ThreadFactory { + + private val delegate = Preconditions.checkNotNull(delegate) + private val created: Counter + private val running: Gauge + private val terminated: Counter + + init { + Preconditions.checkNotNull(name) + Preconditions.checkNotNull(help) + this.created = + Counter.build() + .name("${name}_threads_created") + .help("$help threads created") + .register() + this.running = + Gauge.build() + .name("${name}_threads_running") + .help("$help threads running") + .register() + this.terminated = + Counter.build() + .name("${name}_threads_terminated") + .help("$help threads terminated") + .register() + } + + override fun newThread(runnable: Runnable): Thread { + val wrappedRunnable = InstrumentedRunnable(runnable) + val thread = this.delegate.newThread(wrappedRunnable) + this.created.inc() + return thread + } + + private inner class InstrumentedRunnable constructor(private val runnable: Runnable) : Runnable { + override fun run() { + running.inc() + try { + runnable.run() + } finally { + running.dec() + terminated.inc() + } + } + } + + companion object { + fun newInstrumentedThreadFactory(name: String, + help: String, + daemon: Boolean): ThreadFactory { + val threadFactory = ThreadFactoryBuilder().setNameFormat(name + "-%d").setDaemon(daemon).build() + return InstrumentedThreadFactory(threadFactory, name, help) + } + } + +} diff --git a/src/main/java/io/prometheus/common/MetricsConfig.java b/src/main/java/io/prometheus/common/MetricsConfig.java deleted file mode 100644 index 44bfec4e..00000000 --- a/src/main/java/io/prometheus/common/MetricsConfig.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.google.auto.value.AutoValue; - -@AutoValue -public abstract class MetricsConfig { - public static MetricsConfig create(final boolean enabled, - final int port, - final ConfigVals.Proxy2.Metrics2 metrics) { - - return new AutoValue_MetricsConfig(enabled, - port, - metrics.path, - metrics.standardExportsEnabled, - metrics.memoryPoolsExportsEnabled, - metrics.garbageCollectorExportsEnabled, - metrics.threadExportsEnabled, - metrics.classLoadingExportsEnabled, - metrics.versionInfoExportsEnabled); - } - - public static MetricsConfig create(final boolean enabled, - final int port, - final ConfigVals.Agent.Metrics metrics) { - - return new AutoValue_MetricsConfig(enabled, - port, - metrics.path, - metrics.standardExportsEnabled, - metrics.memoryPoolsExportsEnabled, - metrics.garbageCollectorExportsEnabled, - metrics.threadExportsEnabled, - metrics.classLoadingExportsEnabled, - metrics.versionInfoExportsEnabled); - } - - public abstract boolean enabled(); - - public abstract int port(); - - public abstract String path(); - - public abstract boolean standardExportsEnabled(); - - public abstract boolean memoryPoolsExportsEnabled(); - - public abstract boolean garbageCollectorExportsEnabled(); - - public abstract boolean threadExportsEnabled(); - - public abstract boolean classLoadingExportsEnabled(); - - public abstract boolean versionInfoExportsEnabled(); -} - - diff --git a/src/main/java/io/prometheus/common/MetricsConfig.kt b/src/main/java/io/prometheus/common/MetricsConfig.kt new file mode 100644 index 00000000..ceb4b89a --- /dev/null +++ b/src/main/java/io/prometheus/common/MetricsConfig.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +data class MetricsConfig(val enabled: Boolean, + val port: Int, + val path: String, + val standardExportsEnabled: Boolean, + val memoryPoolsExportsEnabled: Boolean, + val garbageCollectorExportsEnabled: Boolean, + val threadExportsEnabled: Boolean, + val classLoadingExportsEnabled: Boolean, + val versionInfoExportsEnabled: Boolean) { + + companion object { + fun create(enabled: Boolean, port: Int, metrics: ConfigVals.Proxy2.Metrics2) = + MetricsConfig(enabled, + port, + metrics.path, + metrics.standardExportsEnabled, + metrics.memoryPoolsExportsEnabled, + metrics.garbageCollectorExportsEnabled, + metrics.threadExportsEnabled, + metrics.classLoadingExportsEnabled, + metrics.versionInfoExportsEnabled) + + fun create(enabled: Boolean, port: Int, metrics: ConfigVals.Agent.Metrics) = + MetricsConfig(enabled, + port, + metrics.path, + metrics.standardExportsEnabled, + metrics.memoryPoolsExportsEnabled, + metrics.garbageCollectorExportsEnabled, + metrics.threadExportsEnabled, + metrics.classLoadingExportsEnabled, + metrics.versionInfoExportsEnabled) + } +} + + diff --git a/src/main/java/io/prometheus/common/MetricsService.java b/src/main/java/io/prometheus/common/MetricsService.java deleted file mode 100644 index 4ef59093..00000000 --- a/src/main/java/io/prometheus/common/MetricsService.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.codahale.metrics.health.HealthCheck; -import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.AbstractIdleService; -import com.google.common.util.concurrent.MoreExecutors; -import io.prometheus.client.exporter.MetricsServlet; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import static java.lang.String.format; - -public class MetricsService - extends AbstractIdleService { - - private final int port; - private final String path; - private final Server server; - private final HealthCheck healthCheck = - new HealthCheck() { - @Override - protected Result check() - throws Exception { - return server.isRunning() ? Result.healthy() : Result.unhealthy("Jetty server not running"); - } - }; - - public MetricsService(final int port, final String path) { - this.port = port; - this.path = path; - this.server = new Server(this.port); - - final ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - this.server.setHandler(context); - context.addServlet(new ServletHolder(new MetricsServlet()), "/" + this.path); - - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - @Override - protected void startUp() - throws Exception { - this.server.start(); - } - - @Override - protected void shutDown() - throws Exception { - this.server.stop(); - } - - public HealthCheck getHealthCheck() { return this.healthCheck; } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("url", format("http://localhost:%d/%s", this.port, this.path)) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/common/MetricsService.kt b/src/main/java/io/prometheus/common/MetricsService.kt new file mode 100644 index 00000000..a8e637e7 --- /dev/null +++ b/src/main/java/io/prometheus/common/MetricsService.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import com.codahale.metrics.health.HealthCheck +import com.google.common.base.MoreObjects +import com.google.common.util.concurrent.AbstractIdleService +import com.google.common.util.concurrent.MoreExecutors +import io.prometheus.client.exporter.MetricsServlet +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.servlet.ServletContextHandler +import org.eclipse.jetty.servlet.ServletHolder + +class MetricsService(private val port: Int, private val path: String) : AbstractIdleService() { + private val server: Server = Server(this.port) + val healthCheck: HealthCheck = object : HealthCheck() { + @Throws(Exception::class) + override fun check(): HealthCheck.Result { + return if (server.isRunning) HealthCheck.Result.healthy() else HealthCheck.Result.unhealthy("Jetty server not running") + } + } + + init { + val context = ServletContextHandler() + context.contextPath = "/" + this.server.handler = context + context.addServlet(ServletHolder(MetricsServlet()), "/" + this.path) + + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + override fun startUp() { + this.server.start() + } + + override fun shutDown() { + this.server.stop() + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("url", "http://localhost:${this.port}/${this.path}") + .toString() +} diff --git a/src/main/java/io/prometheus/common/SamplerGauge.java b/src/main/java/io/prometheus/common/SamplerGauge.java deleted file mode 100644 index cdbb8e27..00000000 --- a/src/main/java/io/prometheus/common/SamplerGauge.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.google.common.collect.Lists; -import io.prometheus.client.Collector; - -import java.util.Collections; -import java.util.List; - -public class SamplerGauge - extends Collector { - - private final String name; - private final String help; - private final SamplerGaugeData samplerGaugeData; - - public SamplerGauge(final String name, final String help, final SamplerGaugeData samplerGaugeData) { - this.name = name; - this.help = help; - this.samplerGaugeData = samplerGaugeData; - } - - @Override - public List collect() { - final MetricFamilySamples.Sample sample = new MetricFamilySamples.Sample(this.name, - Collections.emptyList(), - Collections.emptyList(), - this.samplerGaugeData.value()); - return Lists.newArrayList(new MetricFamilySamples(this.name, - Type.GAUGE, - this.help, - Lists.newArrayList(sample))); - } -} diff --git a/src/main/java/io/prometheus/common/SamplerGauge.kt b/src/main/java/io/prometheus/common/SamplerGauge.kt new file mode 100644 index 00000000..1276a76f --- /dev/null +++ b/src/main/java/io/prometheus/common/SamplerGauge.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import io.prometheus.client.Collector + +class SamplerGauge(private val name: String, + private val help: String, + private val samplerGaugeData: SamplerGaugeData) : Collector() { + + override fun collect(): List { + val sample = MetricFamilySamples.Sample(this.name, + emptyList(), + emptyList(), + this.samplerGaugeData.value()) + return listOf(Collector.MetricFamilySamples(this.name, + Collector.Type.GAUGE, + this.help, + listOf(sample))) + } +} diff --git a/src/main/java/io/prometheus/common/SystemMetrics.java b/src/main/java/io/prometheus/common/SystemMetrics.java deleted file mode 100644 index 13e4594c..00000000 --- a/src/main/java/io/prometheus/common/SystemMetrics.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import io.prometheus.client.hotspot.ClassLoadingExports; -import io.prometheus.client.hotspot.GarbageCollectorExports; -import io.prometheus.client.hotspot.MemoryPoolsExports; -import io.prometheus.client.hotspot.StandardExports; -import io.prometheus.client.hotspot.ThreadExports; -import io.prometheus.client.hotspot.VersionInfoExports; - -public class SystemMetrics { - private static boolean initialized = false; - - private SystemMetrics() { - } - - public static synchronized void initialize(final boolean enableStandardExports, - final boolean enableMemoryPoolsExports, - final boolean enableGarbageCollectorExports, - final boolean enableThreadExports, - final boolean enableClassLoadingExports, - final boolean enableVersionInfoExports) { - if (!initialized) { - if (enableStandardExports) - new StandardExports().register(); - if (enableMemoryPoolsExports) - new MemoryPoolsExports().register(); - if (enableGarbageCollectorExports) - new GarbageCollectorExports().register(); - if (enableThreadExports) - new ThreadExports().register(); - if (enableClassLoadingExports) - new ClassLoadingExports().register(); - if (enableVersionInfoExports) - new VersionInfoExports().register(); - initialized = true; - } - } - -} diff --git a/src/main/java/io/prometheus/common/SystemMetrics.kt b/src/main/java/io/prometheus/common/SystemMetrics.kt new file mode 100644 index 00000000..0b819c3d --- /dev/null +++ b/src/main/java/io/prometheus/common/SystemMetrics.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import io.prometheus.client.Collector +import io.prometheus.client.hotspot.* + +object SystemMetrics { + private var initialized = false + + @Synchronized + fun initialize(enableStandardExports: Boolean, + enableMemoryPoolsExports: Boolean, + enableGarbageCollectorExports: Boolean, + enableThreadExports: Boolean, + enableClassLoadingExports: Boolean, + enableVersionInfoExports: Boolean) { + if (!initialized) { + if (enableStandardExports) + StandardExports().register() + if (enableMemoryPoolsExports) + MemoryPoolsExports().register() + if (enableGarbageCollectorExports) + GarbageCollectorExports().register() + if (enableThreadExports) + ThreadExports().register() + if (enableClassLoadingExports) + ClassLoadingExports().register() + if (enableVersionInfoExports) + VersionInfoExports().register() + initialized = true + } + } +} diff --git a/src/main/java/io/prometheus/common/Utils.java b/src/main/java/io/prometheus/common/Utils.java deleted file mode 100644 index ed39b1da..00000000 --- a/src/main/java/io/prometheus/common/Utils.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.internal.Console; -import com.codahale.metrics.health.HealthCheck; -import com.google.common.base.Charsets; -import com.google.common.base.Joiner; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.io.CharStreams; -import com.google.common.util.concurrent.Service; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import com.typesafe.config.ConfigParseOptions; -import com.typesafe.config.ConfigSyntax; -import io.prometheus.Proxy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.InetAddress; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static java.lang.String.format; - -public class Utils { - - private static final Logger logger = LoggerFactory.getLogger(Utils.class); - - private Utils() { - } - - public static String getBanner(final String filename) { - try (final InputStream in = logger.getClass().getClassLoader().getResourceAsStream(filename)) { - final String banner = CharStreams.toString(new InputStreamReader(in, Charsets.UTF_8.name())); - final List lines = Splitter.on("\n").splitToList(banner); - - // Use Atomic values because filter requires finals - // Trim initial and trailing blank lines, but preserve blank lines in middle; - final AtomicInteger first = new AtomicInteger(-1); - final AtomicInteger last = new AtomicInteger(-1); - final AtomicInteger lineNum = new AtomicInteger(0); - lines.forEach( - line -> { - if (line.trim().length() > 0) { - if (first.get() == -1) - first.set(lineNum.get()); - last.set(lineNum.get()); - } - lineNum.incrementAndGet(); - }); - - lineNum.set(0); - final String noNulls = - Joiner.on("\n") - .skipNulls() - .join( - lines.stream() - .filter( - input -> { - final int currLine = lineNum.getAndIncrement(); - return currLine >= first.get() && currLine <= last.get(); - }) - .map(input -> format(" %s", input)) - .collect(Collectors.toList())); - return format("%n%n%s%n%n", noNulls); - } - catch (Exception e) { - return format("Banner %s cannot be found", filename); - } - } - - public static Config readConfig(final String cliConfig, - final String envConfig, - final ConfigParseOptions configParseOptions, - final Config fallback, - final boolean exitOnMissingConfig) { - - final String configName = cliConfig != null ? cliConfig : System.getenv(envConfig); - - if (Strings.isNullOrEmpty(configName)) { - if (exitOnMissingConfig) { - logger.error("A configuration file or url must be specified with --getConfig or ${}", envConfig); - System.exit(1); - } - return fallback; - } - - if (isUrlPrefix(configName)) { - try { - final ConfigSyntax configSyntax = getConfigSyntax(configName); - return ConfigFactory.parseURL(new URL(configName), configParseOptions.setSyntax(configSyntax)) - .withFallback(fallback); - } - catch (Exception e) { - logger.error(e.getCause() instanceof FileNotFoundException - ? format("Invalid getConfig url: %s", configName) - : format("Exception: %s - %s", e.getClass().getSimpleName(), e.getMessage()), e); - } - } - else { - try { - return ConfigFactory.parseFileAnySyntax(new File(configName), configParseOptions) - .withFallback(fallback); - } - catch (Exception e) { - logger.error(e.getCause() instanceof FileNotFoundException - ? format("Invalid getConfig filename: %s", configName) - : format("Exception: %s - %s", e.getClass().getSimpleName(), e.getMessage()), e); - } - } - - System.exit(1); - return fallback; // Never reached - } - - private static ConfigSyntax getConfigSyntax(final String configName) { - if (isJsonSuffix(configName)) - return ConfigSyntax.JSON; - else if (isPropertiesSuffix(configName)) - return ConfigSyntax.PROPERTIES; - else - return ConfigSyntax.CONF; - } - - private static boolean isUrlPrefix(final String str) { - return str.toLowerCase().startsWith("http://") || str.toLowerCase().startsWith("https://"); - } - - private static boolean isJsonSuffix(final String str) { - return str.toLowerCase().endsWith(".json") || str.toLowerCase().endsWith(".jsn"); - } - - private static boolean isPropertiesSuffix(final String str) { - return str.toLowerCase().endsWith(".properties") || str.toLowerCase().endsWith(".props"); - } - - public static String getHostName() { - try { - return InetAddress.getLocalHost().getHostName(); - } - catch (UnknownHostException e) { - return "Unknown"; - } - } - - public static HealthCheck queueHealthCheck(final Queue queue, final int size) { - return new HealthCheck() { - @Override - protected Result check() - throws Exception { - return queue.size() < size ? Result.healthy() : Result.unhealthy("Large size: %d", queue.size()); - } - }; - } - - public static HealthCheck mapHealthCheck(final Map map, final int size) { - return new HealthCheck() { - @Override - protected Result check() - throws Exception { - return map.size() < size ? Result.healthy() : Result.unhealthy("Large size: %d", map.size()); - } - }; - } - - public static void sleepForMillis(final long millis) { - try { - Thread.sleep(millis); - } - catch (InterruptedException e) { - // Ignore - } - } - - public static void sleepForSecs(final long secs) { - try { - Thread.sleep(toMillis(secs)); - } - catch (InterruptedException e) { - // Ignore - } - } - - public static String getVersionDesc(final boolean asJson) { - final VersionAnnotation val = Proxy.class.getPackage().getAnnotation(VersionAnnotation.class); - return asJson ? format("{\"Version\": \"%s\", \"Release Date\": \"%s\"}", val.version(), val.date()) - : format("Version: %s Release Date: %s", val.version(), val.date()); - } - - public static Thread shutDownHookAction(final Service service) { - return new Thread(() -> { - JCommander.getConsole().println(format("*** %s shutting down ***", service.getClass().getSimpleName())); - service.stopAsync(); - JCommander.getConsole().println(format("*** %s shut down complete ***", service.getClass().getSimpleName())); - }); - } - - public static long toMillis(final long secs) { return secs * 1000; } - - public static long toSecs(final long millis) { return millis / 1000; } - - public static class VersionValidator - implements IParameterValidator { - @Override - public void validate(final String name, final String value) { - final Console console = JCommander.getConsole(); - console.println(getVersionDesc(false)); - System.exit(0); - } - } -} diff --git a/src/main/java/io/prometheus/common/Utils.kt b/src/main/java/io/prometheus/common/Utils.kt new file mode 100644 index 00000000..eca08c6a --- /dev/null +++ b/src/main/java/io/prometheus/common/Utils.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import com.beust.jcommander.IParameterValidator +import com.beust.jcommander.JCommander +import com.codahale.metrics.health.HealthCheck +import com.google.common.base.Charsets +import com.google.common.base.Joiner +import com.google.common.base.Splitter +import com.google.common.io.CharStreams +import com.google.common.util.concurrent.Service +import io.prometheus.Proxy +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.InputStreamReader +import java.net.InetAddress +import java.net.UnknownHostException +import java.util.* +import java.util.concurrent.atomic.AtomicInteger + +object Utils { + val logger: Logger = LoggerFactory.getLogger(Utils::class.java) +} + +val hostName: String + get() { + return try { + InetAddress.getLocalHost().hostName + } catch (e: UnknownHostException) { + "Unknown" + } + } + +fun getBanner(filename: String): String { + try { + Utils.logger.javaClass.classLoader.getResourceAsStream(filename).use { + val banner = CharStreams.toString(InputStreamReader(it, Charsets.UTF_8.name())) + val lines: List = Splitter.on("\n").splitToList(banner) + + // Use Atomic values because filter requires finals + // Trim initial and trailing blank lines, but preserve blank lines in middle; + val first = AtomicInteger(-1) + val last = AtomicInteger(-1) + val lineNum = AtomicInteger(0) + lines.forEach { + if (it.trim { it <= ' ' }.isNotEmpty()) { + if (first.get() == -1) + first.set(lineNum.get()) + last.set(lineNum.get()) + } + lineNum.incrementAndGet() + } + + lineNum.set(0) + + val vals = lines + .filter { + val currLine = lineNum.getAndIncrement() + currLine >= first.get() && currLine <= last.get() + } + .map { " " + it } + .toList() + + val noNulls = Joiner.on("\n").skipNulls().join(vals) + return "\n\n$noNulls\n\n" + } + } catch (e: Exception) { + return "Banner $filename cannot be found" + } +} + +fun queueHealthCheck(queue: Queue<*>, size: Int) = + object : HealthCheck() { + @Throws(Exception::class) + override fun check(): HealthCheck.Result { + return if (queue.size < size) HealthCheck.Result.healthy() else HealthCheck.Result.unhealthy("Large size: %d", queue.size) + } + } + +fun mapHealthCheck(map: Map<*, *>, size: Int) = + object : HealthCheck() { + @Throws(Exception::class) + override fun check(): HealthCheck.Result { + return if (map.size < size) HealthCheck.Result.healthy() else HealthCheck.Result.unhealthy("Large size: %d", map.size) + } + } + +fun sleepForMillis(millis: Long) = + try { + Thread.sleep(millis) + } catch (e: InterruptedException) { + // Ignore + } + +fun sleepForSecs(secs: Long) = + try { + Thread.sleep(secs.toMillis()) + } catch (e: InterruptedException) { + // Ignore + } + +fun getVersionDesc(asJson: Boolean): String { + val annotation = Proxy::class.java.`package`.getAnnotation(VersionAnnotation::class.java) + return if (asJson) + """{"Version": "${annotation.version}", "Release Date": "${annotation.date}"}""" + else + """Version: ${annotation.version} Release Date: ${annotation.date}""" +} + +fun shutDownHookAction(service: Service): Thread = + Thread { + println("*** ${service.javaClass.simpleName} shutting down ***") + service.stopAsync() + println("*** ${service.javaClass.simpleName} shut down complete ***") + } + +class VersionValidator : IParameterValidator { + override fun validate(name: String, value: String) { + val console = JCommander.getConsole() + console.println(getVersionDesc(false)) + System.exit(0) + } +} + +fun Long.toMillis(): Long = this * 1000 + +fun Long.toSecs(): Long = this / 1000 + diff --git a/src/main/java/io/prometheus/common/VersionServlet.java b/src/main/java/io/prometheus/common/VersionServlet.java deleted file mode 100644 index 26c29b27..00000000 --- a/src/main/java/io/prometheus/common/VersionServlet.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; - -public class VersionServlet - extends HttpServlet { - - private static final long serialVersionUID = -9115048679370256251L; - - @Override - protected void doGet(HttpServletRequest req, - HttpServletResponse resp) - throws ServletException, IOException { - resp.setStatus(HttpServletResponse.SC_OK); - resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); - resp.setContentType("text/plain"); - try (final PrintWriter writer = resp.getWriter()) { - writer.println(Utils.getVersionDesc(true)); - } - } -} diff --git a/src/main/java/io/prometheus/common/VersionServlet.kt b/src/main/java/io/prometheus/common/VersionServlet.kt new file mode 100644 index 00000000..1dcb8b93 --- /dev/null +++ b/src/main/java/io/prometheus/common/VersionServlet.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import java.io.IOException +import javax.servlet.ServletException +import javax.servlet.http.HttpServlet +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +class VersionServlet : HttpServlet() { + + @Throws(ServletException::class, IOException::class) + override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) { + resp.apply { + status = HttpServletResponse.SC_OK + setHeader("Cache-Control", "must-revalidate,no-cache,no-store") + contentType = "text/plain" + writer.use { it.println(getVersionDesc(true)) } + } + } + + companion object { + private val serialVersionUID = -9115048679370256251L + } +} diff --git a/src/main/java/io/prometheus/common/ZipkinConfig.java b/src/main/java/io/prometheus/common/ZipkinConfig.java deleted file mode 100644 index 90b9ffaa..00000000 --- a/src/main/java/io/prometheus/common/ZipkinConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import com.google.auto.value.AutoValue; - -@AutoValue -public abstract class ZipkinConfig { - public static ZipkinConfig create(final ConfigVals.Proxy2.Internal2.Zipkin2 zipkin) { - return new AutoValue_ZipkinConfig(zipkin.enabled, - zipkin.hostname, - zipkin.port, - zipkin.path, - zipkin.serviceName); - } - - public static ZipkinConfig create(final ConfigVals.Agent.Internal.Zipkin zipkin) { - return new AutoValue_ZipkinConfig(zipkin.enabled, - zipkin.hostname, - zipkin.port, - zipkin.path, - zipkin.serviceName); - } - - public abstract boolean enabled(); - - public abstract String hostname(); - - public abstract int port(); - - public abstract String path(); - - public abstract String serviceName(); -} - - diff --git a/src/main/java/io/prometheus/common/ZipkinConfig.kt b/src/main/java/io/prometheus/common/ZipkinConfig.kt new file mode 100644 index 00000000..11984e31 --- /dev/null +++ b/src/main/java/io/prometheus/common/ZipkinConfig.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +data class ZipkinConfig(val enabled: Boolean, + val hostname: String, + val port: Int, + val path: String, + val serviceName: String) { + + companion object { + fun create(zipkin: ConfigVals.Proxy2.Internal2.Zipkin2): ZipkinConfig = + ZipkinConfig(zipkin.enabled, + zipkin.hostname, + zipkin.port, + zipkin.path, + zipkin.serviceName) + + fun create(zipkin: ConfigVals.Agent.Internal.Zipkin): ZipkinConfig = + ZipkinConfig(zipkin.enabled, + zipkin.hostname, + zipkin.port, + zipkin.path, + zipkin.serviceName) + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/common/ZipkinReporterService.java b/src/main/java/io/prometheus/common/ZipkinReporterService.java deleted file mode 100644 index 3070fdf8..00000000 --- a/src/main/java/io/prometheus/common/ZipkinReporterService.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.common; - -import brave.Tracer; -import brave.Tracing; -import com.github.kristofa.brave.Brave; -import com.github.kristofa.brave.TracerAdapter; -import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.AbstractIdleService; -import com.google.common.util.concurrent.MoreExecutors; -import zipkin.Span; -import zipkin.reporter.AsyncReporter; -import zipkin.reporter.Sender; -import zipkin.reporter.okhttp3.OkHttpSender; - -import java.io.IOException; - -public class ZipkinReporterService - extends AbstractIdleService { - - private final String url; - private final String serviceName; - private final Sender sender; - private final AsyncReporter reporter; - private final Brave brave; - - public ZipkinReporterService(final String url, final String serviceName) { - this.url = url; - this.serviceName = serviceName; - this.sender = OkHttpSender.create(this.url); - this.reporter = AsyncReporter.builder(this.sender).build(); - this.brave = TracerAdapter.newBrave(this.newTracer(this.serviceName)); - - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - public Tracer newTracer(final String serviceName) { - return Tracing.newBuilder() - .localServiceName(serviceName) - .reporter(this.reporter) - .build() - .tracer(); - } - - @Override - protected void startUp() { - // Empty - } - - @Override - protected void shutDown() - throws IOException { - this.sender.close(); - this.reporter.close(); - } - - public Brave getBrave() { return this.brave; } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("serviceName", serviceName) - .add("url", url) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/common/ZipkinReporterService.kt b/src/main/java/io/prometheus/common/ZipkinReporterService.kt new file mode 100644 index 00000000..614a4d09 --- /dev/null +++ b/src/main/java/io/prometheus/common/ZipkinReporterService.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.common + +import brave.Tracing +import com.google.common.base.MoreObjects +import com.google.common.util.concurrent.AbstractIdleService +import com.google.common.util.concurrent.MoreExecutors +import zipkin2.reporter.AsyncReporter +import zipkin2.reporter.okhttp3.OkHttpSender + +class ZipkinReporterService(private val url: String) : AbstractIdleService() { + private val sender = OkHttpSender.create(this.url) + private val reporter = AsyncReporter.create(this.sender) + + init { + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + fun newTracing(serviceName: String): Tracing = + Tracing.newBuilder() + .localServiceName(serviceName) + .spanReporter(this.reporter) + .build() + + override fun startUp() { + // Empty + } + + override fun shutDown() { + this.reporter.close() + this.sender.close() + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("url", url) + .toString() +} diff --git a/src/main/java/io/prometheus/package-info.java b/src/main/java/io/prometheus/package-info.java index cc27e905..08e44fed 100644 --- a/src/main/java/io/prometheus/package-info.java +++ b/src/main/java/io/prometheus/package-info.java @@ -14,7 +14,7 @@ * limitations under the License. */ -@VersionAnnotation(version = "1.2.5", date = "12/7/17") +@VersionAnnotation(version = "1.3.0", date = "12/24/17") package io.prometheus; import io.prometheus.common.VersionAnnotation; \ No newline at end of file diff --git a/src/main/java/io/prometheus/proxy/AgentContext.java b/src/main/java/io/prometheus/proxy/AgentContext.java deleted file mode 100644 index a8415558..00000000 --- a/src/main/java/io/prometheus/proxy/AgentContext.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import com.google.common.base.MoreObjects; -import io.prometheus.Proxy; - -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; - -import static java.lang.String.format; - -public class AgentContext { - - private static final AtomicLong AGENT_ID_GENERATOR = new AtomicLong(0); - - private final String agentId = format("%s", AGENT_ID_GENERATOR.incrementAndGet()); - private final AtomicBoolean valid = new AtomicBoolean(true); - private final AtomicLong lastActivityTime = new AtomicLong(); - private final AtomicReference agentName = new AtomicReference<>(); - private final AtomicReference hostname = new AtomicReference<>(); - - private final String remoteAddr; - private final BlockingQueue scrapeRequestQueue; - private final long waitMillis; - - public AgentContext(final Proxy proxy, final String remoteAddr) { - this.remoteAddr = remoteAddr; - final int queueSize = proxy.getConfigVals().internal.scrapeRequestQueueSize; - this.scrapeRequestQueue = new ArrayBlockingQueue<>(queueSize); - this.waitMillis = proxy.getConfigVals().internal.scrapeRequestQueueCheckMillis; - - this.markActivity(); - } - - public String getAgentId() { return this.agentId; } - - public String getHostname() { return this.hostname.get(); } - - public void setHostname(String hostname) { this.hostname.set(hostname); } - - public String getAgentName() { return this.agentName.get(); } - - public void setAgentName(String agentName) { this.agentName.set(agentName); } - - public String getRemoteAddr() { return this.remoteAddr; } - - public void addToScrapeRequestQueue(final ScrapeRequestWrapper scrapeRequest) { - this.scrapeRequestQueue.add(scrapeRequest); - } - - public int scrapeRequestQueueSize() { return this.scrapeRequestQueue.size(); } - - public ScrapeRequestWrapper pollScrapeRequestQueue() { - try { - return this.scrapeRequestQueue.poll(waitMillis, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - return null; - } - } - - public long inactivitySecs() { return (System.currentTimeMillis() - this.lastActivityTime.get()) / 1000;} - - public boolean isValid() { return this.valid.get();} - - public void markInvalid() { this.valid.set(false);} - - public void markActivity() { this.lastActivityTime.set(System.currentTimeMillis()); } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("agentId", this.agentId) - .add("valid", this.isValid()) - .add("remoteAddr", this.remoteAddr) - .add("agentName", this.agentName) - .add("hostname", this.hostname) - .add("inactivitySecs", this.inactivitySecs()) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/proxy/AgentContext.kt b/src/main/java/io/prometheus/proxy/AgentContext.kt new file mode 100644 index 00000000..d26e8428 --- /dev/null +++ b/src/main/java/io/prometheus/proxy/AgentContext.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import com.google.common.base.MoreObjects +import io.prometheus.Proxy +import io.prometheus.common.toSecs +import java.util.concurrent.ArrayBlockingQueue +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.atomic.AtomicReference + +class AgentContext(proxy: Proxy, private val remoteAddr: String) { + + val agentId = AGENT_ID_GENERATOR.incrementAndGet().toString() + private val validRef = AtomicBoolean(true) + private val lastActivityTime = AtomicLong() + private val agentNameRef = AtomicReference() + private val hostnameRef = AtomicReference() + private val scrapeRequestQueue = ArrayBlockingQueue(proxy.configVals.internal.scrapeRequestQueueSize) + private val waitMillis = proxy.configVals.internal.scrapeRequestQueueCheckMillis.toLong() + + var valid: Boolean + get() = this.validRef.get() + private set(v) = this.validRef.set(v) + + var hostname: String? + get() = this.hostnameRef.get() + set(v) = this.hostnameRef.set(v) + + + var agentName: String? + get() = this.agentNameRef.get() + set(v) = this.agentNameRef.set(v) + + init { + this.markActivity() + } + + fun addToScrapeRequestQueue(scrapeRequest: ScrapeRequestWrapper) = this.scrapeRequestQueue.add(scrapeRequest) + + fun scrapeRequestQueueSize(): Int = this.scrapeRequestQueue.size + + fun pollScrapeRequestQueue(): ScrapeRequestWrapper? = + try { + this.scrapeRequestQueue.poll(waitMillis, TimeUnit.MILLISECONDS) + } catch (e: InterruptedException) { + null + } + + fun inactivitySecs(): Long = (System.currentTimeMillis() - this.lastActivityTime.get()).toSecs() + + fun markInvalid() { + this.valid = false + } + + fun markActivity() = this.lastActivityTime.set(System.currentTimeMillis()) + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("agentId", this.agentId) + .add("valid", this.valid) + .add("remoteAddr", this.remoteAddr) + .add("agentName", this.agentName) + .add("hostname", this.hostname) + .add("inactivitySecs", this.inactivitySecs()) + .toString() + + companion object { + private val AGENT_ID_GENERATOR = AtomicLong(0) + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java deleted file mode 100644 index 30e714c9..00000000 --- a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.AbstractExecutionThreadService; -import com.google.common.util.concurrent.MoreExecutors; -import io.prometheus.Proxy; -import io.prometheus.common.GenericServiceListener; -import io.prometheus.common.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AgentContextCleanupService - extends AbstractExecutionThreadService { - - private static final Logger logger = LoggerFactory.getLogger(AgentContextCleanupService.class); - - private final Proxy proxy; - - public AgentContextCleanupService(final Proxy proxy) { - this.proxy = proxy; - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - @Override - protected void run() - throws Exception { - final long maxInactivitySecs = this.proxy.getConfigVals().internal.maxAgentInactivitySecs; - final long threadPauseSecs = this.proxy.getConfigVals().internal.staleAgentCheckPauseSecs; - while (this.isRunning()) { - this.proxy.getAgentContextMap() - .forEach( - (agentId, agentContext) -> { - final long inactivitySecs = agentContext.inactivitySecs(); - if (inactivitySecs > maxInactivitySecs) { - logger.info("Evicting agent after {} secs of inactivty {}", inactivitySecs, agentContext); - this.proxy.removeAgentContext(agentId); - this.proxy.getMetrics().agentEvictions.inc(); - } - }); - Utils.sleepForSecs(threadPauseSecs); - } - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("max inactivity secs", this.proxy.getConfigVals().internal.maxAgentInactivitySecs) - .add("pause secs", this.proxy.getConfigVals().internal.staleAgentCheckPauseSecs) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt new file mode 100644 index 00000000..7b2e16dd --- /dev/null +++ b/src/main/java/io/prometheus/proxy/AgentContextCleanupService.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import com.google.common.base.MoreObjects +import com.google.common.util.concurrent.AbstractExecutionThreadService +import com.google.common.util.concurrent.MoreExecutors +import io.prometheus.Proxy +import io.prometheus.common.GenericServiceListener +import io.prometheus.common.sleepForSecs +import org.slf4j.LoggerFactory + +class AgentContextCleanupService(private val proxy: Proxy) : AbstractExecutionThreadService() { + + init { + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + @Throws(Exception::class) + override fun run() { + val maxInactivitySecs = this.proxy.configVals.internal.maxAgentInactivitySecs.toLong() + val threadPauseSecs = this.proxy.configVals.internal.staleAgentCheckPauseSecs.toLong() + while (this.isRunning) { + this.proxy.agentContextMap + .forEach { agentId, agentContext -> + val inactivitySecs = agentContext.inactivitySecs() + if (inactivitySecs > maxInactivitySecs) { + logger.info("Evicting agent after $inactivitySecs secs of inactivty $agentContext") + this.proxy.removeAgentContext(agentId) + if (this.proxy.metricsEnabled) + this.proxy.metrics!!.agentEvictions.inc() + } + } + sleepForSecs(threadPauseSecs) + } + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("max inactivity secs", this.proxy.configVals.internal.maxAgentInactivitySecs) + .add("pause secs", this.proxy.configVals.internal.staleAgentCheckPauseSecs) + .toString() + + companion object { + private val logger = LoggerFactory.getLogger(AgentContextCleanupService::class.java) + } +} diff --git a/src/main/java/io/prometheus/proxy/ProxyGrpcService.java b/src/main/java/io/prometheus/proxy/ProxyGrpcService.java deleted file mode 100644 index 9488432b..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyGrpcService.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import com.codahale.metrics.health.HealthCheck; -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.AbstractIdleService; -import com.google.common.util.concurrent.MoreExecutors; -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.grpc.ServerInterceptor; -import io.grpc.ServerInterceptors; -import io.grpc.ServerServiceDefinition; -import io.grpc.inprocess.InProcessServerBuilder; -import io.prometheus.Proxy; -import io.prometheus.common.GenericServiceListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.List; - -import static com.google.common.base.Strings.isNullOrEmpty; - -public class ProxyGrpcService - extends AbstractIdleService { - - private static final Logger logger = LoggerFactory.getLogger(ProxyGrpcService.class); - - private final String serverName; - private final int port; - private final boolean inProcessServer; - private final Server grpcServer; - - private ProxyGrpcService(final Proxy proxy, final int port, final String serverName) { - this.port = port; - this.serverName = serverName; - this.inProcessServer = !isNullOrEmpty(serverName); - - final List interceptors = Lists.newArrayList(new ProxyInterceptor()); - - /* - if (proxy.getConfigVals().grpc.metricsEnabled) - interceptors.add(MonitoringServerInterceptor.create(proxy.getConfigVals().grpc.allMetricsReported - ? Configuration.allMetrics() - : Configuration.cheapMetricsOnly())); - if (proxy.isZipkinEnabled() && proxy.getConfigVals().grpc.zipkinReportingEnabled) - interceptors.add(BraveGrpcServerInterceptor.create(proxy.getZipkinReporterService().getBrave())); - */ - - final ProxyServiceImpl proxyService = new ProxyServiceImpl(proxy); - final ServerServiceDefinition serviceDef = ServerInterceptors.intercept(proxyService.bindService(), interceptors); - - this.grpcServer = this.inProcessServer ? InProcessServerBuilder.forName(this.serverName) - .addService(serviceDef) - .addTransportFilter(new ProxyTransportFilter(proxy)) - .build() - : ServerBuilder.forPort(this.port) - .addService(serviceDef) - .addTransportFilter(new ProxyTransportFilter(proxy)) - .build(); - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - public static ProxyGrpcService create(final Proxy proxy, final int grpcPort) { - return new ProxyGrpcService(proxy, grpcPort, null); - } - - public static ProxyGrpcService create(final Proxy proxy, final String serverName) { - return new ProxyGrpcService(proxy, -1, Preconditions.checkNotNull(serverName)); - } - - @Override - protected void startUp() - throws IOException { - this.grpcServer.start(); - } - - @Override - protected void shutDown() { this.grpcServer.shutdown(); } - - public HealthCheck getHealthCheck() { - return new HealthCheck() { - @Override - protected Result check() - throws Exception { - return grpcServer.isShutdown() || grpcServer.isShutdown() ? Result.unhealthy("gRPC Server is not runing") - : Result.healthy(); - } - }; - } - - public int getPort() { return this.grpcServer.getPort(); } - - @Override - public String toString() { - final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this); - if (this.inProcessServer) { - helper.add("serverType", "InProcess"); - helper.add("serverName", this.serverName); - } - else { - helper.add("serverType", "Netty"); - helper.add("port", this.port); - } - return helper.toString(); - } -} diff --git a/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt b/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt new file mode 100644 index 00000000..5c1433ed --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyGrpcService.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import brave.Tracing +import brave.grpc.GrpcTracing +import com.codahale.metrics.health.HealthCheck +import com.google.common.base.MoreObjects +import com.google.common.base.Preconditions +import com.google.common.util.concurrent.AbstractIdleService +import com.google.common.util.concurrent.MoreExecutors +import io.grpc.Server +import io.grpc.ServerBuilder +import io.grpc.ServerInterceptor +import io.grpc.ServerInterceptors +import io.grpc.inprocess.InProcessServerBuilder +import io.prometheus.Proxy +import io.prometheus.common.GenericServiceListener +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException + +class ProxyGrpcService private constructor(proxy: Proxy, + private val port: Int, + private val inProcessServerName: String?) : AbstractIdleService() { + private val inProcessServer = !inProcessServerName.isNullOrBlank() + val healthCheck: HealthCheck + get() = object : HealthCheck() { + @Throws(Exception::class) + override fun check(): HealthCheck.Result { + return if (grpcServer.isShutdown || grpcServer.isShutdown) + HealthCheck.Result.unhealthy("gRPC server is not running") + else + HealthCheck.Result.healthy() + } + } + + private val grpcServer: Server + private val _grpcTracing: Tracing? + private val grpcTracing: GrpcTracing? + + init { + if (proxy.zipkinEnabled) { + this._grpcTracing = proxy.zipkinReporterService!!.newTracing("grpc_server") + this.grpcTracing = GrpcTracing.create(this._grpcTracing) + } + else { + this._grpcTracing = null + this.grpcTracing = null + } + + val serverBuilder = + if (this.inProcessServer) + InProcessServerBuilder.forName(this.inProcessServerName) + else + ServerBuilder.forPort(this.port) + + val proxyService = ProxyServiceImpl(proxy) + val interceptors = mutableListOf(ProxyInterceptor()) + if (proxy.zipkinEnabled) + interceptors.add(this.grpcTracing!!.newServerInterceptor()) + val serviceDef = ServerInterceptors.intercept(proxyService.bindService(), interceptors) + + this.grpcServer = + serverBuilder + .addService(serviceDef) + .addTransportFilter(ProxyTransportFilter(proxy)) + .build() + + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + @Throws(IOException::class) + override fun startUp() { + this.grpcServer.start() + } + + override fun shutDown() { + this._grpcTracing?.close() + this.grpcServer.shutdown() + } + + override fun toString() = + with(MoreObjects.toStringHelper(this)) { + if (inProcessServer) { + add("serverType", "InProcess") + add("serverName", inProcessServerName) + } + else { + add("serverType", "Netty") + add("port", port) + } + toString() + } + + companion object { + val logger: Logger = LoggerFactory.getLogger(ProxyGrpcService::class.java) + + fun create(proxy: Proxy, grpcPort: Int) = ProxyGrpcService(proxy, grpcPort, null) + + fun create(proxy: Proxy, serverName: String) = ProxyGrpcService(proxy, -1, Preconditions.checkNotNull(serverName)) + } +} diff --git a/src/main/java/io/prometheus/proxy/ProxyHttpService.java b/src/main/java/io/prometheus/proxy/ProxyHttpService.java deleted file mode 100644 index 1e87dade..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyHttpService.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import brave.Span; -import brave.Tracer; -import com.github.kristofa.brave.sparkjava.BraveTracing; -import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.AbstractIdleService; -import com.google.common.util.concurrent.MoreExecutors; -import io.prometheus.Proxy; -import io.prometheus.common.ConfigVals; -import io.prometheus.common.GenericServiceListener; -import io.prometheus.grpc.ScrapeResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import spark.ExceptionHandlerImpl; -import spark.Request; -import spark.Response; -import spark.Service; - -import static com.google.common.net.HttpHeaders.ACCEPT; -import static com.google.common.net.HttpHeaders.ACCEPT_ENCODING; -import static com.google.common.net.HttpHeaders.CONTENT_ENCODING; - -public class ProxyHttpService - extends AbstractIdleService { - - private static final Logger logger = LoggerFactory.getLogger(ProxyHttpService.class); - - private final Proxy proxy; - private final int port; - private final Service http; - private final Tracer tracer; - private final ConfigVals.Proxy2 configVals; - - public ProxyHttpService(final Proxy proxy, final int port) { - this.proxy = proxy; - this.port = port; - this.http = Service.ignite(); - this.http.port(this.port); - this.http.threadPool(this.proxy.getConfigVals().http.maxThreads, - this.proxy.getConfigVals().http.minThreads, - this.proxy.getConfigVals().http.idleTimeoutMillis); - this.tracer = this.proxy.isZipkinEnabled() - ? this.proxy.getZipkinReporterService().newTracer("proxy-http") - : null; - this.configVals = this.proxy.getConfigVals(); - - this.addListener(new GenericServiceListener(this), MoreExecutors.directExecutor()); - } - - @Override - protected void startUp() { - if (this.proxy.isZipkinEnabled()) { - final BraveTracing tracing = BraveTracing.create(this.proxy.getBrave()); - this.http.before(tracing.before()); - this.http.exception(Exception.class, - tracing.exception( - new ExceptionHandlerImpl(Exception.class) { - @Override - public void handle(Exception e, Request request, Response response) { - response.status(404); - logger.error("Error in ProxyHttpService", e); - } - })); - this.http.afterAfter(tracing.afterAfter()); - } - - this.http.get("/*", - (req, res) -> { - res.header("cache-control", "must-revalidate,no-cache,no-store"); - - final Span span = this.tracer != null ? this.tracer.newTrace() - .name("round-trip") - .tag("version", "1.2.5") - .start() - : null; - try { - if (!this.proxy.isRunning()) { - logger.error("Proxy stopped"); - res.status(503); - this.updateScrapeRequests("proxy_stopped"); - return null; - } - - final String[] vals = req.splat(); - if (vals == null || vals.length == 0) { - logger.info("Request missing path"); - res.status(404); - this.updateScrapeRequests("missing_path"); - return null; - } - - final String path = vals[0]; - - if (this.configVals.internal.blitz.enabled && path.equals(this.configVals.internal.blitz.path)) { - res.status(200); - res.type("text/plain"); - return "42"; - } - - final AgentContext agentContext = this.proxy.getAgentContextByPath(path); - - if (agentContext == null) { - logger.debug("Invalid path request /{}", path); - res.status(404); - this.updateScrapeRequests("invalid_path"); - return null; - } - - if (!agentContext.isValid()) { - logger.error("Invalid AgentContext"); - res.status(404); - this.updateScrapeRequests("invalid_agent_context"); - return null; - } - - if (span != null) - span.tag("path", path); - - return submitScrapeRequest(req, res, agentContext, path, span); - - } - finally { - if (span != null) - span.finish(); - } - }); - - } - - @Override - protected void shutDown() { - this.http.stop(); - } - - private String submitScrapeRequest(final Request req, final Response res, final AgentContext agentContext, - final String path, final Span span) { - final ScrapeRequestWrapper scrapeRequest = new ScrapeRequestWrapper(this.proxy, - agentContext, - span, - path, - req.headers(ACCEPT)); - try { - this.proxy.addToScrapeRequestMap(scrapeRequest); - agentContext.addToScrapeRequestQueue(scrapeRequest); - - final int timeoutSecs = this.configVals.internal.scrapeRequestTimeoutSecs; - final int checkMillis = this.configVals.internal.scrapeRequestCheckMillis; - while (true) { - // Returns false if timed out - if (scrapeRequest.waitUntilCompleteMillis(checkMillis)) - break; - - // Check if agent is disconnected or agent is hung - if (scrapeRequest.ageInSecs() >= timeoutSecs - || !scrapeRequest.getAgentContext().isValid() - || !this.proxy.isRunning()) { - res.status(503); - this.updateScrapeRequests("time_out"); - return null; - } - } - } - finally { - final ScrapeRequestWrapper prev = this.proxy.removeFromScrapeRequestMap(scrapeRequest.getScrapeId()); - if (prev == null) - logger.error("Scrape request {} missing in map", scrapeRequest.getScrapeId()); - } - - logger.debug("Results returned from {} for {}", agentContext, scrapeRequest); - - final ScrapeResponse scrapeResponse = scrapeRequest.getScrapeResponse(); - final int statusCode = scrapeResponse.getStatusCode(); - res.status(statusCode); - - // Do not return content on error status codes - if (statusCode >= 400) { - this.updateScrapeRequests("path_not_found"); - return null; - } - else { - final String acceptEncoding = req.headers(ACCEPT_ENCODING); - if (acceptEncoding != null && acceptEncoding.contains("gzip")) - res.header(CONTENT_ENCODING, "gzip"); - res.type(scrapeResponse.getContentType()); - this.updateScrapeRequests("success"); - return scrapeRequest.getScrapeResponse().getText(); - } - } - - private void updateScrapeRequests(final String type) { - if (this.proxy.isMetricsEnabled()) - this.proxy.getMetrics().scrapeRequests.labels(type).inc(); - } - - public int getPort() { return this.port; } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("port", port) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/proxy/ProxyHttpService.kt b/src/main/java/io/prometheus/proxy/ProxyHttpService.kt new file mode 100644 index 00000000..e48cd452 --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyHttpService.kt @@ -0,0 +1,190 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import brave.sparkjava.SparkTracing +import com.google.common.base.MoreObjects +import com.google.common.net.HttpHeaders.* +import com.google.common.util.concurrent.AbstractIdleService +import com.google.common.util.concurrent.MoreExecutors +import io.prometheus.Proxy +import io.prometheus.common.GenericServiceListener +import org.slf4j.LoggerFactory +import spark.* + + +class ProxyHttpService(private val proxy: Proxy, val port: Int) : AbstractIdleService() { + private val configVals = this.proxy.configVals + private val tracing = this.proxy.zipkinReporterService?.newTracing("proxy-http") + private val http: Service = + Service.ignite() + .apply { + port(port) + threadPool(proxy.configVals.http.maxThreads, + proxy.configVals.http.minThreads, + proxy.configVals.http.idleTimeoutMillis) + } + + init { + this.addListener(GenericServiceListener(this), MoreExecutors.directExecutor()) + } + + override fun startUp() { + if (this.proxy.zipkinEnabled) { + val tracing = SparkTracing.create(this.proxy.tracing) + this.http.before(tracing.before()) + + val impl = object : ExceptionHandlerImpl(Exception::class.java) { + override fun handle(e: Exception, request: Request, response: Response) { + response.status(404) + logger.error("Error in ProxyHttpService", e) + } + } + + this.http.exception(Exception::class.java, tracing.exception(impl)) + this.http.afterAfter(tracing.afterAfter()) + } + + if (this.proxy.zipkinEnabled) { + val sparkTracing = SparkTracing.create(this.tracing) + Spark.before(sparkTracing.before()) + Spark.exception(Exception::class.java, + sparkTracing.exception { _, _, response -> response.body("exception") }) + Spark.afterAfter(sparkTracing.afterAfter()) + } + + this.http.get("/*", + Route { req, res -> + res.header("cache-control", "must-revalidate,no-cache,no-store") + + if (!proxy.isRunning) { + logger.error("Proxy stopped") + res.status(503) + updateScrapeRequests("proxy_stopped") + return@Route null + } + + val vals = req.splat() + + if (vals == null || vals.isEmpty()) { + logger.info("Request missing path") + res.status(404) + updateScrapeRequests("missing_path") + return@Route null + } + + val path = vals[0] + + if (configVals.internal.blitz.enabled && path == configVals.internal.blitz.path) { + res.status(200) + res.type("text/plain") + return@Route "42" + } + + val agentContext = proxy.getAgentContextByPath(path) + + if (agentContext == null) { + logger.debug("Invalid path request /\${path") + res.status(404) + updateScrapeRequests("invalid_path") + return@Route null + } + + if (!agentContext.valid) { + logger.error("Invalid AgentContext") + res.status(404) + updateScrapeRequests("invalid_agent_context") + return@Route null + } + + return@Route submitScrapeRequest(req, res, agentContext, path) + + }) + } + + override fun shutDown() { + this.tracing?.close() + this.http.stop() + } + + private fun submitScrapeRequest(req: Request, + res: Response, + agentContext: AgentContext?, + path: String): String? { + val scrapeRequest = ScrapeRequestWrapper(this.proxy, + agentContext!!, + path, + req.headers(ACCEPT)) + try { + this.proxy.addToScrapeRequestMap(scrapeRequest) + agentContext.addToScrapeRequestQueue(scrapeRequest) + + val timeoutSecs = this.configVals.internal.scrapeRequestTimeoutSecs + val checkMillis = this.configVals.internal.scrapeRequestCheckMillis + while (true) { + // Returns false if timed out + if (scrapeRequest.waitUntilCompleteMillis(checkMillis.toLong())) + break + + // Check if agent is disconnected or agent is hung + if (scrapeRequest.ageInSecs() >= timeoutSecs || !scrapeRequest.agentContext.valid || !this.proxy.isRunning) { + res.status(503) + this.updateScrapeRequests("time_out") + return null + } + } + } finally { + val prev = this.proxy.removeFromScrapeRequestMap(scrapeRequest.scrapeId) + if (prev == null) + logger.error("Scrape request ${scrapeRequest.scrapeId} missing in map") + } + + logger.debug("Results returned from $agentContext for $scrapeRequest") + + val scrapeResponse = scrapeRequest.scrapeResponse + val statusCode = scrapeResponse.statusCode + res.status(statusCode) + + // Do not return content on error status codes + return if (statusCode >= 400) { + this.updateScrapeRequests("path_not_found") + null + } + else { + val acceptEncoding = req.headers(ACCEPT_ENCODING) + if (acceptEncoding != null && acceptEncoding.contains("gzip")) + res.header(CONTENT_ENCODING, "gzip") + res.type(scrapeResponse.contentType) + this.updateScrapeRequests("success") + scrapeRequest.scrapeResponse.text + } + } + + private fun updateScrapeRequests(type: String) { + if (this.proxy.metricsEnabled) + this.proxy.metrics!!.scrapeRequests.labels(type).inc() + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("port", port) + .toString() + + companion object { + private val logger = LoggerFactory.getLogger(ProxyHttpService::class.java) + } +} diff --git a/src/main/java/io/prometheus/proxy/ProxyInterceptor.java b/src/main/java/io/prometheus/proxy/ProxyInterceptor.java deleted file mode 100644 index 80d85b4d..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyInterceptor.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import io.grpc.Attributes; -import io.grpc.ForwardingServerCall; -import io.grpc.Metadata; -import io.grpc.MethodDescriptor; -import io.grpc.ServerCall; -import io.grpc.ServerCallHandler; -import io.grpc.ServerInterceptor; -import io.prometheus.Proxy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ProxyInterceptor - implements ServerInterceptor { - - private static final Logger logger = LoggerFactory.getLogger(ProxyInterceptor.class); - private static final Metadata.Key META_AGENT_ID = Metadata.Key.of(Proxy.AGENT_ID, Metadata.ASCII_STRING_MARSHALLER); - - @Override - public ServerCall.Listener interceptCall(final ServerCall call, - final Metadata requestHeaders, - final ServerCallHandler handler) { - final Attributes attributes = call.getAttributes(); - final MethodDescriptor methodDescriptor = call.getMethodDescriptor(); - // final String methodName = methodDescriptor.getFullMethodName(); - // logger.info("Intercepting {}", methodName); - - return handler.startCall( - new ForwardingServerCall.SimpleForwardingServerCall(call) { - @Override - public void sendHeaders(Metadata headers) { - // agent_id was assigned in ServerTransportFilter - final String agentId = attributes.get(Proxy.ATTRIB_AGENT_ID); - if (agentId != null) - headers.put(META_AGENT_ID, agentId); - super.sendHeaders(headers); - } - }, - requestHeaders); - } -} diff --git a/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt b/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt new file mode 100644 index 00000000..f132ea6e --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyInterceptor.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import io.grpc.* +import io.prometheus.Proxy + +class ProxyInterceptor : ServerInterceptor { + + override fun interceptCall(call: ServerCall, + requestHeaders: Metadata, + handler: ServerCallHandler): ServerCall.Listener { + val attributes = call.attributes + //val methodDescriptor = call.methodDescriptor + // final String methodName = methodDescriptor.getFullMethodName(); + // logger.info("Intercepting {}", methodName); + + return handler.startCall( + object : ForwardingServerCall.SimpleForwardingServerCall(call) { + override fun sendHeaders(headers: Metadata) { + // agent_id was assigned in ServerTransportFilter + val agentId = attributes.get(Proxy.ATTRIB_AGENT_ID) + if (agentId != null) + headers.put(META_AGENT_ID, agentId) + super.sendHeaders(headers) + } + }, + requestHeaders) + } + + companion object { + private val META_AGENT_ID = Metadata.Key.of(Proxy.AGENT_ID, Metadata.ASCII_STRING_MARSHALLER) + } +} diff --git a/src/main/java/io/prometheus/proxy/ProxyMetrics.java b/src/main/java/io/prometheus/proxy/ProxyMetrics.java deleted file mode 100644 index 6d0a9be0..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyMetrics.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import io.prometheus.Proxy; -import io.prometheus.client.Counter; -import io.prometheus.client.Gauge; -import io.prometheus.client.Summary; -import io.prometheus.common.SamplerGauge; - -public class ProxyMetrics { - - public final Counter scrapeRequests = Counter.build() - .name("proxy_scrape_requests") - .help("Proxy scrape requests") - .labelNames("type") - .register(); - - public final Counter connects = Counter.build() - .name("proxy_connect_count") - .help("Proxy connect count") - .register(); - - public final Counter agentEvictions = Counter.build() - .name("proxy_eviction_count") - .help("Proxy eviction count") - .register(); - - public final Counter heartbeats = Counter.build() - .name("proxy_heartbeat_count") - .help("Proxy heartbeat count") - .register(); - - public final Summary scrapeRequestLatency = Summary.build() - .name("proxy_scrape_request_latency_seconds") - .help("Proxy scrape request latency in seconds") - .register(); - - public ProxyMetrics(Proxy proxy) { - - Gauge.build() - .name("proxy_start_time_seconds") - .help("Proxy start time in seconds") - .register() - .setToCurrentTime(); - - new SamplerGauge("proxy_agent_map_size", - "Proxy connected agents", - proxy::getAgentContextSize).register(); - - new SamplerGauge("proxy_path_map_size", - "Proxy path map size", - proxy::getPathMapSize).register(); - - new SamplerGauge("proxy_scrape_map_size", - "Proxy scrape map size", - proxy::getScrapeMapSize).register(); - - new SamplerGauge("proxy_cummulative_agent_queue_size", - "Proxy cummulative agent queue size", - proxy::getTotalAgentRequestQueueSize).register(); - } -} diff --git a/src/main/java/io/prometheus/proxy/ProxyMetrics.kt b/src/main/java/io/prometheus/proxy/ProxyMetrics.kt new file mode 100644 index 00000000..bfa595b2 --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyMetrics.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import io.prometheus.Proxy +import io.prometheus.client.Collector +import io.prometheus.client.Counter +import io.prometheus.client.Gauge +import io.prometheus.client.Summary +import io.prometheus.common.SamplerGauge +import io.prometheus.common.SamplerGaugeData + +class ProxyMetrics(proxy: Proxy) { + + val scrapeRequests: Counter = + Counter.build() + .name("proxy_scrape_requests") + .help("Proxy scrape requests") + .labelNames("type") + .register() + + val connects: Counter = + Counter.build() + .name("proxy_connect_count") + .help("Proxy connect count") + .register() + + val agentEvictions: Counter = + Counter.build() + .name("proxy_eviction_count") + .help("Proxy eviction count") + .register() + + val heartbeats: Counter = + Counter.build() + .name("proxy_heartbeat_count") + .help("Proxy heartbeat count") + .register() + + val scrapeRequestLatency: Summary = + Summary.build() + .name("proxy_scrape_request_latency_seconds") + .help("Proxy scrape request latency in seconds") + .register() + + init { + Gauge.build() + .name("proxy_start_time_seconds") + .help("Proxy start time in seconds") + .register() + .setToCurrentTime() + + SamplerGauge("proxy_agent_map_size", + "Proxy connected agents", + SamplerGaugeData { proxy.agentContextSize.toDouble() }).register() + + SamplerGauge("proxy_path_map_size", + "Proxy path map size", + SamplerGaugeData { proxy.pathMapSize.toDouble() }).register() + + SamplerGauge("proxy_scrape_map_size", + "Proxy scrape map size", + SamplerGaugeData { proxy.scrapeMapSize.toDouble() }).register() + + SamplerGauge("proxy_cummulative_agent_queue_size", + "Proxy cummulative agent queue size", + SamplerGaugeData { proxy.totalAgentRequestQueueSize.toDouble() }).register() + } +} diff --git a/src/main/java/io/prometheus/proxy/ProxyOptions.java b/src/main/java/io/prometheus/proxy/ProxyOptions.java deleted file mode 100644 index 4906feaa..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyOptions.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import com.beust.jcommander.Parameter; -import com.google.common.collect.Iterables; -import io.prometheus.Proxy; -import io.prometheus.common.BaseOptions; -import io.prometheus.common.ConfigVals; - -import java.util.Collections; -import java.util.List; - -import static io.prometheus.common.EnvVars.AGENT_PORT; -import static io.prometheus.common.EnvVars.PROXY_CONFIG; -import static io.prometheus.common.EnvVars.PROXY_PORT; - -public class ProxyOptions - extends BaseOptions { - - @Parameter(names = {"-p", "--port"}, description = "Listen port for Prometheus") - private Integer proxyPort = null; - @Parameter(names = {"-a", "--agent_port"}, description = "Listen port for agents") - private Integer agentPort = null; - - public ProxyOptions(final List args) { - this(Iterables.toArray(args != null ? args : Collections.emptyList(), String.class)); - } - - public ProxyOptions(final String[] argv) { - super(Proxy.class.getSimpleName(), argv, PROXY_CONFIG.name(), false); - this.assignConfigVals(this.getConfigVals()); - } - - @Override - protected void assignConfigVals(final ConfigVals configVals) { - if (this.proxyPort == null) - this.proxyPort = PROXY_PORT.getEnv(configVals.proxy.http.port); - - if (this.agentPort == null) - this.agentPort = AGENT_PORT.getEnv(configVals.proxy.agent.port); - - this.assignAdminEnabled(configVals.proxy.admin.enabled); - this.assignAdminPort(configVals.proxy.admin.port); - this.assignMetricsEnabled(configVals.proxy.metrics.enabled); - this.assignMetricsPort(configVals.proxy.metrics.port); - } - - public int getProxyPort() { return this.proxyPort; } - - public int getAgentPort() { return this.agentPort; } -} \ No newline at end of file diff --git a/src/main/java/io/prometheus/proxy/ProxyOptions.kt b/src/main/java/io/prometheus/proxy/ProxyOptions.kt new file mode 100644 index 00000000..f6486325 --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyOptions.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import com.beust.jcommander.Parameter +import com.google.common.collect.Iterables +import io.prometheus.Proxy +import io.prometheus.common.BaseOptions +import io.prometheus.common.EnvVars.* + +class ProxyOptions(argv: Array) : BaseOptions(Proxy::class.java.simpleName, argv, PROXY_CONFIG.name, false) { + + @Parameter(names = ["-p", "--port"], description = "Listen port for Prometheus") + var proxyPort: Int? = null + private set + @Parameter(names = ["-a", "--agent_port"], description = "Listen port for agents") + var agentPort: Int? = null + private set + + constructor(args: List) : this(Iterables.toArray(args, String::class.java)) + + init { + this.parseOptions() + } + + override fun assignConfigVals() { + if (this.proxyPort == null) + this.proxyPort = PROXY_PORT.getEnv(this.configVals!!.proxy.http.port) + + if (this.agentPort == null) + this.agentPort = AGENT_PORT.getEnv(this.configVals!!.proxy.agent.port) + + this.assignAdminEnabled(this.configVals!!.proxy.admin.enabled) + this.assignAdminPort(this.configVals!!.proxy.admin.port) + this.assignMetricsEnabled(this.configVals!!.proxy.metrics.enabled) + this.assignMetricsPort(this.configVals!!.proxy.metrics.port) + } +} \ No newline at end of file diff --git a/src/main/java/io/prometheus/proxy/ProxyServiceImpl.java b/src/main/java/io/prometheus/proxy/ProxyServiceImpl.java deleted file mode 100644 index 9efcc511..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyServiceImpl.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import com.google.protobuf.Empty; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.grpc.stub.StreamObserver; -import io.prometheus.Proxy; -import io.prometheus.grpc.AgentInfo; -import io.prometheus.grpc.HeartBeatRequest; -import io.prometheus.grpc.HeartBeatResponse; -import io.prometheus.grpc.PathMapSizeRequest; -import io.prometheus.grpc.PathMapSizeResponse; -import io.prometheus.grpc.ProxyServiceGrpc; -import io.prometheus.grpc.RegisterAgentRequest; -import io.prometheus.grpc.RegisterAgentResponse; -import io.prometheus.grpc.RegisterPathRequest; -import io.prometheus.grpc.RegisterPathResponse; -import io.prometheus.grpc.ScrapeRequest; -import io.prometheus.grpc.ScrapeResponse; -import io.prometheus.grpc.UnregisterPathRequest; -import io.prometheus.grpc.UnregisterPathResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.atomic.AtomicLong; - -import static java.lang.String.format; - -class ProxyServiceImpl - extends ProxyServiceGrpc.ProxyServiceImplBase { - - private static final Logger logger = LoggerFactory.getLogger(ProxyServiceImpl.class); - private static final AtomicLong PATH_ID_GENERATOR = new AtomicLong(0); - - private final Proxy proxy; - - public ProxyServiceImpl(final Proxy proxy) { - this.proxy = proxy; - } - - @Override - public void connectAgent(final Empty request, final StreamObserver responseObserver) { - if (this.proxy.isMetricsEnabled()) - this.proxy.getMetrics().connects.inc(); - responseObserver.onNext(Empty.getDefaultInstance()); - responseObserver.onCompleted(); - } - - @Override - public void registerAgent(final RegisterAgentRequest request, - final StreamObserver responseObserver) { - final String agentId = request.getAgentId(); - final AgentContext agentContext = this.proxy.getAgentContext(agentId); - if (agentContext == null) { - logger.info("registerAgent() missing AgentContext agentId: {}", agentId); - } - else { - agentContext.setAgentName(request.getAgentName()); - agentContext.setHostname(request.getHostname()); - agentContext.markActivity(); - } - - final RegisterAgentResponse response = RegisterAgentResponse.newBuilder() - .setValid(agentContext != null) - .setReason(format("Invalid agentId: %s", agentId)) - .setAgentId(agentId) - .build(); - responseObserver.onNext(response); - responseObserver.onCompleted(); - } - - @Override - public void registerPath(final RegisterPathRequest request, - final StreamObserver responseObserver) { - final String path = request.getPath(); - if (this.proxy.containsPath(path)) - logger.info("Overwriting path /{}", path); - - final String agentId = request.getAgentId(); - final AgentContext agentContext = this.proxy.getAgentContext(agentId); - final RegisterPathResponse response = RegisterPathResponse.newBuilder() - .setValid(agentContext != null) - .setReason(format("Invalid agentId: %s", agentId)) - .setPathCount(this.proxy.pathMapSize()) - .setPathId(agentContext != null - ? PATH_ID_GENERATOR.getAndIncrement() - : -1) - .build(); - if (agentContext == null) { - logger.error("Missing AgentContext for agentId: {}", agentId); - } - else { - this.proxy.addPath(path, agentContext); - agentContext.markActivity(); - } - - responseObserver.onNext(response); - responseObserver.onCompleted(); - } - - @Override - public void unregisterPath(final UnregisterPathRequest request, - final StreamObserver responseObserver) { - final String path = request.getPath(); - final String agentId = request.getAgentId(); - final AgentContext agentContext = this.proxy.getAgentContext(agentId); - - final UnregisterPathResponse.Builder responseBuilder = UnregisterPathResponse.newBuilder(); - - if (agentContext == null) { - logger.error("Missing AgentContext for agentId: {}", agentId); - responseBuilder.setValid(false).setReason(format("Invalid agentId: %s", agentId)); - } - else { - this.proxy.removePath(path, agentId, responseBuilder); - agentContext.markActivity(); - } - - responseObserver.onNext(responseBuilder.build()); - responseObserver.onCompleted(); - } - - @Override - public void pathMapSize(PathMapSizeRequest request, StreamObserver responseObserver) { - final PathMapSizeResponse response = PathMapSizeResponse.newBuilder() - .setPathCount(this.proxy.pathMapSize()) - .build(); - responseObserver.onNext(response); - responseObserver.onCompleted(); - } - - @Override - public void sendHeartBeat(final HeartBeatRequest request, final StreamObserver responseObserver) { - if (this.proxy.isMetricsEnabled()) - this.proxy.getMetrics().heartbeats.inc(); - - final String agentId = request.getAgentId(); - final AgentContext agentContext = this.proxy.getAgentContext(agentId); - if (agentContext == null) - logger.info("sendHeartBeat() missing AgentContext agentId: {}", agentId); - else - agentContext.markActivity(); - - responseObserver.onNext(HeartBeatResponse.newBuilder() - .setValid(agentContext != null) - .setReason(format("Invalid agentId: %s", agentId)) - .build()); - responseObserver.onCompleted(); - } - - @Override - public void readRequestsFromProxy(final AgentInfo agentInfo, final StreamObserver responseObserver) { - final String agentId = agentInfo.getAgentId(); - final AgentContext agentContext = this.proxy.getAgentContext(agentId); - if (agentContext != null) { - while (this.proxy.isRunning() && agentContext.isValid()) { - final ScrapeRequestWrapper scrapeRequest = agentContext.pollScrapeRequestQueue(); - if (scrapeRequest != null) { - scrapeRequest.annotateSpan("send-to-agent"); - responseObserver.onNext(scrapeRequest.getScrapeRequest()); - } - } - } - responseObserver.onCompleted(); - } - - @Override - public StreamObserver writeResponsesToProxy(StreamObserver responseObserver) { - return new StreamObserver() { - @Override - public void onNext(final ScrapeResponse response) { - final long scrapeId = response.getScrapeId(); - final ScrapeRequestWrapper scrapeRequest = proxy.getFromScrapeRequestMap(scrapeId); - if (scrapeRequest == null) { - logger.error("Missing ScrapeRequestWrapper for scrape_id: {}", scrapeId); - } - else { - scrapeRequest.setScrapeResponse(response) - .markComplete() - .annotateSpan("received-from-agent") - .getAgentContext().markActivity(); - } - } - - @Override - public void onError(Throwable t) { - final Status status = Status.fromThrowable(t); - if (status != Status.CANCELLED) - logger.info("Error in writeResponsesToProxy(): {}", status); - try { - responseObserver.onNext(Empty.getDefaultInstance()); - responseObserver.onCompleted(); - } - catch (StatusRuntimeException e) { - // logger.warn("StatusRuntimeException", e); - // Ignore - } - } - - @Override - public void onCompleted() { - responseObserver.onNext(Empty.getDefaultInstance()); - responseObserver.onCompleted(); - } - }; - } -} diff --git a/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt b/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt new file mode 100644 index 00000000..be8116df --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyServiceImpl.kt @@ -0,0 +1,190 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import com.google.protobuf.Empty +import io.grpc.Status +import io.grpc.StatusRuntimeException +import io.grpc.stub.StreamObserver +import io.prometheus.Proxy +import io.prometheus.grpc.* +import org.slf4j.LoggerFactory +import java.util.concurrent.atomic.AtomicLong + +internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpc.ProxyServiceImplBase() { + + override fun connectAgent(request: Empty, responseObserver: StreamObserver) { + this.proxy.metrics?.connects?.inc() + responseObserver.apply { + onNext(Empty.getDefaultInstance()) + onCompleted() + } + } + + override fun registerAgent(request: RegisterAgentRequest, + responseObserver: StreamObserver) { + val agentId = request.agentId + val agentContext = this.proxy.getAgentContext(agentId) + if (agentContext == null) + logger.info("registerAgent() missing AgentContext agentId: $agentId") + else + agentContext.apply { + agentName = request.agentName + hostname = request.hostname + markActivity() + } + + responseObserver.apply { + onNext(RegisterAgentResponse.newBuilder() + .setValid(agentContext != null) + .setReason("Invalid agentId: $agentId") + .setAgentId(agentId) + .build()) + onCompleted() + } + } + + override fun registerPath(request: RegisterPathRequest, + responseObserver: StreamObserver) { + val path = request.path + if (this.proxy.containsPath(path)) + logger.info("Overwriting path /$path") + + val agentId = request.agentId + val agentContext = this.proxy.getAgentContext(agentId) + val response = + RegisterPathResponse.newBuilder() + .setValid(agentContext != null) + .setReason("Invalid agentId: $agentId") + .setPathCount(this.proxy.pathMapSize()) + .setPathId(if (agentContext != null) PATH_ID_GENERATOR.getAndIncrement() else -1) + .build() + if (agentContext == null) { + logger.error("Missing AgentContext for agentId: $agentId") + } + else { + this.proxy.addPath(path, agentContext) + agentContext.markActivity() + } + + responseObserver.apply { + onNext(response) + onCompleted() + } + } + + override fun unregisterPath(request: UnregisterPathRequest, + responseObserver: StreamObserver) { + val path = request.path + val agentId = request.agentId + val agentContext = this.proxy.getAgentContext(agentId) + + val responseBuilder = UnregisterPathResponse.newBuilder() + + if (agentContext == null) { + logger.error("Missing AgentContext for agentId: $agentId") + responseBuilder.setValid(false).reason = "Invalid agentId: $agentId" + } + else { + this.proxy.removePath(path, agentId, responseBuilder) + agentContext.markActivity() + } + + responseObserver.apply { + onNext(responseBuilder.build()) + onCompleted() + } + } + + override fun pathMapSize(request: PathMapSizeRequest, responseObserver: StreamObserver) { + responseObserver.apply { + onNext(PathMapSizeResponse.newBuilder().setPathCount(proxy.pathMapSize()).build()) + onCompleted() + } + } + + override fun sendHeartBeat(request: HeartBeatRequest, responseObserver: StreamObserver) { + this.proxy.metrics?.heartbeats?.inc() + val agentContext = this.proxy.getAgentContext(request.agentId) + agentContext?.markActivity() ?: logger.info("sendHeartBeat() missing AgentContext agentId: ${request.agentId}") + responseObserver.apply { + onNext(HeartBeatResponse.newBuilder() + .setValid(agentContext != null) + .setReason("Invalid agentId: ${request.agentId}") + .build()) + onCompleted() + } + } + + override fun readRequestsFromProxy(agentInfo: AgentInfo, responseObserver: StreamObserver) { + val agentId = agentInfo.agentId + val agentContext = this.proxy.getAgentContext(agentId) + if (agentContext != null) { + while (this.proxy.isRunning && agentContext.valid) { + val scrapeRequest = agentContext.pollScrapeRequestQueue() + if (scrapeRequest != null) { + responseObserver.onNext(scrapeRequest.scrapeRequest) + } + } + } + responseObserver.onCompleted() + } + + override fun writeResponsesToProxy(responseObserver: StreamObserver): StreamObserver { + return object : StreamObserver { + override fun onNext(response: ScrapeResponse) { + val scrapeRequest = proxy.getFromScrapeRequestMap(response.scrapeId) + if (scrapeRequest == null) + logger.error("Missing ScrapeRequestWrapper for scrape_id: ${response.scrapeId}") + else + scrapeRequest.apply { + setScrapeResponse(response) + markComplete() + agentContext.markActivity() + } + } + + override fun onError(t: Throwable) { + val status = Status.fromThrowable(t) + if (status !== Status.CANCELLED) + logger.info("Error in writeResponsesToProxy(): $status") + try { + responseObserver.apply { + onNext(Empty.getDefaultInstance()) + onCompleted() + } + } catch (e: StatusRuntimeException) { + // logger.warn("StatusRuntimeException", e); + // Ignore + } + + } + + override fun onCompleted() { + responseObserver.apply { + onNext(Empty.getDefaultInstance()) + onCompleted() + } + } + } + } + + companion object { + private val logger = LoggerFactory.getLogger(ProxyServiceImpl::class.java) + private val PATH_ID_GENERATOR = AtomicLong(0) + } +} diff --git a/src/main/java/io/prometheus/proxy/ProxyTransportFilter.java b/src/main/java/io/prometheus/proxy/ProxyTransportFilter.java deleted file mode 100644 index 04ea171a..00000000 --- a/src/main/java/io/prometheus/proxy/ProxyTransportFilter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - -import io.grpc.Attributes; -import io.grpc.ServerTransportFilter; -import io.prometheus.Proxy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Optional; - -public class ProxyTransportFilter - extends ServerTransportFilter { - - private static final Logger logger = LoggerFactory.getLogger(ProxyTransportFilter.class); - - private final Proxy proxy; - - public ProxyTransportFilter(Proxy proxy) { - this.proxy = proxy; - } - - private String getRemoteAddr(Attributes attributes) { - final Optional> keyOptional = attributes.keys() - .stream() - .filter(key -> "remote-addr".equals(key.toString())) - .findFirst(); - if (keyOptional.isPresent()) { - final Attributes.Key key = (Attributes.Key) keyOptional.get(); - final Object val = attributes.get(key); - if (val != null) - return val.toString(); - } - return "Unknown"; - } - - @Override - public Attributes transportReady(final Attributes attributes) { - final String remoteAddr = this.getRemoteAddr(attributes); - final AgentContext agentContext = new AgentContext(this.proxy, remoteAddr); - this.proxy.addAgentContext(agentContext); - logger.info("Connected to {}", agentContext); - return Attributes.newBuilder() - .set(Proxy.ATTRIB_AGENT_ID, agentContext.getAgentId()) - .setAll(attributes) - .build(); - } - - @Override - public void transportTerminated(final Attributes attributes) { - final String agentId = attributes.get(Proxy.ATTRIB_AGENT_ID); - this.proxy.removePathByAgentId(agentId); - final AgentContext agentContext = this.proxy.removeAgentContext(agentId); - if (agentContext != null) - logger.info("Disconnected from {}", agentContext); - else - logger.info("Disconnected with invalid agentId: {}", agentId); - super.transportTerminated(attributes); - } -} diff --git a/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt b/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt new file mode 100644 index 00000000..6d8da472 --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ProxyTransportFilter.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + +import io.grpc.Attributes +import io.grpc.ServerTransportFilter +import io.prometheus.Proxy +import org.slf4j.LoggerFactory + +class ProxyTransportFilter(private val proxy: Proxy) : ServerTransportFilter() { + + private fun getRemoteAddr(attributes: Attributes): String { + val key = attributes.keys().first { "remote-addr" == it.toString() } + return if (key == null) "Unknown" else attributes.get(key)?.toString() ?: "Unknown" + } + + override fun transportReady(attributes: Attributes): Attributes { + val remoteAddr = this.getRemoteAddr(attributes) + val agentContext = AgentContext(this.proxy, remoteAddr) + this.proxy.addAgentContext(agentContext) + logger.info("Connected to $agentContext") + return Attributes.newBuilder() + .set(Proxy.ATTRIB_AGENT_ID, agentContext.agentId) + .setAll(attributes) + .build() + } + + override fun transportTerminated(attributes: Attributes?) { + val agentId = attributes!!.get(Proxy.ATTRIB_AGENT_ID) + this.proxy.removePathByAgentId(agentId) + val agentContext = this.proxy.removeAgentContext(agentId) + if (agentContext != null) + logger.info("Disconnected from $agentContext") + else + logger.info("Disconnected with invalid agentId: $agentId") + super.transportTerminated(attributes) + } + + companion object { + private val logger = LoggerFactory.getLogger(ProxyTransportFilter::class.java) + } +} diff --git a/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.java b/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.java deleted file mode 100644 index aaad9432..00000000 --- a/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus.proxy; - - -import brave.Span; -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; -import io.prometheus.Proxy; -import io.prometheus.client.Summary; -import io.prometheus.grpc.ScrapeRequest; -import io.prometheus.grpc.ScrapeResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; - -import static com.google.common.base.Strings.isNullOrEmpty; - -public class ScrapeRequestWrapper { - - private static final Logger logger = LoggerFactory.getLogger(ScrapeRequestWrapper.class); - private static final AtomicLong SCRAPE_ID_GENERATOR = new AtomicLong(0); - - private final long createTime = System.currentTimeMillis(); - private final CountDownLatch complete = new CountDownLatch(1); - private final AtomicReference scrapeResponseRef = new AtomicReference<>(); - - private final AgentContext agentContext; - private final Span rootSpan; - private final Summary.Timer requestTimer; - private final ScrapeRequest scrapeRequest; - - public ScrapeRequestWrapper(final Proxy proxy, - final AgentContext agentContext, - final Span rootSpan, - final String path, - final String accept) { - this.agentContext = Preconditions.checkNotNull(agentContext); - this.rootSpan = rootSpan; - this.requestTimer = proxy.isMetricsEnabled() ? proxy.getMetrics().scrapeRequestLatency.startTimer() : null; - ScrapeRequest.Builder builder = ScrapeRequest.newBuilder() - .setAgentId(agentContext.getAgentId()) - .setScrapeId(SCRAPE_ID_GENERATOR.getAndIncrement()) - .setPath(path); - if (!isNullOrEmpty(accept)) - builder = builder.setAccept(accept); - this.scrapeRequest = builder.build(); - } - - public ScrapeRequestWrapper annotateSpan(final String value) { - if (this.rootSpan != null) - this.rootSpan.annotate(value); - return this; - } - - public AgentContext getAgentContext() { return this.agentContext; } - - public long getScrapeId() { return this.scrapeRequest.getScrapeId(); } - - public ScrapeRequest getScrapeRequest() { return this.scrapeRequest; } - - public ScrapeResponse getScrapeResponse() { return this.scrapeResponseRef.get(); } - - public ScrapeRequestWrapper setScrapeResponse(final ScrapeResponse scrapeResponse) { - this.scrapeResponseRef.set(scrapeResponse); - return this; - } - - public long ageInSecs() { return (System.currentTimeMillis() - this.createTime) / 1000;} - - public ScrapeRequestWrapper markComplete() { - if (this.requestTimer != null) - this.requestTimer.observeDuration(); - this.complete.countDown(); - return this; - } - - public boolean waitUntilCompleteMillis(final long waitMillis) { - try { - return this.complete.await(waitMillis, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - // Ignore - } - return false; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("scrapeId", scrapeRequest.getScrapeId()) - .add("path", scrapeRequest.getPath()) - .toString(); - } -} diff --git a/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt b/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt new file mode 100644 index 00000000..25a911a8 --- /dev/null +++ b/src/main/java/io/prometheus/proxy/ScrapeRequestWrapper.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus.proxy + + +import com.google.common.base.MoreObjects +import com.google.common.base.Preconditions +import io.prometheus.Proxy +import io.prometheus.grpc.ScrapeRequest +import io.prometheus.grpc.ScrapeResponse +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.atomic.AtomicReference + +class ScrapeRequestWrapper(proxy: Proxy, + agentContext: AgentContext, + path: String, + accept: String?) { + + private val createTime = System.currentTimeMillis() + private val complete = CountDownLatch(1) + private val scrapeResponseRef = AtomicReference() + private val requestTimer = proxy.metrics?.scrapeRequestLatency?.startTimer() + + val agentContext: AgentContext = Preconditions.checkNotNull(agentContext) + + val scrapeRequest: ScrapeRequest + + val scrapeId: Long + get() = this.scrapeRequest.scrapeId + + val scrapeResponse: ScrapeResponse + get() = this.scrapeResponseRef.get() + + init { + var builder = + ScrapeRequest.newBuilder() + .setAgentId(agentContext.agentId) + .setScrapeId(SCRAPE_ID_GENERATOR.getAndIncrement()) + .setPath(path) + if (!accept.isNullOrBlank()) + builder = builder.setAccept(accept) + this.scrapeRequest = builder.build() + } + + fun setScrapeResponse(scrapeResponse: ScrapeResponse) = this.scrapeResponseRef.set(scrapeResponse) + + fun ageInSecs(): Long = (System.currentTimeMillis() - this.createTime) / 1000 + + fun markComplete() { + this.requestTimer?.observeDuration() + this.complete.countDown() + } + + fun waitUntilCompleteMillis(waitMillis: Long): Boolean { + try { + return this.complete.await(waitMillis, TimeUnit.MILLISECONDS) + } catch (e: InterruptedException) { + // Ignore + } + + return false + } + + override fun toString() = + MoreObjects.toStringHelper(this) + .add("scrapeId", scrapeRequest.scrapeId) + .add("path", scrapeRequest.path) + .toString() + + companion object { + private val SCRAPE_ID_GENERATOR = AtomicLong(0) + } +} diff --git a/src/test/java/io/prometheus/AdminDefaultPathTest.java b/src/test/java/io/prometheus/AdminDefaultPathTest.java deleted file mode 100644 index ec947ef0..00000000 --- a/src/test/java/io/prometheus/AdminDefaultPathTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import okhttp3.Request; -import okhttp3.Response; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeoutException; - -import static io.prometheus.TestConstants.OK_HTTP_CLIENT; -import static java.lang.String.format; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; - -public class AdminDefaultPathTest { - - private static final Logger logger = LoggerFactory.getLogger(AdminDefaultPathTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy(null, true, false, Collections.emptyList()); - AGENT = TestUtils.startAgent(null, true, false, Collections.emptyList()); - - AGENT.awaitInitialConnection(5, SECONDS); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - @Test - public void proxyPingPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.pingPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string()).startsWith("pong"); - } - } - - @Test - public void agentPingPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.pingPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string()).startsWith("pong"); - } - } - - @Test - public void proxyVersionPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.versionPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string()).contains("Version"); - } - } - - @Test - public void agentVersionPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.versionPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string()).contains("Version"); - } - } - - @Test - public void proxyHealthCheckPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.healthCheckPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string().length()).isGreaterThan(10); - } - } - - @Test - public void agentHealthCheckPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.healthCheckPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.body().string().length()).isGreaterThan(10); - } - } - - @Test - public void proxyThreadDumpPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.threadDumpPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.body().string().length()).isGreaterThan(10); - } - } - - @Test - public void agentThreadDumpPathTest() - throws Exception { - String url = format("http://localhost:%d/%s", AGENT.getConfigVals().admin.port, AGENT.getConfigVals().admin.threadDumpPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.body().string().length()).isGreaterThan(10); - } - } -} diff --git a/src/test/java/io/prometheus/AdminDefaultPathTest.kt b/src/test/java/io/prometheus/AdminDefaultPathTest.kt new file mode 100644 index 00000000..7cfe5cd6 --- /dev/null +++ b/src/test/java/io/prometheus/AdminDefaultPathTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.TestConstants.OK_HTTP_CLIENT +import io.prometheus.client.CollectorRegistry +import okhttp3.Request +import org.assertj.core.api.Assertions.assertThat +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.io.IOException +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException + +class AdminDefaultPathTest { + + @Test + fun proxyPingPathTest() { + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.pingPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).startsWith("pong") + } + } + + @Test + fun agentPingPathTest() { + val url = "http://localhost:${AGENT!!.configVals.admin.port}/${AGENT!!.configVals.admin.pingPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).startsWith("pong") + } + } + + @Test + fun proxyVersionPathTest() { + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.versionPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).contains("Version") + } + } + + @Test + fun agentVersionPathTest() { + val url = "http://localhost:${AGENT!!.configVals.admin.port}/${AGENT!!.configVals.admin.versionPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).contains("Version") + } + } + + @Test + fun proxyHealthCheckPathTest() { + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.healthCheckPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string().length).isGreaterThan(10) + } + } + + @Test + fun agentHealthCheckPathTest() { + val url = "http://localhost:${AGENT!!.configVals.admin.port}/${AGENT!!.configVals.admin.healthCheckPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + } + + @Test + fun proxyThreadDumpPathTest() { + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.threadDumpPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + } + + @Test + fun agentThreadDumpPathTest() { + val url = "http://localhost:${AGENT!!.configVals.admin.port}/${AGENT!!.configVals.admin.threadDumpPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + } + + companion object { + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + @Throws(IOException::class, InterruptedException::class, TimeoutException::class) + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + PROXY = TestUtils.startProxy(null, true, false, emptyList()) + AGENT = TestUtils.startAgent(null, true, false, emptyList()) + + AGENT!!.awaitInitialConnection(5, SECONDS) + } + + @JvmStatic + @AfterClass + @Throws(InterruptedException::class, TimeoutException::class) + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } +} diff --git a/src/test/java/io/prometheus/AdminEmptyPathTest.java b/src/test/java/io/prometheus/AdminEmptyPathTest.java deleted file mode 100644 index 2c539149..00000000 --- a/src/test/java/io/prometheus/AdminEmptyPathTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import okhttp3.Request; -import okhttp3.Response; -import org.assertj.core.util.Lists; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeoutException; - -import static io.prometheus.TestConstants.OK_HTTP_CLIENT; -import static java.lang.String.format; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; - -public class AdminEmptyPathTest { - - private static final Logger logger = LoggerFactory.getLogger(AdminEmptyPathTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - final List args = Lists.newArrayList(); - args.add("-Dproxy.admin.port=8098"); - args.add("-Dproxy.admin.pingPath=\"\""); - args.add("-Dproxy.admin.versionPath=\"\""); - args.add("-Dproxy.admin.healthCheckPath=\"\""); - args.add("-Dproxy.admin.threadDumpPath=\"\""); - PROXY = TestUtils.startProxy(null, true, false, args); - AGENT = TestUtils.startAgent(null, true, false, Collections.emptyList()); - - AGENT.awaitInitialConnection(5, SECONDS); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - @Test - public void proxyPingPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.port).isEqualTo(8098); - assertThat(PROXY.getConfigVals().admin.pingPath).isEqualTo(""); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.pingPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - } - - @Test - public void proxyVersionPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.port).isEqualTo(8098); - assertThat(PROXY.getConfigVals().admin.versionPath).isEqualTo(""); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.versionPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - } - - @Test - public void proxyHealthCheckPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.healthCheckPath).isEqualTo(""); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.healthCheckPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - } - - @Test - public void proxyThreadDumpPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.threadDumpPath).isEqualTo(""); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.threadDumpPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - } -} diff --git a/src/test/java/io/prometheus/AdminEmptyPathTest.kt b/src/test/java/io/prometheus/AdminEmptyPathTest.kt new file mode 100644 index 00000000..6d5f12b4 --- /dev/null +++ b/src/test/java/io/prometheus/AdminEmptyPathTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.TestConstants.OK_HTTP_CLIENT +import io.prometheus.client.CollectorRegistry +import okhttp3.Request +import org.assertj.core.api.Assertions.assertThat +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.io.IOException +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException + +class AdminEmptyPathTest { + + @Test + fun proxyPingPathTest() { + assertThat(PROXY!!.configVals.admin.port).isEqualTo(8098) + assertThat(PROXY!!.configVals.admin.pingPath).isEqualTo("") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.pingPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + } + + @Test + fun proxyVersionPathTest() { + assertThat(PROXY!!.configVals.admin.port).isEqualTo(8098) + assertThat(PROXY!!.configVals.admin.versionPath).isEqualTo("") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.versionPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + } + + @Test + fun proxyHealthCheckPathTest() { + assertThat(PROXY!!.configVals.admin.healthCheckPath).isEqualTo("") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.healthCheckPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + } + + @Test + fun proxyThreadDumpPathTest() { + assertThat(PROXY!!.configVals.admin.threadDumpPath).isEqualTo("") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.threadDumpPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + } + + companion object { + + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + @Throws(IOException::class, InterruptedException::class, TimeoutException::class) + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + val args = listOf("-Dproxy.admin.port=8098", + "-Dproxy.admin.pingPath=\"\"", + "-Dproxy.admin.versionPath=\"\"", + "-Dproxy.admin.healthCheckPath=\"\"", + "-Dproxy.admin.threadDumpPath=\"\"") + PROXY = TestUtils.startProxy(null, true, false, args) + AGENT = TestUtils.startAgent(null, true, false, emptyList()) + + AGENT!!.awaitInitialConnection(5, SECONDS) + } + + @JvmStatic + @AfterClass + @Throws(InterruptedException::class, TimeoutException::class) + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } +} diff --git a/src/test/java/io/prometheus/AdminNonDefaultPathTest.java b/src/test/java/io/prometheus/AdminNonDefaultPathTest.java deleted file mode 100644 index e11fc087..00000000 --- a/src/test/java/io/prometheus/AdminNonDefaultPathTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import okhttp3.Request; -import okhttp3.Response; -import org.assertj.core.util.Lists; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeoutException; - -import static io.prometheus.TestConstants.OK_HTTP_CLIENT; -import static java.lang.String.format; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; - -public class AdminNonDefaultPathTest { - - private static final Logger logger = LoggerFactory.getLogger(AdminNonDefaultPathTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - final List args = Lists.newArrayList(); - args.add("-Dproxy.admin.port=8099"); - args.add("-Dproxy.admin.pingPath=pingPath2"); - args.add("-Dproxy.admin.versionPath=versionPath2"); - args.add("-Dproxy.admin.healthCheckPath=healthCheckPath2"); - args.add("-Dproxy.admin.threadDumpPath=threadDumpPath2"); - PROXY = TestUtils.startProxy(null, true, false, args); - AGENT = TestUtils.startAgent(null, true, false, Collections.emptyList()); - - AGENT.awaitInitialConnection(5, SECONDS); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - @Test - public void proxyPingPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.port).isEqualTo(8099); - assertThat(PROXY.getConfigVals().admin.pingPath).isEqualTo("pingPath2"); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.pingPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string()).startsWith("pong"); - } - } - - @Test - public void proxyVersionPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.port).isEqualTo(8099); - assertThat(PROXY.getConfigVals().admin.versionPath).isEqualTo("versionPath2"); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.versionPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string()).contains("Version"); - } - } - - @Test - public void proxyHealthCheckPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.healthCheckPath).isEqualTo("healthCheckPath2"); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.healthCheckPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - assertThat(response.body().string().length()).isGreaterThan(10); - } - } - - @Test - public void proxyThreadDumpPathTest() - throws Exception { - assertThat(PROXY.getConfigVals().admin.threadDumpPath).isEqualTo("threadDumpPath2"); - String url = format("http://localhost:%d/%s", PROXY.getConfigVals().admin.port, PROXY.getConfigVals().admin.threadDumpPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.body().string().length()).isGreaterThan(10); - } - } -} diff --git a/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt b/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt new file mode 100644 index 00000000..70aa860f --- /dev/null +++ b/src/test/java/io/prometheus/AdminNonDefaultPathTest.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.TestConstants.OK_HTTP_CLIENT +import io.prometheus.client.CollectorRegistry +import okhttp3.Request +import org.assertj.core.api.Assertions.assertThat +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.io.IOException +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException + +class AdminNonDefaultPathTest { + + @Test + fun proxyPingPathTest() { + assertThat(PROXY!!.configVals.admin.port).isEqualTo(8099) + assertThat(PROXY!!.configVals.admin.pingPath).isEqualTo("pingPath2") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.pingPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).startsWith("pong") + } + } + + @Test + fun proxyVersionPathTest() { + assertThat(PROXY!!.configVals.admin.port).isEqualTo(8099) + assertThat(PROXY!!.configVals.admin.versionPath).isEqualTo("versionPath2") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.versionPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string()).contains("Version") + } + } + + @Test + fun proxyHealthCheckPathTest() { + assertThat(PROXY!!.configVals.admin.healthCheckPath).isEqualTo("healthCheckPath2") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.healthCheckPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { + assertThat(it.code()).isEqualTo(200) + assertThat(it.body()!!.string().length).isGreaterThan(10) + } + } + + @Test + fun proxyThreadDumpPathTest() { + assertThat(PROXY!!.configVals.admin.threadDumpPath).isEqualTo("threadDumpPath2") + val url = "http://localhost:${PROXY!!.configVals.admin.port}/${PROXY!!.configVals.admin.threadDumpPath}" + val request = Request.Builder().url(url) + OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.body()!!.string().length).isGreaterThan(10) } + } + + companion object { + + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + @Throws(IOException::class, InterruptedException::class, TimeoutException::class) + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + val args = listOf("-Dproxy.admin.port=8099", + "-Dproxy.admin.pingPath=pingPath2", + "-Dproxy.admin.versionPath=versionPath2", + "-Dproxy.admin.healthCheckPath=healthCheckPath2", + "-Dproxy.admin.threadDumpPath=threadDumpPath2" + ) + PROXY = TestUtils.startProxy(null, true, false, args) + AGENT = TestUtils.startAgent(null, true, false, emptyList()) + + AGENT!!.awaitInitialConnection(5, SECONDS) + } + + @JvmStatic + @AfterClass + @Throws(InterruptedException::class, TimeoutException::class) + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } +} diff --git a/src/test/java/io/prometheus/AutoValueTest.java b/src/test/java/io/prometheus/AutoValueTest.java deleted file mode 100644 index 608533fb..00000000 --- a/src/test/java/io/prometheus/AutoValueTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import com.typesafe.config.ConfigParseOptions; -import com.typesafe.config.ConfigSyntax; -import io.prometheus.common.AdminConfig; -import io.prometheus.common.ConfigVals; -import io.prometheus.common.MetricsConfig; -import io.prometheus.common.ZipkinConfig; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AutoValueTest { - - private ConfigVals configVals(final String str) { - final Config config = ConfigFactory.parseString(str, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF)); - return new ConfigVals(config.withFallback(ConfigFactory.load().resolve()).resolve()); - } - - @Test - public void adminConfigTest() { - ConfigVals vals = configVals("agent.admin.enabled=true"); - AdminConfig c = AdminConfig.create(vals.agent.admin.enabled, -1, vals.agent.admin); - assertThat(c.enabled()).isTrue(); - - vals = configVals("agent.admin.port=888"); - c = AdminConfig.create(vals.agent.admin.enabled, vals.agent.admin.port, vals.agent.admin); - assertThat(c.enabled()).isFalse(); - assertThat(c.port()).isEqualTo(888); - - c = AdminConfig.create(true, 444, configVals("agent.admin.pingPath=a pingpath val").agent.admin); - assertThat(c.pingPath()).isEqualTo("a pingpath val"); - - c = AdminConfig.create(true, 444, configVals("agent.admin.versionPath=a versionpath val").agent.admin); - assertThat(c.versionPath()).isEqualTo("a versionpath val"); - - c = AdminConfig.create(true, 444, configVals("agent.admin.healthCheckPath=a healthCheckPath val").agent.admin); - assertThat(c.healthCheckPath()).isEqualTo("a healthCheckPath val"); - - c = AdminConfig.create(true, 444, configVals("agent.admin.threadDumpPath=a threadDumpPath val").agent.admin); - assertThat(c.threadDumpPath()).isEqualTo("a threadDumpPath val"); - } - - @Test - public void metricsConfigTest() { - MetricsConfig c = MetricsConfig.create(true, 555, configVals("agent.metrics.enabled=true").agent.metrics); - assertThat(c.enabled()).isTrue(); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.hostname=testval").agent.metrics); - assertThat(c.port()).isEqualTo(555); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.path=a path val").agent.metrics); - assertThat(c.path()).isEqualTo("a path val"); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.standardExportsEnabled=true").agent.metrics); - assertThat(c.standardExportsEnabled()).isTrue(); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.memoryPoolsExportsEnabled=true").agent.metrics); - assertThat(c.memoryPoolsExportsEnabled()).isTrue(); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.garbageCollectorExportsEnabled=true").agent.metrics); - assertThat(c.garbageCollectorExportsEnabled()).isTrue(); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.threadExportsEnabled=true").agent.metrics); - assertThat(c.threadExportsEnabled()).isTrue(); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.classLoadingExportsEnabled=true").agent.metrics); - assertThat(c.classLoadingExportsEnabled()).isTrue(); - - c = MetricsConfig.create(true, 555, configVals("agent.metrics.versionInfoExportsEnabled=true").agent.metrics); - assertThat(c.versionInfoExportsEnabled()).isTrue(); - } - - @Test - public void zipkinConfigTest() { - ZipkinConfig c = ZipkinConfig.create(configVals("agent.internal.zipkin.enabled=true").agent.internal.zipkin); - assertThat(c.enabled()).isTrue(); - - c = ZipkinConfig.create(configVals("agent.internal.zipkin.hostname=testval").agent.internal.zipkin); - assertThat(c.hostname()).isEqualTo("testval"); - - c = ZipkinConfig.create(configVals("agent.internal.zipkin.port=999").agent.internal.zipkin); - assertThat(c.port()).isEqualTo(999); - - c = ZipkinConfig.create(configVals("agent.internal.zipkin.path=a path val").agent.internal.zipkin); - assertThat(c.path()).isEqualTo("a path val"); - - c = ZipkinConfig.create(configVals("agent.internal.zipkin.serviceName=a service name").agent.internal.zipkin); - assertThat(c.serviceName()).isEqualTo("a service name"); - } -} diff --git a/src/test/java/io/prometheus/DataClassTest.kt b/src/test/java/io/prometheus/DataClassTest.kt new file mode 100644 index 00000000..4e43eabc --- /dev/null +++ b/src/test/java/io/prometheus/DataClassTest.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import com.typesafe.config.ConfigFactory +import com.typesafe.config.ConfigParseOptions +import com.typesafe.config.ConfigSyntax +import io.prometheus.common.AdminConfig +import io.prometheus.common.ConfigVals +import io.prometheus.common.MetricsConfig +import io.prometheus.common.ZipkinConfig +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class DataClassTest { + + private fun configVals(str: String): ConfigVals { + val config = ConfigFactory.parseString(str, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF)) + return ConfigVals(config.withFallback(ConfigFactory.load().resolve()).resolve()) + } + + @Test + fun adminConfigTest() { + var vals = configVals("agent.admin.enabled=true") + var c = AdminConfig.create(vals.agent.admin.enabled, -1, vals.agent.admin) + assertThat(c.enabled).isTrue() + + vals = configVals("agent.admin.port=888") + c = AdminConfig.create(vals.agent.admin.enabled, vals.agent.admin.port, vals.agent.admin) + assertThat(c.enabled).isFalse() + assertThat(c.port).isEqualTo(888) + + c = AdminConfig.create(true, 444, configVals("agent.admin.pingPath=a pingpath val").agent.admin) + assertThat(c.pingPath).isEqualTo("a pingpath val") + + c = AdminConfig.create(true, 444, configVals("agent.admin.versionPath=a versionpath val").agent.admin) + assertThat(c.versionPath).isEqualTo("a versionpath val") + + c = AdminConfig.create(true, 444, configVals("agent.admin.healthCheckPath=a healthCheckPath val").agent.admin) + assertThat(c.healthCheckPath).isEqualTo("a healthCheckPath val") + + c = AdminConfig.create(true, 444, configVals("agent.admin.threadDumpPath=a threadDumpPath val").agent.admin) + assertThat(c.threadDumpPath).isEqualTo("a threadDumpPath val") + } + + @Test + fun metricsConfigTest() { + var c = MetricsConfig.create(true, 555, configVals("agent.metrics.enabled=true").agent.metrics) + assertThat(c.enabled).isTrue() + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.hostname=testval").agent.metrics) + assertThat(c.port).isEqualTo(555) + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.path=a path val").agent.metrics) + assertThat(c.path).isEqualTo("a path val") + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.standardExportsEnabled=true").agent.metrics) + assertThat(c.standardExportsEnabled).isTrue() + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.memoryPoolsExportsEnabled=true").agent.metrics) + assertThat(c.memoryPoolsExportsEnabled).isTrue() + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.garbageCollectorExportsEnabled=true").agent.metrics) + assertThat(c.garbageCollectorExportsEnabled).isTrue() + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.threadExportsEnabled=true").agent.metrics) + assertThat(c.threadExportsEnabled).isTrue() + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.classLoadingExportsEnabled=true").agent.metrics) + assertThat(c.classLoadingExportsEnabled).isTrue() + + c = MetricsConfig.create(true, 555, configVals("agent.metrics.versionInfoExportsEnabled=true").agent.metrics) + assertThat(c.versionInfoExportsEnabled).isTrue() + } + + @Test + fun zipkinConfigTest() { + var c = ZipkinConfig.create(configVals("agent.internal.zipkin.enabled=true").agent.internal.zipkin) + assertThat(c.enabled).isTrue() + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.hostname=testval").agent.internal.zipkin) + assertThat(c.hostname).isEqualTo("testval") + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.port=999").agent.internal.zipkin) + assertThat(c.port).isEqualTo(999) + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.path=a path val").agent.internal.zipkin) + assertThat(c.path).isEqualTo("a path val") + + c = ZipkinConfig.create(configVals("agent.internal.zipkin.serviceName=a service name").agent.internal.zipkin) + assertThat(c.serviceName).isEqualTo("a service name") + } +} diff --git a/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java deleted file mode 100644 index 2e82fb25..00000000 --- a/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeoutException; - -import static java.util.concurrent.TimeUnit.SECONDS; - -public class InProcessTestNoAdminMetricsTest { - - private static final Logger logger = LoggerFactory.getLogger(InProcessTestNoAdminMetricsTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy("nometrics", false, false, Collections.emptyList()); - AGENT = TestUtils.startAgent("nometrics", false, false, Collections.emptyList()); - - AGENT.awaitInitialConnection(10, SECONDS); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - - @Test - public void missingPathTest() - throws Exception { - Tests.missingPathTest(); - } - - @Test - public void invalidPathTest() - throws Exception { - Tests.invalidPathTest(); - } - - @Test - public void addRemovePathsTest() - throws Exception { - Tests.addRemovePathsTest(AGENT); - } - - @Test - public void threadedAddRemovePathsTest() - throws Exception { - Tests.threadedAddRemovePathsTest(AGENT); - } - - @Test - public void invalidAgentUrlTest() - throws Exception { - Tests.invalidAgentUrlTest(AGENT); - } - - @Test - public void timeoutTest() - throws Exception { - Tests.timeoutTest(AGENT); - } - - @Test - public void proxyCallTest() - throws Exception { - Tests.proxyCallTest(AGENT, 25, 50, 1000, 25); - } - -} diff --git a/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt new file mode 100644 index 00000000..e8824e77 --- /dev/null +++ b/src/test/java/io/prometheus/InProcessTestNoAdminMetricsTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.client.CollectorRegistry +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.util.concurrent.TimeUnit.SECONDS + +class InProcessTestNoAdminMetricsTest { + + @Test + fun missingPathTest() { + Tests.missingPathTest() + } + + @Test + fun invalidPathTest() { + Tests.invalidPathTest() + } + + @Test + fun addRemovePathsTest() { + Tests.addRemovePathsTest(AGENT!!) + } + + @Test + fun threadedAddRemovePathsTest() { + Tests.threadedAddRemovePathsTest(AGENT!!) + } + + @Test + fun invalidAgentUrlTest() { + Tests.invalidAgentUrlTest(AGENT!!) + } + + @Test + fun timeoutTest() { + Tests.timeoutTest(AGENT!!) + } + + @Test + fun proxyCallTest() { + Tests.proxyCallTest(AGENT!!, 25, 50, 1000, 25) + } + + companion object { + + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + PROXY = TestUtils.startProxy("nometrics", false, false, emptyList()) + AGENT = TestUtils.startAgent("nometrics", false, false, emptyList()) + + AGENT!!.awaitInitialConnection(10, SECONDS) + } + + @JvmStatic + @AfterClass + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } + +} diff --git a/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java deleted file mode 100644 index ca2fb14a..00000000 --- a/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeoutException; - -import static java.util.concurrent.TimeUnit.SECONDS; - -public class InProcessTestWithAdminMetricsTest { - - private static final Logger logger = LoggerFactory.getLogger(InProcessTestWithAdminMetricsTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy("withmetrics", true, true, Collections.emptyList()); - AGENT = TestUtils.startAgent("withmetrics", true, true, Collections.emptyList()); - - AGENT.awaitInitialConnection(10, SECONDS); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - @Test - public void missingPathTest() - throws Exception { - Tests.missingPathTest(); - } - - @Test - public void invalidPathTest() - throws Exception { - Tests.invalidPathTest(); - } - - @Test - public void addRemovePathsTest() - throws Exception { - Tests.addRemovePathsTest(AGENT); - } - - @Test - public void threadedAddRemovePathsTest() - throws Exception { - Tests.threadedAddRemovePathsTest(AGENT); - } - - @Test - public void invalidAgentUrlTest() - throws Exception { - Tests.invalidAgentUrlTest(AGENT); - } - - @Test - public void timeoutTest() - throws Exception { - Tests.timeoutTest(AGENT); - } - - // proxyCallTest() called in InProcess tests -} diff --git a/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt new file mode 100644 index 00000000..28ece43d --- /dev/null +++ b/src/test/java/io/prometheus/InProcessTestWithAdminMetricsTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.client.CollectorRegistry +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.util.concurrent.TimeUnit.SECONDS + +class InProcessTestWithAdminMetricsTest { + + @Test + fun missingPathTest() { + Tests.missingPathTest() + } + + @Test + fun invalidPathTest() { + Tests.invalidPathTest() + } + + @Test + fun addRemovePathsTest() { + Tests.addRemovePathsTest(AGENT!!) + } + + @Test + fun threadedAddRemovePathsTest() { + Tests.threadedAddRemovePathsTest(AGENT!!) + } + + @Test + fun invalidAgentUrlTest() { + Tests.invalidAgentUrlTest(AGENT!!) + } + + @Test + fun timeoutTest() { + Tests.timeoutTest(AGENT!!) + } + + companion object { + + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + PROXY = TestUtils.startProxy("withmetrics", true, true, emptyList()) + AGENT = TestUtils.startAgent("withmetrics", true, true, emptyList()) + + AGENT!!.awaitInitialConnection(10, SECONDS) + } + + @JvmStatic + @AfterClass + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } + + // proxyCallTest() called in InProcess tests +} diff --git a/src/test/java/io/prometheus/MiscTests.kt b/src/test/java/io/prometheus/MiscTests.kt new file mode 100644 index 00000000..4a2a8ee0 --- /dev/null +++ b/src/test/java/io/prometheus/MiscTests.kt @@ -0,0 +1,240 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import com.google.common.collect.Maps +import io.prometheus.agent.RequestFailureException +import io.prometheus.common.sleepForMillis +import io.prometheus.common.sleepForSecs +import okhttp3.Request +import org.assertj.core.api.Assertions.assertThat +import spark.Service +import java.lang.Math.abs +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger +import java.util.stream.IntStream + +object Tests { + + fun missingPathTest() { + val url = "http://localhost:${TestConstants.PROXY_PORT}/" + val request = Request.Builder().url(url) + TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + } + + fun invalidPathTest() { + val url = "http://localhost:${TestConstants.PROXY_PORT}/invalid_path" + val request = Request.Builder().url(url) + TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + } + + fun addRemovePathsTest(agent: Agent) { + // Take into account pre-existing paths already registered + val originalSize = agent.pathMapSize() + + var cnt = 0 + IntStream.range(0, TestConstants.REPS) + .forEach { + val path = "test-$it" + agent.registerPath(path, "http://localhost:${TestConstants.PROXY_PORT}/$path") + cnt++ + assertThat(agent.pathMapSize()).isEqualTo(originalSize + cnt) + agent.unregisterPath(path) + cnt-- + assertThat(agent.pathMapSize()).isEqualTo(originalSize + cnt) + } + } + + fun threadedAddRemovePathsTest(agent: Agent) { + val paths = mutableListOf() + val cnt = AtomicInteger(0) + val latch1 = CountDownLatch(TestConstants.REPS) + val latch2 = CountDownLatch(TestConstants.REPS) + + // Take into account pre-existing paths already registered + val originalSize = agent.pathMapSize() + + IntStream.range(0, TestConstants.REPS) + .forEach { + TestConstants.EXECUTOR_SERVICE.submit( + { + val path = "test-${cnt.getAndIncrement()}" + synchronized(paths) { + paths.add(path) + } + try { + val url = "http://localhost:${TestConstants.PROXY_PORT}/$path" + agent.registerPath(path, url) + latch1.countDown() + } catch (e: RequestFailureException) { + e.printStackTrace() + } + }) + } + + assertThat(latch1.await(5, TimeUnit.SECONDS)).isTrue() + assertThat(paths.size).isEqualTo(TestConstants.REPS) + assertThat(agent.pathMapSize()).isEqualTo(originalSize + TestConstants.REPS) + + paths.forEach { + TestConstants + .EXECUTOR_SERVICE + .submit({ + try { + agent.unregisterPath(it) + latch2.countDown() + } catch (e: RequestFailureException) { + e.printStackTrace() + } + }) + } + + // Wait for all unregistrations to complete + assertThat(latch2.await(5, TimeUnit.SECONDS)).isTrue() + assertThat(agent.pathMapSize()).isEqualTo(originalSize) + } + + fun invalidAgentUrlTest(agent: Agent) { + val badPath = "badPath" + + agent.registerPath(badPath, "http://localhost:33/metrics") + + val url = "http://localhost:${TestConstants.PROXY_PORT}/$badPath" + val request = Request.Builder().url(url) + TestConstants + .OK_HTTP_CLIENT + .newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + + agent.unregisterPath(badPath) + } + + fun timeoutTest(agent: Agent) { + val agentPort = 9700 + val proxyPath = "proxy-timeout" + val agentPath = "agent-timeout" + + val http = Service.ignite() + http.port(agentPort) + .get("/$agentPath") { _, res -> + res.type("text/plain") + sleepForSecs(10) + "I timed out" + } + val agentUrl = "http://localhost:$agentPort/$agentPath" + agent.registerPath("/$proxyPath", agentUrl) + + val proxyUrl = "http://localhost:${TestConstants.PROXY_PORT}/$proxyPath" + val request = Request.Builder().url(proxyUrl) + TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute().use { assertThat(it.code()).isEqualTo(404) } + + agent.unregisterPath("/$proxyPath") + http.stop() + } + + fun proxyCallTest(agent: Agent, + httpServerCount: Int, + pathCount: Int, + queryCount: Int, + pauseMillis: Long) { + + val startingPort = 9600 + val httpServers = mutableListOf() + val pathMap = Maps.newConcurrentMap() + + // Take into account pre-existing paths already registered + val originalSize = agent.pathMapSize() + + // Create the endpoints + IntStream.range(0, httpServerCount) + .forEach { i -> + val http = Service.ignite() + http.port(startingPort + i) + .threadPool(30, 10, 1000) + .get("/agent-$i") { _, res -> + res.type("text/plain") + "value: $i" + } + httpServers.add(http) + } + + // Create the paths + IntStream.range(0, pathCount) + .forEach { + val index = abs(TestConstants.RANDOM.nextInt()) % httpServers.size + agent.registerPath("proxy-$it", "http://localhost:${startingPort + index}/agent-$index") + pathMap.put(it, index) + } + + assertThat(agent.pathMapSize()).isEqualTo(originalSize + pathCount) + + // Call the proxy sequentially + IntStream.range(0, queryCount) + .forEach { + callProxy(pathMap) + sleepForMillis(pauseMillis) + } + + // Call the proxy in parallel + val threadedQueryCount = 100 + val latch = CountDownLatch(threadedQueryCount) + IntStream.range(0, threadedQueryCount) + .forEach { + TestConstants.EXECUTOR_SERVICE + .submit { + try { + callProxy(pathMap) + latch.countDown() + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue() + + val errorCnt = AtomicInteger() + pathMap.forEach { + try { + agent.unregisterPath("proxy-${it.key}") + } catch (e: RequestFailureException) { + errorCnt.incrementAndGet() + } + } + + assertThat(errorCnt.get()).isEqualTo(0) + assertThat(agent.pathMapSize()).isEqualTo(originalSize) + + httpServers.forEach(Service::stop) + } +} + +private fun callProxy(pathMap: Map) { + // Choose one of the pathMap values + val index = abs(TestConstants.RANDOM.nextInt() % pathMap.size) + val httpVal = pathMap[index] + val url = "http://localhost:${TestConstants.PROXY_PORT}/proxy-$index" + val request = Request.Builder().url(url) + TestConstants.OK_HTTP_CLIENT + .newCall(request.build()) + .execute() + .use { + assertThat(it.code()).isEqualTo(200) + val body = it.body()!!.string() + assertThat(body).isEqualTo("value: $httpVal") + } +} diff --git a/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java deleted file mode 100644 index d21b0e43..00000000 --- a/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeoutException; - -import static java.util.concurrent.TimeUnit.SECONDS; - -public class NettyTestNoAdminMetricsTest { - - private static final Logger logger = LoggerFactory.getLogger(NettyTestNoAdminMetricsTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy(null, false, false, Collections.emptyList()); - AGENT = TestUtils.startAgent(null, false, false, Collections.emptyList()); - - AGENT.awaitInitialConnection(10, SECONDS); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - @Test - public void missingPathTest() - throws Exception { - Tests.missingPathTest(); - } - - @Test - public void invalidPathTest() - throws Exception { - Tests.invalidPathTest(); - } - - @Test - public void addRemovePathsTest() - throws Exception { - Tests.addRemovePathsTest(AGENT); - } - - @Test - public void threadedAddRemovePathsTest() - throws Exception { - Tests.threadedAddRemovePathsTest(AGENT); - } - - @Test - public void invalidAgentUrlTest() - throws Exception { - Tests.invalidAgentUrlTest(AGENT); - } - - @Test - public void timeoutTest() - throws Exception { - Tests.timeoutTest(AGENT); - } - - // proxyCallTest() called in InProcess tests -} diff --git a/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt new file mode 100644 index 00000000..9903c65d --- /dev/null +++ b/src/test/java/io/prometheus/NettyTestNoAdminMetricsTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.client.CollectorRegistry +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException + +class NettyTestNoAdminMetricsTest { + + @Test + fun missingPathTest() { + Tests.missingPathTest() + } + + @Test + fun invalidPathTest() { + Tests.invalidPathTest() + } + + @Test + fun addRemovePathsTest() { + Tests.addRemovePathsTest(AGENT!!) + } + + @Test + fun threadedAddRemovePathsTest() { + Tests.threadedAddRemovePathsTest(AGENT!!) + } + + @Test + fun invalidAgentUrlTest() { + Tests.invalidAgentUrlTest(AGENT!!) + } + + @Test + fun timeoutTest() { + Tests.timeoutTest(AGENT!!) + } + + companion object { + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + PROXY = TestUtils.startProxy(null, false, false, emptyList()) + AGENT = TestUtils.startAgent(null, false, false, emptyList()) + + AGENT!!.awaitInitialConnection(10, SECONDS) + } + + @JvmStatic + @AfterClass + @Throws(InterruptedException::class, TimeoutException::class) + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } + + // proxyCallTest() called in InProcess tests +} diff --git a/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java deleted file mode 100644 index b3acb2d9..00000000 --- a/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.client.CollectorRegistry; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.TimeoutException; - -import static io.prometheus.common.Utils.sleepForSecs; -import static java.util.concurrent.TimeUnit.SECONDS; - -public class NettyTestWithAdminMetricsTest { - - private static final Logger logger = LoggerFactory.getLogger(NettyTestWithAdminMetricsTest.class); - - private static Proxy PROXY = null; - private static Agent AGENT = null; - - @BeforeClass - public static void setUp() - throws IOException, InterruptedException, TimeoutException { - CollectorRegistry.defaultRegistry.clear(); - PROXY = TestUtils.startProxy(null, true, true, Collections.emptyList()); - AGENT = TestUtils.startAgent(null, true, true, Collections.emptyList()); - - AGENT.awaitInitialConnection(10, SECONDS); - - // Wait long enough to trigger heartbeat for code coverage - sleepForSecs(15); - } - - @AfterClass - public static void takeDown() - throws InterruptedException, TimeoutException { - PROXY.stopAsync(); - PROXY.awaitTerminated(5, SECONDS); - AGENT.stopAsync(); - AGENT.awaitTerminated(5, SECONDS); - } - - @Test - public void missingPathTest() - throws Exception { - Tests.missingPathTest(); - } - - @Test - public void invalidPathTest() - throws Exception { - Tests.invalidPathTest(); - } - - @Test - public void addRemovePathsTest() - throws Exception { - Tests.addRemovePathsTest(AGENT); - } - - @Test - public void threadedAddRemovePathsTest() - throws Exception { - Tests.threadedAddRemovePathsTest(AGENT); - } - - @Test - public void invalidAgentUrlTest() - throws Exception { - Tests.invalidAgentUrlTest(AGENT); - } - - @Test - public void timeoutTest() - throws Exception { - Tests.timeoutTest(AGENT); - } - - // proxyCallTest() called in InProcess tests -} diff --git a/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt new file mode 100644 index 00000000..7bce92e9 --- /dev/null +++ b/src/test/java/io/prometheus/NettyTestWithAdminMetricsTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.client.CollectorRegistry +import io.prometheus.common.sleepForSecs +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException + +class NettyTestWithAdminMetricsTest { + + @Test + fun missingPathTest() { + Tests.missingPathTest() + } + + @Test + fun invalidPathTest() { + Tests.invalidPathTest() + } + + @Test + fun addRemovePathsTest() { + Tests.addRemovePathsTest(AGENT!!) + } + + @Test + fun threadedAddRemovePathsTest() { + Tests.threadedAddRemovePathsTest(AGENT!!) + } + + @Test + fun invalidAgentUrlTest() { + Tests.invalidAgentUrlTest(AGENT!!) + } + + @Test + fun timeoutTest() { + Tests.timeoutTest(AGENT!!) + } + + companion object { + + private var PROXY: Proxy? = null + private var AGENT: Agent? = null + + @JvmStatic + @BeforeClass + fun setUp() { + CollectorRegistry.defaultRegistry.clear() + PROXY = TestUtils.startProxy(null, true, true, emptyList()) + AGENT = TestUtils.startAgent(null, true, true, emptyList()) + + AGENT!!.awaitInitialConnection(10, SECONDS) + + // Wait long enough to trigger heartbeat for code coverage + sleepForSecs(15) + } + + @JvmStatic + @AfterClass + @Throws(InterruptedException::class, TimeoutException::class) + fun takeDown() { + PROXY!!.stopAsync() + PROXY!!.awaitTerminated(5, SECONDS) + AGENT!!.stopAsync() + AGENT!!.awaitTerminated(5, SECONDS) + } + } + + // proxyCallTest() called in InProcess tests +} diff --git a/src/test/java/io/prometheus/OptionsTest.java b/src/test/java/io/prometheus/OptionsTest.java deleted file mode 100644 index fd528b1f..00000000 --- a/src/test/java/io/prometheus/OptionsTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.agent.AgentOptions; -import io.prometheus.common.ConfigVals; -import io.prometheus.proxy.ProxyOptions; -import org.junit.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.util.Lists.newArrayList; - -public class OptionsTest { - - private static String CONFIG = "https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/etc/test-configs/junit-test.conf"; - - @Test - public void verifyDefaultValues() { - final ConfigVals configVals = readProxyOptions(newArrayList()); - assertThat(configVals.proxy.http.port).isEqualTo(8080); - assertThat(configVals.proxy.internal.zipkin.enabled).isEqualTo(false); - } - - @Test - public void verifyConfValues() { - final ConfigVals configVals = readProxyOptions(newArrayList("--config", CONFIG)); - assertThat(configVals.proxy.http.port).isEqualTo(8181); - assertThat(configVals.proxy.internal.zipkin.enabled).isEqualTo(true); - } - - @Test - public void verifyUnquotedPropValue() { - final ConfigVals configVals = readProxyOptions(newArrayList("-Dproxy.http.port=9393", "-Dproxy.internal.zipkin.enabled=true")); - assertThat(configVals.proxy.http.port).isEqualTo(9393); - assertThat(configVals.proxy.internal.zipkin.enabled).isEqualTo(true); - } - - @Test - public void verifyQuotedPropValue() { - final ConfigVals configVals = readProxyOptions(newArrayList("-D\"proxy.http.port=9394\"")); - assertThat(configVals.proxy.http.port).isEqualTo(9394); - } - - @Test - public void verifyPathConfigs() { - final ConfigVals configVals = readAgentOptions(newArrayList("--config", CONFIG)); - assertThat(configVals.agent.pathConfigs.size()).isEqualTo(3); - } - - - public void verifyProxyDefaults() { - final ProxyOptions options = new ProxyOptions(newArrayList()); - - assertThat(options.getProxyPort()).isEqualTo(8080); - assertThat(options.getAgentPort()).isEqualTo(50021); - } - - public void verifyAgentDefaults() { - AgentOptions options = new AgentOptions(newArrayList("--name", "test-name", "--proxy", "host5"), - false); - - assertThat(options.isMetricsEnabled()).isEqualTo(false); - assertThat(options.getDynamicParams().size()).isEqualTo(0); - assertThat(options.getAgentName()).isEqualTo("test-name"); - assertThat(options.getProxyHostname()).isEqualTo("host5"); - } - - private ConfigVals readProxyOptions(final List argList) { - final ProxyOptions options = new ProxyOptions(argList); - return options.getConfigVals(); - } - - private ConfigVals readAgentOptions(final List argList) { - AgentOptions options = new AgentOptions(argList, false); - return options.getConfigVals(); - } -} \ No newline at end of file diff --git a/src/test/java/io/prometheus/OptionsTest.kt b/src/test/java/io/prometheus/OptionsTest.kt new file mode 100644 index 00000000..d77a5fa4 --- /dev/null +++ b/src/test/java/io/prometheus/OptionsTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.agent.AgentOptions +import io.prometheus.common.ConfigVals +import io.prometheus.proxy.ProxyOptions +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class OptionsTest { + + @Test + fun verifyDefaultValues() { + val configVals = readProxyOptions(listOf()) + assertThat(configVals!!.proxy.http.port).isEqualTo(8080) + assertThat(configVals.proxy.internal.zipkin.enabled).isEqualTo(false) + } + + @Test + fun verifyConfValues() { + val configVals = readProxyOptions(listOf("--config", CONFIG)) + assertThat(configVals!!.proxy.http.port).isEqualTo(8181) + assertThat(configVals.proxy.internal.zipkin.enabled).isEqualTo(true) + } + + @Test + fun verifyUnquotedPropValue() { + val configVals = readProxyOptions(listOf("-Dproxy.http.port=9393", "-Dproxy.internal.zipkin.enabled=true")) + assertThat(configVals!!.proxy.http.port).isEqualTo(9393) + assertThat(configVals.proxy.internal.zipkin.enabled).isEqualTo(true) + } + + @Test + fun verifyQuotedPropValue() { + val configVals = readProxyOptions(listOf("-D\"proxy.http.port=9394\"")) + assertThat(configVals!!.proxy.http.port).isEqualTo(9394) + } + + @Test + fun verifyPathConfigs() { + val configVals = readAgentOptions(listOf("--config", CONFIG)) + assertThat(configVals!!.agent.pathConfigs.size).isEqualTo(3) + } + + + @Test + fun verifyProxyDefaults() { + val options = ProxyOptions(listOf()) + + assertThat(options.proxyPort).isEqualTo(8080) + assertThat(options.agentPort).isEqualTo(50051) + } + + @Test + fun verifyAgentDefaults() { + val options = AgentOptions(listOf("--name", "test-name", "--proxy", "host5"), + false) + + assertThat(options.metricsEnabled).isEqualTo(false) + assertThat(options.dynamicParams.size).isEqualTo(0) + assertThat(options.agentName).isEqualTo("test-name") + assertThat(options.proxyHostname).isEqualTo("host5") + } + + private fun readProxyOptions(argList: List): ConfigVals? { + val options = ProxyOptions(argList) + return options.configVals + } + + private fun readAgentOptions(argList: List): ConfigVals? { + val options = AgentOptions(argList, false) + return options.configVals + } + + companion object { + + private val CONFIG = "https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/etc/test-configs/junit-test.conf" + } +} \ No newline at end of file diff --git a/src/test/java/io/prometheus/TestConstants.java b/src/test/java/io/prometheus/TestConstants.java deleted file mode 100644 index 99d4801a..00000000 --- a/src/test/java/io/prometheus/TestConstants.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import com.google.common.collect.Lists; -import okhttp3.OkHttpClient; - -import java.util.List; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class TestConstants { - static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool(); - static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient(); - static final Random RANDOM = new Random(); - static final int REPS = 1000; - static final int PROXY_PORT = 9500; - static final List args = Lists.newArrayList("--config", "https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/etc/test-configs/travis.conf"); - - private TestConstants() {} -} \ No newline at end of file diff --git a/src/test/java/io/prometheus/TestConstants.kt b/src/test/java/io/prometheus/TestConstants.kt new file mode 100644 index 00000000..0cf8ad67 --- /dev/null +++ b/src/test/java/io/prometheus/TestConstants.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import okhttp3.OkHttpClient +import java.util.* +import java.util.concurrent.Executors + +object TestConstants { + internal val EXECUTOR_SERVICE = Executors.newCachedThreadPool() + internal val OK_HTTP_CLIENT = OkHttpClient() + internal val RANDOM = Random() + internal val REPS = 1000 + internal val PROXY_PORT = 9500 + internal val args = listOf("--config", "https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/etc/test-configs/travis.conf") +} \ No newline at end of file diff --git a/src/test/java/io/prometheus/TestUtils.java b/src/test/java/io/prometheus/TestUtils.java deleted file mode 100644 index 5d4085e4..00000000 --- a/src/test/java/io/prometheus/TestUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import io.prometheus.agent.AgentOptions; -import io.prometheus.common.Utils; -import io.prometheus.proxy.ProxyOptions; -import org.assertj.core.util.Lists; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static java.lang.String.format; - -public class TestUtils { - - private static final Logger logger = LoggerFactory.getLogger(TestUtils.class); - - public static Proxy startProxy(String serverName, boolean adminEnabled, boolean metricsEnabled, List argv) - throws IOException, TimeoutException { - - final List args = Lists.newArrayList(TestConstants.args); - args.addAll(argv); - args.add(format("-Dproxy.admin.enabled=%s", adminEnabled)); - args.add(format("-Dproxy.metrics.enabled=%s", metricsEnabled)); - ProxyOptions options = new ProxyOptions(args); - - logger.info(Utils.getBanner("banners/proxy.txt")); - logger.info(Utils.getVersionDesc(false)); - - Proxy proxy = new Proxy(options, TestConstants.PROXY_PORT, serverName, true); - proxy.startAsync(); - proxy.awaitRunning(5, TimeUnit.SECONDS); - return proxy; - } - - public static Agent startAgent(String serverName, boolean adminEnabled, boolean metricsEnabled, List argv) - throws IOException, TimeoutException { - - final List args = Lists.newArrayList(TestConstants.args); - args.addAll(argv); - args.add(format("-Dagent.admin.enabled=%s", adminEnabled)); - args.add(format("-Dagent.metrics.enabled=%s", metricsEnabled)); - AgentOptions options = new AgentOptions(args, false); - - logger.info(Utils.getBanner("banners/agent.txt")); - logger.info(Utils.getVersionDesc(false)); - - Agent agent = new Agent(options, serverName, true); - agent.startAsync(); - agent.awaitRunning(5, TimeUnit.SECONDS); - return agent; - } -} diff --git a/src/test/java/io/prometheus/TestUtils.kt b/src/test/java/io/prometheus/TestUtils.kt new file mode 100644 index 00000000..e67d23f5 --- /dev/null +++ b/src/test/java/io/prometheus/TestUtils.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2017, Paul Ambrose All rights reserved. + * + * 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 io.prometheus + +import io.prometheus.agent.AgentOptions +import io.prometheus.common.getBanner +import io.prometheus.common.getVersionDesc +import io.prometheus.proxy.ProxyOptions +import org.slf4j.LoggerFactory +import java.io.IOException +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +object TestUtils { + + private val logger = LoggerFactory.getLogger(TestUtils::class.java) + + @Throws(IOException::class, TimeoutException::class) + fun startProxy(serverName: String?, adminEnabled: Boolean, metricsEnabled: Boolean, argv: List): Proxy { + val args = + mutableListOf().apply { + addAll(TestConstants.args) + addAll(argv) + add("-Dproxy.admin.enabled=$adminEnabled") + add("-Dproxy.metrics.enabled=$metricsEnabled") + } + val options = ProxyOptions(args) + + logger.info(getBanner("banners/proxy.txt")) + logger.info(getVersionDesc(false)) + + val proxy = Proxy(options, TestConstants.PROXY_PORT, serverName, true) + proxy.startAsync() + proxy.awaitRunning(5, TimeUnit.SECONDS) + return proxy + } + + @Throws(IOException::class, TimeoutException::class) + fun startAgent(serverName: String?, adminEnabled: Boolean, metricsEnabled: Boolean, argv: List): Agent { + val args = + mutableListOf().apply { + addAll(TestConstants.args) + addAll(argv) + add("-Dagent.admin.enabled=$adminEnabled") + add("-Dagent.metrics.enabled=$metricsEnabled") + } + val options = AgentOptions(args, false) + + logger.info(getBanner("banners/agent.txt")) + logger.info(getVersionDesc(false)) + + return Agent(options, serverName, true).apply { + startAsync() + awaitRunning(5, TimeUnit.SECONDS) + } + } +} diff --git a/src/test/java/io/prometheus/Tests.java b/src/test/java/io/prometheus/Tests.java deleted file mode 100644 index d17b5058..00000000 --- a/src/test/java/io/prometheus/Tests.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2017, Paul Ambrose All rights reserved. - * - * 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 io.prometheus; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import io.prometheus.agent.RequestFailureException; -import io.prometheus.common.Utils; -import okhttp3.Request; -import okhttp3.Response; -import spark.Service; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.IntStream; - -import static io.prometheus.TestConstants.RANDOM; -import static java.lang.Math.abs; -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; - -public class Tests { - - public static void missingPathTest() - throws Exception { - String url = format("http://localhost:%d/", TestConstants.PROXY_PORT); - Request.Builder request = new Request.Builder().url(url); - try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - } - - public static void invalidPathTest() - throws Exception { - String url = format("http://localhost:%d/invalid_path", TestConstants.PROXY_PORT); - Request.Builder request = new Request.Builder().url(url); - try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - } - - public static void addRemovePathsTest(final Agent agent) - throws Exception { - - // Take into account pre-existing paths already registered - int originalSize = agent.pathMapSize(); - - int cnt = 0; - for (int i = 0; i < TestConstants.REPS; i++) { - final String path = format("test-%d", i); - agent.registerPath(path, format("http://localhost:%d/%s", TestConstants.PROXY_PORT, path)); - cnt++; - assertThat(agent.pathMapSize()).isEqualTo(originalSize + cnt); - agent.unregisterPath(path); - cnt--; - assertThat(agent.pathMapSize()).isEqualTo(originalSize + cnt); - } - } - - public static void threadedAddRemovePathsTest(final Agent agent) - throws Exception { - final List paths = Lists.newArrayList(); - final AtomicInteger cnt = new AtomicInteger(0); - final CountDownLatch latch1 = new CountDownLatch(TestConstants.REPS); - final CountDownLatch latch2 = new CountDownLatch(TestConstants.REPS); - - // Take into account pre-existing paths already registered - int originalSize = agent.pathMapSize(); - - IntStream.range(0, TestConstants.REPS) - .forEach(val -> { - TestConstants.EXECUTOR_SERVICE.submit( - () -> { - final String path = format("test-%d", cnt.getAndIncrement()); - synchronized (paths) { - paths.add(path); - } - try { - final String url = format("http://localhost:%d/%s", TestConstants.PROXY_PORT, path); - agent.registerPath(path, url); - latch1.countDown(); - } - catch (RequestFailureException e) { - e.printStackTrace(); - } - }); - }); - - assertThat(latch1.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(paths.size()).isEqualTo(TestConstants.REPS); - assertThat(agent.pathMapSize()).isEqualTo(originalSize + TestConstants.REPS); - - paths.forEach( - (path) -> { - TestConstants.EXECUTOR_SERVICE.submit( - () -> { - try { - agent.unregisterPath(path); - latch2.countDown(); - } - catch (RequestFailureException e) { - e.printStackTrace(); - } - }); - }); - - // Wait for all unregistrations to complete - assertThat(latch2.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(agent.pathMapSize()).isEqualTo(originalSize); - } - - public static void invalidAgentUrlTest(final Agent agent) - throws Exception { - final String badPath = "badPath"; - - agent.registerPath(badPath, "http://localhost:33/metrics"); - - String url = format("http://localhost:%d/%s", TestConstants.PROXY_PORT, badPath); - Request.Builder request = new Request.Builder().url(url); - try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - - agent.unregisterPath(badPath); - } - - public static void timeoutTest(final Agent agent) - throws Exception { - int agentPort = 9700; - String proxyPath = "proxy-timeout"; - String agentPath = "agent-timeout"; - - Service http = Service.ignite(); - http.port(agentPort) - .get(format("/%s", agentPath), - (req, res) -> { - res.type("text/plain"); - Utils.sleepForSecs(10); - return "I timed out"; - }); - String agentUrl = format("http://localhost:%d/%s", agentPort, agentPath); - agent.registerPath("/" + proxyPath, agentUrl); - - String proxyUrl = format("http://localhost:%d/%s", TestConstants.PROXY_PORT, proxyPath); - Request.Builder request = new Request.Builder().url(proxyUrl); - try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(404); - } - - agent.unregisterPath("/" + proxyPath); - http.stop(); - } - - public static void proxyCallTest(final Agent agent, - final int httpServerCount, - final int pathCount, - final int queryCount, - final long pauseMillis) - throws Exception { - - final int startingPort = 9600; - final List httpServers = Lists.newArrayList(); - final Map pathMap = Maps.newConcurrentMap(); - - // Take into account pre-existing paths already registered - int originalSize = agent.pathMapSize(); - - // Create the endpoints - IntStream.range(0, httpServerCount) - .forEach(i -> { - Service http = Service.ignite(); - http.port(startingPort + i) - .threadPool(30, 10, 1000) - .get(format("/agent-%d", i), - (req, res) -> { - res.type("text/plain"); - return format("value: %d", i); - }); - httpServers.add(http); - }); - - // Create the paths - for (int i = 0; i < pathCount; i++) { - int index = abs(RANDOM.nextInt()) % httpServers.size(); - String url = format("http://localhost:%d/agent-%d", startingPort + index, index); - agent.registerPath(format("proxy-%d", i), url); - pathMap.put(i, index); - } - - assertThat(agent.pathMapSize()).isEqualTo(originalSize + pathCount); - - // Call the proxy sequentially - for (int i = 0; i < queryCount; i++) { - callProxy(pathMap); - Utils.sleepForMillis(pauseMillis); - } - - // Call the proxy in parallel - int threadedQueryCount = 100; - final CountDownLatch latch = new CountDownLatch(threadedQueryCount); - IntStream.range(0, threadedQueryCount) - .forEach( - i -> { - TestConstants.EXECUTOR_SERVICE.submit( - () -> { - try { - callProxy(pathMap); - latch.countDown(); - } - catch (Exception e) { - e.printStackTrace(); - } - }); - }); - - assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); - - final AtomicInteger errorCnt = new AtomicInteger(); - pathMap.forEach( - (k, v) -> { - try { - agent.unregisterPath(format("proxy-%d", k)); - } - catch (RequestFailureException e) { - errorCnt.incrementAndGet(); - } - }); - - assertThat(errorCnt.get()).isEqualTo(0); - assertThat(agent.pathMapSize()).isEqualTo(originalSize); - - httpServers.forEach(Service::stop); - } - - private static void callProxy(final Map pathMap) - throws Exception { - // Choose one of the pathMap values - int index = abs(RANDOM.nextInt() % pathMap.size()); - int httpVal = pathMap.get(index); - String url = format("http://localhost:%d/proxy-%d", TestConstants.PROXY_PORT, index); - Request.Builder request = new Request.Builder().url(url); - try (Response response = TestConstants.OK_HTTP_CLIENT.newCall(request.build()).execute()) { - assertThat(response.code()).isEqualTo(200); - String body = response.body().string(); - assertThat(body).isEqualTo(format("value: %d", httpVal)); - } - } -}