Skip to content
This repository has been archived by the owner on Sep 14, 2020. It is now read-only.

Commit

Permalink
Merge pull request #97 from nolar/12-evc-story
Browse files Browse the repository at this point in the history
Update the EphemeralVolumeClaim story-telling
  • Loading branch information
nolar authored Jan 30, 2020
2 parents 6d979a2 + cfd8296 commit 6eb5a5a
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 50 deletions.
22 changes: 16 additions & 6 deletions docs/walkthrough/creation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ We want to create a real ``PersistentVolumeClaim`` object
immediately when an ``EphemeralVolumeClaim`` is created this way:

.. code-block:: yaml
:name: evc
:caption: evc.yaml
:name: evc
:caption: evc.yaml
apiVersion: zalando.org/v1
kind: EphemeralVolumeClaim
metadata:
name: my-claim
spec:
size: 10G
size: 1G
.. code-block:: bash
Expand All @@ -28,8 +28,8 @@ First, let's define a template of the persistent volume claim
(with the Python template string, so that no extra template engines are needed):

.. code-block:: yaml
:name: pvc
:caption: pvc.yaml
:name: pvc
:caption: pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
Expand All @@ -46,13 +46,14 @@ First, let's define a template of the persistent volume claim
Let's extend our only handler.
We will use the official Kubernetes client library:
We will use the official Kubernetes client library (``pip install kubernetes``):

.. code-block:: python
:name: creation
:linenos:
:caption: ephemeral.py
import os
import kopf
import kubernetes
import yaml
Expand Down Expand Up @@ -92,5 +93,14 @@ Wait 1-2 seconds, and take a look:
Now, the PVC can be attached to the pods by the same name, as EVC is named.

.. note::
If you have to re-run the operator, and hit a HTTP 409 error saying
"persistentvolumeclaims "my-claim" already exists",
then remove it manually:

.. code-block:: bash
kubectl delete pvc my-claim
.. seealso::
See also :doc:`/handlers`, :doc:`/errors`, :doc:`/hierarchies`.
17 changes: 10 additions & 7 deletions docs/walkthrough/deletion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,25 @@ __ https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/
Let's extend the creation handler:

.. code-block:: python
:name: adopting
:linenos:
:caption: ephemeral.py
:emphasize-lines: 18
:name: adopting
:linenos:
:caption: ephemeral.py
:emphasize-lines: 18
import os
import kopf
import kubernetes
import yaml
@kopf.on.create('zalando.org', 'v1', 'ephemeralvolumeclaims')
def create_fn(meta, spec, namespace, logger, **kwargs):
def create_fn(meta, spec, namespace, logger, body, **kwargs):
name = meta.get('name')
size = spec.get('size')
if not size:
raise kopf.PermanentError(f"Size must be set. Got {size!r}.")
path = os.path.join(os.path.dirname(__file__), 'pvc-tpl.yaml')
path = os.path.join(os.path.dirname(__file__), 'pvc.yaml')
tmpl = open(path, 'rt').read()
text = tmpl.format(name=name, size=size)
data = yaml.safe_load(text)
Expand All @@ -62,7 +63,9 @@ Let's extend the creation handler:
logger.info(f"PVC child is created: %s", obj)
With this one line, `kopf.adopt` marks the PVC as child of EVC.
return {'pvc-name': obj.metadata.name}
With this one line, `kopf.adopt` marks the PVC as a child of EVC.
This includes: the name auto-generation (if absent), the label propagation,
the namespace assignment to the parent's object namespace,
and, finally, the owner referencing.
Expand Down
23 changes: 18 additions & 5 deletions docs/walkthrough/diffs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ but we will use another feature of Kopf to track one specific field only:
:emphasize-lines: 1, 5
@kopf.on.field('zalando.org', 'v1', 'ephemeralvolumeclaims', field='metadata.labels')
def relabel(old, new, **kwargs):
def relabel(old, new, status, namespace, **kwargs):
pvc_name = status['pvc-name']
pvc_name = status['create_fn']['pvc-name']
pvc_patch = {'metadata': {'labels': new}}
api = kubernetes.client.CoreV1Api()
Expand Down Expand Up @@ -73,7 +73,7 @@ A diff-object has this structure (as an example)::
[('add', ('metadata', 'labels', 'label1'), None, 'new-value'),
('change', ('metadata', 'labels', 'label2'), 'old-value', 'new-value'),
('remove', ('metadata', 'labels', 'label3'), 'old-value', None),
('change', ('spec', 'size'), '10G', '100G')]
('change', ('spec', 'size'), '1G', '2G')]

