Skip to content

Commit 78e4fb8

Browse files
authored
Add gRPC credentials (#186)
* Add gRPC credentials to cofiguration * Add tests * Update README.md * Use default DYNAMIC_CONFIG_FILE_PATH env var for temporalio/auto-setup container in CI * Update README.md
1 parent dd0a489 commit 78e4fb8

File tree

7 files changed

+122
-9
lines changed

7 files changed

+122
-9
lines changed

.circleci/config.yml

-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ jobs:
2929
- POSTGRES_USER=postgres
3030
- POSTGRES_PWD=temporal
3131
- POSTGRES_SEEDS=postgres
32-
- DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml
3332

3433
environment:
3534
- TEMPORAL_HOST=temporal

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Temporal.configure do |config|
5959
config.port = 7233
6060
config.namespace = 'ruby-samples'
6161
config.task_queue = 'hello-world'
62+
config.credentials = :this_channel_is_insecure
6263
end
6364

6465
begin
@@ -114,6 +115,57 @@ curl -O https://raw.githubusercontent.com/temporalio/docker-compose/main/docker-
114115
docker-compose up
115116
```
116117

118+
## Using Credentials
119+
120+
### SSL
121+
122+
In many production deployments you will end up connecting to your Temporal Services via SSL. In this
123+
case you must read the public certificate of the CA that issued your Temporal server's SSL certificate and create
124+
an instance of [gRPC Channel Credentials](https://grpc.io/docs/guides/auth/#with-server-authentication-ssltls-1).
125+
126+
Configure your Temporal connection:
127+
128+
```ruby
129+
Temporal.configure do |config|
130+
config.host = 'localhost'
131+
config.port = 7233
132+
config.namespace = 'ruby-samples'
133+
config.task_queue = 'hello-world'
134+
config.credentials = GRPC::Core::ChannelCredentials.new(root_cert, client_key, client_chain)
135+
end
136+
```
137+
138+
### OAuth2 Token
139+
140+
Use gRPC Call Credentials to add OAuth2 token to gRPC calls:
141+
142+
```ruby
143+
Temporal.configure do |config|
144+
config.host = 'localhost'
145+
config.port = 7233
146+
config.namespace = 'ruby-samples'
147+
config.task_queue = 'hello-world'
148+
config.credentials = GRPC::Core::CallCredentials.new(updater_proc)
149+
end
150+
```
151+
`updater_proc` should be a method that returns `proc`. See an example of `updater_proc` in [googleauth](https://www.rubydoc.info/gems/googleauth/0.1.0/Signet/OAuth2/Client) library.
152+
153+
### Combining Credentials
154+
155+
To configure both SSL and OAuth2 token cedentials use `compose` method:
156+
157+
```ruby
158+
Temporal.configure do |config|
159+
config.host = 'localhost'
160+
config.port = 7233
161+
config.namespace = 'ruby-samples'
162+
config.task_queue = 'hello-world'
163+
config.credentials = GRPC::Core::ChannelCredentials.new(root_cert, client_key, client_chain).compose(
164+
GRPC::Core::CallCredentials.new(token.updater_proc)
165+
)
166+
end
167+
```
168+
117169
## Workflows
118170

119171
A workflow is defined using pure Ruby code, however it should contain only a high-level

lib/temporal/configuration.rb

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77

88
module Temporal
99
class Configuration
10-
Connection = Struct.new(:type, :host, :port, keyword_init: true)
10+
Connection = Struct.new(:type, :host, :port, :credentials, keyword_init: true)
1111
Execution = Struct.new(:namespace, :task_queue, :timeouts, :headers, keyword_init: true)
1212

1313
attr_reader :timeouts, :error_handlers
1414
attr_writer :converter
15-
attr_accessor :connection_type, :host, :port, :logger, :metrics_adapter, :namespace, :task_queue, :headers
15+
attr_accessor :connection_type, :host, :port, :credentials, :logger, :metrics_adapter, :namespace, :task_queue, :headers
1616

1717
# See https://docs.temporal.io/blog/activity-timeouts/ for general docs.
1818
# We want an infinite execution timeout for cron schedules and other perpetual workflows.
@@ -53,6 +53,7 @@ def initialize
5353
@headers = DEFAULT_HEADERS
5454
@converter = DEFAULT_CONVERTER
5555
@error_handlers = []
56+
@credentials = :this_channel_is_insecure
5657
end
5758

5859
def on_error(&block)
@@ -79,7 +80,8 @@ def for_connection
7980
Connection.new(
8081
type: connection_type,
8182
host: host,
82-
port: port
83+
port: port,
84+
credentials: credentials
8385
).freeze
8486
end
8587

lib/temporal/connection.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ def self.generate(configuration)
1010
connection_class = CLIENT_TYPES_MAP[configuration.type]
1111
host = configuration.host
1212
port = configuration.port
13+
credentials = configuration.credentials
1314

1415
hostname = `hostname`
1516
thread_id = Thread.current.object_id
1617
identity = "#{thread_id}@#{hostname}"
1718

18-
connection_class.new(host, port, identity)
19+
connection_class.new(host, port, identity, credentials)
1920
end
2021
end
2122
end

lib/temporal/connection/grpc.rb

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ class GRPC
3131
max_page_size: 100
3232
}.freeze
3333

34-
def initialize(host, port, identity, options = {})
34+
def initialize(host, port, identity, credentials, options = {})
3535
@url = "#{host}:#{port}"
3636
@identity = identity
37+
@credentials = credentials
3738
@poll = true
3839
@poll_mutex = Mutex.new
3940
@poll_request = nil
@@ -536,12 +537,12 @@ def cancel_polling_request
536537

537538
private
538539

539-
attr_reader :url, :identity, :options, :poll_mutex, :poll_request
540+
attr_reader :url, :identity, :credentials, :options, :poll_mutex, :poll_request
540541

541542
def client
542543
@client ||= Temporal::Api::WorkflowService::V1::WorkflowService::Stub.new(
543544
url,
544-
:this_channel_is_insecure,
545+
credentials,
545546
timeout: 60
546547
)
547548
end
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
describe Temporal::Connection do
2+
subject { described_class.generate(config.for_connection) }
3+
4+
let(:connection_type) { :grpc }
5+
let(:credentials) { nil }
6+
let(:config) do
7+
config = Temporal::Configuration.new
8+
config.connection_type = connection_type
9+
config.credentials = credentials if credentials
10+
config
11+
end
12+
13+
context 'insecure' do
14+
let(:credentials) { :this_channel_is_insecure }
15+
16+
it 'generates a grpc connection' do
17+
expect(subject).to be_kind_of(Temporal::Connection::GRPC)
18+
expect(subject.send(:identity)).not_to be_nil
19+
expect(subject.send(:credentials)).to eq(:this_channel_is_insecure)
20+
end
21+
end
22+
23+
context 'ssl' do
24+
let(:credentials) { GRPC::Core::ChannelCredentials.new }
25+
26+
it 'generates a grpc connection' do
27+
expect(subject).to be_kind_of(Temporal::Connection::GRPC)
28+
expect(subject.send(:identity)).not_to be_nil
29+
expect(subject.send(:credentials)).to be_kind_of(GRPC::Core::ChannelCredentials)
30+
end
31+
end
32+
33+
context 'oauth2' do
34+
let(:credentials) { GRPC::Core::CallCredentials.new(proc { { authorization: 'token' } }) }
35+
36+
it 'generates a grpc connection' do
37+
expect(subject).to be_kind_of(Temporal::Connection::GRPC)
38+
expect(subject.send(:identity)).not_to be_nil
39+
expect(subject.send(:credentials)).to be_kind_of(GRPC::Core::CallCredentials)
40+
end
41+
end
42+
43+
context 'ssl + oauth2' do
44+
let(:credentials) do
45+
GRPC::Core::ChannelCredentials.new.compose(
46+
GRPC::Core::CallCredentials.new(
47+
proc { { authorization: 'token' } }
48+
)
49+
)
50+
end
51+
52+
it 'generates a grpc connection' do
53+
expect(subject).to be_kind_of(Temporal::Connection::GRPC)
54+
expect(subject.send(:identity)).not_to be_nil
55+
expect(subject.send(:credentials)).to be_kind_of(GRPC::Core::ChannelCredentials)
56+
end
57+
end
58+
end

spec/unit/lib/temporal/grpc_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
let(:run_id) { SecureRandom.uuid }
1111
let(:now) { Time.now}
1212

13-
subject { Temporal::Connection::GRPC.new(nil, nil, identity) }
13+
subject { Temporal::Connection::GRPC.new(nil, nil, identity, :this_channel_is_insecure) }
1414

1515
class TestDeserializer
1616
extend Temporal::Concerns::Payloads

0 commit comments

Comments
 (0)