Skip to content

Commit 912ea8f

Browse files
committed
add test that Nexus quiesces when reading a blueprint saying so
1 parent b09b83f commit 912ea8f

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

nexus/tests/integration_tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ mod pantry;
3535
mod password_login;
3636
mod probe;
3737
mod projects;
38+
mod quiesce;
3839
mod quotas;
3940
mod rack;
4041
mod role_assignments;
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use anyhow::{Context, anyhow};
6+
use nexus_auth::context::OpContext;
7+
use nexus_client::types::QuiesceState;
8+
use nexus_reconfigurator_planning::blueprint_builder::BlueprintBuilder;
9+
use nexus_reconfigurator_planning::planner::PlannerRng;
10+
use nexus_reconfigurator_preparation::PlanningInputFromDb;
11+
use nexus_test_interface::NexusServer;
12+
use nexus_test_utils_macros::nexus_test;
13+
use nexus_types::deployment::BlueprintTargetSet;
14+
use nexus_types::deployment::PlannerChickenSwitches;
15+
use omicron_common::api::external::Error;
16+
use omicron_test_utils::dev::poll::CondCheckError;
17+
use omicron_test_utils::dev::poll::wait_for_condition;
18+
use omicron_uuid_kinds::GenericUuid;
19+
use std::time::Duration;
20+
21+
type ControlPlaneTestContext =
22+
nexus_test_utils::ControlPlaneTestContext<omicron_nexus::Server>;
23+
24+
/// Tests that Nexus quiesces when the blueprint says that it should
25+
#[nexus_test]
26+
async fn test_quiesce(cptestctx: &ControlPlaneTestContext) {
27+
let log = &cptestctx.logctx.log;
28+
let nexus = &cptestctx.server.server_context().nexus;
29+
let datastore = nexus.datastore();
30+
let opctx = OpContext::for_tests(log.clone(), datastore.clone());
31+
let nexus_internal_url = format!(
32+
"http://{}",
33+
cptestctx.server.get_http_server_internal_address().await
34+
);
35+
let nexus_client =
36+
nexus_client::Client::new(&nexus_internal_url, log.clone());
37+
38+
// Collect what we need to modify the blueprint.
39+
let collection = wait_for_condition(
40+
|| async {
41+
let collection = datastore
42+
.inventory_get_latest_collection(&opctx)
43+
.await
44+
.map_err(CondCheckError::Failed)?;
45+
match collection {
46+
Some(s) => Ok(s),
47+
None => Err(CondCheckError::<Error>::NotYet),
48+
}
49+
},
50+
&Duration::from_secs(1),
51+
&Duration::from_secs(60),
52+
)
53+
.await
54+
.expect("initial inventory collection");
55+
56+
let chicken_switches = datastore
57+
.reconfigurator_chicken_switches_get_latest(&opctx)
58+
.await
59+
.expect("obtained latest chicken switches")
60+
.map_or_else(PlannerChickenSwitches::default, |cs| {
61+
cs.switches.planner_switches
62+
});
63+
let planning_input = PlanningInputFromDb::assemble(
64+
&opctx,
65+
&datastore,
66+
chicken_switches,
67+
None,
68+
)
69+
.await
70+
.expect("planning input");
71+
let target_blueprint = nexus
72+
.blueprint_target_view(&opctx)
73+
.await
74+
.expect("fetch current target config");
75+
let blueprint1 = nexus
76+
.blueprint_view(&opctx, *target_blueprint.target_id.as_untyped_uuid())
77+
.await
78+
.expect("fetch current target blueprint");
79+
80+
// Now, update the target blueprint to reflect that Nexus should quiesce.
81+
// We don't need it to be enabled to still reflect quiescing.
82+
let mut builder = BlueprintBuilder::new_based_on(
83+
log,
84+
&blueprint1,
85+
&planning_input,
86+
&collection,
87+
"test-suite",
88+
PlannerRng::from_entropy(),
89+
)
90+
.expect("creating BlueprintBuilder");
91+
builder
92+
.set_nexus_generation(
93+
blueprint1.nexus_generation,
94+
blueprint1.nexus_generation.next(),
95+
)
96+
.expect("failed to set blueprint's Nexus generation");
97+
let blueprint2 = builder.build();
98+
nexus
99+
.blueprint_import(&opctx, blueprint2.clone())
100+
.await
101+
.expect("importing new blueprint");
102+
nexus
103+
.blueprint_target_set(
104+
&opctx,
105+
BlueprintTargetSet { enabled: false, target_id: blueprint2.id },
106+
)
107+
.await
108+
.expect("setting new target");
109+
110+
// Wait for Nexus to quiesce.
111+
let _ = wait_for_condition(
112+
|| async {
113+
let quiesce = nexus_client
114+
.quiesce_get()
115+
.await
116+
.context("fetching quiesce state")
117+
.map_err(CondCheckError::Failed)?
118+
.into_inner();
119+
eprintln!("quiesce state: {:#?}\n", quiesce);
120+
match quiesce.state {
121+
QuiesceState::Undetermined => {
122+
Err(CondCheckError::Failed(anyhow!(
123+
"quiesce state should have been determined before \
124+
test started"
125+
)))
126+
}
127+
QuiesceState::Running => Err(CondCheckError::NotYet),
128+
QuiesceState::DrainingSagas { .. }
129+
| QuiesceState::DrainingDb { .. }
130+
| QuiesceState::RecordingQuiesce { .. }
131+
| QuiesceState::Quiesced { .. } => Ok(()),
132+
}
133+
},
134+
&Duration::from_millis(50),
135+
&Duration::from_secs(30),
136+
)
137+
.await
138+
.expect("Nexus should have quiesced");
139+
}

0 commit comments

Comments
 (0)