diff --git a/docs/src/KUBERNETES.md b/docs/src/KUBERNETES.md index 402aa4e7..81c4633b 100644 --- a/docs/src/KUBERNETES.md +++ b/docs/src/KUBERNETES.md @@ -12,11 +12,11 @@ The path to the file is found in the `$KUBERNETES_PATCH_PATH` environment variab ### Create * `operation` — specifies an operation's type. - * `CreateOrUpdate` — accept a Kubernetes object. + * `CreateOrUpdate` — accept a Kubernetes object. It retrieves an object, and if it already exists, computes a JSON Merge Patch and applies it (will not update .status field). If it does not exist, we create the object. - * `Create` — will fail if an object already exists - * `CreateIfNotExists` — create an object if such an object does not already + * `Create` — will fail if an object already exists + * `CreateIfNotExists` — create an object if such an object does not already exist by namespace/name. * `object` — full object specification including "apiVersion", "kind" and all necessary metadata. Can be a normal JSON or YAML object or a stringified JSON or YAML object. @@ -25,6 +25,54 @@ The path to the file is found in the `$KUBERNETES_PATCH_PATH` environment variab ```json { "operation": "CreateOrUpdate", + "oldObject": { + "apiVersion": "apps/v1", + "kind": "DaemonSet", + "metadata": { + "name": "flannel", + "namespace": "d8-flannel" + }, + "spec": { + "selector": { + "matchLabels": { + "app": "flannel" + } + }, + "template": { + "metadata": { + "labels": { + "app": "flannel", + "tier": "old-node" + } + }, + "spec": { + "containers": [ + { + "args": [ + "--ip-masq", + "--kube-subnet-mgr" + ], + "image": "flannel:v0.11", + "name": "kube-flannel", + "securityContext": { + "privileged": true + } + } + ], + "hostNetwork": true, + "imagePullSecrets": [ + { + "name": "registry" + } + ], + "terminationGracePeriodSeconds": 5 + } + }, + "updateStrategy": { + "type": "RollingUpdate" + } + } + }, "object": { "apiVersion": "apps/v1", "kind": "DaemonSet", @@ -123,7 +171,7 @@ object: | ### Patch -Use `JQPatch` for almost everything. Consider using `MergePatch` or `JSONPatch` if you are attempting to modify +Use `JQPatch` for almost everything. Consider using `MergePatch` or `JSONPatch` if you are attempting to modify rapidly changing object, for example `status` field with many concurrent changes (and incrementing `resourceVersion`). Be careful, when updating a `.status` field. If a `/status` subresource is enabled on a resource, @@ -140,6 +188,7 @@ More info [here][spec-and-status]. * `name` — object's name. * `jqFilter` — describes transformations to perform on an object. * `subresource` — a subresource name if subresource is to be transformed. For example, `status`. + ##### Example ```json diff --git a/pkg/kube_events_manager/resource_informer.go b/pkg/kube_events_manager/resource_informer.go index db75f57f..e66d7a7e 100644 --- a/pkg/kube_events_manager/resource_informer.go +++ b/pkg/kube_events_manager/resource_informer.go @@ -266,22 +266,22 @@ func (ei *resourceInformer) LoadExistedObjects() error { } func (ei *resourceInformer) OnAdd(obj interface{}) { - ei.HandleWatchEvent(obj, WatchEventAdded) + ei.HandleWatchEvent(nil, obj, WatchEventAdded) } -func (ei *resourceInformer) OnUpdate(_, newObj interface{}) { - ei.HandleWatchEvent(newObj, WatchEventModified) +func (ei *resourceInformer) OnUpdate(oldObj, newObj interface{}) { + ei.HandleWatchEvent(oldObj, newObj, WatchEventModified) } func (ei *resourceInformer) OnDelete(obj interface{}) { - ei.HandleWatchEvent(obj, WatchEventDeleted) + ei.HandleWatchEvent(nil, obj, WatchEventDeleted) } // HandleKubeEvent register object in cache. Pass object to callback if object's checksum is changed. // TODO refactor: pass KubeEvent as argument // TODO add delay to merge Added and Modified events (node added and then labels applied — one hook run on Added+Modified is enough) // func (ei *resourceInformer) HandleKubeEvent(obj *unstructured.Unstructured, objectId string, filterResult string, newChecksum string, eventType WatchEventType) { -func (ei *resourceInformer) HandleWatchEvent(object interface{}, eventType WatchEventType) { +func (ei *resourceInformer) HandleWatchEvent(oldObject, object interface{}, eventType WatchEventType) { // check if stop if ei.stopped { return @@ -297,6 +297,11 @@ func (ei *resourceInformer) HandleWatchEvent(object interface{}, eventType Watch } obj := object.(*unstructured.Unstructured) + var oldObj *unstructured.Unstructured + if oldObject != nil { + oldObj = oldObject.(*unstructured.Unstructured) + } + resourceId := ResourceId(obj) // Always calculate checksum and update cache, because we need an actual state in ei.cachedObjects. @@ -319,6 +324,8 @@ func (ei *resourceInformer) HandleWatchEvent(object interface{}, eventType Watch if !ei.Monitor.KeepFullObjectsInMemory { objFilterRes.RemoveFullObject() + } else if oldObj != nil { + objFilterRes.OldObject = oldObj } // Do not fire Added or Modified if object is in cache and its checksum is equal to the newChecksum. diff --git a/pkg/kube_events_manager/types/types.go b/pkg/kube_events_manager/types/types.go index 63f53b83..64878487 100644 --- a/pkg/kube_events_manager/types/types.go +++ b/pkg/kube_events_manager/types/types.go @@ -41,6 +41,7 @@ type ObjectAndFilterResult struct { RemoveObject bool } Object *unstructured.Unstructured // here is a pointer because of MarshalJSON receiver + OldObject *unstructured.Unstructured // here is a pointer because of MarshalJSON receiver and also it's nullable for some events FilterResult interface{} }