Skip to content

Commit

Permalink
Feature/agent config (#3)
Browse files Browse the repository at this point in the history
* add ConfigurationResolver

* refactor test

* remove tests

* update README

* add system-lambda for testing

* refactoring

* refactor ConnectionManager
  • Loading branch information
EddeCCC authored Jul 18, 2024
1 parent 558831d commit cbe976f
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 28 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ contain extension jars) for the `otel.javaagent.extensions` value.
## Network communication

The extension contains a client, who is able to communicate with other servers via HTTPS.
To configure TLS
You can set the server url via system or environmental properties.
You have the option to use a keystore for TLS:

1. Provide a local keystore, which contains the certificate of your server
2. Modify the startup command to add the path to the keystore as well as the password. For example:
Expand All @@ -38,6 +39,7 @@ To configure TLS
java -javaagent:path/to/opentelemetry-javaagent.jar \
-Dotel.javaagent.extensions=build/libs/opentelemetry-javaagent.jar \
-Dotel.service.name="my-service" \
-Dinspectit.config.http.url="https://{server-host:port}/api/v1/connections" \
-Djavax.net.ssl.trustStore="path\to\keystore\agent-keystore.jks" \
-Djavax.net.ssl.trustStorePassword="password"
-jar myapp.jar
Expand All @@ -50,7 +52,7 @@ The repository was build upon this example project: https://github.com/open-tele
### Why Gepard?
Gepard is the German name for the animal cheetah as well as an acronym for:

"**G**anzheitliche, **e**ffizienz-orientierte, **P**erformance **A**nwendungsüberwachung mit **R**eporting und **D**iagnose"
"**G**anzheitliche, **e**ffizienz-orientierte, **P**erformance **A**nwendungsüberwachung mit **R**eporting und **D**iagnose",

meaning: holistic, efficiency-orientated, performance application monitoring with reporting and diagnostics.
which means: holistic, efficiency-orientated, performance application monitoring with reporting and diagnostics.

11 changes: 8 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ dependencies {

testImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}")
// configure environmental variables
testImplementation("com.github.stefanbirkner:system-lambda:1.2.1")

