Skip to content

Commit 49e9052

Browse files
committed
add opamp command processor
1 parent c8592f9 commit 49e9052

4 files changed

Lines changed: 108 additions & 7 deletions

File tree

opamp-client/src/main/java/io/opentelemetry/opamp/client/OpampClient.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import opamp.proto.AgentDescription;
1212
import opamp.proto.RemoteConfigStatus;
1313
import opamp.proto.ServerErrorResponse;
14+
import opamp.proto.ServerToAgentCommand;
1415

1516
public interface OpampClient extends Closeable {
1617

@@ -79,4 +80,14 @@ interface Callbacks {
7980
*/
8081
void onMessage(OpampClient client, MessageData messageData);
8182
}
83+
84+
@FunctionalInterface
85+
interface CommandProcessor {
86+
/**
87+
* Called when the Agent receives a command from the Server.
88+
*
89+
* @param command The command requested by the Server.
90+
*/
91+
void onCommand(ServerToAgentCommand command);
92+
}
8293
}

opamp-client/src/main/java/io/opentelemetry/opamp/client/OpampClientBuilder.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public final class OpampClientBuilder {
3737
HttpRequestService.create(OkHttpSender.create("http://localhost:4320/v1/opamp"));
3838
@Nullable private byte[] instanceUid;
3939
@Nullable private State.EffectiveConfig effectiveConfigState;
40+
@Nullable private OpampClient.CommandProcessor commandProcessor;
4041

4142
OpampClientBuilder() {}
4243

@@ -376,6 +377,20 @@ public OpampClientBuilder setEffectiveConfigState(State.EffectiveConfig effectiv
376377
return this;
377378
}
378379

380+
/**
381+
* Sets the command processor. This command processor will be called for every ServerToAgent
382+
* message that contains a non-null command.
383+
*
384+
* @param commandProcessor The command processor.
385+
* @return this
386+
*/
387+
@CanIgnoreReturnValue
388+
public OpampClientBuilder setCommandProcessor(
389+
@Nullable OpampClient.CommandProcessor commandProcessor) {
390+
this.commandProcessor = commandProcessor;
391+
return this;
392+
}
393+
379394
public OpampClient build(OpampClient.Callbacks callbacks) {
380395
List<KeyValue> protoIdentifyingAttributes = new ArrayList<>();
381396
List<KeyValue> protoNonIdentifyingAttributes = new ArrayList<>();
@@ -402,7 +417,7 @@ public OpampClient build(OpampClient.Callbacks callbacks) {
402417
new State.InstanceUid(instanceUid),
403418
new State.Flags(0L),
404419
effectiveConfigState);
405-
return OpampClientImpl.create(service, state, callbacks);
420+
return OpampClientImpl.create(service, state, callbacks, commandProcessor);
406421
}
407422

408423
private static State.EffectiveConfig createEffectiveConfigNoop() {

opamp-client/src/main/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImpl.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.concurrent.atomic.AtomicBoolean;
3232
import java.util.function.Supplier;
3333
import javax.annotation.Nonnull;
34+
import javax.annotation.Nullable;
3435
import okio.ByteString;
3536
import opamp.proto.AgentDescription;
3637
import opamp.proto.AgentToServer;
@@ -45,12 +46,16 @@
4546
*/
4647
public final class OpampClientImpl
4748
implements OpampClient, ObservableState.Listener, RequestService.Callback, Supplier<Request> {
49+
50+
private static final CommandProcessor NOOP_COMMAND_PROCESSOR = command -> {};
51+
4852
private final RequestService requestService;
4953
private final AgentToServerAppenders appenders;
5054
private final OpampClientState state;
5155
private final RecipeManager recipeManager;
5256
private final AtomicBoolean hasStopped = new AtomicBoolean(false);
5357
private final Callbacks callbacks;
58+
private final CommandProcessor commandProcessor;
5459

5560
/** Fields that must always be sent. */
5661
private static final List<Field> REQUIRED_FIELDS;
@@ -83,6 +88,16 @@ public final class OpampClientImpl
8388

8489
public static OpampClientImpl create(
8590
RequestService requestService, OpampClientState state, Callbacks callbacks) {
91+
return create(requestService, state, callbacks, NOOP_COMMAND_PROCESSOR);
92+
}
93+
94+
public static OpampClientImpl create(
95+
RequestService requestService,
96+
OpampClientState state,
97+
Callbacks callbacks,
98+
@Nullable CommandProcessor commandProcessor) {
99+
CommandProcessor nonNullCommandProcessor =
100+
commandProcessor == null ? NOOP_COMMAND_PROCESSOR : commandProcessor;
86101
AgentToServerAppenders appenders =
87102
new AgentToServerAppenders(
88103
AgentDescriptionAppender.create(state.agentDescription),
@@ -95,7 +110,12 @@ public static OpampClientImpl create(
95110
AgentDisconnectAppender.create());
96111
OpampClientImpl client =
97112
new OpampClientImpl(
98-
requestService, appenders, state, RecipeManager.create(REQUIRED_FIELDS), callbacks);
113+
requestService,
114+
appenders,
115+
state,
116+
RecipeManager.create(REQUIRED_FIELDS),
117+
callbacks,
118+
nonNullCommandProcessor);
99119

100120
// Start
101121
requestService.start(client, client);
@@ -111,12 +131,14 @@ private OpampClientImpl(
111131
AgentToServerAppenders appenders,
112132
OpampClientState state,
113133
RecipeManager recipeManager,
114-
Callbacks callbacks) {
134+
Callbacks callbacks,
135+
CommandProcessor commandProcessor) {
115136
this.requestService = requestService;
116137
this.appenders = appenders;
117138
this.state = state;
118139
this.recipeManager = recipeManager;
119140
this.callbacks = callbacks;
141+
this.commandProcessor = commandProcessor;
120142
}
121143

122144
@Override
@@ -186,16 +208,15 @@ private void handleResponsePayload(ServerToAgent response) {
186208
}
187209
handleAgentIdentification(response);
188210

189-
boolean notifyOnMessage = false;
190211
MessageData.Builder messageBuilder = MessageData.builder();
191212

192213
if (response.remote_config != null) {
193-
notifyOnMessage = true;
194214
messageBuilder.setRemoteConfig(response.remote_config);
215+
callbacks.onMessage(this, messageBuilder.build());
195216
}
196217

197-
if (notifyOnMessage) {
198-
callbacks.onMessage(this, messageBuilder.build());
218+
if (response.command != null) {
219+
commandProcessor.onCommand(response.command);
199220
}
200221
}
201222

opamp-client/src/test/java/io/opentelemetry/opamp/client/internal/impl/OpampClientImplTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Objects;
3030
import java.util.concurrent.TimeUnit;
3131
import java.util.concurrent.atomic.AtomicInteger;
32+
import java.util.concurrent.atomic.AtomicReference;
3233
import java.util.function.Supplier;
3334
import javax.annotation.Nonnull;
3435
import mockwebserver3.MockResponse;
@@ -51,6 +52,7 @@
5152
import opamp.proto.RemoteConfigStatuses;
5253
import opamp.proto.ServerErrorResponse;
5354
import opamp.proto.ServerToAgent;
55+
import opamp.proto.ServerToAgentCommand;
5456
import opamp.proto.ServerToAgentFlags;
5557
import org.jetbrains.annotations.Nullable;
5658
import org.junit.jupiter.api.AfterEach;
@@ -219,6 +221,36 @@ void onSuccess_withNoChangesToReport_doNotNotifyCallbackOnMessage() {
219221
verify(callbacks, never()).onMessage(eq(client), any());
220222
}
221223

224+
@Test
225+
void onSuccess_withCommand_notifyCommandProcessor() {
226+
TestCommandProcessor commandProcessor = new TestCommandProcessor();
227+
initializeClient(new ServerToAgent.Builder().build(), commandProcessor);
228+
ServerToAgentCommand command = new ServerToAgentCommand.Builder().build();
229+
enqueueServerToAgentResponse(new ServerToAgent.Builder().command(command).build());
230+
231+
// Force request
232+
requestService.sendRequest();
233+
234+
await().atMost(Duration.ofSeconds(5)).until(() -> commandProcessor.onCommandCalls.get() == 1);
235+
236+
assertThat(commandProcessor.latestCommand.get()).isEqualTo(command);
237+
verify(callbacks, never()).onMessage(eq(client), any());
238+
}
239+
240+
@Test
241+
void onSuccess_withNoCommand_doNotNotifyCommandProcessor() {
242+
TestCommandProcessor commandProcessor = new TestCommandProcessor();
243+
initializeClient(new ServerToAgent.Builder().build(), commandProcessor);
244+
enqueueServerToAgentResponse(new ServerToAgent.Builder().build());
245+
246+
// Force request
247+
requestService.sendRequest();
248+
249+
await().atMost(Duration.ofSeconds(5)).until(() -> callbacks.onConnectCalls.get() == 2);
250+
251+
assertThat(commandProcessor.onCommandCalls.get()).isZero();
252+
}
253+
222254
@Test
223255
void verifyAgentDescriptionSetter() {
224256
initializeClient();
@@ -402,6 +434,17 @@ private RecordedRequest initializeClient(ServerToAgent initialResponse) {
402434
return takeRequest();
403435
}
404436

437+
private RecordedRequest initializeClient(
438+
ServerToAgent initialResponse, OpampClient.CommandProcessor commandProcessor) {
439+
// Prepare first request on start
440+
enqueueServerToAgentResponse(initialResponse);
441+
442+
callbacks = spy(new TestCallbacks());
443+
client = OpampClientImpl.create(requestService, state, callbacks, commandProcessor);
444+
445+
return takeRequest();
446+
}
447+
405448
private static class TestEffectiveConfig extends State.EffectiveConfig {
406449
private opamp.proto.EffectiveConfig config;
407450

@@ -471,4 +514,15 @@ public void onMessage(OpampClient client, MessageData messageData) {
471514
onMessageCalls.incrementAndGet();
472515
}
473516
}
517+
518+
private static class TestCommandProcessor implements OpampClient.CommandProcessor {
519+
private final AtomicInteger onCommandCalls = new AtomicInteger();
520+
private final AtomicReference<ServerToAgentCommand> latestCommand = new AtomicReference<>();
521+
522+
@Override
523+
public void onCommand(ServerToAgentCommand command) {
524+
latestCommand.set(command);
525+
onCommandCalls.incrementAndGet();
526+
}
527+
}
474528
}

0 commit comments

Comments
 (0)