Lab 2: Create the Lab Operator Project

Create the Lab Operator Project

In this part of the lab we will create a demo Ansible operator and deploy it to our Cluster.

  1. Create the ansible-operator-frontend directory

    cd
    mkdir ansible-operator
    cd ~/ansible-operator 
           
    

    ``

  2. Create the ansible-operator-frontend Project

    operator-sdk new ansible-operator-frontend --type=ansible --api-version=ansiblelab.ibm.com/v1beta1 --kind=MyAnsibleLabDemo 
       
    > INFO[0000] Creating new Ansible operator 'ansible-operator-frontend'. 
    > INFO[0000] Created deploy/service_account.yaml          
    > INFO[0000] Created deploy/role.yaml                     
    > INFO[0000] Created deploy/role_binding.yaml             
    > INFO[0000] Created deploy/crds/lab_v1beta1_MyAnsibleLabDemo_crd.yaml       
    ...
       
    

    ``

    You can ignore the errors concerning git

  3. Change to the ansible-operator-frontend directory

    cd ~/ansible-operator/ansible-operator-frontend
           
    

    ``

  4. The basic file structure for the Ansible Operator has been created

    ll
       
    > total 36
    > drwxr-x--- 7 training training 4096 Jul  1 17:29 ./
    > drwxrwxr-x 3 training training 4096 Jul  1 17:29 ../
    > drwxr-x--- 3 training training 4096 Jul  1 17:29 build/
    > drwxr-x--- 3 training training 4096 Jul  1 17:29 deploy/
    > drwxrwxr-x 7 training training 4096 Jul  1 17:29 .git/
    > drwxr-x--- 5 training training 4096 Jul  1 17:29 molecule/
    > drwxr-x--- 3 training training 4096 Jul  1 17:29 roles/
    > -rw-r--r-- 1 training training  140 Jul  1 17:29 .travis.yml
    > -rw-r--r-- 1 training training  121 Jul  1 17:29 watches.yaml
    

    ``

Create the Lab Operator API

With the above, the API has already been created (unlike for GO operators) and added to the new Custom Resource (CR), with APIVersion ansiblelab.ibm.com/v1beta1 and Kind MyAnsibleLabDemo.

  1. Add the deployment.image field to the Custom Resource

    Training VM

    gedit ~/ansible-operator/ansible-operator-frontend/deploy/crds/ansiblelab.ibm.com_v1beta1_myansiblelabdemo_cr.yaml &
    
    

    ``

    Cloud/Standalone

    nano ~/ansible-operator/ansible-operator-frontend/deploy/crds/ansiblelab.ibm.com_v1beta1_myansiblelabdemo_cr.yaml
    

    ``

    Add the last two lines from the following at the end of the file and save to make it look the same

    apiVersion: ansiblelab.ibm.com/v1beta1
    kind: MyAnsibleLabDemo
    metadata:
      name: example-MyAnsibleLabDemo
    spec:
      --> Add fields here
      size: 3
      demo:
        image: niklaushirt/k8sdemo:1.0.0
    

    ``

  2. Save and quit

**We have now finished setting up the API and CRDs **

Create the Lab Operator Controller

By default, an Ansible role executes the tasks defined at roles/tasks/main.yml. For defining our deployment we will use the k8s module of Ansible.