// Otel Java instrumentation that we use and extend during integration tests
otel("io.opentelemetry.javaagent:opentelemetry-javaagent:${versions.opentelemetryJavaagent}")
Expand Down Expand Up @@ -145,6 +147,11 @@ tasks {
inputs.files(layout.files(tasks.shadowJar))
inputs.files(layout.files(tasks.extendedAgent))

// Necessary to use system-lambda in tests
// See: https://github.com/stefanbirkner/system-lambda/issues/10
jvmArgs '--add-opens', 'java.base/java.util=ALL-UNNAMED'
jvmArgs '--add-opens', 'java.base/java.lang=ALL-UNNAMED'

systemProperty 'io.opentelemetry.smoketest.agentPath', configurations.otel.singleFile.absolutePath
systemProperty 'io.opentelemetry.smoketest.extendedAgentPath', tasks.extendedAgent.archiveFile.get().asFile.absolutePath
systemProperty 'io.opentelemetry.smoketest.extensionPath', tasks.shadowJar.archiveFile.get().asFile.absolutePath
Expand All @@ -156,9 +163,7 @@ tasks {

assemble.dependsOn(shadowJar)

dockerPrepare {
dependsOn extendedAgent
}
dockerPrepare.dependsOn(extendedAgent)

docker {
Provider<Directory> agentJar = layout.buildDirectory.dir("/libs/opentelemetry-javaagent.jar")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
import net.bytebuddy.agent.builder.AgentBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rocks.inspectit.gepard.agent.config.ConfigurationResolver;
import rocks.inspectit.gepard.agent.notify.NotificationManager;

@SuppressWarnings("unused")
@AutoService(AgentExtension.class)
public class InspectitAgentExtension implements AgentExtension {
private static final Logger log = LoggerFactory.getLogger(InspectitAgentExtension.class);

/** Hard coded server url, which should configurable be in the future */
private static final String SERVER_URL = "https://localhost:8080/api/v1/connections";

/**
* Entrypoint for the inspectIT gepard extension
*
Expand All @@ -28,9 +26,15 @@ public class InspectitAgentExtension implements AgentExtension {
public AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config) {
log.info("Starting inspectIT Gepard agent extension ...");

boolean successful = NotificationManager.sendStartNotification(SERVER_URL);
if (successful) log.info("Successfully notified configuration server about start");
else log.info("Could not notify configuration server about start");
String url = ConfigurationResolver.getServerUrl();
if (url.isEmpty()) log.info("No configuration server url was provided");
else {
log.info("Sending start notification to configuration server with url: {}", url);
boolean successful = NotificationManager.sendStartNotification(url);

if (successful) log.info("Successfully notified configuration server about start");
else log.warn("Could not notify configuration server about start");
}

return agentBuilder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package rocks.inspectit.gepard.agent.config;

import java.util.Objects;

/**
* This resolver provides the configured configuration server url. Currently, it is possible to
* configure the url via system properties or environmental properties. System properties are higher
* prioritized than environmental properties.
*/
public class ConfigurationResolver {

private static final String SERVER_URL_SYSTEM_PROPERTY = "inspectit.config.http.url";

private static final String SERVER_URL_ENV_PROPERTY = "INSPECTIT_CONFIG_HTTP_URL";

private ConfigurationResolver() {}

/**
* Get the configured configuration server url. If no url was configured, an empty string will be
* returned.
*
* @return the configured configuration server url
*/
public static String getServerUrl() {
String serverUrlSystemProperty = System.getProperty(SERVER_URL_SYSTEM_PROPERTY);
if (Objects.nonNull(serverUrlSystemProperty)) return serverUrlSystemProperty;

String serverUrlEnvProperty = System.getenv(SERVER_URL_ENV_PROPERTY);
return Objects.nonNull(serverUrlEnvProperty) ? serverUrlEnvProperty : "";
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package rocks.inspectit.gepard.agent.notify.http;

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.util.Objects;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.config.RequestConfig;
Expand All @@ -13,7 +15,6 @@
import org.apache.hc.client5.http.ssl.*;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.apache.hc.core5.util.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -75,15 +76,20 @@ private static RequestConfig getRequestConfig() {
*/
private static AsyncClientConnectionManager getConnectionManager()
throws GeneralSecurityException, IOException {
TrustStrategy trustStrategy =
(chain, authType) -> {
final X509Certificate certificate = chain[0];
// Later the CN should be configurable
return "CN=localhost".equalsIgnoreCase(certificate.getSubjectX500Principal().getName());
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStrategy).build();
HostnameVerifier hostnameVerifier = HttpsSupport.getDefaultHostnameVerifier();
String keystorePath = System.getProperty("javax.net.ssl.trustStore");
String keystorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
SSLContext sslContext;

if (Objects.nonNull(keystorePath) || Objects.nonNull(keystorePassword)) {
KeyStore keyStore =
KeyStore.getInstance(new File(keystorePath), keystorePassword.toCharArray());
sslContext = SSLContexts.custom().loadTrustMaterial(keyStore, null).build();
} else {
log.info("No keystore found. Using default SSL context");
sslContext = SSLContexts.custom().loadTrustMaterial(TrustAllStrategy.INSTANCE).build();
}

HostnameVerifier hostnameVerifier = HttpsSupport.getDefaultHostnameVerifier();
TlsStrategy tlsStrategy =
ClientTlsStrategyBuilder.create()
.setSslContext(sslContext)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package rocks.inspectit.gepard.agent.notify.http.exception;

/**
* Exception, for errors in the configuration of the HTTP client
*/
/** Exception, for errors in the configuration of the HTTP client */
public class ClientConfigurationException extends RuntimeException {

public ClientConfigurationException(String message, Throwable cause) {
super(message, cause);
}
public ClientConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package rocks.inspectit.gepard.agent.config;

import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties;
import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class ConfigurationResolverTest {

private static final String SERVER_URL_SYSTEM_PROPERTY = "inspectit.config.http.url";

private static final String SERVER_URL_ENV_PROPERTY = "INSPECTIT_CONFIG_HTTP_URL";

private static final String TEST_URL = "https://inspectit.rocks/";

@Test
void resolverReturnsUrlIfSystemPropertyExists() throws Exception {
restoreSystemProperties(
() -> {
System.setProperty(SERVER_URL_SYSTEM_PROPERTY, TEST_URL);

String url = ConfigurationResolver.getServerUrl();

assertEquals(TEST_URL, url);
});
}

@Test
void resolverReturnsUrlIfEnvironmentPropertyExists() throws Exception {
String url =
withEnvironmentVariable(SERVER_URL_ENV_PROPERTY, TEST_URL)
.execute(ConfigurationResolver::getServerUrl);

assertEquals(TEST_URL, url);
}

@Test
void resolverReturnsSystemPropertyIfSystemAndEnvPropertyExist() throws Exception {
String envTestUrl = TEST_URL + "1";
restoreSystemProperties(
() -> {
System.setProperty(SERVER_URL_SYSTEM_PROPERTY, TEST_URL);

String url =
withEnvironmentVariable(SERVER_URL_ENV_PROPERTY, envTestUrl)
.execute(ConfigurationResolver::getServerUrl);

assertEquals(TEST_URL, url);
});
}

@Test
void resolverReturnsEmptyStringIfNoPropertyExists() {
String url = ConfigurationResolver.getServerUrl();

assertTrue(url.isEmpty());
}
}

0 comments on commit cbe976f

Please sign in to comment.