I'm trying to understand Kubernetes behaviour as it pertains to custom resources and their subresources -- specifically the status
subresource.
Specifically, I want to update the status
subresource without modifying the parent custom resource.
To the best of my understanding, this should be possible. I've reviewed the documentation [here][1], but I cannot seem to get it to work as anticipated.
I am testing using Docker Desktop's Kubernetes 1.19.3
.
Here is the scenario ...
- Create this simple CRD:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: greetings.k8s.test.io
spec:
group: k8s.test.io
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
description: Greeting is the Schema for the Greetings Operator
type: object
properties:
message:
description: A friendly greeting
type: string
default: Hello World!
status:
type: object
properties:
ready:
description: The resource's readiness
type: boolean
additionalPrinterColumns:
- name: ready
type: boolean
description: Readiness of the created resource
jsonPath: .status.ready
subresources:
status: {}
scope: Namespaced
names:
plural: greetings
singular: greeting
kind: Greeting
shortNames:
- grt
- Create a demo resource:
apiVersion: k8s.test.io/v1alpha1
kind: Greeting
metadata:
name: demo
spec:
message: Hi there!
- Fire up a proxy:
kubectl proxy &
- Establish a watch on custom resource:
curl -L -s -X GET -H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
127.0.0.1:8001/apis//k8s.test.io/v1alpha1/watch/namespaces/default/greetings
At this point, you should see output similar the following:
{"type":"ADDED","object":{"apiVersion":"k8s.test.io/v1alpha1","kind":"Greeting","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"k8s.test.io/v1alpha1\",\"kind\":\"Greeting\",\"metadata\":{\"annotations\":{},\"name\":\"demo\",\"namespace\":\"default\"},\"spec\":{\"message\":\"Hi there!\"}}\n"},"creationTimestamp":"2020-12-10T04:02:55Z","generation":1,"managedFields":[{"apiVersion":"k8s.test.io/v1alpha1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:kubectl.kubernetes.io/last-applied-configuration":{}}},"f:spec":{".":{},"f:message":{}}},"manager":"kubectl-client-side-apply","operation":"Update","time":"2020-12-10T04:02:55Z"}],"name":"demo","namespace":"default","resourceVersion":"532930","selfLink":"/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo","uid":"40f3a618-74e5-4b14-9bd4-2eb47366d804"},"spec":{"message":"Hi there!"}}}
PATCH
the/status
subresource
curl -k -s -X PATCH -H "Accept: application/json, */*" \
-H "Content-Type: application/merge-patch+json" \
127.0.0.1:8001/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo/status \
--data '{"status":{"ready":true}}'
Confusingly, after submitting the PATCH
our watch on the parent resource produces ...
{"type":"MODIFIED","object":{"apiVersion":"k8s.test.io/v1alpha1","kind":"Greeting","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"k8s.test.io/v1alpha1\",\"kind\":\"Greeting\",\"metadata\":{\"annotations\":{},\"name\":\"demo\",\"namespace\":\"default\"},\"spec\":{\"message\":\"Hi there!\"}}\n"},"creationTimestamp":"2020-12-10T04:02:55Z","generation":1,"managedFields":[{"apiVersion":"k8s.test.io/v1alpha1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:kubectl.kubernetes.io/last-applied-configuration":{}}},"f:spec":{".":{},"f:message":{}}},"manager":"kubectl-client-side-apply","operation":"Update","time":"2020-12-10T04:02:55Z"},{"apiVersion":"k8s.test.io/v1alpha1","fieldsType":"FieldsV1","fieldsV1":{"f:status":{".":{},"f:ready":{}}},"manager":"curl","operation":"Update","time":"2020-12-10T04:05:22Z"}],"name":"demo","namespace":"default","resourceVersion":"533184","selfLink":"/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo","uid":"40f3a618-74e5-4b14-9bd4-2eb47366d804"},"spec":{"message":"Hi there!"},"status":{"ready":true}}}
Why is it MODIFYING the parent resource??
And if we inspect the resource, we can see that that status
of the subresource was definitely updated:
$ k get grt demo
NAME READY
demo true
I don't know if this behaviour is expected and my understanding is faulty OR if my PATCH
methodology is flawed.
Hope somebody can help out. Thanks.
UPDATE:
In addition to PATCH
I can also confirm that PUT
(while a more complicated operation) exhibits the exact same behaviour.
Example:
curl -k -s -X PUT -H "Accept: application/json, */*" \
-H "Content-Type: application/json" \
127.0.0.1:8001/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo/status \
--data '{"apiVersion":"k8s.test.io/v1alpha1","kind":"Greeting","metadata":{"name":"demo","resourceVersion":"533184"},"status":{"ready":false}}'
¯\(ツ)/¯
The
status
subresource isn't actually a different object; it is just a separate API path that can only modify the top-levelstatus:
block in the object. This is useful because you can set up an RBAC policy for your controller to allow read access to the whole object, but only allow writing the object's status.In particular, you see this in your last command. If you
kubectl get grt demo -o yaml
, the extended YAML syntax will include thestatus:
sub-block. The controller updating the status will cause a visible change in thestatus:
, so you should reasonably expect that a watch will see this as a change.