1) Define the Lab Operator Controller

  1. Edit the ansible-operator-frontend Controller

    Training VM

    gedit ~/ansible-operator/ansible-operator-frontend/roles/myansiblelabdemo/tasks/main.yml &
    

    ``

    Cloud/Standalone

    nano ~/ansible-operator/ansible-operator-frontend/roles/myansiblelabdemo/tasks/main.yml	```
    
    
  2. Replace the content with the following:

    - name: Create the k8sdemo deployment
      community.kubernetes.k8s:
        definition:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: k8sdemo
            namespace: default
          labels:
            app: k8sdemo
          spec:
            selector:
             matchLabels:
               app: k8sdemo
            replicas: 1
            template:
              metadata:
                labels:
                  app: k8sdemo
              spec:
                containers:
                - name: k8sdemo
                  image: "{{demo.image}}"
                  imagePullPolicy: IfNotPresent 
                  ports:
                  - containerPort: 3000
                  env:
                    - name: PORT
                      value : "3000"
                    - name: APPLICATION_NAME
                      value: k8sdemo
                    - name: BACKEND_URL
                      value: http://k8sdemo-backend-service.default.svc:3000/api
       
    - name: Create the k8sdemo service
      community.kubernetes.k8s:
        definition:
          apiVersion: v1
          kind: Service
          metadata:
            name: k8sdemo-service
            namespace: default
          spec:
            selector:
              app: k8sdemo
            ports:
              - protocol: TCP
                port: 3000
                targetPort: 3000
                nodePort: 32123
            type: NodePort  
       
    

    ``

This will ensure that the Pod will be created with the Image information defined in the Custom Resource (CR) definition ("{{deployment.image}}"). This picks up the value defined in the CR.

  1. Save and Quit

2) Build the Lab Operator Controller

  1. Now let’s build the Operator container

    operator-sdk build localhost:5000/ansible-operator-frontend:ansible
         
    > INFO[0000] Building OCI image localhost:5000/ansible-operator-frontend:ansible
    > Sending build context to Docker daemon  49.15kB
    > Step 1/3 : FROM quay.io/operator-framework/ansible-operator:v0.10.0
    >  ---> 168416e214f1
    > Step 2/3 : COPY watches.yaml ${HOME}/watches.yaml
    >  ---> Using cache
    >  ---> 43f81409e05d
    > Step 3/3 : COPY roles/ ${HOME}/roles/
    >  ---> 0ad354c77a7a
    > Successfully built 0ad354c77a7a
    > Successfully tagged localhost:5000/ansible-operator-frontend:ansible
    > INFO[0001] Operator build complete.  
    

    ``

    Where localhost:5000/ansible-operator-frontend:ansible is the name of the Docker image to be created.

  2. And push the Operator container to the local registry

    Training VM

    First execute this in a separate Terminal Window/Tab in order to be able to access the private registry:

kubectl port-forward –namespace kube-system $(kubectl get po -n kube-system | grep kube-registry-v0 | \awk ‘{print $1;}') 5000:5000

> Forwarding from 127.0.0.1:5000 -> 5000
> Forwarding from [::1]:5000 -> 5000

 ![](../images/cloud.png) **Cloud/Standalone**
 
 ![](../images/important.png) **Cloud/Standalone** This does probably not work if you are using a standalone or cloud environment.
 You can ignore this as we will be using a version already provided on Docker hub.




5. And then push the image:

 ![](../images/vm.png) **Training VM**
 
 ```bash
 docker push localhost:5000/ansible-operator-frontend:ansible
 ```

 ![](../images/cloud.png) **Cloud/Standalone**
 
 ![](../images/important.png) **Cloud/Standalone** This does probably not work if you are using a standalone or cloud environment.
 You can ignore this as we will be using a version already provided on Docker hub.




### 3) Prepare the Deployment for the Lab Operator Controller


The deployment manifest for the Operator (that was generated automatically) looks like this:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ansible-operator-frontend
spec:
replicas: 1
selector:
 matchLabels:
   name: ansible-operator-frontend
template:
 metadata:
   labels:
     name: ansible-operator-frontend
 spec:
   serviceAccountName: ansible-operator-frontend
   containers:
     - name: ansible
       command:
       - /usr/local/bin/ao-logs
       - /tmp/ansible-operator/runner
       - stdout
       --> Replace this with the built image name
       image: "{{ REPLACE_IMAGE }}"
       imagePullPolicy: "{{ pull_policy|default('Always') }}"
       volumeMounts:
       - mountPath: /tmp/ansible-operator/runner
         name: runner
         readOnly: true
     - name: operator
       --> Replace this with the built image name
       image: "{{ REPLACE_IMAGE }}"
       imagePullPolicy: "{{ pull_policy|default('Always') }}"
       volumeMounts:
       - mountPath: /tmp/ansible-operator/runner
         name: runner
       env:
         - name: WATCH_NAMESPACE
           valueFrom:
             fieldRef:
               fieldPath: metadata.namespace
         - name: POD_NAME
           valueFrom:
             fieldRef:
               fieldPath: metadata.name
         - name: OPERATOR_NAME
           value: "ansible-operator-frontend"
   volumes:
     - name: runner
       emptyDir: {}


Modify the YAML to use the image that we have pushed to the registry in the Operator deployment

cp ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml.bak
	
sed -i 's|REPLACE_IMAGE|niklaushirt/ansible-operator-frontend:ansible|g' ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml
sed -i 's|{{ pull_policy.* }}|Always|g' ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml

On Mac please use:
sed -i '' 's|REPLACE_IMAGE|niklaushirt/ansible-operator-frontend:ansible|g' ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml
sed -i '' 's|{{ pull_policy.* }}|Always|g' ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml

more ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml

To the attentive observer, we’re using the prepared image from the docker hub, due to some routing issues for Kubernetes to pull the image internally - thanks minikube. The Training VM also uses a slightly older

2) Deploy the Lab Operator

  1. Deploy the ansible-operator-frontend Custom Resource Definition

    kubectl apply -f  ~/ansible-operator/ansible-operator-frontend/deploy/crds/ansiblelab.ibm.com_myansiblelabdemos_crd.yaml
    > customresourcedefinition.apiextensions.k8s.io/MyAnsibleLabDemos.ansiblelab.ibm.com created
    

    ``

  2. Create the ansible-operator-frontend Service Account

    kubectl apply -f  ~/ansible-operator/ansible-operator-frontend/deploy/service_account.yaml
    kubectl apply -f  ~/ansible-operator/ansible-operator-frontend/deploy/role.yaml
    kubectl apply -f  ~/ansible-operator/ansible-operator-frontend/deploy/role_binding.yaml
       
    > serviceaccount/ansible-operator-frontend created
    > role.rbac.authorization.k8s.io/ansible-operator-frontend created
    > rolebinding.rbac.authorization.k8s.io/ansible-operator-frontend created
    
    

    ``

  3. Create the ansible-operator-frontend Operator

    kubectl apply -f  ~/ansible-operator/ansible-operator-frontend/deploy/operator.yaml
       
    > deployment.apps/ansible-operator-frontend created
    
    

    ``

  4. Check and wait for the Operator to be running

    kubectl get pods
       
    > NAME                                       READY   STATUS    RESTARTS   AGE
    > ansible-operator-frontend-fd78bcf5-zxgws   1/1     Running   0          43m
    

    ``

  5. Get the log of the Operator Pod (use the Pod name from the previous step)

 kubectl logs -c operator ansible-operator-frontend-fd78bcf5-zxgws
 
  > {"level":"info","ts":1593618304.1191652,"logger":"cmd","msg":"Go Version: go1.12.5"}
  > {"level":"info","ts":1593618304.1194193,"logger":"cmd","msg":"Go OS/Arch: linux/amd64"}
  > {"level":"info","ts":1593618304.1194592,"logger":"cmd","msg":"Version of operator-sdk: v0.8.0"}
  > {"level":"info","ts":1593618304.1194792,"logger":"cmd","msg":"Watching namespace.","Namespace":"default"}
  > {"level":"info","ts":1593618304.1632428,"logger":"leader","msg":"Trying to become the leader."}
  
  > {"level":"info","ts":1593618338.328806,"logger":"leader","msg":"Became the leader."}
  > {"level":"info","ts":1593618338.3861306,"logger":"proxy","msg":"Starting to serve","Address":"127.0.0.1:8888"}
  > {"level":"info","ts":1593618338.386716,"logger":"manager","msg":"Using default value for workers 1"}
  > {"level":"info","ts":1593618338.3867893,"logger":"ansible-controller","msg":"Watching resource","Options.Group":"ansiblelab.ibm.com","Options.Version":"v1beta1","Options.Kind":"MyAnsibleLabDemo"}
  > {"level":"info","ts":1593618338.387424,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"myansiblelabdemo-controller","source":"kind source: ansiblelab.ibm.com/v1beta1, Kind=MyAnsibleLabDemo"}
  > {"level":"info","ts":1593618338.487868,"logger":"kubebuilder.controller","msg":"Starting Controller","controller":"myansiblelabdemo-controller"}
  > {"level":"info","ts":1593618338.5881243,"logger":"kubebuilder.controller","msg":"Starting workers","controller":"myansiblelabdemo-controller","worker count":1}

This means that the Operator is working and ready.