K8s Operator - Annotations
Tags: kubernetes, tutorial, devops, sre
Published: 2023-07-03 13:00:00

Now that we know how to manage resources status and conditions, it's time to see **annotations**.

All the annotations comes from **[Kubebuilder](https://book.kubebuilder.io/)**.

There are a lot of different annotations, and we already saw some of them because we defined **RBAC rights for our operator** with annotations.

```go
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete  
// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
```

Today, we will see 4 annotations which will help us to be more efficient when using custom resources and/or to debug them.

---

## 1. Resource

This annotation must be **above the resource struct definition**. *(in api/.../xxx_types.go)*

Composed with 3 major parts,  they are used to :
- **Define the operator "scope"** *(if the operator is Namespace-scope or Cluster-scope)*
- **Define the list of alternate names** that can be used to access the resource *(When you do `kubectl get ...`)*. Each name must be separated by `;`
- **Define the singular for the resource name** *(Also to use it when you do `kubectl get ...`)*

```go  
//+kubebuilder:resource:scope=Cluster,shortName=myproxies;mp,singular=myproxy
type MyProxy struct {  
   metav1.TypeMeta   `json:",inline"`  
   metav1.ObjectMeta `json:"metadata,omitempty"`  
  
   Spec   MyProxySpec   `json:"spec,omitempty"`  
   Status MyProxyStatus `json:"status,omitempty"`  
}
```

---

## 2. Printcolumn

Having custom resources is good, but being able to display important informations with `kubectl get` is better!

Still **above the resource struct definition** *(in api/.../xxx_types.go)*, you can define custom columns with the following pattern
```go
// +kubebuilder:printcolumn:name="NAME",type=TYPE,JSONPath=JSONPATH
```

**NAME** : The name of the custom column.

**TYPE** : To define the type of data you want to display.  *(ex: String, number...)*
Check the [**OpenAPI Specification**](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#data-types) to know which  type can be used.

**JSONPATH** : To define the **JSONPath** to use to access the information **in the custom resource**. If the information isn't available in the resource, you won't be able to display it.
Check the [**Kubernetes documentation**](https://kubernetes.io/docs/reference/kubectl/jsonpath/) to know how to write a JSONPath value.

*Example*
```go
// +kubebuilder:printcolumn:name="Name",type=string,JSONPath=`.spec.name`
```

Here are 3 more specific cases about this annotation!

### 2.1. Priority
When we want to get the information of a resource, we can use the option `-o wide` to get more informations than default.

To do it with the operator, you just need to add `,priority=10` *(or any value strictly greater than 0)* in the printcolumn definition of the value you only want to display with the option `-o wide`.

*Example*
```go
// +kubebuilder:printcolumn:name="Name",type=string,JSONPath=`.spec.name`,priority=10
```

### 2.2.  Age
By default when you do `kubectl get ...` on your custom resource, their **age is shown**. But once you've **added one printcolumn, the information isn't displayed anymore**.

So if you want to keep the information, here is the annotation you must add.
```go  
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=.metadata.creationTimestamp
```

### 2.3.  Ready
Previously in this serie, we talked about conditions and we said that it can be useful to know if everything goes well. With the following column, we will be able to easily see if everything goes well or not.
```go
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
```

---

## 3. Validation

The following annotation simply add **validations on your spec fields**.

Here are a bunch of them :

- **Make mandatory a field** :  `//+kubebuilder:validation:Required`
- **Make a field optional** : `// +kubebuilder:validation:Optional`
- **Define the minimum length of a string** : `//+kubebuilder:validation:MinLength=8`
- **Define the maximum length of a string** : `//+kubebuilder:validation:MaxLength=20`
- **Check if the value is contain in a Enum list** : `// +kubebuilder:validation:Enum=earth;wind;fire`
- **Define the minimum number of elements in an array** : `//+kubebuilder:validation:MinItems=2`
- For a number, **check if the value is a multiple of a particular value** : `//+kubebuilder:validation:MultipleOf=15`

There are a lot more, and if you are interested, go check **[the KubeBuilder  documentation](https://book.kubebuilder.io/reference/markers/crd-validation.html)**. But with all these examples, you have a great idea about what can be done or not.

---

## 4. Default

The last annotation we will see today is the one which **defines a default value for a spec field**. 

It can be really useful in case of an optional field..

*Examples*
```go
// +kubebuilder:default:=test
```

```go
// +kubebuilder:default:=0
```

---

Now that you know all of these annotations, you will be able to easily do a lot more with your operator.

In the next part of this serie, we will leave the tutorial part to talk about an operator which already exists : **Crossplane**!

I hope it will help you and if you have any questions *(there are not dumb questions)* or some points are not clear for you, don't hesitate to add your question in the comments or to contact me directly on  [LinkedIn](https://www.linkedin.com/in/maxime-guilbert-14b6a5128/).

---

<center><h3>You want to support me?</h3></center>
<a href="https://www.buymeacoffee.com/mxguilbert7" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>