@@ -20,6 +20,10 @@ import (
2020 corev1 "k8s.io/api/core/v1"
2121 apierrors "k8s.io/apimachinery/pkg/api/errors"
2222 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+ "k8s.io/apimachinery/pkg/runtime"
24+ "k8s.io/apimachinery/pkg/runtime/schema"
25+ "k8s.io/apimachinery/pkg/runtime/serializer"
26+ jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json"
2327 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2428 "k8s.io/client-go/tools/record"
2529 "k8s.io/utils/ptr"
@@ -43,6 +47,16 @@ import (
4347
4448const lastAppliedOperatingSystemConfigFilePath = nodeagentv1alpha1 .BaseDir + "/last-applied-osc.yaml"
4549
50+ var codec runtime.Codec
51+
52+ func init () {
53+ scheme := runtime .NewScheme ()
54+ utilruntime .Must (extensionsv1alpha1 .AddToScheme (scheme ))
55+ ser := jsonserializer .NewSerializerWithOptions (jsonserializer .DefaultMetaFactory , scheme , scheme , jsonserializer.SerializerOptions {Yaml : true , Pretty : false , Strict : false })
56+ versions := schema .GroupVersions ([]schema.GroupVersion {nodeagentv1alpha1 .SchemeGroupVersion , extensionsv1alpha1 .SchemeGroupVersion })
57+ codec = serializer .NewCodecFactory (scheme ).CodecForVersions (ser , ser , versions , versions )
58+ }
59+
4660// Reconciler decodes the OperatingSystemConfig resources from secrets and applies the systemd units and files to the
4761// node.
4862type Reconciler struct {
@@ -85,11 +99,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
8599 return reconcile.Result {}, nil
86100 }
87101
88- osc , oscRaw , oscChecksum , err := extractOSCFromSecret (secret )
102+ osc , oscChecksum , err := extractOSCFromSecret (secret )
89103 if err != nil {
90104 return reconcile.Result {}, fmt .Errorf ("failed extracting OSC from secret: %w" , err )
91105 }
92106
107+ log .Info ("Applying containerd configuration" )
108+ if err := r .ReconcileContainerdConfig (ctx , log , osc ); err != nil {
109+ return reconcile.Result {}, fmt .Errorf ("failed reconciling containerd configuration: %w" , err )
110+ }
111+
93112 oscChanges , err := computeOperatingSystemConfigChanges (r .FS , osc )
94113 if err != nil {
95114 return reconcile.Result {}, fmt .Errorf ("failed calculating the OSC changes: %w" , err )
@@ -100,11 +119,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
100119 return reconcile.Result {}, nil
101120 }
102121
103- log .Info ("Applying containerd configuration" )
104- if err := r .ReconcileContainerdConfig (ctx , log , osc .Spec .CRIConfig ); err != nil {
105- return reconcile.Result {}, fmt .Errorf ("failed reconciling containerd configuration: %w" , err )
106- }
107-
108122 log .Info ("Applying new or changed inline files" )
109123 if err := r .applyChangedInlineFiles (log , oscChanges .files .changed ); err != nil {
110124 return reconcile.Result {}, fmt .Errorf ("failed applying changed inline files: %w" , err )
@@ -170,7 +184,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
170184 )
171185
172186 log .Info ("Persisting current operating system config as 'last-applied' file to the disk" , "path" , lastAppliedOperatingSystemConfigFilePath )
173- if err := r .FS .WriteFile (lastAppliedOperatingSystemConfigFilePath , oscRaw , 0644 ); err != nil {
187+ oscRaw , err := runtime .Encode (codec , osc )
188+ if err != nil {
189+ return reconcile.Result {}, fmt .Errorf ("unable to encode OSC: %w" , err )
190+ }
191+
192+ if err := r .FS .WriteFile (lastAppliedOperatingSystemConfigFilePath , oscRaw , 0600 ); err != nil {
174193 return reconcile.Result {}, fmt .Errorf ("unable to write current OSC to file path %q: %w" , lastAppliedOperatingSystemConfigFilePath , err )
175194 }
176195
@@ -387,14 +406,21 @@ func (r *Reconciler) applyChangedUnits(ctx context.Context, log logr.Logger, uni
387406
388407func (r * Reconciler ) removeDeletedUnits (ctx context.Context , log logr.Logger , node client.Object , units []extensionsv1alpha1.Unit ) error {
389408 for _ , unit := range units {
409+ // The unit has been created by gardener-node-agent if it has content.
410+ // Otherwise, it might be a default OS unit which was enabled/disabled or where drop-ins were added.
411+ unitCreatedByNodeAgent := unit .Content != nil
412+
390413 unitFilePath := path .Join (etcSystemdSystem , unit .Name )
391414
392415 unitFileExists , err := r .FS .Exists (unitFilePath )
393416 if err != nil {
394417 return fmt .Errorf ("unable to check whether unit file %q exists: %w" , unitFilePath , err )
395418 }
396419
397- if unitFileExists {
420+ // Only stop and remove the unit file if it was created by gardener-node-agent. Otherwise, this could affect
421+ // default OS units where we add and remove drop-ins only. If operators want to stop and disable units,
422+ // they can do it by adding a unit to OSC which applies the `stop` command.
423+ if unitFileExists && unitCreatedByNodeAgent {
398424 if err := r .DBus .Disable (ctx , unit .Name ); err != nil {
399425 return fmt .Errorf ("unable to disable deleted unit %q: %w" , unit .Name , err )
400426 }
@@ -405,11 +431,37 @@ func (r *Reconciler) removeDeletedUnits(ctx context.Context, log logr.Logger, no
405431
406432 if err := r .FS .Remove (unitFilePath ); err != nil && ! errors .Is (err , afero .ErrFileNotFound ) {
407433 return fmt .Errorf ("unable to delete systemd unit file of deleted unit %q: %w" , unit .Name , err )
434+ } else {
435+ log .Info ("Unit was not created by gardener-node-agent, skipping deletion of unit file" , "unitName" , unit .Name )
436+ }
437+ }
438+
439+ dropInFolder := unitFilePath + ".d"
440+
441+ if exists , err := r .FS .Exists (dropInFolder ); err != nil {
442+ return fmt .Errorf ("unable to check whether drop-in folder %q exists: %w" , dropInFolder , err )
443+ } else if exists {
444+ for _ , dropIn := range unit .DropIns {
445+ dropInFilePath := path .Join (dropInFolder , dropIn .Name )
446+ if err := r .FS .Remove (dropInFilePath ); err != nil && ! errors .Is (err , afero .ErrFileNotFound ) {
447+ return fmt .Errorf ("unable to delete drop-in file %q of deleted unit %q: %w" , dropInFilePath , unit .Name , err )
448+ }
449+ }
450+
451+ if empty , err := r .FS .IsEmpty (dropInFolder ); err != nil {
452+ return fmt .Errorf ("unable to check whether drop-in folder %q is empty: %w" , dropInFolder , err )
453+ } else if empty {
454+ if err := r .FS .RemoveAll (dropInFolder ); err != nil && ! errors .Is (err , afero .ErrFileNotFound ) {
455+ return fmt .Errorf ("unable to delete systemd drop-in folder of deleted unit %q: %w" , unit .Name , err )
456+ }
408457 }
409458 }
410459
411- if err := r .FS .RemoveAll (unitFilePath + ".d" ); err != nil && ! errors .Is (err , afero .ErrFileNotFound ) {
412- return fmt .Errorf ("unable to delete systemd drop-in folder of deleted unit %q: %w" , unit .Name , err )
460+ // If the unit was not created by gardener-node-agent, but it exists on the node and was removed from OSC. Restart it to apply changes.
461+ if unitFileExists && ! unitCreatedByNodeAgent {
462+ if err := r .DBus .Restart (ctx , r .Recorder , node , unit .Name ); err != nil {
463+ return fmt .Errorf ("unable to restart unit %q removed from OSC but not created by gardener-node-agent: %w" , unit .Name , err )
464+ }
413465 }
414466
415467 log .Info ("Successfully removed no longer needed unit" , "unitName" , unit .Name )
0 commit comments