Writing a Kubernetes Operator in Golang

I decided to write this post after struggling to find documentation on how to write a Kubernetes operator that went through the code with the reader using a real life example.

The example we’re going to use here is: In our Kubernetes cluster each Namespace represents a teams sandbox environment, we would like to lock down access so that teams can only play in their sandbox.

This can be achieved by assigning a group to a user that has a RoleBinding for their particular Namespace and a ClusterRole with edit access. Here is the YAML representation of what I mentioned above:

We could manually create this RoleBinding each time but after 100 namespaces this might get a bit tedious. This is where Kubernetes operators help as they allow us to automate the creation of resources in Kubernetes based on changes to resources. In our case we want to create a RoleBinding when a Namespace is created.

First we start by creating a main function which will perform the required setup for running the operator and then invoke the operator action:

Here we’re doing the following:

  1. Setting up a listener for specific OS signals to trigger a graceful shutdown of the operator.
  2. Using a WaitGroup to make sure that before terminating the application we stop all executing go functions gracefully.
  3. Gaining access to the cluster by creating a clientset.
  4. Running the NamespaceController which will be where all our logic will sit.

Then we need the core of the logic which in our case is called the NamespaceController:

Here we’re setting up a SharedIndexInformer which will listen to changes to namespaces in an efficient way using a data cache. Then we attach an EventHandler to the informer so that when a Namespace is added the createRoleBinding function is invoked.

The next step is to define the createRoleBinding function:

Here we’re receiving the Namespace as obj and casting it into a Namespace object. We then define our RoleBinding based loosely on the YAML file we mentioned at the beginning using the provided Namespace object and then create the RoleBinding. Finally we log whether the creation is successful or not.

The last function we need to define is the Run function:

Here we’re telling the WaitGroup that we’re about to invoke a go function and then we run the namespaceInformer that we defined earlier. When the stop signal is closed it will end the go function, tell the WaitGroup it’s no longer running and then this function will return.

Information about building and running this operator in a Kubernetes cluster can be found in the github repository.

Now we have an operator that creates a RoleBinding when a Namespace is created within the Kubernetes cluster.