-
Notifications
You must be signed in to change notification settings - Fork 282
Spin up should check required variables #3213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
73a0280
54915b2
9eb086c
ffa4aad
f72cda6
a51d601
4a038e4
03bf252
fed8aaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
//! Environment variable utilities | ||
|
||
/// Defines the default environment variable prefix used by Spin. | ||
pub const DEFAULT_ENV_PREFIX: &str = "SPIN_VARIABLE"; | ||
|
||
/// Creates an environment variable key based on the given prefix and key. | ||
pub fn env_key(prefix: Option<String>, key: &str) -> String { | ||
let prefix = prefix.unwrap_or_else(|| DEFAULT_ENV_PREFIX.to_string()); | ||
let upper_key = key.to_ascii_uppercase(); | ||
let key = format!("{prefix}_{upper_key}"); | ||
key | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
|
||
pub mod arg_parser; | ||
pub mod data_dir; | ||
pub mod env; | ||
pub mod paths; | ||
pub mod sha256; | ||
pub mod sloth; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
use spin_app::locked::LockedApp; | ||
use spin_common::env::env_key; | ||
use spin_factors::{ | ||
anyhow::{self, Context}, | ||
wasmtime::{component::Linker, Config, Engine}, | ||
|
@@ -100,6 +101,8 @@ impl<T: RuntimeFactors> TestEnvironment<T> { | |
pub async fn build_locked_app(manifest: &toml::Table) -> anyhow::Result<LockedApp> { | ||
let toml_str = toml::to_string(manifest).context("failed serializing manifest")?; | ||
let dir = tempfile::tempdir().context("failed creating tempdir")?; | ||
// `foo` variable is set to require. As we're not providing a default value, env is checked. | ||
std::env::set_var(env_key(None, "foo"), "baz"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still say we should be able to transform a TOML manifest into a LockedApp without doing validation on variables. |
||
let path = dir.path().join("spin.toml"); | ||
std::fs::write(&path, toml_str).context("failed writing manifest")?; | ||
spin_loader::from_file(&path, FilesMountStrategy::Direct, None).await | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,9 @@ spin-factor-wasi = { path = "../factor-wasi" } | |
spin-factors = { path = "../factors" } | ||
spin-factors-executor = { path = "../factors-executor" } | ||
spin-telemetry = { path = "../telemetry" } | ||
spin-variables = { path = "../variables" } | ||
tokio = { workspace = true, features = ["fs", "rt"] } | ||
toml = { workspace = true } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Ryan had tried to push TOML dependencies into the runtime config crate, so that the trigger crate was abstracted from the physical representation of the runtime config? (For e.g. SpinKube scenarios.) I am not sure though. |
||
tracing = { workspace = true } | ||
|
||
[dev-dependencies] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
use spin_core::async_trait; | ||
use spin_factors::RuntimeFactors; | ||
use spin_factors_executor::ExecutorHooks; | ||
use spin_variables::{VariableProviderConfiguration, VariableSourcer}; | ||
|
||
/// Implements TriggerHooks, sorting required variables | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorting? Maybe comment on why they need sorting? |
||
pub struct VariableSorterExecutorHooks { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about the naming here. Why is it an "Executor" hook? |
||
table: toml::Table, | ||
} | ||
|
||
impl VariableSorterExecutorHooks { | ||
pub fn new(table: toml::Table) -> Self { | ||
Self { table } | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for VariableSorterExecutorHooks { | ||
async fn configure_app( | ||
&self, | ||
configured_app: &spin_factors::ConfiguredApp<F>, | ||
) -> anyhow::Result<()> { | ||
for (key, variable) in configured_app.app().variables() { | ||
self.variable_env_checker(key.clone(), variable.clone())?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl VariableSourcer for VariableSorterExecutorHooks { | ||
fn variable_env_checker(&self, key: String, val: spin_app::Variable) -> anyhow::Result<()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
let configs = spin_variables::variable_provider_config_from_toml(&self.table)?; | ||
|
||
if let Some(config) = configs.into_iter().next() { | ||
let (dotenv_path, prefix) = match config { | ||
VariableProviderConfiguration::Env(env) => (env.dotenv_path, env.prefix), | ||
_ => (None, None), | ||
}; | ||
return self.check(key, val, dotenv_path, prefix); | ||
} | ||
|
||
Err(anyhow::anyhow!("No environment variable provider found")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what "provider" refers to in this context. Have I misunderstood the purpose of this function and it's actually trying to check if the env provider is present? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,12 @@ mod env; | |
mod statik; | ||
mod vault; | ||
|
||
use std::path::PathBuf; | ||
|
||
pub use azure_key_vault::*; | ||
pub use env::*; | ||
use spin_common::{env::env_key, ui::quoted_path}; | ||
use spin_locked_app::Variable; | ||
pub use statik::*; | ||
pub use vault::*; | ||
|
||
|
@@ -38,6 +42,24 @@ pub fn runtime_config_from_toml(table: &impl GetTomlValue) -> anyhow::Result<Run | |
Ok(RuntimeConfig { providers }) | ||
} | ||
|
||
pub fn variable_provider_config_from_toml( | ||
table: &impl GetTomlValue, | ||
) -> anyhow::Result<Vec<VariableProviderConfiguration>> { | ||
if let Some(array) = table | ||
.get("variables_provider") | ||
.or_else(|| table.get("config_provider")) | ||
{ | ||
array | ||
.clone() | ||
.try_into::<Vec<VariableProviderConfiguration>>() | ||
.map_err(|e| anyhow::anyhow!("Failed to parse variable provider configuration: {}", e)) | ||
} else { | ||
Ok(vec![VariableProviderConfiguration::Env( | ||
EnvVariablesConfig::default(), | ||
)]) | ||
} | ||
} | ||
|
||
/// A runtime configuration used in the Spin CLI for one type of variable provider. | ||
#[derive(Debug, Deserialize)] | ||
#[serde(rename_all = "snake_case", tag = "type")] | ||
|
@@ -70,3 +92,34 @@ impl VariableProviderConfiguration { | |
Ok(provider) | ||
} | ||
} | ||
|
||
pub trait VariableSourcer { | ||
fn variable_env_checker(&self, key: String, val: Variable) -> anyhow::Result<()>; | ||
|
||
fn check( | ||
&self, | ||
key: String, | ||
mut val: Variable, | ||
dotenv_path: Option<PathBuf>, | ||
prefix: Option<String>, | ||
) -> anyhow::Result<()> { | ||
if val.default.is_some() { | ||
return Ok(()); | ||
} | ||
|
||
if let Some(path) = dotenv_path { | ||
_ = std::env::set_current_dir(path); | ||
} | ||
|
||
match std::env::var(env_key(prefix, &key)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can't do this. There are providers other than the environment one. It is legal for a variable to come from Vault or Azure KeyVault or the static provider rather than the environment one. I tried to stress this in my previous review, sorry - this is fundamental. |
||
Ok(v) => { | ||
val.default = Some(v); | ||
Ok(()) | ||
} | ||
Err(_) => Err(anyhow::anyhow!( | ||
"Variable data not provided for {}", | ||
quoted_path(key) | ||
)), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a few of these unrelated layout changes - maybe turn off auto formatting for this file?