Skip to content

Commit

Permalink
Merge pull request #149 from kate-goldenring/runtime-and-app-env-config
Browse files Browse the repository at this point in the history
Make container environment variables accessible as application variables
  • Loading branch information
Mossaka authored Jul 18, 2024
2 parents 4d02489 + 40fa6df commit 5ecf6d6
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
10 changes: 10 additions & 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 containerd-shim-spin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ ctrlc = { version = "3.2", features = ["termination"] }

[dev-dependencies]
wat = "1"
temp-env = "0.3.6"
76 changes: 76 additions & 0 deletions containerd-shim-spin/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ const OCI_LAYER_MEDIA_TYPE_WASM: &str = "application/vnd.wasm.content.layer.v1+w
/// Expected location of the Spin manifest when loading from a file rather than
/// an OCI image
const SPIN_MANIFEST_FILE_PATH: &str = "/spin.toml";
/// Known prefix for the Spin application variables environment variable
/// provider: https://github.com/fermyon/spin/blob/436ad589237c02f7aa4693e984132808fd80b863/crates/variables/src/provider/env.rs#L9
const SPIN_APPLICATION_VARIABLE_PREFIX: &str = "SPIN_VARIABLE";

#[derive(Clone)]
pub struct SpinEngine {
Expand Down Expand Up @@ -205,6 +208,7 @@ impl SpinEngine {
env::set_var("XDG_CACHE_HOME", &cache_dir);
let app_source = self.app_source(ctx, &cache).await?;
let resolved_app_source = self.resolve_app_source(app_source.clone(), &cache).await?;
configure_application_variables_from_environment_variables(&resolved_app_source)?;
let trigger_cmds = trigger_command_for_resolved_app_source(&resolved_app_source)
.with_context(|| format!("Couldn't find trigger executor for {app_source:?}"))?;
let locked_app = self.load_resolved_app_source(resolved_app_source).await?;
Expand Down Expand Up @@ -513,6 +517,21 @@ impl ResolvedAppSource {
ensure!(!types.is_empty(), "no triggers in app");
Ok(types.into_iter().map(|t| t.as_str()).collect())
}

pub fn variables(&self) -> Vec<&str> {
match self {
ResolvedAppSource::File { manifest, .. } => manifest
.variables
.keys()
.map(|k| k.as_ref())
.collect::<Vec<_>>(),
ResolvedAppSource::OciRegistry { locked_app } => locked_app
.variables
.keys()
.map(|k| k.as_ref())
.collect::<Vec<_>>(),
}
}
}

fn trigger_command_for_resolved_app_source(resolved: &ResolvedAppSource) -> Result<Vec<String>> {
Expand All @@ -533,10 +552,67 @@ fn trigger_command_for_resolved_app_source(resolved: &ResolvedAppSource) -> Resu
Ok(trigger_types.iter().map(|x| x.to_string()).collect())
}

// For each Spin app variable, checks if a container environment variable with
// the same name exists and duplicates it in the environment with the
// application variable prefix
fn configure_application_variables_from_environment_variables(
resolved: &ResolvedAppSource,
) -> Result<()> {
resolved
.variables()
.into_iter()
.map(str::to_ascii_uppercase)
.for_each(|variable| {
env::var(&variable)
.map(|val| {
let prefixed = format!("{}_{}", SPIN_APPLICATION_VARIABLE_PREFIX, variable);
env::set_var(prefixed, val);
})
.ok();
});
Ok(())
}

#[cfg(test)]
mod tests {
use std::env;

use super::*;

#[test]
fn test_configure_application_variables_from_environment_variables() {
temp_env::with_vars(
[
("SPIN_VARIABLE_DO_NOT_RESET", Some("val1")),
("SHOULD_BE_PREFIXED", Some("val2")),
("ignored_if_not_uppercased_env", Some("val3")),
],
|| {
let app_json = r#"
{
"spin_lock_version": 1,
"entrypoint": "test",
"components": [],
"variables": {"should_be_prefixed": { "required": "true"}, "do_not_reset" : { "required": "true"}, "not_set_as_container_env": { "required": "true"}, "ignored_if_not_uppercased_env": { "required": "true"}},
"triggers": []
}"#;
let locked_app = LockedApp::from_json(app_json.as_bytes()).unwrap();
let resolved = ResolvedAppSource::OciRegistry { locked_app };

configure_application_variables_from_environment_variables(&resolved).unwrap();
assert_eq!(env::var("SPIN_VARIABLE_DO_NOT_RESET").unwrap(), "val1");
assert_eq!(
env::var("SPIN_VARIABLE_SHOULD_BE_PREFIXED").unwrap(),
"val2"
);
assert!(env::var("SPIN_VARIABLE_NOT_SET_AS_CONTAINER_ENV").is_err());
assert!(env::var("SPIN_VARIABLE_IGNORED_IF_NOT_UPPERCASED_ENV").is_err());
// Original env vars are still retained but not set in variable provider
assert!(env::var("SHOULD_BE_PREFIXED").is_ok());
},
);
}

#[test]
fn can_parse_spin_address() {
let parsed = parse_addr(SPIN_ADDR_DEFAULT).unwrap();
Expand Down

0 comments on commit 5ecf6d6

Please sign in to comment.