Skip to content

Commit 7f1e07f

Browse files
authored
Send blueprint disks & datasets to simulated sled agent (#8358)
Fixes #7081, fixes #8222. Tracks disks and datasets added to the blueprint in `nexus-test-utils`, and sends them to the appropriate simulated sled-agents. Also adds trivial but successful execution to the `test_blueprint_planner` introduced in #8287.
1 parent aec3cd8 commit 7f1e07f

File tree

2 files changed

+235
-152
lines changed

2 files changed

+235
-152
lines changed

nexus/src/app/background/tasks/blueprint_planner.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,14 @@ impl BackgroundTask for BlueprintPlanner {
257257
#[cfg(test)]
258258
mod test {
259259
use super::*;
260+
use crate::app::background::Activator;
261+
use crate::app::background::tasks::blueprint_execution::BlueprintExecutor;
260262
use crate::app::background::tasks::blueprint_load::TargetBlueprintLoader;
261263
use crate::app::background::tasks::inventory_collection::InventoryCollector;
262264
use nexus_inventory::now_db_precision;
263265
use nexus_test_utils_macros::nexus_test;
266+
use nexus_types::deployment::PendingMgsUpdates;
267+
use omicron_uuid_kinds::OmicronZoneUuid;
264268

265269
type ControlPlaneTestContext =
266270
nexus_test_utils::ControlPlaneTestContext<crate::Server>;
@@ -289,7 +293,7 @@ mod test {
289293
cptestctx.logctx.log.clone(),
290294
&[cptestctx.internal_dns.dns_server.local_address()],
291295
)
292-
.unwrap();
296+
.expect("can't start resolver");
293297
let mut collector = InventoryCollector::new(
294298
datastore.clone(),
295299
resolver.clone(),
@@ -322,7 +326,7 @@ mod test {
322326
let status = serde_json::from_value::<BlueprintPlannerStatus>(
323327
planner.activate(&opctx).await,
324328
)
325-
.unwrap();
329+
.expect("can't activate planner");
326330
let blueprint_id = match status {
327331
BlueprintPlannerStatus::Targeted {
328332
parent_blueprint_id,
@@ -347,11 +351,73 @@ mod test {
347351
blueprint.diff_since_blueprint(initial_blueprint).has_changes()
348352
);
349353

350-
// Planning again should not change the plan.
354+
// Planning again should not change the plan, because nothing has changed.
351355
let status = serde_json::from_value::<BlueprintPlannerStatus>(
352356
planner.activate(&opctx).await,
353357
)
354-
.unwrap();
358+
.expect("can't re-activate planner");
359+
assert_eq!(
360+
status,
361+
BlueprintPlannerStatus::Unchanged {
362+
parent_blueprint_id: blueprint_id,
363+
}
364+
);
365+
366+
// Enable execution.
367+
let mut target = *target;
368+
target.enabled = true;
369+
datastore
370+
.blueprint_target_set_current_enabled(&opctx, target)
371+
.await
372+
.expect("can't enable execution");
373+
374+
// Ping the loader again so it gets the updated target.
375+
loader.activate(&opctx).await;
376+
let (target, blueprint) = &*rx_loader
377+
.borrow_and_update()
378+
.clone()
379+
.expect("failed to re-load blueprint");
380+
assert_eq!(target.target_id, blueprint.id);
381+
assert_eq!(target.target_id, blueprint_id);
382+
assert!(
383+
blueprint.diff_since_blueprint(initial_blueprint).has_changes()
384+
);
385+
386+
// Trigger an inventory collection.
387+
collector.activate(&opctx).await;
388+
389+
// Execute the plan.
390+
let (dummy_tx, _dummy_rx) = watch::channel(PendingMgsUpdates::new());
391+
let mut executor = BlueprintExecutor::new(
392+
datastore.clone(),
393+
resolver.clone(),
394+
rx_loader.clone(),
395+
OmicronZoneUuid::new_v4(),
396+
Activator::new(),
397+
dummy_tx,
398+
);
399+
let value = executor.activate(&opctx).await;
400+
let value = value.as_object().expect("response is not a JSON object");
401+
assert_eq!(value["target_id"], blueprint.id.to_string());
402+
assert!(value["enabled"].as_bool().expect("enabled should be boolean"));
403+
assert!(value["execution_error"].is_null());
404+
assert!(
405+
!value["event_report"]
406+
.as_object()
407+
.expect("event report should be an object")["Ok"]
408+
.as_object()
409+
.expect("event report is not Ok")["step_events"]
410+
.as_array()
411+
.expect("steps should be an array")
412+
.is_empty()
413+
);
414+
415+
// Planning again should not change the plan, because the execution
416+
// is all fake.
417+
let status = serde_json::from_value::<BlueprintPlannerStatus>(
418+
planner.activate(&opctx).await,
419+
)
420+
.expect("can't re-activate planner");
355421
assert_eq!(
356422
status,
357423
BlueprintPlannerStatus::Unchanged {

0 commit comments

Comments
 (0)