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.
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.
In below example, spec change results in generation number increment, however metadata change didn't.
Text Box
Before change
{u'status': {u'loadBalancer': {}}, u'kind': u'Ingr
ess', u'spec': {u'rules': [{u'host': u'hotdrinks.beverages.com', u'http': {u'paths': [{u'path': u'/', u'backend': {u'serviceName'
: u'frontend-apply', u'servicePort': 8087}}]}}]}, u'apiVersion': u'extensions/v1beta1', u'metadata': {u'name': u'web-ingress-appl
y', u'generation': 27, u'namespace': u'default', u'resourceVersion': u'9348778', u'creationTimestamp': u'2018-09-21T13:00:28Z', u
'annotations': {u'ingress.citrix.com/frontend-ip': u'10.102.53.239', u'kubectl.kubernetes.io/last-applied-configuration': u'{"api
Version":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"ingress.citrix.com/frontend-ip":"10.102.53.239","ingre
ss.citrix.com/insecure-port":"5084"},"name":"web-ingress-apply","namespace":"default"},"spec":{"rules":[{"host":"hotdrinks.bevera
ges.com","http":{"paths":[{"backend":{"serviceName":"frontend-apply","servicePort":8087},"path":"/"}]}}]}}\n', u'ingress.citrix.c
om/insecure-port': u'5084'}, u'selfLink': u'/apis/extensions/v1beta1/namespaces/default/ingresses/web-ingress-apply', u'uid': u'5
014a5d0-bd9e-11e8-b4a7-eabc5652ca9b'}}
After change
{u'status': {u'loadBalancer': {}}, u'kind': u'Ingr
ess', u'spec': {u'rules': [{u'host': u'hotdrinks.beverages.com', u'http': {u'paths': [{u'path': u'/', u'backend': {u'serviceName'
: u'frontend-apply', u'servicePort': 8028}}]}}]}, u'apiVersion': u'extensions/v1beta1', u'metadata': {u'name': u'web-ingress-appl
y', u'generation': 28, u'namespace': u'default', u'resourceVersion': u'9349030', u'creationTimestamp': u'2018-09-21T13:00:28Z', u
'annotations': {u'ingress.citrix.com/frontend-ip': u'10.102.53.239', u'kubectl.kubernetes.io/last-applied-configuration': u'{"api
Version":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"ingress.citrix.com/frontend-ip":"10.102.53.239","ingre
ss.citrix.com/insecure-port":"5084"},"name":"web-ingress-apply","namespace":"default"},"spec":{"rules":[{"host":"hotdrinks.bevera
ges.com","http":{"paths":[{"backend":{"serviceName":"frontend-apply","servicePort":8028},"path":"/"}]}}]}}\n', u'ingress.citrix.c
om/insecure-port': u'5084'}, u'selfLink': u'/apis/extensions/v1beta1/namespaces/default/ingresses/web-ingress-apply', u'uid': u'5
014a5d0-bd9e-11e8-b4a7-eabc5652ca9b'}}
Change in metadata
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": {}
}
After changing metadata value
$ 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": {}
}
}
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.
https://kubernetes.io/docs/reference/using-api/api-concepts/
https://stackoverflow.com/questions/47100389/what-is-the-difference-between-a-resourceversion-and-a-generation