Skip to content

Commit 3407175

Browse files
authored
Initial implementation of remote config handling (signalfx#2878)
1 parent 42a70cd commit 3407175

5 files changed

Lines changed: 204 additions & 12 deletions

File tree

opamp/src/main/java/com/splunk/opentelemetry/opamp/OpampActivator.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public class OpampActivator implements AgentListener {
4545

4646
@Override
4747
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
48-
OpampClientConfiguration config =
48+
OpampClientConfiguration opampClientConfiguration =
4949
OpampClientConfigurationFactory.createConfiguration(autoConfiguredOpenTelemetrySdk);
50-
if (!config.isEnabled()) {
50+
if (!opampClientConfiguration.isEnabled()) {
5151
return;
5252
}
5353

@@ -56,12 +56,13 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr
5656
createEffectiveConfigFactory(autoConfiguredOpenTelemetrySdk);
5757
State.EffectiveConfig effectiveConfig = buildEffectiveConfig(effectiveConfigFactory);
5858

59+
ServerToAgentMessageHandler serverToAgentMessageHandler = new ServerToAgentMessageHandler();
60+
5961
OpampClient client =
6062
startOpampClient(
61-
effectiveConfig,
62-
config.getEndpoint(),
63+
opampClientConfiguration,
6364
resource,
64-
config.getPollingInterval(),
65+
effectiveConfig,
6566
new OpampClient.Callbacks() {
6667
@Override
6768
public void onConnect(OpampClient opampClient) {
@@ -81,7 +82,8 @@ public void onErrorResponse(
8182

8283
@Override
8384
public void onMessage(OpampClient opampClient, MessageData messageData) {
84-
logger.fine(messageData::toString);
85+
logger.fine(() -> "Received message: " + messageData);
86+
serverToAgentMessageHandler.handleMessage(messageData, opampClient);
8587
}
8688
});
8789

@@ -105,14 +107,17 @@ private EffectiveConfigFactory createEffectiveConfigFactory(AutoConfiguredOpenTe
105107
}
106108

107109
static OpampClient startOpampClient(
108-
State.EffectiveConfig effectiveConfig,
109-
String endpoint,
110+
OpampClientConfiguration opampClientConfiguration,
110111
Resource resource,
111-
long pollingDurationMillis,
112+
State.EffectiveConfig effectiveConfig,
112113
OpampClient.Callbacks callbacks) {
113114

114115
OpampClientBuilder builder = OpampClient.builder();
115116
builder.enableEffectiveConfigReporting();
117+
builder.enableRemoteConfig();
118+
119+
String endpoint = opampClientConfiguration.getEndpoint();
120+
long pollingDurationMillis = opampClientConfiguration.getPollingInterval();
116121
if (endpoint != null) {
117122
PeriodicDelay pollingDelay =
118123
PeriodicDelay.ofFixedDuration(Duration.ofMillis(pollingDurationMillis));
@@ -121,6 +126,7 @@ static OpampClient startOpampClient(
121126
HttpRequestService.create(okhttp, pollingDelay, DEFAULT_DELAY_BETWEEN_RETRIES);
122127
builder.setRequestService(httpSender);
123128
}
129+
124130
OpampAgentAttributes agentAttributes = new OpampAgentAttributes(resource);
125131
agentAttributes.addIdentifyingAttributes(builder);
126132
agentAttributes.addNonIdentifyingAttributes(builder);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.opamp;
18+
19+
import io.opentelemetry.opamp.client.OpampClient;
20+
import java.util.Map;
21+
import java.util.Objects;
22+
import java.util.logging.Logger;
23+
import opamp.proto.AgentConfigFile;
24+
import opamp.proto.AgentRemoteConfig;
25+
import opamp.proto.RemoteConfigStatus;
26+
import opamp.proto.RemoteConfigStatuses;
27+
28+
public class RemoteConfigProcessor {
29+
private static final Logger logger = Logger.getLogger(RemoteConfigProcessor.class.getName());
30+
31+
private static final String REMOTE_CONFIG_FILE_NAME = "splunk.remote.config";
32+
33+
public void applyConfig(AgentRemoteConfig remoteConfig, OpampClient opampClient) {
34+
Objects.requireNonNull(opampClient);
35+
36+
Map<String, AgentConfigFile> configMap = remoteConfig.config.config_map;
37+
AgentConfigFile configFile = configMap.get(REMOTE_CONFIG_FILE_NAME);
38+
if (configFile == null) {
39+
logger.warning(
40+
"Received server message with remote config, but config is missing '"
41+
+ REMOTE_CONFIG_FILE_NAME
42+
+ "' file. Files provided: "
43+
+ configMap.keySet());
44+
return;
45+
}
46+
47+
// TODO: provide implementation
48+
49+
// Confirm to the OpAMP Server that remote config has been applied.
50+
opampClient.setRemoteConfigStatus(
51+
new RemoteConfigStatus.Builder()
52+
.last_remote_config_hash(remoteConfig.config_hash)
53+
.status(RemoteConfigStatuses.RemoteConfigStatuses_APPLIED)
54+
.build());
55+
}
56+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.opamp;
18+
19+
import com.google.common.annotations.VisibleForTesting;
20+
import io.opentelemetry.opamp.client.OpampClient;
21+
import io.opentelemetry.opamp.client.internal.response.MessageData;
22+
23+
public class ServerToAgentMessageHandler {
24+
private final RemoteConfigProcessor remoteConfigProcessor;
25+
26+
public ServerToAgentMessageHandler() {
27+
this(new RemoteConfigProcessor());
28+
}
29+
30+
@VisibleForTesting
31+
ServerToAgentMessageHandler(RemoteConfigProcessor remoteConfigProcessor) {
32+
this.remoteConfigProcessor = remoteConfigProcessor;
33+
}
34+
35+
public void handleMessage(MessageData message, OpampClient opampClient) {
36+
if (message.getRemoteConfig() != null) {
37+
remoteConfigProcessor.applyConfig(message.getRemoteConfig(), opampClient);
38+
}
39+
}
40+
}

opamp/src/test/java/com/splunk/opentelemetry/opamp/OpampActivatorTest.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,17 @@ void testOpamp() throws Exception {
138138
buildEffectiveConfig(new EnvVarsEffectiveConfigFileFactory(config));
139139

140140
CompletableFuture<MessageData> result = new CompletableFuture<>();
141+
OpampClientConfiguration opampClientConfiguration =
142+
OpampClientConfiguration.builder()
143+
.withEnabled(true)
144+
.withEndpoint(server.httpUri().toString())
145+
.withPollingInterval(500)
146+
.build();
141147
OpampClient client =
142148
OpampActivator.startOpampClient(
143-
effectiveConfig,
144-
server.httpUri().toString(),
149+
opampClientConfiguration,
145150
resource,
146-
500,
151+
effectiveConfig,
147152
new OpampClient.Callbacks() {
148153
@Override
149154
public void onConnect(OpampClient opampClient) {}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.opamp;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.mockito.Mockito.never;
21+
import static org.mockito.Mockito.verify;
22+
23+
import io.opentelemetry.opamp.client.OpampClient;
24+
import java.util.Map;
25+
import okio.ByteString;
26+
import opamp.proto.AgentConfigFile;
27+
import opamp.proto.AgentConfigMap;
28+
import opamp.proto.AgentRemoteConfig;
29+
import opamp.proto.RemoteConfigStatus;
30+
import opamp.proto.RemoteConfigStatuses;
31+
import org.junit.jupiter.api.Test;
32+
import org.mockito.ArgumentCaptor;
33+
34+
class RemoteConfigProcessorTest {
35+
private final RemoteConfigProcessor handler = new RemoteConfigProcessor();
36+
private final OpampClient opampClient = org.mockito.Mockito.mock(OpampClient.class);
37+
38+
@Test
39+
void shouldMarkRemoteConfigAsApplied() {
40+
// given
41+
ByteString configHash = ByteString.encodeUtf8("test-config-hash");
42+
AgentRemoteConfig remoteConfig =
43+
createRemoteConfig(
44+
configHash,
45+
Map.of(
46+
"splunk.remote.config",
47+
new AgentConfigFile.Builder().body(ByteString.encodeUtf8("test-config")).build()));
48+
49+
// when
50+
handler.applyConfig(remoteConfig, opampClient);
51+
52+
// then
53+
ArgumentCaptor<RemoteConfigStatus> statusCaptor =
54+
ArgumentCaptor.forClass(RemoteConfigStatus.class);
55+
verify(opampClient).setRemoteConfigStatus(statusCaptor.capture());
56+
RemoteConfigStatus status = statusCaptor.getValue();
57+
assertThat(status.last_remote_config_hash).isEqualTo(configHash);
58+
assertThat(status.status).isEqualTo(RemoteConfigStatuses.RemoteConfigStatuses_APPLIED);
59+
}
60+
61+
@Test
62+
void shouldIgnoreRemoteConfigWithoutExpectedConfigFile() {
63+
// given
64+
AgentRemoteConfig remoteConfig =
65+
createRemoteConfig(
66+
ByteString.encodeUtf8("test-config-hash"),
67+
Map.of(
68+
"other.config",
69+
new AgentConfigFile.Builder().body(ByteString.encodeUtf8("test-config")).build()));
70+
71+
// when
72+
handler.applyConfig(remoteConfig, opampClient);
73+
74+
// then
75+
verify(opampClient, never()).setRemoteConfigStatus(org.mockito.ArgumentMatchers.any());
76+
}
77+
78+
private static AgentRemoteConfig createRemoteConfig(
79+
ByteString configHash, Map<String, AgentConfigFile> configMap) {
80+
return new AgentRemoteConfig.Builder()
81+
.config_hash(configHash)
82+
.config(new AgentConfigMap.Builder().config_map(configMap).build())
83+
.build();
84+
}
85+
}

0 commit comments

Comments
 (0)