Cadence® Connection Examples Using TLS

Instaclustr
2 min readDec 11, 2023

--

In Cadence® 0.24.0, TLS support was introduced for the gRPC transport (https://github.com/uber/cadence/pull/4606). In this blog we provide a couple of sample code snippets — one using the Go SDK and the other using the Java SDK — to demonstrate how to establish connection to the Cadence server using TLS. For both the snippets, we assume a X.509 Certificate Authority (CA) certificate from the Cadence server in PEM-encoded (ASCII) format called ca-certificate.pem.

Go

To create a Worker using Go, the following code snippet can be used:

package main
import (
"fmt"
"github.com/uber-go/tally"
apiv1 "github.com/uber/cadence-idl/go/proto/api/v1"
"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
"go.uber.org/cadence/compatibility"
"go.uber.org/cadence/worker"
"go.uber.org/yarpc"
"go.uber.org/yarpc/transport/grpc"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"go.uber.org/yarpc/peer"
"go.uber.org/yarpc/peer/hostport"
"google.golang.org/grpc/credentials"
)

var HostPort = "<HOST>:<PORT>"
var Domain = "<DOMAIN>"
var TaskListName = "<TASK>"
var ClientName = "<CLIENT>"
var CadenceService = "cadence-frontend"

func main() {
…………………
…………………
…………………
worker := getWorker(buildLogger(), buildCadenceClient())
…………………
…………………
…………………
}

func buildLogger() *zap.Logger {
config := zap.NewDevelopmentConfig()
config.Level.SetLevel(zapcore.InfoLevel)

var err error
logger, err := config.Build()
if err != nil {
panic("Failed to setup logger")
}

return logger
}

func buildCadenceClient() workflowserviceclient.Interface {
grpcTransport := grpc.NewTransport()
var dialOptions []grpc.DialOption

caCert, err := ioutil.ReadFile("ca-certificate.pem")
if err != nil {
fmt.Printf("Failed to load server CA certificate: %v", zap.Error(err))
}

caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
fmt.Errorf("Failed to add server CA's certificate")
}

tlsConfig := tls.Config{
RootCAs: caCertPool,
}

creds := credentials.NewTLS(&tlsConfig)
dialOptions = append(dialOptions, grpc.DialerCredentials(creds))

dialer := grpcTransport.NewDialer(dialOptions...)
outbound := grpcTransport.NewOutbound(
peer.NewSingle(hostport.PeerIdentifier(HostPort), dialer)
)

dispatcher := yarpc.NewDispatcher(yarpc.Config{
Name: ClientName,
Outbounds: yarpc.Outbounds{
CadenceService: {Unary: outbound},
},
})
if err := dispatcher.Start(); err != nil {
panic("Failed to start dispatcher")
}

clientConfig := dispatcher.ClientConfig(CadenceService)

return compatibility.NewThrift2ProtoAdapter(
apiv1.NewDomainAPIYARPCClient(clientConfig),
apiv1.NewWorkflowAPIYARPCClient(clientConfig),
apiv1.NewWorkerAPIYARPCClient(clientConfig),
apiv1.NewVisibilityAPIYARPCClient(clientConfig),
)
}

func getWorker(logger *zap.Logger, service workflowserviceclient.Interface) worker.Worker {
workerOptions := worker.Options{
Logger: logger,
MetricsScope: tally.NewTestScope(TaskListName, map[string]string{}),
}

worker := worker.New(
service,
Domain,
TaskListName,
workerOptions)

return worker
}

Java

In order to use the Java SDK to establish a connection to the Cadence server over TLS, the following Maven dependencies are needed:

<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>com.uber.cadence</groupId>
<artifactId>cadence-client</artifactId>
<version>3.7.2</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.17.Final</version>
</dependency>

The following code snippet demonstrates how to create a new Worker.

<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>com.uber.cadence</groupId>
<artifactId>cadence-client</artifactId>
<version>3.7.2</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.17.Final</version>
</dependency>
The following code snippet demonstrates how to create a new Worker.

import com.uber.cadence.client.WorkflowClient;
import com.uber.cadence.client.WorkflowClientOptions;
import com.uber.cadence.internal.compatibility.Thrift2ProtoAdapter;
import com.uber.cadence.internal.compatibility.proto.serviceclient.IGrpcServiceStubs;
import com.uber.cadence.serviceclient.ClientOptions;
import com.uber.cadence.worker.Worker;
import com.uber.cadence.worker.WorkerFactory;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NettyChannelBuilder;
import java.io.File;

……………………
……………………
……………………

WorkflowClient workflowClient =
WorkflowClient.newInstance(
new Thrift2ProtoAdapter(
IGrpcServiceStubs.newInstance(
ClientOptions.newBuilder()
.setGRPCChannel(
NettyChannelBuilder.forAddress("<HOST>", <PORT>)
.useTransportSecurity()
.defaultLoadBalancingPolicy("round_robin")
.sslContext(GrpcSslContexts.forClient()
.trustManager(new File("ca-certificate.pem"))
.build()
)
.build()
)
.build()
)
),
WorkflowClientOptions.newBuilder()
.setDomain("<DOMAIN>")
.build()
);

WorkerFactory factory = WorkerFactory.newInstance(workflowClient);
Worker worker = factory.newWorker("<ACTIVITY>");

……………………
……………………
……………………

Originally published at https://www.instaclustr.com on December 2nd, 2022.

--

--

Instaclustr
Instaclustr

Written by Instaclustr

Managed platform for open source technologies including Apache Cassandra, Apache Kafka, Apache ZooKeepere, Redis, Elasticsearch and PostgreSQL

No responses yet