Skip to content

Commit 51b5f95

Browse files
THardy98dependabot[bot]cretz
authored
Environment configuration (#326)
* envconfig impl * linting * fix steep type checks * formatting * Refactor envconfig classes to use Data.define for immutability - Refactor ClientConfigTLS, ClientConfigProfile, and ClientConfig to use Data.define * Rename hash methods to use idiomatic Ruby naming - Rename from_hash → from_h for all envconfig classes - Rename to_hash → to_h for all envconfig classes * Replace TLS config hash with Connection::TLSOptions object - Rename `to_connect_tls_config` → `to_tls_options` - Return `Connection::TLSOptions` object instead of plain Hash * Refactor to use inline pattern instead of temporary variables * Refactor to_client_connect_options to return tuple for one-liner usage Changed `to_client_connect_options` to return `[positional_args, keyword_args]` tuple instead of a hash, enabling clean one-liner client connections: ```ruby args, kwargs = profile.to_client_connect_options client = Temporalio::Client.connect(*args, **kwargs) ``` * Make hash methods private * Remove File.exist? check by using type system for path vs data distinction - TOML paths (*_path fields) become Pathname objects - TOML data (*_data fields) remain String objects * Use symbol keys instead of string keys in Rust FFI bridge Changed all hash keys from strings to symbols in the Rust-to-Ruby bridge * Bump golang.org/x/net in /temporalio/test/golangworker (#303) * Bump tracing-subscriber from 0.3.19 to 0.3.20 in /temporalio (#331) * Reduce the scope of the illegal call tracer and other tracing fixes (#332) * Remove unnecessary fully qualified import names * Remove envconfig import in temporalio.rb * _source_to_path_and_data prefixed with underscore and private doc visibility * TLS disabled field now optional * Rubocop fixes * Steep fixes * Renamed envconfig.rb -> env_config.rb Renamed to_tls_options -> to_client_tls_options * Pass data string as RString, Rust bridge converts to Vec * Rename load_client_connect_config -> load_client_connect_options * Format envconfig.rs * Nits * Update core submodule * Revert irrelevant cancellation changes * Update Cargo.lock * Propagate set_headers failure in client bridge * Add experimental warning --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chad Retz <[email protected]>
1 parent d3391ec commit 51b5f95

File tree

12 files changed

+1766
-250
lines changed

12 files changed

+1766
-250
lines changed

temporalio/Cargo.lock

Lines changed: 162 additions & 240 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

temporalio/ext/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ prost = "0.13"
1717
rb-sys = "0.9"
1818
temporal-client = { version = "0.1.0", path = "./sdk-core/client" }
1919
temporal-sdk-core = { version = "0.1.0", path = "./sdk-core/core", features = ["ephemeral-server"] }
20-
temporal-sdk-core-api = { version = "0.1.0", path = "./sdk-core/core-api" }
20+
temporal-sdk-core-api = { version = "0.1.0", path = "./sdk-core/core-api", features = ["envconfig"] }
2121
temporal-sdk-core-protos = { version = "0.1.0", path = "./sdk-core/sdk-core-protos" }
2222
tokio = "1.37"
2323
tokio-stream = "0.1"

temporalio/ext/sdk-core

Submodule sdk-core updated 115 files

temporalio/ext/src/client.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,11 @@ impl Client {
237237
self.invoke_rpc(service, callback, call)
238238
}
239239

240-
pub fn update_metadata(&self, headers: HashMap<String, String>) {
241-
self.core.get_client().set_headers(headers);
240+
pub fn update_metadata(&self, headers: HashMap<String, String>) -> Result<(), Error> {
241+
self.core
242+
.get_client()
243+
.set_headers(headers)
244+
.map_err(|err| error!("Invalid headers: {}", err))
242245
}
243246

244247
pub fn update_api_key(&self, api_key: Option<String>) {

temporalio/ext/src/envconfig.rs

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
use std::collections::HashMap;
2+
3+
use magnus::{Error, RHash, RString, Ruby, class, function, prelude::*, scan_args};
4+
use temporal_sdk_core_api::envconfig::{
5+
ClientConfig as CoreClientConfig, ClientConfigCodec,
6+
ClientConfigProfile as CoreClientConfigProfile, ClientConfigTLS as CoreClientConfigTLS,
7+
DataSource, LoadClientConfigOptions, LoadClientConfigProfileOptions,
8+
load_client_config as core_load_client_config,
9+
load_client_config_profile as core_load_client_config_profile,
10+
};
11+
12+
use crate::{ROOT_MOD, error};
13+
14+
pub fn init(ruby: &Ruby) -> Result<(), Error> {
15+
let root_mod = ruby.get_inner(&ROOT_MOD);
16+
17+
let class = root_mod.define_class("EnvConfig", class::object())?;
18+
class.define_singleton_method("load_client_config", function!(load_client_config, -1))?;
19+
class.define_singleton_method(
20+
"load_client_connect_config",
21+
function!(load_client_connect_config, -1),
22+
)?;
23+
24+
Ok(())
25+
}
26+
27+
fn data_source_to_hash(ruby: &Ruby, ds: &DataSource) -> Result<RHash, Error> {
28+
let hash = RHash::new();
29+
match ds {
30+
DataSource::Path(p) => {
31+
hash.aset(ruby.sym_new("path"), ruby.str_new(p))?;
32+
}
33+
DataSource::Data(d) => {
34+
hash.aset(ruby.sym_new("data"), ruby.str_from_slice(d))?;
35+
}
36+
}
37+
Ok(hash)
38+
}
39+
40+
fn tls_to_hash(ruby: &Ruby, tls: &CoreClientConfigTLS) -> Result<RHash, Error> {
41+
let hash = RHash::new();
42+
hash.aset(ruby.sym_new("disabled"), tls.disabled)?;
43+
44+
if let Some(v) = &tls.client_cert {
45+
hash.aset(ruby.sym_new("client_cert"), data_source_to_hash(ruby, v)?)?;
46+
}
47+
if let Some(v) = &tls.client_key {
48+
hash.aset(ruby.sym_new("client_key"), data_source_to_hash(ruby, v)?)?;
49+
}
50+
if let Some(v) = &tls.server_ca_cert {
51+
hash.aset(
52+
ruby.sym_new("server_ca_cert"),
53+
data_source_to_hash(ruby, v)?,
54+
)?;
55+
}
56+
if let Some(v) = &tls.server_name {
57+
hash.aset(ruby.sym_new("server_name"), ruby.str_new(v))?;
58+
}
59+
hash.aset(
60+
ruby.sym_new("disable_host_verification"),
61+
tls.disable_host_verification,
62+
)?;
63+
64+
Ok(hash)
65+
}
66+
67+
fn codec_to_hash(ruby: &Ruby, codec: &ClientConfigCodec) -> Result<RHash, Error> {
68+
let hash = RHash::new();
69+
if let Some(v) = &codec.endpoint {
70+
hash.aset(ruby.sym_new("endpoint"), ruby.str_new(v))?;
71+
}
72+
if let Some(v) = &codec.auth {
73+
hash.aset(ruby.sym_new("auth"), ruby.str_new(v))?;
74+
}
75+
Ok(hash)
76+
}
77+
78+
fn profile_to_hash(ruby: &Ruby, profile: &CoreClientConfigProfile) -> Result<RHash, Error> {
79+
let hash = RHash::new();
80+
81+
if let Some(v) = &profile.address {
82+
hash.aset(ruby.sym_new("address"), ruby.str_new(v))?;
83+
}
84+
if let Some(v) = &profile.namespace {
85+
hash.aset(ruby.sym_new("namespace"), ruby.str_new(v))?;
86+
}
87+
if let Some(v) = &profile.api_key {
88+
hash.aset(ruby.sym_new("api_key"), ruby.str_new(v))?;
89+
}
90+
if let Some(tls) = &profile.tls {
91+
hash.aset(ruby.sym_new("tls"), tls_to_hash(ruby, tls)?)?;
92+
}
93+
if let Some(codec) = &profile.codec {
94+
hash.aset(ruby.sym_new("codec"), codec_to_hash(ruby, codec)?)?;
95+
}
96+
if !profile.grpc_meta.is_empty() {
97+
let grpc_meta_hash = RHash::new();
98+
for (k, v) in &profile.grpc_meta {
99+
grpc_meta_hash.aset(ruby.str_new(k), ruby.str_new(v))?;
100+
}
101+
hash.aset(ruby.sym_new("grpc_meta"), grpc_meta_hash)?;
102+
}
103+
104+
Ok(hash)
105+
}
106+
107+
fn core_config_to_hash(ruby: &Ruby, core_config: &CoreClientConfig) -> Result<RHash, Error> {
108+
let profiles_hash = RHash::new();
109+
for (name, profile) in &core_config.profiles {
110+
let profile_hash = profile_to_hash(ruby, profile)?;
111+
profiles_hash.aset(ruby.str_new(name), profile_hash)?;
112+
}
113+
Ok(profiles_hash)
114+
}
115+
116+
fn load_client_config_inner(
117+
ruby: &Ruby,
118+
config_source: Option<DataSource>,
119+
config_file_strict: bool,
120+
disable_file: bool,
121+
env_vars: Option<HashMap<String, String>>,
122+
) -> Result<RHash, Error> {
123+
let core_config = if disable_file {
124+
CoreClientConfig::default()
125+
} else {
126+
let options = LoadClientConfigOptions {
127+
config_source,
128+
config_file_strict,
129+
};
130+
core_load_client_config(options, env_vars.as_ref())
131+
.map_err(|e| error!("EnvConfig error: {}", e))?
132+
};
133+
134+
core_config_to_hash(ruby, &core_config)
135+
}
136+
137+
fn load_client_connect_config_inner(
138+
ruby: &Ruby,
139+
config_source: Option<DataSource>,
140+
profile: Option<String>,
141+
disable_file: bool,
142+
disable_env: bool,
143+
config_file_strict: bool,
144+
env_vars: Option<HashMap<String, String>>,
145+
) -> Result<RHash, Error> {
146+
let options = LoadClientConfigProfileOptions {
147+
config_source,
148+
config_file_profile: profile,
149+
config_file_strict,
150+
disable_file,
151+
disable_env,
152+
};
153+
154+
let profile = core_load_client_config_profile(options, env_vars.as_ref())
155+
.map_err(|e| error!("EnvConfig error: {}", e))?;
156+
157+
profile_to_hash(ruby, &profile)
158+
}
159+
160+
// load_client_config(path: String|nil, data: String|nil, disable_file: bool, config_file_strict: bool, env_vars: Hash|nil)
161+
fn load_client_config(args: &[magnus::Value]) -> Result<RHash, Error> {
162+
let ruby = Ruby::get().expect("Not in Ruby thread");
163+
let args = scan_args::scan_args::<
164+
(Option<String>, Option<RString>, bool, bool),
165+
(Option<HashMap<String, String>>,),
166+
(),
167+
(),
168+
(),
169+
(),
170+
>(args)?;
171+
let (path, data, disable_file, config_file_strict) = args.required;
172+
let (env_vars,) = args.optional;
173+
174+
let config_source = match (path, data) {
175+
(Some(p), None) => Some(DataSource::Path(p)),
176+
(None, Some(d)) => {
177+
let bytes = unsafe { d.as_slice().to_vec() };
178+
Some(DataSource::Data(bytes))
179+
}
180+
(None, None) => None,
181+
(Some(_), Some(_)) => {
182+
return Err(error!(
183+
"Cannot specify both path and data for config source"
184+
));
185+
}
186+
};
187+
188+
load_client_config_inner(
189+
&ruby,
190+
config_source,
191+
config_file_strict,
192+
disable_file,
193+
env_vars,
194+
)
195+
}
196+
197+
// load_client_connect_config(profile: String|nil, path: String|nil, data: String|nil, disable_file: bool, disable_env: bool, config_file_strict: bool, env_vars: Hash|nil)
198+
fn load_client_connect_config(args: &[magnus::Value]) -> Result<RHash, Error> {
199+
let ruby = Ruby::get().expect("Not in Ruby thread");
200+
let args = scan_args::scan_args::<
201+
(
202+
Option<String>,
203+
Option<String>,
204+
Option<RString>,
205+
bool,
206+
bool,
207+
bool,
208+
),
209+
(Option<HashMap<String, String>>,),
210+
(),
211+
(),
212+
(),
213+
(),
214+
>(args)?;
215+
let (profile, path, data, disable_file, disable_env, config_file_strict) = args.required;
216+
let (env_vars,) = args.optional;
217+
218+
let config_source = match (path, data) {
219+
(Some(p), None) => Some(DataSource::Path(p)),
220+
(None, Some(d)) => {
221+
let bytes = unsafe { d.as_slice().to_vec() };
222+
Some(DataSource::Data(bytes))
223+
}
224+
(None, None) => None,
225+
(Some(_), Some(_)) => {
226+
return Err(error!(
227+
"Cannot specify both path and data for config source"
228+
));
229+
}
230+
};
231+
232+
load_client_connect_config_inner(
233+
&ruby,
234+
config_source,
235+
profile,
236+
disable_file,
237+
disable_env,
238+
config_file_strict,
239+
env_vars,
240+
)
241+
}

temporalio/ext/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use magnus::{Error, ExceptionClass, RModule, Ruby, prelude::*, value::Lazy};
22

33
mod client;
44
mod client_rpc_generated;
5+
mod envconfig;
56
mod metric;
67
mod runtime;
78
mod testing;
@@ -50,6 +51,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
5051
Lazy::force(&ROOT_ERR, ruby);
5152

5253
client::init(ruby)?;
54+
envconfig::init(ruby)?;
5355
metric::init(ruby)?;
5456
runtime::init(ruby)?;
5557
testing::init(ruby)?;

temporalio/lib/temporalio/cancellation.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ def prepare_cancel(reason:)
167167
to_return.values
168168
end
169169

170-
def canceled_mutex_synchronize(&)
171-
Workflow::Unsafe.illegal_call_tracing_disabled { @canceled_mutex.synchronize(&) }
170+
def canceled_mutex_synchronize(&block)
171+
Workflow::Unsafe.illegal_call_tracing_disabled { @canceled_mutex.synchronize(&block) }
172172
end
173173
end
174174
end

0 commit comments

Comments
 (0)