@@ -33,6 +33,7 @@ use nexus_db_errors::public_error_from_diesel;
3333use nexus_db_lookup:: DbConnection ;
3434use nexus_db_schema:: schema:: fm_alert_request:: dsl as alert_req_dsl;
3535use nexus_db_schema:: schema:: fm_case:: dsl as case_dsl;
36+ use nexus_db_schema:: schema:: fm_case_impacts_sp_slot:: dsl as impacted_sp_dsl;
3637use nexus_db_schema:: schema:: fm_ereport_in_case:: dsl as case_ereport_dsl;
3738use nexus_db_schema:: schema:: fm_sitrep:: dsl as sitrep_dsl;
3839use nexus_db_schema:: schema:: fm_sitrep_history:: dsl as history_dsl;
@@ -130,7 +131,8 @@ impl DataStore {
130131 Ok ( Some ( ( version, sitrep) ) )
131132 }
132133
133- /// Reads the entire content of the sitrep with the provided ID, if one exists.
134+ /// Reads the entire content of the sitrep with the provided ID, if one
135+ /// exists.
134136 pub async fn fm_sitrep_read (
135137 & self ,
136138 opctx : & OpContext ,
@@ -356,27 +358,105 @@ impl DataStore {
356358 pub async fn fm_sitrep_insert (
357359 & self ,
358360 opctx : & OpContext ,
359- sitrep : & Sitrep ,
361+ sitrep : Sitrep ,
360362 ) -> Result < ( ) , InsertSitrepError > {
361363 let conn = self . pool_connection_authorized ( opctx) . await ?;
362364
363365 // TODO(eliza): there should probably be an authz object for the fm sitrep?
364366 opctx. authorize ( authz:: Action :: Modify , & authz:: FLEET ) . await ?;
365367
368+ let sitrep_id = sitrep. id ( ) ;
369+
366370 // Create the sitrep metadata record.
371+ //
372+ // NOTE: we must insert this record before anything else, because it's
373+ // how orphaned sitreps are found when performing garbage collection.
374+ // Were we to first insert some other records and insert the metadata
375+ // record *last*, we could die when we have inserted some sitrep data
376+ // but have yet to create the metadata record. If this occurs, those
377+ // records could not be easily found by the garbage collection task.
378+ // Those (unused) records would then be permanently leaked without
379+ // manual human intervention to delete them.
367380 diesel:: insert_into ( sitrep_dsl:: fm_sitrep)
368- . values ( model:: SitrepMetadata :: from ( sitrep. metadata . clone ( ) ) )
381+ . values ( model:: SitrepMetadata :: from ( sitrep. metadata ) )
369382 . execute_async ( & * conn)
370383 . await
371384 . map_err ( |e| {
372385 public_error_from_diesel ( e, ErrorHandler :: Server )
373386 . internal_context ( "failed to insert sitrep metadata record" )
374387 } ) ?;
375388
376- // TODO(eliza): other sitrep records would be inserted here...
389+ // Create case records.
390+ let mut cases = Vec :: with_capacity ( sitrep. cases . len ( ) ) ;
391+ for case in sitrep. cases {
392+ // TODO(eliza): some of this could be done in parallel using a
393+ // `ParallelTaskSet`, if the time it takes to insert a sitrep were
394+ // to become important?
395+ let model:: fm:: Case {
396+ metadata,
397+ ereports,
398+ alerts_requested,
399+ impacted_sp_slots,
400+ } = model:: fm:: Case :: from_sitrep ( sitrep_id, case) ;
401+
402+ if !ereports. is_empty ( ) {
403+ diesel:: insert_into ( case_ereport_dsl:: fm_ereport_in_case)
404+ . values ( ereports)
405+ . execute_async ( & * conn)
406+ . await
407+ . map_err ( |e| {
408+ public_error_from_diesel ( e, ErrorHandler :: Server )
409+ . internal_context ( format ! (
410+ "failed to insert ereport records for case {}" ,
411+ metadata. id
412+ ) )
413+ } ) ?;
414+ }
415+
416+ if !alerts_requested. is_empty ( ) {
417+ diesel:: insert_into ( alert_req_dsl:: fm_alert_request)
418+ . values ( alerts_requested)
419+ . execute_async ( & * conn)
420+ . await
421+ . map_err ( |e| {
422+ public_error_from_diesel ( e, ErrorHandler :: Server )
423+ . internal_context ( format ! (
424+ "failed to insert ereport alert requests for case {}" ,
425+ metadata. id
426+ ) )
427+ } ) ?;
428+ }
429+
430+ if !impacted_sp_slots. is_empty ( ) {
431+ diesel:: insert_into ( impacted_sp_dsl:: fm_case_impacts_sp_slot)
432+ . values ( impacted_sp_slots)
433+ . execute_async ( & * conn)
434+ . await
435+ . map_err ( |e| {
436+ public_error_from_diesel ( e, ErrorHandler :: Server )
437+ . internal_context ( format ! (
438+ "failed to insert impacted SP slots for case {}" ,
439+ metadata. id
440+ ) )
441+ } ) ?;
442+ }
443+
444+ cases. push ( metadata) ;
445+ }
446+
447+ if !cases. is_empty ( ) {
448+ diesel:: insert_into ( case_dsl:: fm_case)
449+ . values ( cases)
450+ . execute_async ( & * conn)
451+ . await
452+ . map_err ( |e| {
453+ public_error_from_diesel ( e, ErrorHandler :: Server )
454+ . internal_context ( "failed to insert case records" )
455+ } ) ?;
456+ }
377457
378458 // Now, try to make the sitrep current.
379- let query = Self :: insert_sitrep_version_query ( sitrep . id ( ) ) ;
459+ let query = Self :: insert_sitrep_version_query ( sitrep_id ) ;
380460 query
381461 . execute_async ( & * conn)
382462 . await
@@ -387,7 +467,7 @@ impl DataStore {
387467 ) if info. message ( )
388468 == Self :: PARENT_NOT_CURRENT_ERROR_MESSAGE =>
389469 {
390- InsertSitrepError :: ParentNotCurrent ( sitrep . id ( ) )
470+ InsertSitrepError :: ParentNotCurrent ( sitrep_id )
391471 }
392472 err => {
393473 let err =
@@ -943,7 +1023,7 @@ mod tests {
9431023 cases : Default :: default ( ) ,
9441024 } ;
9451025
946- datastore. fm_sitrep_insert ( & opctx, & sitrep) . await . unwrap ( ) ;
1026+ datastore. fm_sitrep_insert ( & opctx, sitrep. clone ( ) ) . await . unwrap ( ) ;
9471027
9481028 let current = datastore
9491029 . fm_sitrep_read_current ( & opctx)
@@ -962,7 +1042,7 @@ mod tests {
9621042
9631043 // Trying to insert the same sitrep again should fail.
9641044 let err =
965- datastore. fm_sitrep_insert ( & opctx, & sitrep) . await . unwrap_err ( ) ;
1045+ datastore. fm_sitrep_insert ( & opctx, sitrep. clone ( ) ) . await . unwrap_err ( ) ;
9661046 assert ! ( err. to_string( ) . contains( "duplicate key" ) ) ;
9671047
9681048 // Clean up.
@@ -989,7 +1069,7 @@ mod tests {
9891069 } ,
9901070 cases : Default :: default ( ) ,
9911071 } ;
992- datastore. fm_sitrep_insert ( & opctx, & sitrep1) . await . unwrap ( ) ;
1072+ datastore. fm_sitrep_insert ( & opctx, sitrep1. clone ( ) ) . await . unwrap ( ) ;
9931073
9941074 // Create a second sitrep with the first as parent
9951075 let sitrep2 = nexus_types:: fm:: Sitrep {
@@ -1003,7 +1083,7 @@ mod tests {
10031083 } ,
10041084 cases : Default :: default ( ) ,
10051085 } ;
1006- datastore. fm_sitrep_insert ( & opctx, & sitrep2) . await . expect (
1086+ datastore. fm_sitrep_insert ( & opctx, sitrep2. clone ( ) ) . await . expect (
10071087 "inserting a sitrep whose parent is current should succeed" ,
10081088 ) ;
10091089
@@ -1044,7 +1124,7 @@ mod tests {
10441124 } ,
10451125 cases : Default :: default ( ) ,
10461126 } ;
1047- datastore. fm_sitrep_insert ( & opctx, & sitrep1) . await . unwrap ( ) ;
1127+ datastore. fm_sitrep_insert ( & opctx, sitrep1. clone ( ) ) . await . unwrap ( ) ;
10481128
10491129 // Try to insert a sitrep with a non-existent parent ID
10501130 let nonexistent_id = SitrepUuid :: new_v4 ( ) ;
@@ -1060,7 +1140,7 @@ mod tests {
10601140 cases : Default :: default ( ) ,
10611141 } ;
10621142
1063- let result = datastore. fm_sitrep_insert ( & opctx, & sitrep2) . await ;
1143+ let result = datastore. fm_sitrep_insert ( & opctx, sitrep2) . await ;
10641144
10651145 // Should fail with ParentNotCurrent error
10661146 match result {
@@ -1094,7 +1174,7 @@ mod tests {
10941174 } ,
10951175 cases : Default :: default ( ) ,
10961176 } ;
1097- datastore. fm_sitrep_insert ( & opctx, & sitrep1) . await . unwrap ( ) ;
1177+ datastore. fm_sitrep_insert ( & opctx, sitrep1. clone ( ) ) . await . unwrap ( ) ;
10981178
10991179 // Create a second sitrep with the first as parent
11001180 let sitrep2 = nexus_types:: fm:: Sitrep {
@@ -1108,7 +1188,7 @@ mod tests {
11081188 } ,
11091189 cases : Default :: default ( ) ,
11101190 } ;
1111- datastore. fm_sitrep_insert ( & opctx, & sitrep2) . await . unwrap ( ) ;
1191+ datastore. fm_sitrep_insert ( & opctx, sitrep2. clone ( ) ) . await . unwrap ( ) ;
11121192
11131193 // Try to create a third sitrep with sitrep1 (outdated) as parent.
11141194 // This should fail, as sitrep2 is now the current sitrep.
@@ -1123,7 +1203,7 @@ mod tests {
11231203 } ,
11241204 cases : Default :: default ( ) ,
11251205 } ;
1126- let result = datastore. fm_sitrep_insert ( & opctx, & sitrep3) . await ;
1206+ let result = datastore. fm_sitrep_insert ( & opctx, sitrep3. clone ( ) ) . await ;
11271207
11281208 // Should fail with ParentNotCurrent error
11291209 match result {
@@ -1165,7 +1245,7 @@ mod tests {
11651245 cases : Default :: default ( ) ,
11661246 } ;
11671247 datastore
1168- . fm_sitrep_insert ( & opctx, & sitrep1)
1248+ . fm_sitrep_insert ( & opctx, sitrep1. clone ( ) )
11691249 . await
11701250 . expect ( "inserting initial sitrep should succeed" ) ;
11711251
@@ -1206,7 +1286,7 @@ mod tests {
12061286 cases : Default :: default ( ) ,
12071287 } ;
12081288 datastore
1209- . fm_sitrep_insert ( & opctx, & sitrep2)
1289+ . fm_sitrep_insert ( & opctx, sitrep2. clone ( ) )
12101290 . await
12111291 . expect ( "inserting child sitrep should succeed" ) ;
12121292
@@ -1269,7 +1349,7 @@ mod tests {
12691349 } ,
12701350 cases : Default :: default ( ) ,
12711351 } ;
1272- match datastore. fm_sitrep_insert ( & opctx, & sitrep) . await {
1352+ match datastore. fm_sitrep_insert ( & opctx, sitrep) . await {
12731353 Ok ( _) => {
12741354 panic ! ( "inserting sitrep v{v} orphan {i} should not succeed" )
12751355 }
0 commit comments