Skip to content

Commit 6bf3c72

Browse files
controller, daemon, and upgrade monitor: update MachineConfigNode update flow to include image mode status reporting fields
1 parent ac9ba66 commit 6bf3c72

File tree

4 files changed

+256
-3
lines changed

4 files changed

+256
-3
lines changed

pkg/controller/common/layered_node_state.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,19 @@ func (l *LayeredNodeState) CheckNodeCandidacyForUpdate(layered bool, pool *mcfgv
321321

322322
return true
323323
}
324+
325+
// GetDesiredAnnotationsFromMachineOSConfig gets the desired config version and desired image values from the associated MOSC and MOSB
326+
func (l *LayeredNodeState) GetDesiredAnnotationsFromMachineConfigPool(mcp *mcfgv1.MachineConfigPool) (desriedConfig string) {
327+
return mcp.Spec.Configuration.Name
328+
}
329+
330+
// GetDesiredAnnotationsFromMachineOSConfig gets the desired config version and desired image values from the associated MOSC and MOSB
331+
func (l *LayeredNodeState) GetDesiredAnnotationsFromMachineOSConfig(mosc *mcfgv1.MachineOSConfig, mosb *mcfgv1.MachineOSBuild) (desriedConfig, desiredImage string) {
332+
desiredImage = ""
333+
moscs := NewMachineOSConfigState(mosc)
334+
if moscs.HasOSImage() {
335+
desiredImage = moscs.GetOSImage()
336+
}
337+
338+
return mosb.Spec.MachineConfig.Name, desiredImage
339+
}

pkg/controller/node/node_controller.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
helpers "github.com/openshift/machine-config-operator/pkg/helpers"
12+
"github.com/openshift/machine-config-operator/pkg/upgrademonitor"
1213

1314
configv1 "github.com/openshift/api/config/v1"
1415
features "github.com/openshift/api/features"
@@ -1288,11 +1289,19 @@ func (ctrl *Controller) updateCandidateNode(mosc *mcfgv1.MachineOSConfig, mosb *
12881289
}
12891290

12901291
lns := ctrlcommon.NewLayeredNodeState(oldNode)
1292+
desiredConfig := ""
1293+
desiredImage := ""
12911294
if !layered {
12921295
lns.SetDesiredStateFromPool(pool)
1296+
// If pool is not layered, the desired image annotation is removed (see the `delete`
1297+
// call in `SetDesiredStateFromPool`), so only the desired config version must be set.
1298+
desiredConfig = lns.GetDesiredAnnotationsFromMachineConfigPool(pool)
12931299
} else {
12941300
lns.SetDesiredStateFromMachineOSConfig(mosc, mosb)
1301+
desiredConfig, desiredImage = lns.GetDesiredAnnotationsFromMachineOSConfig(mosc, mosb)
12951302
}
1303+
// TODO: maybe add a try/catch for the error handling if the MCN does not yest exist
1304+
upgrademonitor.UpdateMachineConfigNodeSpecDesiredAnnotations(ctrl.fgHandler, ctrl.client, nodeName, desiredConfig, desiredImage)
12961305

12971306
// Set the desired state to match the pool.
12981307

pkg/daemon/update.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,11 +2567,37 @@ func (dn *Daemon) updateLayeredOS(config *mcfgv1.MachineConfig) error {
25672567
}
25682568
}
25692569

2570+
// For image mode status reporting we need the node's MCP association to populate it's MCN
2571+
pool := ""
2572+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2573+
pool, err = helpers.GetPrimaryPoolNameForMCN(dn.mcpLister, dn.node)
2574+
if err != nil {
2575+
return err
2576+
}
2577+
}
2578+
25702579
if isOsImagePresent {
25712580
if err := dn.NodeUpdaterClient.RebaseLayeredFromContainerStorage(newURL); err != nil {
25722581
return fmt.Errorf("failed to update OS from local storage: %s: %w", newURL, err)
25732582
}
25742583
} else {
2584+
// Report ImagePulledFromRegistry condition as unknown (pulling)
2585+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2586+
err := upgrademonitor.GenerateAndApplyMachineConfigNodes(
2587+
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Pulling OS image %s from registry", newURL)},
2588+
nil,
2589+
metav1.ConditionUnknown,
2590+
metav1.ConditionFalse,
2591+
dn.node,
2592+
dn.mcfgClient,
2593+
dn.fgHandler,
2594+
pool,
2595+
)
2596+
if err != nil {
2597+
klog.Errorf("Error setting ImagePulledFromRegistry condition to unknown: %v", err)
2598+
}
2599+
}
2600+
25752601
// Workaround for OCPBUGS-43406, retry the remote rebase with backoff,
25762602
// such that if we happen to update while the CoreDNS pod is being restarted,
25772603
// the next retry should succeed if no other issues are present.
@@ -2588,8 +2614,41 @@ func (dn *Daemon) updateLayeredOS(config *mcfgv1.MachineConfig) error {
25882614
}
25892615
return true, nil
25902616
}); err != nil {
2617+
// Report ImagePulledFromRegistry condition as false (failed)
2618+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2619+
err = upgrademonitor.GenerateAndApplyMachineConfigNodes(
2620+
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Failed to pull OS image %s from registry: %v", newURL, err)},
2621+
nil,
2622+
metav1.ConditionFalse,
2623+
metav1.ConditionFalse,
2624+
dn.node,
2625+
dn.mcfgClient,
2626+
dn.fgHandler,
2627+
pool,
2628+
)
2629+
if err != nil {
2630+
klog.Errorf("Error setting ImagePulledFromRegistry condition to false: %v", err)
2631+
}
2632+
}
25912633
return fmt.Errorf("Failed to update OS to %s after retries: %w", newURL, err)
25922634
}
2635+
2636+
// Report ImagePulledFromRegistry condition as true (success)
2637+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2638+
err := upgrademonitor.GenerateAndApplyMachineConfigNodes(
2639+
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Successfully pulled OS image %s from registry", newURL)},
2640+
nil,
2641+
metav1.ConditionTrue,
2642+
metav1.ConditionFalse,
2643+
dn.node,
2644+
dn.mcfgClient,
2645+
dn.fgHandler,
2646+
pool,
2647+
)
2648+
if err != nil {
2649+
klog.Errorf("Error setting ImagePulledFromRegistry condition to true: %v", err)
2650+
}
2651+
}
25932652
}
25942653

25952654
return nil
@@ -2738,13 +2797,74 @@ func (dn *CoreOSDaemon) applyLayeredOSChanges(mcDiff machineConfigDiff, oldConfi
27382797
var osExtensionsContentDir string
27392798
var err error
27402799

2800+
// For image mode status reporting we need the node's MCP association to populate it's MCN
2801+
pool := ""
2802+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2803+
pool, err = helpers.GetPrimaryPoolNameForMCN(dn.mcpLister, dn.node)
2804+
if err != nil {
2805+
return err
2806+
}
2807+
}
2808+
27412809
if newConfig.Spec.BaseOSExtensionsContainerImage != "" && (mcDiff.osUpdate || mcDiff.extensions || mcDiff.kernelType) && !mcDiff.oclEnabled {
27422810
// TODO(jkyros): the original intent was that we use the extensions container as a service, but that currently results
27432811
// in a lot of complexity due to boostrap and firstboot where the service isn't easily available, so for now we are going
27442812
// to extract them to disk like we did previously.
2813+
2814+
// Report ImagePulledFromRegistry condition as unknown (pulling)
2815+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2816+
err := upgrademonitor.GenerateAndApplyMachineConfigNodes(
2817+
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Pulling image %s from registry", newConfig.Spec.BaseOSExtensionsContainerImage)},
2818+
nil,
2819+
metav1.ConditionUnknown,
2820+
metav1.ConditionFalse,
2821+
dn.node,
2822+
dn.mcfgClient,
2823+
dn.fgHandler,
2824+
pool,
2825+
)
2826+
if err != nil {
2827+
klog.Errorf("Error setting ImagePulledFromRegistry condition to unknown: %v", err)
2828+
}
2829+
}
2830+
27452831
if osExtensionsContentDir, err = ExtractExtensionsImage(newConfig.Spec.BaseOSExtensionsContainerImage); err != nil {
2832+
// Report ImagePulledFromRegistry condition as false (failed)
2833+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2834+
err := upgrademonitor.GenerateAndApplyMachineConfigNodes(
2835+
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Failed to pull image %s from registry: %v", newConfig.Spec.BaseOSExtensionsContainerImage, err)},
2836+
nil,
2837+
metav1.ConditionFalse,
2838+
metav1.ConditionFalse,
2839+
dn.node,
2840+
dn.mcfgClient,
2841+
dn.fgHandler,
2842+
pool,
2843+
)
2844+
if err != nil {
2845+
klog.Errorf("Error setting ImagePulledFromRegistry condition to false: %v", err)
2846+
}
2847+
}
27462848
return err
27472849
}
2850+
2851+
// Report ImagePulledFromRegistry condition as true (success)
2852+
if dn.fgHandler != nil && dn.fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
2853+
err := upgrademonitor.GenerateAndApplyMachineConfigNodes(
2854+
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Successfully pulled image %s from registry", newConfig.Spec.BaseOSExtensionsContainerImage)},
2855+
nil,
2856+
metav1.ConditionTrue,
2857+
metav1.ConditionFalse,
2858+
dn.node,
2859+
dn.mcfgClient,
2860+
dn.fgHandler,
2861+
pool,
2862+
)
2863+
if err != nil {
2864+
klog.Errorf("Error setting ImagePulledFromRegistry condition to true: %v", err)
2865+
}
2866+
}
2867+
27482868
// Delete extracted OS image once we are done.
27492869
defer os.RemoveAll(osExtensionsContentDir)
27502870

