Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: Accessing old object on watchEvent #517

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
57 changes: 53 additions & 4 deletions docs/src/KUBERNETES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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"
}
}
},
Comment on lines +28 to +75
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"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"
}
}
},

This is not the right doc. The doc is about Kubernetes object modification, not about binding contexts.
The right doc is here https:/flant/shell-operator/blob/main/docs/src/HOOKS.md#kubernetes-binding-context-example

"object": {
"apiVersion": "apps/v1",
"kind": "DaemonSet",
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
19 changes: 14 additions & 5 deletions pkg/kube_events_manager/resource_informer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
nabokihms marked this conversation as resolved.
Show resolved Hide resolved
}
nabokihms marked this conversation as resolved.
Show resolved Hide resolved

resourceId := ResourceId(obj)

// Always calculate checksum and update cache, because we need an actual state in ei.cachedObjects.
Expand All @@ -319,6 +324,10 @@ func (ei *resourceInformer) HandleWatchEvent(object interface{}, eventType Watch

if !ei.Monitor.KeepFullObjectsInMemory {
objFilterRes.RemoveFullObject()
} else if ei.Monitor.KeepFullObjectsInMemory {
if oldObj != nil {
objFilterRes.OldObject = oldObj
}
nabokihms marked this conversation as resolved.
Show resolved Hide resolved
}

// Do not fire Added or Modified if object is in cache and its checksum is equal to the newChecksum.
Expand Down
1 change: 1 addition & 0 deletions pkg/kube_events_manager/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
}

Expand Down
Loading