Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ require (
github.com/google/renameio v0.1.0
github.com/imdario/mergo v0.3.16
github.com/opencontainers/go-digest v1.0.0
github.com/openshift/api v0.0.0-20250425163235-9b80d67473bc
github.com/openshift/api v0.0.0-20250620092249-a8cbc218cd2c
github.com/openshift/client-go v0.0.0-20250425165505-5f55ff6979a1
github.com/openshift/library-go v0.0.0-20250129210218-fe56c2cf5d70
github.com/openshift/runtime-utils v0.0.0-20230921210328-7bdb5b9c177b
Expand Down Expand Up @@ -350,3 +350,7 @@ require (
)

replace k8s.io/kube-openapi => github.com/openshift/kube-openapi v0.0.0-20230816122517-ffc8f001abb0

replace github.com/openshift/client-go => github.com/naseerahkani/client-go v0.0.0-20250722005126-8e9e555e298c

replace github.com/openshift/api => github.com/naseerahkani/api v0.0.0-20250724174438-d1048fccac8c
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,10 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
github.com/naseerahkani/api v0.0.0-20250724174438-d1048fccac8c h1:FjVVDcuMUlrxIDZYx8zPJChnrJg1wC1bonhrpB2yrgM=
github.com/naseerahkani/api v0.0.0-20250724174438-d1048fccac8c/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw=
github.com/naseerahkani/client-go v0.0.0-20250722005126-8e9e555e298c h1:f8AqcpybuKmUyGKCvq4pkOHIsgxGMvrmI5+yq6ZFTvo=
github.com/naseerahkani/client-go v0.0.0-20250722005126-8e9e555e298c/go.mod h1:AFenInkrazQ4DaGyy1GXrUQPGdVzv/uXMp6sGbR5bXs=
github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg=
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
Expand Down Expand Up @@ -513,10 +517,6 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/openshift/api v0.0.0-20250425163235-9b80d67473bc h1:BGKjHtYzBweOSu1UwTnNqtPbJZ4VzOTqVFlUDpP+6U8=
github.com/openshift/api v0.0.0-20250425163235-9b80d67473bc/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw=
github.com/openshift/client-go v0.0.0-20250425165505-5f55ff6979a1 h1:2HPG58V07TrrSGBviNPd0PY42vYHPPCIEwj/pb9nUlY=
github.com/openshift/client-go v0.0.0-20250425165505-5f55ff6979a1/go.mod h1:kH5mjMfcHCF0tEnxwvNJTLMnlbrEt3Ua+vMVGvBOK5w=
github.com/openshift/kube-openapi v0.0.0-20230816122517-ffc8f001abb0 h1:GPlAy197Jkr+D0T2FNWanamraTdzS/r9ZkT29lxvHaA=
github.com/openshift/kube-openapi v0.0.0-20230816122517-ffc8f001abb0/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
github.com/openshift/library-go v0.0.0-20250129210218-fe56c2cf5d70 h1:VLj8CU9q009xlMuR4wNcqDX4lVa2Ji3u/iYnBLHtQUc=
Expand Down
9 changes: 9 additions & 0 deletions pkg/controller/node/node_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,15 @@ func (ctrl *Controller) updateNode(old, cur interface{}) {
}
}

if fg.Enabled(features.FeatureGateImageModeStatusReporting) {
// check if second part of conditional is necessary
if oldNode.Annotations[daemonconsts.CurrentImageAnnotationKey] != oldNode.Annotations[daemonconsts.DesiredImageAnnotationKey] && curLNS.IsNodeDone() {
ctrl.logPoolNode(pool, curNode, "Completed update to %s", curNode.Annotations[daemonconsts.DesiredImageAnnotationKey])
changed = true
}

}

