Skip to content

Commit cd32621

Browse files
RishabhSainiisabella-janssen
authored andcommitted
helpers: compute systemdUnits that added and changed between Ign configs
update: only write added or updated systemd units unless forcefile exists then rewrite all systemd units on_disk_validation: check if systemd units in config enabled correctly tests: update MCN condition transition test to account for quick transitions through 'Unknown' state (cherry picked from commit 4512ec4)
1 parent 91ec81a commit cd32621

3 files changed

Lines changed: 93 additions & 20 deletions

File tree

pkg/controller/common/helpers.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,12 +1141,25 @@ func CalculateConfigFileDiffs(oldIgnConfig, newIgnConfig *ign3types.Config) []st
11411141
return diffFileSet
11421142
}
11431143

1144-
// CalculateConfigUnitDiffs compares the units present in two ignition configurations and returns the list of units
1145-
// that are different between them
1146-
//
1147-
//nolint:dupl
1148-
func CalculateConfigUnitDiffs(oldIgnConfig, newIgnConfig *ign3types.Config) []string {
1149-
// Go through the units and see what is new or different
1144+
type UnitDiff struct {
1145+
Added []ign3types.Unit
1146+
Removed []ign3types.Unit
1147+
Updated []ign3types.Unit
1148+
}
1149+
1150+
// GetChangedConfigUnitsByType compares the units present in two ignition configurations, one
1151+
// old config and the other new, a struct with units mapped to the type of change:
1152+
// - New unit added: "Added"
1153+
// - Unit previously existing removed: "Removed"
1154+
// - Existing unit changed in some way: "Updated"
1155+
func GetChangedConfigUnitsByType(oldIgnConfig, newIgnConfig *ign3types.Config) (unitDiffs UnitDiff) {
1156+
diffUnit := UnitDiff{
1157+
Added: []ign3types.Unit{},
1158+
Removed: []ign3types.Unit{},
1159+
Updated: []ign3types.Unit{},
1160+
}
1161+
1162+
// Get the sets of the old and new units from the ignition configurations
11501163
oldUnitSet := make(map[string]ign3types.Unit)
11511164
for _, u := range oldIgnConfig.Systemd.Units {
11521165
oldUnitSet[u.Name] = u
@@ -1155,26 +1168,25 @@ func CalculateConfigUnitDiffs(oldIgnConfig, newIgnConfig *ign3types.Config) []st
11551168
for _, u := range newIgnConfig.Systemd.Units {
11561169
newUnitSet[u.Name] = u
11571170
}
1158-
diffUnitSet := []string{}
11591171

11601172
// First check if any units were removed
11611173
for unit := range oldUnitSet {
11621174
_, ok := newUnitSet[unit]
11631175
if !ok {
1164-
diffUnitSet = append(diffUnitSet, unit)
1176+
diffUnit.Removed = append(diffUnit.Removed, oldUnitSet[unit])
11651177
}
11661178
}
11671179

1168-
// Now check if any units were added/changed
1180+
// Now check if any units were added or updated
11691181
for name, newUnit := range newUnitSet {
11701182
oldUnit, ok := oldUnitSet[name]
11711183
if !ok {
1172-
diffUnitSet = append(diffUnitSet, name)
1184+
diffUnit.Added = append(diffUnit.Added, newUnitSet[name])
11731185
} else if !reflect.DeepEqual(oldUnit, newUnit) {
1174-
diffUnitSet = append(diffUnitSet, name)
1186+
diffUnit.Updated = append(diffUnit.Updated, newUnitSet[name])
11751187
}
11761188
}
1177-
return diffUnitSet
1189+
return diffUnit
11781190
}
11791191

11801192
// NewIgnFile returns a simple ignition3 file from just path and file contents.

pkg/daemon/on_disk_validation.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8+
"strings"
89

910
ign2types "github.com/coreos/ignition/config/v2_2/types"
1011
ign3types "github.com/coreos/ignition/v2/config/v3_5/types"
@@ -100,7 +101,12 @@ func checkV3Unit(unit ign3types.Unit, systemdPath string) error {
100101
return nil
101102
}
102103

103-
return checkFileContentsAndMode(path, []byte(*unit.Contents), defaultFilePermissions)
104+
err := checkFileContentsAndMode(path, []byte(*unit.Contents), defaultFilePermissions)
105+
if err != nil {
106+
return err
107+
}
108+
109+
return checkUnitEnabled(unit.Name, unit.Enabled)
104110
}
105111

106112
// checkV3Units validates the contents of all the units in the
@@ -230,6 +236,29 @@ func checkV2Files(files []ign2types.File) error {
230236
return nil
231237
}
232238

239+
// checkUnitEnabled checks whether a systemd unit is enabled as expected.
240+
func checkUnitEnabled(name string, expectedEnabled *bool) error {
241+
if expectedEnabled == nil {
242+
return nil
243+
}
244+
outBytes, _ := runGetOut("systemctl", "is-enabled", name)
245+
out := strings.TrimSpace(string(outBytes))
246+
247+
switch {
248+
case *expectedEnabled:
249+
// If expected to be enabled, reject known "not enabled" states
250+
if out == "disabled" || out == "masked" || out == "masked-runtime" || out == "not-found" {
251+
return fmt.Errorf("unit %q expected enabled=true, but systemd reports %q", name, out)
252+
}
253+
case !*expectedEnabled:
254+
// If expected to be disabled, reject any of the enabled-like states
255+
if out == "enabled" || out == "enabled-runtime" {
256+
return fmt.Errorf("unit %q expected enabled=false, but systemd reports %q", name, out)
257+
}
258+
}
259+
return nil
260+
}
261+
233262
// checkFileContentsAndMode reads the file from the filepath and compares its
234263
// contents and mode with the expectedContent and mode parameters. It logs an
235264
// error in case of an error or mismatch and returns the status of the

pkg/daemon/update.go

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"path/filepath"
1313
"reflect"
1414
goruntime "runtime"
15+
"slices"
1516
"strconv"
1617
"strings"
1718
"sync"
@@ -1233,7 +1234,14 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig, skipCertifi
12331234
logSystem("Starting update from %s to %s: %+v", oldConfigName, newConfigName, diff)
12341235

12351236
diffFileSet := ctrlcommon.CalculateConfigFileDiffs(&oldIgnConfig, &newIgnConfig)
1236-
diffUnitSet := ctrlcommon.CalculateConfigUnitDiffs(&oldIgnConfig, &newIgnConfig)
1237+
// Get the added and updated units
1238+
unitDiff := ctrlcommon.GetChangedConfigUnitsByType(&oldIgnConfig, &newIgnConfig)
1239+
addedOrChangedUnits := slices.Concat(unitDiff.Added, unitDiff.Updated)
1240+
// Get the names of all units changed in some way (added, removed, or updated)
1241+
var allChangedUnitNames []string
1242+
for _, unit := range append(addedOrChangedUnits, unitDiff.Removed...) {
1243+
allChangedUnitNames = append(allChangedUnitNames, unit.Name)
1244+
}
12371245

12381246
var fg featuregates.FeatureGate
12391247

@@ -1247,6 +1255,10 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig, skipCertifi
12471255
}
12481256
}
12491257

