From 8a50dc0a650a9f008967e8f02f4bbbfccef886e4 Mon Sep 17 00:00:00 2001 From: cleverchuk Date: Tue, 2 Jul 2024 09:48:10 -0400 Subject: [PATCH] NH-37575: download agent from GitHub --- benchmark/build.gradle.kts | 1 - .../java/io/opentelemetry/OverheadTests.java | 50 ++++-- .../java/io/opentelemetry/agents/Agent.java | 3 +- .../opentelemetry/agents/AgentResolver.java | 3 +- .../agents/LatestSolarwindsAgentResolver.java | 142 +++++++++++++----- .../containers/AOTestCollectorContainer.java | 33 ++-- .../containers/PetClinicRestContainer.java | 43 ++++-- .../results/PrintStreamPersister.java | 3 +- .../opentelemetry/util/NamingConventions.java | 8 +- 9 files changed, 196 insertions(+), 90 deletions(-) diff --git a/benchmark/build.gradle.kts b/benchmark/build.gradle.kts index 1e29ff52..0f5f3f9b 100644 --- a/benchmark/build.gradle.kts +++ b/benchmark/build.gradle.kts @@ -6,7 +6,6 @@ plugins { spotless { java { googleJavaFormat() - licenseHeaderFile(rootProject.file("../buildscripts/spotless.license.java"), "(package|import|public)") target("src/**/*.java") } } diff --git a/benchmark/src/test/java/io/opentelemetry/OverheadTests.java b/benchmark/src/test/java/io/opentelemetry/OverheadTests.java index 53eabe02..6fda088d 100644 --- a/benchmark/src/test/java/io/opentelemetry/OverheadTests.java +++ b/benchmark/src/test/java/io/opentelemetry/OverheadTests.java @@ -40,7 +40,12 @@ public class OverheadTests { private static GenericContainer aoCollector; private final NamingConventions namingConventions = new NamingConventions(); private final Map runDurations = new HashMap<>(); - private static Set verboseConfig = new HashSet<>(Arrays.asList(Strings.split(System.getenv("CONTAINER_LOGS") != null ? System.getenv("CONTAINER_LOGS") : "", '|'))); + private static Set verboseConfig = + new HashSet<>( + Arrays.asList( + Strings.split( + System.getenv("CONTAINER_LOGS") != null ? System.getenv("CONTAINER_LOGS") : "", + '|'))); @BeforeAll static void setUp() { @@ -104,14 +109,18 @@ void runAppOnce(TestConfig config, Agent agent) throws Exception { if (verboseConfig.contains("all") || verboseConfig.contains("app")) { String logs = petclinic.getLogs(); - System.err.println(String.format("\n\n===============%s====================\n\n%s\n\n===============================" - , agent.getName(), logs)); + System.err.println( + String.format( + "\n\n===============%s====================\n\n%s\n\n===============================", + agent.getName(), logs)); } if (verboseConfig.contains("all") || verboseConfig.contains("collector")) { String aoCollectorLogs = aoCollector.getLogs(); - System.err.println(String.format("\n\n===============%s====================\n\n%s\n\n===============================" - , aoCollector.getDockerImageName(), aoCollectorLogs)); + System.err.println( + String.format( + "\n\n===============%s====================\n\n%s\n\n===============================", + aoCollector.getDockerImageName(), aoCollectorLogs)); } // This is required to get a graceful exit of the VM before testcontainers kills it forcibly. // Without it, our jfr file will be empty. @@ -136,22 +145,33 @@ private void startRecording(Agent agent, GenericContainer petclinic) throws E petclinic.execInContainer(command); } - private void doWarmupPhase(TestConfig testConfig, GenericContainer petclinic) throws IOException, InterruptedException { - System.out.println("Performing startup warming phase for " + testConfig.getWarmupSeconds() + " seconds..."); + private void doWarmupPhase(TestConfig testConfig, GenericContainer petclinic) + throws IOException, InterruptedException { + System.out.println( + "Performing startup warming phase for " + testConfig.getWarmupSeconds() + " seconds..."); // excluding the JFR recording from the warmup causes strange inconsistencies in the results System.out.println("Starting disposable JFR warmup recording..."); - String[] startCommand = {"jcmd", "1", "JFR.start", "settings=/app/overhead.jfc", "dumponexit=true", "name=warmup", "filename=warmup.jfr"}; + String[] startCommand = { + "jcmd", + "1", + "JFR.start", + "settings=/app/overhead.jfc", + "dumponexit=true", + "name=warmup", + "filename=warmup.jfr" + }; petclinic.execInContainer(startCommand); - long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(testConfig.getWarmupSeconds()); - while(System.currentTimeMillis() < deadline) { + long deadline = + System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(testConfig.getWarmupSeconds()); + while (System.currentTimeMillis() < deadline) { GenericContainer k6 = - new GenericContainer<>(DockerImageName.parse("loadimpact/k6")) - .withNetwork(NETWORK) - .withCopyFileToContainer(MountableFile.forHostPath("./k6"), "/app") - .withCommand("run", "-u", "5", "-i", "200", "/app/basic.js") - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new GenericContainer<>(DockerImageName.parse("loadimpact/k6")) + .withNetwork(NETWORK) + .withCopyFileToContainer(MountableFile.forHostPath("./k6"), "/app") + .withCommand("run", "-u", "5", "-i", "200", "/app/basic.js") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); k6.start(); } diff --git a/benchmark/src/test/java/io/opentelemetry/agents/Agent.java b/benchmark/src/test/java/io/opentelemetry/agents/Agent.java index 0c01d408..6569fc77 100644 --- a/benchmark/src/test/java/io/opentelemetry/agents/Agent.java +++ b/benchmark/src/test/java/io/opentelemetry/agents/Agent.java @@ -22,7 +22,8 @@ public class Agent { new Agent("latest", "latest mainstream release", OTEL_LATEST); public static final Agent LATEST_SNAPSHOT = new Agent("snapshot", "latest available snapshot version from main"); - public static final Agent NH_LATEST_RELEASE = new Agent(LatestSolarwindsAgentResolver.useAOAgent ? "AO" : "NH", "latest Solarwinds agent"); + public static final Agent NH_LATEST_RELEASE = + new Agent(LatestSolarwindsAgentResolver.useAOAgent ? "AO" : "NH", "latest Solarwinds agent"); private final String name; private final String description; diff --git a/benchmark/src/test/java/io/opentelemetry/agents/AgentResolver.java b/benchmark/src/test/java/io/opentelemetry/agents/AgentResolver.java index 6c7201b3..2ebd12d5 100644 --- a/benchmark/src/test/java/io/opentelemetry/agents/AgentResolver.java +++ b/benchmark/src/test/java/io/opentelemetry/agents/AgentResolver.java @@ -19,7 +19,8 @@ public class AgentResolver { private final LatestAgentSnapshotResolver snapshotResolver = new LatestAgentSnapshotResolver(); - private final LatestSolarwindsAgentResolver nighthawkAgentResolver = new LatestSolarwindsAgentResolver(); + private final LatestSolarwindsAgentResolver nighthawkAgentResolver = + new LatestSolarwindsAgentResolver(); public Optional resolve(Agent agent) throws Exception { if (Agent.NONE.equals(agent)) { diff --git a/benchmark/src/test/java/io/opentelemetry/agents/LatestSolarwindsAgentResolver.java b/benchmark/src/test/java/io/opentelemetry/agents/LatestSolarwindsAgentResolver.java index 1038d1d4..98298471 100644 --- a/benchmark/src/test/java/io/opentelemetry/agents/LatestSolarwindsAgentResolver.java +++ b/benchmark/src/test/java/io/opentelemetry/agents/LatestSolarwindsAgentResolver.java @@ -1,52 +1,120 @@ package io.opentelemetry.agents; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Optional; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.testcontainers.shaded.com.fasterxml.jackson.core.type.TypeReference; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; public class LatestSolarwindsAgentResolver { - private static final String NH_URL = "https://agent-binaries.global.st-ssp.solarwinds.com/apm/java/latest/solarwinds-apm-agent.jar"; - private static final String AO_URL = "https://files.appoptics.com/java/latest/appoptics-agent.jar"; - private static final String NH_AGENT_JAR_NAME = "solarwinds-apm-agent.jar"; - private static final String AO_AGENT_JAR_NAME = "appoptics-agent.jar"; - public static boolean useAOAgent = "AO".equals(System.getenv("AGENT_TYPE")); - - Optional resolve() throws Exception { - return Optional.of(downloadAgent()); - } - - private Path downloadAgent() throws Exception { - String assetURL; - if (useAOAgent) { - assetURL = AO_URL; - } else { - assetURL = NH_URL; - } + private static final String NH_URL = " https://api.github.com/repos/solarwinds/apm-java/releases"; + private static final String AO_URL = + "https://files.appoptics.com/java/latest/appoptics-agent.jar"; + private static final String NH_AGENT_JAR_NAME = "solarwinds-apm-agent.jar"; + private static final String AO_AGENT_JAR_NAME = "appoptics-agent.jar"; + public static boolean useAOAgent = "AO".equals(System.getenv("AGENT_TYPE")); - OkHttpClient client = new OkHttpClient(); - Request request = new Request.Builder().url(assetURL) - .header("Authorization", "token " + System.getenv("GITHUB_TOKEN")) - .header("Accept", "application/octet-stream").build(); - - Path path = Paths.get(".", useAOAgent ? AO_AGENT_JAR_NAME : NH_AGENT_JAR_NAME); - try (Response response = client.newCall(request).execute()) { - assert response.body() != null; - byte[] fileRaw = response.body().bytes(); - Files.write( - path, - fileRaw, - StandardOpenOption.CREATE, - StandardOpenOption.WRITE, - StandardOpenOption.TRUNCATE_EXISTING); + Optional resolve() throws Exception { + return Optional.of(downloadAgent()); + } + + private Path downloadAgent() throws Exception { + String assetURL; + OkHttpClient client = new OkHttpClient(); + if (useAOAgent) { + assetURL = AO_URL; + } else { + assetURL = NH_URL; + Request request = + new Request.Builder() + .url(NH_URL) + .header("Authorization", "token " + System.getenv("GITHUB_TOKEN")) + .header("Accept", "application/vnd.github.v3+json") + .build(); + + try (Response response = client.newCall(request).execute()) { + assert response.body() != null; + byte[] raw = response.body().bytes(); + + ObjectMapper mapper = new ObjectMapper(); + List releases = + mapper.readValue(raw, new TypeReference>() {}); + + outerLoop: + for (GithubRelease release : releases) { + for (Asset asset : release.assets) { + if (asset.name.equals(NH_AGENT_JAR_NAME)) { + assetURL = asset.url; + break outerLoop; + } + } } - return path; + if (assetURL == null) { + throw new RuntimeException("Asset url not found for the NH agent."); + } + } + } + + Request request = + new Request.Builder() + .url(assetURL) + .header("Authorization", "token " + System.getenv("GITHUB_TOKEN")) + .header("Accept", "application/octet-stream") + .build(); + + Path path = Paths.get(".", useAOAgent ? AO_AGENT_JAR_NAME : NH_AGENT_JAR_NAME); + try (Response response = client.newCall(request).execute()) { + assert response.body() != null; + byte[] fileRaw = response.body().bytes(); + Files.write( + path, + fileRaw, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING); + } + return path; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class GithubRelease { + private List assets; + + public List getAssets() { + return assets; + } + + public void setAssets(List assets) { + this.assets = assets; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Asset { + private String url; + private String name; + + public String getUrl() { + return url; + } + + public String getName() { + return name; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setName(String name) { + this.name = name; } + } } diff --git a/benchmark/src/test/java/io/opentelemetry/containers/AOTestCollectorContainer.java b/benchmark/src/test/java/io/opentelemetry/containers/AOTestCollectorContainer.java index 6f1670f2..e3456169 100644 --- a/benchmark/src/test/java/io/opentelemetry/containers/AOTestCollectorContainer.java +++ b/benchmark/src/test/java/io/opentelemetry/containers/AOTestCollectorContainer.java @@ -10,23 +10,22 @@ import org.testcontainers.utility.MountableFile; public class AOTestCollectorContainer { - static final int COLLECTOR_PORT = 12223; - static final int COLLECTOR_HEALTH_CHECK_PORT = 8181; + static final int COLLECTOR_PORT = 12223; + static final int COLLECTOR_HEALTH_CHECK_PORT = 8181; - private static final Logger logger = LoggerFactory.getLogger(CollectorContainer.class); + private static final Logger logger = LoggerFactory.getLogger(CollectorContainer.class); - - public static GenericContainer build(Network network) { - return new GenericContainer<>( - DockerImageName.parse("ghcr.io/solarwinds/apm-agent-test-collector:v2.0.4")) - .withNetwork(network) - .withNetworkAliases("AOCollector") - .withLogConsumer(new Slf4jLogConsumer(logger)) - .withExposedPorts(COLLECTOR_PORT, COLLECTOR_HEALTH_CHECK_PORT) - .waitingFor(Wait.forHttp("/collectors").forPort(COLLECTOR_HEALTH_CHECK_PORT)) - .withCopyFileToContainer( - MountableFile.forClasspathResource("test-server-grpc.crt"), "/server-grpc.crt") - .withCopyFileToContainer( - MountableFile.forClasspathResource("test-server-grpc.pem"), "/server-grpc.pem"); - } + public static GenericContainer build(Network network) { + return new GenericContainer<>( + DockerImageName.parse("ghcr.io/solarwinds/apm-agent-test-collector:v2.0.4")) + .withNetwork(network) + .withNetworkAliases("AOCollector") + .withLogConsumer(new Slf4jLogConsumer(logger)) + .withExposedPorts(COLLECTOR_PORT, COLLECTOR_HEALTH_CHECK_PORT) + .waitingFor(Wait.forHttp("/collectors").forPort(COLLECTOR_HEALTH_CHECK_PORT)) + .withCopyFileToContainer( + MountableFile.forClasspathResource("test-server-grpc.crt"), "/server-grpc.crt") + .withCopyFileToContainer( + MountableFile.forClasspathResource("test-server-grpc.pem"), "/server-grpc.pem"); + } } diff --git a/benchmark/src/test/java/io/opentelemetry/containers/PetClinicRestContainer.java b/benchmark/src/test/java/io/opentelemetry/containers/PetClinicRestContainer.java index 7be8744d..0dfef92a 100644 --- a/benchmark/src/test/java/io/opentelemetry/containers/PetClinicRestContainer.java +++ b/benchmark/src/test/java/io/opentelemetry/containers/PetClinicRestContainer.java @@ -71,11 +71,11 @@ public GenericContainer build() throws Exception { .withEnv("SW_APM_DEBUG_LEVEL", "info") .withEnv("SW_APM_COLLECTOR", "AOCollector:12223") .withEnv("SW_APM_TRUSTEDPATH", "/test-server-grpc.crt") - .withEnv("APPOPTICS_DEBUG_LEVEL", "info") - .withEnv("APPOPTICS_COLLECTOR", "AOCollector:12223") - .withEnv("APPOPTICS_TRUSTEDPATH", "/test-server-grpc.crt") + .withEnv("APPOPTICS_DEBUG_LEVEL", "info") + .withEnv("APPOPTICS_COLLECTOR", "AOCollector:12223") + .withEnv("APPOPTICS_TRUSTEDPATH", "/test-server-grpc.crt") .withCopyFileToContainer( - MountableFile.forClasspathResource("test-server-grpc.crt"), "/test-server-grpc.crt") + MountableFile.forClasspathResource("test-server-grpc.crt"), "/test-server-grpc.crt") .dependsOn(collector) .withCommand(buildCommandline(agentJar)); agentJar.ifPresent( @@ -91,20 +91,33 @@ private String[] buildCommandline(Optional agentJar) { List result = new ArrayList<>(); if (!agent.equals(Agent.NH_LATEST_RELEASE)) { result.addAll( - Arrays.asList( - "java", - "-Dotel.traces.exporter=otlp", - "-Dotel.imr.export.interval=5000", - "-Dotel.exporter.otlp.insecure=true", - "-Dotel.exporter.otlp.protocol=grpc", - "-Dotel.exporter.otlp.endpoint=http://collector:4317", - "-Dotel.resource.attributes=service.name=petclinic-otel-overhead")); + Arrays.asList( + "java", + "-Dotel.traces.exporter=otlp", + "-Dotel.imr.export.interval=5000", + "-Dotel.exporter.otlp.insecure=true", + "-Dotel.exporter.otlp.protocol=grpc", + "-Dotel.exporter.otlp.endpoint=http://collector:4317", + "-Dotel.resource.attributes=service.name=petclinic-otel-overhead")); } else { - result.addAll(Arrays.asList("java", - "-Dotel.solarwinds.service.key=" + System.getenv("SW_APM_SERVICE_KEY") + ":sw-java-benchmark")); + result.addAll( + Arrays.asList( + "java", + "-Dotel.solarwinds.service.key=" + + System.getenv("SW_APM_SERVICE_KEY") + + ":sw-java-benchmark")); } result.addAll(this.agent.getAdditionalJvmArgs()); - agentJar.ifPresent(path -> result.add("-javaagent:/app/" + path.getFileName() + (LatestSolarwindsAgentResolver.useAOAgent ? "=service_key="+System.getenv("SW_APM_SERVICE_KEY")+":sw-java-benchmark":""))); + agentJar.ifPresent( + path -> + result.add( + "-javaagent:/app/" + + path.getFileName() + + (LatestSolarwindsAgentResolver.useAOAgent + ? "=service_key=" + + System.getenv("SW_APM_SERVICE_KEY") + + ":sw-java-benchmark" + : ""))); result.add("-jar"); result.add("/app/spring-petclinic-rest.jar"); System.err.println("Running app with command:\n" + String.join(" ", result)); diff --git a/benchmark/src/test/java/io/opentelemetry/results/PrintStreamPersister.java b/benchmark/src/test/java/io/opentelemetry/results/PrintStreamPersister.java index 495049ec..3a5c85c6 100644 --- a/benchmark/src/test/java/io/opentelemetry/results/PrintStreamPersister.java +++ b/benchmark/src/test/java/io/opentelemetry/results/PrintStreamPersister.java @@ -52,7 +52,8 @@ public void write(List results) { display(results, "| Min heap used (MB)", res -> format(res.getMinHeapUsedMB())); display(results, "| Max heap used (MB)", res -> format(res.getMaxHeapUsedMB())); display(results, "| Thread switch rate", res -> String.valueOf(res.maxThreadContextSwitchRate)); - display(results, "| GC time (ms)", res -> String.valueOf(NANOSECONDS.toMillis(res.totalGCTime))); + display( + results, "| GC time (ms)", res -> String.valueOf(NANOSECONDS.toMillis(res.totalGCTime))); display( results, "| GC pause time (ms)", diff --git a/benchmark/src/test/java/io/opentelemetry/util/NamingConventions.java b/benchmark/src/test/java/io/opentelemetry/util/NamingConventions.java index 6caf28df..903105b4 100644 --- a/benchmark/src/test/java/io/opentelemetry/util/NamingConventions.java +++ b/benchmark/src/test/java/io/opentelemetry/util/NamingConventions.java @@ -11,12 +11,16 @@ public class NamingConventions { public final NamingConvention container = new NamingConvention("/results"); public final NamingConvention local = new NamingConvention("."); - /** @return Root path for the local naming convention (where results are output) */ + /** + * @return Root path for the local naming convention (where results are output) + */ public String localResults() { return local.root(); } - /** @return Root path for the container naming convention (where results are output) */ + /** + * @return Root path for the container naming convention (where results are output) + */ public String containerResults() { return container.root(); }