For the field-handlers, it will be the same,
just the field path will be relative to the handled field,
Expand All @@ -95,10 +95,10 @@ exactly as needed for the patch object (i.e. the field is present there):
:emphasize-lines: 4
@kopf.on.field('zalando.org', 'v1', 'ephemeralvolumeclaims', field='metadata.labels')
def relabel(diff, **kwargs):
def relabel(diff, status, namespace, **kwargs):
labels_patch = {field[0]: new for op, field, old, new in diff}
pvc_name = status['pvc-name']
pvc_name = status['create_fn']['pvc-name']
pvc_patch = {'metadata': {'labels': labels_patch}}
api = kubernetes.client.CoreV1Api()
Expand All @@ -112,3 +112,16 @@ Note that the unrelated labels that were put on the PVC ---e.g., manually,
from the template, by other controllers/operators, beside the labels
coming from the parent EVC--- are persisted and never touched
(unless the same-named label is applied to EVC and propagated to the PVC).

.. code-block:: bash
kubectl describe pvc my-claim
.. code-block:: none
Name: my-claim
Namespace: default
StorageClass: standard
Status: Bound
Labels: application=some-app
owner=me
8 changes: 8 additions & 0 deletions docs/walkthrough/prerequisites.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ Otherwise, let's install minikube locally (e.g. for MacOS):
* `Install kubectl <https://kubernetes.io/docs/tasks/tools/install-kubectl/>`_
* :doc:`Install minikube </minikube>` (a local Kubernetes cluster)
* :doc:`Install Kopf </install>`

.. warning::
Unfortunately, Minikube cannot handle the PVC/PV resizing,
as it uses the HostPath provider internally.
You can either skip the :doc:`updates` step of this tutorial
(where the sizes of the volumes are changed),
or you can use an external Kubernetes cluster
with real dynamically sized volumes.
12 changes: 10 additions & 2 deletions docs/walkthrough/problem.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ __ https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-co
- name: main
resources:
requests:
ephemeral-storage: 10G
ephemeral-storage: 1G
limits:
ephemeral-storage: 10G
ephemeral-storage: 1G
There is a `PersistentVolumeClaim`__ resource kind, but it is persistent,
i.e. not deleted after they are created (only manually deletable).
Expand Down Expand Up @@ -74,3 +74,11 @@ The lifecycle of an ``EphemeralVolumeClaim`` is this:

* Deletes the ``PersistentVolumeClaim`` after either the pod is finished,
or the wait time has elapsed.

.. seealso::
This documentation only highlights the main patterns & tricks of Kopf,
but does not dive deep into the implementation of the operator's domain.
The fully functional solution for ``EphemeralVolumeClaim`` resources,
which is used for this documentation, is available at the following link:

* https://github.com/nolar/ephemeral-volume-claims
8 changes: 4 additions & 4 deletions docs/walkthrough/resources.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Custom Resource Definition
Let us define a CRD (custom resource definition) for our object.

.. code-block:: yaml
:caption: crd.yaml
:name: crd-yaml
:caption: crd.yaml
:name: crd-yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
Expand Down Expand Up @@ -57,8 +57,8 @@ logic behind the objects yet.
Let's make a sample object:

.. code-block:: yaml
:caption: obj.yaml
:name: obj-yaml
:caption: obj.yaml
:name: obj-yaml
apiVersion: zalando.org/v1
kind: EphemeralVolumeClaim
Expand Down
23 changes: 17 additions & 6 deletions docs/walkthrough/starting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ Let's start with the an operator skeleton that does nothing useful --
just to see how it can be started.

.. code-block:: python
:name: skeleton
:linenos:
:caption: ephemeral.py
:name: skeleton
:linenos:
:caption: ephemeral.py
import kopf
Expand All @@ -37,9 +37,20 @@ The output looks like this:

