Commit ebc397b6 authored by Donnie Adams's avatar Donnie Adams Committed by Brad Davidson

Remove objects when removed from manifests (#5560)

* Remove objects when removed from manifests If a user puts a file in /var/lib/rancher/k3s/server/manifests/ then the objects contained therein are deployed to the cluster. If the objects are removed from that file, they are not removed from the cluster. This change tracks the GVKs in the files and will remove objects when there are removed from the cluster. Signed-off-by: 's avatarDonnie Adams <donnie.adams@suse.com> (cherry picked from commit c38a8c3b) Signed-off-by: 's avatarBrad Davidson <brad.davidson@rancher.com>
parent e6385b23
...@@ -28,7 +28,6 @@ import ( ...@@ -28,7 +28,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
yamlDecoder "k8s.io/apimachinery/pkg/util/yaml" yamlDecoder "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
...@@ -166,7 +165,6 @@ func (w *watcher) deploy(path string, compareChecksum bool) error { ...@@ -166,7 +165,6 @@ func (w *watcher) deploy(path string, compareChecksum bool) error {
} }
addon.Spec.Source = path addon.Spec.Source = path
addon.Status.GVKs = nil
// Create the new Addon now so that we can use it to report Events when parsing/applying the manifest // Create the new Addon now so that we can use it to report Events when parsing/applying the manifest
// Events need the UID and ObjectRevision set to function properly // Events need the UID and ObjectRevision set to function properly
...@@ -192,7 +190,7 @@ func (w *watcher) deploy(path string, compareChecksum bool) error { ...@@ -192,7 +190,7 @@ func (w *watcher) deploy(path string, compareChecksum bool) error {
// Attempt to parse the YAML/JSON into objects. Failure at this point would be due to bad file content - not YAML/JSON, // Attempt to parse the YAML/JSON into objects. Failure at this point would be due to bad file content - not YAML/JSON,
// YAML/JSON that can't be converted to Kubernetes objects, etc. // YAML/JSON that can't be converted to Kubernetes objects, etc.
objectSet, err := objectSet(content) objects, err := objectSet(content)
if err != nil { if err != nil {
w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ParseManifestFailed", "Parse manifest at %q failed: %v", path, err) w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ParseManifestFailed", "Parse manifest at %q failed: %v", path, err)
return err return err
...@@ -200,8 +198,11 @@ func (w *watcher) deploy(path string, compareChecksum bool) error { ...@@ -200,8 +198,11 @@ func (w *watcher) deploy(path string, compareChecksum bool) error {
// Attempt to apply the changes. Failure at this point would be due to more complicated issues - invalid changes to // Attempt to apply the changes. Failure at this point would be due to more complicated issues - invalid changes to
// existing objects, rejected by validating webhooks, etc. // existing objects, rejected by validating webhooks, etc.
// WithGVK searches for objects using both GVKs currently listed in the manifest, as well as GVKs previously
// applied. This ensures that objects don't get orphaned when they are removed from the file - if the apply
// doesn't know to search that GVK for owner references, it won't find and delete them.
w.recorder.Eventf(&addon, corev1.EventTypeNormal, "ApplyingManifest", "Applying manifest at %q", path) w.recorder.Eventf(&addon, corev1.EventTypeNormal, "ApplyingManifest", "Applying manifest at %q", path)
if err := w.apply.WithOwner(&addon).Apply(objectSet); err != nil { if err := w.apply.WithOwner(&addon).WithGVK(addon.Status.GVKs...).Apply(objects); err != nil {
w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ApplyManifestFailed", "Applying manifest at %q failed: %v", path, err) w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ApplyManifestFailed", "Applying manifest at %q failed: %v", path, err)
return err return err
} }
...@@ -209,6 +210,7 @@ func (w *watcher) deploy(path string, compareChecksum bool) error { ...@@ -209,6 +210,7 @@ func (w *watcher) deploy(path string, compareChecksum bool) error {
// Emit event, Update Addon checksum only if apply was successful // Emit event, Update Addon checksum only if apply was successful
w.recorder.Eventf(&addon, corev1.EventTypeNormal, "AppliedManifest", "Applied manifest at %q", path) w.recorder.Eventf(&addon, corev1.EventTypeNormal, "AppliedManifest", "Applied manifest at %q", path)
addon.Spec.Checksum = checksum addon.Spec.Checksum = checksum
addon.Status.GVKs = objects.GVKs()
_, err = w.addons.Update(&addon) _, err = w.addons.Update(&addon)
return err return err
} }
...@@ -225,17 +227,14 @@ func (w *watcher) delete(path string) error { ...@@ -225,17 +227,14 @@ func (w *watcher) delete(path string) error {
content, err := ioutil.ReadFile(path) content, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ReadManifestFailed", "Read manifest at %q failed: %v", path, err) w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ReadManifestFailed", "Read manifest at %q failed: %v", path, err)
return err } else {
} if o, err := objectSet(content); err != nil {
w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ParseManifestFailed", "Parse manifest at %q failed: %v", path, err)
objectSet, err := objectSet(content) } else {
if err != nil { // Search for objects using both GVKs currently listed in the file, as well as GVKs previously applied.
w.recorder.Eventf(&addon, corev1.EventTypeWarning, "ParseManifestFailed", "Parse manifest at %q failed: %v", path, err) // This ensures that any conflicts between competing deploy controllers are handled properly.
return err addon.Status.GVKs = append(addon.Status.GVKs, o.GVKs()...)
} }
var gvk []schema.GroupVersionKind
for k := range objectSet.ObjectsByGVK() {
gvk = append(gvk, k)
} }
// ensure that the addon is completely removed before deleting the objectSet, // ensure that the addon is completely removed before deleting the objectSet,
...@@ -246,7 +245,7 @@ func (w *watcher) delete(path string) error { ...@@ -246,7 +245,7 @@ func (w *watcher) delete(path string) error {
} }
// apply an empty set with owner & gvk data to delete // apply an empty set with owner & gvk data to delete
if err := w.apply.WithOwner(&addon).WithGVK(gvk...).Apply(nil); err != nil { if err := w.apply.WithOwner(&addon).WithGVK(addon.Status.GVKs...).ApplyObjects(); err != nil {
return err return err
} }
...@@ -272,9 +271,7 @@ func objectSet(content []byte) (*objectset.ObjectSet, error) { ...@@ -272,9 +271,7 @@ func objectSet(content []byte) (*objectset.ObjectSet, error) {
return nil, err return nil, err
} }
os := objectset.NewObjectSet() return objectset.NewObjectSet(objs...), nil
os.Add(objs...)
return os, nil
} }
// basename returns a file's basename by returning everything before the first period // basename returns a file's basename by returning everything before the first period
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment