Skip to content

Commit 1e06a55

Browse files
authored
feat: TLS Helloworld sample for gRPC calls to cadence (#114)
* TLS Helloworld sample for gRPC calls to cadence
1 parent 48e7403 commit 1e06a55

File tree

5 files changed

+253
-2
lines changed

5 files changed

+253
-2
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ vendor/
1515
# Executables produced by cadence-samples repo
1616
bin/
1717
docker-compose.yml
18+
19+
# Credentials
20+
new_samples/client_samples/helloworld_tls/credentials/
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
## Pre-requisites
2+
3+
Follow this document to start cadence server:
4+
https://github.com/cadence-workflow/cadence/blob/e1267de12f8bc670fc84fab456d3495c8fc2f8a8/CONTRIBUTING.md#L1
5+
6+
1. **Build tools in cadence server**
7+
```bash
8+
make bins
9+
```
10+
11+
2. **Start cassandra**
12+
```bash
13+
docker compose -f ./docker/dev/cassandra.yml up -d
14+
```
15+
16+
3. **Install schema**
17+
```bash
18+
make install-schema
19+
```
20+
21+
4. **Start cadence server with TLS**
22+
```bash
23+
./cadence-server --env development --zone tls start
24+
```
25+
26+
## Running the Sample
27+
28+
### Step 1: Download Certificates
29+
Download certificates from config/credentials of cadence server and place them in below folder
30+
31+
```bash
32+
new_samples/client_samples/helloworld_tls/credentials
33+
```
34+
35+
### Step 2: Register the Domain
36+
Before running workflows, you must register the "default" domain:
37+
38+
```bash
39+
cd new_samples/client_samples/helloworld_tls
40+
go run register_domain.go
41+
```
42+
43+
Expected output:
44+
```
45+
Successfully registered domain {"domain": "default"}
46+
```
47+
48+
If the domain already exists, you'll see:
49+
```
50+
Domain already exists {"domain": "default"}
51+
```
52+
53+
### Step 3: Run the Sample
54+
In another terminal:
55+
```bash
56+
cd new_samples/client_samples/helloworld_tls
57+
go run hello_world_tls.go
58+
```
59+
60+
## References
61+
62+
- [Cadence Official Certificates](https://github.com/cadence-workflow/cadence/tree/master/config/credentials)
63+
- [Cadence Documentation](https://cadenceworkflow.io/)
64+
- [Go TLS Package](https://pkg.go.dev/crypto/tls)
65+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"crypto/x509"
7+
"fmt"
8+
"os"
9+
"time"
10+
11+
"github.com/google/uuid"
12+
"github.com/uber-common/cadence-samples/new_samples/worker"
13+
"go.uber.org/cadence/.gen/go/shared"
14+
"go.uber.org/yarpc/transport/grpc"
15+
"go.uber.org/zap"
16+
"google.golang.org/grpc/credentials"
17+
)
18+
19+
func main() {
20+
withTLSDialOption, err := withTLSDialOption()
21+
if err != nil {
22+
panic(err)
23+
}
24+
25+
cadenceClient := worker.BuildCadenceClient(withTLSDialOption)
26+
logger := worker.BuildLogger()
27+
28+
domain := "default"
29+
tasklist := "default-tasklist"
30+
workflowID := uuid.New().String()
31+
requestID := uuid.New().String()
32+
executionTimeout := int32(60)
33+
closeTimeout := int32(60)
34+
35+
workflowType := "cadence_samples.HelloWorldWorkflow"
36+
input := []byte(`{"message": "Uber"}`)
37+
38+
req := shared.StartWorkflowExecutionRequest{
39+
Domain: &domain,
40+
WorkflowId: &workflowID,
41+
WorkflowType: &shared.WorkflowType{
42+
Name: &workflowType,
43+
},
44+
TaskList: &shared.TaskList{
45+
Name: &tasklist,
46+
},
47+
Input: input,
48+
ExecutionStartToCloseTimeoutSeconds: &executionTimeout,
49+
TaskStartToCloseTimeoutSeconds: &closeTimeout,
50+
RequestId: &requestID,
51+
}
52+
53+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
54+
defer cancel()
55+
resp, err := cadenceClient.StartWorkflowExecution(ctx, &req)
56+
if err != nil {
57+
logger.Error("Failed to create workflow", zap.Error(err))
58+
panic("Failed to create workflow.")
59+
}
60+
61+
logger.Info("successfully started HelloWorld workflow", zap.String("runID", resp.GetRunId()))
62+
}
63+
64+
func withTLSDialOption() (grpc.DialOption, error) {
65+
// Present client cert for mutual TLS (if enabled on server)
66+
clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key")
67+
if err != nil {
68+
return nil, fmt.Errorf("Failed to load client certificate: %v", zap.Error(err))
69+
}
70+
71+
// Load server CA
72+
caCert, err := os.ReadFile("credentials/keytest.crt")
73+
if err != nil {
74+
return nil, fmt.Errorf("Failed to load server CA certificate: %v", zap.Error(err))
75+
}
76+
caCertPool := x509.NewCertPool()
77+
caCertPool.AppendCertsFromPEM(caCert)
78+
tlsConfig := tls.Config{
79+
InsecureSkipVerify: true,
80+
RootCAs: caCertPool,
81+
Certificates: []tls.Certificate{clientCert},
82+
}
83+
creds := credentials.NewTLS(&tlsConfig)
84+
grpc.DialerCredentials(creds)
85+
return grpc.DialerCredentials(creds), nil
86+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"crypto/x509"
7+
"fmt"
8+
"os"
9+
"time"
10+
11+
"github.com/uber-common/cadence-samples/new_samples/worker"
12+
"go.uber.org/cadence/.gen/go/shared"
13+
"go.uber.org/yarpc/transport/grpc"
14+
"go.uber.org/zap"
15+
"google.golang.org/grpc/credentials"
16+
)
17+
18+
func main() {
19+
logger := worker.BuildLogger()
20+
logger.Info("Registering default domain for cadence-vishwa with TLS...")
21+
22+
withTLSDialOption, err := buildTLSDialOption()
23+
if err != nil {
24+
logger.Fatal("Failed to build TLS dial option", zap.Error(err))
25+
}
26+
27+
cadenceClient := worker.BuildCadenceClient(withTLSDialOption)
28+
29+
// Register the domain
30+
domain := "default"
31+
retentionDays := int32(7)
32+
emitMetric := true
33+
34+
req := &shared.RegisterDomainRequest{
35+
Name: &domain,
36+
Description: stringPtr("Default domain for cadence samples"),
37+
WorkflowExecutionRetentionPeriodInDays: &retentionDays,
38+
EmitMetric: &emitMetric,
39+
}
40+
41+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
42+
defer cancel()
43+
44+
err = cadenceClient.RegisterDomain(ctx, req)
45+
if err != nil {
46+
// Check if domain already exists
47+
if _, ok := err.(*shared.DomainAlreadyExistsError); ok {
48+
logger.Info("Domain already exists", zap.String("domain", domain))
49+
return
50+
}
51+
logger.Fatal("Failed to register domain", zap.Error(err))
52+
}
53+
54+
logger.Info("Successfully registered domain", zap.String("domain", domain))
55+
}
56+
57+
func buildTLSDialOption() (grpc.DialOption, error) {
58+
// Load client certificate
59+
clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key")
60+
if err != nil {
61+
return nil, fmt.Errorf("failed to load client certificate: %w", err)
62+
}
63+
64+
// Load server CA
65+
caCert, err := os.ReadFile("credentials/keytest.crt")
66+
if err != nil {
67+
return nil, fmt.Errorf("failed to load server CA certificate: %w", err)
68+
}
69+
70+
caCertPool := x509.NewCertPool()
71+
if !caCertPool.AppendCertsFromPEM(caCert) {
72+
return nil, fmt.Errorf("failed to append CA certificate")
73+
}
74+
75+
tlsConfig := &tls.Config{
76+
InsecureSkipVerify: true,
77+
RootCAs: caCertPool,
78+
Certificates: []tls.Certificate{clientCert},
79+
MinVersion: tls.VersionTLS12,
80+
}
81+
82+
creds := credentials.NewTLS(tlsConfig)
83+
return grpc.DialerCredentials(creds), nil
84+
}
85+
86+
func stringPtr(s string) *string {
87+
return &s
88+
}

new_samples/worker/worker.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"go.uber.org/cadence/worker"
1212
"go.uber.org/cadence/workflow"
1313
"go.uber.org/yarpc"
14+
"go.uber.org/yarpc/peer"
15+
yarpchostport "go.uber.org/yarpc/peer/hostport"
1416
"go.uber.org/yarpc/transport/grpc"
1517
"go.uber.org/zap"
1618
"go.uber.org/zap/zapcore"
@@ -69,11 +71,18 @@ func StartWorker() {
6971

7072
}
7173

72-
func BuildCadenceClient() workflowserviceclient.Interface {
74+
func BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {
75+
grpcTransport := grpc.NewTransport()
76+
myChooser := peer.NewSingle(
77+
yarpchostport.Identify(HostPort),
78+
grpcTransport.NewDialer(dialOptions...),
79+
)
80+
outbound := grpcTransport.NewOutbound(myChooser)
81+
7382
dispatcher := yarpc.NewDispatcher(yarpc.Config{
7483
Name: ClientName,
7584
Outbounds: yarpc.Outbounds{
76-
CadenceService: {Unary: grpc.NewTransport().NewSingleOutbound(HostPort)},
85+
CadenceService: {Unary: outbound},
7786
},
7887
})
7988
if err := dispatcher.Start(); err != nil {

0 commit comments

Comments
 (0)