Skip to content

Commit

Permalink
feat: include agent info into polling (#29)
Browse files Browse the repository at this point in the history
* feat: include agent info into polling

* feat: log based on custom header

* refactor: delete unnecessary code

* fix: remove StartNotifierTest.java since the according class was deleted

* fix: attempt to stabilize the IdentityManagerTest

* fix: Fix IdentityManagerTest and add salt to hashing algorithm

* fix: refactoring according review comments

---------

Co-authored-by: Benjamin Clauss <[email protected]>
  • Loading branch information
levinkerschberger and binarycoded authored Nov 26, 2024
1 parent 386b2e7 commit 6be3dd2
Show file tree
Hide file tree
Showing 14 changed files with 86 additions and 262 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ public AgentBuilder extend(AgentBuilder agentBuilder, ConfigProperties config) {

// Notify configuration server about this agent
NotificationManager notificationManager = NotificationManager.create();
notificationManager.sendStartNotification();
// Set up shutdown notification to configuration server
notificationManager.setUpShutdownNotification();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ public class HttpConfigurationCallback implements FutureCallback<SimpleHttpRespo

@Override
public void completed(SimpleHttpResponse result) {
log.info(
"Fetched configuration from configuration server and received status code {}",
result.getCode());

logStatus(result);
// Publish Event
if (result.getCode() == 200) {
if (result.getCode() == 200 || result.getCode() == 201) {
String body = result.getBodyText();

try {
Expand All @@ -45,4 +43,21 @@ public void failed(Exception ex) {
public void cancelled() {
log.info("Cancelled configuration fetch");
}

private void logStatus(SimpleHttpResponse response) {
int statusCode = response.getCode();

if (statusCode == 200) {
log.info("Configuration fetched successfully");
} else if (statusCode == 201) {
log.info(
"Connection to configuration server was successfully established. Configuration fetched successfully");
} else if (statusCode == 404) {
log.error("Configuration not found on configuration server");
} else {
log.error(
"Unexpected status code: {}. Please check the configuration server connection.",
statusCode);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.core5.net.URIBuilder;
import rocks.inspectit.gepard.agent.internal.identity.model.AgentInfo;
import rocks.inspectit.gepard.commons.model.agent.Agent;

/**
* This factory should create different HTTP requests for the configuration server to fetch
* inspectit configurations.
*/
public class HttpConfigurationFactory {

public static final String X_GEPARD_SERVICE_NAME = "x-gepard-service-name";
public static final String X_GEPARD_VM_ID = "x-gepard-vm-id";
public static final String X_GEPARD_GEPARD_VERSION = "x-gepard-gepard-version";
public static final String X_GEPARD_OTEL_VERSION = "x-gepard-otel-version";
public static final String X_GEPARD_JAVA_VERSION = "x-gepard-java-version";
public static final String X_GEPARD_START_TIME = "x-gepard-start-time";
public static final String X_GEPARD_ATTRIBUTE = "x-gepard-attribute-";

private HttpConfigurationFactory() {}

/**
Expand All @@ -23,8 +34,32 @@ private HttpConfigurationFactory() {}
*/
public static SimpleHttpRequest createConfigurationRequest(String baseUrl)
throws URISyntaxException {
URI uri = new URIBuilder(baseUrl + "/agent-configuration").build();

return SimpleRequestBuilder.get(uri).build();
AgentInfo agentInfo = AgentInfo.INFO;

URI uri = new URIBuilder(baseUrl + "/agent-configuration/" + agentInfo.getAgentId()).build();

SimpleRequestBuilder requestBuilder = SimpleRequestBuilder.get(uri);

return buildRequestWithHeaders(requestBuilder, agentInfo.getAgent()).build();
}

private static SimpleRequestBuilder buildRequestWithHeaders(
SimpleRequestBuilder requestBuilder, Agent agent) {
Map<String, String> headers =
Map.of(
X_GEPARD_SERVICE_NAME, agent.getServiceName(),
X_GEPARD_VM_ID, agent.getVmId(),
X_GEPARD_GEPARD_VERSION, agent.getGepardVersion(),
X_GEPARD_OTEL_VERSION, agent.getOtelVersion(),
X_GEPARD_JAVA_VERSION, agent.getJavaVersion(),
X_GEPARD_START_TIME, agent.getStartTime().toString());

headers.forEach(requestBuilder::addHeader);
agent
.getAttributes()
.forEach((key, value) -> requestBuilder.addHeader(X_GEPARD_ATTRIBUTE + key, value));

return requestBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Objects;
import rocks.inspectit.gepard.agent.internal.identity.model.IdentityInfo;

Expand Down Expand Up @@ -38,13 +40,16 @@ public IdentityInfo getIdentityInfo() {
* @return the SHA3-256 hashed <code>String</code>
*/
private static String hash(String input) {
String salt = generateSalt();
String saltedInput = salt + input;

MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA3-256");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("SHA3-256 not supported", e);
}
byte[] bytes = messageDigest.digest(input.getBytes(StandardCharsets.UTF_8));
byte[] bytes = messageDigest.digest(saltedInput.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
Expand All @@ -55,4 +60,12 @@ private static String hash(String input) {
}
return hexString.toString();
}

/** Generates a secure random 128bit string/salt */
private static String generateSalt() {
byte[] salt = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@ public class NotificationManager {

private final String serverBaseUrl;

private final StartNotifier startNotifier;

private final ShutdownNotifier shutdownNotifier;

private NotificationManager(String serverBaseUrl) {
this.serverBaseUrl = serverBaseUrl;
this.startNotifier = new StartNotifier();
this.shutdownNotifier = new ShutdownNotifier();
}

Expand All @@ -35,22 +32,6 @@ public static NotificationManager create() {
return new NotificationManager(url);
}

/**
* Sends a message to the configuration server, to notify it about this agent starting, if a
* configuration server url was provided.
*
* @return true, if the notification was executed successfully
*/
public boolean sendStartNotification() {
boolean successful = false;
if (serverBaseUrl.isEmpty()) log.info("No configuration server url was provided");
else {
log.info("Sending start notification to configuration server with url: {}", serverBaseUrl);
successful = startNotifier.sendNotification(serverBaseUrl);
}
return successful;
}

/**
* Sets up a shutdown notification to the configuration server, if a configuration server url was
* provided.
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,6 @@ public class NotificationFactory {

private NotificationFactory() {}

/**
* Create an HTTP post request to notify the configuration server about the starting agent and
* it's information.
*
* @param baseUrl the base url of the configuration server
* @return the HTTP post request, containing agent information
* @throws URISyntaxException invalid uri
* @throws JsonProcessingException corrupted agent information
*/
public static SimpleHttpRequest createStartNotification(String baseUrl)
throws URISyntaxException, JsonProcessingException {
String agentId = AgentInfo.INFO.getAgentId();
URI uri = new URI(baseUrl + "/connections/" + agentId);
String agentInfoString = mapper.writeValueAsString(AgentInfo.INFO.getAgent());

return SimpleRequestBuilder.post(uri)
.setBody(agentInfoString, ContentType.APPLICATION_JSON)
.setHeader("content-type", "application/json")
.build();
}

/**
* Create an HTTP put request to notify the configuration server about the shutting down agent.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
import org.mockserver.model.HttpError;
import rocks.inspectit.gepard.agent.MockServerTestBase;
import rocks.inspectit.gepard.agent.configuration.persistence.ConfigurationPersistence;
import rocks.inspectit.gepard.agent.internal.identity.model.AgentInfo;

class HttpConfigurationPollerTest extends MockServerTestBase {

private static HttpConfigurationPoller poller;

private static ConfigurationPersistence persistence;

private static String AGENT_ID = AgentInfo.INFO.getAgentId();

@BeforeEach
void beforeEach() {
persistence = mock(ConfigurationPersistence.class);
Expand All @@ -33,7 +36,7 @@ void afterEach() {
@Test
void runDoesntLoadLocalConfigurationWhenPollingIsSuccessful() {
mockServer
.when(request().withMethod("GET").withPath("/api/v1/agent-configuration"))
.when(request().withMethod("GET").withPath("/api/v1/agent-configuration/" + AGENT_ID))
.respond(response().withStatusCode(200));
poller.run();
verify(persistence, never()).loadLocalConfiguration();
Expand All @@ -52,7 +55,7 @@ void runLoadsLocalConfigurationOnlyOnceWhenConnectionFailed() {
@Test
void configurationRequestIsSentSuccessfully() {
mockServer
.when(request().withMethod("GET").withPath("/api/v1/agent-configuration"))
.when(request().withMethod("GET").withPath("/api/v1/agent-configuration/" + AGENT_ID))
.respond(response().withStatusCode(200));

boolean successful = poller.pollConfiguration();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.mockserver.client.MockServerClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.MockServerContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.DockerImageName;
Expand All @@ -22,8 +20,6 @@
*/
public class ConfigurationServerMock {

private static final Logger logger = LoggerFactory.getLogger(ConfigurationServerMock.class);

private MockServerContainer server;
private MockServerClient serverClient;

Expand Down Expand Up @@ -56,7 +52,7 @@ public void configServerSetup(String config_path) throws IOException {
String body = FileUtils.readFileToString(file, "UTF-8");

serverClient
.when(request().withMethod("GET").withPath("/api/v1/agent-configuration"))
.when(request().withMethod("GET").withPath("/api/v1/agent-configuration/.*"))
.respond(response().withStatusCode(200).withBody(body));

serverClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Field;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import rocks.inspectit.gepard.agent.internal.identity.model.IdentityInfo;

@ExtendWith(MockitoExtension.class)
class IdentityManagerTest {

@BeforeEach
public void setup() throws NoSuchFieldException, IllegalAccessException {
Field instance = IdentityManager.class.getDeclaredField("instance");
instance.setAccessible(true);
instance.set(null, null);
}

@Test
void testCreateIdentityManagerSuccessfully() {
RuntimeMXBean mockRuntimeMXBean = mock(RuntimeMXBean.class);
Expand All @@ -33,9 +39,7 @@ void testCreateIdentityManagerSuccessfully() {

assertNotNull(identityInfo);
assertEquals("12345@mockedHostName", identityInfo.vmId());
assertEquals(
"d29aca592fc2071bcef6577d649071d4d54a8ae6cd5c0be0e51f28af2867f207",
identityInfo.agentId());
assertNotNull(identityInfo.agentId());
}
}
}
Loading

0 comments on commit 6be3dd2

Please sign in to comment.