diff --git a/Makefile b/Makefile index 66796987..326de093 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,14 @@ -VERSION=1.6.4 +VERSION=1.7.0 default: compile clean: ./gradlew clean -compile: +stubs: + ./gradlew generateProto + +compile: stubs ./gradlew build -xtest build: compile diff --git a/README.md b/README.md index 31f4b8d8..9cd0b3b6 100644 --- a/README.md +++ b/README.md @@ -97,8 +97,8 @@ scrape_configs: The docker images are available via: ```bash -docker pull pambrose/prometheus-proxy:1.6.4 -docker pull pambrose/prometheus-agent:1.6.4 +docker pull pambrose/prometheus-proxy:1.7.0 +docker pull pambrose/prometheus-agent:1.7.0 ``` Start a proxy container with: @@ -107,7 +107,7 @@ Start a proxy container with: docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ --env ADMIN_ENABLED=true \ --env METRICS_ENABLED=true \ - pambrose/prometheus-proxy:1.6.4 + pambrose/prometheus-proxy:1.7.0 ``` Start an agent container with: @@ -115,7 +115,7 @@ Start an agent container with: ```bash docker run --rm -p 8083:8083 -p 8093:8093 \ --env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-agent:1.6.4 + pambrose/prometheus-agent:1.7.0 ``` Using the config file [simple.conf](https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf), @@ -131,7 +131,7 @@ is in your current directory, run an agent container with: docker run --rm -p 8083:8083 -p 8093:8093 \ --mount type=bind,source="$(pwd)"/prom-agent.conf,target=/app/prom-agent.conf \ --env AGENT_CONFIG=prom-agent.conf \ - pambrose/prometheus-agent:1.6.4 + pambrose/prometheus-agent:1.7.0 ``` **Note:** The `WORKDIR` of the proxy and agent images is `/app`, so make sure @@ -226,11 +226,11 @@ The gRPC docs describe [how to setup TLS](https://github.com/grpc/grpc-java/tree The [repo](https://github.com/pambrose/prometheus-proxy/tree/master/testing/certs) includes the certificates and keys necessary to test TLS support. -These settings are required to run TLS without mutual authentication: +Running TLS without mutual authentication requires these settingss: * certChainFilePath and privateKeyFilePath on the proxy * trustCertCollectionFilePath on the agent -These settings are required to run TLS with mutual authentication: +Running TLS with mutual authentication requires these settingss: * certChainFilePath, privateKeyFilePath and trustCertCollectionFilePath on the proxy * certChainFilePath, privateKeyFilePath and trustCertCollectionFilePath on the agent @@ -250,7 +250,7 @@ docker run --rm -p 8082:8082 -p 8092:8092 -p 50440:50440 -p 8080:8080 \ --env PROXY_CONFIG=tls-no-mutual-auth.conf \ --env ADMIN_ENABLED=true \ --env METRICS_ENABLED=true \ - pambrose/prometheus-proxy:1.6.4 + pambrose/prometheus-proxy:1.7.0 docker run --rm -p 8083:8083 -p 8093:8093 \ --mount type=bind,source="$(pwd)"/testing/certs,target=/app/testing/certs \ @@ -258,7 +258,7 @@ docker run --rm -p 8083:8083 -p 8093:8093 \ --env AGENT_CONFIG=tls-no-mutual-auth.conf \ --env PROXY_HOSTNAME=mymachine.lan:50440 \ --name docker-agent \ - pambrose/prometheus-agent:1.6.4 + pambrose/prometheus-agent:1.7.0 ``` **Note:** The `WORKDIR` of the proxy and agent images is `/app`, so make sure diff --git a/bin/docker-agent.sh b/bin/docker-agent.sh index 7131b204..62cfd906 100755 --- a/bin/docker-agent.sh +++ b/bin/docker-agent.sh @@ -3,4 +3,4 @@ docker run --rm -p 8083:8083 -p 8093:8093 \ --env AGENT_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ --env PROXY_HOSTNAME=mymachine.lan \ - pambrose/prometheus-agent:1.6.4 + pambrose/prometheus-agent:1.7.0 diff --git a/bin/docker-proxy.sh b/bin/docker-proxy.sh index 46ab3528..b3064cb3 100755 --- a/bin/docker-proxy.sh +++ b/bin/docker-proxy.sh @@ -2,4 +2,4 @@ docker run --rm -p 8082:8082 -p 8092:8092 -p 50051:50051 -p 8080:8080 \ --env PROXY_CONFIG='https://raw.githubusercontent.com/pambrose/prometheus-proxy/master/examples/simple.conf' \ - pambrose/prometheus-proxy:1.6.4 + pambrose/prometheus-proxy:1.7.0 diff --git a/build.gradle b/build.gradle index 71f3e289..0bdd55c5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,17 @@ plugins { id 'idea' id 'java' - id 'org.jetbrains.kotlin.jvm' version '1.3.71' - id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.71' + id 'org.jetbrains.kotlin.jvm' version '1.3.72' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.72' id 'com.google.protobuf' version '0.8.12' id "com.github.ben-manes.versions" version '0.28.0' - id 'com.github.johnrengelman.shadow' version '5.2.0' + id 'com.github.johnrengelman.shadow' version '6.0.0' id 'jacoco' id 'com.github.kt3k.coveralls' version '2.10.1' } group = 'io.prometheus' -version = '1.6.4' +version = '1.7.0' sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -27,66 +27,117 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-reflect" - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${serialization_version}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutines_version}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${coroutines_version}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:${coroutines_version}" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:$coroutines_version" - implementation "io.grpc:grpc-all:${grpc_version}" + //implementation "io.grpc:grpc-all:$grpc_version" + implementation "io.grpc:grpc-netty-shaded:${grpc_version}" + implementation "io.grpc:grpc-protobuf:${grpc_version}" + implementation "io.grpc:grpc-stub:${grpc_version}" + implementation "io.grpc:grpc-services:${grpc_version}" - implementation "com.github.pambrose.common-utils:core-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:corex-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:dropwizard-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:guava-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:grpc-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:jetty-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:ktor-client-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:prometheus-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:service-utils:${utils_version}" - implementation "com.github.pambrose.common-utils:zipkin-utils:${utils_version}" + implementation "io.grpc:grpc-kotlin-stub:${gengrpc_version}" - implementation "org.eclipse.jetty:jetty-servlet:${jetty_version}" + implementation "com.github.pambrose.common-utils:core-utils:$utils_version" + implementation "com.github.pambrose.common-utils:corex-utils:$utils_version" + implementation "com.github.pambrose.common-utils:dropwizard-utils:$utils_version" + implementation "com.github.pambrose.common-utils:guava-utils:$utils_version" + implementation "com.github.pambrose.common-utils:grpc-utils:$utils_version" + implementation "com.github.pambrose.common-utils:jetty-utils:$utils_version" + implementation "com.github.pambrose.common-utils:ktor-client-utils:$utils_version" + implementation "com.github.pambrose.common-utils:prometheus-utils:$utils_version" + implementation "com.github.pambrose.common-utils:service-utils:$utils_version" + implementation "com.github.pambrose.common-utils:zipkin-utils:$utils_version" - implementation "javax.annotation:javax.annotation-api:${annotation_version}" - implementation "com.beust:jcommander:${jcommander_version}" - implementation "com.typesafe:config:${typesafe_version}" + implementation "org.eclipse.jetty:jetty-servlet:$jetty_version" - implementation "io.prometheus:simpleclient:${prometheus_version}" + implementation "javax.annotation:javax.annotation-api:$annotation_version" + implementation "com.beust:jcommander:$jcommander_version" + implementation "com.typesafe:config:$typesafe_version" - implementation "io.ktor:ktor-server-core:${ktor_version}" - implementation "io.ktor:ktor-server-cio:${ktor_version}" - implementation "io.ktor:ktor-client-core:${ktor_version}" - implementation "io.ktor:ktor-client-cio:${ktor_version}" + implementation "io.prometheus:simpleclient:$prometheus_version" - implementation "io.dropwizard.metrics:metrics-healthchecks:${dropwizard_version}" + implementation "io.ktor:ktor-server-core:$ktor_version" + implementation "io.ktor:ktor-server-cio:$ktor_version" + implementation "io.ktor:ktor-client-core:$ktor_version" + implementation "io.ktor:ktor-client-cio:$ktor_version" - implementation "io.zipkin.brave:brave-instrumentation-grpc:${zipkin_version}" + implementation "io.dropwizard.metrics:metrics-healthchecks:$dropwizard_version" - implementation "ch.qos.logback:logback-classic:${logback_version}" - implementation "org.slf4j:jul-to-slf4j:${slf4j_version}" - implementation "io.github.microutils:kotlin-logging:${logging_version}" + implementation "io.zipkin.brave:brave-instrumentation-grpc:$zipkin_version" - testImplementation "org.amshove.kluent:kluent:${kluent_version}" + implementation "io.github.microutils:kotlin-logging:$logging_version" + implementation "ch.qos.logback:logback-classic:$logback_version" + implementation "org.slf4j:jul-to-slf4j:$slf4j_version" + + testImplementation "org.amshove.kluent:kluent:$kluent_version" testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_version" } +/* +sourceSets { + main.java.srcDirs += ['src/main/java'] + main.kotlin.srcDirs += ['src/main/kotlin'] + test.java.srcDirs += ['src/test/java'] + test.kotlin.srcDirs += ['src/test/kotlin'] + main.resources.srcDirs += ['src/main/resources'] + test.resources.srcDirs += ['src/main/testresources'] +} +*/ + compileKotlin.dependsOn ':generateProto' protobuf { - protoc { - artifact = "com.google.protobuf:protoc:${protoc_version}" - } + protoc { artifact = "com.google.protobuf:protoc:$protoc_version" } plugins { - grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpc_version}" } + grpc { artifact = "io.grpc:protoc-gen-grpc-java:$grpc_version" } + + // Specify protoc to generate using our grpc kotlin plugin + grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:${gengrpc_version}" } } generateProtoTasks { - all()*.plugins { - grpc {} + all().each { task -> + task.plugins { + // Generate Java gRPC classes + grpc { } + // Generate Kotlin gRPC using the custom plugin from library + grpckt { } + } } } } +compileKotlin.dependsOn ':generateProto' + +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs += ['-Xuse-experimental=kotlin.time.ExperimentalTime', + '-Xuse-experimental=kotlinx.serialization.UnstableDefault', + '-Xuse-experimental=kotlin.ExperimentalUnsignedTypes', + '-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi', + '-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi', + '-Xuse-experimental=io.ktor.util.KtorExperimentalAPI', + '-Xuse-experimental=kotlinx.serialization.UnstableDefault'] + } +} + +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs += ['-Xuse-experimental=kotlin.time.ExperimentalTime', + '-Xuse-experimental=kotlinx.serialization.UnstableDefault', + '-Xuse-experimental=kotlin.ExperimentalUnsignedTypes', + '-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi', + '-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi', + '-Xuse-experimental=io.ktor.util.KtorExperimentalAPI', + '-Xuse-experimental=kotlinx.serialization.UnstableDefault'] + } +} + configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } @@ -131,32 +182,6 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs += ['-Xuse-experimental=kotlin.time.ExperimentalTime', - '-Xuse-experimental=kotlinx.serialization.UnstableDefault', - '-Xuse-experimental=kotlin.ExperimentalUnsignedTypes', - '-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi', - '-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi', - '-Xuse-experimental=io.ktor.util.KtorExperimentalAPI', - '-Xuse-experimental=kotlinx.serialization.UnstableDefault'] - } -} - -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs += ['-Xuse-experimental=kotlin.time.ExperimentalTime', - '-Xuse-experimental=kotlinx.serialization.UnstableDefault', - '-Xuse-experimental=kotlin.ExperimentalUnsignedTypes', - '-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi', - '-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi', - '-Xuse-experimental=io.ktor.util.KtorExperimentalAPI', - '-Xuse-experimental=kotlinx.serialization.UnstableDefault'] - } -} - // Required for multiple uberjar targets shadowJar { mergeServiceFiles() diff --git a/etc/compose/proxy.yml b/etc/compose/proxy.yml index 39e3aa19..3064469d 100644 --- a/etc/compose/proxy.yml +++ b/etc/compose/proxy.yml @@ -1,6 +1,6 @@ prometheus-proxy: autoredeploy: true - image: 'pambrose/prometheus-proxy:1.6.4' + image: 'pambrose/prometheus-proxy:1.7.0' ports: - '8080:8080' - '8082:8082' diff --git a/etc/config/config.conf b/etc/config/config.conf index f61f6adf..6741bd2d 100644 --- a/etc/config/config.conf +++ b/etc/config/config.conf @@ -50,8 +50,8 @@ proxy { maxAgentInactivitySecs = 15 // Seconds of inactivity before agent is evicted staleAgentCheckPauseSecs = 10 // Pause interval for agent cleanup - scrapeRequestTimeoutSecs = 5 - scrapeRequestCheckMillis = 500 + scrapeRequestTimeoutSecs = 5 // Timeout for scrape requests + scrapeRequestCheckMillis = 500 // Pause time between checks for scrape request timeout scrapeRequestBacklogUnhealthySize = 25 // Threshold for returning an unhealthy healthcheck scrapeRequestMapUnhealthySize = 25 // Threshold for returning an unhealthy healthcheck diff --git a/gradle.properties b/gradle.properties index 3a414e8a..9f60dd09 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,18 +1,26 @@ +kotlin.code.style=official +kotlin.incremental=true +org.gradle.daemon=true +org.gradle.configureondemand=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 annotation_version=1.3.2 -coroutines_version=1.3.4 -dropwizard_version=4.1.5 -grpc_version=1.28.1 +coroutines_version=1.3.6 +dropwizard_version=4.1.9 +gengrpc_version=0.1.3 +grpc_version=1.30.1 jcommander_version=1.78 jetty_version=9.4.22.v20191022 junit_version=5.6.1 kluent_version=1.61 ktor_version=1.3.2 logback_version=1.2.3 -logging_version=1.7.9 -prometheus_version=0.8.1 -protoc_version=3.11.4 +logging_version=1.8.0.1 +prometheus_version=0.9.0 +protoc_version=3.12.3 serialization_version=0.20.0-1.3.70-eap-274-2 slf4j_version=1.7.28 typesafe_version=1.4.0 -utils_version=b107c71 -zipkin_version=5.11.2 +utils_version=cca9a6d +zipkin_version=5.12.3 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f3d88b1c..62d4c053 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b916d573..bb8b2fc2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Mar 25 12:27:26 PDT 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7d..fbd7c515 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 62bd9b9c..5093609d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% diff --git a/src/main/java/io/prometheus/common/ConfigVals.java b/src/main/java/io/prometheus/common/ConfigVals.java index 500fc1e4..f9f2befa 100644 --- a/src/main/java/io/prometheus/common/ConfigVals.java +++ b/src/main/java/io/prometheus/common/ConfigVals.java @@ -1,389 +1,372 @@ -/* - * Copyright © 2020 Paul Ambrose (pambrose@mac.com) - * - * 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.9.95 on Wed Dec 18 21:18:09 PST 2019 +// generated by tscfg 0.9.95 on Fri May 08 22:56:40 PDT 2020 // source: etc/config/config.conf package io.prometheus.common; public class ConfigVals { - public final ConfigVals.Agent agent; - public final ConfigVals.Proxy2 proxy; - - public ConfigVals(com.typesafe.config.Config c) { - final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator(); - final java.lang.String parentPath = ""; - this.agent = c.hasPathOrNull("agent") ? new ConfigVals.Agent(c.getConfig("agent"), parentPath + "agent.", $tsCfgValidator) : new ConfigVals.Agent(com.typesafe.config.ConfigFactory.parseString("agent{}"), parentPath + "agent.", $tsCfgValidator); - this.proxy = c.hasPathOrNull("proxy") ? new ConfigVals.Proxy2(c.getConfig("proxy"), parentPath + "proxy.", $tsCfgValidator) : new ConfigVals.Proxy2(com.typesafe.config.ConfigFactory.parseString("proxy{}"), parentPath + "proxy.", $tsCfgValidator); - $tsCfgValidator.validate(); - } - - public static class Agent { - public final Agent.Admin admin; - public final int chunkContentSizeKbs; - public final Agent.Internal internal; - public final Agent.Metrics metrics; - public final int minGzipSizeBytes; - public final java.lang.String name; - public final java.util.List pathConfigs; - public final Agent.Proxy proxy; - public final Agent.Tls tls; - - public Agent(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.admin = c.hasPathOrNull("admin") ? new Agent.Admin(c.getConfig("admin"), parentPath + "admin.", $tsCfgValidator) : new Agent.Admin(com.typesafe.config.ConfigFactory.parseString("admin{}"), parentPath + "admin.", $tsCfgValidator); - this.chunkContentSizeKbs = c.hasPathOrNull("chunkContentSizeKbs") ? c.getInt("chunkContentSizeKbs") : 32; - this.internal = c.hasPathOrNull("internal") ? new Agent.Internal(c.getConfig("internal"), parentPath + "internal.", $tsCfgValidator) : new Agent.Internal(com.typesafe.config.ConfigFactory.parseString("internal{}"), parentPath + "internal.", $tsCfgValidator); - this.metrics = c.hasPathOrNull("metrics") ? new Agent.Metrics(c.getConfig("metrics"), parentPath + "metrics.", $tsCfgValidator) : new Agent.Metrics(com.typesafe.config.ConfigFactory.parseString("metrics{}"), parentPath + "metrics.", $tsCfgValidator); - this.minGzipSizeBytes = c.hasPathOrNull("minGzipSizeBytes") ? c.getInt("minGzipSizeBytes") : 512; - this.name = c.hasPathOrNull("name") ? c.getString("name") : ""; - this.pathConfigs = $_LAgent_PathConfigs$Elm(c.getList("pathConfigs"), parentPath, $tsCfgValidator); - this.proxy = c.hasPathOrNull("proxy") ? new Agent.Proxy(c.getConfig("proxy"), parentPath + "proxy.", $tsCfgValidator) : new Agent.Proxy(com.typesafe.config.ConfigFactory.parseString("proxy{}"), parentPath + "proxy.", $tsCfgValidator); - this.tls = c.hasPathOrNull("tls") ? new Agent.Tls(c.getConfig("tls"), parentPath + "tls.", $tsCfgValidator) : new Agent.Tls(com.typesafe.config.ConfigFactory.parseString("tls{}"), parentPath + "tls.", $tsCfgValidator); + public final ConfigVals.Agent agent; + public final ConfigVals.Proxy2 proxy; + + public ConfigVals(com.typesafe.config.Config c) { + final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator(); + final java.lang.String parentPath = ""; + this.agent = c.hasPathOrNull("agent") ? new ConfigVals.Agent(c.getConfig("agent"), parentPath + "agent.", $tsCfgValidator) : new ConfigVals.Agent(com.typesafe.config.ConfigFactory.parseString("agent{}"), parentPath + "agent.", $tsCfgValidator); + this.proxy = c.hasPathOrNull("proxy") ? new ConfigVals.Proxy2(c.getConfig("proxy"), parentPath + "proxy.", $tsCfgValidator) : new ConfigVals.Proxy2(com.typesafe.config.ConfigFactory.parseString("proxy{}"), parentPath + "proxy.", $tsCfgValidator); + $tsCfgValidator.validate(); } - private static java.util.List $_LAgent_PathConfigs$Elm(com.typesafe.config.ConfigList cl, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - java.util.ArrayList al = new java.util.ArrayList<>(); - for (com.typesafe.config.ConfigValue cv : cl) { - al.add(new Agent.PathConfigs$Elm(((com.typesafe.config.ConfigObject) cv).toConfig(), parentPath, $tsCfgValidator)); - } - return java.util.Collections.unmodifiableList(al); - } - - public static class Admin { - public final boolean debugEnabled; - public final boolean enabled; - public final java.lang.String healthCheckPath; - public final java.lang.String pingPath; - public final int port; - public final java.lang.String threadDumpPath; - public final java.lang.String versionPath; - - public Admin(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.debugEnabled = c.hasPathOrNull("debugEnabled") && c.getBoolean("debugEnabled"); - this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); - this.healthCheckPath = c.hasPathOrNull("healthCheckPath") ? c.getString("healthCheckPath") : "healthcheck"; - this.pingPath = c.hasPathOrNull("pingPath") ? c.getString("pingPath") : "ping"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8093; - this.threadDumpPath = c.hasPathOrNull("threadDumpPath") ? c.getString("threadDumpPath") : "threaddump"; - this.versionPath = c.hasPathOrNull("versionPath") ? c.getString("versionPath") : "version"; - } - } + public static class Agent { + public final Agent.Admin admin; + public final int chunkContentSizeKbs; + public final Agent.Internal internal; + public final Agent.Metrics metrics; + public final int minGzipSizeBytes; + public final java.lang.String name; + public final java.util.List pathConfigs; + public final Agent.Proxy proxy; + public final Agent.Tls tls; + + public Agent(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.admin = c.hasPathOrNull("admin") ? new Agent.Admin(c.getConfig("admin"), parentPath + "admin.", $tsCfgValidator) : new Agent.Admin(com.typesafe.config.ConfigFactory.parseString("admin{}"), parentPath + "admin.", $tsCfgValidator); + this.chunkContentSizeKbs = c.hasPathOrNull("chunkContentSizeKbs") ? c.getInt("chunkContentSizeKbs") : 32; + this.internal = c.hasPathOrNull("internal") ? new Agent.Internal(c.getConfig("internal"), parentPath + "internal.", $tsCfgValidator) : new Agent.Internal(com.typesafe.config.ConfigFactory.parseString("internal{}"), parentPath + "internal.", $tsCfgValidator); + this.metrics = c.hasPathOrNull("metrics") ? new Agent.Metrics(c.getConfig("metrics"), parentPath + "metrics.", $tsCfgValidator) : new Agent.Metrics(com.typesafe.config.ConfigFactory.parseString("metrics{}"), parentPath + "metrics.", $tsCfgValidator); + this.minGzipSizeBytes = c.hasPathOrNull("minGzipSizeBytes") ? c.getInt("minGzipSizeBytes") : 512; + this.name = c.hasPathOrNull("name") ? c.getString("name") : ""; + this.pathConfigs = $_LAgent_PathConfigs$Elm(c.getList("pathConfigs"), parentPath, $tsCfgValidator); + this.proxy = c.hasPathOrNull("proxy") ? new Agent.Proxy(c.getConfig("proxy"), parentPath + "proxy.", $tsCfgValidator) : new Agent.Proxy(com.typesafe.config.ConfigFactory.parseString("proxy{}"), parentPath + "proxy.", $tsCfgValidator); + this.tls = c.hasPathOrNull("tls") ? new Agent.Tls(c.getConfig("tls"), parentPath + "tls.", $tsCfgValidator) : new Agent.Tls(com.typesafe.config.ConfigFactory.parseString("tls{}"), parentPath + "tls.", $tsCfgValidator); + } - public static class Internal { - public final int heartbeatCheckPauseMillis; - public final boolean heartbeatEnabled; - public final int heartbeatMaxInactivitySecs; - public final int reconnectPauseSecs; - public final int scrapeRequestBacklogUnhealthySize; - public final Internal.Zipkin zipkin; - - public static class Zipkin { - public final boolean enabled; - public final boolean grpcReportingEnabled; - public final java.lang.String hostname; - public final java.lang.String path; - public final int port; - public final java.lang.String serviceName; - - public Zipkin(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - 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/v2/spans"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 9411; - this.serviceName = c.hasPathOrNull("serviceName") ? c.getString("serviceName") : "prometheus-agent"; + private static java.util.List $_LAgent_PathConfigs$Elm(com.typesafe.config.ConfigList cl, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + java.util.ArrayList al = new java.util.ArrayList<>(); + for (com.typesafe.config.ConfigValue cv : cl) { + al.add(new Agent.PathConfigs$Elm(((com.typesafe.config.ConfigObject) cv).toConfig(), parentPath, $tsCfgValidator)); + } + return java.util.Collections.unmodifiableList(al); } - } - - public Internal(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.heartbeatCheckPauseMillis = c.hasPathOrNull("heartbeatCheckPauseMillis") ? c.getInt("heartbeatCheckPauseMillis") : 500; - this.heartbeatEnabled = !c.hasPathOrNull("heartbeatEnabled") || c.getBoolean("heartbeatEnabled"); - this.heartbeatMaxInactivitySecs = c.hasPathOrNull("heartbeatMaxInactivitySecs") ? c.getInt("heartbeatMaxInactivitySecs") : 5; - this.reconnectPauseSecs = c.hasPathOrNull("reconnectPauseSecs") ? c.getInt("reconnectPauseSecs") : 3; - this.scrapeRequestBacklogUnhealthySize = c.hasPathOrNull("scrapeRequestBacklogUnhealthySize") ? c.getInt("scrapeRequestBacklogUnhealthySize") : 25; - this.zipkin = c.hasPathOrNull("zipkin") ? new Internal.Zipkin(c.getConfig("zipkin"), parentPath + "zipkin.", $tsCfgValidator) : new Internal.Zipkin(com.typesafe.config.ConfigFactory.parseString("zipkin{}"), parentPath + "zipkin.", $tsCfgValidator); - } - } - public static class Metrics { - public final boolean classLoadingExportsEnabled; - public final boolean enabled; - public final boolean garbageCollectorExportsEnabled; - public final Metrics.Grpc grpc; - public final boolean memoryPoolsExportsEnabled; - public final java.lang.String path; - public final int port; - public final boolean standardExportsEnabled; - public final boolean threadExportsEnabled; - public final boolean versionInfoExportsEnabled; - - public static class Grpc { - public final boolean allMetricsReported; - public final boolean metricsEnabled; - - public Grpc(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.allMetricsReported = c.hasPathOrNull("allMetricsReported") && c.getBoolean("allMetricsReported"); - this.metricsEnabled = c.hasPathOrNull("metricsEnabled") && c.getBoolean("metricsEnabled"); + public static class Admin { + public final boolean debugEnabled; + public final boolean enabled; + public final java.lang.String healthCheckPath; + public final java.lang.String pingPath; + public final int port; + public final java.lang.String threadDumpPath; + public final java.lang.String versionPath; + + public Admin(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.debugEnabled = c.hasPathOrNull("debugEnabled") && c.getBoolean("debugEnabled"); + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.healthCheckPath = c.hasPathOrNull("healthCheckPath") ? c.getString("healthCheckPath") : "healthcheck"; + this.pingPath = c.hasPathOrNull("pingPath") ? c.getString("pingPath") : "ping"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8093; + this.threadDumpPath = c.hasPathOrNull("threadDumpPath") ? c.getString("threadDumpPath") : "threaddump"; + this.versionPath = c.hasPathOrNull("versionPath") ? c.getString("versionPath") : "version"; + } } - } - - public Metrics(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.classLoadingExportsEnabled = c.hasPathOrNull("classLoadingExportsEnabled") && c.getBoolean("classLoadingExportsEnabled"); - this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); - this.garbageCollectorExportsEnabled = c.hasPathOrNull("garbageCollectorExportsEnabled") && c.getBoolean("garbageCollectorExportsEnabled"); - this.grpc = c.hasPathOrNull("grpc") ? new Metrics.Grpc(c.getConfig("grpc"), parentPath + "grpc.", $tsCfgValidator) : new Metrics.Grpc(com.typesafe.config.ConfigFactory.parseString("grpc{}"), parentPath + "grpc.", $tsCfgValidator); - this.memoryPoolsExportsEnabled = c.hasPathOrNull("memoryPoolsExportsEnabled") && c.getBoolean("memoryPoolsExportsEnabled"); - this.path = c.hasPathOrNull("path") ? c.getString("path") : "metrics"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8083; - this.standardExportsEnabled = c.hasPathOrNull("standardExportsEnabled") && c.getBoolean("standardExportsEnabled"); - this.threadExportsEnabled = c.hasPathOrNull("threadExportsEnabled") && c.getBoolean("threadExportsEnabled"); - this.versionInfoExportsEnabled = c.hasPathOrNull("versionInfoExportsEnabled") && c.getBoolean("versionInfoExportsEnabled"); - } - } - public static class PathConfigs$Elm { - public final java.lang.String name; - public final java.lang.String path; - public final java.lang.String url; - - public PathConfigs$Elm(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.name = $_reqStr(parentPath, c, "name", $tsCfgValidator); - this.path = $_reqStr(parentPath, c, "path", $tsCfgValidator); - this.url = $_reqStr(parentPath, c, "url", $tsCfgValidator); - } - - private static java.lang.String $_reqStr(java.lang.String parentPath, com.typesafe.config.Config c, java.lang.String path, $TsCfgValidator $tsCfgValidator) { - if (c == null) return null; - try { - return c.getString(path); + public static class Internal { + public final int heartbeatCheckPauseMillis; + public final boolean heartbeatEnabled; + public final int heartbeatMaxInactivitySecs; + public final int reconnectPauseSecs; + public final int scrapeRequestBacklogUnhealthySize; + public final Internal.Zipkin zipkin; + + public Internal(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.heartbeatCheckPauseMillis = c.hasPathOrNull("heartbeatCheckPauseMillis") ? c.getInt("heartbeatCheckPauseMillis") : 500; + this.heartbeatEnabled = !c.hasPathOrNull("heartbeatEnabled") || c.getBoolean("heartbeatEnabled"); + this.heartbeatMaxInactivitySecs = c.hasPathOrNull("heartbeatMaxInactivitySecs") ? c.getInt("heartbeatMaxInactivitySecs") : 5; + this.reconnectPauseSecs = c.hasPathOrNull("reconnectPauseSecs") ? c.getInt("reconnectPauseSecs") : 3; + this.scrapeRequestBacklogUnhealthySize = c.hasPathOrNull("scrapeRequestBacklogUnhealthySize") ? c.getInt("scrapeRequestBacklogUnhealthySize") : 25; + this.zipkin = c.hasPathOrNull("zipkin") ? new Internal.Zipkin(c.getConfig("zipkin"), parentPath + "zipkin.", $tsCfgValidator) : new Internal.Zipkin(com.typesafe.config.ConfigFactory.parseString("zipkin{}"), parentPath + "zipkin.", $tsCfgValidator); + } + + public static class Zipkin { + public final boolean enabled; + public final boolean grpcReportingEnabled; + public final java.lang.String hostname; + public final java.lang.String path; + public final int port; + public final java.lang.String serviceName; + + public Zipkin(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + 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/v2/spans"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 9411; + this.serviceName = c.hasPathOrNull("serviceName") ? c.getString("serviceName") : "prometheus-agent"; + } + } } - catch (com.typesafe.config.ConfigException e) { - $tsCfgValidator.addBadPath(parentPath + path, e); - return null; + + public static class Metrics { + public final boolean classLoadingExportsEnabled; + public final boolean enabled; + public final boolean garbageCollectorExportsEnabled; + public final Metrics.Grpc grpc; + public final boolean memoryPoolsExportsEnabled; + public final java.lang.String path; + public final int port; + public final boolean standardExportsEnabled; + public final boolean threadExportsEnabled; + public final boolean versionInfoExportsEnabled; + + public Metrics(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.classLoadingExportsEnabled = c.hasPathOrNull("classLoadingExportsEnabled") && c.getBoolean("classLoadingExportsEnabled"); + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.garbageCollectorExportsEnabled = c.hasPathOrNull("garbageCollectorExportsEnabled") && c.getBoolean("garbageCollectorExportsEnabled"); + this.grpc = c.hasPathOrNull("grpc") ? new Metrics.Grpc(c.getConfig("grpc"), parentPath + "grpc.", $tsCfgValidator) : new Metrics.Grpc(com.typesafe.config.ConfigFactory.parseString("grpc{}"), parentPath + "grpc.", $tsCfgValidator); + this.memoryPoolsExportsEnabled = c.hasPathOrNull("memoryPoolsExportsEnabled") && c.getBoolean("memoryPoolsExportsEnabled"); + this.path = c.hasPathOrNull("path") ? c.getString("path") : "metrics"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8083; + this.standardExportsEnabled = c.hasPathOrNull("standardExportsEnabled") && c.getBoolean("standardExportsEnabled"); + this.threadExportsEnabled = c.hasPathOrNull("threadExportsEnabled") && c.getBoolean("threadExportsEnabled"); + this.versionInfoExportsEnabled = c.hasPathOrNull("versionInfoExportsEnabled") && c.getBoolean("versionInfoExportsEnabled"); + } + + public static class Grpc { + public final boolean allMetricsReported; + public final boolean metricsEnabled; + + public Grpc(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.allMetricsReported = c.hasPathOrNull("allMetricsReported") && c.getBoolean("allMetricsReported"); + this.metricsEnabled = c.hasPathOrNull("metricsEnabled") && c.getBoolean("metricsEnabled"); + } + } } - } - } + public static class PathConfigs$Elm { + public final java.lang.String name; + public final java.lang.String path; + public final java.lang.String url; + + public PathConfigs$Elm(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.name = $_reqStr(parentPath, c, "name", $tsCfgValidator); + this.path = $_reqStr(parentPath, c, "path", $tsCfgValidator); + this.url = $_reqStr(parentPath, c, "url", $tsCfgValidator); + } + + private static java.lang.String $_reqStr(java.lang.String parentPath, com.typesafe.config.Config c, java.lang.String path, $TsCfgValidator $tsCfgValidator) { + if (c == null) return null; + try { + return c.getString(path); + } catch (com.typesafe.config.ConfigException e) { + $tsCfgValidator.addBadPath(parentPath + path, e); + return null; + } + } - public static class Proxy { - public final java.lang.String hostname; - public final int port; + } - public Proxy(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.hostname = c.hasPathOrNull("hostname") ? c.getString("hostname") : "localhost"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 50051; - } - } + public static class Proxy { + public final java.lang.String hostname; + public final int port; - public static class Tls { - public final java.lang.String certChainFilePath; - public final java.lang.String overrideAuthority; - public final java.lang.String privateKeyFilePath; - public final java.lang.String trustCertCollectionFilePath; - - public Tls(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.certChainFilePath = c.hasPathOrNull("certChainFilePath") ? c.getString("certChainFilePath") : ""; - this.overrideAuthority = c.hasPathOrNull("overrideAuthority") ? c.getString("overrideAuthority") : ""; - this.privateKeyFilePath = c.hasPathOrNull("privateKeyFilePath") ? c.getString("privateKeyFilePath") : ""; - this.trustCertCollectionFilePath = c.hasPathOrNull("trustCertCollectionFilePath") ? c.getString("trustCertCollectionFilePath") : ""; - } - } - } - - public static class Proxy2 { - public final Proxy2.Admin2 admin; - public final Proxy2.Agent2 agent; - public final Proxy2.Http http; - public final Proxy2.Internal2 internal; - public final Proxy2.Metrics2 metrics; - public final Proxy2.Tls2 tls; - - public Proxy2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.admin = c.hasPathOrNull("admin") ? new Proxy2.Admin2(c.getConfig("admin"), parentPath + "admin.", $tsCfgValidator) : new Proxy2.Admin2(com.typesafe.config.ConfigFactory.parseString("admin{}"), parentPath + "admin.", $tsCfgValidator); - this.agent = c.hasPathOrNull("agent") ? new Proxy2.Agent2(c.getConfig("agent"), parentPath + "agent.", $tsCfgValidator) : new Proxy2.Agent2(com.typesafe.config.ConfigFactory.parseString("agent{}"), parentPath + "agent.", $tsCfgValidator); - this.http = c.hasPathOrNull("http") ? new Proxy2.Http(c.getConfig("http"), parentPath + "http.", $tsCfgValidator) : new Proxy2.Http(com.typesafe.config.ConfigFactory.parseString("http{}"), parentPath + "http.", $tsCfgValidator); - this.internal = c.hasPathOrNull("internal") ? new Proxy2.Internal2(c.getConfig("internal"), parentPath + "internal.", $tsCfgValidator) : new Proxy2.Internal2(com.typesafe.config.ConfigFactory.parseString("internal{}"), parentPath + "internal.", $tsCfgValidator); - this.metrics = c.hasPathOrNull("metrics") ? new Proxy2.Metrics2(c.getConfig("metrics"), parentPath + "metrics.", $tsCfgValidator) : new Proxy2.Metrics2(com.typesafe.config.ConfigFactory.parseString("metrics{}"), parentPath + "metrics.", $tsCfgValidator); - this.tls = c.hasPathOrNull("tls") ? new Proxy2.Tls2(c.getConfig("tls"), parentPath + "tls.", $tsCfgValidator) : new Proxy2.Tls2(com.typesafe.config.ConfigFactory.parseString("tls{}"), parentPath + "tls.", $tsCfgValidator); - } + public Proxy(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.hostname = c.hasPathOrNull("hostname") ? c.getString("hostname") : "localhost"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 50051; + } + } - public static class Admin2 { - public final boolean debugEnabled; - public final boolean enabled; - public final java.lang.String healthCheckPath; - public final java.lang.String pingPath; - public final int port; - public final int recentRequestsQueueSize; - public final java.lang.String threadDumpPath; - public final java.lang.String versionPath; - - public Admin2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.debugEnabled = c.hasPathOrNull("debugEnabled") && c.getBoolean("debugEnabled"); - this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); - this.healthCheckPath = c.hasPathOrNull("healthCheckPath") ? c.getString("healthCheckPath") : "healthcheck"; - this.pingPath = c.hasPathOrNull("pingPath") ? c.getString("pingPath") : "ping"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8092; - this.recentRequestsQueueSize = c.hasPathOrNull("recentRequestsQueueSize") ? c.getInt("recentRequestsQueueSize") : 50; - this.threadDumpPath = c.hasPathOrNull("threadDumpPath") ? c.getString("threadDumpPath") : "threaddump"; - this.versionPath = c.hasPathOrNull("versionPath") ? c.getString("versionPath") : "version"; - } + public static class Tls { + public final java.lang.String certChainFilePath; + public final java.lang.String overrideAuthority; + public final java.lang.String privateKeyFilePath; + public final java.lang.String trustCertCollectionFilePath; + + public Tls(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.certChainFilePath = c.hasPathOrNull("certChainFilePath") ? c.getString("certChainFilePath") : ""; + this.overrideAuthority = c.hasPathOrNull("overrideAuthority") ? c.getString("overrideAuthority") : ""; + this.privateKeyFilePath = c.hasPathOrNull("privateKeyFilePath") ? c.getString("privateKeyFilePath") : ""; + this.trustCertCollectionFilePath = c.hasPathOrNull("trustCertCollectionFilePath") ? c.getString("trustCertCollectionFilePath") : ""; + } + } } - public static class Agent2 { - public final int port; + public static class Proxy2 { + public final Proxy2.Admin2 admin; + public final Proxy2.Agent2 agent; + public final Proxy2.Http http; + public final Proxy2.Internal2 internal; + public final Proxy2.Metrics2 metrics; + public final Proxy2.Tls2 tls; + + public Proxy2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.admin = c.hasPathOrNull("admin") ? new Proxy2.Admin2(c.getConfig("admin"), parentPath + "admin.", $tsCfgValidator) : new Proxy2.Admin2(com.typesafe.config.ConfigFactory.parseString("admin{}"), parentPath + "admin.", $tsCfgValidator); + this.agent = c.hasPathOrNull("agent") ? new Proxy2.Agent2(c.getConfig("agent"), parentPath + "agent.", $tsCfgValidator) : new Proxy2.Agent2(com.typesafe.config.ConfigFactory.parseString("agent{}"), parentPath + "agent.", $tsCfgValidator); + this.http = c.hasPathOrNull("http") ? new Proxy2.Http(c.getConfig("http"), parentPath + "http.", $tsCfgValidator) : new Proxy2.Http(com.typesafe.config.ConfigFactory.parseString("http{}"), parentPath + "http.", $tsCfgValidator); + this.internal = c.hasPathOrNull("internal") ? new Proxy2.Internal2(c.getConfig("internal"), parentPath + "internal.", $tsCfgValidator) : new Proxy2.Internal2(com.typesafe.config.ConfigFactory.parseString("internal{}"), parentPath + "internal.", $tsCfgValidator); + this.metrics = c.hasPathOrNull("metrics") ? new Proxy2.Metrics2(c.getConfig("metrics"), parentPath + "metrics.", $tsCfgValidator) : new Proxy2.Metrics2(com.typesafe.config.ConfigFactory.parseString("metrics{}"), parentPath + "metrics.", $tsCfgValidator); + this.tls = c.hasPathOrNull("tls") ? new Proxy2.Tls2(c.getConfig("tls"), parentPath + "tls.", $tsCfgValidator) : new Proxy2.Tls2(com.typesafe.config.ConfigFactory.parseString("tls{}"), parentPath + "tls.", $tsCfgValidator); + } - public Agent2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 50051; - } - } + public static class Admin2 { + public final boolean debugEnabled; + public final boolean enabled; + public final java.lang.String healthCheckPath; + public final java.lang.String pingPath; + public final int port; + public final int recentRequestsQueueSize; + public final java.lang.String threadDumpPath; + public final java.lang.String versionPath; + + public Admin2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.debugEnabled = c.hasPathOrNull("debugEnabled") && c.getBoolean("debugEnabled"); + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.healthCheckPath = c.hasPathOrNull("healthCheckPath") ? c.getString("healthCheckPath") : "healthcheck"; + this.pingPath = c.hasPathOrNull("pingPath") ? c.getString("pingPath") : "ping"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8092; + this.recentRequestsQueueSize = c.hasPathOrNull("recentRequestsQueueSize") ? c.getInt("recentRequestsQueueSize") : 50; + this.threadDumpPath = c.hasPathOrNull("threadDumpPath") ? c.getString("threadDumpPath") : "threaddump"; + this.versionPath = c.hasPathOrNull("versionPath") ? c.getString("versionPath") : "version"; + } + } - public static class Http { - public final int idleTimeoutSecs; - public final int maxThreads; - public final int minThreads; - public final int port; - - public Http(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.idleTimeoutSecs = c.hasPathOrNull("idleTimeoutSecs") ? c.getInt("idleTimeoutSecs") : 45; - this.maxThreads = c.hasPathOrNull("maxThreads") ? c.getInt("maxThreads") : -1; - this.minThreads = c.hasPathOrNull("minThreads") ? c.getInt("minThreads") : -1; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8080; - } - } + public static class Agent2 { + public final int port; - public static class Internal2 { - public final Internal2.Blitz blitz; - public final int chunkContextMapUnhealthySize; - public final int maxAgentInactivitySecs; - public final int scrapeRequestBacklogUnhealthySize; - public final int scrapeRequestCheckMillis; - public final int scrapeRequestMapUnhealthySize; - public final int scrapeRequestTimeoutSecs; - public final boolean staleAgentCheckEnabled; - public final int staleAgentCheckPauseSecs; - public final Internal2.Zipkin2 zipkin; - - public Internal2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.blitz = c.hasPathOrNull("blitz") ? new Internal2.Blitz(c.getConfig("blitz"), parentPath + "blitz.", $tsCfgValidator) : new Internal2.Blitz(com.typesafe.config.ConfigFactory.parseString("blitz{}"), parentPath + "blitz.", $tsCfgValidator); - this.chunkContextMapUnhealthySize = c.hasPathOrNull("chunkContextMapUnhealthySize") ? c.getInt("chunkContextMapUnhealthySize") : 25; - this.maxAgentInactivitySecs = c.hasPathOrNull("maxAgentInactivitySecs") ? c.getInt("maxAgentInactivitySecs") : 15; - this.scrapeRequestBacklogUnhealthySize = c.hasPathOrNull("scrapeRequestBacklogUnhealthySize") ? c.getInt("scrapeRequestBacklogUnhealthySize") : 25; - this.scrapeRequestCheckMillis = c.hasPathOrNull("scrapeRequestCheckMillis") ? c.getInt("scrapeRequestCheckMillis") : 500; - this.scrapeRequestMapUnhealthySize = c.hasPathOrNull("scrapeRequestMapUnhealthySize") ? c.getInt("scrapeRequestMapUnhealthySize") : 25; - this.scrapeRequestTimeoutSecs = c.hasPathOrNull("scrapeRequestTimeoutSecs") ? c.getInt("scrapeRequestTimeoutSecs") : 5; - this.staleAgentCheckEnabled = !c.hasPathOrNull("staleAgentCheckEnabled") || c.getBoolean("staleAgentCheckEnabled"); - this.staleAgentCheckPauseSecs = c.hasPathOrNull("staleAgentCheckPauseSecs") ? c.getInt("staleAgentCheckPauseSecs") : 10; - this.zipkin = c.hasPathOrNull("zipkin") ? new Internal2.Zipkin2(c.getConfig("zipkin"), parentPath + "zipkin.", $tsCfgValidator) : new Internal2.Zipkin2(com.typesafe.config.ConfigFactory.parseString("zipkin{}"), parentPath + "zipkin.", $tsCfgValidator); - } - - public static class Blitz { - public final boolean enabled; - public final java.lang.String path; - - public Blitz(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); - this.path = c.hasPathOrNull("path") ? c.getString("path") : "mu-dd4bffbb-ff2e9926-5a80952c-1c6cb64d.txt"; + public Agent2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 50051; + } } - } - - public static class Zipkin2 { - public final boolean enabled; - public final boolean grpcReportingEnabled; - public final java.lang.String hostname; - public final java.lang.String path; - public final int port; - public final java.lang.String serviceName; - - public Zipkin2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - 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/v2/spans"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 9411; - this.serviceName = c.hasPathOrNull("serviceName") ? c.getString("serviceName") : "prometheus-proxy"; + + public static class Http { + public final int idleTimeoutSecs; + public final int maxThreads; + public final int minThreads; + public final int port; + + public Http(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.idleTimeoutSecs = c.hasPathOrNull("idleTimeoutSecs") ? c.getInt("idleTimeoutSecs") : 45; + this.maxThreads = c.hasPathOrNull("maxThreads") ? c.getInt("maxThreads") : -1; + this.minThreads = c.hasPathOrNull("minThreads") ? c.getInt("minThreads") : -1; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8080; + } } - } - } - public static class Metrics2 { - public final boolean classLoadingExportsEnabled; - public final boolean enabled; - public final boolean garbageCollectorExportsEnabled; - public final Metrics2.Grpc2 grpc; - public final boolean memoryPoolsExportsEnabled; - public final java.lang.String path; - public final int port; - public final boolean standardExportsEnabled; - public final boolean threadExportsEnabled; - public final boolean versionInfoExportsEnabled; - - public static class Grpc2 { - public final boolean allMetricsReported; - public final boolean metricsEnabled; - - public Grpc2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.allMetricsReported = c.hasPathOrNull("allMetricsReported") && c.getBoolean("allMetricsReported"); - this.metricsEnabled = c.hasPathOrNull("metricsEnabled") && c.getBoolean("metricsEnabled"); + public static class Internal2 { + public final Internal2.Blitz blitz; + public final int chunkContextMapUnhealthySize; + public final int maxAgentInactivitySecs; + public final int scrapeRequestBacklogUnhealthySize; + public final int scrapeRequestCheckMillis; + public final int scrapeRequestMapUnhealthySize; + public final int scrapeRequestTimeoutSecs; + public final boolean staleAgentCheckEnabled; + public final int staleAgentCheckPauseSecs; + public final Internal2.Zipkin2 zipkin; + + public Internal2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.blitz = c.hasPathOrNull("blitz") ? new Internal2.Blitz(c.getConfig("blitz"), parentPath + "blitz.", $tsCfgValidator) : new Internal2.Blitz(com.typesafe.config.ConfigFactory.parseString("blitz{}"), parentPath + "blitz.", $tsCfgValidator); + this.chunkContextMapUnhealthySize = c.hasPathOrNull("chunkContextMapUnhealthySize") ? c.getInt("chunkContextMapUnhealthySize") : 25; + this.maxAgentInactivitySecs = c.hasPathOrNull("maxAgentInactivitySecs") ? c.getInt("maxAgentInactivitySecs") : 15; + this.scrapeRequestBacklogUnhealthySize = c.hasPathOrNull("scrapeRequestBacklogUnhealthySize") ? c.getInt("scrapeRequestBacklogUnhealthySize") : 25; + this.scrapeRequestCheckMillis = c.hasPathOrNull("scrapeRequestCheckMillis") ? c.getInt("scrapeRequestCheckMillis") : 500; + this.scrapeRequestMapUnhealthySize = c.hasPathOrNull("scrapeRequestMapUnhealthySize") ? c.getInt("scrapeRequestMapUnhealthySize") : 25; + this.scrapeRequestTimeoutSecs = c.hasPathOrNull("scrapeRequestTimeoutSecs") ? c.getInt("scrapeRequestTimeoutSecs") : 5; + this.staleAgentCheckEnabled = !c.hasPathOrNull("staleAgentCheckEnabled") || c.getBoolean("staleAgentCheckEnabled"); + this.staleAgentCheckPauseSecs = c.hasPathOrNull("staleAgentCheckPauseSecs") ? c.getInt("staleAgentCheckPauseSecs") : 10; + this.zipkin = c.hasPathOrNull("zipkin") ? new Internal2.Zipkin2(c.getConfig("zipkin"), parentPath + "zipkin.", $tsCfgValidator) : new Internal2.Zipkin2(com.typesafe.config.ConfigFactory.parseString("zipkin{}"), parentPath + "zipkin.", $tsCfgValidator); + } + + public static class Blitz { + public final boolean enabled; + public final java.lang.String path; + + public Blitz(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.path = c.hasPathOrNull("path") ? c.getString("path") : "mu-dd4bffbb-ff2e9926-5a80952c-1c6cb64d.txt"; + } + } + + public static class Zipkin2 { + public final boolean enabled; + public final boolean grpcReportingEnabled; + public final java.lang.String hostname; + public final java.lang.String path; + public final int port; + public final java.lang.String serviceName; + + public Zipkin2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + 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/v2/spans"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 9411; + this.serviceName = c.hasPathOrNull("serviceName") ? c.getString("serviceName") : "prometheus-proxy"; + } + } + } + + public static class Metrics2 { + public final boolean classLoadingExportsEnabled; + public final boolean enabled; + public final boolean garbageCollectorExportsEnabled; + public final Metrics2.Grpc2 grpc; + public final boolean memoryPoolsExportsEnabled; + public final java.lang.String path; + public final int port; + public final boolean standardExportsEnabled; + public final boolean threadExportsEnabled; + public final boolean versionInfoExportsEnabled; + + public Metrics2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.classLoadingExportsEnabled = c.hasPathOrNull("classLoadingExportsEnabled") && c.getBoolean("classLoadingExportsEnabled"); + this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); + this.garbageCollectorExportsEnabled = c.hasPathOrNull("garbageCollectorExportsEnabled") && c.getBoolean("garbageCollectorExportsEnabled"); + this.grpc = c.hasPathOrNull("grpc") ? new Metrics2.Grpc2(c.getConfig("grpc"), parentPath + "grpc.", $tsCfgValidator) : new Metrics2.Grpc2(com.typesafe.config.ConfigFactory.parseString("grpc{}"), parentPath + "grpc.", $tsCfgValidator); + this.memoryPoolsExportsEnabled = c.hasPathOrNull("memoryPoolsExportsEnabled") && c.getBoolean("memoryPoolsExportsEnabled"); + this.path = c.hasPathOrNull("path") ? c.getString("path") : "metrics"; + this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8082; + this.standardExportsEnabled = c.hasPathOrNull("standardExportsEnabled") && c.getBoolean("standardExportsEnabled"); + this.threadExportsEnabled = c.hasPathOrNull("threadExportsEnabled") && c.getBoolean("threadExportsEnabled"); + this.versionInfoExportsEnabled = c.hasPathOrNull("versionInfoExportsEnabled") && c.getBoolean("versionInfoExportsEnabled"); + } + + public static class Grpc2 { + public final boolean allMetricsReported; + public final boolean metricsEnabled; + + public Grpc2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.allMetricsReported = c.hasPathOrNull("allMetricsReported") && c.getBoolean("allMetricsReported"); + this.metricsEnabled = c.hasPathOrNull("metricsEnabled") && c.getBoolean("metricsEnabled"); + } + } } - } - - public Metrics2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.classLoadingExportsEnabled = c.hasPathOrNull("classLoadingExportsEnabled") && c.getBoolean("classLoadingExportsEnabled"); - this.enabled = c.hasPathOrNull("enabled") && c.getBoolean("enabled"); - this.garbageCollectorExportsEnabled = c.hasPathOrNull("garbageCollectorExportsEnabled") && c.getBoolean("garbageCollectorExportsEnabled"); - this.grpc = c.hasPathOrNull("grpc") ? new Metrics2.Grpc2(c.getConfig("grpc"), parentPath + "grpc.", $tsCfgValidator) : new Metrics2.Grpc2(com.typesafe.config.ConfigFactory.parseString("grpc{}"), parentPath + "grpc.", $tsCfgValidator); - this.memoryPoolsExportsEnabled = c.hasPathOrNull("memoryPoolsExportsEnabled") && c.getBoolean("memoryPoolsExportsEnabled"); - this.path = c.hasPathOrNull("path") ? c.getString("path") : "metrics"; - this.port = c.hasPathOrNull("port") ? c.getInt("port") : 8082; - this.standardExportsEnabled = c.hasPathOrNull("standardExportsEnabled") && c.getBoolean("standardExportsEnabled"); - this.threadExportsEnabled = c.hasPathOrNull("threadExportsEnabled") && c.getBoolean("threadExportsEnabled"); - this.versionInfoExportsEnabled = c.hasPathOrNull("versionInfoExportsEnabled") && c.getBoolean("versionInfoExportsEnabled"); - } - } - public static class Tls2 { - public final java.lang.String certChainFilePath; - public final java.lang.String privateKeyFilePath; - public final java.lang.String trustCertCollectionFilePath; + public static class Tls2 { + public final java.lang.String certChainFilePath; + public final java.lang.String privateKeyFilePath; + public final java.lang.String trustCertCollectionFilePath; - public Tls2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { - this.certChainFilePath = c.hasPathOrNull("certChainFilePath") ? c.getString("certChainFilePath") : ""; - this.privateKeyFilePath = c.hasPathOrNull("privateKeyFilePath") ? c.getString("privateKeyFilePath") : ""; - this.trustCertCollectionFilePath = c.hasPathOrNull("trustCertCollectionFilePath") ? c.getString("trustCertCollectionFilePath") : ""; - } + public Tls2(com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator) { + this.certChainFilePath = c.hasPathOrNull("certChainFilePath") ? c.getString("certChainFilePath") : ""; + this.privateKeyFilePath = c.hasPathOrNull("privateKeyFilePath") ? c.getString("privateKeyFilePath") : ""; + this.trustCertCollectionFilePath = c.hasPathOrNull("trustCertCollectionFilePath") ? c.getString("trustCertCollectionFilePath") : ""; + } + } } - } - private static final class $TsCfgValidator { - private final java.util.List badPaths = new java.util.ArrayList<>(); + private static final class $TsCfgValidator { + private final java.util.List badPaths = new java.util.ArrayList<>(); - void addBadPath(java.lang.String path, com.typesafe.config.ConfigException e) { - badPaths.add("'" + path + "': " + e.getClass().getName() + "(" + e.getMessage() + ")"); - } + void addBadPath(java.lang.String path, com.typesafe.config.ConfigException e) { + badPaths.add("'" + path + "': " + e.getClass().getName() + "(" + e.getMessage() + ")"); + } - void validate() { - if (!badPaths.isEmpty()) { - java.lang.StringBuilder sb = new java.lang.StringBuilder("Invalid configuration:"); - for (java.lang.String path : badPaths) { - sb.append("\n ").append(path); + void validate() { + if (!badPaths.isEmpty()) { + java.lang.StringBuilder sb = new java.lang.StringBuilder("Invalid configuration:"); + for (java.lang.String path : badPaths) { + sb.append("\n ").append(path); + } + throw new com.typesafe.config.ConfigException(sb.toString()) { + }; + } } - throw new com.typesafe.config.ConfigException(sb.toString()) { - }; - } } - } } diff --git a/src/main/java/io/prometheus/common/VersionAnnotation.java b/src/main/java/io/prometheus/common/VersionAnnotation.java index 580e9cef..71586878 100644 --- a/src/main/java/io/prometheus/common/VersionAnnotation.java +++ b/src/main/java/io/prometheus/common/VersionAnnotation.java @@ -25,7 +25,7 @@ @Target(ElementType.PACKAGE) public @interface VersionAnnotation { - String version(); + String version(); - String date(); + String date(); } diff --git a/src/main/java/io/prometheus/package-info.java b/src/main/java/io/prometheus/package-info.java index a4ef792e..b80cbe3c 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.6.4", date = "4/30/20") +@VersionAnnotation(version = "1.7.0", date = "7/8/20") package io.prometheus; import io.prometheus.common.VersionAnnotation; \ No newline at end of file diff --git a/src/main/kotlin/io/prometheus/Agent.kt b/src/main/kotlin/io/prometheus/Agent.kt index bb72a426..070f52f8 100644 --- a/src/main/kotlin/io/prometheus/Agent.kt +++ b/src/main/kotlin/io/prometheus/Agent.kt @@ -29,14 +29,9 @@ import com.github.pambrose.common.util.getBanner import com.github.pambrose.common.util.hostInfo import com.github.pambrose.common.util.simpleClassName import com.google.common.util.concurrent.RateLimiter +import io.grpc.Status import io.grpc.StatusRuntimeException -import io.prometheus.agent.AgentConnectionContext -import io.prometheus.agent.AgentGrpcService -import io.prometheus.agent.AgentHttpService -import io.prometheus.agent.AgentMetrics -import io.prometheus.agent.AgentOptions -import io.prometheus.agent.AgentPathManager -import io.prometheus.agent.RequestFailureException +import io.prometheus.agent.* import io.prometheus.client.Summary import io.prometheus.common.BaseOptions.Companion.DEBUG import io.prometheus.common.ConfigVals @@ -44,9 +39,7 @@ import io.prometheus.common.ConfigWrappers.newAdminConfig import io.prometheus.common.ConfigWrappers.newMetricsConfig import io.prometheus.common.ConfigWrappers.newZipkinConfig import io.prometheus.common.getVersionDesc -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.* import mu.KLogging import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit.MILLISECONDS @@ -63,9 +56,7 @@ class Agent(val options: AgentOptions, testMode: Boolean = false, initBlock: (Agent.() -> Unit)? = null) : GenericService(options.configVals, - newAdminConfig(options.adminEnabled, - options.adminPort, - options.configVals.agent.admin), + newAdminConfig(options.adminEnabled, options.adminPort, options.configVals.agent.admin), newMetricsConfig(options.metricsEnabled, options.metricsPort, options.configVals.agent.metrics), @@ -122,7 +113,16 @@ class Agent(val options: AgentOptions, override fun run() { - fun connectToProxy() { + fun exceptionHandler(name: String) = + CoroutineExceptionHandler { _, e -> + if (grpcService.agent.isRunning) + Status.fromThrowable(e) + .apply { + AgentGrpcService.logger.error { "Error in $name(): $code $description" } + } + } + + suspend fun connectToProxy() { // Reset gRPC stubs if previous iteration had a successful connection, i.e., the agentId != "" if (agentId.isNotEmpty()) { grpcService.resetGrpcStubs() @@ -140,15 +140,24 @@ class Agent(val options: AgentOptions, pathManager.registerPaths() val connectionContext = AgentConnectionContext() - grpcService.readRequestsFromProxy(agentHttpService, connectionContext) - runBlocking { - launch(Dispatchers.Default) { startHeartBeat(connectionContext) } + coroutineScope { + launch(Dispatchers.Default + exceptionHandler("readRequestsFromProxy")) { + grpcService.readRequestsFromProxy(agentHttpService, connectionContext) + } - launch(Dispatchers.Default) { grpcService.writeResponsesToProxyUntilDisconnected(connectionContext) } + launch(Dispatchers.Default + exceptionHandler("startHeartBeat")) { + startHeartBeat(connectionContext) + } + + // This exceptionHandler is not necessary + launch(Dispatchers.Default + exceptionHandler("writeResponsesToProxyUntilDisconnected")) { + grpcService.writeResponsesToProxyUntilDisconnected(connectionContext) + } - for (scrapeRequestAction in connectionContext.scrapeRequestsChannel) { - launch(Dispatchers.Default) { + launch(Dispatchers.Default + exceptionHandler("scrapeResultsChannel.send")) { + // This is terminated by connectionContext.close() + for (scrapeRequestAction in connectionContext.scrapeRequestsChannel) { // The url fetch occurs during the invoke() on the scrapeRequestAction val scrapeResponse = scrapeRequestAction.invoke() connectionContext.scrapeResultsChannel.send(scrapeResponse) @@ -160,15 +169,21 @@ class Agent(val options: AgentOptions, while (isRunning) { try { - connectToProxy() - } catch (e: RequestFailureException) { + runBlocking { + connectToProxy() + } + } + catch (e: RequestFailureException) { logger.info { "Disconnected from proxy at $proxyHost after invalid response ${e.message}" } - } catch (e: StatusRuntimeException) { + } + catch (e: StatusRuntimeException) { logger.info { "Disconnected from proxy at $proxyHost" } - } catch (e: Throwable) { + } + catch (e: Throwable) { // Catch anything else to avoid exiting retry loop logger.warn { "Throwable caught ${e.simpleClassName} ${e.message}" } - } finally { + } + finally { logger.info { "Waited ${reconnectLimiter.acquire().roundToInt().seconds} to reconnect" } } } @@ -182,28 +197,32 @@ class Agent(val options: AgentOptions, override fun registerHealthChecks() { super.registerHealthChecks() - healthCheckRegistry.register("scrape_request_backlog_check", - newBacklogHealthCheck(scrapeRequestBacklogSize.get(), - agentConfigVals.scrapeRequestBacklogUnhealthySize)) + healthCheckRegistry.register( + "scrape_request_backlog_check", + newBacklogHealthCheck( + scrapeRequestBacklogSize.get(), + agentConfigVals.scrapeRequestBacklogUnhealthySize + ) + ) } private suspend fun startHeartBeat(connectionContext: AgentConnectionContext) = - if (agentConfigVals.heartbeatEnabled) { - val heartbeatPauseTime = agentConfigVals.heartbeatCheckPauseMillis.milliseconds - val maxInactivityTime = agentConfigVals.heartbeatMaxInactivitySecs.seconds - logger.info { "Heartbeat scheduled to fire after $maxInactivityTime of inactivity" } - - while (isRunning && connectionContext.connected) { - val timeSinceLastWrite = lastMsgSentMark.elapsedNow() - if (timeSinceLastWrite > maxInactivityTime) - grpcService.sendHeartBeat(connectionContext) - delay(heartbeatPauseTime) - } - logger.info { "Heartbeat completed" } - } - else { - logger.info { "Heartbeat disabled" } + if (agentConfigVals.heartbeatEnabled) { + val heartbeatPauseTime = agentConfigVals.heartbeatCheckPauseMillis.milliseconds + val maxInactivityTime = agentConfigVals.heartbeatMaxInactivitySecs.seconds + logger.info { "Heartbeat scheduled to fire after $maxInactivityTime of inactivity" } + + while (isRunning && connectionContext.connected) { + val timeSinceLastWrite = lastMsgSentMark.elapsedNow() + if (timeSinceLastWrite > maxInactivityTime) + grpcService.sendHeartBeat() + delay(heartbeatPauseTime) } + logger.info { "Heartbeat completed" } + } + else { + logger.info { "Heartbeat disabled" } + } internal fun updateScrapeCounter(type: String) { if (type.isNotEmpty()) @@ -215,7 +234,7 @@ class Agent(val options: AgentOptions, } internal fun awaitInitialConnection(timeout: Duration) = - initialConnectionLatch.await(timeout.toLongMilliseconds(), MILLISECONDS) + initialConnectionLatch.await(timeout.toLongMilliseconds(), MILLISECONDS) internal fun metrics(args: AgentMetrics.() -> Unit) { if (isMetricsEnabled) @@ -228,13 +247,13 @@ class Agent(val options: AgentOptions, } override fun toString() = - toStringElements { - add("agentId", agentId) - add("agentName", agentName) - add("proxyHost", proxyHost) - add("adminService", if (isAdminEnabled) adminService else "Disabled") - add("metricsService", if (isMetricsEnabled) metricsService else "Disabled") - } + toStringElements { + add("agentId", agentId) + add("agentName", agentName) + add("proxyHost", proxyHost) + add("adminService", if (isAdminEnabled) adminService else "Disabled") + add("metricsService", if (isMetricsEnabled) metricsService else "Disabled") + } companion object : KLogging() { @JvmStatic diff --git a/src/main/kotlin/io/prometheus/Proxy.kt b/src/main/kotlin/io/prometheus/Proxy.kt index 58abad49..3e4be394 100644 --- a/src/main/kotlin/io/prometheus/Proxy.kt +++ b/src/main/kotlin/io/prometheus/Proxy.kt @@ -37,15 +37,7 @@ import io.prometheus.common.ConfigWrappers.newMetricsConfig import io.prometheus.common.ConfigWrappers.newZipkinConfig import io.prometheus.common.GrpcObjects.EMPTY_AGENTID import io.prometheus.common.getVersionDesc -import io.prometheus.proxy.AgentContext -import io.prometheus.proxy.AgentContextCleanupService -import io.prometheus.proxy.AgentContextManager -import io.prometheus.proxy.ProxyGrpcService -import io.prometheus.proxy.ProxyHttpService -import io.prometheus.proxy.ProxyMetrics -import io.prometheus.proxy.ProxyOptions -import io.prometheus.proxy.ProxyPathManager -import io.prometheus.proxy.ScrapeRequestManager +import io.prometheus.proxy.* import kotlinx.coroutines.runBlocking import mu.KLogging import java.time.LocalDateTime @@ -70,12 +62,13 @@ class Proxy(val options: ProxyOptions, private val proxyConfigVals: ConfigVals.Proxy2.Internal2 = configVals.proxy.internal private val httpService = ProxyHttpService(this, proxyHttpPort) - private val recentActions: EvictingQueue = EvictingQueue.create(configVals.proxy.admin.recentRequestsQueueSize) + private val recentActions: EvictingQueue = + EvictingQueue.create(configVals.proxy.admin.recentRequestsQueueSize) private val grpcService = - if (inProcessServerName.isEmpty()) - ProxyGrpcService(this, port = options.proxyAgentPort) - else - ProxyGrpcService(this, inProcessName = inProcessServerName) + if (inProcessServerName.isEmpty()) + ProxyGrpcService(this, port = options.proxyAgentPort) + else + ProxyGrpcService(this, inProcessName = inProcessServerName) private val agentCleanupService by lazy { AgentContextCleanupService(this, proxyConfigVals) { addServices(this) } } @@ -110,7 +103,7 @@ class Proxy(val options: ProxyOptions, pathManager.toPlainText(), if (recentActions.size > 0) "\n$cnt most recent Requests:" else "", recentActions.reversed().joinToString("\n")) - .joinToString("\n") + .joinToString("\n") }) } } @@ -148,30 +141,30 @@ class Proxy(val options: ProxyOptions, override fun registerHealthChecks() { super.registerHealthChecks() healthCheckRegistry - .apply { - register("grpc_service", grpcService.healthCheck) - register("chunking_map_check", - newMapHealthCheck(agentContextManager.chunkedContextMap, - proxyConfigVals.chunkContextMapUnhealthySize)) - register("scrape_response_map_check", - newMapHealthCheck(scrapeRequestManager.scrapeRequestMap, - proxyConfigVals.scrapeRequestMapUnhealthySize)) - register("agent_scrape_request_backlog", - healthCheck { - val unhealthySize = proxyConfigVals.scrapeRequestBacklogUnhealthySize - val vals = - agentContextManager.agentContextMap.entries - .filter { it.value.scrapeRequestBacklogSize >= unhealthySize } - .map { "${it.value} ${it.value.scrapeRequestBacklogSize}" } - if (vals.isEmpty()) { - HealthCheck.Result.healthy() - } - else { - val s = Joiner.on(", ").join(vals) - HealthCheck.Result.unhealthy("Large agent scrape request backlog: $s") - } - }) - } + .apply { + register("grpc_service", grpcService.healthCheck) + register("chunking_map_check", + newMapHealthCheck(agentContextManager.chunkedContextMap, + proxyConfigVals.chunkContextMapUnhealthySize)) + register("scrape_response_map_check", + newMapHealthCheck(scrapeRequestManager.scrapeRequestMap, + proxyConfigVals.scrapeRequestMapUnhealthySize)) + register("agent_scrape_request_backlog", + healthCheck { + val unhealthySize = proxyConfigVals.scrapeRequestBacklogUnhealthySize + val vals = + agentContextManager.agentContextMap.entries + .filter { it.value.scrapeRequestBacklogSize >= unhealthySize } + .map { "${it.value} ${it.value.scrapeRequestBacklogSize}" } + if (vals.isEmpty()) { + HealthCheck.Result.healthy() + } + else { + val s = Joiner.on(", ").join(vals) + HealthCheck.Result.unhealthy("Large agent scrape request backlog: $s") + } + }) + } } internal fun removeAgentContext(agentId: String): AgentContext? { @@ -202,11 +195,11 @@ class Proxy(val options: ProxyOptions, } override fun toString() = - toStringElements { - add("proxyPort", httpService.httpPort) - add("adminService", if (isAdminEnabled) adminService else "Disabled") - add("metricsService", if (isMetricsEnabled) metricsService else "Disabled") - } + toStringElements { + add("proxyPort", httpService.httpPort) + add("adminService", if (isAdminEnabled) adminService else "Disabled") + add("metricsService", if (isMetricsEnabled) metricsService else "Disabled") + } companion object : KLogging() { internal const val AGENT_ID = "agent-id" diff --git a/src/main/kotlin/io/prometheus/agent/AgentClientInterceptor.kt b/src/main/kotlin/io/prometheus/agent/AgentClientInterceptor.kt index 83c2075c..1b6bc6e1 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentClientInterceptor.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentClientInterceptor.kt @@ -18,14 +18,7 @@ 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.grpc.* import io.prometheus.Agent import io.prometheus.Proxy import io.prometheus.common.GrpcObjects.EMPTY_AGENTID @@ -37,34 +30,34 @@ internal class AgentClientInterceptor(private val agent: Agent) : ClientIntercep callOptions: CallOptions, next: Channel): ClientCall = // final String methodName = method.getFullMethodName(); - // logger.info {"Intercepting {}", methodName); - object : - ForwardingClientCall.SimpleForwardingClientCall( - agent.grpcService.channel.newCall(method, callOptions)) { - override fun start(responseListener: Listener, metadata: Metadata) { - super.start( - object : ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { - override fun onHeaders(headers: Metadata?) { - if (headers == null) { - logger.error { "Missing headers" } - } - else { - // Grab agent_id from headers if not already assigned - if (agent.agentId.isEmpty()) { - headers.get(Metadata.Key.of(Proxy.AGENT_ID, Metadata.ASCII_STRING_MARSHALLER))?.also { - agent.agentId = it - check(agent.agentId.isNotEmpty()) { EMPTY_AGENTID } - logger.debug { "Assigned agentId to $agent" } - } ?: logger.error { "Headers missing AGENT_ID key" } - } + // logger.info {"Intercepting {}", methodName); + object : + ForwardingClientCall.SimpleForwardingClientCall( + agent.grpcService.channel.newCall(method, callOptions)) { + override fun start(responseListener: Listener, metadata: Metadata) { + super.start( + object : ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { + override fun onHeaders(headers: Metadata?) { + if (headers == null) { + logger.error { "Missing headers" } + } + else { + // Grab agent_id from headers if not already assigned + if (agent.agentId.isEmpty()) { + headers.get(Metadata.Key.of(Proxy.AGENT_ID, Metadata.ASCII_STRING_MARSHALLER))?.also { + agent.agentId = it + check(agent.agentId.isNotEmpty()) { EMPTY_AGENTID } + logger.debug { "Assigned agentId to $agent" } + } ?: logger.error { "Headers missing AGENT_ID key" } } - super.onHeaders(headers) } - }, - metadata - ) - } + super.onHeaders(headers) + } + }, + metadata + ) } + } companion object : KLogging() } diff --git a/src/main/kotlin/io/prometheus/agent/AgentConnectionContext.kt b/src/main/kotlin/io/prometheus/agent/AgentConnectionContext.kt index bf7d6f30..6905fd51 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentConnectionContext.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentConnectionContext.kt @@ -19,16 +19,17 @@ package io.prometheus.agent import com.github.pambrose.common.delegate.AtomicDelegates.atomicBoolean +import io.ktor.utils.io.core.Closeable import io.prometheus.common.ScrapeRequestAction import io.prometheus.common.ScrapeResults import kotlinx.coroutines.channels.Channel -internal class AgentConnectionContext { +internal class AgentConnectionContext : Closeable { private var disconnected by atomicBoolean(false) val scrapeRequestsChannel = Channel(Channel.UNLIMITED) val scrapeResultsChannel = Channel(Channel.UNLIMITED) - fun disconnect() { + override fun close() { disconnected = true scrapeRequestsChannel.cancel() scrapeResultsChannel.cancel() diff --git a/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt b/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt index c82b3d66..971634e8 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentGrpcService.kt @@ -21,7 +21,6 @@ package io.prometheus.agent import brave.grpc.GrpcTracing import com.github.pambrose.common.delegate.AtomicDelegates.atomicBoolean import com.github.pambrose.common.dsl.GrpcDsl.channel -import com.github.pambrose.common.dsl.GrpcDsl.streamObserver import com.github.pambrose.common.util.simpleClassName import com.github.pambrose.common.utils.TlsContext import com.github.pambrose.common.utils.TlsContext.Companion.PLAINTEXT_CONTEXT @@ -42,22 +41,25 @@ import io.prometheus.common.GrpcObjects.newScrapeResponseSummary import io.prometheus.common.GrpcObjects.toScrapeResponse import io.prometheus.common.GrpcObjects.toScrapeResponseHeader import io.prometheus.common.ScrapeResults -import io.prometheus.grpc.ProxyServiceGrpc -import io.prometheus.grpc.ProxyServiceGrpc.ProxyServiceBlockingStub -import io.prometheus.grpc.ProxyServiceGrpc.ProxyServiceStub -import kotlinx.coroutines.runBlocking +import io.prometheus.grpc.ChunkedScrapeResponse +import io.prometheus.grpc.ProxyServiceGrpcKt +import io.prometheus.grpc.ScrapeRequest +import io.prometheus.grpc.ScrapeResponse +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.consumeAsFlow import mu.KLogging import java.io.ByteArrayInputStream import java.util.concurrent.CountDownLatch import java.util.zip.CRC32 import kotlin.properties.Delegates.notNull -internal class AgentGrpcService(private val agent: Agent, +internal class AgentGrpcService(internal val agent: Agent, private val options: AgentOptions, private val inProcessServerName: String) { private var grpcStarted by atomicBoolean(false) - private var blockingStub: ProxyServiceBlockingStub by notNull() - private var asyncStub: ProxyServiceStub by notNull() + private var stub: ProxyServiceGrpcKt.ProxyServiceCoroutineStub by notNull() private val tracing by lazy { agent.zipkinReporterService.newTracing("grpc_client") } private val grpcTracing by lazy { GrpcTracing.create(tracing) } @@ -69,14 +71,14 @@ internal class AgentGrpcService(private val agent: Agent, init { val schemeStripped = - options.proxyHostname - .run { - when { - startsWith(HTTP_PREFIX) -> removePrefix(HTTP_PREFIX) - startsWith(HTTPS_PREFIX) -> removePrefix(HTTPS_PREFIX) - else -> this - } - } + options.proxyHostname + .run { + when { + startsWith(HTTP_PREFIX) -> removePrefix(HTTP_PREFIX) + startsWith(HTTPS_PREFIX) -> removePrefix(HTTPS_PREFIX) + else -> this + } + } if (":" in schemeStripped) { val vals = schemeStripped.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() @@ -89,16 +91,19 @@ internal class AgentGrpcService(private val agent: Agent, } tlsContext = - agent.options.run { - if (certChainFilePath.isNotEmpty() - || privateKeyFilePath.isNotEmpty() - || trustCertCollectionFilePath.isNotEmpty()) - buildClientTlsContext(certChainFilePath = certChainFilePath, - privateKeyFilePath = privateKeyFilePath, - trustCertCollectionFilePath = trustCertCollectionFilePath) - else - PLAINTEXT_CONTEXT - } + agent.options.run { + if (certChainFilePath.isNotEmpty() + || privateKeyFilePath.isNotEmpty() + || trustCertCollectionFilePath.isNotEmpty() + ) + buildClientTlsContext( + certChainFilePath = certChainFilePath, + privateKeyFilePath = privateKeyFilePath, + trustCertCollectionFilePath = trustCertCollectionFilePath + ) + else + PLAINTEXT_CONTEXT + } resetGrpcStubs() } @@ -121,203 +126,209 @@ internal class AgentGrpcService(private val agent: Agent, grpcStarted = true channel = - channel(hostName = hostName, - port = port, - tlsContext = tlsContext, - overrideAuthority = agent.options.overrideAuthority, - inProcessServerName = inProcessServerName) { - if (agent.isZipkinEnabled) - intercept(grpcTracing.newClientInterceptor()) - } + channel(hostName = hostName, + port = port, + tlsContext = tlsContext, + overrideAuthority = agent.options.overrideAuthority, + inProcessServerName = inProcessServerName) { + if (agent.isZipkinEnabled) + intercept(grpcTracing.newClientInterceptor()) + } val interceptors = listOf(AgentClientInterceptor(agent)) - blockingStub = ProxyServiceGrpc.newBlockingStub(ClientInterceptors.intercept(channel, interceptors)) - asyncStub = ProxyServiceGrpc.newStub(ClientInterceptors.intercept(channel, interceptors)) + stub = ProxyServiceGrpcKt.ProxyServiceCoroutineStub(ClientInterceptors.intercept(channel, interceptors)) } // If successful, this will create an agentContext on the Proxy and an interceptor will add an agent_id to the headers` - fun connectAgent() = - try { - logger.info { "Connecting to proxy at ${agent.proxyHost} using ${tlsContext.desc()}..." } - blockingStub.connectAgent(Empty.getDefaultInstance()) - logger.info { "Connected to proxy at ${agent.proxyHost} using ${tlsContext.desc()}" } - agent.metrics { connectCount.labels("success").inc() } - true - } catch (e: StatusRuntimeException) { - agent.metrics { connectCount.labels("failure").inc() } - logger.info { "Cannot connect to proxy at ${agent.proxyHost} using ${tlsContext.desc()} - ${e.simpleClassName}: ${e.message}" } - false - } + suspend fun connectAgent() = + try { + logger.info { "Connecting to proxy at ${agent.proxyHost} using ${tlsContext.desc()}..." } + stub.connectAgent(Empty.getDefaultInstance()) + logger.info { "Connected to proxy at ${agent.proxyHost} using ${tlsContext.desc()}" } + agent.metrics { connectCount.labels("success").inc() } + true + } + catch (e: StatusRuntimeException) { + agent.metrics { connectCount.labels("failure").inc() } + logger.info { "Cannot connect to proxy at ${agent.proxyHost} using ${tlsContext.desc()} - ${e.simpleClassName}: ${e.message}" } + false + } - fun registerAgent(initialConnectionLatch: CountDownLatch) { + suspend fun registerAgent(initialConnectionLatch: CountDownLatch) { val request = newRegisterAgentRequest(agent.agentId, agent.agentName, hostName) - blockingStub.registerAgent(request).also { response -> - agent.markMsgSent() - if (!response.valid) - throw RequestFailureException("registerAgent() - ${response.reason}") - } + stub.registerAgent(request) + .also { response -> + agent.markMsgSent() + if (!response.valid) + throw RequestFailureException("registerAgent() - ${response.reason}") + } initialConnectionLatch.countDown() } - fun pathMapSize(): Int { - val request = GrpcObjects.newPathMapSizeRequest(agent.agentId) - return blockingStub.pathMapSize(request).run { - agent.markMsgSent() - pathCount + fun pathMapSize() = + runBlocking { + val request = GrpcObjects.newPathMapSizeRequest(agent.agentId) + stub.pathMapSize(request) + .run { + agent.markMsgSent() + pathCount + } } - } - fun registerPathOnProxy(path: String): Long { + suspend fun registerPathOnProxy(path: String): Long { val request = GrpcObjects.newRegisterPathRequest(agent.agentId, path) - return blockingStub.registerPath(request).run { - agent.markMsgSent() - if (!valid) - throw RequestFailureException("registerPath() - $reason") - pathId - } + return stub.registerPath(request) + .run { + agent.markMsgSent() + if (!valid) + throw RequestFailureException("registerPath() - $reason") + pathId + } } - fun unregisterPathOnProxy(path: String) { + suspend fun unregisterPathOnProxy(path: String) { val request = GrpcObjects.newUnregisterPathRequest(agent.agentId, path) - blockingStub.unregisterPath(request) - .apply { - agent.markMsgSent() - if (!valid) - throw RequestFailureException("unregisterPath() - $reason") - } + stub.unregisterPath(request) + .apply { + agent.markMsgSent() + if (!valid) + throw RequestFailureException("unregisterPath() - $reason") + } } - fun sendHeartBeat(connectionContext: AgentConnectionContext) { + suspend fun sendHeartBeat() { if (agent.agentId.isEmpty()) return try { val request = GrpcObjects.newHeartBeatRequest(agent.agentId) - blockingStub - .sendHeartBeat(request) - .apply { - agent.markMsgSent() - if (!valid) { - logger.error { "AgentId ${agent.agentId} not found on proxy" } - throw StatusRuntimeException(Status.NOT_FOUND) - } + stub.sendHeartBeat(request) + .apply { + agent.markMsgSent() + if (!valid) { + logger.error { "AgentId ${agent.agentId} not found on proxy" } + throw StatusRuntimeException(Status.NOT_FOUND) } - } catch (e: StatusRuntimeException) { + } + } + catch (e: StatusRuntimeException) { logger.error { "Hearbeat failed ${e.status}" } - connectionContext.disconnect() } } - fun readRequestsFromProxy(agentHttpService: AgentHttpService, connectionContext: AgentConnectionContext) { - asyncStub.readRequestsFromProxy( - newAgentInfo(agent.agentId), - streamObserver { - onNext { request -> - // This will block, but only very briefly for the duration of the send. - // The actual fetch happens at the other end of the channel, not here. - runBlocking { - logger.debug { "readRequestsFromProxy(): \n$request" } - connectionContext.scrapeRequestsChannel.send { agentHttpService.fetchScrapeUrl(request) } - agent.scrapeRequestBacklogSize.incrementAndGet() - } - } - - onError { throwable -> - if (agent.isRunning) - Status.fromThrowable(throwable).apply { - logger.error { "Error in readRequestsFromProxy(): $code $description" } - } - connectionContext.disconnect() - } - - onCompleted { - connectionContext.disconnect() - } - }) + suspend fun readRequestsFromProxy(agentHttpService: AgentHttpService, connectionContext: AgentConnectionContext) { + connectionContext + .use { + val agentInfo = newAgentInfo(agent.agentId) + val replies = stub.readRequestsFromProxy(agentInfo) + replies.collect { request: ScrapeRequest -> + // The actual fetch happens at the other end of the channel, not here. + logger.debug { "readRequestsFromProxy():\n$request" } + connectionContext.scrapeRequestsChannel.send { agentHttpService.fetchScrapeUrl(request) } + agent.scrapeRequestBacklogSize.incrementAndGet() + } + } } - suspend fun writeResponsesToProxyUntilDisconnected(connectionContext: AgentConnectionContext) { + private suspend fun processScrapeResults(scrapeResultsChannel: Channel, + nonChunkedChannel: Channel, + chunkedChannel: Channel) = + try { + for (scrapeResults: ScrapeResults in scrapeResultsChannel) { + val scrapeId = scrapeResults.scrapeId - val emptyResponseObserver = - streamObserver { - onNext { - // Ignore Empty return value - } + if (!scrapeResults.zipped) { + logger.debug { "Writing non-chunked msg scrapeId: $scrapeId length: ${scrapeResults.contentAsText.length}" } + nonChunkedChannel.send(scrapeResults.toScrapeResponse()) + agent.metrics { scrapeResultCount.labels("non-gzipped").inc() } + } + else { + val zipped = scrapeResults.contentAsZipped + logger.debug { "Comparing ${zipped.size} and ${options.chunkContentSizeKbs}" } - onError { throwable -> - if (agent.isRunning) - Status.fromThrowable(throwable).apply { - logger.error { "Error in writeResponsesToProxyUntilDisconnected(): $code $description" } - } - connectionContext.disconnect() + if (zipped.size < options.chunkContentSizeKbs) { + logger.debug { "Writing zipped non-chunked msg scrapeId: $scrapeId length: ${zipped.size}" } + nonChunkedChannel.send(scrapeResults.toScrapeResponse()) + agent.metrics { scrapeResultCount.labels("gzipped").inc() } } + else { + scrapeResults.toScrapeResponseHeader() + .also { + logger.debug { "Writing header length: ${zipped.size} for scrapeId: $scrapeId " } + chunkedChannel.send(it) + } - onCompleted { - connectionContext.disconnect() + var totalByteCount = 0 + var totalChunkCount = 0 + val checksum = CRC32() + val bais = ByteArrayInputStream(zipped) + val buffer = ByteArray(options.chunkContentSizeKbs) + var readByteCount: Int + + while (bais.read(buffer).also { bytesRead -> readByteCount = bytesRead } > 0) { + totalChunkCount++ + totalByteCount += readByteCount + checksum.update(buffer, 0, buffer.size) + + newScrapeResponseChunk(scrapeId, totalChunkCount, readByteCount, checksum, buffer) + .also { + logger.debug { "Writing chunk $totalChunkCount for scrapeId: $scrapeId" } + chunkedChannel.send(it) + } + } + + newScrapeResponseSummary(scrapeId, totalChunkCount, totalByteCount, checksum) + .also { + logger.debug { "Writing summary totalChunkCount: $totalChunkCount for scrapeID: $scrapeId" } + chunkedChannel.send(it) + agent.metrics { scrapeResultCount.labels("chunked").inc() } + } } } - val nonchunkedObserver = asyncStub.writeResponsesToProxy(emptyResponseObserver) - val chunkedObserver = asyncStub.writeChunkedResponsesToProxy(emptyResponseObserver) + agent.markMsgSent() + agent.scrapeRequestBacklogSize.decrementAndGet() + } + } + finally { + nonChunkedChannel.close() + chunkedChannel.close() + } + + suspend fun writeResponsesToProxyUntilDisconnected(connectionContext: AgentConnectionContext) { + fun exceptionHandler() = + CoroutineExceptionHandler { _, e -> + if (agent.isRunning) + Status.fromThrowable(e) + .apply { + logger.error { "Error in writeResponsesToProxyUntilDisconnected(): $code $description" } + } + } - for (scrapeResults: ScrapeResults in connectionContext.scrapeResultsChannel) { - val scrapedId = scrapeResults.scrapeId + coroutineScope { + val nonChunkedChannel = Channel(Channel.UNLIMITED) + val chunkedChannel = Channel(Channel.UNLIMITED) - if (!scrapeResults.zipped) { - logger.debug { "Writing non-chunked msg scrapeId: $scrapedId length: ${scrapeResults.contentAsText.length}" } - nonchunkedObserver.onNext(scrapeResults.toScrapeResponse()) - agent.metrics { scrapeResultCount.labels("non-gzipped").inc() } + launch(Dispatchers.Default + exceptionHandler()) { + processScrapeResults(connectionContext.scrapeResultsChannel, nonChunkedChannel, chunkedChannel) } - else { - val zipped = scrapeResults.contentAsZipped - logger.debug { "Comparing ${zipped.size} and ${options.chunkContentSizeKbs}" } - - if (zipped.size < options.chunkContentSizeKbs) { - logger.debug { "Writing zipped non-chunked msg scrapeId: $scrapedId length: ${zipped.size}" } - nonchunkedObserver.onNext(scrapeResults.toScrapeResponse()) - agent.metrics { scrapeResultCount.labels("gzipped").inc() } - } - else { - scrapeResults.toScrapeResponseHeader().also { - logger.debug { "Writing header length: ${zipped.size} for scrapeId: $scrapedId " } - chunkedObserver.onNext(it) - } - var totalByteCount = 0 - var totalChunkCount = 0 - val checksum = CRC32() - val bais = ByteArrayInputStream(zipped) - val buffer = ByteArray(options.chunkContentSizeKbs) - var readByteCount: Int - - while (bais.read(buffer).also { bytesRead -> readByteCount = bytesRead } > 0) { - totalChunkCount++ - totalByteCount += readByteCount - checksum.update(buffer, 0, buffer.size) - - newScrapeResponseChunk(scrapeResults.scrapeId, totalChunkCount, readByteCount, checksum, buffer).also { - logger.debug { "Writing chunk $totalChunkCount for scrapeId: $scrapedId" } - chunkedObserver.onNext(it) + connectionContext + .use { + coroutineScope { + launch(Dispatchers.Default + exceptionHandler()) { + stub.writeResponsesToProxy(nonChunkedChannel.consumeAsFlow()) } - } - newScrapeResponseSummary(scrapeResults.scrapeId, totalChunkCount, totalByteCount, checksum).also { - logger.debug { "Writing summary totalChunkCount: $totalChunkCount for scrapeID: $scrapedId" } - chunkedObserver.onNext(it) - agent.metrics { scrapeResultCount.labels("chunked").inc() } + launch(Dispatchers.Default + exceptionHandler()) { + stub.writeChunkedResponsesToProxy(chunkedChannel.consumeAsFlow()) + } } - } - } - agent.markMsgSent() - agent.scrapeRequestBacklogSize.decrementAndGet() + logger.info { "Disconnected from proxy at ${agent.proxyHost}" } + } } - - logger.info { "Disconnected from proxy at ${agent.proxyHost}" } - - nonchunkedObserver.onCompleted() - chunkedObserver.onCompleted() } companion object : KLogging() diff --git a/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt b/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt index f96f5023..dcb3fd38 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentHttpService.kt @@ -19,7 +19,7 @@ package io.prometheus.agent import com.github.pambrose.common.dsl.KtorDsl.get -import com.github.pambrose.common.dsl.KtorDsl.http +import com.github.pambrose.common.dsl.KtorDsl.withHttpClient import com.github.pambrose.common.util.simpleClassName import com.github.pambrose.common.util.zip import com.google.common.net.HttpHeaders @@ -39,51 +39,54 @@ import java.util.concurrent.atomic.AtomicReference internal class AgentHttpService(val agent: Agent) { suspend fun fetchScrapeUrl(request: ScrapeRequest): ScrapeResults = - ScrapeResults(agentId = request.agentId, scrapeId = request.scrapeId).also { scrapeResults -> - val scrapeMsg = AtomicReference("") - val path = request.path - val encodedQueryParams = request.encodedQueryParams - val pathContext = agent.pathManager[path] + ScrapeResults(agentId = request.agentId, scrapeId = request.scrapeId).also { scrapeResults -> + val scrapeMsg = AtomicReference("") + val path = request.path + val encodedQueryParams = request.encodedQueryParams + val pathContext = agent.pathManager[path] - if (pathContext == null) { - logger.warn { "Invalid path in fetchScrapeUrl(): $path" } - scrapeMsg.set("invalid_path") - if (request.debugEnabled) - scrapeResults.setDebugInfo("None", "Invalid path: $path") - } - else { - val requestTimer = if (agent.isMetricsEnabled) agent.startTimer() else null - // Add the incoming query params to the url - val url = pathContext.url + - (if (encodedQueryParams.isNotEmpty()) - "?${URLDecoder.decode(encodedQueryParams, Charsets.UTF_8.name())}" - else "") + if (pathContext == null) { + logger.warn { "Invalid path in fetchScrapeUrl(): $path" } + scrapeMsg.set("invalid_path") + if (request.debugEnabled) + scrapeResults.setDebugInfo("None", "Invalid path: $path") + } + else { + val requestTimer = if (agent.isMetricsEnabled) agent.startTimer() else null + // Add the incoming query params to the url + val url = pathContext.url + + (if (encodedQueryParams.isNotEmpty()) + "?${URLDecoder.decode(encodedQueryParams, Charsets.UTF_8.name())}" + else "") - logger.debug { "Fetching $pathContext" } - if (encodedQueryParams.isNotEmpty()) - logger.debug { "URL: $url" } + logger.debug { "Fetching $pathContext" } + if (encodedQueryParams.isNotEmpty()) + logger.debug { "URL: $url" } - // Content is fetched here - try { - http { - get(url, setup(request), getBlock(url, scrapeResults, scrapeMsg, request.debugEnabled)) - } - } catch (e: IOException) { - logger.info { "Failed HTTP request: $url [${e.simpleClassName}: ${e.message}]" } - if (request.debugEnabled) - scrapeResults.setDebugInfo(url, "${e.simpleClassName} - ${e.message}") - } catch (e: Throwable) { - logger.warn(e) { "fetchScrapeUrl() $e - $url" } - if (request.debugEnabled) - scrapeResults.setDebugInfo(url, "${e.simpleClassName} - ${e.message}") - } finally { - requestTimer?.observeDuration() + // Content is fetched here + try { + withHttpClient { + get(url, setup(request), getBlock(url, scrapeResults, scrapeMsg, request.debugEnabled)) } } - - agent.updateScrapeCounter(scrapeMsg.get()) + catch (e: IOException) { + logger.info { "Failed HTTP request: $url [${e.simpleClassName}: ${e.message}]" } + if (request.debugEnabled) + scrapeResults.setDebugInfo(url, "${e.simpleClassName} - ${e.message}") + } + catch (e: Throwable) { + logger.warn(e) { "fetchScrapeUrl() $e - $url" } + if (request.debugEnabled) + scrapeResults.setDebugInfo(url, "${e.simpleClassName} - ${e.message}") + } + finally { + requestTimer?.observeDuration() + } } + agent.updateScrapeCounter(scrapeMsg.get()) + } + private fun setup(request: ScrapeRequest): HttpRequestBuilder.() -> Unit = { val accept: String? = request.accept if (accept?.isNotEmpty() == true) @@ -94,31 +97,31 @@ internal class AgentHttpService(val agent: Agent) { responseArg: ScrapeResults, scrapeCounterMsg: AtomicReference, debugEnabled: Boolean): suspend (HttpResponse) -> Unit = - { response -> - responseArg.statusCode = response.status.value + { response -> + responseArg.statusCode = response.status.value - if (response.status.isSuccess()) { - responseArg.apply { - contentType = response.headers[HttpHeaders.CONTENT_TYPE].orEmpty() - // Zip the content here - val content = response.readText() - zipped = content.length > agent.configVals.agent.minGzipSizeBytes - if (zipped) - contentAsZipped = content.zip() - else - contentAsText = content - validResponse = true - } - if (debugEnabled) - responseArg.setDebugInfo(url) - scrapeCounterMsg.set("success") - } - else { - if (debugEnabled) - responseArg.setDebugInfo(url, "Unsuccessful response code ${responseArg.statusCode}") - scrapeCounterMsg.set("unsuccessful") + if (response.status.isSuccess()) { + responseArg.apply { + contentType = response.headers[HttpHeaders.CONTENT_TYPE].orEmpty() + // Zip the content here + val content = response.readText() + zipped = content.length > agent.configVals.agent.minGzipSizeBytes + if (zipped) + contentAsZipped = content.zip() + else + contentAsText = content + validResponse = true } + if (debugEnabled) + responseArg.setDebugInfo(url) + scrapeCounterMsg.set("success") + } + else { + if (debugEnabled) + responseArg.setDebugInfo(url, "Unsuccessful response code ${responseArg.statusCode}") + scrapeCounterMsg.set("unsuccessful") } + } companion object : KLogging() } \ No newline at end of file diff --git a/src/main/kotlin/io/prometheus/agent/AgentMetrics.kt b/src/main/kotlin/io/prometheus/agent/AgentMetrics.kt index f6e248e9..23038141 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentMetrics.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentMetrics.kt @@ -27,28 +27,28 @@ import io.prometheus.Agent internal class AgentMetrics(agent: Agent) { val scrapeResultCount = - counter { - name("agent_scrape_result_count") - help("Agent scrape result count") - labelNames("type") - } + counter { + name("agent_scrape_result_count") + help("Agent scrape result count") + labelNames("type") + } val scrapeRequestCount = - counter { - name("agent_scrape_request_count") - help("Agent scrape request count") - labelNames("type") - } + counter { + name("agent_scrape_request_count") + help("Agent scrape request count") + labelNames("type") + } val connectCount = - counter { - name("agent_connect_count") - help("Agent connect count") - labelNames("type") - } + counter { + name("agent_connect_count") + help("Agent connect count") + labelNames("type") + } val scrapeRequestLatency = - summary { + summary { name("agent_scrape_request_latency_seconds") help("Agent scrape request latency in seconds") labelNames("agent_name") diff --git a/src/main/kotlin/io/prometheus/agent/AgentOptions.kt b/src/main/kotlin/io/prometheus/agent/AgentOptions.kt index 18131851..2b83c489 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentOptions.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentOptions.kt @@ -22,24 +22,19 @@ 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.AGENT_CONFIG -import io.prometheus.common.EnvVars.AGENT_NAME -import io.prometheus.common.EnvVars.CHUNK_CONTENT_SIZE_KBS -import io.prometheus.common.EnvVars.MIN_GZIP_SIZE_BYTES -import io.prometheus.common.EnvVars.OVERRIDE_AUTHORITY -import io.prometheus.common.EnvVars.PROXY_HOSTNAME +import io.prometheus.common.EnvVars.* - class AgentOptions(argv: Array, exitOnMissingConfig: Boolean) : - BaseOptions(Agent::class.java.name, argv, AGENT_CONFIG.name, exitOnMissingConfig) { +class AgentOptions(argv: Array, exitOnMissingConfig: Boolean) : + BaseOptions(Agent::class.java.name, argv, AGENT_CONFIG.name, exitOnMissingConfig) { - constructor(args: List, exitOnMissingConfig: Boolean) : - this(Iterables.toArray(args, String::class.java), exitOnMissingConfig) + constructor(args: List, exitOnMissingConfig: Boolean) : + this(Iterables.toArray(args, String::class.java), exitOnMissingConfig) - @Parameter(names = ["-p", "--proxy"], description = "Proxy hostname") - var proxyHostname = "" - private set + @Parameter(names = ["-p", "--proxy"], description = "Proxy hostname") + var proxyHostname = "" + private set - @Parameter(names = ["-n", "--name"], description = "Agent name") + @Parameter(names = ["-n", "--name"], description = "Agent name") var agentName = "" private set diff --git a/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt b/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt index 9e212af1..6a734aa2 100644 --- a/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt +++ b/src/main/kotlin/io/prometheus/agent/AgentPathManager.kt @@ -36,27 +36,27 @@ internal class AgentPathManager(private val agent: Agent) { fun pathMapSize(): Int = agent.grpcService.pathMapSize() private val pathConfigs = - agentConfigVals.pathConfigs - .map { - mapOf(NAME to "\"" + it.name + "\"", - PATH to it.path, - URL to it.url) - } - .onEach { logger.info { "Proxy path /${it["path"]} will be assigned to ${it["url"]}" } } - - fun registerPaths() = - pathConfigs.forEach { - val path = it["path"] - val url = it["url"] - if (path != null && url != null) { - registerPath(path, url) - } - else { - logger.error { "Null path/url values: $path/$url" } - } + agentConfigVals.pathConfigs + .map { + mapOf( + NAME to """"it.name""", + PATH to it.path, + URL to it.url + ) } + .onEach { logger.info { "Proxy path /${it["path"]} will be assigned to ${it["url"]}" } } + + suspend fun registerPaths() = + pathConfigs.forEach { + val path = it["path"] + val url = it["url"] + if (path != null && url != null) + registerPath(path, url) + else + logger.error { "Null path/url values: $path/$url" } + } - fun registerPath(pathVal: String, url: String) { + suspend fun registerPath(pathVal: String, url: String) { require(pathVal.isNotEmpty()) { EMPTY_PATH } require(url.isNotEmpty()) { "Empty URL" } val path = if (pathVal.startsWith("/")) pathVal.substring(1) else pathVal @@ -66,7 +66,7 @@ internal class AgentPathManager(private val agent: Agent) { pathContextMap[path] = PathContext(pathId, path, url) } - fun unregisterPath(pathVal: String) { + suspend fun unregisterPath(pathVal: String) { require(pathVal.isNotEmpty()) { EMPTY_PATH } val path = if (pathVal.startsWith("/")) pathVal.substring(1) else pathVal agent.grpcService.unregisterPathOnProxy(path) @@ -81,7 +81,7 @@ internal class AgentPathManager(private val agent: Agent) { val maxName = pathConfigs.map { it[NAME]?.length ?: 0 }.max() ?: 0 val maxPath = pathConfigs.map { it[PATH]?.length ?: 0 }.max() ?: 0 return "Agent Path Configs:\n" + "Name".padEnd(maxName + 1) + "Path".padEnd(maxPath + 2) + "URL\n" + - pathConfigs.joinToString("\n") { c -> "${c[NAME]?.padEnd(maxName)} /${c[PATH]?.padEnd(maxPath)} ${c[URL]}" } + pathConfigs.joinToString("\n") { c -> "${c[NAME]?.padEnd(maxName)} /${c[PATH]?.padEnd(maxPath)} ${c[URL]}" } } companion object : KLogging() { diff --git a/src/main/kotlin/io/prometheus/common/BaseOptions.kt b/src/main/kotlin/io/prometheus/common/BaseOptions.kt index bd3e1c7a..ba02a83a 100644 --- a/src/main/kotlin/io/prometheus/common/BaseOptions.kt +++ b/src/main/kotlin/io/prometheus/common/BaseOptions.kt @@ -23,16 +23,8 @@ import com.beust.jcommander.JCommander import com.beust.jcommander.Parameter import com.beust.jcommander.ParameterException import com.github.pambrose.common.util.simpleClassName -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 io.prometheus.common.EnvVars.ADMIN_ENABLED -import io.prometheus.common.EnvVars.ADMIN_PORT -import io.prometheus.common.EnvVars.DEBUG_ENABLED -import io.prometheus.common.EnvVars.METRICS_ENABLED -import io.prometheus.common.EnvVars.METRICS_PORT +import com.typesafe.config.* +import io.prometheus.common.EnvVars.* import mu.KLogging import java.io.File import java.io.FileNotFoundException @@ -105,18 +97,19 @@ abstract class BaseOptions protected constructor(private val progName: String, fun parseArgs(argv: Array?) { try { val jcom = - JCommander(this) - .apply { - programName = progName - setCaseSensitiveOptions(false) - parse(*argv ?: arrayOf()) - } + JCommander(this) + .apply { + programName = progName + setCaseSensitiveOptions(false) + parse(*argv ?: arrayOf()) + } if (usage) { jcom.usage() exitProcess(0) } - } catch (e: ParameterException) { + } + catch (e: ParameterException) { logger.error(e) { e.message } exitProcess(1) } @@ -155,17 +148,17 @@ abstract class BaseOptions protected constructor(private val progName: String, protected fun assignCertChainFilePath(defaultVal: String) { if (certChainFilePath.isEmpty()) - certChainFilePath = EnvVars.CERT_CHAIN_FILE_PATH.getEnv(defaultVal) + certChainFilePath = CERT_CHAIN_FILE_PATH.getEnv(defaultVal) } protected fun assignPrivateKeyFilePath(defaultVal: String) { if (privateKeyFilePath.isEmpty()) - privateKeyFilePath = EnvVars.PRIVATE_KEY_FILE_PATH.getEnv(defaultVal) + privateKeyFilePath = PRIVATE_KEY_FILE_PATH.getEnv(defaultVal) } protected fun assignTrustCertCollectionFilePath(defaultVal: String) { if (trustCertCollectionFilePath.isEmpty()) - trustCertCollectionFilePath = EnvVars.TRUST_CERT_COLLECTION_FILE_PATH.getEnv(defaultVal) + trustCertCollectionFilePath = TRUST_CERT_COLLECTION_FILE_PATH.getEnv(defaultVal) } private fun readConfig(envConfig: String, exitOnMissingConfig: Boolean) { @@ -174,8 +167,8 @@ abstract class BaseOptions protected constructor(private val progName: String, ConfigParseOptions.defaults().setAllowMissing(false), ConfigFactory.load().resolve(), exitOnMissingConfig) - .resolve(ConfigResolveOptions.defaults()) - .resolve() + .resolve(ConfigResolveOptions.defaults()) + .resolve() dynamicParams.forEach { (k, v) -> // Strip quotes @@ -200,11 +193,11 @@ abstract class BaseOptions protected constructor(private val progName: String, fun String.isPropertiesSuffix() = toLowerCase().endsWith(".properties") || toLowerCase().endsWith(".props") fun getConfigSyntax(configName: String) = - when { - configName.isJsonSuffix() -> ConfigSyntax.JSON - configName.isPropertiesSuffix() -> ConfigSyntax.PROPERTIES - else -> ConfigSyntax.CONF - } + when { + configName.isJsonSuffix() -> ConfigSyntax.JSON + configName.isPropertiesSuffix() -> ConfigSyntax.PROPERTIES + else -> ConfigSyntax.CONF + } when { configName.isBlank() -> { @@ -219,8 +212,9 @@ abstract class BaseOptions protected constructor(private val progName: String, try { val configSyntax = getConfigSyntax(configName) return ConfigFactory.parseURL(URL(configName), configParseOptions.setSyntax(configSyntax)) - .withFallback(fallback) - } catch (e: Exception) { + .withFallback(fallback) + } + catch (e: Exception) { if (e.cause is FileNotFoundException) logger.error { "Invalid config url: $configName" } else @@ -231,7 +225,8 @@ abstract class BaseOptions protected constructor(private val progName: String, else -> { try { return ConfigFactory.parseFileAnySyntax(File(configName), configParseOptions).withFallback(fallback) - } catch (e: Exception) { + } + catch (e: Exception) { if (e.cause is FileNotFoundException) logger.error { "Invalid config filename: $configName" } else diff --git a/src/main/kotlin/io/prometheus/common/ConfigWrappers.kt b/src/main/kotlin/io/prometheus/common/ConfigWrappers.kt index bf7f51f0..1484684d 100644 --- a/src/main/kotlin/io/prometheus/common/ConfigWrappers.kt +++ b/src/main/kotlin/io/prometheus/common/ConfigWrappers.kt @@ -24,15 +24,15 @@ import com.github.pambrose.common.service.ZipkinConfig internal object ConfigWrappers { fun newAdminConfig(enabled: Boolean, port: Int, admin: ConfigVals.Proxy2.Admin2) = - AdminConfig(enabled, - port, - admin.pingPath, - admin.versionPath, - admin.healthCheckPath, - admin.threadDumpPath) + AdminConfig(enabled, + port, + admin.pingPath, + admin.versionPath, + admin.healthCheckPath, + admin.threadDumpPath) fun newAdminConfig(enabled: Boolean, port: Int, admin: ConfigVals.Agent.Admin) = - AdminConfig(enabled, + AdminConfig(enabled, port, admin.pingPath, admin.versionPath, diff --git a/src/main/kotlin/io/prometheus/common/GrpcObjects.kt b/src/main/kotlin/io/prometheus/common/GrpcObjects.kt index 742e24cc..add318f6 100644 --- a/src/main/kotlin/io/prometheus/common/GrpcObjects.kt +++ b/src/main/kotlin/io/prometheus/common/GrpcObjects.kt @@ -19,23 +19,7 @@ package io.prometheus.common import com.google.protobuf.ByteString -import io.prometheus.grpc.AgentInfo -import io.prometheus.grpc.ChunkData -import io.prometheus.grpc.ChunkedScrapeResponse -import io.prometheus.grpc.HeaderData -import io.prometheus.grpc.HeartBeatRequest -import io.prometheus.grpc.HeartBeatResponse -import io.prometheus.grpc.PathMapSizeRequest -import io.prometheus.grpc.PathMapSizeResponse -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.SummaryData -import io.prometheus.grpc.UnregisterPathRequest -import io.prometheus.grpc.UnregisterPathResponse +import io.prometheus.grpc.* import java.util.zip.CRC32 internal object GrpcObjects { @@ -44,59 +28,52 @@ internal object GrpcObjects { const val EMPTY_PATH = "Empty path" fun newHeartBeatRequest(agentId: String): HeartBeatRequest = - HeartBeatRequest.newBuilder().run { - this.agentId = agentId - build() - } + heartBeatRequest { + this.agentId = agentId + } fun newHeartBeatResponse(valid: Boolean, reason: String): HeartBeatResponse = - HeartBeatResponse.newBuilder().run { - this.valid = valid - this.reason = reason - build() - } + heartBeatResponse { + this.valid = valid + this.reason = reason + } fun newRegisterAgentRequest(agentId: String, agentName: String, hostName: String): RegisterAgentRequest { require(agentId.isNotEmpty()) { EMPTY_AGENTID } - return RegisterAgentRequest.newBuilder().run { + return registerAgentRequest { this.agentId = agentId this.agentName = agentName this.hostName = hostName - build() } } fun newRegisterAgentResponse(valid: Boolean, reason: String, agentId: String): RegisterAgentResponse { require(agentId.isNotEmpty()) { EMPTY_AGENTID } - return RegisterAgentResponse.newBuilder().run { + return registerAgentResponse { this.valid = valid this.reason = reason this.agentId = agentId - build() } } fun newPathMapSizeRequest(agentId: String): PathMapSizeRequest { require(agentId.isNotEmpty()) { EMPTY_AGENTID } - return PathMapSizeRequest.newBuilder().run { + return pathMapSizeRequest { this.agentId = agentId - build() } } fun newPathMapSizeResponse(pathCount: Int): PathMapSizeResponse = - PathMapSizeResponse.newBuilder().run { - this.pathCount = pathCount - build() - } + pathMapSizeResponse { + this.pathCount = pathCount + } fun newRegisterPathRequest(agentId: String, path: String): RegisterPathRequest { require(agentId.isNotEmpty()) { EMPTY_AGENTID } require(path.isNotEmpty()) { EMPTY_PATH } - return RegisterPathRequest.newBuilder().run { + return registerPathRequest { this.agentId = agentId this.path = path - build() } } @@ -104,13 +81,12 @@ internal object GrpcObjects { reason: String, pathCount: Int, pathId: Long): RegisterPathResponse = - RegisterPathResponse.newBuilder().run { - this.valid = valid - this.reason = reason - this.pathCount = pathCount - this.pathId = pathId - build() - } + registerPathResponse { + this.valid = valid + this.reason = reason + this.pathCount = pathCount + this.pathId = pathId + } fun newScrapeRequest(agentId: String, scrapeId: Long, @@ -119,120 +95,210 @@ internal object GrpcObjects { accept: String?, debugEnabled: Boolean): ScrapeRequest { require(agentId.isNotEmpty()) { EMPTY_AGENTID } - return ScrapeRequest.newBuilder().let { builder -> - builder.agentId = agentId - builder.scrapeId = scrapeId - builder.path = path - builder.encodedQueryParams = encodedQueryParams - builder.debugEnabled = debugEnabled + return scrapeRequest { + this.agentId = agentId + this.scrapeId = scrapeId + this.path = path + this.encodedQueryParams = encodedQueryParams + this.debugEnabled = debugEnabled if (!accept.isNullOrBlank()) - builder.accept = accept - builder.build() + this.accept = accept } } fun ScrapeResponse.toScrapeResults(): ScrapeResults = - ScrapeResults( - agentId = agentId, - scrapeId = scrapeId, - validResponse = validResponse, - statusCode = statusCode, - contentType = contentType, - zipped = zipped, - failureReason = failureReason, - url = url - ).also { results -> - if (zipped) - results.contentAsZipped = contentAsZipped.toByteArray() - else - results.contentAsText = contentAsText - } + ScrapeResults( + agentId = agentId, + scrapeId = scrapeId, + validResponse = validResponse, + statusCode = statusCode, + contentType = contentType, + zipped = zipped, + failureReason = failureReason, + url = url + ).also { results -> + if (zipped) + results.contentAsZipped = contentAsZipped.toByteArray() + else + results.contentAsText = contentAsText + } fun ScrapeResults.toScrapeResponse(): ScrapeResponse = - ScrapeResponse.newBuilder().let { builder -> - builder.agentId = agentId - builder.scrapeId = scrapeId - builder.validResponse = validResponse - builder.statusCode = statusCode - builder.contentType = contentType - builder.zipped = zipped - if (zipped) - builder.contentAsZipped = ByteString.copyFrom(contentAsZipped) - else - builder.contentAsText = contentAsText - builder.failureReason = failureReason - builder.url = url - builder.build() - } + scrapeResponse { + val other = this@toScrapeResponse + agentId = other.agentId + scrapeId = other.scrapeId + validResponse = other.validResponse + statusCode = other.statusCode + contentType = other.contentType + zipped = other.zipped + if (zipped) + contentAsZipped = ByteString.copyFrom(other.contentAsZipped) + else + contentAsText = other.contentAsText + failureReason = other.failureReason + url = other.url + } fun ScrapeResults.toScrapeResponseHeader(): ChunkedScrapeResponse = - ChunkedScrapeResponse.newBuilder().let { builder -> - builder.header = - HeaderData.newBuilder().run { - headerAgentId = agentId - headerScrapeId = scrapeId - headerValidResponse = validResponse - headerStatusCode = statusCode - headerContentType = contentType - headerFailureReason = failureReason - headerUrl = url - build() - } - builder.build() - } + chunkedScrapeResponse { + header = + headerData { + val other = this@toScrapeResponseHeader + headerAgentId = other.agentId + headerScrapeId = other.scrapeId + headerValidResponse = other.validResponse + headerStatusCode = other.statusCode + headerContentType = other.contentType + headerFailureReason = other.failureReason + headerUrl = other.url + } + } fun newScrapeResponseChunk(scrapeId: Long, totalChunkCount: Int, readByteCount: Int, checksum: CRC32, buffer: ByteArray): ChunkedScrapeResponse = - ChunkedScrapeResponse.newBuilder().let { builder -> - builder.chunk = - ChunkData.newBuilder().run { - chunkScrapeId = scrapeId - chunkCount = totalChunkCount - chunkByteCount = readByteCount - chunkChecksum = checksum.value - chunkBytes = ByteString.copyFrom(buffer) - build() - } - builder.build() - } + chunkedScrapeResponse { + chunk = + chunkData { + chunkScrapeId = scrapeId + chunkCount = totalChunkCount + chunkByteCount = readByteCount + chunkChecksum = checksum.value + chunkBytes = ByteString.copyFrom(buffer) + } + } fun newScrapeResponseSummary(scrapeId: Long, totalChunkCount: Int, totalByteCount: Int, checksum: CRC32): ChunkedScrapeResponse = - ChunkedScrapeResponse.newBuilder().let { builder -> - builder.summary = - SummaryData.newBuilder().run { - summaryScrapeId = scrapeId - summaryChunkCount = totalChunkCount - summaryByteCount = totalByteCount - summaryChecksum = checksum.value - build() - } - builder.build() - } + chunkedScrapeResponse { + summary = + summaryData { + summaryScrapeId = scrapeId + summaryChunkCount = totalChunkCount + summaryByteCount = totalByteCount + summaryChecksum = checksum.value + } + } fun newUnregisterPathRequest(agentId: String, path: String): UnregisterPathRequest { require(agentId.isNotEmpty()) { EMPTY_AGENTID } require(path.isNotEmpty()) { EMPTY_PATH } - return UnregisterPathRequest.newBuilder().run { + return unregisterPathRequest { this.agentId = agentId this.path = path - build() } } - fun newUnregisterPathResponseBuilder(): UnregisterPathResponse.Builder = UnregisterPathResponse.newBuilder() - fun newAgentInfo(agentId: String): AgentInfo { require(agentId.isNotEmpty()) { EMPTY_AGENTID } - return AgentInfo.newBuilder().run { - this.agentId = agentId - build() - } + return agentInfo { this.agentId = agentId } } + + private fun heartBeatRequest(block: HeartBeatRequest.Builder.() -> Unit): HeartBeatRequest = + HeartBeatRequest.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun heartBeatResponse(block: HeartBeatResponse.Builder.() -> Unit): HeartBeatResponse = + HeartBeatResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun registerAgentRequest(block: RegisterAgentRequest.Builder.() -> Unit): RegisterAgentRequest = + RegisterAgentRequest.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun registerAgentResponse(block: RegisterAgentResponse.Builder.() -> Unit): RegisterAgentResponse = + RegisterAgentResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun pathMapSizeRequest(block: PathMapSizeRequest.Builder.() -> Unit): PathMapSizeRequest = + PathMapSizeRequest.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun pathMapSizeResponse(block: PathMapSizeResponse.Builder.() -> Unit): PathMapSizeResponse = + PathMapSizeResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun registerPathRequest(block: RegisterPathRequest.Builder.() -> Unit): RegisterPathRequest = + RegisterPathRequest.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun registerPathResponse(block: RegisterPathResponse.Builder.() -> Unit): RegisterPathResponse = + RegisterPathResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun scrapeRequest(block: ScrapeRequest.Builder.() -> Unit): ScrapeRequest = + ScrapeRequest.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun scrapeResponse(block: ScrapeResponse.Builder.() -> Unit): ScrapeResponse = + ScrapeResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun headerData(block: HeaderData.Builder.() -> Unit): HeaderData = + HeaderData.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun chunkData(block: ChunkData.Builder.() -> Unit): ChunkData = + ChunkData.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun summaryData(block: SummaryData.Builder.() -> Unit): SummaryData = + SummaryData.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun chunkedScrapeResponse(block: ChunkedScrapeResponse.Builder.() -> Unit): ChunkedScrapeResponse = + ChunkedScrapeResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun unregisterPathRequest(block: UnregisterPathRequest.Builder.() -> Unit): UnregisterPathRequest = + UnregisterPathRequest.newBuilder().let { + block.invoke(it) + it.build() + } + + internal fun unregisterPathResponse(block: UnregisterPathResponse.Builder.() -> Unit): UnregisterPathResponse = + UnregisterPathResponse.newBuilder().let { + block.invoke(it) + it.build() + } + + private fun agentInfo(block: AgentInfo.Builder.() -> Unit): AgentInfo = + AgentInfo.newBuilder().let { + block.invoke(it) + it.build() + } } diff --git a/src/main/kotlin/io/prometheus/common/Utils.kt b/src/main/kotlin/io/prometheus/common/Utils.kt index 36748d7d..206dd018 100644 --- a/src/main/kotlin/io/prometheus/common/Utils.kt +++ b/src/main/kotlin/io/prometheus/common/Utils.kt @@ -24,12 +24,12 @@ import io.prometheus.Proxy import kotlin.system.exitProcess internal fun getVersionDesc(asJson: Boolean = false): String = - Proxy::class.java.`package`.getAnnotation(VersionAnnotation::class.java).run { - if (asJson) - """{"Version": "$version", "Release Date": "$date"}""" - else - "Version: $version Release Date: $date" - } + Proxy::class.java.`package`.getAnnotation(VersionAnnotation::class.java).run { + if (asJson) + """{"Version": "$version", "Release Date": "$date"}""" + else + "Version: $version Release Date: $date" + } internal class VersionValidator : IParameterValidator { override fun validate(name: String, value: String) { diff --git a/src/main/kotlin/io/prometheus/proxy/AgentContext.kt b/src/main/kotlin/io/prometheus/proxy/AgentContext.kt index bdf693e9..6ffdcbe2 100644 --- a/src/main/kotlin/io/prometheus/proxy/AgentContext.kt +++ b/src/main/kotlin/io/prometheus/proxy/AgentContext.kt @@ -64,7 +64,8 @@ internal class AgentContext(private val remoteAddr: String) { } suspend fun readScrapeRequest(): ScrapeRequestWrapper? = - scrapeRequestChannel.receiveOrNull()?.apply { + scrapeRequestChannel.receiveOrNull() + ?.apply { channelBacklogSize.decrementAndGet() } @@ -85,15 +86,15 @@ internal class AgentContext(private val remoteAddr: String) { } override fun toString() = - toStringElements { - add("agentId", agentId) - add("valid", valid) - add("agentName", agentName) - add("hostName", hostName) - add("remoteAddr", remoteAddr) - add("lastRequestDuration", lastRequestDuration) - //add("inactivityDuration", inactivityDuration) - } + toStringElements { + add("agentId", agentId) + add("valid", valid) + add("agentName", agentName) + add("hostName", hostName) + add("remoteAddr", remoteAddr) + add("lastRequestDuration", lastRequestDuration) + //add("inactivityDuration", inactivityDuration) + } companion object { private val AGENT_ID_GENERATOR = AtomicLong(0) diff --git a/src/main/kotlin/io/prometheus/proxy/AgentContextCleanupService.kt b/src/main/kotlin/io/prometheus/proxy/AgentContextCleanupService.kt index 1fe8df02..c8de7942 100644 --- a/src/main/kotlin/io/prometheus/proxy/AgentContextCleanupService.kt +++ b/src/main/kotlin/io/prometheus/proxy/AgentContextCleanupService.kt @@ -43,23 +43,23 @@ internal class AgentContextCleanupService(private val proxy: Proxy, val pauseTime = configVals.staleAgentCheckPauseSecs.seconds while (isRunning) { proxy.agentContextManager.agentContextMap - .forEach { (agentId, agentContext) -> - val inactivityDuration = agentContext.inactivityDuration - if (inactivityDuration > maxInactivityTime) { - logger.info { "Evicting agent after $inactivityDuration of inactivity $agentContext" } - proxy.removeAgentContext(agentId) - proxy.metrics { agentEvictionCount.inc() } - } + .forEach { (agentId, agentContext) -> + val inactivityDuration = agentContext.inactivityDuration + if (inactivityDuration > maxInactivityTime) { + logger.info { "Evicting agent after $inactivityDuration of inactivity $agentContext" } + proxy.removeAgentContext(agentId) + proxy.metrics { agentEvictionCount.inc() } } + } sleep(pauseTime) } } override fun toString() = - toStringElements { - add("max inactivity secs", configVals.maxAgentInactivitySecs) - add("pause secs", configVals.staleAgentCheckPauseSecs) - } + toStringElements { + add("max inactivity secs", configVals.maxAgentInactivitySecs) + add("pause secs", configVals.staleAgentCheckPauseSecs) + } companion object : KLogging() } diff --git a/src/main/kotlin/io/prometheus/proxy/ChunkedContext.kt b/src/main/kotlin/io/prometheus/proxy/ChunkedContext.kt index 2d666261..01cae935 100644 --- a/src/main/kotlin/io/prometheus/proxy/ChunkedContext.kt +++ b/src/main/kotlin/io/prometheus/proxy/ChunkedContext.kt @@ -33,16 +33,16 @@ internal class ChunkedContext(response: ChunkedScrapeResponse) { private set val scrapeResults = - response.header.run { - ScrapeResults(validResponse = headerValidResponse, - scrapeId = headerScrapeId, - agentId = headerAgentId, - statusCode = headerStatusCode, - zipped = true, - failureReason = headerFailureReason, - url = headerUrl, - contentType = headerContentType) - } + response.header.run { + ScrapeResults(validResponse = headerValidResponse, + scrapeId = headerScrapeId, + agentId = headerAgentId, + statusCode = headerStatusCode, + zipped = true, + failureReason = headerFailureReason, + url = headerUrl, + contentType = headerContentType) + } fun applyChunk(data: ByteArray, chunkByteCount: Int, chunkCount: Int, chunkChecksum: Long) { totalChunkCount++ diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyGrpcService.kt b/src/main/kotlin/io/prometheus/proxy/ProxyGrpcService.kt index f3f04e36..ee76cb94 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyGrpcService.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyGrpcService.kt @@ -42,12 +42,12 @@ internal class ProxyGrpcService(private val proxy: Proxy, private val inProcessName: String = "") : GenericIdleService() { val healthCheck = - healthCheck { - if (grpcServer.isShutdown || grpcServer.isTerminated) - HealthCheck.Result.unhealthy("gRPC server is not running") - else - HealthCheck.Result.healthy() - } + healthCheck { + if (grpcServer.isShutdown || grpcServer.isTerminated) + HealthCheck.Result.unhealthy("gRPC server is not running") + else + HealthCheck.Result.healthy() + } private val grpcServer: Server @@ -57,24 +57,24 @@ internal class ProxyGrpcService(private val proxy: Proxy, init { val options = proxy.options val tlsContext = - if (options.certChainFilePath.isNotEmpty() || options.privateKeyFilePath.isNotEmpty()) - buildServerTlsContext(certChainFilePath = options.certChainFilePath, - privateKeyFilePath = options.privateKeyFilePath, - trustCertCollectionFilePath = options.trustCertCollectionFilePath) - else - PLAINTEXT_CONTEXT + if (options.certChainFilePath.isNotEmpty() || options.privateKeyFilePath.isNotEmpty()) + buildServerTlsContext(certChainFilePath = options.certChainFilePath, + privateKeyFilePath = options.privateKeyFilePath, + trustCertCollectionFilePath = options.trustCertCollectionFilePath) + else + PLAINTEXT_CONTEXT grpcServer = - server(port = port, - tlsContext = tlsContext, - inProcessServerName = inProcessName) { - val proxyService = ProxyServiceImpl(proxy) - val interceptors = mutableListOf(ProxyInterceptor()) - if (proxy.isZipkinEnabled) - interceptors += grpcTracing.newServerInterceptor() - addService(ServerInterceptors.intercept(proxyService.bindService(), interceptors)) - addTransportFilter(ProxyTransportFilter(proxy)) - } + server(port = port, + tlsContext = tlsContext, + inProcessServerName = inProcessName) { + val proxyService = ProxyServiceImpl(proxy) + val interceptors = mutableListOf(ProxyInterceptor()) + if (proxy.isZipkinEnabled) + interceptors += grpcTracing.newServerInterceptor() + addService(ServerInterceptors.intercept(proxyService.bindService(), interceptors)) + addTransportFilter(ProxyTransportFilter(proxy)) + } grpcServer.shutdownWithJvm(2.seconds) @@ -92,16 +92,16 @@ internal class ProxyGrpcService(private val proxy: Proxy, } override fun toString() = - toStringElements { - if (inProcessName.isNotEmpty()) { - add("serverType", "InProcess") - add("serverName", inProcessName) - } - else { - add("serverType", "Netty") - add("port", port) - } + toStringElements { + if (inProcessName.isNotEmpty()) { + add("serverType", "InProcess") + add("serverName", inProcessName) + } + else { + add("serverType", "Netty") + add("port", port) } + } companion object : KLogging() } diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt b/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt index b43f2dae..4821c3e9 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyHttpConfig.kt @@ -21,16 +21,8 @@ import io.ktor.application.Application import io.ktor.application.ApplicationCall import io.ktor.application.call import io.ktor.application.install -import io.ktor.features.Compression -import io.ktor.features.DefaultHeaders -import io.ktor.features.deflate -import io.ktor.features.gzip -import io.ktor.features.minimumSize -import io.ktor.http.ContentType -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpStatusCode -import io.ktor.http.formUrlEncode -import io.ktor.http.isSuccess +import io.ktor.features.* +import io.ktor.http.* import io.ktor.request.ApplicationRequest import io.ktor.request.header import io.ktor.request.path @@ -77,7 +69,7 @@ internal fun Application.configServer(proxy: Proxy) { ProxyHttpService.logger.debug { "Servicing request for path: $path" + - (if (encodedQueryParams.isNotEmpty()) " with query params $encodedQueryParams" else "") + (if (encodedQueryParams.isNotEmpty()) " with query params $encodedQueryParams" else "") } when { @@ -132,22 +124,22 @@ internal fun Application.configServer(proxy: Proxy) { else -> { submitScrapeRequest(proxy, path, encodedQueryParams, agentContext, call.request, call.response) - .also { response -> + .also { response -> - var status = "/$path - ${response.updateMsg} - ${response.statusCode}" - if (!response.statusCode.isSuccess()) - status += " reason: [${response.failureReason}]" - status += " time: ${response.fetchDuration} url: ${response.url}" + var status = "/$path - ${response.updateMsg} - ${response.statusCode}" + if (!response.statusCode.isSuccess()) + status += " reason: [${response.failureReason}]" + status += " time: ${response.fetchDuration} url: ${response.url}" - proxy.logActivity(status) + proxy.logActivity(status) - responseResults.apply { - statusCode = response.statusCode - contentType = response.contentType - contentText = response.contentText - updateMsg = response.updateMsg - } + responseResults.apply { + statusCode = response.statusCode + contentType = response.contentType + contentText = response.contentText + updateMsg = response.updateMsg } + } } } @@ -204,53 +196,54 @@ private suspend fun submitScrapeRequest(proxy: Proxy, updateMsg = "timed_out", fetchDuration = scrapeRequest.ageDuration()) } - } finally { + } + finally { val scrapeId = scrapeRequest.scrapeId proxy.scrapeRequestManager.removeFromScrapeRequestMap(scrapeId) - ?: ProxyHttpService.logger.error { "Scrape request $scrapeId missing in map" } + ?: ProxyHttpService.logger.error { "Scrape request $scrapeId missing in map" } } ProxyHttpService.logger.debug { "Results returned from $agentContext for $scrapeRequest" } scrapeRequest.scrapeResults - .also { scrapeResults -> - HttpStatusCode.fromValue(scrapeResults.statusCode) - .also { statusCode -> - scrapeResults.contentType.split("/") - .also { contentTypeElems -> - - val contentType = - if (contentTypeElems.size == 2) - ContentType(contentTypeElems[0], contentTypeElems[1]) - else - ContentType.Text.Plain - - // Do not return content on error status codes - return if (!statusCode.isSuccess()) { - scrapeRequest.scrapeResults.run { - ScrapeRequestResponse(statusCode = statusCode, - contentType = contentType, - failureReason = failureReason, - url = url, - updateMsg = "path_not_found", - fetchDuration = scrapeRequest.ageDuration()) - } - } - else { - scrapeRequest.scrapeResults.run { - // Unzip content here - ScrapeRequestResponse(statusCode = statusCode, - contentType = contentType, - contentText = if (zipped) contentAsZipped.unzip() else contentAsText, - failureReason = failureReason, - url = url, - updateMsg = "success", - fetchDuration = scrapeRequest.ageDuration()) - } - } - } + .also { scrapeResults -> + HttpStatusCode.fromValue(scrapeResults.statusCode) + .also { statusCode -> + scrapeResults.contentType.split("/") + .also { contentTypeElems -> + + val contentType = + if (contentTypeElems.size == 2) + ContentType(contentTypeElems[0], contentTypeElems[1]) + else + ContentType.Text.Plain + + // Do not return content on error status codes + return if (!statusCode.isSuccess()) { + scrapeRequest.scrapeResults.run { + ScrapeRequestResponse(statusCode = statusCode, + contentType = contentType, + failureReason = failureReason, + url = url, + updateMsg = "path_not_found", + fetchDuration = scrapeRequest.ageDuration()) + } + } + else { + scrapeRequest.scrapeResults.run { + // Unzip content here + ScrapeRequestResponse(statusCode = statusCode, + contentType = contentType, + contentText = if (zipped) contentAsZipped.unzip() else contentAsText, + failureReason = failureReason, + url = url, + updateMsg = "success", + fetchDuration = scrapeRequest.ageDuration()) + } + } } - } + } + } } private class ScrapeRequestResponse(val statusCode: HttpStatusCode, diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyHttpService.kt b/src/main/kotlin/io/prometheus/proxy/ProxyHttpService.kt index fe3507be..14180799 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyHttpService.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyHttpService.kt @@ -33,11 +33,12 @@ import kotlin.time.seconds internal class ProxyHttpService(private val proxy: Proxy, val httpPort: Int) : GenericIdleService() { private val proxyConfigVals = proxy.configVals.proxy private val idleTimeout = - if (proxyConfigVals.http.idleTimeoutSecs == -1) 45.seconds else proxyConfigVals.http.idleTimeoutSecs.seconds + if (proxyConfigVals.http.idleTimeoutSecs == -1) 45.seconds else proxyConfigVals.http.idleTimeoutSecs.seconds private val tracing by lazy { proxy.zipkinReporterService.newTracing("proxy-http") } - private val config: CIOApplicationEngine.Configuration.() -> Unit = { connectionIdleTimeoutSeconds = idleTimeout.inSeconds.toInt() } + private val config: CIOApplicationEngine.Configuration.() -> Unit = + { connectionIdleTimeoutSeconds = idleTimeout.inSeconds.toInt() } private val httpServer = embeddedServer(CIO, port = httpPort, configure = config) { configServer(proxy) } init { diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyInterceptor.kt b/src/main/kotlin/io/prometheus/proxy/ProxyInterceptor.kt index 70c84cc0..da0b4e9c 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyInterceptor.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyInterceptor.kt @@ -18,11 +18,7 @@ package io.prometheus.proxy -import io.grpc.ForwardingServerCall -import io.grpc.Metadata -import io.grpc.ServerCall -import io.grpc.ServerCallHandler -import io.grpc.ServerInterceptor +import io.grpc.* import io.prometheus.Proxy internal class ProxyInterceptor : ServerInterceptor { @@ -36,14 +32,14 @@ internal class ProxyInterceptor : ServerInterceptor { // logger.info {"Intercepting {}", methodName); return handler.startCall( - object : ForwardingServerCall.SimpleForwardingServerCall(call) { - override fun sendHeaders(headers: Metadata) { - // agent_id was assigned in ServerTransportFilter - attributes.get(Proxy.ATTRIB_AGENT_ID)?.also { headers.put(META_AGENT_ID, it) } - super.sendHeaders(headers) - } - }, - requestHeaders + object : ForwardingServerCall.SimpleForwardingServerCall(call) { + override fun sendHeaders(headers: Metadata) { + // agent_id was assigned in ServerTransportFilter + attributes.get(Proxy.ATTRIB_AGENT_ID)?.also { headers.put(META_AGENT_ID, it) } + super.sendHeaders(headers) + } + }, + requestHeaders ) } diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyMetrics.kt b/src/main/kotlin/io/prometheus/proxy/ProxyMetrics.kt index 4bf22bcc..15e5433e 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyMetrics.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyMetrics.kt @@ -27,29 +27,29 @@ import io.prometheus.Proxy internal class ProxyMetrics(proxy: Proxy) { val scrapeRequestCount = - counter { - name("proxy_scrape_requests") - help("Proxy scrape requests") - labelNames("type") - } + counter { + name("proxy_scrape_requests") + help("Proxy scrape requests") + labelNames("type") + } val connectCount = - counter { - name("proxy_connect_count") - help("Proxy connect count") - } + counter { + name("proxy_connect_count") + help("Proxy connect count") + } val agentEvictionCount = - counter { - name("proxy_eviction_count") - help("Proxy eviction count") - } + counter { + name("proxy_eviction_count") + help("Proxy eviction count") + } val heartbeatCount = - counter { - name("proxy_heartbeat_count") - help("Proxy heartbeat count") - } + counter { + name("proxy_heartbeat_count") + help("Proxy heartbeat count") + } val scrapeRequestLatency = summary { diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyOptions.kt b/src/main/kotlin/io/prometheus/proxy/ProxyOptions.kt index 44151236..ca4e020c 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyOptions.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyOptions.kt @@ -22,9 +22,7 @@ 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.AGENT_PORT -import io.prometheus.common.EnvVars.PROXY_CONFIG -import io.prometheus.common.EnvVars.PROXY_PORT +import io.prometheus.common.EnvVars.* class ProxyOptions(argv: Array) : BaseOptions(Proxy::class.java.simpleName, argv, PROXY_CONFIG.name) { @@ -33,6 +31,7 @@ class ProxyOptions(argv: Array) : BaseOptions(Proxy::class.java.simpleNa @Parameter(names = ["-p", "--port"], description = "Proxy listen port") var proxyHttpPort = -1 private set + @Parameter(names = ["-a", "--agent_port"], description = "gRPC listen port for Agents") var proxyAgentPort = -1 private set diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyPathManager.kt b/src/main/kotlin/io/prometheus/proxy/ProxyPathManager.kt index a18327e1..69534c8d 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyPathManager.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyPathManager.kt @@ -21,6 +21,7 @@ package io.prometheus.proxy import com.google.common.collect.Maps.newConcurrentMap import io.prometheus.common.GrpcObjects.EMPTY_AGENTID import io.prometheus.common.GrpcObjects.EMPTY_PATH +import io.prometheus.common.GrpcObjects.unregisterPathResponse import io.prometheus.grpc.UnregisterPathResponse import mu.KLogging import java.util.concurrent.ConcurrentMap @@ -45,16 +46,16 @@ internal class ProxyPathManager(private val isTestMode: Boolean) { } } - fun removePath(path: String, agentId: String, responseBuilder: UnregisterPathResponse.Builder) { + fun removePath(path: String, agentId: String): UnregisterPathResponse { require(path.isNotEmpty()) { EMPTY_PATH } require(agentId.isNotEmpty()) { EMPTY_AGENTID } synchronized(pathMap) { val agentContext = pathMap[path] - when { + return when { agentContext == null -> { val msg = "Unable to remove path /$path - path not found" logger.error { msg } - responseBuilder.apply { + unregisterPathResponse { valid = false reason = msg } @@ -62,7 +63,7 @@ internal class ProxyPathManager(private val isTestMode: Boolean) { agentContext.agentId != agentId -> { val msg = "Unable to remove path /$path - invalid agentId: $agentId (owner is ${agentContext.agentId})" logger.error { msg } - responseBuilder.apply { + unregisterPathResponse { valid = false reason = msg } @@ -71,7 +72,7 @@ internal class ProxyPathManager(private val isTestMode: Boolean) { pathMap.remove(path) if (!isTestMode) logger.info { "Removed path /$path for $agentContext" } - responseBuilder.apply { + unregisterPathResponse { valid = true reason = "" } @@ -86,23 +87,23 @@ internal class ProxyPathManager(private val isTestMode: Boolean) { pathMap.forEach { (k, v) -> if (v.agentId == agentId) pathMap.remove(k)?.also { if (!isTestMode) logger.info { "Removed path /$k for context: $it" } } - ?: logger.error { "Missing path /$k for agentId: $agentId" } + ?: logger.error { "Missing path /$k for agentId: $agentId" } } } } fun toPlainText() = - if (pathMap.isEmpty()) { - "No agents connected." - } - else { - val maxPath = pathMap.keys.map { it.length }.max() ?: 0 - "Proxy Path Map:\n" + "Path".padEnd(maxPath + 2) + "Agent Context\n" + - pathMap - .toSortedMap() - .map { c -> "/${c.key.padEnd(maxPath)} ${c.value}" } - .joinToString("\n") - } + if (pathMap.isEmpty()) { + "No agents connected." + } + else { + val maxPath = pathMap.keys.map { it.length }.max() ?: 0 + "Proxy Path Map:\n" + "Path".padEnd(maxPath + 2) + "Agent Context\n" + + pathMap + .toSortedMap() + .map { c -> "/${c.key.padEnd(maxPath)} ${c.value}" } + .joinToString("\n") + } companion object : KLogging() } \ No newline at end of file diff --git a/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt b/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt index 9208f046..4ad1934f 100644 --- a/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt +++ b/src/main/kotlin/io/prometheus/proxy/ProxyServiceImpl.kt @@ -18,69 +18,45 @@ package io.prometheus.proxy -import com.github.pambrose.common.dsl.GrpcDsl.streamObserver 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.common.GrpcObjects.newHeartBeatResponse import io.prometheus.common.GrpcObjects.newPathMapSizeResponse import io.prometheus.common.GrpcObjects.newRegisterAgentResponse import io.prometheus.common.GrpcObjects.newRegisterPathResponse -import io.prometheus.common.GrpcObjects.newUnregisterPathResponseBuilder import io.prometheus.common.GrpcObjects.toScrapeResults -import io.prometheus.grpc.AgentInfo -import io.prometheus.grpc.ChunkedScrapeResponse -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 kotlinx.coroutines.runBlocking +import io.prometheus.common.GrpcObjects.unregisterPathResponse +import io.prometheus.grpc.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flow import mu.KLogging import java.util.concurrent.atomic.AtomicLong -internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpc.ProxyServiceImplBase() { +internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpcKt.ProxyServiceCoroutineImplBase() { - override fun connectAgent(request: Empty, responseObserver: StreamObserver) { + override suspend fun connectAgent(request: Empty): Empty { proxy.metrics { connectCount.inc() } - - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() - } + return Empty.getDefaultInstance() } - override fun registerAgent(request: RegisterAgentRequest, - responseObserver: StreamObserver) { + override suspend fun registerAgent(request: RegisterAgentRequest): RegisterAgentResponse { val agentId = request.agentId var valid = false proxy.agentContextManager.getAgentContext(agentId) - ?.apply { - valid = true - agentName = request.agentName - hostName = request.hostName - markActivityTime(false) - logger.info { "Connected to $this" } - } ?: logger.info { "registerAgent() missing AgentContext agentId: $agentId" } - - responseObserver.apply { - onNext(newRegisterAgentResponse(valid, "Invalid agentId: $agentId", agentId)) - onCompleted() - } + ?.apply { + valid = true + agentName = request.agentName + hostName = request.hostName + markActivityTime(false) + logger.info { "Connected to $this" } + } ?: logger.info { "registerAgent() missing AgentContext agentId: $agentId" } + + return newRegisterAgentResponse(valid, "Invalid agentId: $agentId", agentId) } - override fun registerPath(request: RegisterPathRequest, - responseObserver: StreamObserver) { + override suspend fun registerPath(request: RegisterPathRequest): RegisterPathResponse { val path = request.path if (path in proxy.pathManager) logger.info { "Overwriting path /$path" } @@ -94,167 +70,113 @@ internal class ProxyServiceImpl(private val proxy: Proxy) : ProxyServiceGrpc.Pro markActivityTime(false) } ?: logger.error { "Missing AgentContext for agentId: $agentId" } - responseObserver.apply { - onNext( - newRegisterPathResponse(valid, - "Invalid agentId: $agentId", - proxy.pathManager.pathMapSize, - if (valid) PATH_ID_GENERATOR.getAndIncrement() else -1) - ) - onCompleted() - } + return newRegisterPathResponse(valid, + "Invalid agentId: $agentId", + proxy.pathManager.pathMapSize, + if (valid) PATH_ID_GENERATOR.getAndIncrement() else -1) } - override fun unregisterPath(request: UnregisterPathRequest, - responseObserver: StreamObserver) { + override suspend fun unregisterPath(request: UnregisterPathRequest): UnregisterPathResponse { val agentId = request.agentId val agentContext = proxy.agentContextManager.getAgentContext(agentId) - val responseBuilder = newUnregisterPathResponseBuilder() - if (agentContext == null) { + return if (agentContext == null) { logger.error { "Missing AgentContext for agentId: $agentId" } - responseBuilder.apply { + unregisterPathResponse { valid = false reason = "Invalid agentId: $agentId" } } else { - proxy.pathManager.removePath(request.path, agentId, responseBuilder) - agentContext.markActivityTime(false) - } - - responseObserver.apply { - onNext(responseBuilder.build()) - onCompleted() + proxy.pathManager.removePath(request.path, agentId).apply { agentContext.markActivityTime(false) } } } - override fun pathMapSize(request: PathMapSizeRequest, responseObserver: StreamObserver) { - responseObserver.apply { - onNext(newPathMapSizeResponse(proxy.pathManager.pathMapSize)) - onCompleted() - } - } + override suspend fun pathMapSize(request: PathMapSizeRequest) = + newPathMapSizeResponse(proxy.pathManager.pathMapSize) - override fun sendHeartBeat(request: HeartBeatRequest, responseObserver: StreamObserver) { + override suspend fun sendHeartBeat(request: HeartBeatRequest): HeartBeatResponse { proxy.metrics { heartbeatCount.inc() } val agentContext = proxy.agentContextManager.getAgentContext(request.agentId) agentContext?.markActivityTime(false) - ?: logger.info { "sendHeartBeat() missing AgentContext agentId: ${request.agentId}" } - responseObserver.apply { - onNext(newHeartBeatResponse(agentContext != null, "Invalid agentId: ${request.agentId}")) - onCompleted() - } + ?: logger.info { "sendHeartBeat() missing AgentContext agentId: ${request.agentId}" } + return newHeartBeatResponse(agentContext != null, "Invalid agentId: ${request.agentId}") } - override fun readRequestsFromProxy(agentInfo: AgentInfo, responseObserver: StreamObserver) { - responseObserver.also { observer -> - proxy.agentContextManager.getAgentContext(agentInfo.agentId) - ?.also { agentContext -> - runBlocking { - while (proxy.isRunning && agentContext.isValid()) - agentContext.readScrapeRequest()?.apply { - observer.onNext(scrapeRequest) - } - } - } - observer.onCompleted() + override fun readRequestsFromProxy(request: AgentInfo): Flow { + return flow { + proxy.agentContextManager.getAgentContext(request.agentId) + ?.also { agentContext -> + while (proxy.isRunning && agentContext.isValid()) + agentContext.readScrapeRequest()?.apply { emit(scrapeRequest) } + } } } - override fun writeResponsesToProxy(responseObserver: StreamObserver): StreamObserver = - streamObserver { - onNext { response -> - val scrapeResults = response.toScrapeResults() - proxy.scrapeRequestManager.assignScrapeResults(scrapeResults) - } - - onError { throwable -> - if (proxy.isRunning) - Status.fromThrowable(throwable).also { arg -> - if (arg.code != Status.Code.CANCELLED) - logger.error(throwable) { "Error in writeResponsesToProxy(): $arg" } - } - - try { - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() - } - } catch (e: StatusRuntimeException) { - // logger.warn(e) {"StatusRuntimeException"}; - // Ignore + override suspend fun writeResponsesToProxy(requests: Flow): Empty { + try { + requests.collect { response -> + val scrapeResults = response.toScrapeResults() + proxy.scrapeRequestManager.assignScrapeResults(scrapeResults) + } + } + catch (throwable: Throwable) { + if (proxy.isRunning) + Status.fromThrowable(throwable) + .also { arg -> + if (arg.code != Status.Code.CANCELLED) + logger.error(throwable) { "Error in writeResponsesToProxy(): $arg" } } - } + } + return Empty.getDefaultInstance() + } - onCompleted { - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() + override suspend fun writeChunkedResponsesToProxy(requests: Flow): Empty { + try { + requests.collect { response -> + val ooc = response.chunkOneOfCase + val chunkedContextMap = proxy.agentContextManager.chunkedContextMap + when (ooc.name.toLowerCase()) { + "header" -> { + val scrapeId = response.header.headerScrapeId + logger.debug { "Reading header for scrapeId: $scrapeId}" } + chunkedContextMap[scrapeId] = ChunkedContext(response) } - } - } - - override fun writeChunkedResponsesToProxy(responseObserver: StreamObserver): StreamObserver = - streamObserver { - - onNext { response -> - val ooc = response.chunkOneOfCase - val chunkedContextMap = proxy.agentContextManager.chunkedContextMap - when (ooc.name.toLowerCase()) { - "header" -> { - val scrapeId = response.header.headerScrapeId - logger.debug { "Reading header for scrapeId: $scrapeId}" } - chunkedContextMap[scrapeId] = ChunkedContext(response) - } - "chunk" -> { - response.chunk.apply { + "chunk" -> { + response.chunk + .apply { logger.debug { "Reading chunk $chunkCount for scrapeId: $chunkScrapeId" } val context = chunkedContextMap[chunkScrapeId] check(context != null) { "Missing chunked context with scrapeId: $chunkScrapeId" } context.applyChunk(chunkBytes.toByteArray(), chunkByteCount, chunkCount, chunkChecksum) } - } - "summary" -> { - response.summary.apply { + } + "summary" -> { + response.summary + .apply { val context = chunkedContextMap.remove(summaryScrapeId) check(context != null) { "Missing chunked context with scrapeId: $summaryScrapeId" } logger.debug { "Reading summary chunkCount: ${context.totalChunkCount} byteCount: ${context.totalByteCount} for scrapeId: $summaryScrapeId" } context.applySummary(summaryChunkCount, summaryByteCount, summaryChecksum) proxy.scrapeRequestManager.assignScrapeResults(context.scrapeResults) } - } - else -> throw IllegalStateException("Invalid field name in writeChunkedResponsesToProxy()") - } - } - - onError { throwable -> - if (proxy.isRunning) - Status.fromThrowable(throwable).also { arg -> - if (arg.code != Status.Code.CANCELLED) - logger.error(throwable) { "Error in writeChunkedResponsesToProxy(): $arg" } - } - - try { - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() - } - } catch (e: StatusRuntimeException) { - // logger.warn(e) {"StatusRuntimeException"}; - // Ignore - } - } - - onCompleted { - responseObserver.apply { - onNext(Empty.getDefaultInstance()) - onCompleted() } + else -> throw IllegalStateException("Invalid field name in writeChunkedResponsesToProxy()") } } + } + catch (throwable: Throwable) { + if (proxy.isRunning) + Status.fromThrowable(throwable) + .also { arg -> + if (arg.code != Status.Code.CANCELLED) + logger.error(throwable) { "Error in writeChunkedResponsesToProxy(): $arg" } + } + } + return Empty.getDefaultInstance() + } companion object : KLogging() { private val PATH_ID_GENERATOR = AtomicLong(0) } -} +} \ No newline at end of file diff --git a/src/main/kotlin/io/prometheus/proxy/ScrapeRequestManager.kt b/src/main/kotlin/io/prometheus/proxy/ScrapeRequestManager.kt index dfdc426a..ab8ce31c 100644 --- a/src/main/kotlin/io/prometheus/proxy/ScrapeRequestManager.kt +++ b/src/main/kotlin/io/prometheus/proxy/ScrapeRequestManager.kt @@ -39,11 +39,11 @@ internal class ScrapeRequestManager { fun assignScrapeResults(scrapeResults: ScrapeResults) { val scrapeId = scrapeResults.scrapeId scrapeRequestMap[scrapeId] - ?.also { wrapper -> - wrapper.scrapeResults = scrapeResults - wrapper.markComplete() - wrapper.agentContext.markActivityTime(true) - } ?: logger.error { "Missing ScrapeRequestWrapper for scrape_id: $scrapeId" } + ?.also { wrapper -> + wrapper.scrapeResults = scrapeResults + wrapper.markComplete() + wrapper.agentContext.markActivityTime(true) + } ?: logger.error { "Missing ScrapeRequestWrapper for scrape_id: $scrapeId" } } fun removeFromScrapeRequestMap(scrapeId: Long): ScrapeRequestWrapper? { diff --git a/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt b/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt index 3ba68a36..77eae6f0 100644 --- a/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt +++ b/src/main/kotlin/io/prometheus/proxy/ScrapeRequestWrapper.kt @@ -66,7 +66,8 @@ internal class ScrapeRequestWrapper(proxy: Proxy, try { completeChannel.receive() true - } catch (e: ClosedReceiveChannelException) { + } + catch (e: ClosedReceiveChannelException) { true } } != null diff --git a/src/test/kotlin/io/prometheus/AdminDefaultPathTest.kt b/src/test/kotlin/io/prometheus/AdminDefaultPathTest.kt index 90c40938..d22698f5 100644 --- a/src/test/kotlin/io/prometheus/AdminDefaultPathTest.kt +++ b/src/test/kotlin/io/prometheus/AdminDefaultPathTest.kt @@ -39,76 +39,76 @@ class AdminDefaultPathTest { @Test fun proxyPingPathTest() { proxyConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText() shouldStartWith "pong" - } + .also { admin -> + blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText() shouldStartWith "pong" } + } } @Test fun agentPingPathTest() { agentConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText() shouldStartWith "pong" - } + .also { admin -> + blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText() shouldStartWith "pong" } + } } @Test fun proxyVersionPathTest() { agentConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText() shouldContain "Version" - } + .also { admin -> + blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText() shouldContain "Version" } + } } @Test fun agentVersionPathTest() { agentConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText() shouldContain "Version" - } + .also { admin -> + blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText() shouldContain "Version" } + } } @Test fun proxyHealthCheckPathTest() { proxyConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText().length shouldBeGreaterThan 10 - } + .also { admin -> + blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText().length shouldBeGreaterThan 10 } + } } @Test fun agentHealthCheckPathTest() { agentConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> - response.readText().length shouldBeGreaterThan 10 - } + .also { admin -> + blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> + response.readText().length shouldBeGreaterThan 10 } + } } @Test fun proxyThreadDumpPathTest() { proxyConfigVals.admin - .also { admin -> - blockingGet("${admin.port}/${admin.threadDumpPath}".addPrefix()) { response -> - response.readText().length shouldBeGreaterThan 10 - } + .also { admin -> + blockingGet("${admin.port}/${admin.threadDumpPath}".addPrefix()) { response -> + response.readText().length shouldBeGreaterThan 10 } + } } @Test diff --git a/src/test/kotlin/io/prometheus/AdminEmptyPathTest.kt b/src/test/kotlin/io/prometheus/AdminEmptyPathTest.kt index 8cf921ed..581dfa81 100644 --- a/src/test/kotlin/io/prometheus/AdminEmptyPathTest.kt +++ b/src/test/kotlin/io/prometheus/AdminEmptyPathTest.kt @@ -35,51 +35,51 @@ class AdminEmptyPathTest { @Test fun proxyPingPathTest() { proxyConfigVals.admin - .also { admin -> - admin.port shouldBeEqualTo 8098 - admin.pingPath shouldBeEqualTo "" + .also { admin -> + admin.port shouldBeEqualTo 8098 + admin.pingPath shouldBeEqualTo "" - blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.NotFound - } + blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.NotFound } + } } @Test fun proxyVersionPathTest() { proxyConfigVals.admin - .also { admin -> - admin.port shouldBeEqualTo 8098 - admin.versionPath shouldBeEqualTo "" + .also { admin -> + admin.port shouldBeEqualTo 8098 + admin.versionPath shouldBeEqualTo "" - blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.NotFound - } + blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.NotFound } + } } @Test fun proxyHealthCheckPathTest() { proxyConfigVals.admin - .also { admin -> - admin.healthCheckPath shouldBeEqualTo "" + .also { admin -> + admin.healthCheckPath shouldBeEqualTo "" - blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.NotFound - } + blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.NotFound } + } } @Test fun proxyThreadDumpPathTest() { proxyConfigVals.admin - .also { admin -> - admin.threadDumpPath shouldBeEqualTo "" + .also { admin -> + admin.threadDumpPath shouldBeEqualTo "" - blockingGet("${admin.port}/${admin.threadDumpPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.NotFound - } + blockingGet("${admin.port}/${admin.threadDumpPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.NotFound } + } } companion object : CommonCompanion() { diff --git a/src/test/kotlin/io/prometheus/AdminNonDefaultPathTest.kt b/src/test/kotlin/io/prometheus/AdminNonDefaultPathTest.kt index d2d01b9a..735efd08 100644 --- a/src/test/kotlin/io/prometheus/AdminNonDefaultPathTest.kt +++ b/src/test/kotlin/io/prometheus/AdminNonDefaultPathTest.kt @@ -39,54 +39,54 @@ class AdminNonDefaultPathTest { @Test fun proxyPingPathTest() { proxyConfigVals.admin - .also { admin -> - admin.port shouldBeEqualTo 8099 - admin.pingPath shouldBeEqualTo "pingPath2" - - blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText() shouldStartWith "pong" - } + .also { admin -> + admin.port shouldBeEqualTo 8099 + admin.pingPath shouldBeEqualTo "pingPath2" + + blockingGet("${admin.port}/${admin.pingPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText() shouldStartWith "pong" } + } } @Test fun proxyVersionPathTest() { proxyConfigVals.admin - .also { admin -> - admin.port shouldBeEqualTo 8099 - admin.versionPath shouldBeEqualTo "versionPath2" - - blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText() shouldContain "Version" - } + .also { admin -> + admin.port shouldBeEqualTo 8099 + admin.versionPath shouldBeEqualTo "versionPath2" + + blockingGet("${admin.port}/${admin.versionPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText() shouldContain "Version" } + } } @Test fun proxyHealthCheckPathTest() { proxyConfigVals.admin - .also { admin -> - admin.healthCheckPath shouldBeEqualTo "healthCheckPath2" + .also { admin -> + admin.healthCheckPath shouldBeEqualTo "healthCheckPath2" - blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> - response.status shouldBeEqualTo HttpStatusCode.OK - response.readText().length shouldBeGreaterThan 10 - } + blockingGet("${admin.port}/${admin.healthCheckPath}".addPrefix()) { response -> + response.status shouldBeEqualTo HttpStatusCode.OK + response.readText().length shouldBeGreaterThan 10 } + } } @Test fun proxyThreadDumpPathTest() { proxyConfigVals.admin - .also { admin -> - admin.threadDumpPath shouldBeEqualTo "threadDumpPath2" + .also { admin -> + admin.threadDumpPath shouldBeEqualTo "threadDumpPath2" - blockingGet("${admin.port}/${admin.threadDumpPath}".addPrefix()) { response -> - response.readText().length shouldBeGreaterThan 10 - } + blockingGet("${admin.port}/${admin.threadDumpPath}".addPrefix()) { response -> + response.readText().length shouldBeGreaterThan 10 } + } } companion object : CommonCompanion() { diff --git a/src/test/kotlin/io/prometheus/CommonCompanion.kt b/src/test/kotlin/io/prometheus/CommonCompanion.kt index 303f89e0..9124fc73 100644 --- a/src/test/kotlin/io/prometheus/CommonCompanion.kt +++ b/src/test/kotlin/io/prometheus/CommonCompanion.kt @@ -35,8 +35,13 @@ open class CommonCompanion : KLogging() { CollectorRegistry.defaultRegistry.clear() runBlocking { - launch(Dispatchers.Default) { proxy = proxySetup.invoke() } - launch(Dispatchers.Default) { agent = agentSetup.invoke().apply { awaitInitialConnection(10.seconds) } } + launch(Dispatchers.Default + exceptionHandler(logger)) { + proxy = proxySetup.invoke() + } + + launch(Dispatchers.Default + exceptionHandler(logger)) { + agent = agentSetup.invoke().apply { awaitInitialConnection(10.seconds) } + } } actions.invoke() @@ -48,7 +53,7 @@ open class CommonCompanion : KLogging() { runBlocking { for (service in listOf(proxy, agent)) { logger.info { "Stopping ${service.simpleClassName}" } - launch(Dispatchers.Default) { service.stopSync() } + launch(Dispatchers.Default + exceptionHandler(logger)) { service.stopSync() } } } diff --git a/src/test/kotlin/io/prometheus/CommonTests.kt b/src/test/kotlin/io/prometheus/CommonTests.kt index 335a71cf..37bb7faa 100644 --- a/src/test/kotlin/io/prometheus/CommonTests.kt +++ b/src/test/kotlin/io/prometheus/CommonTests.kt @@ -25,12 +25,13 @@ import io.prometheus.SimpleTests.invalidAgentUrlTest import io.prometheus.SimpleTests.invalidPathTest import io.prometheus.SimpleTests.missingPathTest import io.prometheus.SimpleTests.threadedAddRemovePathsTest +import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test abstract class CommonTests(private val args: ProxyCallTestArgs) { @Test - fun proxyCallTest() = ProxyTests.proxyCallTest(args) + fun proxyCallTest() = runBlocking { ProxyTests.proxyCallTest(args) } @Test fun missingPathTest() = missingPathTest(simpleClassName) @@ -39,21 +40,23 @@ abstract class CommonTests(private val args: ProxyCallTestArgs) { fun invalidPathTest() = invalidPathTest(simpleClassName) @Test - fun addRemovePathsTest() = addRemovePathsTest(args.agent.pathManager, simpleClassName) + fun addRemovePathsTest() = runBlocking { addRemovePathsTest(args.agent.pathManager, simpleClassName) } @Test - fun threadedAddRemovePathsTest() = threadedAddRemovePathsTest(args.agent.pathManager, simpleClassName) + fun threadedAddRemovePathsTest() = runBlocking { threadedAddRemovePathsTest(args.agent.pathManager, simpleClassName) } @Test - fun invalidAgentUrlTest() = invalidAgentUrlTest(args.agent.pathManager, simpleClassName) + fun invalidAgentUrlTest() = runBlocking { invalidAgentUrlTest(args.agent.pathManager, simpleClassName) } @Test - fun timeoutTest() = timeoutTest(args.agent.pathManager, simpleClassName) + fun timeoutTest() = runBlocking { timeoutTest(args.agent.pathManager, simpleClassName) } companion object { const val HTTP_SERVER_COUNT = 5 const val PATH_COUNT = 50 const val SEQUENTIAL_QUERY_COUNT = 200 - const val PARALLEL_QUERY_COUNT = 25 + const val PARALLEL_QUERY_COUNT = 10 + const val MIN_DELAY_MILLIS = 400 + const val MAX_DELAY_MILLIS = 600 } } \ No newline at end of file diff --git a/src/test/kotlin/io/prometheus/InProcessTestNoAdminMetricsTest.kt b/src/test/kotlin/io/prometheus/InProcessTestNoAdminMetricsTest.kt index 957bcb60..fabab7f8 100644 --- a/src/test/kotlin/io/prometheus/InProcessTestNoAdminMetricsTest.kt +++ b/src/test/kotlin/io/prometheus/InProcessTestNoAdminMetricsTest.kt @@ -29,11 +29,12 @@ class InProcessTestNoAdminMetricsTest : CommonTests(ProxyCallTestArgs(agent = ag caller = simpleClassName)) { companion object : CommonCompanion() { - @JvmStatic @BeforeAll - fun setUp() = setItUp({ startProxy("nometrics") }, - { startAgent(serverName = "nometrics", chunkContentSizeKbs = 5) }) + fun setUp() = + setItUp( + { startProxy("nometrics") }, + { startAgent(serverName = "nometrics", chunkContentSizeKbs = 5) }) @JvmStatic @AfterAll diff --git a/src/test/kotlin/io/prometheus/InProcessTestWithAdminMetricsTest.kt b/src/test/kotlin/io/prometheus/InProcessTestWithAdminMetricsTest.kt index 42f76f9c..d2de691d 100644 --- a/src/test/kotlin/io/prometheus/InProcessTestWithAdminMetricsTest.kt +++ b/src/test/kotlin/io/prometheus/InProcessTestWithAdminMetricsTest.kt @@ -24,21 +24,21 @@ import io.prometheus.TestUtils.startProxy import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll -class InProcessTestWithAdminMetricsTest : CommonTests(ProxyCallTestArgs(agent = agent, - startPort = 10700, - caller = simpleClassName)) { +class InProcessTestWithAdminMetricsTest : + CommonTests(ProxyCallTestArgs(agent = agent, startPort = 10700, caller = simpleClassName)) { companion object : CommonCompanion() { - @JvmStatic @BeforeAll - fun setUp() = setItUp({ startProxy("withmetrics", adminEnabled = true, metricsEnabled = true) }, - { - startAgent(serverName = "withmetrics", - adminEnabled = true, - metricsEnabled = true, - chunkContentSizeKbs = 5) - }) + fun setUp() = + setItUp( + { startProxy("withmetrics", adminEnabled = true, metricsEnabled = true) }, + { + startAgent(serverName = "withmetrics", + adminEnabled = true, + metricsEnabled = true, + chunkContentSizeKbs = 5) + }) @JvmStatic @AfterAll diff --git a/src/test/kotlin/io/prometheus/NettyTestWithAdminMetricsTest.kt b/src/test/kotlin/io/prometheus/NettyTestWithAdminMetricsTest.kt index c9467143..31720ad2 100644 --- a/src/test/kotlin/io/prometheus/NettyTestWithAdminMetricsTest.kt +++ b/src/test/kotlin/io/prometheus/NettyTestWithAdminMetricsTest.kt @@ -19,8 +19,7 @@ package io.prometheus import com.github.pambrose.common.dsl.KtorDsl.get -import com.github.pambrose.common.dsl.KtorDsl.http -import com.github.pambrose.common.dsl.KtorDsl.newHttpClient +import com.github.pambrose.common.dsl.KtorDsl.withHttpClient import com.github.pambrose.common.util.simpleClassName import com.github.pambrose.common.util.sleep import io.ktor.client.statement.readText @@ -41,29 +40,25 @@ class NettyTestWithAdminMetricsTest : CommonTests(ProxyCallTestArgs(agent = agen @Test fun adminDebugCallsTest() { - newHttpClient() - .use { httpClient -> - runBlocking { - http(httpClient) { - get("8093/debug".addPrefix()) { response -> - val body = response.readText() - body.length shouldBeGreaterThan 100 - response.status shouldBeEqualTo HttpStatusCode.OK - } - } + runBlocking { + withHttpClient { + get("8093/debug".addPrefix()) { response -> + val body = response.readText() + body.length shouldBeGreaterThan 100 + response.status shouldBeEqualTo HttpStatusCode.OK + } + } - http(httpClient) { - get("8092/debug".addPrefix()) { response -> - val body = response.readText() - body.length shouldBeGreaterThan 100 - response.status shouldBeEqualTo HttpStatusCode.OK - } - } - } + withHttpClient { + get("8092/debug".addPrefix()) { response -> + val body = response.readText() + body.length shouldBeGreaterThan 100 + response.status shouldBeEqualTo HttpStatusCode.OK } + } + } } - companion object : CommonCompanion() { @JvmStatic diff --git a/src/test/kotlin/io/prometheus/ProxyTests.kt b/src/test/kotlin/io/prometheus/ProxyTests.kt index 859fcf15..b59a96e6 100644 --- a/src/test/kotlin/io/prometheus/ProxyTests.kt +++ b/src/test/kotlin/io/prometheus/ProxyTests.kt @@ -21,8 +21,8 @@ package io.prometheus import com.github.pambrose.common.coroutine.delay import com.github.pambrose.common.dsl.KtorDsl.blockingGet import com.github.pambrose.common.dsl.KtorDsl.get -import com.github.pambrose.common.dsl.KtorDsl.http -import com.github.pambrose.common.dsl.KtorDsl.newHttpClient +import com.github.pambrose.common.dsl.KtorDsl.httpClient +import com.github.pambrose.common.dsl.KtorDsl.withHttpClient import com.github.pambrose.common.util.random import com.google.common.collect.Maps.newConcurrentMap import io.ktor.application.call @@ -37,17 +37,15 @@ import io.ktor.server.cio.CIO import io.ktor.server.cio.CIOApplicationEngine import io.ktor.server.engine.embeddedServer import io.prometheus.CommonTests.Companion.HTTP_SERVER_COUNT +import io.prometheus.CommonTests.Companion.MAX_DELAY_MILLIS +import io.prometheus.CommonTests.Companion.MIN_DELAY_MILLIS import io.prometheus.CommonTests.Companion.PARALLEL_QUERY_COUNT import io.prometheus.CommonTests.Companion.PATH_COUNT import io.prometheus.CommonTests.Companion.SEQUENTIAL_QUERY_COUNT import io.prometheus.TestConstants.PROXY_PORT import io.prometheus.agent.AgentPathManager import io.prometheus.agent.RequestFailureException -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeoutOrNull +import kotlinx.coroutines.* import mu.KLogging import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNull @@ -70,25 +68,25 @@ class ProxyCallTestArgs(val agent: Agent, internal object ProxyTests : KLogging() { - fun timeoutTest(pathManager: AgentPathManager, - caller: String, - agentPort: Int = 9900, - agentPath: String = "agent-timeout", - proxyPath: String = "proxy-timeout") { + suspend fun timeoutTest(pathManager: AgentPathManager, + caller: String, + agentPort: Int = 9900, + agentPath: String = "agent-timeout", + proxyPath: String = "proxy-timeout") { logger.debug { "Calling timeoutTest() from $caller" } val httpServer = - embeddedServer(CIO, port = agentPort) { - routing { - get("/$agentPath") { - delay(10.seconds) - call.respondText("This is never reached", Text.Plain) - } + embeddedServer(CIO, port = agentPort) { + routing { + get("/$agentPath") { + delay(10.seconds) + call.respondText("This is never reached", Text.Plain) } } + } - runBlocking { - launch(Dispatchers.Default) { + coroutineScope { + launch(Dispatchers.Default + exceptionHandler(logger)) { logger.info { "Starting httpServer" } httpServer.start() delay(5.seconds) @@ -96,26 +94,27 @@ internal object ProxyTests : KLogging() { } pathManager.registerPath("/$proxyPath", "$agentPort/$agentPath".addPrefix()) + blockingGet("$PROXY_PORT/$proxyPath".addPrefix()) { response -> response.status shouldBeEqualTo HttpStatusCode.ServiceUnavailable } + pathManager.unregisterPath("/$proxyPath") - runBlocking { - launch(Dispatchers.Default) { + coroutineScope { + launch(Dispatchers.Default + exceptionHandler(logger)) { logger.info { "Stopping httpServer" } httpServer.stop(5.seconds.toLongMilliseconds(), 5.seconds.toLongMilliseconds()) - delay(5.seconds) + delay(6.seconds) } } } - private class HttpServerWrapper(val port: Int, val server: CIOApplicationEngine) private val contentMap = mutableMapOf() - fun proxyCallTest(args: ProxyCallTestArgs) { + suspend fun proxyCallTest(args: ProxyCallTestArgs) { logger.info { "Calling proxyCallTest() from ${args.caller}" } val pathMap: ConcurrentMap = newConcurrentMap() @@ -126,39 +125,39 @@ internal object ProxyTests : KLogging() { // Create the endpoints logger.info { "Creating ${args.httpServerCount} httpServers" } val httpServers = - List(args.httpServerCount) { i -> - val port = args.startPort + i - - // Create fake content - val s = "This is the content for an endpoint for server# $i on $port\n" - val builder = StringBuilder() - val len = - when (i % 3) { - 0 -> 100_000 - 1 -> 1000 - else -> 1 - } - repeat(len) { builder.append(s + "${it}\n") } - contentMap[i] = builder.toString() - - HttpServerWrapper(port = port, - server = embeddedServer(CIO, port = port) { - routing { - get("/agent-$i") { - call.respondText(contentMap[i]!!, Text.Plain) - } + List(args.httpServerCount) { i -> + val port = args.startPort + i + + // Create fake content + val s = "This is the content for an endpoint for server# $i on $port\n" + val builder = StringBuilder() + val len = + when (i % 3) { + 0 -> 100_000 + 1 -> 1000 + else -> 1 + } + repeat(len) { builder.append(s + "${it}\n") } + contentMap[i] = builder.toString() + + HttpServerWrapper(port = port, + server = embeddedServer(CIO, port = port) { + routing { + get("/agent-$i") { + call.respondText(contentMap[i]!!, Text.Plain) } - }) - } + } + }) + } logger.debug { "Starting ${args.httpServerCount} httpServers" } - runBlocking { - httpServers.forEach { httpServer -> - launch(Dispatchers.Default) { - logger.info { "Starting httpServer listening on ${httpServer.port}" } - httpServer.server.start() - delay(2.seconds) + coroutineScope { + httpServers.forEach { wrapper -> + launch(Dispatchers.Default + exceptionHandler(logger)) { + logger.info { "Starting httpServer listening on ${wrapper.port}" } + wrapper.server.start() + delay(5.seconds) } } } @@ -178,57 +177,51 @@ internal object ProxyTests : KLogging() { // Call the proxy sequentially logger.info { "Calling proxy sequentially ${args.sequentialQueryCount} times" } Executors.newSingleThreadExecutor().asCoroutineDispatcher() - .use { dispatcher -> - runBlocking { - withTimeoutOrNull(1.minutes.toLongMilliseconds()) { - newHttpClient().also { httpClient -> - val counter = AtomicInteger(0) - repeat(args.sequentialQueryCount) { cnt -> - val job = - launch(dispatcher + coroutineExceptionHandler(logger)) { - callProxy(httpClient, pathMap, "Sequential $cnt") - counter.incrementAndGet() - } - - job.join() - job.getCancellationException().cause.shouldBeNull() - + .use { dispatcher -> + withTimeoutOrNull(1.minutes.toLongMilliseconds()) { + httpClient { client -> + val counter = AtomicInteger(0) + repeat(args.sequentialQueryCount) { cnt -> + val job = + launch(dispatcher + exceptionHandler(logger)) { + callProxy(client, pathMap, "Sequential $cnt") + counter.incrementAndGet() } - counter.get() shouldBeEqualTo args.sequentialQueryCount - } + job.join() + job.getCancellationException().cause.shouldBeNull() } + + counter.get() shouldBeEqualTo args.sequentialQueryCount } } + } // Call the proxy in parallel logger.info { "Calling proxy in parallel ${args.parallelQueryCount} times" } - Executors.newFixedThreadPool(10).asCoroutineDispatcher() - .use { dispatcher -> - runBlocking { - withTimeoutOrNull(1.minutes.toLongMilliseconds()) { - newHttpClient() - .also { httpClient -> - val counter = AtomicInteger(0) - val jobs = - List(args.parallelQueryCount) { cnt -> - launch(dispatcher + coroutineExceptionHandler(logger)) { - delay((200..400).random().milliseconds) - callProxy(httpClient, pathMap, "Parallel $cnt") - counter.incrementAndGet() - } - } - - jobs.forEach { job -> - job.join() - job.getCancellationException().cause.shouldBeNull() - } - - counter.get() shouldBeEqualTo args.parallelQueryCount - } + Executors.newFixedThreadPool(5).asCoroutineDispatcher() + .use { dispatcher -> + withTimeoutOrNull(1.minutes.toLongMilliseconds()) { + httpClient { client -> + val counter = AtomicInteger(0) + val jobs = + List(args.parallelQueryCount) { cnt -> + launch(dispatcher + exceptionHandler(logger)) { + delay((MIN_DELAY_MILLIS..MAX_DELAY_MILLIS).random().milliseconds) + callProxy(client, pathMap, "Parallel $cnt") + counter.incrementAndGet() + } + } + + jobs.forEach { job -> + job.join() + job.getCancellationException().cause.shouldBeNull() } + + counter.get() shouldBeEqualTo args.parallelQueryCount } } + } logger.debug { "Unregistering paths" } val counter = AtomicInteger(0) @@ -237,7 +230,8 @@ internal object ProxyTests : KLogging() { try { args.agent.pathManager.unregisterPath("proxy-${path.key}") counter.incrementAndGet() - } catch (e: RequestFailureException) { + } + catch (e: RequestFailureException) { errorCnt.incrementAndGet() } } @@ -247,9 +241,9 @@ internal object ProxyTests : KLogging() { args.agent.grpcService.pathMapSize() shouldBeEqualTo originalSize logger.info { "Shutting down ${httpServers.size} httpServers" } - runBlocking { + coroutineScope { httpServers.forEach { httpServer -> - launch(Dispatchers.Default) { + launch(Dispatchers.Default + exceptionHandler(logger)) { logger.info { "Shutting down httpServer listening on ${httpServer.port}" } httpServer.server.stop(5.seconds.toLongMilliseconds(), 5.seconds.toLongMilliseconds()) delay(5.seconds) @@ -267,7 +261,7 @@ internal object ProxyTests : KLogging() { val httpIndex = pathMap[index] httpIndex.shouldNotBeNull() - http(httpClient) { + withHttpClient(httpClient) { get("$PROXY_PORT/proxy-$index".addPrefix()) { response -> val body = response.readText() body shouldBeEqualTo contentMap[httpIndex] diff --git a/src/test/kotlin/io/prometheus/SimpleTests.kt b/src/test/kotlin/io/prometheus/SimpleTests.kt index 892c12d7..6a3fd640 100644 --- a/src/test/kotlin/io/prometheus/SimpleTests.kt +++ b/src/test/kotlin/io/prometheus/SimpleTests.kt @@ -20,11 +20,10 @@ package io.prometheus import com.github.pambrose.common.dsl.KtorDsl.blockingGet import io.ktor.http.HttpStatusCode +import io.prometheus.TestConstants.PROXY_PORT import io.prometheus.agent.AgentPathManager import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeoutOrNull @@ -38,19 +37,19 @@ internal object SimpleTests : KLogging() { fun missingPathTest(caller: String) { logger.debug { "Calling missingPathTest() from $caller" } - blockingGet("${TestConstants.PROXY_PORT}/".addPrefix()) { response -> + blockingGet("$PROXY_PORT/".addPrefix()) { response -> response.status shouldBeEqualTo HttpStatusCode.NotFound } } fun invalidPathTest(caller: String) { logger.debug { "Calling invalidPathTest() from $caller" } - blockingGet("${TestConstants.PROXY_PORT}/invalid_path".addPrefix()) { response -> + blockingGet("$PROXY_PORT/invalid_path".addPrefix()) { response -> response.status shouldBeEqualTo HttpStatusCode.NotFound } } - fun addRemovePathsTest(pathManager: AgentPathManager, caller: String) { + suspend fun addRemovePathsTest(pathManager: AgentPathManager, caller: String) { logger.debug { "Calling addRemovePathsTest() from $caller" } // Take into account pre-existing paths already registered @@ -60,7 +59,7 @@ internal object SimpleTests : KLogging() { repeat(TestConstants.REPS) { i -> val path = "test-$i" pathManager.let { manager -> - manager.registerPath(path, "${TestConstants.PROXY_PORT}/$path".addPrefix()) + manager.registerPath(path, "$PROXY_PORT/$path".addPrefix()) cnt++ manager.pathMapSize() shouldBeEqualTo originalSize + cnt manager.unregisterPath(path) @@ -70,60 +69,59 @@ internal object SimpleTests : KLogging() { } } - fun invalidAgentUrlTest(pathManager: AgentPathManager, caller: String, badPath: String = "badPath") { + suspend fun invalidAgentUrlTest(pathManager: AgentPathManager, caller: String, badPath: String = "badPath") { logger.debug { "Calling invalidAgentUrlTest() from $caller" } pathManager.registerPath(badPath, "33/metrics".addPrefix()) - blockingGet("${TestConstants.PROXY_PORT}/$badPath".addPrefix()) { response -> + blockingGet("$PROXY_PORT/$badPath".addPrefix()) { response -> response.status shouldBeEqualTo HttpStatusCode.NotFound } pathManager.unregisterPath(badPath) } - fun threadedAddRemovePathsTest(pathManager: AgentPathManager, caller: String) { + suspend fun threadedAddRemovePathsTest(pathManager: AgentPathManager, caller: String) { logger.debug { "Calling threadedAddRemovePathsTest() from $caller" } val paths: MutableList = mutableListOf() // Take into account pre-existing paths already registered val originalSize = pathManager.pathMapSize() - runBlocking { - withTimeoutOrNull(30.seconds.toLongMilliseconds()) { - val mutex = Mutex() - val jobs = - List(TestConstants.REPS) { i -> - GlobalScope.launch(Dispatchers.Default + coroutineExceptionHandler(logger)) { - val path = "test-$i}" - val url = "${TestConstants.PROXY_PORT}/$path".addPrefix() - mutex.withLock { paths += path } - pathManager.registerPath(path, url) - } - } - - jobs.forEach { job -> - job.join() - job.getCancellationException().cause.shouldBeNull() + withTimeoutOrNull(30.seconds.toLongMilliseconds()) { + val mutex = Mutex() + val jobs = + List(TestConstants.REPS) { i -> + launch(Dispatchers.Default + exceptionHandler(logger)) { + val path = "test-$i}" + val url = "$PROXY_PORT/$path".addPrefix() + mutex.withLock { paths += path } + pathManager.registerPath(path, url) + } } - }.shouldNotBeNull() - } + + jobs.forEach { job -> + job.join() + job.getCancellationException().cause.shouldBeNull() + } + + }.shouldNotBeNull() paths.size shouldBeEqualTo TestConstants.REPS pathManager.pathMapSize() shouldBeEqualTo (originalSize + TestConstants.REPS) - runBlocking { - withTimeoutOrNull(30.seconds.toLongMilliseconds()) { - val jobs = - List(paths.size) { - GlobalScope.launch(Dispatchers.Default + coroutineExceptionHandler(logger)) { - pathManager.unregisterPath(paths[it]) - } - } - jobs.forEach { job -> - job.join() - job.getCancellationException().cause.shouldBeNull() - }.shouldNotBeNull() + withTimeoutOrNull(30.seconds.toLongMilliseconds()) { + val jobs = + List(paths.size) { + launch(Dispatchers.Default + exceptionHandler(logger)) { + pathManager.unregisterPath(paths[it]) + } + } + + jobs.forEach { job -> + job.join() + job.getCancellationException().cause.shouldBeNull() } - } + + }.shouldNotBeNull() pathManager.pathMapSize() shouldBeEqualTo originalSize } diff --git a/src/test/kotlin/io/prometheus/TestUtils.kt b/src/test/kotlin/io/prometheus/TestUtils.kt index 9c685c37..3ea1c3f0 100644 --- a/src/test/kotlin/io/prometheus/TestUtils.kt +++ b/src/test/kotlin/io/prometheus/TestUtils.kt @@ -41,13 +41,13 @@ object TestUtils : KLogging() { } val proxyOptions = ProxyOptions(mutableListOf() - .apply { - addAll(TestConstants.CONFIG_ARG) - addAll(argv) - add("-Dproxy.admin.enabled=$adminEnabled") - add("-Dproxy.admin.debugEnabled=$debugEnabled") - add("-Dproxy.metrics.enabled=$metricsEnabled") - }) + .apply { + addAll(TestConstants.CONFIG_ARG) + addAll(argv) + add("-Dproxy.admin.enabled=$adminEnabled") + add("-Dproxy.admin.debugEnabled=$debugEnabled") + add("-Dproxy.metrics.enabled=$metricsEnabled") + }) return Proxy(options = proxyOptions, proxyHttpPort = PROXY_PORT, inProcessServerName = serverName, @@ -67,27 +67,27 @@ object TestUtils : KLogging() { } val agentOptions = AgentOptions(mutableListOf() - .apply { - addAll(TestConstants.CONFIG_ARG) - addAll(argv) - add("-Dagent.admin.enabled=$adminEnabled") - add("-Dagent.admin.debugEnabled=$debugEnabled") - add("-Dagent.metrics.enabled=$metricsEnabled") - if (chunkContentSizeKbs != -1) - add("-Dagent.chunkContentSizeKbs=$chunkContentSizeKbs") - }, + .apply { + addAll(TestConstants.CONFIG_ARG) + addAll(argv) + add("-Dagent.admin.enabled=$adminEnabled") + add("-Dagent.admin.debugEnabled=$debugEnabled") + add("-Dagent.metrics.enabled=$metricsEnabled") + if (chunkContentSizeKbs != -1) + add("-Dagent.chunkContentSizeKbs=$chunkContentSizeKbs") + }, false) return Agent(options = agentOptions, inProcessServerName = serverName, testMode = true) { startSync() } } } -fun coroutineExceptionHandler(logger: KLogger) = - CoroutineExceptionHandler { _, e -> - if (e is ClosedSelectorException) - logger.info { "CoroutineExceptionHandler caught: $e" } - else - logger.warn(e) { "CoroutineExceptionHandler caught: $e" } - } +fun exceptionHandler(logger: KLogger) = + CoroutineExceptionHandler { _, e -> + if (e is ClosedSelectorException) + logger.info { "CoroutineExceptionHandler caught: $e" } + else + logger.warn(e) { "CoroutineExceptionHandler caught: $e" } + } fun String.addPrefix(): String { val prefix = "http://localhost:"