use std::collections::BTreeMap;
use std::path::Path;
use std::time::{Duration, SystemTime};
use olai_stack_runtime::{
EnvLayers, Layer, Overlay, SecretError, SecretProvider, SecretProviders, SecretValue,
};
use olai_stack_state::{SecretRef, VarSet, VarValue};
struct Vault;
impl SecretProvider for Vault {
fn name(&self) -> &str {
"vault"
}
fn get(&self, key: &str) -> Result<SecretValue, SecretError> {
Ok(SecretValue::new(format!("value-of-{key}")))
}
}
fn map(pairs: &[(&str, &str)]) -> BTreeMap<String, String> {
pairs
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()
}
fn vars(pairs: &[(&str, VarValue)]) -> VarSet {
let mut set = VarSet::new();
for (k, v) in pairs {
set.set(*k, v.clone());
}
set
}
fn lit(v: &str) -> VarValue {
VarValue::Literal(v.into())
}
fn secret(key: &str) -> VarValue {
VarValue::Secret(SecretRef {
provider: "vault".into(),
key: key.into(),
})
}
fn check(name: &str, layers: EnvLayers) {
let providers = SecretProviders::new().with(Vault);
let resolved = layers.resolve(&providers).unwrap();
let actual = serde_json::to_string_pretty(&resolved.masked()).unwrap() + "\n";
assert!(!actual.contains("value-of-"), "{actual}");
assert!(!actual.contains("typed-in"), "{actual}");
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/effective_env")
.join(format!("{name}.json"));
if std::env::var_os("BLESS").is_some() {
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, &actual).unwrap();
return;
}
let expected = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("{}: {e} (run with BLESS=1)", path.display()));
assert_eq!(actual, expected, "{name} drifted (re-bless with BLESS=1)");
}
#[test]
fn precedence_runs_low_to_high() {
let layers = EnvLayers::new()
.with_vars(
Layer::InstanceOverrides,
&vars(&[
("LEVEL", lit("instance")),
("COMPOSE_PROJECT_NAME", lit("nope")),
("LOCAL_RUSTFS_SECRET_KEY", lit("typed-in")),
]),
)
.with_plain(Layer::CatalogDefaults, &map(&[("LEVEL", "catalog")]))
.with_secrets(
Layer::CatalogDefaults,
&map(&[
("LOCAL_POSTGRES_PASSWORD", "postgres"),
("LOCAL_RUSTFS_SECRET_KEY", "rustfsadmin"),
]),
)
.with_plain(
Layer::RevisionManifest,
&map(&[("LEVEL", "manifest"), ("DATA_ROOT", "./.data")]),
)
.with_plain(
Layer::Placement,
&map(&[
("COMPOSE_PROJECT_NAME", "demo_dev_r1"),
("TRESTLE_SLOT", "1"),
]),
)
.with_plain(
Layer::ClientEnv,
&map(&[
("LEVEL", "client"),
("GATEWAY_URL", "http://localhost:10080"),
]),
)
.with_vars(
Layer::WorkspaceVars,
&vars(&[
("LEVEL", lit("workspace")),
("LOCAL_POSTGRES_PASSWORD", secret("pg")),
("COMPOSE_PROJECT_NAME", lit("nope")),
]),
)
.with_vars(
Layer::EnvironmentVars,
&vars(&[("LEVEL", lit("environment")), ("REGION", lit("eu-west-1"))]),
)
.with_vars(
Layer::RevisionVars,
&vars(&[("LEVEL", lit("revision")), ("FEATURE", lit("on"))]),
)
.with_overlay(
"neon",
&Overlay {
vars: map(&[("LEVEL", "neon")]),
endpoints: map(&[("UC_URL", "https://uc.example")]),
credentials: [("PGPASSWORD".to_owned(), SecretValue::new("branch-pw"))].into(),
expires_at: Some(SystemTime::UNIX_EPOCH + Duration::from_secs(1)),
},
)
.with_overlay(
"user",
&Overlay {
vars: map(&[("LEVEL", "user"), ("UC_URL", "http://localhost:8080")]),
..Overlay::default()
},
);
check("precedence", layers);
}
#[test]
fn sources_apply_in_the_order_given() {
let overlay = |v: &str| Overlay {
vars: map(&[("WHO", v)]),
..Overlay::default()
};
let layers = EnvLayers::new()
.with_overlay("zeta", &overlay("zeta"))
.with_overlay("alpha", &overlay("alpha"));
check("source_order", layers);
}
#[test]
fn an_unresolvable_reference_is_an_error() {
let layers = EnvLayers::new().with_vars(
Layer::WorkspaceVars,
&vars(&[(
"TOKEN",
VarValue::Secret(SecretRef {
provider: "keychain".into(),
key: "t".into(),
}),
)]),
);
let err = layers
.resolve(&SecretProviders::new().with(Vault))
.unwrap_err();
assert!(matches!(err, SecretError::UnknownProvider(p) if p == "keychain"));
}
#[test]
fn an_expired_overlay_is_noticed() {
let overlay = Overlay {
expires_at: Some(SystemTime::now() - Duration::from_secs(1)),
..Overlay::default()
};
assert!(overlay.is_expired(SystemTime::now()));
assert!(!Overlay::default().is_expired(SystemTime::now()));
}