From 2e782a77e99480a48679cf7977697350feab6a3a Mon Sep 17 00:00:00 2001 From: Dotan Nahum Date: Fri, 17 May 2024 15:29:30 +0300 Subject: [PATCH 1/3] add etcd, param slicing, paging fix in ssm --- Cargo.lock | 320 +++ teller-providers/Cargo.toml | 8 + teller-providers/src/lib.rs | 3 + teller-providers/src/providers/etcd.rs | 241 ++ .../src/providers/hashicorp_consul.rs | 5 +- .../src/providers/hashicorp_vault.rs | 1 - teller-providers/src/providers/mod.rs | 7 + ..._test_utils__[get-secret_development].snap | 18 +- ...tils__[get-secret_multiple_lotsakeys].snap | 2506 +++++++++++++++++ ...iders__test_utils__[get-selective-db].snap | 33 + ...tils__[put-secret_multiple_lotsakeys].snap | 7 + teller-providers/src/providers/ssm.rs | 53 +- teller-providers/src/providers/test_utils.rs | 44 + teller-providers/src/registry.rs | 1 + 14 files changed, 3214 insertions(+), 33 deletions(-) create mode 100644 teller-providers/src/providers/etcd.rs create mode 100644 teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_multiple_lotsakeys].snap create mode 100644 teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-selective-db].snap create mode 100644 teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[put-secret_multiple_lotsakeys].snap diff --git a/Cargo.lock b/Cargo.lock index 56a5fcd2..9bb057f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,28 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.59", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -474,6 +496,51 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding 2.3.1", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -1186,6 +1253,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1202,6 +1290,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcd-client" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae697f3928e8c89ae6f4dcf788059f49fd01a76dc53e63628f5a33881f5715e" +dependencies = [ + "http 0.2.12", + "prost", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", + "tower", + "tower-service", +] + [[package]] name = "exitcode" version = "1.1.2" @@ -1236,6 +1340,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.30" @@ -1710,6 +1820,18 @@ dependencies = [ "tokio-rustls 0.25.0", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1945,6 +2067,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.2" @@ -1977,6 +2105,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "native-tls" version = "0.2.11" @@ -2230,6 +2364,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + [[package]] name = "phf" version = "0.11.2" @@ -2318,6 +2462,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.59", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -2327,6 +2481,59 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.59", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.59", +] + +[[package]] +name = "prost-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +dependencies = [ + "prost", +] + [[package]] name = "quick-error" version = "2.0.1" @@ -3262,8 +3469,10 @@ dependencies = [ "aws-sdk-ssm", "base64 0.22.0", "crc32c", + "dockertest", "dockertest-server", "dotenvy", + "etcd-client", "fs-err", "google-secretmanager1", "home", @@ -3278,8 +3487,10 @@ dependencies = [ "serde_variant", "serde_yaml", "strum 0.25.0", + "test-log", "thiserror", "tokio", + "tracing", "vaultrs", ] @@ -3326,6 +3537,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.59", +] + [[package]] name = "textwrap" version = "0.16.1" @@ -3429,6 +3662,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.2.0" @@ -3471,6 +3714,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -3507,6 +3761,72 @@ dependencies = [ "winnow", ] +[[package]] +name = "tonic" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper", + "hyper-timeout", + "percent-encoding 2.3.1", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn 2.0.59", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" diff --git a/teller-providers/Cargo.toml b/teller-providers/Cargo.toml index a6796d08..4da034db 100644 --- a/teller-providers/Cargo.toml +++ b/teller-providers/Cargo.toml @@ -20,6 +20,7 @@ default = [ "aws_secretsmanager", "google_secretmanager", "hashicorp_consul", + "etcd", ] ssm = ["aws", "dep:aws-sdk-ssm"] @@ -29,6 +30,7 @@ hashicorp_vault = ["dep:vaultrs", "dep:rustify"] dotenv = ["dep:dotenvy"] hashicorp_consul = ["dep:rs-consul"] aws = ["dep:aws-config"] +etcd = ["dep:etcd-client"] [dependencies] async-trait = { workspace = true } @@ -44,6 +46,7 @@ fs-err = "2.9.0" home = "0.5.5" hyper = "0.14" base64 = "0.22.0" +tokio = "1" # gcp google-secretmanager1 = { version = "5.0.2", optional = true } crc32c = { version = "0.6", optional = true } @@ -61,7 +64,12 @@ rustify = { version = "0.5.3", optional = true } # HashiCorp Consul rs-consul = { version = "0.6.0", optional = true } +etcd-client = { version = "0.12", optional = true } + [dev-dependencies] insta = { workspace = true } dockertest-server = { version = "0.1.7", features = ["hashi", "cloud"] } +dockertest = "0.3.0" tokio = { workspace = true } +test-log = "0.2" +tracing = "0.1" diff --git a/teller-providers/src/lib.rs b/teller-providers/src/lib.rs index 1f86f642..0da97fd9 100644 --- a/teller-providers/src/lib.rs +++ b/teller-providers/src/lib.rs @@ -65,6 +65,9 @@ pub enum Error { #[error("LIST {path}: {msg}")] ListError { path: String, msg: String }, + + #[error("{0}")] + CreateProviderError(String), } pub type Result = std::result::Result; diff --git a/teller-providers/src/providers/etcd.rs b/teller-providers/src/providers/etcd.rs new file mode 100644 index 00000000..301060e8 --- /dev/null +++ b/teller-providers/src/providers/etcd.rs @@ -0,0 +1,241 @@ +//! Hashicorp Consul +//! +//! +//! ## Example configuration +//! +//! ```yaml +//! providers: +//! consul1: +//! kind: hashicorp_consul +//! # options: ... +//! ``` +//! ## Options +//! +//! See [`EtcdOptions`] for more. +//! + +use async_trait::async_trait; +use etcd_client::{Client, DeleteOptions, GetOptions, KvClient}; +use serde_derive::{Deserialize, Serialize}; +use tokio::sync::Mutex; + +use super::ProviderKind; +use crate::{ + config::{PathMap, ProviderInfo, KV}, + Error, Provider, Result, +}; + +#[allow(clippy::module_name_repetitions)] +#[derive(Default, Serialize, Deserialize, Debug, Clone)] +pub struct EtcdOptions { + /// Etcd address. + pub address: Option, +} + +pub struct Etcd { + pub client: Mutex, + pub name: String, +} + +fn to_err(_pm: &PathMap, err: etcd_client::Error) -> Error { + Error::Any(Box::new(err)) +} +async fn create_client() -> Result { + Ok(Client::connect(["127.0.0.1:2379"], None) + .await + .map_err(|err| Error::CreateProviderError(err.to_string()))?) +} + +impl Etcd { + /// Create a new hashicorp Consul + /// + /// # Errors + /// + /// This function will return an error if cannot create a provider + pub async fn new(name: &str, opts: Option) -> Result { + let opts = opts.unwrap_or_default(); + + let address = opts + .address + .as_ref() + .ok_or_else(|| Error::Message("address not present.".to_string()))?; + + Ok(Self { + client: Mutex::new( + Client::connect([address], None) + .await + .map_err(|err| Error::CreateProviderError(err.to_string()))?, + ), + name: name.to_string(), + }) + } +} + +#[async_trait] +impl Provider for Etcd { + fn kind(&self) -> ProviderInfo { + ProviderInfo { + kind: ProviderKind::Etcd, + name: self.name.clone(), + } + } + + async fn get(&self, pm: &PathMap) -> Result> { + let mut client = create_client().await?; + + let res = if pm.keys.is_empty() { + client + .get(pm.path.as_str(), Some(GetOptions::new().with_prefix())) + .await + .map_err(|err| to_err(pm, err))? + .kvs() + .to_vec() + } else { + let mut res = Vec::new(); + for key in pm.keys.keys() { + let fetched = client + .get(format!("{}/{}", pm.path.as_str(), key), None) + .await + .map_err(|err| to_err(pm, err))? + .kvs() + .to_vec(); + res.extend(fetched); + } + res + }; + + drop(client); + + if res.is_empty() { + return Err(Error::NotFound { + msg: "not found".to_string(), + path: pm.path.clone(), + }); + } + + let mut results = vec![]; + for kv_pair in res { + let key = kv_pair.key_str().map_err(|err| to_err(pm, err))?; + + // strip path pref + let key = key + .strip_prefix(&pm.path) + .map_or(key, |s| s.trim_start_matches('/')); + + let val = kv_pair.value_str().map_err(|err| to_err(pm, err))?; + + results.push(KV::from_value(val, key, key, pm, self.kind())); + } + + Ok(results) + } + + async fn put(&self, pm: &PathMap, kvs: &[KV]) -> Result<()> { + let mut client = create_client().await?; + for kv in kvs { + client + .put( + format!("{}/{}", pm.path, kv.key).as_str(), + kv.value.as_bytes().to_vec(), + None, + ) + .await + .map_err(|e| to_err(pm, e))?; + } + drop(client); + + Ok(()) + } + + async fn del(&self, pm: &PathMap) -> Result<()> { + let mut client = create_client().await?; + if pm.keys.is_empty() { + client + .delete( + pm.path.as_str(), + Some(DeleteOptions::default().with_prefix()), + ) + .await + .map_err(|err| to_err(pm, err))?; + } else { + for key in pm.keys.keys().map(|kv| format!("{}/{kv}", &pm.path)) { + client + .delete(key, None) + .await + .map_err(|err| to_err(pm, err))?; + } + }; + drop(client); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::providers::test_utils; + + const PORT: u32 = 2379; + + #[test_log::test] + #[cfg(not(windows))] + fn sanity_test() { + use std::{collections::HashMap, env, time::Duration}; + + use dockertest::{waitfor, Composition, DockerTest, Image}; + + if env::var("RUNNER_OS").unwrap_or_default() == "macOS" { + return; + } + let mut test = DockerTest::new(); + let wait = Box::new(waitfor::MessageWait { + message: "serving client traffic insecurely".to_string(), + source: waitfor::MessageSource::Stderr, + timeout: 20, + }); + + let mut env = HashMap::new(); + + env.insert("ALLOW_NONE_AUTHENTICATION".to_string(), "yes".to_string()); + + #[cfg(target_arch = "aarch64")] + env.insert("ETCD_UNSUPPORTED_ARCH".to_string(), "arm64".to_string()); + + #[cfg(target_arch = "aarch64")] + let image_name = "bitnami/etcd"; + #[cfg(not(target_arch = "aarch64"))] + let image_name = "bitnami/etcd"; + + let image = Image::with_repository(image_name) + .pull_policy(dockertest::PullPolicy::IfNotPresent) + .source(dockertest::Source::DockerHub); + let mut etcd_container = Composition::with_image(image) + .with_container_name("etcd-server") + .with_env(env) + .with_wait_for(wait); + etcd_container.port_map(PORT, PORT); + + test.add_composition(etcd_container); + + test.run(|ops| async move { + let _instance = ops.handle("etcd-server"); + let address = format!("localhost:{PORT}"); + // banner is not enough, we have to wait for the image to stabilize + + let p = Box::new( + super::Etcd::new( + "etcd", + Some(EtcdOptions { + address: Some(address), + }), + ) + .await + .unwrap(), + ) as Box; + + test_utils::ProviderTest::new(p).run().await; + }); + } +} diff --git a/teller-providers/src/providers/hashicorp_consul.rs b/teller-providers/src/providers/hashicorp_consul.rs index 40c967c7..f3030909 100644 --- a/teller-providers/src/providers/hashicorp_consul.rs +++ b/teller-providers/src/providers/hashicorp_consul.rs @@ -133,7 +133,10 @@ impl Provider for HashiCorpConsul { let (_, key) = kv_pair.key.rsplit_once('/').unwrap_or(("", &kv_pair.key)); - results.push(KV::from_value(&val, key, key, pm, self.kind())); + // take all or slice the requested keys + if pm.keys.is_empty() || pm.keys.contains_key(key) { + results.push(KV::from_value(&val, key, key, pm, self.kind())); + } } Ok(results) diff --git a/teller-providers/src/providers/hashicorp_vault.rs b/teller-providers/src/providers/hashicorp_vault.rs index a4e8ef89..6de3a68e 100644 --- a/teller-providers/src/providers/hashicorp_vault.rs +++ b/teller-providers/src/providers/hashicorp_vault.rs @@ -98,7 +98,6 @@ fn parse_path(pm: &PathMap) -> Result<(&str, &str, &str)> { } fn xerr(pm: &PathMap, e: ClientError) -> Error { - println!("{e:?}"); match e { ClientError::RestClientError { source } => match source { rustify::errors::ClientError::ServerResponseError { code, content } => { diff --git a/teller-providers/src/providers/mod.rs b/teller-providers/src/providers/mod.rs index 6d46b181..b88ca07a 100644 --- a/teller-providers/src/providers/mod.rs +++ b/teller-providers/src/providers/mod.rs @@ -27,6 +27,9 @@ pub mod google_secretmanager; #[cfg(feature = "hashicorp_consul")] pub mod hashicorp_consul; +#[cfg(feature = "etcd")] +pub mod etcd; + lazy_static! { pub static ref PROVIDER_KINDS: String = { let providers: Vec = ProviderKind::iter() @@ -66,6 +69,10 @@ pub enum ProviderKind { #[cfg(feature = "google_secretmanager")] #[serde(rename = "google_secretmanager")] GoogleSecretManager, + + #[cfg(feature = "etcd")] + #[serde(rename = "etcd")] + Etcd, } impl std::fmt::Display for ProviderKind { diff --git a/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_development].snap b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_development].snap index 44b168bf..4c243031 100644 --- a/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_development].snap +++ b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_development].snap @@ -4,9 +4,9 @@ expression: res --- [ KV { - value: "DEBUG", - key: "log_level", - from_key: "log_level", + value: "Teller", + key: "app", + from_key: "app", path: Some( PathInfo { id: "", @@ -29,9 +29,9 @@ expression: res ), }, KV { - value: "Teller", - key: "app", - from_key: "app", + value: "{\"DB_PASS\": \"1234\",\"DB_NAME\": \"FOO\"}", + key: "db", + from_key: "db", path: Some( PathInfo { id: "", @@ -54,9 +54,9 @@ expression: res ), }, KV { - value: "{\"DB_PASS\": \"1234\",\"DB_NAME\": \"FOO\"}", - key: "db", - from_key: "db", + value: "DEBUG", + key: "log_level", + from_key: "log_level", path: Some( PathInfo { id: "", diff --git a/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_multiple_lotsakeys].snap b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_multiple_lotsakeys].snap new file mode 100644 index 00000000..092ff3bf --- /dev/null +++ b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-secret_multiple_lotsakeys].snap @@ -0,0 +1,2506 @@ +--- +source: teller-providers/src/providers/test_utils.rs +expression: res +--- +[ + KV { + value: "bar", + key: "foo_0", + from_key: "foo_0", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_1", + from_key: "foo_1", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_10", + from_key: "foo_10", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_11", + from_key: "foo_11", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_12", + from_key: "foo_12", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_13", + from_key: "foo_13", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_14", + from_key: "foo_14", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_15", + from_key: "foo_15", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_16", + from_key: "foo_16", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_17", + from_key: "foo_17", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_18", + from_key: "foo_18", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_19", + from_key: "foo_19", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_2", + from_key: "foo_2", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_20", + from_key: "foo_20", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_21", + from_key: "foo_21", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_22", + from_key: "foo_22", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_23", + from_key: "foo_23", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_24", + from_key: "foo_24", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_25", + from_key: "foo_25", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_26", + from_key: "foo_26", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_27", + from_key: "foo_27", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_28", + from_key: "foo_28", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_29", + from_key: "foo_29", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_3", + from_key: "foo_3", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_30", + from_key: "foo_30", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_31", + from_key: "foo_31", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_32", + from_key: "foo_32", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_33", + from_key: "foo_33", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_34", + from_key: "foo_34", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_35", + from_key: "foo_35", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_36", + from_key: "foo_36", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_37", + from_key: "foo_37", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_38", + from_key: "foo_38", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_39", + from_key: "foo_39", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_4", + from_key: "foo_4", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_40", + from_key: "foo_40", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_41", + from_key: "foo_41", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_42", + from_key: "foo_42", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_43", + from_key: "foo_43", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_44", + from_key: "foo_44", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_45", + from_key: "foo_45", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_46", + from_key: "foo_46", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_47", + from_key: "foo_47", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_48", + from_key: "foo_48", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_49", + from_key: "foo_49", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_5", + from_key: "foo_5", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_50", + from_key: "foo_50", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_51", + from_key: "foo_51", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_52", + from_key: "foo_52", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_53", + from_key: "foo_53", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_54", + from_key: "foo_54", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_55", + from_key: "foo_55", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_56", + from_key: "foo_56", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_57", + from_key: "foo_57", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_58", + from_key: "foo_58", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_59", + from_key: "foo_59", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_6", + from_key: "foo_6", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_60", + from_key: "foo_60", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_61", + from_key: "foo_61", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_62", + from_key: "foo_62", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_63", + from_key: "foo_63", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_64", + from_key: "foo_64", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_65", + from_key: "foo_65", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_66", + from_key: "foo_66", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_67", + from_key: "foo_67", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_68", + from_key: "foo_68", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_69", + from_key: "foo_69", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_7", + from_key: "foo_7", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_70", + from_key: "foo_70", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_71", + from_key: "foo_71", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_72", + from_key: "foo_72", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_73", + from_key: "foo_73", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_74", + from_key: "foo_74", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_75", + from_key: "foo_75", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_76", + from_key: "foo_76", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_77", + from_key: "foo_77", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_78", + from_key: "foo_78", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_79", + from_key: "foo_79", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_8", + from_key: "foo_8", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_80", + from_key: "foo_80", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_81", + from_key: "foo_81", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_82", + from_key: "foo_82", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_83", + from_key: "foo_83", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_84", + from_key: "foo_84", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_85", + from_key: "foo_85", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_86", + from_key: "foo_86", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_87", + from_key: "foo_87", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_88", + from_key: "foo_88", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_89", + from_key: "foo_89", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_9", + from_key: "foo_9", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_90", + from_key: "foo_90", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_91", + from_key: "foo_91", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_92", + from_key: "foo_92", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_93", + from_key: "foo_93", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_94", + from_key: "foo_94", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_95", + from_key: "foo_95", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_96", + from_key: "foo_96", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_97", + from_key: "foo_97", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_98", + from_key: "foo_98", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + KV { + value: "bar", + key: "foo_99", + from_key: "foo_99", + path: Some( + PathInfo { + id: "", + path: "secret/multiple/lotsakeys", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, +] diff --git a/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-selective-db].snap b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-selective-db].snap new file mode 100644 index 00000000..29a73da4 --- /dev/null +++ b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[get-selective-db].snap @@ -0,0 +1,33 @@ +--- +source: teller-providers/src/providers/test_utils.rs +expression: res +--- +Ok( + [ + KV { + value: "{\"DB_PASS\": \"1234\",\"DB_NAME\": \"FOO\"}", + key: "db", + from_key: "db", + path: Some( + PathInfo { + id: "", + path: "secret/development", + }, + ), + provider: Some( + ProviderInfo { + kind: PROVIDER_KIND, + name: PROVIDER_NAME, + }, + ), + meta: Some( + MetaInfo { + sensitivity: None, + redact_with: None, + source: None, + sink: None, + }, + ), + }, + ], +) diff --git a/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[put-secret_multiple_lotsakeys].snap b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[put-secret_multiple_lotsakeys].snap new file mode 100644 index 00000000..8c4d91a8 --- /dev/null +++ b/teller-providers/src/providers/snapshots/teller_providers__providers__test_utils__[put-secret_multiple_lotsakeys].snap @@ -0,0 +1,7 @@ +--- +source: teller-providers/src/providers/test_utils.rs +expression: res +--- +Ok( + (), +) diff --git a/teller-providers/src/providers/ssm.rs b/teller-providers/src/providers/ssm.rs index be720d0d..759029b1 100644 --- a/teller-providers/src/providers/ssm.rs +++ b/teller-providers/src/providers/ssm.rs @@ -126,47 +126,56 @@ impl Provider for SSM { async fn get(&self, pm: &PathMap) -> Result> { let mut out = Vec::new(); if pm.keys.is_empty() { - // get parameters by path + // get parameters by path, auto paginate, sends multiple requests let resp = self .client .get_parameters_by_path() .path(&pm.path) .with_decryption(pm.decrypt) + .into_paginator() .send() + .collect::, _>>() .await .map_err(|e| Error::GetError { msg: e.to_string(), path: pm.path.clone(), })?; - let params = resp.parameters(); - if params.is_empty() { + // sematics: total pages empty or *first page* empty is a 404 + if resp.is_empty() + || resp + .first() + .and_then(|params| params.parameters.as_ref()) + .is_some_and(Vec::is_empty) + { return Err(Error::NotFound { msg: "not found".to_string(), path: pm.path.clone(), }); } - for p in params { - let ssm_key = p.name().unwrap_or_default(); - if !ssm_key.starts_with(&pm.path) { - return Err(Error::GetError { - path: pm.path.clone(), - msg: format!("{ssm_key} is not contained in root path"), - }); - } - let relative_key = ssm_key - .strip_prefix(&pm.path) - .map(|k| k.trim_start_matches('/')) - .unwrap_or_default(); + for params in resp { + for p in params.parameters.unwrap_or_default() { + let ssm_key = p.name().unwrap_or_default(); + if !ssm_key.starts_with(&pm.path) { + return Err(Error::GetError { + path: pm.path.clone(), + msg: format!("{ssm_key} is not contained in root path"), + }); + } + + let relative_key = ssm_key + .strip_prefix(&pm.path) + .map_or(ssm_key, |k| k.trim_start_matches('/')); - out.push(KV::from_value( - p.value().unwrap_or_default(), - relative_key, - relative_key, - pm, - self.kind(), - )); + out.push(KV::from_value( + p.value().unwrap_or_default(), + relative_key, + relative_key, + pm, + self.kind(), + )); + } } } else { for (k, v) in &pm.keys { diff --git a/teller-providers/src/providers/test_utils.rs b/teller-providers/src/providers/test_utils.rs index bfad3633..82b2f1f0 100644 --- a/teller-providers/src/providers/test_utils.rs +++ b/teller-providers/src/providers/test_utils.rs @@ -8,6 +8,7 @@ use crate::{Error, Provider}; pub const ROOT_PATH_A: &str = "secret/development"; pub const ROOT_PATH_B: &str = "secret/multiple/app-1"; pub const ROOT_PATH_C: &str = "secret/multiple/app-2"; +pub const ROOT_PATH_PAGING: &str = "secret/multiple/lotsakeys"; const PATH_A_KEY_1: &str = "db"; const PATH_A_KEY_2: &str = "log_level"; const PATH_A_KEY_3: &str = "app"; @@ -67,6 +68,7 @@ impl ProviderTest { self.validate_get_unexisting_key().await; self.validate_put(&path_tree).await; self.validate_get(&path_tree).await; + self.validate_get_selective(&path_tree).await; self.validate_update().await; self.validate_delete().await; self.validate_delete_keys().await; @@ -120,6 +122,19 @@ impl ProviderTest { self.provider.as_ref().kind(), )], ), + ( + ROOT_PATH_PAGING, + (0..100) + .map(|idx| { + KV::from_literal( + "", + &format!("{PATH_C_KEY_1}_{idx}"), + PATH_C_VALUE_1, + self.provider.as_ref().kind(), + ) + }) + .collect::>(), + ), ]) } @@ -141,6 +156,7 @@ impl ProviderTest { .as_ref() .get(&PathMap::from_path(&format!("{ROOT_PATH_A}/invalid-path"))) .await; + println!("validate_get_unexisting_key: {res:?}"); assert!(res.is_err()); } @@ -173,6 +189,7 @@ impl ProviderTest { let mut res = res.unwrap(); res.sort_by(|a: &KV, b| a.value.cmp(&b.value)); + res.sort_by(|a: &KV, b| a.key.cmp(&b.key)); with_settings!({filters => vec![ (format!("{:?}", self.provider.as_ref().kind().kind).as_str(), "PROVIDER_KIND"), @@ -180,6 +197,7 @@ impl ProviderTest { (format!("\".*{ROOT_PATH_A}").as_str(), format!("\"{ROOT_PATH_A}").as_str()), (format!("\".*{ROOT_PATH_B}").as_str(), format!("\"{ROOT_PATH_B}").as_str()), (format!("\".*{ROOT_PATH_C}").as_str(), format!("\"{ROOT_PATH_C}").as_str()), + (format!("\".*{ROOT_PATH_PAGING}").as_str(), format!("\"{ROOT_PATH_PAGING}").as_str()), ]}, { assert_debug_snapshot!( format!("[get-{}]", root_path.replace('/', "_"),), @@ -189,6 +207,32 @@ impl ProviderTest { } } + async fn validate_get_selective(&self, path_tree: &HashMap<&str, Vec>) { + let mut selective_pm = PathMap::from_path(&self.get_key_path(ROOT_PATH_A)); + selective_pm + .keys + .insert(PATH_A_KEY_1.to_string(), PATH_A_KEY_1.to_string()); + + let res = self.provider.as_ref().get(&selective_pm).await; + + println!("validate_get_selective: {res:?}"); + assert!(res.is_ok()); + + with_settings!({filters => vec![ + (format!("{:?}", self.provider.as_ref().kind().kind).as_str(), "PROVIDER_KIND"), + (format!("{:?}", self.provider.as_ref().kind().name).as_str(), "PROVIDER_NAME"), + (format!("\".*{ROOT_PATH_A}").as_str(), format!("\"{ROOT_PATH_A}").as_str()), + (format!("\".*{ROOT_PATH_B}").as_str(), format!("\"{ROOT_PATH_B}").as_str()), + (format!("\".*{ROOT_PATH_C}").as_str(), format!("\"{ROOT_PATH_C}").as_str()), + (format!("\".*{ROOT_PATH_PAGING}").as_str(), format!("\"{ROOT_PATH_PAGING}").as_str()), + ]}, { + assert_debug_snapshot!( + format!("[get-selective-{}]", PATH_A_KEY_1.replace('/', "_"),), + res + ); + }); + } + /// Validates the update operation on a Teller provider after inserting a tree structure. /// /// This function is intended to be executed after running the `validate_get` function to insert a tree structure into the diff --git a/teller-providers/src/registry.rs b/teller-providers/src/registry.rs index 9f81f06e..264d7c6e 100644 --- a/teller-providers/src/registry.rs +++ b/teller-providers/src/registry.rs @@ -78,6 +78,7 @@ impl Registry { .transpose()?, )?) } + ProviderKind::Etcd => todo!(), }; loaded_providers.insert(k.clone(), provider); } From ca3fe464e94c7ccf90ef442aa7b5f1e13f26b6fd Mon Sep 17 00:00:00 2001 From: Dotan Nahum Date: Fri, 17 May 2024 15:36:27 +0300 Subject: [PATCH 2/3] align ci --- .github/workflows/build.yml | 9 +++++++++ .github/workflows/release.yml | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 538fbd22..affbb803 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,9 @@ jobs: - uses: Swatinem/rust-cache@v2 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Run cargo test uses: actions-rs/cargo@v1 with: @@ -55,6 +58,9 @@ jobs: override: true components: llvm-tools-preview + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - uses: Swatinem/rust-cache@v2 - name: Download grcov @@ -91,6 +97,9 @@ jobs: override: true components: rustfmt, clippy + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - uses: Swatinem/rust-cache@v2 - name: Run cargo fmt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index accb493c..2f9fac01 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,6 +38,9 @@ jobs: toolchain: ${{ matrix.rust }} override: true + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - uses: Swatinem/rust-cache@v2 - name: Run cargo test @@ -98,6 +101,9 @@ jobs: with: node-version: 18 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install ${{ matrix.rust }} toolchain uses: actions-rs/toolchain@v1 with: From 72788bed667cb69f543d860ab79f76482044e3af Mon Sep 17 00:00:00 2001 From: Dotan Nahum Date: Sun, 19 May 2024 16:39:52 +0300 Subject: [PATCH 3/3] finalize etcd impl --- teller-providers/src/providers/etcd.rs | 52 ++++++++++++++++++-------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/teller-providers/src/providers/etcd.rs b/teller-providers/src/providers/etcd.rs index 301060e8..dc5639d8 100644 --- a/teller-providers/src/providers/etcd.rs +++ b/teller-providers/src/providers/etcd.rs @@ -1,12 +1,12 @@ -//! Hashicorp Consul +//! etcd //! //! //! ## Example configuration //! //! ```yaml //! providers: -//! consul1: -//! kind: hashicorp_consul +//! etcd1: +//! kind: etcd //! # options: ... //! ``` //! ## Options @@ -15,7 +15,7 @@ //! use async_trait::async_trait; -use etcd_client::{Client, DeleteOptions, GetOptions, KvClient}; +use etcd_client::{Client, ConnectOptions, DeleteOptions, GetOptions}; use serde_derive::{Deserialize, Serialize}; use tokio::sync::Mutex; @@ -25,11 +25,20 @@ use crate::{ Error, Provider, Result, }; +/// Etcd Options +/// #[allow(clippy::module_name_repetitions)] #[derive(Default, Serialize, Deserialize, Debug, Clone)] pub struct EtcdOptions { /// Etcd address. pub address: Option, + + /// An optional 'user' field, which includes a user name and password separated by `:`. + /// + /// Example: `joe:rootpass` + /// + /// see more: [on this github thread](https://github.com/etcd-io/etcd/issues/10338#issuecomment-448280528) + pub user: Option, } pub struct Etcd { @@ -40,14 +49,9 @@ pub struct Etcd { fn to_err(_pm: &PathMap, err: etcd_client::Error) -> Error { Error::Any(Box::new(err)) } -async fn create_client() -> Result { - Ok(Client::connect(["127.0.0.1:2379"], None) - .await - .map_err(|err| Error::CreateProviderError(err.to_string()))?) -} impl Etcd { - /// Create a new hashicorp Consul + /// Create a new etcd provider /// /// # Errors /// @@ -60,9 +64,26 @@ impl Etcd { .as_ref() .ok_or_else(|| Error::Message("address not present.".to_string()))?; + let user = opts + .user + .or_else(|| std::env::var("ETCDCTL_USER").ok()) + .map(|user| { + user.split_once(':') + .ok_or_else(|| { + Error::Message("user field should be in `user:pwd` format".to_string()) + }) + .map(|(user, pwd)| (user.to_string(), pwd.to_string())) + }) + .transpose()?; + + let mut connect_opts = ConnectOptions::new(); + if let Some(user) = user { + connect_opts = connect_opts.with_user(user.0, user.1); + } + Ok(Self { client: Mutex::new( - Client::connect([address], None) + Client::connect([address], Some(connect_opts)) .await .map_err(|err| Error::CreateProviderError(err.to_string()))?, ), @@ -81,7 +102,7 @@ impl Provider for Etcd { } async fn get(&self, pm: &PathMap) -> Result> { - let mut client = create_client().await?; + let mut client = self.client.lock().await.kv_client(); let res = if pm.keys.is_empty() { client @@ -131,7 +152,7 @@ impl Provider for Etcd { } async fn put(&self, pm: &PathMap, kvs: &[KV]) -> Result<()> { - let mut client = create_client().await?; + let mut client = self.client.lock().await.kv_client(); for kv in kvs { client .put( @@ -148,7 +169,7 @@ impl Provider for Etcd { } async fn del(&self, pm: &PathMap) -> Result<()> { - let mut client = create_client().await?; + let mut client = self.client.lock().await.kv_client(); if pm.keys.is_empty() { client .delete( @@ -182,7 +203,7 @@ mod tests { #[test_log::test] #[cfg(not(windows))] fn sanity_test() { - use std::{collections::HashMap, env, time::Duration}; + use std::{collections::HashMap, env}; use dockertest::{waitfor, Composition, DockerTest, Image}; @@ -229,6 +250,7 @@ mod tests { "etcd", Some(EtcdOptions { address: Some(address), + user: None, }), ) .await