From 5bc024b1e8647f4cd832f2095c1d6f1763166fec Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Fri, 10 Oct 2025 14:59:59 -0700 Subject: [PATCH 1/6] TLS Helloworld sample for gRPC calls to cadence --- .gitignore | 3 + .../client_samples/helloworld_tls/README.md | 41 +++++++++ .../credentials/download-certs.sh | 52 ++++++++++++ .../helloworld_tls/hello_world_tls.go | 85 +++++++++++++++++++ .../helloworld_tls/register_domain.go | 73 ++++++++++++++++ new_samples/worker/worker.go | 13 ++- 6 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 new_samples/client_samples/helloworld_tls/README.md create mode 100644 new_samples/client_samples/helloworld_tls/credentials/download-certs.sh create mode 100644 new_samples/client_samples/helloworld_tls/hello_world_tls.go create mode 100644 new_samples/client_samples/helloworld_tls/register_domain.go diff --git a/.gitignore b/.gitignore index 33bb963..d08c137 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ vendor/ # Executables produced by cadence-samples repo bin/ docker-compose.yml + +# Credentials +new_samples/client_samples/helloworld_tls/credentials/ diff --git a/new_samples/client_samples/helloworld_tls/README.md b/new_samples/client_samples/helloworld_tls/README.md new file mode 100644 index 0000000..4285cc7 --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/README.md @@ -0,0 +1,41 @@ +## Running the Sample + +### Step 1: Download Certificates +```bash +cd new_samples/client_samples/helloworld_tls/credentials +chmod +x download-certs.sh +./download-certs.sh +cd .. +``` + +### Step 2: Register the Domain +Before running workflows, you must register the "default" domain: + +```bash +cd new_samples/client_samples/helloworld_tls +go run register_domain.go +``` + +Expected output: +``` +Successfully registered domain {"domain": "default"} +``` + +If the domain already exists, you'll see: +``` +Domain already exists {"domain": "default"} +``` + +### Step 3: Run the Sample +In another terminal: +```bash +cd new_samples/client_samples/helloworld_tls +go run hello_world_tls.go +``` + +## References + +- [Cadence Official Certificates](https://github.com/cadence-workflow/cadence/tree/master/config/credentials) +- [Cadence Documentation](https://cadenceworkflow.io/) +- [Go TLS Package](https://pkg.go.dev/crypto/tls) + diff --git a/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh b/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh new file mode 100644 index 0000000..ed77985 --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Script to download certificates from Cadence repository +# Based on: https://github.com/cadence-workflow/cadence/tree/master/config/credentials + +set -e + +echo "Downloading certificates from Cadence repository..." + +BASE_URL="https://raw.githubusercontent.com/cadence-workflow/cadence/master/config/credentials" + +# Download all certificate files +echo "Downloading ca.cert..." +curl -L -o ca.cert "${BASE_URL}/ca.cert" + +echo "Downloading server.cert..." +curl -L -o server.cert "${BASE_URL}/server.cert" + +echo "Downloading server.key..." +curl -L -o server.key "${BASE_URL}/server.key" + +echo "Downloading client.cert..." +curl -L -o client.cert "${BASE_URL}/client.cert" + +echo "Downloading client.key..." +curl -L -o client.key "${BASE_URL}/client.key" + +# Also try to get .pem versions if they exist +echo "Trying to download .pem versions..." +curl -L -o ca.pem "${BASE_URL}/ca.pem" 2>/dev/null || echo "ca.pem not found, skipping" +curl -L -o server.pem "${BASE_URL}/server.pem" 2>/dev/null || echo "server.pem not found, skipping" +curl -L -o client.pem "${BASE_URL}/client.pem" 2>/dev/null || echo "client.pem not found, skipping" + +# Create symlinks for compatibility with existing code +if [ -f "client.cert" ]; then + ln -sf client.cert client.crt 2>/dev/null || cp client.cert client.crt +fi + +if [ -f "ca.cert" ]; then + ln -sf ca.cert keytest.crt 2>/dev/null || cp ca.cert keytest.crt +fi + +echo "" +echo "✓ Certificates downloaded successfully from Cadence repository!" +echo "" +echo "Downloaded files:" +ls -lh *.cert *.key 2>/dev/null || true +ls -lh *.pem 2>/dev/null || true +ls -lh *.crt 2>/dev/null || true +echo "" +echo "These are the official Cadence test certificates." +echo "Reference: https://github.com/cadence-workflow/cadence/tree/master/config/credentials" + diff --git a/new_samples/client_samples/helloworld_tls/hello_world_tls.go b/new_samples/client_samples/helloworld_tls/hello_world_tls.go new file mode 100644 index 0000000..a65fab5 --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/hello_world_tls.go @@ -0,0 +1,85 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "os" + "time" + + "github.com/google/uuid" + "github.com/uber-common/cadence-samples/new_samples/worker" + "go.uber.org/cadence/.gen/go/shared" + "go.uber.org/yarpc/transport/grpc" + "go.uber.org/zap" + "google.golang.org/grpc/credentials" +) + +func main() { + withTLSDialOption, err := withTLSDialOption() + if err != nil { + panic(err) + } + cadenceClient := worker.BuildCadenceClient(withTLSDialOption) + logger := worker.BuildLogger() + + domain := "default" + tasklist := "default-tasklist" + workflowID := uuid.New().String() + requestID := uuid.New().String() + executionTimeout := int32(60) + closeTimeout := int32(60) + + workflowType := "cadence_samples.HelloWorldWorkflow" + input := []byte(`{"message": "Uber"}`) + + req := shared.StartWorkflowExecutionRequest{ + Domain: &domain, + WorkflowId: &workflowID, + WorkflowType: &shared.WorkflowType{ + Name: &workflowType, + }, + TaskList: &shared.TaskList{ + Name: &tasklist, + }, + Input: input, + ExecutionStartToCloseTimeoutSeconds: &executionTimeout, + TaskStartToCloseTimeoutSeconds: &closeTimeout, + RequestId: &requestID, + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + resp, err := cadenceClient.StartWorkflowExecution(ctx, &req) + if err != nil { + logger.Error("Failed to create workflow", zap.Error(err)) + panic("Failed to create workflow.") + } + + logger.Info("successfully started HelloWorld workflow", zap.String("runID", resp.GetRunId())) +} + +func withTLSDialOption() (grpc.DialOption, error) { + // Present client cert for mutual TLS (if enabled on server) + clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key") + if err != nil { + return nil, fmt.Errorf("Failed to load client certificate: %v", zap.Error(err)) + } + + // Load server CA + caCert, err := os.ReadFile("credentials/keytest.crt") + if err != nil { + return nil, fmt.Errorf("Failed to load server CA certificate: %v", zap.Error(err)) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + tlsConfig := tls.Config{ + InsecureSkipVerify: true, + RootCAs: caCertPool, + Certificates: []tls.Certificate{clientCert}, + } + creds := credentials.NewTLS(&tlsConfig) + grpc.DialerCredentials(creds) + return grpc.DialerCredentials(creds), nil +} diff --git a/new_samples/client_samples/helloworld_tls/register_domain.go b/new_samples/client_samples/helloworld_tls/register_domain.go new file mode 100644 index 0000000..261f64e --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/register_domain.go @@ -0,0 +1,73 @@ +package main + +// func main() { +// logger := worker.BuildLogger() +// logger.Info("Registering default domain for cadence-vishwa with TLS...") + +// withTLSDialOption, err := buildTLSDialOption() +// if err != nil { +// logger.Fatal("Failed to build TLS dial option", zap.Error(err)) +// } + +// cadenceClient := worker.BuildCadenceClient(withTLSDialOption) + +// // Register the domain +// domain := "default" +// retentionDays := int32(7) +// emitMetric := true + +// req := &shared.RegisterDomainRequest{ +// Name: &domain, +// Description: stringPtr("Default domain for cadence samples"), +// WorkflowExecutionRetentionPeriodInDays: &retentionDays, +// EmitMetric: &emitMetric, +// } + +// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// defer cancel() + +// err = cadenceClient.RegisterDomain(ctx, req) +// if err != nil { +// // Check if domain already exists +// if _, ok := err.(*shared.DomainAlreadyExistsError); ok { +// logger.Info("Domain already exists", zap.String("domain", domain)) +// return +// } +// logger.Fatal("Failed to register domain", zap.Error(err)) +// } + +// logger.Info("Successfully registered domain", zap.String("domain", domain)) +// } + +// func buildTLSDialOption() (grpc.DialOption, error) { +// // Load client certificate +// clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key") +// if err != nil { +// return nil, fmt.Errorf("failed to load client certificate: %w", err) +// } + +// // Load server CA +// caCert, err := os.ReadFile("credentials/keytest.crt") +// if err != nil { +// return nil, fmt.Errorf("failed to load server CA certificate: %w", err) +// } + +// caCertPool := x509.NewCertPool() +// if !caCertPool.AppendCertsFromPEM(caCert) { +// return nil, fmt.Errorf("failed to append CA certificate") +// } + +// tlsConfig := &tls.Config{ +// InsecureSkipVerify: true, +// RootCAs: caCertPool, +// Certificates: []tls.Certificate{clientCert}, +// MinVersion: tls.VersionTLS12, +// } + +// creds := credentials.NewTLS(tlsConfig) +// return grpc.DialerCredentials(creds), nil +// } + +// func stringPtr(s string) *string { +// return &s +// } diff --git a/new_samples/worker/worker.go b/new_samples/worker/worker.go index 20f494c..b3901c6 100644 --- a/new_samples/worker/worker.go +++ b/new_samples/worker/worker.go @@ -11,6 +11,8 @@ import ( "go.uber.org/cadence/worker" "go.uber.org/cadence/workflow" "go.uber.org/yarpc" + "go.uber.org/yarpc/peer" + yarpchostport "go.uber.org/yarpc/peer/hostport" "go.uber.org/yarpc/transport/grpc" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -69,11 +71,18 @@ func StartWorker() { } -func BuildCadenceClient() workflowserviceclient.Interface { +func BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface { + grpcTransport := grpc.NewTransport() + myChooser := peer.NewSingle( + yarpchostport.Identify(HostPort), + grpcTransport.NewDialer(dialOptions...), + ) + outbound := grpcTransport.NewOutbound(myChooser) + dispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: ClientName, Outbounds: yarpc.Outbounds{ - CadenceService: {Unary: grpc.NewTransport().NewSingleOutbound(HostPort)}, + CadenceService: {Unary: outbound}, }, }) if err := dispatcher.Start(); err != nil { From 70cbd1da76748465153b4cd69cdffe9808fb5190 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Fri, 10 Oct 2025 15:02:47 -0700 Subject: [PATCH 2/6] TLS Helloworld sample for gRPC calls to cadence: fix --- new_samples/client_samples/helloworld_tls/hello_world_tls.go | 1 + 1 file changed, 1 insertion(+) diff --git a/new_samples/client_samples/helloworld_tls/hello_world_tls.go b/new_samples/client_samples/helloworld_tls/hello_world_tls.go index a65fab5..39c8d6e 100644 --- a/new_samples/client_samples/helloworld_tls/hello_world_tls.go +++ b/new_samples/client_samples/helloworld_tls/hello_world_tls.go @@ -21,6 +21,7 @@ func main() { if err != nil { panic(err) } + cadenceClient := worker.BuildCadenceClient(withTLSDialOption) logger := worker.BuildLogger() From 1e1e1e78c014b3a4a982c53e161867a33a6b3cdc Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Sun, 12 Oct 2025 18:57:06 -0700 Subject: [PATCH 3/6] feat: update the read me with server Pre-requisites --- .../client_samples/helloworld_tls/README.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/new_samples/client_samples/helloworld_tls/README.md b/new_samples/client_samples/helloworld_tls/README.md index 4285cc7..31dce02 100644 --- a/new_samples/client_samples/helloworld_tls/README.md +++ b/new_samples/client_samples/helloworld_tls/README.md @@ -1,3 +1,32 @@ +## Pre-requisites + +Follow this document to start cadence server: +https://github.com/cadence-workflow/cadence/blob/e1267de12f8bc670fc84fab456d3495c8fc2f8a8/CONTRIBUTING.md#L1 + +1. **Build tools in cadence server** + ```bash + make bins + ``` + +2. **Start cassandra** + ```bash + docker compose -f ./docker/dev/cassandra.yml up -d + ``` + +3. **Install schema** + ```bash + make install-schema + ``` + +4. **Start cadence server with TLS** + ```bash + ./cadence-server --zone tls start + ``` + Or + ```bash + ./cadence-server --env development --zone tls start + ``` + ## Running the Sample ### Step 1: Download Certificates From d8b48e2ffc580ddb2c76b9607c2b8c3660b01833 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Sun, 12 Oct 2025 19:01:08 -0700 Subject: [PATCH 4/6] feat: update the read me with server Pre-requisites --- .../helloworld_tls/register_domain.go | 157 ++++++++++-------- 1 file changed, 86 insertions(+), 71 deletions(-) diff --git a/new_samples/client_samples/helloworld_tls/register_domain.go b/new_samples/client_samples/helloworld_tls/register_domain.go index 261f64e..8ab832b 100644 --- a/new_samples/client_samples/helloworld_tls/register_domain.go +++ b/new_samples/client_samples/helloworld_tls/register_domain.go @@ -1,73 +1,88 @@ package main -// func main() { -// logger := worker.BuildLogger() -// logger.Info("Registering default domain for cadence-vishwa with TLS...") - -// withTLSDialOption, err := buildTLSDialOption() -// if err != nil { -// logger.Fatal("Failed to build TLS dial option", zap.Error(err)) -// } - -// cadenceClient := worker.BuildCadenceClient(withTLSDialOption) - -// // Register the domain -// domain := "default" -// retentionDays := int32(7) -// emitMetric := true - -// req := &shared.RegisterDomainRequest{ -// Name: &domain, -// Description: stringPtr("Default domain for cadence samples"), -// WorkflowExecutionRetentionPeriodInDays: &retentionDays, -// EmitMetric: &emitMetric, -// } - -// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) -// defer cancel() - -// err = cadenceClient.RegisterDomain(ctx, req) -// if err != nil { -// // Check if domain already exists -// if _, ok := err.(*shared.DomainAlreadyExistsError); ok { -// logger.Info("Domain already exists", zap.String("domain", domain)) -// return -// } -// logger.Fatal("Failed to register domain", zap.Error(err)) -// } - -// logger.Info("Successfully registered domain", zap.String("domain", domain)) -// } - -// func buildTLSDialOption() (grpc.DialOption, error) { -// // Load client certificate -// clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key") -// if err != nil { -// return nil, fmt.Errorf("failed to load client certificate: %w", err) -// } - -// // Load server CA -// caCert, err := os.ReadFile("credentials/keytest.crt") -// if err != nil { -// return nil, fmt.Errorf("failed to load server CA certificate: %w", err) -// } - -// caCertPool := x509.NewCertPool() -// if !caCertPool.AppendCertsFromPEM(caCert) { -// return nil, fmt.Errorf("failed to append CA certificate") -// } - -// tlsConfig := &tls.Config{ -// InsecureSkipVerify: true, -// RootCAs: caCertPool, -// Certificates: []tls.Certificate{clientCert}, -// MinVersion: tls.VersionTLS12, -// } - -// creds := credentials.NewTLS(tlsConfig) -// return grpc.DialerCredentials(creds), nil -// } - -// func stringPtr(s string) *string { -// return &s -// } +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "os" + "time" + + "github.com/uber-common/cadence-samples/new_samples/worker" + "go.uber.org/cadence/.gen/go/shared" + "go.uber.org/yarpc/transport/grpc" + "go.uber.org/zap" + "google.golang.org/grpc/credentials" +) + +func main() { + logger := worker.BuildLogger() + logger.Info("Registering default domain for cadence-vishwa with TLS...") + + withTLSDialOption, err := buildTLSDialOption() + if err != nil { + logger.Fatal("Failed to build TLS dial option", zap.Error(err)) + } + + cadenceClient := worker.BuildCadenceClient(withTLSDialOption) + + // Register the domain + domain := "default" + retentionDays := int32(7) + emitMetric := true + + req := &shared.RegisterDomainRequest{ + Name: &domain, + Description: stringPtr("Default domain for cadence samples"), + WorkflowExecutionRetentionPeriodInDays: &retentionDays, + EmitMetric: &emitMetric, + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + err = cadenceClient.RegisterDomain(ctx, req) + if err != nil { + // Check if domain already exists + if _, ok := err.(*shared.DomainAlreadyExistsError); ok { + logger.Info("Domain already exists", zap.String("domain", domain)) + return + } + logger.Fatal("Failed to register domain", zap.Error(err)) + } + + logger.Info("Successfully registered domain", zap.String("domain", domain)) +} + +func buildTLSDialOption() (grpc.DialOption, error) { + // Load client certificate + clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key") + if err != nil { + return nil, fmt.Errorf("failed to load client certificate: %w", err) + } + + // Load server CA + caCert, err := os.ReadFile("credentials/keytest.crt") + if err != nil { + return nil, fmt.Errorf("failed to load server CA certificate: %w", err) + } + + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCert) { + return nil, fmt.Errorf("failed to append CA certificate") + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + RootCAs: caCertPool, + Certificates: []tls.Certificate{clientCert}, + MinVersion: tls.VersionTLS12, + } + + creds := credentials.NewTLS(tlsConfig) + return grpc.DialerCredentials(creds), nil +} + +func stringPtr(s string) *string { + return &s +} From 4991fabcf127626def06d9e5dc7487d87464475c Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 13 Oct 2025 10:58:40 -0700 Subject: [PATCH 5/6] Accomodating review comments --- .../client_samples/helloworld_tls/README.md | 7 +-- .../credentials/download-certs.sh | 52 ------------------- 2 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 new_samples/client_samples/helloworld_tls/credentials/download-certs.sh diff --git a/new_samples/client_samples/helloworld_tls/README.md b/new_samples/client_samples/helloworld_tls/README.md index 31dce02..2ef7c6f 100644 --- a/new_samples/client_samples/helloworld_tls/README.md +++ b/new_samples/client_samples/helloworld_tls/README.md @@ -19,10 +19,6 @@ https://github.com/cadence-workflow/cadence/blob/e1267de12f8bc670fc84fab456d3495 ``` 4. **Start cadence server with TLS** - ```bash - ./cadence-server --zone tls start - ``` - Or ```bash ./cadence-server --env development --zone tls start ``` @@ -32,8 +28,7 @@ https://github.com/cadence-workflow/cadence/blob/e1267de12f8bc670fc84fab456d3495 ### Step 1: Download Certificates ```bash cd new_samples/client_samples/helloworld_tls/credentials -chmod +x download-certs.sh -./download-certs.sh +Download certificates from config/credentials of cadence server and place them here cd .. ``` diff --git a/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh b/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh deleted file mode 100644 index ed77985..0000000 --- a/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -# Script to download certificates from Cadence repository -# Based on: https://github.com/cadence-workflow/cadence/tree/master/config/credentials - -set -e - -echo "Downloading certificates from Cadence repository..." - -BASE_URL="https://raw.githubusercontent.com/cadence-workflow/cadence/master/config/credentials" - -# Download all certificate files -echo "Downloading ca.cert..." -curl -L -o ca.cert "${BASE_URL}/ca.cert" - -echo "Downloading server.cert..." -curl -L -o server.cert "${BASE_URL}/server.cert" - -echo "Downloading server.key..." -curl -L -o server.key "${BASE_URL}/server.key" - -echo "Downloading client.cert..." -curl -L -o client.cert "${BASE_URL}/client.cert" - -echo "Downloading client.key..." -curl -L -o client.key "${BASE_URL}/client.key" - -# Also try to get .pem versions if they exist -echo "Trying to download .pem versions..." -curl -L -o ca.pem "${BASE_URL}/ca.pem" 2>/dev/null || echo "ca.pem not found, skipping" -curl -L -o server.pem "${BASE_URL}/server.pem" 2>/dev/null || echo "server.pem not found, skipping" -curl -L -o client.pem "${BASE_URL}/client.pem" 2>/dev/null || echo "client.pem not found, skipping" - -# Create symlinks for compatibility with existing code -if [ -f "client.cert" ]; then - ln -sf client.cert client.crt 2>/dev/null || cp client.cert client.crt -fi - -if [ -f "ca.cert" ]; then - ln -sf ca.cert keytest.crt 2>/dev/null || cp ca.cert keytest.crt -fi - -echo "" -echo "✓ Certificates downloaded successfully from Cadence repository!" -echo "" -echo "Downloaded files:" -ls -lh *.cert *.key 2>/dev/null || true -ls -lh *.pem 2>/dev/null || true -ls -lh *.crt 2>/dev/null || true -echo "" -echo "These are the official Cadence test certificates." -echo "Reference: https://github.com/cadence-workflow/cadence/tree/master/config/credentials" - From 1a12e09dfe5ab0be0c48ca1fd6f9b1074ec6d258 Mon Sep 17 00:00:00 2001 From: "vpatil16@ext.uber.com" Date: Mon, 13 Oct 2025 12:01:18 -0700 Subject: [PATCH 6/6] Accomodating review comments --- new_samples/client_samples/helloworld_tls/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/new_samples/client_samples/helloworld_tls/README.md b/new_samples/client_samples/helloworld_tls/README.md index 2ef7c6f..cf835f5 100644 --- a/new_samples/client_samples/helloworld_tls/README.md +++ b/new_samples/client_samples/helloworld_tls/README.md @@ -26,10 +26,10 @@ https://github.com/cadence-workflow/cadence/blob/e1267de12f8bc670fc84fab456d3495 ## Running the Sample ### Step 1: Download Certificates +Download certificates from config/credentials of cadence server and place them in below folder + ```bash -cd new_samples/client_samples/helloworld_tls/credentials -Download certificates from config/credentials of cadence server and place them here -cd .. +new_samples/client_samples/helloworld_tls/credentials ``` ### Step 2: Register the Domain