@@ -9,10 +9,13 @@ use common::LiveTestContext;
9
9
use common:: reconfigurator:: blueprint_edit_current_target;
10
10
use futures:: TryStreamExt ;
11
11
use live_tests_macros:: live_test;
12
+ use nexus_client:: types:: BackgroundTasksActivateRequest ;
13
+ use nexus_client:: types:: BlueprintTargetSet ;
12
14
use nexus_client:: types:: Saga ;
13
15
use nexus_client:: types:: SagaState ;
14
16
use nexus_inventory:: CollectionBuilder ;
15
17
use nexus_reconfigurator_planning:: blueprint_builder:: BlueprintBuilder ;
18
+ use nexus_reconfigurator_planning:: planner:: Planner ;
16
19
use nexus_reconfigurator_preparation:: PlanningInputFromDb ;
17
20
use nexus_sled_agent_shared:: inventory:: ZoneKind ;
18
21
use nexus_types:: deployment:: SledFilter ;
@@ -156,7 +159,94 @@ async fn test_nexus_add_remove(lc: &LiveTestContext) {
156
159
. await
157
160
. unwrap ( ) ;
158
161
159
- // Wait for some other Nexus instance to pick up the saga.
162
+ // We want to see another Nexus instance pick up the saga.
163
+ //
164
+ // For that to happen, inventory must first reflect that the Nexus we
165
+ // expunged is really gone. Then we must run through another planning
166
+ // round.
167
+ //
168
+ // First, kick one Nexus instance's inventory collector. Otherwise, it
169
+ // might take a while for the system to notice this zone is gone. Having
170
+ // activated the task, it shouldn't take too long to get an inventory
171
+ info ! ( log, "activating inventory collector" ) ;
172
+ nexus
173
+ . bgtask_activate ( & BackgroundTasksActivateRequest {
174
+ bgtask_names : vec ! [ String :: from( "inventory_collection" ) ] ,
175
+ } )
176
+ . await
177
+ . expect ( "activating inventory background task" ) ;
178
+ let latest_collection = wait_for_condition (
179
+ || async {
180
+ let latest_collection = datastore
181
+ . inventory_get_latest_collection ( opctx)
182
+ . await
183
+ . expect ( "latest inventory collection" )
184
+ . expect ( "have a latest inventory collection" ) ;
185
+ debug ! ( log, "got inventory" ; "id" => %latest_collection. id) ;
186
+ let agent = latest_collection. sled_agents . get ( & sled_id) . expect (
187
+ "collection information for the sled we added a Nexus to" ,
188
+ ) ;
189
+ if agent. omicron_zones . zones . iter ( ) . any ( |z| z. id == new_zone. id ) {
190
+ debug ! ( log, "zone still present in inventory" ) ;
191
+ return Err ( CondCheckError :: < ( ) > :: NotYet ) ;
192
+ }
193
+ return Ok ( latest_collection) ;
194
+ } ,
195
+ & Duration :: from_millis ( 3000 ) ,
196
+ & Duration :: from_secs ( 90 ) ,
197
+ )
198
+ . await
199
+ . expect ( "waiting for zone to be gone from inventory" ) ;
200
+
201
+ // Now run through the planner.
202
+ info ! ( log, "running through planner" ) ;
203
+ let planning_input = PlanningInputFromDb :: assemble ( & opctx, & datastore)
204
+ . await
205
+ . expect ( "planning input" ) ;
206
+ let ( _, parent_blueprint) = datastore
207
+ . blueprint_target_get_current_full ( opctx)
208
+ . await
209
+ . expect ( "getting latest target blueprint" ) ;
210
+ let planner = Planner :: new_based_on (
211
+ log. clone ( ) ,
212
+ & parent_blueprint,
213
+ & planning_input,
214
+ "live test suite" ,
215
+ & latest_collection,
216
+ )
217
+ . expect ( "constructing planner" ) ;
218
+ let new_blueprint = planner. plan ( ) . expect ( "creating blueprint" ) ;
219
+
220
+ // The new blueprint ought to have our zone expunged and ready for cleanup.
221
+ // We don't need to check this here. It just provides a better error
222
+ // message if something has gone wrong up to this point.
223
+ let ( _, expunged_zone_config) = new_blueprint
224
+ . all_omicron_zones ( |_| true )
225
+ . find ( |( _sled_id, zone_config) | zone_config. id == new_zone. id )
226
+ . expect ( "expunged zone in new blueprint" ) ;
227
+ assert ! ( expunged_zone_config. disposition. is_ready_for_cleanup( ) ) ;
228
+
229
+ // Now make this the current target.
230
+ info ! (
231
+ log,
232
+ "setting new blueprint target" ;
233
+ "blueprint_id" => ?new_blueprint. id
234
+ ) ;
235
+ nexus
236
+ . blueprint_import ( & new_blueprint)
237
+ . await
238
+ . expect ( "importing new blueprint" ) ;
239
+ nexus
240
+ . blueprint_target_set ( & BlueprintTargetSet {
241
+ enabled : true ,
242
+ target_id : new_blueprint. id ,
243
+ } )
244
+ . await
245
+ . expect ( "setting target blueprint" ) ;
246
+
247
+ // At this point, blueprint execution should re-assign the saga.
248
+ // Wait for that to happen and then for another Nexus instance to pick up
249
+ // the saga.
160
250
let nexus_found = wait_for_condition (
161
251
|| async {
162
252
for nexus_client in & initial_nexus_clients {
@@ -173,8 +263,8 @@ async fn test_nexus_add_remove(lc: &LiveTestContext) {
173
263
174
264
return Err ( CondCheckError :: < ( ) > :: NotYet ) ;
175
265
} ,
176
- & Duration :: from_millis ( 50 ) ,
177
- & Duration :: from_secs ( 60 ) ,
266
+ & Duration :: from_millis ( 1000 ) ,
267
+ & Duration :: from_secs ( 120 ) ,
178
268
)
179
269
. await
180
270
. unwrap ( ) ;
0 commit comments