pkg/upgrademonitor/upgrade_monitor.go

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ func generateAndApplyMachineConfigNodes(
135135

136136
// we use this array to see if the MCN has all of its conditions set
137137
// if not we set a sane default
138+
// TODO: decide if the image reg stuff should be added here too
138139
allConditionTypes := []mcfgv1.StateProgress{
139140
mcfgv1.MachineConfigNodeUpdatePrepared,
140141
mcfgv1.MachineConfigNodeUpdateExecuted,
@@ -217,8 +218,20 @@ func generateAndApplyMachineConfigNodes(
217218

218219
case condition.Status != metav1.ConditionFalse && reset:
219220
condition.Status = metav1.ConditionFalse
220-
condition.Message = fmt.Sprintf("Action during update to %s: %s", newMCNode.Spec.ConfigVersion.Desired, condition.Message)
221221
condition.LastTransitionTime = metav1.Now()
222+
223+
// TODO: test this in the enable and disable case
224+
// Set the update to annotation to the desired rendered MC version by default for the condition message
225+
updateToMessage := fmt.Sprintf("Action during update to %s: %s", newMCNode.Spec.ConfigVersion.Desired, condition.Message)
226+
// Handle OCL update cases differently
227+
if newMCNode.Spec.ConfigImage.DesiredImage != newMCNode.Status.ConfigImage.CurrentImage {
228+
if newMCNode.Spec.ConfigImage.DesiredImage != "" { // Handle case when desired image exists
229+
updateToMessage = fmt.Sprintf("Action during update to %s: %s", newMCNode.Spec.ConfigImage.DesiredImage, condition.Message)
230+
} else { // When the desired image is empty, it means OCL is being disabled; provide a more useful message in this case
231+
updateToMessage = fmt.Sprintf("Action during update to disable image mode: %s", condition.Message)
232+
}
233+
}
234+
condition.Message = updateToMessage
222235
}
223236
condition.DeepCopyInto(&newMCNode.Status.Conditions[i])
224237
}
@@ -250,6 +263,27 @@ func generateAndApplyMachineConfigNodes(
250263
newMCNode.Status.ConfigVersion.Current = node.Annotations[daemonconsts.CurrentMachineConfigAnnotationKey]
251264
}
252265

266+
// Set current and desired image values in MCN.Status.ConfigImage
267+
// This is only done when the ImageModeStatusReporting feature gate is enabled
268+
if fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
269+
// TODO: test if this flow accurately updates the current image annotaiton on OCL disable
270+
newMCNStatusConfigImage := mcfgv1.MachineConfigNodeStatusConfigImage{}
271+
currentImageAnnotation := node.Annotations[daemonconsts.CurrentImageAnnotationKey]
272+
desiredImageAnnotation := node.Annotations[daemonconsts.DesiredImageAnnotationKey]
273+
274+
// Set current image if annotation exists
275+
if currentImageAnnotation != "" {
276+
newMCNStatusConfigImage.CurrentImage = mcfgv1.ImageDigestFormat(currentImageAnnotation)
277+
}
278+
279+
// Set desired image if annotation exists
280+
if desiredImageAnnotation != "" {
281+
newMCNStatusConfigImage.DesiredImage = mcfgv1.ImageDigestFormat(desiredImageAnnotation)
282+
}
283+
284+
newMCNode.Status.ConfigImage = newMCNStatusConfigImage
285+
}
286+
253287
// if we do not need a new MCN, generate the apply configurations for this object
254288
if !needNewMCNode {
255289
statusconfigVersionApplyConfig := machineconfigurationv1.MachineConfigNodeStatusMachineConfigVersion().WithDesired(newMCNode.Status.ConfigVersion.Desired)
@@ -262,6 +296,23 @@ func generateAndApplyMachineConfigNodes(
262296
WithObservedGeneration(newMCNode.Generation + 1).
263297
WithConfigVersion(statusconfigVersionApplyConfig)
264298

299+
// Add ConfigImage to apply configuration if feature gate is enabled and image annotations exist
300+
if fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) && (newMCNode.Status.ConfigImage.CurrentImage != "" || newMCNode.Status.ConfigImage.DesiredImage != "") {
301+
configImageApplyConfig := machineconfigurationv1.MachineConfigNodeStatusConfigImage()
302+
303+
// Set current image if it exists
304+
if newMCNode.Status.ConfigImage.CurrentImage != "" {
305+
configImageApplyConfig = configImageApplyConfig.WithCurrentImage(newMCNode.Status.ConfigImage.CurrentImage)
306+
}
307+
308+
// Set desired image if it exists
309+
if newMCNode.Status.ConfigImage.DesiredImage != "" {
310+
configImageApplyConfig = configImageApplyConfig.WithDesiredImage(newMCNode.Status.ConfigImage.DesiredImage)
311+
}
312+
313+
statusApplyConfig = statusApplyConfig.WithConfigImage(configImageApplyConfig)
314+
}
315+
265316
if fgHandler.Enabled(features.FeatureGatePinnedImages) {
266317
if imageSetApplyConfig == nil {
267318
for _, imageSet := range newMCNode.Status.PinnedImageSets {
@@ -335,6 +386,51 @@ func isSingletonCondition(singletonConditionTypes []mcfgv1.StateProgress, condit
335386
return false
336387
}
337388

389+
// UpdateMachineConfigNodeSpecDesiredAnnotations sets the desired config version and image
390+
// annotation values in the `Spec` of an existing MachineConfigNode resource
391+
func UpdateMachineConfigNodeSpecDesiredAnnotations(fgHandler ctrlcommon.FeatureGatesHandler, mcfgClient mcfgclientset.Interface, nodeName string, desiredConfig string, desiredImage string) error {
392+
if fgHandler == nil {
393+
return nil
394+
}
395+
396+
// Check that the MachineConfigNode and ImageModeStatusReporting feature gates are enabled
397+
if !fgHandler.Enabled(features.FeatureGateMachineConfigNodes) || !fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
398+
klog.Infof("MachineConfigNode or ImageModeStatusReporting FeatureGate is not enabled. Please enable the TechPreviewNoUpgrade FeatureSet to use ImageModeStatusReporting.")
399+
return nil
400+
}
401+
402+
// Get the existing MCN
403+
mcn, mcnErr := mcfgClient.MachineconfigurationV1().MachineConfigNodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
404+
// TODO: check if we need to make an MCN at this point instead then or if we get to this point that we should have an MCN
405+
if mcnErr != nil {
406+
// no existing MCN found since no resource found
407+
if apierrors.IsNotFound(mcnErr) {
408+
return fmt.Errorf("MCN for %s node does not exits. Skipping MCN spec update.", nodeName)
409+
}
410+
return mcnErr
411+
}
412+
413+
// Set the desired config annotation
414+
mcn.Spec.ConfigVersion.Desired = NotYetSet
415+
if desiredConfig != "" {
416+
mcn.Spec.ConfigVersion.Desired = desiredConfig
417+
}
418+
419+
// Set the desired image annotation
420+
mcn.Spec.ConfigImage = mcfgv1.MachineConfigNodeSpecConfigImage{}
421+
if desiredImage != "" {
422+
mcn.Spec.ConfigImage = mcfgv1.MachineConfigNodeSpecConfigImage{
423+
DesiredImage: mcfgv1.ImageDigestFormat(desiredImage),
424+
}
425+
}
426+
427+
// Update the MCN resource
428+
if _, err := mcfgClient.MachineconfigurationV1().MachineConfigNodes().Update(context.TODO(), mcn, metav1.UpdateOptions{FieldManager: "machine-config-operator"}); err != nil {
429+
return fmt.Errorf("failed to update the %s mcn spec with the new desired config and image value: %w", nodeName, err)
430+
}
431+
return nil
432+
}
433+
338434
// GenerateAndApplyMachineConfigNodeSpec generates and applies a new MCN spec based off the node state
339435
func GenerateAndApplyMachineConfigNodeSpec(fgHandler ctrlcommon.FeatureGatesHandler, pool string, node *corev1.Node, mcfgClient mcfgclientset.Interface) error {
340436
if fgHandler == nil || node == nil {
@@ -345,10 +441,11 @@ func GenerateAndApplyMachineConfigNodeSpec(fgHandler ctrlcommon.FeatureGatesHand
345441
klog.Infof("MCN Featuregate is not enabled. Please enable the TechPreviewNoUpgrade featureset to use MachineConfigNodes")
346442
return nil
347443
}
444+
348445
// get the existing MCN, or if it DNE create one below
349446
mcNode, needNewMCNode := createOrGetMachineConfigNode(mcfgClient, node)
350447
newMCNode := mcNode.DeepCopy()
351-
// set the spec config version
448+
// Set the MCN owner references
352449
newMCNode.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
353450
{
354451
APIVersion: "v1",
@@ -358,14 +455,25 @@ func GenerateAndApplyMachineConfigNodeSpec(fgHandler ctrlcommon.FeatureGatesHand
358455
},
359456
}
360457

458+
// Set the desired config version in the MCN
361459
newMCNode.Spec.ConfigVersion = mcfgv1.MachineConfigNodeSpecMachineConfigVersion{
362460
Desired: node.Annotations[daemonconsts.DesiredMachineConfigAnnotationKey],
363461
}
364-
// Set desired config to NotYetSet if the annotation is empty to satisfy API validation
462+
// If the desired config does not yet exist for the node, the desired config should be set to NotYetSet
365463
if newMCNode.Spec.ConfigVersion.Desired == "" {
366464
newMCNode.Spec.ConfigVersion.Desired = NotYetSet
367465
}
368466

467+
// Check that the ImageModeStatusReporting feature gate is enabled
468+
if fgHandler.Enabled(features.FeatureGateImageModeStatusReporting) {
469+
// Set the desired image in the MCN if it exists
470+
newMCNode.Spec.ConfigImage = mcfgv1.MachineConfigNodeSpecConfigImage{}
471+
if node.Annotations[daemonconsts.DesiredImageAnnotationKey] != "" {
472+
newMCNode.Spec.ConfigImage.DesiredImage = mcfgv1.ImageDigestFormat(node.Annotations[daemonconsts.DesiredImageAnnotationKey])
473+
}
474+
}
475+
476+
// Set the MCN pool and node names
369477
newMCNode.Spec.Pool = mcfgv1.MCOObjectReference{
370478
Name: pool,
371479
}

0 commit comments

Comments
 (0)