Skip to content

Commit

Permalink
Add vault variable provider
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Levick <ryan.levick@fermyon.com>
  • Loading branch information
rylev committed Jul 29, 2024
1 parent 1a97874 commit 947b720
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/factor-variables/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ spin-world = { path = "../world" }
tokio = { version = "1", features = ["rt-multi-thread"] }
toml = "0.8"
tracing = { workspace = true }
vaultrs = "0.6.2"

[dev-dependencies]
spin-factors-test = { path = "../factors-test" }
Expand Down
5 changes: 5 additions & 0 deletions crates/factor-variables/src/spin_cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

mod env;
mod statik;
mod vault;

use serde::Deserialize;
use spin_expressions::Provider;
use spin_factors::anyhow;
use statik::StaticVariablesProvider;
use vault::VaultVariablesProvider;

use crate::runtime_config::RuntimeConfig;

Expand All @@ -30,6 +32,8 @@ pub fn runtime_config_from_toml(table: &toml::Table) -> anyhow::Result<Option<Ru
pub enum VariableProviderConfiguration {
/// A static provider of variables.
Static(StaticVariablesProvider),
/// A provider that uses HashiCorp Vault.
Vault(VaultVariablesProvider),
/// An environment variable provider.
Env(env::EnvVariablesConfig),
}
Expand All @@ -44,6 +48,7 @@ impl VariableProviderConfiguration {
|s| std::env::var(s),
config.dotenv_path,
)),
VariableProviderConfiguration::Vault(provider) => Box::new(provider),
}
}
}
55 changes: 55 additions & 0 deletions crates/factor-variables/src/spin_cli/vault.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use serde::{Deserialize, Serialize};
use spin_expressions::async_trait::async_trait;
use spin_factors::anyhow::{self, Context as _};
use tracing::{instrument, Level};
use vaultrs::{
client::{VaultClient, VaultClientSettingsBuilder},
error::ClientError,
kv2,
};

use spin_expressions::{Key, Provider};

#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
/// A config Provider that uses HashiCorp Vault.
pub struct VaultVariablesProvider {
/// The URL of the Vault server.
url: String,
/// The token to authenticate with.
token: String,
/// The mount point of the KV engine.
mount: String,
/// The optional prefix to use for all keys.
#[serde(default)]
prefix: Option<String>,
}

#[async_trait]
impl Provider for VaultVariablesProvider {
#[instrument(name = "spin_variables.get_from_vault", skip(self), err(level = Level::INFO), fields(otel.kind = "client"))]
async fn get(&self, key: &Key) -> anyhow::Result<Option<String>> {
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address(&self.url)
.token(&self.token)
.build()?,
)?;
let path = match &self.prefix {
Some(prefix) => format!("{}/{}", prefix, key.as_str()),
None => key.as_str().to_string(),
};

#[derive(Deserialize, Serialize)]
struct Secret {
value: String,
}
match kv2::read::<Secret>(&client, &self.mount, &path).await {
Ok(secret) => Ok(Some(secret.value)),
// Vault doesn't have this entry so pass along the chain
Err(ClientError::APIError { code: 404, .. }) => Ok(None),
// Other Vault error so bail rather than looking elsewhere
Err(e) => Err(e).context("Failed to check Vault for config"),
}
}
}

0 comments on commit 947b720

Please sign in to comment.