Skip to content

Commit 76ac40e

Browse files
avoid reboots by recreating and reusing MOSB with updated rendered spec
1 parent de92857 commit 76ac40e

File tree

7 files changed

+278
-37
lines changed

7 files changed

+278
-37
lines changed

pkg/controller/build/clients.go

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
type informers struct {
2626
controllerConfigInformer mcfginformersv1.ControllerConfigInformer
2727
machineConfigPoolInformer mcfginformersv1.MachineConfigPoolInformer
28+
machineConfigInformer mcfginformersv1.MachineConfigInformer
2829
jobInformer batchinformersv1.JobInformer
2930
machineOSBuildInformer mcfginformersv1.MachineOSBuildInformer
3031
machineOSConfigInformer mcfginformersv1.MachineOSConfigInformer
@@ -46,6 +47,7 @@ func (i *informers) listers() *listers {
4647
machineOSBuildLister: i.machineOSBuildInformer.Lister(),
4748
machineOSConfigLister: i.machineOSConfigInformer.Lister(),
4849
machineConfigPoolLister: i.machineConfigPoolInformer.Lister(),
50+
machineConfigLister: i.machineConfigInformer.Lister(),
4951
jobLister: i.jobInformer.Lister(),
5052
controllerConfigLister: i.controllerConfigInformer.Lister(),
5153
nodeLister: i.nodeInformer.Lister(),
@@ -57,6 +59,7 @@ type listers struct {
5759
machineOSBuildLister mcfglistersv1.MachineOSBuildLister
5860
machineOSConfigLister mcfglistersv1.MachineOSConfigLister
5961
machineConfigPoolLister mcfglistersv1.MachineConfigPoolLister
62+
machineConfigLister mcfglistersv1.MachineConfigLister
6063
jobLister batchlisterv1.JobLister
6164
controllerConfigLister mcfglistersv1.ControllerConfigLister
6265
nodeLister corelistersv1.NodeLister
@@ -91,6 +94,7 @@ func newInformers(mcfgclient mcfgclientset.Interface, kubeclient clientset.Inter
9194
controllerConfigInformer := mcoInformerFactory.Machineconfiguration().V1().ControllerConfigs()
9295
machineConfigPoolInformer := mcoInformerFactory.Machineconfiguration().V1().MachineConfigPools()
9396
machineOSBuildInformer := mcoInformerFactory.Machineconfiguration().V1().MachineOSBuilds()
97+
machineConfigInformer := mcoInformerFactory.Machineconfiguration().V1().MachineConfigs()
9498
machineOSConfigInformer := mcoInformerFactory.Machineconfiguration().V1().MachineOSConfigs()
9599
jobInformer := coreInformerFactory.Batch().V1().Jobs()
96100
nodeInformer := coreInformerFactoryNodes.Core().V1().Nodes()
@@ -100,6 +104,7 @@ func newInformers(mcfgclient mcfgclientset.Interface, kubeclient clientset.Inter
100104
machineConfigPoolInformer: machineConfigPoolInformer,
101105
machineOSBuildInformer: machineOSBuildInformer,
102106
machineOSConfigInformer: machineOSConfigInformer,
107+
machineConfigInformer: machineConfigInformer,
103108
jobInformer: jobInformer,
104109
nodeInformer: nodeInformer,
105110
toStart: []interface{ Start(<-chan struct{}) }{

pkg/controller/build/reconciler.go

+173-20
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func (b *buildReconciler) updateMachineOSConfig(ctx context.Context, old, cur *m
117117

118118
// Whenever the MachineOSConfig spec has changed, create a new MachineOSBuild.
119119
if !equality.Semantic.DeepEqual(old.Spec, cur.Spec) {
120-
klog.Infof("Detected MachineOSConfig change for %s", cur.Name)
120+
klog.Infof("[updateMachineOSConfig] Detected MachineOSConfig change and kicking off createNewMachineOSBuildOrReuseExisting for %s", cur.Name)
121121
return b.createNewMachineOSBuildOrReuseExisting(ctx, cur, false)
122122
}
123123

@@ -331,7 +331,7 @@ func (b *buildReconciler) updateMachineOSConfigStatus(ctx context.Context, mosc
331331

332332
// skip the status update if the current image pullspec equals the digest image pushspec.
333333
if mosc.Status.CurrentImagePullSpec == mosb.Status.DigestedImagePushSpec {
334-
klog.Infof("MachineOSConfig %q already has final image pushspec for MachineOSBuild %q", mosc.Name, mosb.Name)
334+
klog.Infof("[updateMachineOSConfig] MachineOSConfig %q already has final image pushspec for MachineOSBuild %q", mosc.Name, mosb.Name)
335335
return nil
336336
}
337337

@@ -371,16 +371,56 @@ func (b *buildReconciler) UpdateMachineConfigPool(ctx context.Context, oldMCP, c
371371
// Sepcifically, whenever a new rendered MachineConfig is applied, it will
372372
// create a new MachineOSBuild in response.
373373
func (b *buildReconciler) updateMachineConfigPool(ctx context.Context, oldMCP, curMCP *mcfgv1.MachineConfigPool) error {
374-
if oldMCP.Spec.Configuration.Name != curMCP.Spec.Configuration.Name {
375-
klog.Infof("Rendered config for pool %s changed from %s to %s", curMCP.Name, oldMCP.Spec.Configuration.Name, curMCP.Spec.Configuration.Name)
376-
if err := b.createNewMachineOSBuildOrReuseExistingForPoolChange(ctx, curMCP); err != nil {
377-
return fmt.Errorf("could not create or reuse existing MachineOSBuild for MachineConfigPool %q change: %w", curMCP.Name, err)
374+
375+
// Did the rendered-MC actually change?
376+
needsImageRebuild, err := b.reconcilePoolChange(oldMCP, curMCP)
377+
if err != nil {
378+
return err
379+
}
380+
381+
/////////// REMOVE JUST FOR LOGS ///////////
382+
// Get current and desired MachineConfigs
383+
curMC, err := b.machineConfigLister.Get(oldMCP.Spec.Configuration.Name)
384+
if err != nil {
385+
return fmt.Errorf("error getting current MachineConfig %s: %w", oldMCP.Spec.Configuration.Name, err)
386+
}
387+
388+
desMC, err := b.machineConfigLister.Get(curMCP.Spec.Configuration.Name)
389+
if err != nil {
390+
return fmt.Errorf("error getting desired MachineConfig %s: %w", curMCP.Spec.Configuration.Name, err)
391+
}
392+
393+
klog.Infof("desMC is %s", desMC.Name)
394+
klog.Infof("currMC is %s", curMC.Name)
395+
/////////// REMOVE JUST FOR LOGS ///////////
396+
397+
mosc, err := utils.GetMachineOSConfigForMachineConfigPool(curMCP, b.utilListers())
398+
if err != nil {
399+
return fmt.Errorf("could not find MachineOSConfig for pool %q: %w", curMCP.Name, err)
400+
}
401+
existingName := mosc.Annotations[constants.CurrentMachineOSBuildAnnotationKey]
402+
if existingName == "" {
403+
return fmt.Errorf("no current build annotation on MachineOSConfig %q", mosc.Name)
404+
}
405+
existingMosb, err := b.machineOSBuildLister.Get(existingName)
406+
if err != nil {
407+
return fmt.Errorf("could not fetch existing MachineOSBuild %q: %w", existingName, err)
408+
}
409+
if !needsImageRebuild && existingMosb != nil {
410+
if err := b.deleteMachineOSBuild(ctx, existingMosb); err != nil {
411+
return err
378412
}
413+
return b.createNewMachineOSBuildOrReuseExisting(ctx, mosc, true)
414+
} else {
415+
return b.createNewMachineOSBuildOrReuseExistingForPoolChange(ctx, curMCP)
379416
}
417+
}
380418

381-
// Not sure if we need to do this here yet or not.
382-
// TODO: Determine if we should call b.syncMachineConfigPools() here or not.
383-
return b.syncAll(ctx)
419+
func (b *buildReconciler) patchMOSBLabels(ctx context.Context, mosb *mcfgv1.MachineOSBuild, newMCName string) error {
420+
mosbCopy := mosb.DeepCopy()
421+
mosbCopy.Labels[constants.RenderedMachineConfigLabelKey] = newMCName
422+
_, err := b.mcfgclient.MachineconfigurationV1().MachineOSBuilds().Update(ctx, mosbCopy, metav1.UpdateOptions{})
423+
return err
384424
}
385425

386426
// Adds a MachineOSBuild.
@@ -540,8 +580,7 @@ func (b *buildReconciler) createNewMachineOSBuildOrReuseExisting(ctx context.Con
540580
// previously built image pullspec and adjust the MachineOSConfig to use that
541581
// image instead.
542582
if err == nil && existingMosb != nil {
543-
imageNeedsRebuild, err := b.reuseExistingMachineOSBuildIfPossible(ctx, mosc, existingMosb)
544-
if err != nil {
583+
if err := b.reuseExistingMachineOSBuildIfPossible(ctx, mosc, existingMosb, mcp); err != nil {
545584
return fmt.Errorf("could not reuse existing MachineOSBuild %q for MachineOSConfig %q: %w", existingMosb.Name, mosc.Name, err)
546585
}
547586

@@ -556,8 +595,8 @@ func (b *buildReconciler) createNewMachineOSBuildOrReuseExisting(ctx context.Con
556595
// In this situation, we've determined that the MachineOSBuild does not
557596
// exist, so we need to create it.
558597
if k8serrors.IsNotFound(err) {
559-
_, err := b.mcfgclient.MachineconfigurationV1().MachineOSBuilds().Create(ctx, mosb, metav1.CreateOptions{})
560-
if err != nil && !k8serrors.IsAlreadyExists(err) {
598+
mosb, err := b.mcfgclient.MachineconfigurationV1().MachineOSBuilds().Create(ctx, mosb, metav1.CreateOptions{})
599+
if err != nil {
561600
return fmt.Errorf("could not create new MachineOSBuild %q: %w", mosb.Name, err)
562601
}
563602
klog.Infof("New MachineOSBuild created: %s", mosb.Name)
@@ -607,7 +646,7 @@ func (b *buildReconciler) getCerts() error {
607646
}
608647

609648
// Determines if a preexising MachineOSBuild can be reused and if possible, does it.
610-
func (b *buildReconciler) reuseExistingMachineOSBuildIfPossible(ctx context.Context, mosc *mcfgv1.MachineOSConfig, existingMosb *mcfgv1.MachineOSBuild) (bool, error) {
649+
func (b *buildReconciler) reuseExistingMachineOSBuildIfPossible(ctx context.Context, mosc *mcfgv1.MachineOSConfig, existingMosb *mcfgv1.MachineOSBuild, mcp *mcfgv1.MachineConfigPool) error {
611650
existingMosbState := ctrlcommon.NewMachineOSBuildState(existingMosb)
612651

613652
canBeReused := false
@@ -661,7 +700,11 @@ func (b *buildReconciler) reuseExistingMachineOSBuildIfPossible(ctx context.Cont
661700

662701
// Update the MachineOSConfig to use the preexisting MachineOSBuild.
663702
if err := b.updateMachineOSConfigStatus(ctx, mosc, existingMosb); err != nil {
664-
return canBeReused, fmt.Errorf("could not update MachineOSConfig %q status to reuse preexisting MachineOSBuild %q: %w", mosc.Name, existingMosb.Name, err)
703+
return fmt.Errorf("could not update MachineOSConfig %q status to reuse preexisting MachineOSBuild %q: %w", mosc.Name, existingMosb.Name, err)
704+
}
705+
706+
if err := b.patchMOSBLabels(ctx, existingMosb, mcp.Spec.Configuration.Name); err != nil {
707+
return fmt.Errorf("could not patch MOSB labels")
665708
}
666709
}
667710

@@ -1188,12 +1231,62 @@ func (b *buildReconciler) syncMachineOSBuild(ctx context.Context, mosb *mcfgv1.M
11881231
return fmt.Errorf("could not get MachineConfigPool from MachineOSBuild %q: %w", mosb.Name, err)
11891232
}
11901233

1191-
// An mosb which had previously been forgotten by the queue and is no longer desired by the mcp should not build
11921234
if mosb.ObjectMeta.Labels[constants.RenderedMachineConfigLabelKey] != mcp.Spec.Configuration.Name {
11931235
klog.Infof("The MachineOSBuild %q which builds the rendered Machine Config %q is no longer desired by the MCP %q", mosb.Name, mosb.ObjectMeta.Labels[constants.RenderedMachineConfigLabelKey], mosb.ObjectMeta.Labels[constants.TargetMachineConfigPoolLabelKey])
11941236
return nil
11951237
}
11961238

1239+
// desiredMC, err := b.machineConfigLister.Get(mcp.Spec.Configuration.Name)
1240+
// if err != nil {
1241+
// return fmt.Errorf("error getting desired MC: %w", err)
1242+
// }
1243+
1244+
// // existing MC labels
1245+
// currentWithImage := mosb.Labels[constants.RenderedMCWithImageLabel]
1246+
// currMC := mosb.Labels[constants.RenderedMachineConfigLabelKey]
1247+
1248+
// oldMC, err := b.machineConfigLister.Get(currMC)
1249+
// if err != nil {
1250+
// return fmt.Errorf("could not fetch old MachineConfig %q: %w", currMC, err)
1251+
// }
1252+
1253+
// // new MC labels
1254+
// desMC := desiredMC.Name
1255+
// newWithImage := currentWithImage
1256+
1257+
// mosbCopy := mosb.DeepCopy()
1258+
// updateLabels := false
1259+
1260+
// if currMC != desMC {
1261+
// klog.Info("Current MC does not equal the new MC, we need to update our labels to reflect that")
1262+
// mosbCopy.Labels[constants.RenderedMachineConfigLabelKey] = desMC
1263+
// updateLabels = true
1264+
// }
1265+
1266+
// if ctrlcommon.RequiresRebuild(oldMC, desiredMC) && currentWithImage != newWithImage {
1267+
// mosbCopy.Labels[constants.RenderedMCWithImageLabel] = newWithImage
1268+
// updateLabels = true
1269+
// }
1270+
1271+
// if updateLabels {
1272+
// klog.Infof("syncMachineOSBuild: Updating labels for MOSB %q (currentNoImage=%s, desiredMCName=%s, desiredWithImage=%s, currentWithImage=%s)",
1273+
// mosb.Name, currMC, desMC, newWithImage, currentWithImage)
1274+
1275+
// mosbCopy.Labels[constants.RenderedMachineConfigLabelKey] = desMC
1276+
// mosbCopy.Labels[constants.RenderedMCWithImageLabel] = newWithImage
1277+
1278+
// // Update the MOSB object
1279+
// _, err := b.mcfgclient.MachineconfigurationV1().MachineOSBuilds().Update(ctx, mosbCopy, metav1.UpdateOptions{})
1280+
// if err != nil {
1281+
// klog.Errorf("syncMachineOSBuild: Failed to update MOSB %q labels: %v", mosb.Name, err)
1282+
1283+
// return fmt.Errorf("failed to sync labels: %v", err)
1284+
// }
1285+
// klog.Infof("syncMachineOSBuild: Updated MOSB %q labels to RenderedMCNoImageLabel=%s, RenderedMCWithImageLabel=%s",
1286+
// mosb.Name, desiredMC.Name, newWithImage)
1287+
// return nil
1288+
// }
1289+
11971290
mosbState := ctrlcommon.NewMachineOSBuildState(mosb)
11981291

11991292
if mosbState.IsInTerminalState() {
@@ -1290,7 +1383,7 @@ func (b *buildReconciler) syncMachineOSConfig(ctx context.Context, mosc *mcfgv1.
12901383
}
12911384
}
12921385

1293-
klog.Infof("No matching MachineOSBuild found for MachineOSConfig %q, will create one", mosc.Name)
1386+
klog.Infof("syncMachineOSConfig: No matching MachineOSBuild found for MachineOSConfig %q, will create one by kicking off createNewMachineOSBuildOrReuseExisting", mosc.Name)
12941387
if err := b.createNewMachineOSBuildOrReuseExisting(ctx, mosc, false); err != nil {
12951388
return fmt.Errorf("could not create new or reuse existing MachineOSBuild for MachineOSConfig %q: %w", mosc.Name, err)
12961389
}
@@ -1327,7 +1420,67 @@ func (b *buildReconciler) syncMachineConfigPools(ctx context.Context) error {
13271420
// MachineOSConfigs and MachineOSBuilds, which will create a new MachineOSBuild,
13281421
// if needed.
13291422
func (b *buildReconciler) syncMachineConfigPool(ctx context.Context, mcp *mcfgv1.MachineConfigPool) error {
1330-
return b.timeObjectOperation(mcp, syncingVerb, func() error {
1331-
return b.createNewMachineOSBuildOrReuseExistingForPoolChange(ctx, mcp)
1332-
})
1423+
mosc, err := utils.GetMachineOSConfigForMachineConfigPool(mcp, b.utilListers())
1424+
if err != nil {
1425+
// Not opted in yet, so nothing for us to do.
1426+
klog.V(4).Infof("buildReconciler: no MachineOSConfig for pool %q, skipping", mcp.Name)
1427+
return nil
1428+
}
1429+
// old pool
1430+
old := mcp.DeepCopy()
1431+
old.Spec.Configuration.Name = mcp.Status.Configuration.Name
1432+
1433+
// decide if we annotate or reuse
1434+
needsImageRebuild, err := b.reconcilePoolChange(old, mcp)
1435+
if err != nil {
1436+
return err
1437+
}
1438+
1439+
existingName := mosc.Annotations[constants.CurrentMachineOSBuildAnnotationKey]
1440+
if existingName == "" {
1441+
return fmt.Errorf("no current build annotation on MachineOSConfig %q", mosc.Name)
1442+
}
1443+
1444+
existingMosb, err := b.machineOSBuildLister.Get(existingName)
1445+
if err != nil {
1446+
return fmt.Errorf("could not fetch existing MachineOSBuild %q: %w", existingName, err)
1447+
}
1448+
klog.Infof("existing MOSB is %s", existingMosb.Name)
1449+
1450+
if !needsImageRebuild && existingMosb != nil {
1451+
if err := b.deleteMachineOSBuild(ctx, existingMosb); err != nil {
1452+
return err
1453+
}
1454+
return b.createNewMachineOSBuildOrReuseExisting(ctx, mosc, true)
1455+
}
1456+
return b.createNewMachineOSBuildOrReuseExistingForPoolChange(ctx, mcp)
1457+
1458+
}
1459+
1460+
// reconcilePoolChange returns (annotateRebuild bool, reuseOnly bool, err error)
1461+
func (b *buildReconciler) reconcilePoolChange(oldMCP, curMCP *mcfgv1.MachineConfigPool) (bool, error) {
1462+
1463+
// // if the old mc is the same as the new mc, no updates needed
1464+
// if oldMCP.Spec.Configuration.Name == curMCP.Spec.Configuration.Name {
1465+
// klog.Info("old mc is the same as the new mc")
1466+
// return false, true, nil
1467+
// }
1468+
1469+
curr, err := b.machineConfigLister.Get(oldMCP.Spec.Configuration.Name)
1470+
if err != nil {
1471+
return false, err
1472+
}
1473+
des, err := b.machineConfigLister.Get(curMCP.Spec.Configuration.Name)
1474+
if err != nil {
1475+
return false, err
1476+
}
1477+
1478+
needsImageRebuild := ctrlcommon.RequiresRebuild(curr, des)
1479+
if needsImageRebuild {
1480+
klog.Info("needsimage rebuild is true, signalling a rebuild")
1481+
return true, nil
1482+
}
1483+
1484+
klog.Info("reconciled pool change, no rebuild needed")
1485+
return false, nil
13331486
}

pkg/controller/common/helpers.go

+8
Original file line numberDiff line numberDiff line change
@@ -1462,3 +1462,11 @@ func GetCAsFromConfigMap(cm *corev1.ConfigMap, key string) ([]byte, error) {
14621462
}
14631463
return nil, fmt.Errorf("%s not found in %s/%s", key, cm.Namespace, cm.Name)
14641464
}
1465+
1466+
// Determines if an on-cluster layering image rollout and rebuild is required for the changes applied on the new MC
1467+
func RequiresRebuild(oldMC, newMC *mcfgv1.MachineConfig) bool {
1468+
return oldMC.Spec.OSImageURL != newMC.Spec.OSImageURL ||
1469+
oldMC.Spec.KernelType != newMC.Spec.KernelType ||
1470+
!reflect.DeepEqual(oldMC.Spec.Extensions, newMC.Spec.Extensions) ||
1471+
!reflect.DeepEqual(oldMC.Spec.KernelArguments, newMC.Spec.KernelArguments)
1472+
}

0 commit comments

Comments
 (0)