|
|
@@ -23,6 +23,7 @@ pub struct ServeOpts {
|
|
|
pub auto_power_on: bool,
|
|
|
pub sync_image: bool,
|
|
|
pub sync_image_source: Option<String>,
|
|
|
+ pub build_node: Option<String>,
|
|
|
}
|
|
|
pub(crate) fn shq(s: &str) -> String {
|
|
|
let mut out = String::with_capacity(s.len() + 2);
|
|
|
@@ -80,6 +81,65 @@ pub async fn serve(
|
|
|
)
|
|
|
.await?;
|
|
|
use self::sync::sync_image_to_nodes;
|
|
|
+
|
|
|
+ // Build image on specified node if not found on any cluster node.
|
|
|
+ if let Some(ref build_node_name) = opts.build_node {
|
|
|
+ if !opts.dry_run && !cluster.nodes.iter().any(|n| n.name == *build_node_name) {
|
|
|
+ anyhow::bail!("build-node '{}' not in cluster", build_node_name);
|
|
|
+ }
|
|
|
+ if !opts.dry_run {
|
|
|
+ let build_dir = format!("docker-images/{}", recipe_name);
|
|
|
+ let local_dir = std::path::Path::new(&build_dir);
|
|
|
+ if !local_dir.join("build.sh").exists() {
|
|
|
+ anyhow::bail!(
|
|
|
+ "No build system at '{}' (missing build.sh)",
|
|
|
+ local_dir.display()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ let build_node = cluster
|
|
|
+ .nodes
|
|
|
+ .iter()
|
|
|
+ .find(|n| n.name == *build_node_name)
|
|
|
+ .expect("build-node already validated");
|
|
|
+
|
|
|
+ // Check image presence on the build node.
|
|
|
+ let check = format!(
|
|
|
+ "docker image ls --format '{{{{.ID}}}}' {} | head -1",
|
|
|
+ recipe.docker_image
|
|
|
+ );
|
|
|
+ let present = run_ssh(config, build_node, check, false).await?;
|
|
|
+ if present.trim().is_empty() {
|
|
|
+ info!(
|
|
|
+ "Building image '{}' on '{}'...",
|
|
|
+ recipe.docker_image, build_node.name
|
|
|
+ );
|
|
|
+ // Upload build directory.
|
|
|
+ crate::ssh::upload_dir(
|
|
|
+ config,
|
|
|
+ build_node,
|
|
|
+ local_dir,
|
|
|
+ &format!("/tmp/{}-build", recipe_name),
|
|
|
+ false,
|
|
|
+ )
|
|
|
+ .await?;
|
|
|
+ let build_cmd = format!(
|
|
|
+ "cd /tmp/{}-build && TAG={} bash build.sh 2>&1",
|
|
|
+ recipe_name, recipe.docker_image
|
|
|
+ );
|
|
|
+ crate::ssh::run_ssh_long_running(config, build_node, build_cmd, false).await?;
|
|
|
+ info!(
|
|
|
+ "Image '{}' built on '{}'",
|
|
|
+ recipe.docker_image, build_node.name
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ info!(
|
|
|
+ "Image '{}' already present on '{}'",
|
|
|
+ recipe.docker_image, build_node.name
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
let mods_remote_path = mods::upload_mods_to_cluster(
|
|
|
config,
|
|
|
&cluster,
|