.. code-block:: none
... TODO...
.. todo:: add the outpit sample^^
[2019-05-31 10:42:11,870] kopf.config [DEBUG ] configured via kubeconfig file
[2019-05-31 10:42:11,913] kopf.reactor.peering [WARNING ] Default peering object not found, falling back to the standalone mode.
[2019-05-31 10:42:12,037] kopf.reactor.handlin [DEBUG ] [default/my-claim] First appearance: {'apiVersion': 'zalando.org/v1', 'kind': 'EphemeralVolumeClaim', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"zalando.org/v1","kind":"EphemeralVolumeClaim","metadata":{"annotations":{},"name":"my-claim","namespace":"default"}}\n'}, 'creationTimestamp': '2019-05-29T00:41:57Z', 'generation': 1, 'name': 'my-claim', 'namespace': 'default', 'resourceVersion': '47720', 'selfLink': '/apis/zalando.org/v1/namespaces/default/ephemeralvolumeclaims/my-claim', 'uid': '904c2b9b-81aa-11e9-a202-a6e6b278a294'}}
[2019-05-31 10:42:12,038] kopf.reactor.handlin [DEBUG ] [default/my-claim] Adding the finalizer, thus preventing the actual deletion.
[2019-05-31 10:42:12,038] kopf.reactor.handlin [DEBUG ] [default/my-claim] Patching with: {'metadata': {'finalizers': ['KopfFinalizerMarker']}}
[2019-05-31 10:42:12,165] kopf.reactor.handlin [DEBUG ] [default/my-claim] Creation event: {'apiVersion': 'zalando.org/v1', 'kind': 'EphemeralVolumeClaim', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"zalando.org/v1","kind":"EphemeralVolumeClaim","metadata":{"annotations":{},"name":"my-claim","namespace":"default"}}\n'}, 'creationTimestamp': '2019-05-29T00:41:57Z', 'finalizers': ['KopfFinalizerMarker'], 'generation': 1, 'name': 'my-claim', 'namespace': 'default', 'resourceVersion': '47732', 'selfLink': '/apis/zalando.org/v1/namespaces/default/ephemeralvolumeclaims/my-claim', 'uid': '904c2b9b-81aa-11e9-a202-a6e6b278a294'}}
A handler is called with body: {'apiVersion': 'zalando.org/v1', 'kind': 'EphemeralVolumeClaim', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"zalando.org/v1","kind":"EphemeralVolumeClaim","metadata":{"annotations":{},"name":"my-claim","namespace":"default"}}\n'}, 'creationTimestamp': '2019-05-29T00:41:57Z', 'finalizers': ['KopfFinalizerMarker'], 'generation': 1, 'name': 'my-claim', 'namespace': 'default', 'resourceVersion': '47732', 'selfLink': '/apis/zalando.org/v1/namespaces/default/ephemeralvolumeclaims/my-claim', 'uid': '904c2b9b-81aa-11e9-a202-a6e6b278a294'}, 'spec': {}, 'status': {}}
[2019-05-31 10:42:12,168] kopf.reactor.handlin [DEBUG ] [default/my-claim] Invoking handler 'create_fn'.
[2019-05-31 10:42:12,173] kopf.reactor.handlin [INFO ] [default/my-claim] Handler 'create_fn' succeeded.
[2019-05-31 10:42:12,210] kopf.reactor.handlin [INFO ] [default/my-claim] All handlers succeeded for creation.
[2019-05-31 10:42:12,223] kopf.reactor.handlin [DEBUG ] [default/my-claim] Patching with: {'status': {'kopf': {'progress': None}}, 'metadata': {'annotations': {'kopf.zalando.org/last-handled-configuration': '{"apiVersion": "zalando.org/v1", "kind": "EphemeralVolumeClaim", "metadata": {"name": "my-claim", "namespace": "default"}, "spec": {}}'}}}
[2019-05-31 10:42:12,342] kopf.reactor.handlin [DEBUG ] [default/my-claim] Update event: {'apiVersion': 'zalando.org/v1', 'kind': 'EphemeralVolumeClaim', 'metadata': {'annotations': {'kopf.zalando.org/last-handled-configuration': '{"apiVersion": "zalando.org/v1", "kind": "EphemeralVolumeClaim", "metadata": {"name": "my-claim", "namespace": "default"}, "spec": {}}', 'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"zalando.org/v1","kind":"EphemeralVolumeClaim","metadata":{"annotations":{},"name":"my-claim","namespace":"default"}}\n'}, 'creationTimestamp': '2019-05-29T00:41:57Z', 'finalizers': ['KopfFinalizerMarker'], 'generation': 2, 'name': 'my-claim', 'namespace': 'default', 'resourceVersion': '47735', 'selfLink': '/apis/zalando.org/v1/namespaces/default/ephemeralvolumeclaims/my-claim', 'uid': '904c2b9b-81aa-11e9-a202-a6e6b278a294'}, 'status': {'kopf': {}}}
[2019-05-31 10:42:12,343] kopf.reactor.handlin [INFO ] [default/my-claim] All handlers succeeded for update.
[2019-05-31 10:42:12,362] kopf.reactor.handlin [DEBUG ] [default/my-claim] Patching with: {'status': {'kopf': {'progress': None}}, 'metadata': {'annotations': {'kopf.zalando.org/last-handled-configuration': '{"apiVersion": "zalando.org/v1", "kind": "EphemeralVolumeClaim", "metadata": {"name": "my-claim", "namespace": "default"}, "spec": {}}'}}}
Note that the operator has noticed an object created before the operator
was even started, and handled it -- since it was not handled before.
Expand Down
73 changes: 53 additions & 20 deletions docs/walkthrough/updates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,18 @@
Updating the objects
====================

.. warning::
Unfortunately, Minikube cannot handle the PVC/PV resizing,
as it uses the HostPath provider internally.
You can either skip this step of the tutorial,
or you can use an external Kubernetes cluster
with real dynamically sized volumes.

Previously (:doc:`creation`),
we have implemented a handler for the creation of an ``EphemeralVolumeClaim`` (EVC),
and created the corresponding ``PersistantVolumeClaim`` (PVC).

What will happen if we change the size of the EVC when it already exists?
E.g., with:

.. code-block:: bash
kubectl edit evc my-claim
Or by patching it:

.. code-block:: bash
kubectl patch evc my-claim -p '{"spec": {"resources": {"requests": {"storage": "100G"}}}}'
The PVC must be updated accordingly to match its parent EVC.

First, we have to remember the name of the created PVC:
Expand All @@ -28,17 +23,17 @@ with one additional line:
.. code-block:: python
:linenos:
:caption: ephemeral.py
:emphasize-lines: 23
:emphasize-lines: 24
@kopf.on.create('zalando.org', 'v1', 'ephemeralvolumeclaims')
def create_fn(spec, meta, namespace, logger, **kwargs):
def create_fn(body, spec, meta, namespace, logger, **kwargs):
name = meta.get('name')
size = spec.get('size')
if not size:
raise kopf.PermanentError(f"Size must be set. Got {size!r}.")
path = os.path.join(os.path.dirname(__file__), 'pvc-tpl.yaml')
path = os.path.join(os.path.dirname(__file__), 'pvc.yaml')
tmpl = open(path, 'rt').read()
text = tmpl.format(size=size, name=name)
data = yaml.safe_load(text)
Expand All @@ -61,11 +56,16 @@ We can see that with kubectl:

.. code-block:: bash
kubectl describe evc my-claim
kubectl get -o yaml evc my-claim
.. code-block:: none
.. code-block:: yaml
TODO
spec:
size: 1G
status:
create_fn:
pvc-name: my-claim
kopf: {}
Let's add a yet another handler, but for the "update" cause.
This handler gets this stored PVC name from the creation handler,
Expand All @@ -74,11 +74,11 @@ and patches the PVC with the new size from the EVC::
@kopf.on.update('zalando.org', 'v1', 'ephemeralvolumeclaims')
def update_fn(spec, status, namespace, logger, **kwargs):

size = spec.get('create_fn', {}).get('size', None)
size = spec.get('size', None)
if not size:
raise kopf.PermanentError(f"Size must be set. Got {size!r}.")

pvc_name = status['pvc-name']
pvc_name = status['create_fn']['pvc-name']
pvc_patch = {'spec': {'resources': {'requests': {'storage': size}}}}

api = kubernetes.client.CoreV1Api()
Expand All @@ -89,3 +89,36 @@ and patches the PVC with the new size from the EVC::
)

logger.info(f"PVC child is updated: %s", obj)

Now, let's change the EVC's size:

.. code-block:: bash
kubectl edit evc my-claim
Or by patching it:

.. code-block:: bash
kubectl patch evc my-claim --type merge -p '{"spec": {"size": "2G"}}'
Keep in mind the PVC size can only be increased, never decreased.

Give the operator few seconds to handle the change.

Check the size of the actual PV behind the PVC, which is now increased:

.. code-block:: bash
kubectl get pv
.. code-block:: none
NAME CAPACITY ACCESS MODES ...
pvc-a37b65bd-8384-11e9-b857-42010a800265 2Gi RWO ...
.. warning::
Kubernetes & ``kubectl`` improperly show the capacity of PVCs:
it remains the same (1G) event after the change.
The size of actual PV (Persistent Volume) of each PVC is important!
This issue is not related to Kopf, so we go around it.

0 comments on commit 6eb5a5a

Please sign in to comment.