Constraints and best practices
This is a collection of recommendations for writing controllers with Metacontroller.
If you have something to add to the collection, please send a pull request against this document.
Constraints
Objects relationship
Because of limitations of Kubernetes garbage collection we have following restrictions between objects:
Parent | Child | Related |
---|---|---|
Cluster | - Cluster - Namespaced (any namespace) | - Cluster - Namespaced (any namespace) |
Namespaced | - Namespaced (the same namespace as parent) | - Namespaced (the same namespace as parent) |
Lambda Hooks
Apply Semantics
Because Metacontroller uses apply semantics, you don't have to think about whether a given object needs to be created (because it doesn't exist) or patched (because it exists and some fields don't match your desired state). In either case, you should generate a fresh object from scratch with only the fields you care about filled in.
For example, suppose you create an object like this:
apiVersion: example.com/v1
kind: Foo
metadata:
name: my-foo
spec:
importantField: 1
Then later you decide to change the value of importantField
to 2.
Since Kubernetes API objects can be edited by the API server, users, and other controllers to collaboratively produce emergent behavior, the object you observe might now look like this:
apiVersion: example.com/v1
kind: Foo
metadata:
name: my-foo
stuffFilledByAPIServer: blah
spec:
importantField: 1
otherField: 5
To avoid overwriting the parts of the object you don't care about, you would ordinarily need to either build a patch or use a retry loop to send concurrency-safe updates. With apply semantics, you instead just call your "generate object" function again with the new values you want, and return this (as JSON):
apiVersion: example.com/v1
kind: Foo
metadata:
name: my-foo
spec:
importantField: 2
Metacontroller will take care of merging your change to importantField
while
preserving the fields you don't care about that were set by others.
Side Effects
Your hook code should generally be free of side effects whenever possible. Ideally, you should interpret a call to your hook as asking, "Hypothetically, if the observed state of the world were like this, what would your desired state be?"
In particular, Metacontroller may ask you about such hypothetical scenarios during rolling updates, when your object is undergoing a slow transition between two desired states. If your hook has to produce side effects to work, you should avoid enabling rolling updates on that controller.
Status
If your object uses the Spec/Status convention, keep in mind that the Status returned from your hook should ideally reflect a judgement on only the observed objects that were sent to you. The Status you compute should not yet account for your desired state, because the actual state of the world may not match what you want yet.
For example, if you observe 2 Pods, but you return a desired list of 3 Pods,
you should return a Status that reflects only the observed Pods
(e.g. replicas: 2
).
This is important so that Status reflects present reality, not future desires.
Working with Status subresource in metacontroller
If you would like to expose and use the Status
subresource in your custom resource, you should take care of:
- having a proper CRD schema definition for
Status
section in order to let metacontroller update it successfully - it must be a part of CRD schema, i.e.
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: configmappropagations.examples.metacontroller.io
spec:
...
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
...
status:
type: object
properties:
expected_copies:
type: integer
actual_copies:
type: integer
observedGeneration:
type: integer
required:
- spec
subresources:
status: {}
- your controller must be strict about the types in the schema defined in CRD, i.e., in example above
do not try to set any of the
integer
fields asstring
s, or add additional fields there.
To read more about Status
subresource please look at:
- Kubernetes documentation - https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#status-subresource