Developers guide ================ In this developers guide we will cover the basics you need to develop on the Yaook operators. We generally assume you already had a look at the concepts section above. Operator State Graph -------------------- Operators are build based on a state graph. This graph is a directed-acyclic-graph (DAG) and describes all resources and their relations to fulfill the request to the operator. Take a look at the follwing simplified graph for the galera custom resource. The goal of this operator is to create a galera statefulset with certificates and a specific configuration. In front of the statefulset there should be an HAProxy for loadbalancing. .. image:: figures/galera_graph_simplified.drawio.svg During each run the operator tries to traverse all nodes of the graph. Some nodes (haproxy, public service and database statefulset) have requirements to other nodes. These dependencies define the order in which the nodes are traversed. This means that the database statefulset will only be created once the required service, the certificates and the configuration have been generated. Resources --------- Now lets take a look at an individual resource such as the statefulset of the database. Each resource is represented as a subclass of :class:`yaook.statemachine.Resource`. Each resource exposes two methods to the operator: :meth:`yaook.statemachine.Resource.reconcile` the goal of ``reconcile`` is to make reality match the intent of the resource. If you e.g. want to change the number of replicas in a statefulset, then this method is responsible to write the change to kubernetes. Note that applying the changes to kubernetes does not imply that the resource is immediately provisioned. For instance, a Pod often takes a while to start and become ready. :meth:`yaook.statemachine.Resource.is_ready` the goal of ``is_ready`` is to determine if the operator can assume this resource has been fully reconciled. A resource that returns false here will block all of its dependents. This can for example be used to block until a certificate has been issued. Resources that might return false here MUST implement :meth:`yaook.statemachine.Resource.get_listeners`. They need to install a listener for something which allows them to re-trigger the reconcile. Otherwise, the custom resource will never be touched again. Each resource also has a ``component`` set. This is used to distinguish between different resources of the same kubernetes kind. The ``component`` is normally set automatically in the custom resource class (see below). Kubernetes resources ******************** For most plain kubernetes resources, classes to represent them have already been implemented. They are subclasses of :class:`yaook.statemachine.SingleObjectState` which provides a few more convenience methods. These resources generally need to implement the following methods: :meth:`yaook.statemachine.SingleObjectState._make_body` this method is responsible to create the body of the kubernetes resource. it matches the json of the resource in kubernetes. :meth:`yaook.statemachine.SingleObjectState._needs_update` this method checks if the given existing resource matches the return value of ``_make_body``. While this might seem trivial it is not always easy to differenciate between fields managed by the operator and ones generate by k8s. Check :mod:`yaook.statemachine` for a list of all generally supported resources. The kubernetes resource stores labels to identify the matching resource in the operator and its parent custom resource. The following labels are used for that: ``state.yaook.cloud/component`` the component of the resource. ``state.yaook.cloud/parent-group`` the kubernetes group (first part of ``apiVersion``) of the custom resource that created this resource. ``state.yaook.cloud/parent-name`` the kubernetes name of the custom resource that created this resource. ``state.yaook.cloud/parent-plural`` the kubernetes plural of the custom resource that created this resource. ``state.yaook.cloud/parent-version`` the kubernetes version (second part of ``apiVersion``) of the custom resource that created this resource. Using templates *************** Many resources are featured in the plain version and in a "Templated..." version. The plain version requires of you to overwrite :meth:`yaook.statemachine.SingleObjectState._make_body` (see above). When using e.g. a ``TemplatedDeploymentState`` point the resources to the filename of the jinja template that should be used as a body. The templates are searched for in a templates subdirectory next to the python file in which the CustomResource subclass is defined. Generally, this will be ``yaook/op/$youroperatorname/templates``. Combining resources to fulfill a CustomResource request ------------------------------------------------------- Each custom resource definition in kubernetes is represented by a subclass of :class:`yaook.statemachine.CustomResource`. The subclass defines the the reference to the custom resource definition. It also contains all resources required to fulfill the request of the custom resource. The resources are represented as attributes on the class. The name of the attribute for each resource will be used as the ``component`` of it as described above. The context ----------- The classes of all operators and their resources MUST NOT hold any information about a specific resource. This allows us to reuse the same operator for multiple resources and prevents potential leaking of data between resources. To know which resource is currently being processed a context object :class:`yaook.context.Context` is passed around. It contains all data about the current resource as well as possibilities to interact with the kubernetes api. Triggering the operator ----------------------- The operator relies on external triggers to know when to take action. There are the following kind of triggers: Operator Startup When the operator starts for the first time. It will reconcile all resources learns about from a listing via the Kubernetes API. Changes to a resource Whenever a resource spec changes it will trigger a reconcile. Notifications to listeners As described above Resources can implement :meth:`yaook.statemachine.Resource.get_listeners` to define more reasons for a reconcile run. The operator generally deduplicates reconcile requests. So if multiple sources request a reconcile of a resource the process will still only run once. If currently already a reconcile is running then the reconcile is started again after the current one has finished. The reason of the reconcile is never passed to the actual operator or the individual states. If you changed something in the operator (be it a piece of code or a jinja template) just restart the operator for the changes to take effect. Further reading --------------- The devlopers guide is continued in the following subpages: * :doc:`developers_guide_configuration`