interpolate core variables / secrets into build secret_args

This commit is contained in:
mbecker20
2024-07-01 02:27:03 -07:00
parent c92515cecc
commit b4dc446f95
3 changed files with 99 additions and 15 deletions
+40 -5
View File
@@ -145,8 +145,10 @@ impl Resolve<RunBuild, (User, Update)> for State {
"get builder",
format_serror(&e.context("failed to get builder").into()),
));
return handle_early_return(update, build.id, build.name, false)
.await;
return handle_early_return(
update, build.id, build.name, false,
)
.await;
}
};
@@ -196,6 +198,9 @@ impl Resolve<RunBuild, (User, Update)> for State {
// Interpolate variables / secrets into build args
let mut global_replacers = HashSet::new();
let mut secret_replacers = HashSet::new();
let mut secret_replacers_for_log = HashSet::new();
// Interpolate into build args
for arg in &mut build.config.build_args {
// first pass - global variables
let (res, more_replacers) = svi::interpolate_variables(
@@ -214,10 +219,40 @@ impl Resolve<RunBuild, (User, Update)> for State {
false,
)
.context("failed to interpolate core secrets")?;
secret_replacers_for_log.extend(
more_replacers.iter().map(|(_, variable)| variable.clone()),
);
secret_replacers.extend(more_replacers);
arg.value = res;
}
// Interpolate into secret args
for arg in &mut build.config.secret_args {
// first pass - global variables
let (res, more_replacers) = svi::interpolate_variables(
&arg.value,
&variables,
svi::Interpolator::DoubleBrackets,
false,
)
.context("failed to interpolate global variables")?;
global_replacers.extend(more_replacers);
// second pass - core secrets
let (res, more_replacers) = svi::interpolate_variables(
&res,
&core_config.secrets,
svi::Interpolator::DoubleBrackets,
false,
)
.context("failed to interpolate core secrets")?;
secret_replacers_for_log.extend(
more_replacers.into_iter().map(|(_, variable)| variable),
);
// Secret args don't need to be in replacers sent to periphery.
// The secret args don't end up in the command like build args do.
arg.value = res;
}
// Show which variables were interpolated
if !global_replacers.is_empty() {
update.push_simple_log(
@@ -232,9 +267,9 @@ impl Resolve<RunBuild, (User, Update)> for State {
if !secret_replacers.is_empty() {
update.push_simple_log(
"interpolate core secrets",
secret_replacers
.iter()
.map(|(_, variable)| format!("<span class=\"text-muted-foreground\">replaced:</span> {variable}"))
secret_replacers_for_log
.into_iter()
.map(|variable| format!("<span class=\"text-muted-foreground\">replaced:</span> {variable}"))
.collect::<Vec<_>>()
.join("\n"),
);
+51 -8
View File
@@ -1,4 +1,4 @@
use anyhow::Context;
use anyhow::{anyhow, Context};
use command::run_monitor_command;
use formatting::format_serror;
use monitor_client::entities::{
@@ -20,10 +20,7 @@ use crate::{
};
impl Resolve<build::Build> for State {
#[instrument(
name = "Build",
skip(self, registry_token, core_replacers, aws_ecr)
)]
#[instrument(name = "Build", skip_all)]
async fn resolve(
&self,
build::Build {
@@ -43,6 +40,7 @@ impl Resolve<build::Build> for State {
build_path,
dockerfile_path,
build_args,
secret_args,
labels,
extra_args,
use_buildx,
@@ -88,6 +86,8 @@ impl Resolve<build::Build> for State {
let image_name = get_image_name(&build, |_| aws_ecr)
.context("failed to make image name")?;
let build_args = parse_build_args(build_args);
let _secret_args =
parse_secret_args(secret_args, *skip_secret_interp)?;
let labels = parse_labels(labels);
let extra_args = parse_extra_args(extra_args);
let buildx = if *use_buildx { " buildx" } else { "" };
@@ -100,9 +100,9 @@ impl Resolve<build::Build> for State {
// Construct command
let command = format!(
"cd {} && docker{buildx} build{build_args}{extra_args}{labels}{image_tags} -f {dockerfile_path} .{push_command}",
build_dir.display()
);
"cd {} && docker{buildx} build{build_args}{_secret_args}{extra_args}{labels}{image_tags} -f {dockerfile_path} .{push_command}",
build_dir.display()
);
if *skip_secret_interp {
let build_log =
@@ -133,6 +133,8 @@ impl Resolve<build::Build> for State {
logs.push(build_log);
}
cleanup_secret_env_vars(secret_args);
Ok(logs)
}
}
@@ -152,6 +154,47 @@ fn parse_build_args(build_args: &[EnvironmentVar]) -> String {
.join("")
}
fn parse_secret_args(
secret_args: &[EnvironmentVar],
skip_secret_interp: bool,
) -> anyhow::Result<String> {
let periphery_config = periphery_config();
Ok(
secret_args
.iter()
.map(|EnvironmentVar { variable, value }| {
if variable.is_empty() {
return Err(anyhow!("secret variable cannot be empty string"))
} else if variable.contains('=') {
return Err(anyhow!("invalid variable {variable}. variable cannot contain '='"))
}
let value = if skip_secret_interp {
value.to_string()
} else {
svi::interpolate_variables(
value,
&periphery_config.secrets,
svi::Interpolator::DoubleBrackets,
true,
)
.context(
"failed to interpolate periphery secrets into build secrets",
)?.0
};
std::env::set_var(variable, value);
anyhow::Ok(format!(" --secret id={variable}"))
})
.collect::<anyhow::Result<Vec<_>>>()?
.join(""),
)
}
fn cleanup_secret_env_vars(secret_args: &[EnvironmentVar]) {
secret_args.iter().for_each(
|EnvironmentVar { variable, .. }| std::env::remove_var(variable),
)
}
//
impl Resolve<GetImageList> for State {
+8 -2
View File
@@ -143,7 +143,7 @@ pub struct BuildConfig {
pub extra_args: Vec<String>,
/// Docker build arguments.
///
///
/// These values are visible in the final image by running `docker inspect`.
#[serde(
default,
@@ -157,9 +157,15 @@ pub struct BuildConfig {
pub build_args: Vec<EnvironmentVar>,
/// Secret arguments.
///
///
/// These values remain hidden in the final image by using
/// docker secret mounts. See `<https://docs.docker.com/build/building/secrets>`.
///
/// To use the values, add commands like this in the Dockerfile:
/// ```
/// RUN --mount=type=secret,id=SECRET_KEY \
/// SECRET_VALUE=$(cat /run/secrets/SECRET_KEY) ...
/// ```
#[serde(
default,
deserialize_with = "super::env_vars_deserializer"