|
@@ -71,8 +71,6 @@ pub async fn serve(
|
|
|
let mut recipe = load_recipe(recipe_name, &config.recipe_sources).await?;
|
|
let mut recipe = load_recipe(recipe_name, &config.recipe_sources).await?;
|
|
|
let rn = recipe.name.clone();
|
|
let rn = recipe.name.clone();
|
|
|
let re = recipe.engine.clone();
|
|
let re = recipe.engine.clone();
|
|
|
- // CLI `--cluster auto` (the default) falls back to the recipe's `cluster`
|
|
|
|
|
- // field — same behavior as the tmux path.
|
|
|
|
|
let cluster_name = if cluster_name == "auto" {
|
|
let cluster_name = if cluster_name == "auto" {
|
|
|
recipe.cluster.clone().unwrap_or_else(|| "auto".to_string())
|
|
recipe.cluster.clone().unwrap_or_else(|| "auto".to_string())
|
|
|
} else {
|
|
} else {
|
|
@@ -185,6 +183,15 @@ pub async fn serve(
|
|
|
cluster.name, nn, nn
|
|
cluster.name, nn, nn
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
+ // Trim to the launch set (the `required_nodes` cap) BEFORE the pre-serve
|
|
|
|
|
+ // hooks and image sync so every phase targets exactly the nodes the recipe
|
|
|
|
|
+ // launches on, not every free node in the cluster. `start_cluster`'s trim
|
|
|
|
|
+ // becomes a no-op afterwards.
|
|
|
|
|
+ if let Some(n) = recipe.required_nodes
|
|
|
|
|
+ && (n as usize) < cluster.nodes.len()
|
|
|
|
|
+ {
|
|
|
|
|
+ cluster.nodes.truncate(n as usize);
|
|
|
|
|
+ }
|
|
|
crate::before_serve::run(
|
|
crate::before_serve::run(
|
|
|
config,
|
|
config,
|
|
|
&cluster,
|
|
&cluster,
|
|
@@ -194,10 +201,9 @@ pub async fn serve(
|
|
|
)
|
|
)
|
|
|
.await?;
|
|
.await?;
|
|
|
|
|
|
|
|
- // `built_on` records the node that (re)built the image this serve: after
|
|
|
|
|
- // a fresh build that image is authoritative — the sync step below must
|
|
|
|
|
- // pull FROM it, never let a peer's older image (by creation timestamp)
|
|
|
|
|
- // overwrite the new build.
|
|
|
|
|
|
|
+ // `built_on` records the node that (re)built the image: the sync step
|
|
|
|
|
+ // below must pull FROM it, never let a peer's older image overwrite the
|
|
|
|
|
+ // new build.
|
|
|
let mut built_on: Option<String> = None;
|
|
let mut built_on: Option<String> = None;
|
|
|
if let Some(ref build_node_name) = opts.build_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) {
|
|
if !opts.dry_run && !cluster.nodes.iter().any(|n| n.name == *build_node_name) {
|