1258+
// Check for forcefile before calculatePostConfigChange* functions delete it.
1259+
// This is needed for updateFiles to know whether to write all units (OCPBUGS-74692).
1260+
forceFilePresent := forceFileExists()
1261+
12501262
var nodeDisruptionActions []opv1.NodeDisruptionPolicyStatusAction
12511263
var actions []string
12521264
// If FeatureGateNodeDisruptionPolicy is set, calculate NodeDisruptionPolicy based actions for this MC diff
@@ -1359,13 +1371,13 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig, skipCertifi
13591371
}
13601372

13611373
// update files on disk that need updating
1362-
if err := dn.updateFiles(oldIgnConfig, newIgnConfig, skipCertificateWrite); err != nil {
1374+
if err := dn.updateFiles(oldIgnConfig, newIgnConfig, addedOrChangedUnits, skipCertificateWrite, forceFilePresent); err != nil {
13631375
return err
13641376
}
13651377

13661378
defer func() {
13671379
if retErr != nil {
1368-
if err := dn.updateFiles(newIgnConfig, oldIgnConfig, skipCertificateWrite); err != nil {
1380+
if err := dn.updateFiles(newIgnConfig, oldIgnConfig, addedOrChangedUnits, skipCertificateWrite, false); err != nil {
13691381
errs := kubeErrs.NewAggregate([]error{err, retErr})
13701382
retErr = fmt.Errorf("error rolling back files writes: %w", errs)
13711383
return
@@ -1491,15 +1503,21 @@ func (dn *Daemon) updateHypershift(oldConfig, newConfig *mcfgv1.MachineConfig, d
14911503
return fmt.Errorf("parsing new Ignition config failed: %w", err)
14921504
}
14931505

1506+
unitDiff := ctrlcommon.GetChangedConfigUnitsByType(&oldIgnConfig, &newIgnConfig)
1507+
addedOrChangedUnits := slices.Concat(unitDiff.Added, unitDiff.Updated)
1508+
1509+
// Check for forcefile to support config drift recovery (OCPBUGS-74692)
1510+
forceFilePresent := forceFileExists()
1511+
14941512
// update files on disk that need updating
14951513
// We should't skip the certificate write in HyperShift since it does not run the extra daemon process
1496-
if err := dn.updateFiles(oldIgnConfig, newIgnConfig, false); err != nil {
1514+
if err := dn.updateFiles(oldIgnConfig, newIgnConfig, addedOrChangedUnits, false, forceFilePresent); err != nil {
14971515
return err
14981516
}
14991517

15001518
defer func() {
15011519
if retErr != nil {
1502-
if err := dn.updateFiles(newIgnConfig, oldIgnConfig, false); err != nil {
1520+
if err := dn.updateFiles(newIgnConfig, oldIgnConfig, addedOrChangedUnits, false, false); err != nil {
15031521
errs := kubeErrs.NewAggregate([]error{err, retErr})
15041522
retErr = fmt.Errorf("error rolling back files writes: %w", errs)
15051523
return
@@ -1990,12 +2008,26 @@ func (dn *CoreOSDaemon) switchKernel(oldConfig, newConfig *mcfgv1.MachineConfig)
19902008
// whatever has been written is picked up by the appropriate daemons, if
19912009
// required. in particular, a daemon-reload and restart for any unit files
19922010
// touched.
1993-
func (dn *Daemon) updateFiles(oldIgnConfig, newIgnConfig ign3types.Config, skipCertificateWrite bool) error {
2011+
func (dn *Daemon) updateFiles(oldIgnConfig, newIgnConfig ign3types.Config, addedOrChangedUnits []ign3types.Unit, skipCertificateWrite, forceFilePresent bool) error {
19942012
klog.Info("Updating files")
19952013
if err := dn.writeFiles(newIgnConfig.Storage.Files, skipCertificateWrite); err != nil {
19962014
return err
19972015
}
1998-
if err := dn.writeUnits(newIgnConfig.Systemd.Units); err != nil {
2016+
2017+
// With OCPBUGS-58023, we updated this flow to only write units that were either added or
2018+
// updated. As can be seen in OCPBUGS-74692, this impacted the traditional method to recover
2019+
// from config drifts with systemd units. It makes the `touch /run/machine-config-daemon-force`
2020+
// command useless since the new flow does not rewrite all files, only the ones that have been
2021+
// added or changed with the latest MC. To keep the fix for OCPBUGS-58023 and allow continue
2022+
// supporting the traditional config drift recovery for systemd units, all units should be
2023+
// written when a forcefile exists.
2024+
unitsToWrite := addedOrChangedUnits
2025+
if forceFilePresent {
2026+
klog.Info("Forcefile exists, writing all units")
2027+
unitsToWrite = newIgnConfig.Systemd.Units
2028+
}
2029+
2030+
if err := dn.writeUnits(unitsToWrite); err != nil {
19992031
return err
20002032
}
20012033
return dn.deleteStaleData(oldIgnConfig, newIgnConfig)

0 commit comments

Comments
 (0)