@@ -204,7 +204,7 @@ func (r *BMCVersionSetReconciler) reconcile(
204204 return ctrl.Result {}, fmt .Errorf ("failed to delete orphaned BMCVersion resources %w" , err )
205205 }
206206
207- if err := r .patchBMCVersionfromTemplate (ctx , log , & bmcVersionSet . Spec . BMCVersionTemplate , ownedBMCVersions ); err != nil {
207+ if err := r .patchBMCVersionfromTemplate (ctx , log , bmcList , bmcVersionSet , ownedBMCVersions ); err != nil {
208208 return ctrl.Result {}, fmt .Errorf ("failed to patch BMCVersion spec from template %w" , err )
209209 }
210210
@@ -240,9 +240,16 @@ func (r *BMCVersionSetReconciler) createMissingBMCVersions(
240240 ObjectMeta : metav1.ObjectMeta {
241241 GenerateName : "bmc-version-set-" ,
242242 }}
243-
243+ serverMaintenanceRefsProvided , err := r .getProvidedServerMaintenanceRefs (ctx , & bmc , bmcVersionSet )
244+ if err != nil {
245+ errs = append (errs , fmt .Errorf ("failed to get provided ServerMaintenanceRefs for BMC %s: %w" , bmc .Name , err ))
246+ continue
247+ }
244248 opResult , err := controllerutil .CreateOrPatch (ctx , r .Client , newBMCVersion , func () error {
245249 newBMCVersion .Spec .BMCVersionTemplate = * bmcVersionSet .Spec .BMCVersionTemplate .DeepCopy ()
250+ // patch ServerMaintenance referenced by BMC's Servers
251+ newBMCVersion .Spec .ServerMaintenanceRefs = serverMaintenanceRefsProvided
252+
246253 newBMCVersion .Spec .BMCRef = & corev1.LocalObjectReference {Name : bmc .Name }
247254 return controllerutil .SetControllerReference (bmcVersionSet , newBMCVersion , r .Client .Scheme ())
248255 })
@@ -255,6 +262,52 @@ func (r *BMCVersionSetReconciler) createMissingBMCVersions(
255262 return errors .Join (errs ... )
256263}
257264
265+ func (r * BMCVersionSetReconciler ) getProvidedServerMaintenanceRefs (
266+ ctx context.Context ,
267+ bmc * metalv1alpha1.BMC ,
268+ bmcVersionSet * metalv1alpha1.BMCVersionSet ,
269+ ) ([]metalv1alpha1.ServerMaintenanceRefItem , error ) {
270+ serverMaintenanceRefMap := make (map [string ]metalv1alpha1.ServerMaintenanceRefItem )
271+ for _ , ref := range bmcVersionSet .Spec .BMCVersionTemplate .ServerMaintenanceRefs {
272+ if ref .ServerMaintenanceRef != nil {
273+ serverMaintenanceRefMap [ref .ServerMaintenanceRef .Name ] = ref
274+ }
275+ }
276+ // get servers for this BMC
277+ serverList , err := r .getBMCOwnedServers (ctx , bmc )
278+ if err != nil {
279+ return nil , fmt .Errorf ("failed to get servers owned by BMC %s: %w" , bmc .Name , err )
280+ }
281+ // get map of servers owned by this BMC
282+ serverMap := make (map [string ]struct {})
283+ for _ , server := range serverList .Items {
284+ serverMap [server .Name ] = struct {}{}
285+ }
286+ // filter the serverMaintenances which are for servers owned by this BMC
287+ serverMaintenancesList := & metalv1alpha1.ServerMaintenanceList {}
288+ err = clientutils .ListAndFilter (ctx , r .Client , serverMaintenancesList , func (object client.Object ) (bool , error ) {
289+ serverMaintenance := object .(* metalv1alpha1.ServerMaintenance )
290+ if serverMaintenance .Spec .ServerRef == nil {
291+ return false , nil
292+ }
293+ if _ , exists := serverMap [serverMaintenance .Spec .ServerRef .Name ]; ! exists {
294+ return false , nil
295+ }
296+ return true , nil
297+ })
298+ if err != nil {
299+ return nil , fmt .Errorf ("failed to list ServerMaintenance for BMC's %s: Servers. Error %w" , bmc .Name , err )
300+ }
301+
302+ serverMaintenanceRefs := []metalv1alpha1.ServerMaintenanceRefItem {}
303+ for _ , serverMaintenance := range serverMaintenancesList .Items {
304+ if ref , exists := serverMaintenanceRefMap [serverMaintenance .Name ]; exists {
305+ serverMaintenanceRefs = append (serverMaintenanceRefs , ref )
306+ }
307+ }
308+ return serverMaintenanceRefs , nil
309+ }
310+
258311func (r * BMCVersionSetReconciler ) deleteOrphanBMCVersions (
259312 ctx context.Context ,
260313 log logr.Logger ,
@@ -288,21 +341,80 @@ func (r *BMCVersionSetReconciler) deleteOrphanBMCVersions(
288341func (r * BMCVersionSetReconciler ) patchBMCVersionfromTemplate (
289342 ctx context.Context ,
290343 log logr.Logger ,
291- bmcVersionTemplate * metalv1alpha1.BMCVersionTemplate ,
344+ bmcList * metalv1alpha1.BMCList ,
345+ bmcVersionSet * metalv1alpha1.BMCVersionSet ,
292346 bmcVersionList * metalv1alpha1.BMCVersionList ,
293347) error {
294348 if len (bmcVersionList .Items ) == 0 {
295349 log .V (1 ).Info ("No BMCVersion found, skipping spec template update" )
296350 return nil
297351 }
352+ bmcNameMap := make (map [string ]metalv1alpha1.BMC )
353+ for _ , bmc := range bmcList .Items {
354+ bmcNameMap [bmc .Name ] = bmc
355+ }
298356
299357 var errs []error
300358 for _ , bmcVersion := range bmcVersionList .Items {
301- if bmcVersion .Status .State == metalv1alpha1 .BMCVersionStateInProgress {
359+ if bmcVersion .Status .State == metalv1alpha1 .BMCVersionStateInProgress && bmcVersion .Status .UpgradeTask != nil {
360+ log .V (1 ).Info ("Skipping BMCVersion spec patching as it is in InProgress state with an active UpgradeTask" )
361+ continue
362+ }
363+ bmc , exists := bmcNameMap [bmcVersion .Spec .BMCRef .Name ]
364+ if ! exists {
365+ errs = append (errs , fmt .Errorf ("BMC %s not found for BMCVersion %s" , bmcVersion .Spec .BMCRef .Name , bmcVersion .Name ))
366+ continue
367+ }
368+ serverMaintenanceRefsProvided , err := r .getProvidedServerMaintenanceRefs (ctx , & bmc , bmcVersionSet )
369+ if err != nil {
370+ errs = append (errs , fmt .Errorf ("failed to get ServerMaintenanceRefs for BMC %s: %w" , bmc .Name , err ))
371+ continue
372+ }
373+
374+ serverMaintenancesRefsCreated , err := r .getCreatedServerMaintenanceRefs (ctx , & bmcVersion )
375+ if err != nil {
376+ errs = append (errs , fmt .Errorf ("failed to get owned ServerMaintenance for BMCVersion %s: %w" , bmcVersion .Name , err ))
302377 continue
303378 }
379+
380+ var serverMaintenanceRefsMerged []metalv1alpha1.ServerMaintenanceRefItem
381+ if len (serverMaintenancesRefsCreated ) > 0 && len (serverMaintenanceRefsProvided ) > 0 {
382+ // merge provided and created serverMaintenanceRefs
383+ serverMaintenanceRefMap := make (map [string ]metalv1alpha1.ServerMaintenanceRefItem )
384+ for _ , ref := range serverMaintenanceRefsProvided {
385+ if ref .ServerMaintenanceRef != nil {
386+ serverMaintenanceRefMap [ref .ServerMaintenanceRef .Name ] = ref
387+ }
388+ }
389+ for _ , ref := range serverMaintenancesRefsCreated {
390+ if ref .ServerMaintenanceRef != nil {
391+ serverMaintenanceRefMap [ref .ServerMaintenanceRef .Name ] = ref
392+ }
393+ }
394+ for _ , ref := range serverMaintenanceRefMap {
395+ serverMaintenanceRefsMerged = append (serverMaintenanceRefsMerged , ref )
396+ }
397+
398+ // check if the lenght is as expected
399+ server , err := r .getBMCOwnedServers (ctx , & bmc )
400+ if err != nil {
401+ errs = append (errs , fmt .Errorf ("failed to get servers owned by BMC %s to verify serverMaintenanceRef: %w" , bmc .Name , err ))
402+ continue
403+ }
404+ if len (serverMaintenanceRefsMerged ) > len (server .Items ) {
405+ errs = append (errs , fmt .Errorf ("number of ServerMaintenanceRefs %d exceeds number of Servers %d for BMC %s" ,
406+ len (serverMaintenanceRefsMerged ), len (server .Items ), bmc .Name ))
407+ continue
408+ }
409+ } else if len (serverMaintenanceRefsProvided ) > 0 {
410+ serverMaintenanceRefsMerged = serverMaintenanceRefsProvided
411+ } else {
412+ serverMaintenanceRefsMerged = serverMaintenancesRefsCreated
413+ }
414+
304415 opResult , err := controllerutil .CreateOrPatch (ctx , r .Client , & bmcVersion , func () error {
305- bmcVersion .Spec .BMCVersionTemplate = * bmcVersionTemplate .DeepCopy ()
416+ bmcVersion .Spec .BMCVersionTemplate = * bmcVersionSet .Spec .BMCVersionTemplate .DeepCopy ()
417+ bmcVersion .Spec .BMCVersionTemplate .ServerMaintenanceRefs = serverMaintenanceRefsMerged
306418 return nil
307419 }) //nolint:errcheck
308420 if err != nil {
@@ -346,6 +458,44 @@ func (r *BMCVersionSetReconciler) getOwnedBMCVersions(
346458 return bmcVersionList , nil
347459}
348460
461+ func (r * BMCVersionSetReconciler ) getBMCOwnedServers (
462+ ctx context.Context ,
463+ bmc * metalv1alpha1.BMC ,
464+ ) (* metalv1alpha1.ServerList , error ) {
465+ serverList := & metalv1alpha1.ServerList {}
466+ if err := clientutils .ListAndFilterControlledBy (ctx , r .Client , bmc , serverList ); err != nil {
467+ return nil , err
468+ }
469+ return serverList , nil
470+ }
471+
472+ func (r * BMCVersionSetReconciler ) getCreatedServerMaintenanceRefs (
473+ ctx context.Context ,
474+ bmcVersion * metalv1alpha1.BMCVersion ,
475+ ) ([]metalv1alpha1.ServerMaintenanceRefItem , error ) {
476+ serverMaintenanceRefMap := make (map [string ]metalv1alpha1.ServerMaintenanceRefItem )
477+ for _ , ref := range bmcVersion .Spec .BMCVersionTemplate .ServerMaintenanceRefs {
478+ if ref .ServerMaintenanceRef != nil {
479+ serverMaintenanceRefMap [ref .ServerMaintenanceRef .Name ] = ref
480+ }
481+ }
482+
483+ serverMaintenanceList := & metalv1alpha1.ServerMaintenanceList {}
484+ if err := clientutils .ListAndFilterControlledBy (ctx , r .Client , bmcVersion , serverMaintenanceList ); err != nil {
485+ return nil , err
486+ }
487+
488+ bmcVersion .Spec .ServerMaintenanceRefs = []metalv1alpha1.ServerMaintenanceRefItem {}
489+
490+ serverMaintenanceRefs := []metalv1alpha1.ServerMaintenanceRefItem {}
491+ for _ , serverMaintenance := range serverMaintenanceList .Items {
492+ if ref , exists := serverMaintenanceRefMap [serverMaintenance .Name ]; exists {
493+ serverMaintenanceRefs = append (serverMaintenanceRefs , ref )
494+ }
495+ }
496+ return serverMaintenanceRefs , nil
497+ }
498+
349499func (r * BMCVersionSetReconciler ) getBMCBySelector (
350500 ctx context.Context ,
351501 bmcVersionSet * metalv1alpha1.BMCVersionSet ,
0 commit comments