Implement actor client metadata. (#1165)

Signed-off-by: Artur Souza <asouza.pro@gmail.com>
Co-authored-by: Cassie Coyle <cassie@diagrid.io>
This commit is contained in:
Artur Souza 2024-12-03 13:52:14 -08:00 committed by GitHub
parent bd1667b043
commit fcdf3c3188
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 92 additions and 20 deletions

View File

@ -22,6 +22,9 @@ import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.Map;
/**
* Holds a client for Dapr sidecar communication. ActorClient should be reused.
*/
@ -59,7 +62,7 @@ public class ActorClient implements AutoCloseable {
* @param overrideProperties Override properties.
*/
public ActorClient(Properties overrideProperties) {
this(buildManagedChannel(overrideProperties), null, overrideProperties.getValue(Properties.API_TOKEN));
this(overrideProperties, null);
}
/**
@ -69,21 +72,38 @@ public class ActorClient implements AutoCloseable {
* @param resiliencyOptions Client resiliency options.
*/
public ActorClient(Properties overrideProperties, ResiliencyOptions resiliencyOptions) {
this(buildManagedChannel(overrideProperties), resiliencyOptions, overrideProperties.getValue(Properties.API_TOKEN));
this(overrideProperties, null, resiliencyOptions);
}
/**
* Instantiates a new channel for Dapr sidecar communication.
*
* @param overrideProperties Override properties.
* @param metadata gRPC metadata or HTTP headers for actor invocation.
* @param resiliencyOptions Client resiliency options.
*/
public ActorClient(Properties overrideProperties, Map<String, String> metadata, ResiliencyOptions resiliencyOptions) {
this(buildManagedChannel(overrideProperties),
metadata,
resiliencyOptions,
overrideProperties.getValue(Properties.API_TOKEN));
}
/**
* Instantiates a new channel for Dapr sidecar communication.
*
* @param grpcManagedChannel gRPC channel.
* @param metadata gRPC metadata or HTTP headers for actor invocation.
* @param resiliencyOptions Client resiliency options.
* @param daprApiToken Dapr API token.
*/
private ActorClient(
ManagedChannel grpcManagedChannel,
Map<String, String> metadata,
ResiliencyOptions resiliencyOptions,
String daprApiToken) {
this.grpcManagedChannel = grpcManagedChannel;
this.daprClient = buildDaprClient(grpcManagedChannel, resiliencyOptions, daprApiToken);
this.daprClient = buildDaprClient(grpcManagedChannel, metadata, resiliencyOptions, daprApiToken);
}
/**
@ -137,10 +157,12 @@ public class ActorClient implements AutoCloseable {
*/
private static DaprClient buildDaprClient(
Channel grpcManagedChannel,
Map<String, String> metadata,
ResiliencyOptions resiliencyOptions,
String daprApiToken) {
return new DaprClientImpl(
DaprGrpc.newStub(grpcManagedChannel),
metadata == null ? null : Collections.unmodifiableMap(metadata),
resiliencyOptions,
daprApiToken);
}

View File

@ -34,6 +34,7 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoSink;
import reactor.util.context.ContextView;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@ -57,19 +58,30 @@ class DaprClientImpl implements DaprClient {
*/
private final DaprClientGrpcInterceptors grpcInterceptors;
/**
* Metadata for actor invocation requests.
*/
private final Map<String, String> metadata;
/**
* Internal constructor.
*
* @param grpcClient Dapr's GRPC client.
* @param metadata gRPC metadata or HTTP headers for actor server to receive.
* @param resiliencyOptions Client resiliency options (optional).
* @param daprApiToken Dapr API token (optional).
*/
DaprClientImpl(DaprGrpc.DaprStub grpcClient, ResiliencyOptions resiliencyOptions, String daprApiToken) {
DaprClientImpl(
DaprGrpc.DaprStub grpcClient,
Map<String, String> metadata,
ResiliencyOptions resiliencyOptions,
String daprApiToken) {
this.client = grpcClient;
this.grpcInterceptors = new DaprClientGrpcInterceptors(daprApiToken,
new TimeoutPolicy(resiliencyOptions == null ? null : resiliencyOptions.getTimeout()));
this.retryPolicy = new RetryPolicy(
resiliencyOptions == null ? null : resiliencyOptions.getMaxRetries());
this.metadata = metadata == null ? Map.of() : metadata;
}
/**
@ -82,6 +94,7 @@ class DaprClientImpl implements DaprClient {
.setActorType(actorType)
.setActorId(actorId)
.setMethod(methodName)
.putAllMetadata(this.metadata)
.setData(jsonPayload == null ? ByteString.EMPTY : ByteString.copyFrom(jsonPayload))
.build();
return Mono.deferContextual(

View File

@ -106,7 +106,7 @@ public class DaprGrpcClientTest {
InProcessChannelBuilder.forName(serverName).directExecutor().build());
// Create a HelloWorldClient using the in-process channel;
client = new DaprClientImpl(DaprGrpc.newStub(channel), null, null);
client = new DaprClientImpl(DaprGrpc.newStub(channel), null, null, null);
}
@Test

View File

@ -187,11 +187,19 @@ public class DaprRun implements Stoppable {
}
public ActorClient newActorClient() {
return this.newActorClient(null);
return this.newActorClient(null, null);
}
public ActorClient newActorClient(Map<String, String> metadata) {
return this.newActorClient(metadata, null);
}
public ActorClient newActorClient(ResiliencyOptions resiliencyOptions) {
return new ActorClient(new Properties(this.getPropertyOverrides()), resiliencyOptions);
return this.newActorClient(null, resiliencyOptions);
}
public ActorClient newActorClient(Map<String, String> metadata, ResiliencyOptions resiliencyOptions) {
return new ActorClient(new Properties(this.getPropertyOverrides()), metadata, resiliencyOptions);
}
public void waitForAppHealth(int maxWaitMilliseconds) throws InterruptedException {

View File

@ -16,12 +16,17 @@ package io.dapr.it.actors;
import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorProxyBuilder;
import io.dapr.it.BaseIT;
import io.dapr.it.DaprRun;
import io.dapr.it.actors.app.MyActor;
import io.dapr.it.actors.app.MyActorService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import static io.dapr.it.Retry.callWithRetry;
import static io.dapr.it.TestUtils.assertThrowsDaprExceptionSubstring;
@ -30,23 +35,24 @@ public class ActorExceptionIT extends BaseIT {
private static Logger logger = LoggerFactory.getLogger(ActorExceptionIT.class);
@Test
public void exceptionTest() throws Exception {
private static DaprRun run;
@BeforeAll
public static void start() throws Exception {
// The call below will fail if service cannot start successfully.
var run = startDaprApp(
run = startDaprApp(
ActorExceptionIT.class.getSimpleName(),
MyActorService.SUCCESS_MESSAGE,
MyActorService.class,
true,
60000);
}
logger.debug("Creating proxy builder");
@Test
public void exceptionTest() throws Exception {
ActorProxyBuilder<MyActor> proxyBuilder =
new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient()));
logger.debug("Creating actorId");
ActorId actorId1 = new ActorId("1");
logger.debug("Building proxy");
MyActor proxy = proxyBuilder.build(actorId1);
MyActor proxy = proxyBuilder.build(new ActorId("1"));
callWithRetry(() -> {
assertThrowsDaprExceptionSubstring(
@ -55,4 +61,20 @@ public class ActorExceptionIT extends BaseIT {
() -> proxy.throwException());
}, 10000);
}
@Test
public void exceptionDueToMetadataTest() throws Exception {
// Setting this HTTP header via actor metadata will cause the Actor HTTP server to error.
Map<String, String> metadata = Map.of("Content-Length", "9999");
ActorProxyBuilder<MyActor> proxyBuilderMetadataOverride =
new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient(metadata)));
MyActor proxyWithMetadata = proxyBuilderMetadataOverride.build(new ActorId("2"));
callWithRetry(() -> {
assertThrowsDaprExceptionSubstring(
"INTERNAL",
"ContentLength=9999 with Body length 13",
() -> proxyWithMetadata.say("hello world"));
}, 10000);
}
}

View File

@ -15,7 +15,7 @@ package io.dapr.internal.grpc;
import io.dapr.internal.grpc.interceptors.DaprApiTokenInterceptor;
import io.dapr.internal.grpc.interceptors.DaprAppIdInterceptor;
import io.dapr.internal.grpc.interceptors.DaprMetadataInterceptor;
import io.dapr.internal.grpc.interceptors.DaprMetadataReceiverInterceptor;
import io.dapr.internal.grpc.interceptors.DaprTimeoutInterceptor;
import io.dapr.internal.grpc.interceptors.DaprTracingInterceptor;
import io.dapr.internal.resiliency.TimeoutPolicy;
@ -35,10 +35,18 @@ public class DaprClientGrpcInterceptors {
private final TimeoutPolicy timeoutPolicy;
/**
* Instantiates a holder of all gRPC interceptors.
*/
public DaprClientGrpcInterceptors() {
this(null, null);
}
/**
* Instantiates a holder of all gRPC interceptors.
* @param daprApiToken Dapr API token.
* @param timeoutPolicy Timeout Policy.
*/
public DaprClientGrpcInterceptors(String daprApiToken, TimeoutPolicy timeoutPolicy) {
this.daprApiToken = daprApiToken;
this.timeoutPolicy = timeoutPolicy;
@ -118,7 +126,7 @@ public class DaprClientGrpcInterceptors {
new DaprApiTokenInterceptor(this.daprApiToken),
new DaprTimeoutInterceptor(this.timeoutPolicy),
new DaprTracingInterceptor(context),
new DaprMetadataInterceptor(metadataConsumer));
new DaprMetadataReceiverInterceptor(metadataConsumer));
}
}

View File

@ -14,7 +14,6 @@ limitations under the License.
package io.dapr.internal.grpc.interceptors;
import io.dapr.client.Headers;
import io.dapr.config.Properties;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;

View File

@ -27,7 +27,7 @@ import java.util.function.Consumer;
/**
* Consumes gRPC metadata.
*/
public class DaprMetadataInterceptor implements ClientInterceptor {
public class DaprMetadataReceiverInterceptor implements ClientInterceptor {
private final Consumer<Metadata> metadataConsumer;
@ -35,7 +35,7 @@ public class DaprMetadataInterceptor implements ClientInterceptor {
* Creates an instance of the consumer for gRPC metadata.
* @param metadataConsumer gRPC metadata consumer
*/
public DaprMetadataInterceptor(Consumer<Metadata> metadataConsumer) {
public DaprMetadataReceiverInterceptor(Consumer<Metadata> metadataConsumer) {
this.metadataConsumer = metadataConsumer;
}