Way to track changes in kubernetes object
Introduction
Laymen explanation
If you have created kubernetes app for processing user provided configuration file(YAML), you must be interested in tracking any edit made by user in this configuration file. For example, consider that this app is acting as ingress controller. If user wants to change the IP address of its ingress resource, your app must reconfigure the new IP in ingress device. But, before that you should be able to identify that user has really made change in IP.
One way will be to maintain previous configuration and compare with new one. Do you really want to do this?
- Will it not make your app heavy-weight?
- What if your app reboots and meanwhile (time taken in reboot) user edited the IP address?
- Will you maintain persistent storage? If yes, it will be anti-pattern.
Are you confused? Kubernetes helps you here.
Technical explanation
All Kubernetes object resource types are required to support consistent lists and an incremental change notification feed called a watch. Every Kubernetes object has a resourceVersion field representing the version of that resource as stored in the underlying database. When retrieving a collection of resources (either namespace or cluster scoped), the response from the server will contain a resourceVersion value that can be used to initiate a watch against the server. The server will return all changes (creates, deletes, and updates) that occur after the supplied resourceVersion. This allows a client to fetch the current state and then watch for changes without missing any updates.
Difference between generation and resource version
In an object, any edit in spec results in generation number increment, however metadata edit doesn't. Resource version changes for any edit in the object (including both metadata and spec)
Example with change in metadata (annotation)
Notice generation attribute and resourceVersion attribute in below example.
values before change
$cat test.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
test: 'yes'
spec:
backend:
serviceName: testsvc
servicePort: 80
$kubectl get ing test-ingress -o json
{
"apiVersion": "extensions/v1beta1",
"kind": "Ingress",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"extensions/v1beta1\",\"kind\":\"Ingress\",\"metadata\":{\"annotations\":{\"test\":\"yes\"},\"name\":\"test-ingress\",\"namespace\":\"default\"},\"spec\":{\"backend\":{\"serviceName\":\"testsvc\",\"servicePort\":80}}}\n",
"test": "yes"
},
"creationTimestamp": "2018-09-27T10:14:00Z",
"generation": 1,
"name": "test-ingress",
"namespace": "default",
"resourceVersion": "9367626",
"selfLink": "/apis/extensions/v1beta1/namespaces/default/ingresses/test-ingress",
"uid": "0d4a73c7-c23e-11e8-b4a7-eabc5652ca9b"
},
"spec": {
"backend": {
"serviceName": "testsvc",
"servicePort": 80
}
},
"status": {
"loadBalancer": {}
}
Values after changing metadata (annotation)
$ cat test.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
test: 'no'
spec:
backend:
serviceName: testsvc
servicePort: 80
$kubectl get ing test-ingress -o json
{
"apiVersion": "extensions/v1beta1",
"kind": "Ingress",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"extensions/v1beta1\",\"kind\":\"Ingress\",\"metadata\":{\"annotations\":{\"test\":\"no\"},\"name\":\"test-ingress\",\"namespace\":\"default\"},\"spec\":{\"backend\":{\"serviceName\":\"testsvc\",\"servicePort\":80}}}\n",
"test": "no"
},
"creationTimestamp": "2018-09-27T10:14:00Z",
"generation": 1,
"name": "test-ingress",
"namespace": "default",
"resourceVersion": "9367749",
"selfLink": "/apis/extensions/v1beta1/namespaces/default/ingresses/test-ingress",
"uid": "0d4a73c7-c23e-11e8-b4a7-eabc5652ca9b"
},
"spec": {
"backend": {
"serviceName": "testsvc",
"servicePort": 80
}
},
"status": {
"loadBalancer": {}
}
}
So, When to use generation?
If you want to act on spec change only, then you can hook into generation field. Note that resourceVersion changes on every write, and is used for optimistic concurrency control. in some objects, generation is incremented by the server as part of persisting writes affecting the spec of an object.