8.8 KiB
- I need a cookie recipe database!
- Preparing the cookierecipes CRD
- Storing some sample recipes
- Creating RBAC resources
- Storing some sample recipes (hopefully this time!!)
- Now can we do anything with our recipes?
- I'm an operator with my pocket calculator
- What do we need?
- Let's review the Containerfile
- Have a look at the Controller
- Now let's also have a look at the deployment
- The ServiceAccount
- Building the stuff together
- Deploying the Operator
- Test the Operator
- Test for updated recipes
I need a cookie recipe database!
…and because it makes total sense, we are going to abuse the K8s API for it.
- thankfully we can extend K8s with Custom Resource Definitions (CRDs)
- but how does it work?
CustomResourceDefinitionsare themselves aResource, based on aResourceDefinition
$ oc api-resources | egrep "(NAME|CustomResourceDefinition)"
NAME SHORTNAMES APIVERSION NAMESPACED KIND
customresourcedefinitions crd,crds apiextensions.k8s.io/v1 false CustomResourceDefinition
$ oc explain crds
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
DESCRIPTION:
CustomResourceDefinition represents a resource that should be exposed on
the API server. Its name MUST be in the format <.spec.name>.<.spec.group>.
[...]
Preparing the cookierecipes CRD
Let's create the CRD from https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-crd.yaml
$ oc new-project kitchen Now using project "kitchen" on server "https://api.crc.testing:6443". $ oc create -f cookie-crd.yaml Error from server (Forbidden): error when creating "cookie-crd.yaml": customresourcedefinitions.apiextensions.k8s.io is forbidden: User "developer" cannot create resource "customresourcedefinitions" in API group "apiextensions.k8s.io" at the cluster scope $ oc login -u kubeadmin $ oc create -f cookie-crd.yaml customresourcedefinition.apiextensions.k8s.io/cookierecipes.de.eenfach.olbohlen created $ oc login -u developer
Now the cluster knows about the CRD and we could store recipes!
Storing some sample recipes
We try to store sample cookie recipes…but:
$ oc create -f sample-cookie.yaml Error from server (Forbidden): error when creating "sample-cookie.yaml": cookierecipes.de.eenfach.olbohlen is forbidden: User "developer" cannot create resource "cookierecipes" in API group "de.eenfach.olbohlen" in the namespace "kitchen" Error from server (Forbidden): error when creating "sample-cookie.yaml": cookierecipes.de.eenfach.olbohlen is forbidden: User "developer" cannot create resource "cookierecipes" in API group "de.eenfach.olbohlen" in the namespace "kitchen"
We need to set up some RBAC resources first:
- a
ClusterRolethat allows viewing recipes - a
ClusterRolethat allows editing recipes - and a
ClusterRoleBindingthat allows that for authenticated users
Creating RBAC resources
Apply the RBAC definitions from: https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-rbac.yaml
$ oc login -u kubeadmin $ oc create -f cookie-rbac.yaml clusterrole.rbac.authorization.k8s.io/cookierecipe-edit created clusterrole.rbac.authorization.k8s.io/cookierecipe-view created clusterrolebinding.rbac.authorization.k8s.io/cookierecipe-edit created
The ClusterRoleBinding "cookierecipe-edit" allows system:authenticated:oauth
group members to edit cookierecipes.
system:authenticated:oauth contains all users that logged in via the OAuth
service (via an IdentityProvider).
Storing some sample recipes (hopefully this time!!)
Now we should be able to create the sample recipes:
$ oc login -u developer $ oc create -f sample-cookie.yaml cookierecipe.de.eenfach.olbohlen/vintage-chocolate-chip created cookierecipe.de.eenfach.olbohlen/double-dipped-shortbread created $ oc get cookierecipe NAME AGE double-dipped-shortbread 17s vintage-chocolate-chip 17s
There is no functionality here - we just stored the recipes in the etcd via the K8s API.
Now can we do anything with our recipes?
Of course we can oc get -o yaml for example on them and filter:
$ oc get cookierecipe vintage-chocolate-chip -o yaml | yq -y .spec.ingredients[0] amount: 150 name: salted butter remarks: softened unit: grams
This is handy, as we can extract exactly the data which we need at a time.
But…it's a lot of manual work…
I'm an operator with my pocket calculator
Operators were introduced as "Kubernetes Native Applications" and that actually
means nothing. Operators are in the end just Pods.
These Pods run one or more containers, but one container should run a Controller
that can interprete your CustomResources.
So let's write a CookieRecipe Operator. In shell-script… :)
Of course this Operator is not compatible with the OperatorLifecycyleManager (OLM),
so we have to install it manually.
What do we need?
We need:
- a ContainerImage
- and therefore probably a Containerfile
- Controller code
Then we are going to build the Operator ContainerImage and push it to a Registry.
Let's review the Containerfile
The Containerfile is here:
https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/Containerfile
the base image is a "kshbase" image, which itself is based upon ubi9 containing also a ksh93 and an oc client.
Have a look at the Controller
The controller is written in KornShell 93 (ksh93), which is mostly bash compatible :)
The code is here:
https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/recipe-processor.ksh
Now let's also have a look at the deployment
Note: this deployment does not use an ImageStream, so it would work also on native k8s
This deployment requires a ServiceAccount called "cookieprocessor", this ServiceAccount provides
a Token to authenticate against the API (which we use in the controller script).
The ServiceAccount
We need a ServiceAccount, but that alone will not help. The ServiceAccount is NOT member
of system:authenticated:oauth, so it can't read cookierecipes based on the ClusterRoleBinding we created earlier.
For that reason we also create a RoleBinding (namespaced!) that allows reading recipes:
https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookieprocessor-sa.yaml
Building the stuff together
$ oc create -f cookieprocessor-sa.yaml serviceaccount/cookieprocessor created rolebinding.rbac.authorization.k8s.io/cookierecipe-view created
The registry docker.eenfach.de requires login credentials, so we need to set up a secret and link it. NOTE all sample files in this repository use mirrored images on quay.io, which does not require a login.
First login to the registry with podman login, then pick the resulting auth.json:
$ podman login -u olbohlen docker.eenfach.de
Password:
Login Succeeded!
$ oc create secret generic docker-eenfach-de \
> --from-file=.dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json \
> --type kubernetes.io/dockerconfigjson
secret/docker-eenfach-de created
$ oc secrets link cookieprocessor docker-eenfach-de --for pull
Deploying the Operator
Now that we have everything in place, we will just deploy the Operator Pod:
$ oc create -f cookie-operator-deployment.yaml deployment.apps/recipe-processor created $ oc get pod NAME READY STATUS RESTARTS AGE recipe-processor-7f9969697b-qt9lv 1/1 Running 0 17s $ oc logs -f recipe-processor-7f9969697b-qt9lv New recipe found: double-dipped-shortbread -------------------------------------------------------------------------- Pre: we heat up the oven to 180 degrees Celsius Fetching ingredients from recipe: ---------------------------------- Fetching 200grams of salted butter (softened) [...]
The Operator will process both sample recipes.
Test the Operator
We should test if the Operator notices new recipes, so let's create a third recipe from https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/oaty-hazelnut-cookies.yaml
$ oc create -f oaty-hazelnut-cookies.yaml cookierecipe.de.eenfach.olbohlen/oaty-hazelnut created
After a few seconds, we should see in the Operator log:
New recipe found: oaty-hazelnut -------------------------------------------------------------------------- Pre: we heat up the oven to 180 degrees Celsius [...]
Test for updated recipes
But what if we update a resource? Keep the oc logs -f on the Operator Pod open, and in another terminal let's patch a recipe.
$ oc patch cookierecipes double-dipped-shortbread --type merge \
> -p '{"spec":{"temperature":172}}'
cookierecipe.de.eenfach.olbohlen/double-dipped-shortbread patched
And again in the log you should see
New recipe found: double-dipped-shortbread -------------------------------------------------------------------------- Pre: we heat up the oven to 172 degrees Celsius Fetching ingredients from recipe: ---------------------------------- [...]