OpenShift Operator SDK: Go Getting Started Guide Part II

10 minute read

38202270

Overview

In this article we will provide a hands-on guide to building your very first operator in Go. Using the Operator SDK we will learn how to create boilerplate code, build and deploy an operator.

This article is part of a series that will walk you through understanding and building operators in Go or Ansible end-to-end.

Setup Development Environment

There are some prerequisites needed to build the operator-sdk and eventually your own operators. In this case I am using Fedora Linux.

Install Docker 17.03+

Add the docker ce repositories. Note: you can also use podman and buildah instead of Docker for those that want a complete and clean divorce.

$ sudo dnf -y install dnf-plugins-core

$ sudo dnf config-manager \
    --add-repo \
    https://download.docker.com/linux/fedora/docker-ce.repo

Install docker-ce

$ sudo dnf -y install docker-ce docker-ce-cli
$ sudo systemctl start docker
$ sudo systemctl enable docker

Install Go 1.13+

Download Go and extract it to /usr/local

$ curl -O https://dl.google.com/go/go1.13.6.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.13.6.linux-amd64.tar.gz

Update bash profile and set GOPATH/GOBIN

$ vi ~/.bash_profile

---
# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin:/usr/local/go/bin
export GOPATH=/home/ktenzer/go
export GOBIN=/home/ktenzer
export PATH
---
$ source ~/.bash_profile

Check Go version

$ go version
go version go1.13.6 linux/amd64

Install Mercurial 3.9+

$ sudo dnf -y install mercurial

Install Bazaar 2.7.0+

$ sudo dnf -y install -y bzr

Build Operator SDK

Now that the prerequisites are installed we will build the operator sdk from source.

$ go get -d github.com/operator-framework/operator-sdk
$ cd $GOPATH/src/github.com/operator-framework/operator-sdk
$ git checkout master
$ make tidy
$ make install

The Makefile will deploy the operator-sdk CLI into the home directory. Move it to /usr/local/bin.

$ sudo mv ~/operator-sdk /usr/local/bin

Check the operator-sdk version

$ operator-sdk version
operator-sdk version: "v0.15.0-5-g6b4e1370", commit: "6b4e1370f18e5d028b283375b740026d2efb0a44", go version: "go1.13.6 linux/amd64"

Building Your First Operator

The SDK is able to generate not only boilerplate code but also the CRDs, controller and API endpoints. In this example we will create a cars operator. It will simply provide an endpoint allowing us to do CRUD on car objects. Each car object will spawn a pod with a base image.

Change directory to the github.com source directory.

$ cd $GOPATH/src/github.com

Note: create directory if it doesn't exist

Using operatore-sdk cli create boilerplate code.

$ operator-sdk new cars-operator --repo github.com/cars-operator
cd cars-operator

Add new API CRD for our car operator

$ operator-sdk add api --api-version=cars.example.com/v1alpha1 \
--kind=Car

Add controller API that watches our cars

$ operator-sdk add controller --api-version=cars.example.com/v1alpha1 \
 --kind=Car

Create User on Quay.io

We will be using quay to store operator images. Authenticate using Github or Gmail to https://quay.io. Once authenticated go to account settings and set a password.

Test Quay.io Credentials

$ sudo docker login quay.io

Build Operator

Using operator-sdk cli build the operator which will push the image to your local Docker registry.

$ sudo PATH=$PATH:/usr/local/go/bin operator-sdk build \
quay.io/ktenzer/cars-operator

Note: Substitute ktenzer for your username.

Push Operator to your Quay.io Account

$ sudo docker push quay.io/ktenzer/cars-operator:latest

Make Quay Repository Public

By default your Quay repository will be private. If we want to access it from OpenShift we need to make it public. Login to quay.io with your username/password

Select the cars-operator repository

Screenshot from 2020-01-25 13-04-42

Under Repository Settings enable visibility to be public

Screenshot from 2020-01-25 13-05-07

Update Image in operator.yaml

We need to point the image to the location in our Quay repository.

vi deploy/operator.yaml
---
# Replace this with the built image name
image: quay.io/ktenzer/cars-operator
command:
---

Deploy Cars Operator on OpenShift

Now that the operator is built and pushed to our Quay repository we can deploy it on an OpenShift cluster.

Authenticate to OpenShift Cluster

$oc login https://api.ocp4.keithtenzer.com:6443

Create new project for our operator

$ oc new-project cars-operator

Setup service accounts and role bindings

$ oc create -f deploy/service_account.yaml
$ oc create -f deploy/role.yaml
$ oc create -f deploy/role_binding.yaml

Create the CRD for our operator

$ oc create -f deploy/crds/cars.example.com_cars_crd.yaml

Deploy our operator

$ oc create -f deploy/operator.yaml

Create the CR for our operator

$ oc create -f deploy/crds/cars.example.com_v1alpha1_car_cr.yaml

Using Cars Operator

The cars operator will automatically deploy an example-car. Whe can query our car object just like any other Kubernetes object. This is the beauty of CR/CRDs and operators. We can easily extend the Kubernetes API without needing to understand it's complexity.

$ oc get car
NAME AGE
example-car 31m

Next we can get information about our example-car.

$ oc get car example-car -o yaml
apiVersion: cars.example.com/v1alpha1
kind: Car
metadata:
  creationTimestamp: "2020-01-25T12:15:45Z"
  generation: 1
  name: example-car
  namespace: cars-operator
  resourceVersion: "2635723"
  selfLink: /apis/cars.example.com/v1alpha1/namespaces/cars-operator/cars/example-car
  uid: 6a424ef9-3f6c-11ea-a391-fa163e9f184b
spec:
  size: 3

Looking at the running pods in our cars-operator project we see the operator and our example-car.

$ oc get pods -n cars-operator
NAME                            READY   STATUS    RESTARTS   AGE
cars-operator-b98bff54d-t2465   1/1     Running   0          5m
example-car-pod                 1/1     Running   0          5m

Create a new Car

Lets now create a BMW car.

$ vi bmw.yaml
kind: Car
metadata:
  name: bmw
spec:
  size: 1
$ oc create -f bmw.yaml

Here we can see we now have a BMW car.

$ oc get car
NAME          AGE
bmw           11m
example-car   31m

Of course we can get information about our BMW car.

$ oc get car bmw -o yaml
apiVersion: cars.example.com/v1alpha1
kind: Car
metadata:
  creationTimestamp: "2020-01-25T12:35:25Z"
  generation: 1
  name: bmw
  namespace: cars-operator
  resourceVersion: "2644044"
  selfLink: /apis/cars.example.com/v1alpha1/namespaces/cars-operator/cars/bmw
  uid: 294bc47f-3f6f-11ea-b32c-fa163e3e8e24
spec:
  size: 1

Finally as with the example-car, the operator will start a new pod when the BMW car is created.

$ oc get pods -n cars-operator
NAME                            READY   STATUS    RESTARTS   AGE
bmw-pod                         1/1     Running   0          10m
cars-operator-b98bff54d-t2465   1/1     Running   0          14m
example-car-pod                 1/1     Running   0          14m

Cleanup

Follow these steps to remove the operator cleanly.

$ oc delete -f deploy/crds/cars.example.com_v1alpha1_car_cr.yaml
$ oc delete -f deploy/operator.yaml
$ oc delete -f deploy/role.yaml
$ oc delete -f deploy/role_binding.yaml
$ oc delete -f deploy/service_account.yaml
$ oc delete -f deploy/crds/cars.example.com_cars_crd.yaml
$ oc delete project cars-operator

Summary

In this article a step-by-step guide was provided to setup a development environment, generate boilerplate code and deploy our custom cars operator using Go on OpenShift using the Operator Framework.

Happy Operatoring!

(c) 2020 Keith Tenzer