Skip to content

Commit 079d1f3

Browse files
authored
feat: Expose envconfig functionality through C bridge (#986)
* feat: Expose envconfig functionality through C bridge * formatting * more formatting fixes * improve some test flakiness for envconfig module tests * feat: Replace callback parameters with options structs in C bridge - Replace multiple function parameters with ClientConfigLoadOptions and ClientConfigProfileLoadOptions structs - Remove async callbacks and change functions - Improve documentation to clarify byte array formats - Add ClientConfigOrFail and ClientConfigProfileOrFail result structs * Update config TLS field in C bridge to be Option * Data in DataSource should be Vec<u8>, remove useless disable_file parameter for ClientConfigLoadOptions * Use TemporalCoreByteArrayRef instead of string pointers * Revert misc changes to existing tests * Add Env prefix befor Config in envconfig c-bridge structs. Run envconfig test using system env vars in separate process * Skip test is env vars aren't set * Linter complaint * remaining lint fixes from cargo 1.88.0 update * cargo test-lint fix
1 parent b3acbe2 commit 079d1f3

16 files changed

Lines changed: 428 additions & 46 deletions

File tree

client/src/workflow_handle/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,7 @@ where
200200
o => Err(anyhow!(
201201
"Server returned an event that didn't match the CloseEvent filter. \
202202
This is either a server bug or a new event the SDK does not understand. \
203-
Event details: {:?}",
204-
o
203+
Event details: {o:?}"
205204
)),
206205
};
207206
}

core-api/src/envconfig.rs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,12 +1592,38 @@ address = "some-address"
15921592

15931593
#[test]
15941594
fn test_load_client_config_profile_from_system_env() {
1595-
// Set up system env vars. These tests can't be run in parallel.
1596-
unsafe {
1597-
std::env::set_var("TEMPORAL_ADDRESS", "system-address");
1598-
std::env::set_var("TEMPORAL_NAMESPACE", "system-namespace");
1599-
}
1595+
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
1596+
let output = std::process::Command::new(cargo)
1597+
.arg("test")
1598+
.arg("-F")
1599+
.arg("envconfig")
1600+
.arg("envconfig::tests::test_load_client_config_profile_from_system_env_impl")
1601+
.arg("--")
1602+
.arg("--exact")
1603+
.arg("--ignored")
1604+
.env("TEMPORAL_ADDRESS", "system-address")
1605+
.env("TEMPORAL_NAMESPACE", "system-namespace")
1606+
.output()
1607+
.expect("Failed to execute subprocess test");
1608+
1609+
assert!(
1610+
output.status.success(),
1611+
"Subprocess test failed:\nstdout: {}\nstderr: {}",
1612+
String::from_utf8_lossy(&output.stdout),
1613+
String::from_utf8_lossy(&output.stderr),
1614+
);
1615+
}
16001616