if !changed {
return
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/daemon/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig, skipCertifi
if err != nil {
klog.Errorf("Error making MCN spec for Update Compatible: %v", err)
}

if drain {
if err := dn.performDrain(); err != nil {
return err
Expand Down Expand Up @@ -1242,6 +1243,7 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig, skipCertifi
if err != nil {
klog.Errorf("Error making MCN for Updated Files and OS: %v", err)
}

// Node Disruption Policies cannot be used during firstboot as API is not accessible.
if !firstBoot {
return dn.performPostConfigChangeNodeDisruptionAction(nodeDisruptionActions, newConfig.GetName())
Expand Down Expand Up @@ -2784,9 +2786,30 @@ func (dn *CoreOSDaemon) applyLayeredOSChanges(mcDiff machineConfigDiff, oldConfi
}
}

// Get MCP associated with node
pool, err := helpers.GetPrimaryPoolNameForMCN(dn.mcpLister, dn.node)
if err != nil {
return err
}

// Update OS
if mcDiff.osUpdate {
if err := dn.updateLayeredOS(newConfig); err != nil {
_, newOCLImage := extractOCLImageFromMachineConfig(newConfig)
err = upgrademonitor.GenerateAndApplyMachineConfigNodes(
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeUpdateExecuted, Reason: string(mcfgv1.MachineConfigNodeImagePulledFromRegistry), Message: fmt.Sprintf("Image %s pulled from registry.", newOCLImage)},
&upgrademonitor.Condition{State: mcfgv1.MachineConfigNodeImagePulledFromRegistry, Reason: fmt.Sprintf("%s%s", string(mcfgv1.MachineConfigNodeUpdateExecuted), string(mcfgv1.MachineConfigNodeImagePulledFromRegistry)), Message: fmt.Sprintf("Image %s pulled from registry.", newOCLImage)},
metav1.ConditionUnknown,
metav1.ConditionTrue,
dn.node,
dn.mcfgClient,
dn.featureGatesAccessor,
pool,
)
if err != nil {
klog.Errorf("Error making MCN for Pulling Image from Registry: %v", err)
}

mcdPivotErr.Inc()
return err
}
Expand Down
12 changes: 11 additions & 1 deletion pkg/operator/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,13 @@ func (optr *Operator) syncMachineConfigNodes(_ *renderConfig, _ *configv1.Cluste
},
},
}

if fg.Enabled(features.FeatureGateImageModeStatusReporting) && node.Annotations[daemonconsts.DesiredImageAnnotationKey] != "" {
newMCS.Spec.ConfigImage = &mcfgv1.MachineConfigNodeSpecConfigImage{
DesiredImage: mcfgv1.ImageDigestFormat(node.Annotations[daemonconsts.DesiredImageAnnotationKey]),
}
}

mcsBytes, err := json.Marshal(newMCS)
if err != nil {
klog.Errorf("error rendering asset for MachineConfigNode %v", err)
Expand All @@ -817,15 +824,18 @@ func (optr *Operator) syncMachineConfigNodes(_ *renderConfig, _ *configv1.Cluste
if err != nil {
return err
}

// if this is the first time we are applying the MCN and the node is ready, set the config version probably
// this should also catch the configImage requirements from the GenerateAndApplyMachineConfigNodeSpec function in upgrademonitor (lines 398-400)
if mcn.Spec.ConfigVersion.Desired == upgrademonitor.NotYetSet {
err = upgrademonitor.GenerateAndApplyMachineConfigNodeSpec(optr.fgAccessor, pool, node, optr.client)
if err != nil {
klog.Errorf("Error making MCN spec for Update Compatible: %v", err)
klog.Errorf("Error making MCN Spec: %v", err)
}
}

}

if mcns != nil {
for _, mcn := range mcns.Items {
if _, ok := nodeMap[mcn.Name]; !ok {
Expand Down
73 changes: 64 additions & 9 deletions pkg/upgrademonitor/upgrade_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,14 @@ func generateAndApplyMachineConfigNodes(
mcfgv1.MachineConfigNodeUpdateComplete,
mcfgv1.MachineConfigNodeResumed,
mcfgv1.MachineConfigNodeUpdateDrained,
mcfgv1.MachineConfigNodeUpdateFilesAndOS,
mcfgv1.MachineConfigNodeUpdateFiles,
mcfgv1.MachineConfigNodeUpdateOS,
mcfgv1.MachineConfigNodeUpdateCordoned,
mcfgv1.MachineConfigNodeUpdateRebooted,
mcfgv1.MachineConfigNodeUpdated,
mcfgv1.MachineConfigNodeUpdateUncordoned,
mcfgv1.MachineConfigNodeNodeDegraded,
mcfgv1.MachineConfigNodeImagePulledFromRegistry,
}
allConditionTypes = append(allConditionTypes, singletonConditionTypes...)

Expand Down Expand Up @@ -220,7 +222,11 @@ func generateAndApplyMachineConfigNodes(

case condition.Status != metav1.ConditionFalse && reset:
condition.Status = metav1.ConditionFalse
// ASK: does this message change if OCL is used
condition.Message = fmt.Sprintf("Action during update to %s: %s", newMCNode.Spec.ConfigVersion.Desired, condition.Message)
if fg.Enabled(features.FeatureGateImageModeStatusReporting) {
condition.Message = fmt.Sprintf("")
}
condition.LastTransitionTime = metav1.Now()
}
condition.DeepCopyInto(&newMCNode.Status.Conditions[i])
Expand All @@ -247,12 +253,37 @@ func generateAndApplyMachineConfigNodes(
} else {
newMCNode.Status.ConfigVersion.Desired = desiredAnnotation
}

// Set current version in MCN.Status.ConfigVersion if node annotation exists
if node.Annotations[daemonconsts.CurrentMachineConfigAnnotationKey] != "" {
newMCNode.Status.ConfigVersion.Current = node.Annotations[daemonconsts.CurrentMachineConfigAnnotationKey]
}

// Set desired version in MCN.Status.ConfigImage
desiredImageAnnotation := node.Annotations[daemonconsts.DesiredImageAnnotationKey]
currentImageAnnotation := node.Annotations[daemonconsts.CurrentImageAnnotationKey]
// if the update is compatible, we can set the desired to the one being used in the update,
// otherwise continue using the placeholder value
if desiredImageAnnotation != "" || currentImageAnnotation != "" {
// desiredImageAnnotation := node.Annotations[daemonconsts.DesiredImageAnnotationKey]
if newMCNode.Status.ConfigImage == nil {
newMCNode.Status.ConfigImage = &mcfgv1.MachineConfigNodeStatusConfigImage{
// DesiredImage: mcfgv1.ImageDigestFormat(desiredImageAnnotation),
}
// } else {
// newMCNode.Status.ConfigImage.DesiredImage = mcfgv1.ImageDigestFormat(desiredImageAnnotation)
}
if desiredImageAnnotation != "" {
newMCNode.Status.ConfigImage.DesiredImage = mcfgv1.ImageDigestFormat(desiredImageAnnotation)
}
if currentImageAnnotation != "" {
newMCNode.Status.ConfigImage.CurrentImage = mcfgv1.ImageDigestFormat(currentImageAnnotation)
}
}
// // Set current version in MCN.Status.ConfigImage if node annotation exists
// if node.Annotations[daemonconsts.CurrentImageAnnotationKey] != "" {
// newMCNode.Status.ConfigImage.CurrentImage = mcfgv1.ImageDigestFormat(node.Annotations[daemonconsts.CurrentImageAnnotationKey])
// }

// if we do not need a new MCN, generate the apply configurations for this object
if !needNewMCNode {
statusconfigVersionApplyConfig := machineconfigurationv1.MachineConfigNodeStatusMachineConfigVersion().WithDesired(newMCNode.Status.ConfigVersion.Desired)
Expand Down Expand Up @@ -290,6 +321,14 @@ func generateAndApplyMachineConfigNodes(
}
}

if fg.Enabled(features.FeatureGateImageModeStatusReporting) {
statusConfigImageApplyImage := machineconfigurationv1.MachineConfigNodeStatusConfigImage().WithDesiredImage(newMCNode.Status.ConfigImage.DesiredImage)
if node.Annotations[daemonconsts.CurrentImageAnnotationKey] != "" {
statusConfigImageApplyImage = statusConfigImageApplyImage.WithCurrentImage(newMCNode.Status.ConfigImage.CurrentImage)
}
Comment on lines +325 to +328
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Why does current image have a nil check, but desired does not?


}

mcnodeApplyConfig := machineconfigurationv1.MachineConfigNode(newMCNode.Name).WithStatus(statusApplyConfig)
_, err := mcfgClient.MachineconfigurationV1().MachineConfigNodes().ApplyStatus(context.TODO(), mcnodeApplyConfig, metav1.ApplyOptions{FieldManager: "machine-config-operator", Force: true})
if err != nil {
Expand All @@ -304,6 +343,13 @@ func generateAndApplyMachineConfigNodes(
if newMCNode.Spec.ConfigVersion.Desired == "" {
newMCNode.Spec.ConfigVersion.Desired = NotYetSet
}

if fg.Enabled(features.FeatureGateImageModeStatusReporting) && node.Annotations[daemonconsts.DesiredImageAnnotationKey] != "" {
newMCNode.Spec.ConfigImage = &mcfgv1.MachineConfigNodeSpecConfigImage{
DesiredImage: mcfgv1.ImageDigestFormat(node.Annotations[daemonconsts.DesiredImageAnnotationKey]),
}

}
newMCNode.Name = node.Name
newMCNode.Spec.Pool = mcfgv1.MCOObjectReference{Name: pool}
newMCNode.Spec.Node = mcfgv1.MCOObjectReference{Name: node.Name}
Expand Down Expand Up @@ -368,22 +414,25 @@ func GenerateAndApplyMachineConfigNodeSpec(fgAccessor featuregates.FeatureGateAc
newMCNode.Spec.ConfigVersion = mcfgv1.MachineConfigNodeSpecMachineConfigVersion{
Desired: node.Annotations[daemonconsts.DesiredMachineConfigAnnotationKey],
}
// Set desired config to NotYetSet if the annotation is empty to satisfy API validation
if newMCNode.Spec.ConfigVersion.Desired == "" {
newMCNode.Spec.ConfigVersion.Desired = NotYetSet
}

newMCNode.Spec.Pool = mcfgv1.MCOObjectReference{
Name: pool,
}
newMCNode.Spec.Node = mcfgv1.MCOObjectReference{
Name: node.Name,
}
if daemonconsts.DesiredImageAnnotationKey != "" && fg.Enabled(features.FeatureGateImageModeStatusReporting) {
newMCNode.Spec.ConfigImage = &mcfgv1.MachineConfigNodeSpecConfigImage{
DesiredImage: daemonconsts.DesiredImageAnnotationKey,
}
}

if !needNewMCNode {
nodeRefApplyConfig := machineconfigurationv1.MCOObjectReference().WithName(newMCNode.Spec.Node.Name)
poolRefApplyConfig := machineconfigurationv1.MCOObjectReference().WithName(newMCNode.Spec.Pool.Name)
specconfigVersionApplyConfig := machineconfigurationv1.MachineConfigNodeSpecMachineConfigVersion().WithDesired(newMCNode.Spec.ConfigVersion.Desired)
specApplyConfig := machineconfigurationv1.MachineConfigNodeSpec().WithNode(nodeRefApplyConfig).WithPool(poolRefApplyConfig).WithConfigVersion(specconfigVersionApplyConfig)
specConfigImageApplyConfig := machineconfigurationv1.MachineConfigNodeSpecConfigImage().WithDesiredImage(newMCNode.Spec.ConfigImage.DesiredImage)
specApplyConfig := machineconfigurationv1.MachineConfigNodeSpec().WithNode(nodeRefApplyConfig).WithPool(poolRefApplyConfig).WithConfigVersion(specconfigVersionApplyConfig).WithConfigImage(specConfigImageApplyConfig)
mcnodeApplyConfig := machineconfigurationv1.MachineConfigNode(newMCNode.Name).WithSpec(specApplyConfig)
_, err := mcfgClient.MachineconfigurationV1().MachineConfigNodes().Apply(context.TODO(), mcnodeApplyConfig, metav1.ApplyOptions{FieldManager: "machine-config-operator", Force: true})
if err != nil {
Expand All @@ -397,17 +446,23 @@ func GenerateAndApplyMachineConfigNodeSpec(fgAccessor featuregates.FeatureGateAc
return err
}
}

return nil
}

// createOrGetMachineConfigNode gets the named MCN or returns a boolean indicating we need to create one
func createOrGetMachineConfigNode(mcfgClient mcfgclientset.Interface, node *corev1.Node) (*mcfgv1.MachineConfigNode, bool) {
mcNode, err := mcfgClient.MachineconfigurationV1().MachineConfigNodes().Get(context.TODO(), node.Name, metav1.GetOptions{})
if mcNode.Name == "" || (err != nil && apierrors.IsNotFound(err)) {
if err != nil {
// no existing MCN found since no resource found, no error yet just create a new one
if apierrors.IsNotFound((err)) {
klog.V(4).Infof("MachineConfigNode for node %q not found, will create a new one", node.Name)
return mcNode, true
}
// true error getting existing MCN
klog.Errorf("error getting existing MCN: %v", err)
return mcNode, true
}

return mcNode, false
}

Expand Down
2 changes: 1 addition & 1 deletion vendor/github.com/openshift/api/.ci-operator.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions vendor/github.com/openshift/api/.golangci.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions vendor/github.com/openshift/api/Dockerfile.ocp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions vendor/github.com/openshift/api/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions vendor/github.com/openshift/api/config/v1/types_apiserver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading