From 3b58aa1ef847d9594b48fe1584d47e7aeb1da64d Mon Sep 17 00:00:00 2001
From: Olaf Bohlen <olbohlen@eenfach.de>
Date: Tue, 21 Mar 2023 15:06:28 +0100
Subject: [PATCH] add also Markdown varian

---
 README.md |  305 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 305 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f9485e9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,305 @@
+
+# Table of Contents
+
+1.  [I need a cookie recipe database!](#org651bf34)
+2.  [Preparing the cookierecipes CRD](#org58b8bb6)
+3.  [Storing some sample recipes](#orgbb687b0)
+4.  [Creating RBAC resources](#org2d91ada)
+5.  [Storing some sample recipes (hopefully this time!!)](#org69aa53f)
+6.  [Now can we do anything with our recipes?](#orgbd83100)
+7.  [I'm an operator with my pocket calculator](#org3bb5d31)
+8.  [What do we need?](#orgcebea86)
+9.  [Let's review the Containerfile](#org6c2e7f3)
+10. [Have a look at the Controller](#org3165f3c)
+11. [Now let's also have a look at the deployment](#org6ec9a40)
+12. [The ServiceAccount](#orgcba3d6d)
+13. [Building the stuff together](#orga6fa80a)
+14. [Deploying the Operator](#org22de8a4)
+15. [Test the Operator](#org1cbedf9)
+16. [Test for updated recipes](#org40457b2)
+
+
+<a id="org651bf34"></a>
+
+# I need a cookie recipe database!
+
+&#x2026;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?
+-   `CustomResourceDefinitions` are themselves a `Resource`, based on a `ResourceDefinition`
+
+    $ 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>.
+    [...]
+
+
+<a id="org58b8bb6"></a>
+
+# 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!
+
+
+<a id="orgbb687b0"></a>
+
+# Storing some sample recipes
+
+We try to store sample cookie recipes&#x2026;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 `ClusterRole` that allows viewing recipes
+-   a `ClusterRole` that allows editing recipes
+-   and a `ClusterRoleBinding` that allows that for authenticated users
+
+
+<a id="org2d91ada"></a>
+
+# 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`).
+
+
+<a id="org69aa53f"></a>
+
+# 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.
+
+
+<a id="orgbd83100"></a>
+
+# 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&#x2026;it's a lot of manual work&#x2026;
+
+
+<a id="org3bb5d31"></a>
+
+# 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&#x2026; :)
+
+Of course this Operator is not compatible with the `OperatorLifecycyleManager` (`OLM`),
+so we have to install it manually.
+
+
+<a id="orgcebea86"></a>
+
+# 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.
+
+
+<a id="org6c2e7f3"></a>
+
+# 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.
+
+
+<a id="org3165f3c"></a>
+
+# 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>
+
+
+<a id="org6ec9a40"></a>
+
+# 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
+
+<https://www.eenfach.de/gitblit/blob/~olbohlen!cookie-operator.git/master/cookie-operator-deployment.yaml>
+
+This deployment requires a `ServiceAccount` called "cookieprocessor", this `ServiceAccount` provides 
+a Token to authenticate against the API (which we use in the controller script).
+
+
+<a id="orgcba3d6d"></a>
+
+# 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>
+
+
+<a id="orga6fa80a"></a>
+
+# 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.
+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
+
+
+<a id="org22de8a4"></a>
+
+# 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.
+
+
+<a id="org1cbedf9"></a>
+
+# 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
+    [...]
+
+
+<a id="org40457b2"></a>
+
+# 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:
+    ----------------------------------
+    [...]
+

--
Gitblit v1.9.3