From f22469699e048d06064db475235fbac231e29d0f Mon Sep 17 00:00:00 2001 From: Drew Newberry Date: Thu, 13 Aug 2026 17:06:45 -0700 Subject: [PATCH] fix(e2e): follow credential storage identity Signed-off-by: Drew Newberry Signed-off-by: Shiju --- e2e/rust/tests/credential_drivers.rs | 107 ++++++++++++++++++++------- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/e2e/rust/tests/credential_drivers.rs b/e2e/rust/tests/credential_drivers.rs index 34a457b542..fd8ebb0607 100644 --- a/e2e/rust/tests/credential_drivers.rs +++ b/e2e/rust/tests/credential_drivers.rs @@ -41,6 +41,12 @@ fn credential_driver() -> String { .unwrap_or_else(|_| "kubernetes-secrets".to_string()) } +#[derive(Debug)] +struct ProviderIdentity { + id: String, + workspace: String, +} + fn vault_namespace() -> String { std::env::var("OPENSHELL_E2E_VAULT_NAMESPACE").unwrap_or_else(|_| "vault".to_string()) } @@ -53,23 +59,28 @@ fn vault_token() -> String { std::env::var("OPENSHELL_E2E_VAULT_TOKEN").unwrap_or_else(|_| "root".to_string()) } -fn managed_kubernetes_secret_name(provider_name: &str) -> String { +fn managed_credential_hash(identity: &ProviderIdentity, provider_name: &str) -> String { + // This test stores an ordinary provider credential, whose storage object ID is + // the provider ID, so both managed drivers use these four identity fields. let mut hasher = Sha256::new(); + hasher.update(identity.workspace.as_bytes()); + hasher.update([0]); + hasher.update(identity.id.as_bytes()); + hasher.update([0]); hasher.update(provider_name.as_bytes()); hasher.update([0]); hasher.update(CREDENTIAL_KEY.as_bytes()); let digest = hasher.finalize(); - let hex = format!("{digest:x}"); + format!("{digest:x}") +} + +fn managed_kubernetes_secret_name(identity: &ProviderIdentity, provider_name: &str) -> String { + let hex = managed_credential_hash(identity, provider_name); format!("openshell-cred-{}", &hex[..40]) } -fn managed_vault_path(provider_name: &str) -> String { - let mut hasher = Sha256::new(); - hasher.update(provider_name.as_bytes()); - hasher.update([0]); - hasher.update(CREDENTIAL_KEY.as_bytes()); - let digest = hasher.finalize(); - let hex = format!("{digest:x}"); +fn managed_vault_path(identity: &ProviderIdentity, provider_name: &str) -> String { + let hex = managed_credential_hash(identity, provider_name); format!("openshell/provider-credentials/{}", &hex[..40]) } @@ -217,6 +228,31 @@ async fn create_provider(name: &str, secret_value: &str) -> Result Result { + let (output, code) = run_cli(&["provider", "list", "--output", "json"]).await; + let clean = strip_ansi(&output); + if code != 0 { + return Err(format!("provider list failed (exit {code}):\n{clean}")); + } + let providers: Vec = serde_json::from_str(&clean) + .map_err(|err| format!("failed to parse provider list JSON: {err}\n{clean}"))?; + let provider = providers + .iter() + .find(|provider| provider["name"].as_str() == Some(provider_name)) + .ok_or_else(|| format!("provider '{provider_name}' was not returned by provider list"))?; + let id = provider["id"] + .as_str() + .filter(|id| !id.is_empty()) + .ok_or_else(|| format!("provider '{provider_name}' did not include an ID"))?; + let workspace = provider["workspace"] + .as_str() + .ok_or_else(|| format!("provider '{provider_name}' did not include a workspace"))?; + Ok(ProviderIdentity { + id: id.to_string(), + workspace: workspace.to_string(), + }) +} + async fn assert_provider_get_does_not_expose_secret( provider_name: &str, secret_value: &str, @@ -296,11 +332,12 @@ async fn configure_vault_storage() -> Result<(), String> { } async fn assert_kubernetes_secret_stored( + identity: &ProviderIdentity, provider_name: &str, secret_value: &str, ) -> Result<(), String> { let namespace = namespace(); - let secret_name = managed_kubernetes_secret_name(provider_name); + let secret_name = managed_kubernetes_secret_name(identity, provider_name); let encoded = kubectl(&[ "-n", &namespace, @@ -322,9 +359,12 @@ async fn assert_kubernetes_secret_stored( Ok(()) } -async fn assert_kubernetes_secret_deleted(provider_name: &str) -> Result<(), String> { +async fn assert_kubernetes_secret_deleted( + identity: &ProviderIdentity, + provider_name: &str, +) -> Result<(), String> { let namespace = namespace(); - let secret_name = managed_kubernetes_secret_name(provider_name); + let secret_name = managed_kubernetes_secret_name(identity, provider_name); match kubectl(&["-n", &namespace, "get", "secret", &secret_name]).await { Ok(output) => Err(format!( "Kubernetes Secret '{secret_name}' still exists after provider deletion:\n{output}" @@ -333,8 +373,12 @@ async fn assert_kubernetes_secret_deleted(provider_name: &str) -> Result<(), Str } } -async fn assert_vault_secret_stored(provider_name: &str, secret_value: &str) -> Result<(), String> { - let logical_path = managed_vault_path(provider_name); +async fn assert_vault_secret_stored( + identity: &ProviderIdentity, + provider_name: &str, + secret_value: &str, +) -> Result<(), String> { + let logical_path = managed_vault_path(identity, provider_name); let output = bao(&[ "kv", "get", @@ -348,8 +392,11 @@ async fn assert_vault_secret_stored(provider_name: &str, secret_value: &str) -> Ok(()) } -async fn assert_vault_secret_deleted(provider_name: &str) -> Result<(), String> { - let logical_path = managed_vault_path(provider_name); +async fn assert_vault_secret_deleted( + identity: &ProviderIdentity, + provider_name: &str, +) -> Result<(), String> { + let logical_path = managed_vault_path(identity, provider_name); match bao(&[ "kv", "get", @@ -367,20 +414,27 @@ async fn assert_vault_secret_deleted(provider_name: &str) -> Result<(), String> async fn assert_backend_stored( driver: &str, + identity: &ProviderIdentity, provider_name: &str, secret_value: &str, ) -> Result<(), String> { match driver { - "kubernetes-secrets" => assert_kubernetes_secret_stored(provider_name, secret_value).await, - "vault" => assert_vault_secret_stored(provider_name, secret_value).await, + "kubernetes-secrets" => { + assert_kubernetes_secret_stored(identity, provider_name, secret_value).await + } + "vault" => assert_vault_secret_stored(identity, provider_name, secret_value).await, other => Err(format!("unsupported credential driver '{other}'")), } } -async fn assert_backend_deleted(driver: &str, provider_name: &str) -> Result<(), String> { +async fn assert_backend_deleted( + driver: &str, + identity: &ProviderIdentity, + provider_name: &str, +) -> Result<(), String> { match driver { - "kubernetes-secrets" => assert_kubernetes_secret_deleted(provider_name).await, - "vault" => assert_vault_secret_deleted(provider_name).await, + "kubernetes-secrets" => assert_kubernetes_secret_deleted(identity, provider_name).await, + "vault" => assert_vault_secret_deleted(identity, provider_name).await, other => Err(format!("unsupported credential driver '{other}'")), } } @@ -409,23 +463,24 @@ async fn provider_credentials_are_stored_in_configured_backend() { .expect("configure Vault storage fixture"); } - let result: Result<(), String> = async { + let result: Result = async { create_provider(&provider_name, &secret_value).await?; assert_provider_get_does_not_expose_secret(&provider_name, &secret_value).await?; - assert_backend_stored(&driver, &provider_name, &secret_value).await?; + let identity = provider_identity(&provider_name).await?; + assert_backend_stored(&driver, &identity, &provider_name, &secret_value).await?; assert_provider_placeholder_available_in_sandbox( &provider_name, &sandbox_name, &secret_value, ) .await?; - Ok(()) + Ok(identity) } .await; delete_provider(&provider_name).await; - assert_backend_deleted(&driver, &provider_name) + let identity = result.expect("credential storage e2e failed"); + assert_backend_deleted(&driver, &identity, &provider_name) .await .expect("credential backend object should be deleted with provider"); - result.expect("credential storage e2e failed"); }