1617+
#[test]
1618+
#[ignore] // Only run when explicitly called
1619+
fn test_load_client_config_profile_from_system_env_impl() {
1620+
// Check if we're in the right context
1621+
if std::env::var("TEMPORAL_ADDRESS").is_err()
1622+
|| std::env::var("TEMPORAL_NAMESPACE").is_err()
1623+
{
1624+
eprintln!("Skipping test - required env vars not set");
1625+
return; // Early return instead of panic
1626+
}
16011627
let options = LoadClientConfigProfileOptions {
16021628
disable_file: true, // Don't load from any files
16031629
..Default::default()
@@ -1607,12 +1633,6 @@ address = "some-address"
16071633
let profile = load_client_config_profile(options, None).unwrap();
16081634
assert_eq!(profile.address.as_ref().unwrap(), "system-address");
16091635
assert_eq!(profile.namespace.as_ref().unwrap(), "system-namespace");
1610-
1611-
// Clean up
1612-
unsafe {
1613-
std::env::remove_var("TEMPORAL_ADDRESS");
1614-
std::env::remove_var("TEMPORAL_NAMESPACE");
1615-
}
16161636
}
16171637

16181638
#[test]

core-c-bridge/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ prost = { workspace = true }
1919
# cause non-determinism.
2020
rand = "0.9.2"
2121
rand_pcg = "0.9.0"
22+
serde = { version = "1.0", features = ["derive"] }
2223
serde_json = "1.0"
2324
tokio = "1.47"
2425
tokio-stream = "0.1"
@@ -38,6 +39,7 @@ features = ["ephemeral-server"]
3839

3940
[dependencies.temporal-sdk-core-api]
4041
path = "../core-api"
42+
features = ["envconfig"]
4143

4244
[dependencies.temporal-sdk-core-protos]
4345
path = "../sdk-core-protos"

core-c-bridge/include/temporal-sdk-core-c-bridge.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,53 @@ typedef void (*TemporalCoreClientRpcCallCallback)(void *user_data,
240240
const struct TemporalCoreByteArray *failure_message,
241241
const struct TemporalCoreByteArray *failure_details);
242242

243+
/**
244+
* OrFail result for client config loading operations.
245+
* Either success or fail will be null, but never both.
246+
* If success is not null, it contains JSON-serialized client configuration data.
247+
* If fail is not null, it contains UTF-8 encoded error message.
248+
* The returned ByteArrays must be freed by the caller.
249+
*/
250+
typedef struct TemporalCoreClientEnvConfigOrFail {
251+
const struct TemporalCoreByteArray *success;
252+
const struct TemporalCoreByteArray *fail;
253+
} TemporalCoreClientEnvConfigOrFail;
254+
255+
/**
256+
* Options for loading client configuration.
257+
*/
258+
typedef struct TemporalCoreClientEnvConfigLoadOptions {
259+
struct TemporalCoreByteArrayRef path;
260+
struct TemporalCoreByteArrayRef data;
261+
bool config_file_strict;
262+
struct TemporalCoreByteArrayRef env_vars;
263+
} TemporalCoreClientEnvConfigLoadOptions;
264+
265+
/**
266+
* OrFail result for client config profile loading operations.
267+
* Either success or fail will be null, but never both.
268+
* If success is not null, it contains JSON-serialized client configuration profile data.
269+
* If fail is not null, it contains UTF-8 encoded error message.
270+
* The returned ByteArrays must be freed by the caller.
271+
*/
272+
typedef struct TemporalCoreClientEnvConfigProfileOrFail {
273+
const struct TemporalCoreByteArray *success;
274+
const struct TemporalCoreByteArray *fail;
275+
} TemporalCoreClientEnvConfigProfileOrFail;
276+
277+
/**
278+
* Options for loading a specific client configuration profile.
279+
*/
280+
typedef struct TemporalCoreClientEnvConfigProfileLoadOptions {
281+
struct TemporalCoreByteArrayRef profile;
282+
struct TemporalCoreByteArrayRef path;
283+
struct TemporalCoreByteArrayRef data;
284+
bool disable_file;
285+
bool disable_env;
286+
bool config_file_strict;
287+
struct TemporalCoreByteArrayRef env_vars;
288+
} TemporalCoreClientEnvConfigProfileLoadOptions;
289+
243290
typedef union TemporalCoreMetricAttributeValue {
244291
struct TemporalCoreByteArrayRef string_value;
245292
int64_t int_value;
@@ -771,6 +818,20 @@ void temporal_core_client_rpc_call(struct TemporalCoreClient *client,
771818
void *user_data,
772819
TemporalCoreClientRpcCallCallback callback);
773820

821+
/**
822+
* Load all client profiles from given sources.
823+
* Returns ClientConfigOrFail with either success JSON or error message.
824+
* The returned ByteArrays must be freed by the caller.
825+
*/
826+
struct TemporalCoreClientEnvConfigOrFail temporal_core_client_env_config_load(const struct TemporalCoreClientEnvConfigLoadOptions *options);
827+
828+
/**
829+
* Load a single client profile from given sources with env overrides.
830+
* Returns ClientConfigProfileOrFail with either success JSON or error message.
831+
* The returned ByteArrays must be freed by the caller.
832+
*/
833+
struct TemporalCoreClientEnvConfigProfileOrFail temporal_core_client_env_config_profile_load(const struct TemporalCoreClientEnvConfigProfileLoadOptions *options);
834+
774835
struct TemporalCoreMetricMeter *temporal_core_metric_meter_new(struct TemporalCoreRuntime *runtime);
775836

776837
void temporal_core_metric_meter_free(struct TemporalCoreMetricMeter *meter);

core-c-bridge/src/client.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ async fn call_workflow_service(
685685
"UpdateWorkerBuildIdCompatibility" => {
686686
rpc_call!(client, call, update_worker_build_id_compatibility)
687687
}
688-
rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
688+
rpc => Err(anyhow::anyhow!("Unknown RPC call {rpc}")),
689689
}
690690
}
691691

@@ -715,7 +715,7 @@ async fn call_operator_service(
715715
"UpdateNexusEndpoint" => {
716716
rpc_call_on_trait!(client, call, OperatorService, update_nexus_endpoint)
717717
}
718-
rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
718+
rpc => Err(anyhow::anyhow!("Unknown RPC call {rpc}")),
719719
}
720720
}
721721

@@ -785,7 +785,7 @@ async fn call_cloud_service(client: &CoreClient, call: &RpcCallOptions) -> anyho
785785
"GetConnectivityRule" => rpc_call!(client, call, get_connectivity_rule),
786786
"GetConnectivityRules" => rpc_call!(client, call, get_connectivity_rules),
787787
"DeleteConnectivityRule" => rpc_call!(client, call, delete_connectivity_rule),
788-
rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
788+
rpc => Err(anyhow::anyhow!("Unknown RPC call {rpc}")),
789789
}
790790
}
791791

@@ -799,7 +799,7 @@ async fn call_test_service(client: &CoreClient, call: &RpcCallOptions) -> anyhow
799799
"Sleep" => rpc_call!(client, call, sleep),
800800
"UnlockTimeSkippingWithSleep" => rpc_call!(client, call, unlock_time_skipping_with_sleep),
801801
"UnlockTimeSkipping" => rpc_call!(client, call, unlock_time_skipping),
802-
rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
802+
rpc => Err(anyhow::anyhow!("Unknown RPC call {rpc}")),
803803
}
804804
}
805805

@@ -814,7 +814,7 @@ async fn call_health_service(
814814
"Watch" => Err(anyhow::anyhow!(
815815
"Health service Watch method is not implemented in C bridge"
816816
)),
817-
rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
817+
rpc => Err(anyhow::anyhow!("Unknown RPC call {rpc}")),
818818
}
819819
}
820820

0 commit comments

Comments
 (0)