-
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?
Spin up should check required variables #3213
Conversation
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
crates/loader/src/local.rs
Outdated
@@ -135,6 +136,21 @@ impl LocalLoader { | |||
}) | |||
} | |||
|
|||
fn env_checker((key, val): (String, Variable)) -> anyhow::Result<(String, Variable)> { |
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.
This function name needs to be more descriptive. What is it checking?
crates/loader/src/local.rs
Outdated
@@ -135,6 +136,21 @@ impl LocalLoader { | |||
}) | |||
} | |||
|
|||
fn env_checker((key, val): (String, Variable)) -> anyhow::Result<(String, Variable)> { |
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.
Not clear why this takes a tuple rather than two parameters?
crates/loader/src/local.rs
Outdated
@@ -88,7 +89,7 @@ impl LocalLoader { | |||
|
|||
let variables = variables | |||
.into_iter() | |||
.map(|(name, v)| Ok((name.to_string(), locked_variable(v)?))) | |||
.map(|(name, v)| Self::env_checker((name.to_string(), locked_variable(v)?))) |
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.
Doing this here means appears to mean we can't even build the LockedApp without having the variables available. How does this worth with e.g. spin registry push
or spin cloud deploy
?
crates/loader/src/local.rs
Outdated
@@ -135,6 +136,21 @@ impl LocalLoader { | |||
}) | |||
} | |||
|
|||
fn env_checker((key, val): (String, Variable)) -> anyhow::Result<(String, Variable)> { | |||
if val.default.is_none() { | |||
if std::env::var(env_key(None, key.as_ref())).is_err() { |
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.
You can't assume that variables will come from the host environment. They could come from Vault or Azure KeyVault - for example passwords or tokens would typically be stashed in vaults and it would be rude to demand users create dummy EVs for them. Variable sources are defined in the runtime config file - you can't validate them until you have the runtime config providers lined up.
crates/loader/src/local.rs
Outdated
@@ -135,6 +136,21 @@ impl LocalLoader { | |||
}) | |||
} | |||
|
|||
fn env_checker((key, val): (String, Variable)) -> anyhow::Result<(String, Variable)> { | |||
if val.default.is_none() { | |||
if std::env::var(env_key(None, key.as_ref())).is_err() { |
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.
How do you know that the user is using the default prefix?
crates/loader/src/local.rs
Outdated
@@ -135,6 +136,21 @@ impl LocalLoader { | |||
}) | |||
} | |||
|
|||
fn env_checker((key, val): (String, Variable)) -> anyhow::Result<(String, Variable)> { | |||
if val.default.is_none() { | |||
if std::env::var(env_key(None, key.as_ref())).is_err() { |
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.
The environment provider appears to support a dotenv_path
setting to merge values from a file - not clear that this is respected here?
crates/loader/src/local.rs
Outdated
@@ -88,7 +89,7 @@ impl LocalLoader { | |||
|
|||
let variables = variables | |||
.into_iter() | |||
.map(|(name, v)| Ok((name.to_string(), locked_variable(v)?))) | |||
.map(|(name, v)| Self::env_checker((name.to_string(), locked_variable(v)?))) |
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.
Maybe a nit, but doing this in the loader produces an error message that implies we can't understand the manifest (usually meaning invalid TOML or incorrect schema): that's potentially misleading.
I think adding a new hook before execution of the CLI for the variables factor in crates/trigger/src/cl as discussed offline earlier might be a better place to run the check. |
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
Interestingly, the test failing in CI doesn't fail locally. Investigating it at the moment. |
Signed-off-by: Aminu 'Seun Joshua <[email protected]>
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.
I feel like I stuffed up communication in my previous review, sorry. The Spin variables system allows the user to configure variables to be read from multiple different sources. You cannot rely on the environment being the only source. You need to rethink this PR to completely forget about the environment. It needs to go through a configured ProviderResolver
or whatever it's called at the moment, the thing that gets set up in the VariablesFactor
and checks all configured sources for values. That will do the environment lookup, and any other lookups - the validation code should not be attempting a partial emulation of its behaviour, you should just use what exists.
@@ -64,12 +64,13 @@ spin-oci = { path = "crates/oci" } | |||
spin-plugins = { path = "crates/plugins" } | |||
spin-runtime-factors = { path = "crates/runtime-factors" } | |||
spin-telemetry = { path = "crates/telemetry", features = [ | |||
"tracing-log-compat", | |||
"tracing-log-compat", |
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?
@@ -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 comment
The 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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Sorting? Maybe comment on why they need sorting?
use spin_variables::{VariableProviderConfiguration, VariableSourcer}; | ||
|
||
/// Implements TriggerHooks, sorting required variables | ||
pub struct VariableSorterExecutorHooks { |
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.
Not sure about the naming here. Why is it an "Executor" hook?
tokio = { workspace = true, features = ["fs", "rt"] } | ||
toml = { workspace = true } |
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.
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.
} | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
variable_env_checker
is a noun. I think the idea here is something like check_variable_has_value
or check_variable_resolves
.
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 comment
The 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?
_ = 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 comment
The 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.
Closes #3115
The PR checks if the variable default isn't provided when required. The check is handled by a new hook:
VariableSorterExecutorHooks
.So now it's captured before runtime, you'd get some error like this