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

Maintenant que l'on sait gérer le statut et les conditions de nos ressources personnalisées, il est de bon ton de faire le tour des **annotations**.

Toutes celles qu'on va utiliser proviennent de **[Kubebuilder](https://book.kubebuilder.io/)**.

Il en existe de différentes sortes, et on en a déjà vu dans les articles précédents car c'est via les annotations kubebuilder qu'on a pu **configurer les droits RBAC pour notre opérateur**.

```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
```

Aujourd'hui, on va voir 4 annotations qui vont vous permettre d'être plus efficace lors de l'utilisation  et/ou du  débuggage de votre opérateur.

---

## 1. Resource

Cette annotation va se retrouver **au dessus de la définition de la structure d'une ressource personnalisée**. *(dans api/.../xxx_types.go)*

Composée de 3 parties principales, ces parties vont vous permettre de :
- **Définir le scope de l'opérateur via le paramètre "scope"**
- **Définir la liste des noms par lesquels vous voudrez accéder aux ressources** *(quand vous faites `kubectl get ...`)* Et les différents noms doivent être séparés par un `;`
- **Définir le singulier pour la ressource** *(Aussi pour quand vous effectuez un `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

Avoir des ressources personalisées c'est bien, mais être capable d'afficher des informations importantes quand on fait un `kubectl get`, c'est mieux!

Toujours **au dessus de la définition de la structure d'une ressource personnalisée** *(dans api/.../xxx_types.go)*, vous allez pouvoir définir les annotations en suivant le modèle suivant
```go
// +kubebuilder:printcolumn:name="NAME",type=TYPE,JSONPath=JSONPATH
```

**NAME** sera à remplacer par le nom de l'information que vous souhaitez afficher. Ce nom sera utilisé comme nom de colonne.

**TYPE** sera à remplacer par le type de valeur à afficher. *(ex: String, number...)*
Référez-vous à la [**Spécification OpenAPI**](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#data-types) pour savoir quel type choisir en fonction de vos besoins.

**JSONPATH** sera à remplacer par le **JSONPath** pour accéder à l'information dans votre ressource. **L'information doit obligatoirement se trouver dans la ressource, que ça soit dans sa spécification ou son statut**.
Référez-vous à la [**documentation de Kubernetes**](https://kubernetes.io/docs/reference/kubectl/jsonpath/) concernant la manière d'écrire cette partie en JSONPath.

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

Déjà avec ça, on est plutôt gâté, mais voici 3 petits points supplémentaires concernant cette annotation!

### 2.1. Priority
En effet quand on veut récupérer les informations d'une ressource dans Kubernetes, il est possible d'utiliser l'option `-o wide` afin d'en afficher plus.

Afin de pouvoir le faire avec votre opérateur, il vous suffit d'ajouter `,priority=10` *(ou n'importe quel autre nombre strictement supérieur à 0)* dans la définition du printcolumn  que vous souhaitez avoir que lors de l'utilisation de l'option `-o wide`.

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

### 2.2.  Age
Par défaut, quand vous faites un `kubectl get ...` de vos ressources **l'age de vos ressources est affiché**. Cependant, dès **que vous définissez un printcolumn, cette information n'est plus affichée**. 

Par conséquent, si vous voulez conserver cette information, vous devez rajouter l'annotation suivante
```go  
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=.metadata.creationTimestamp
```

### 2.3.  Ready
Dans la partie précédente de cette série, nous avons évoqué les conditions. Avec la colonne ci-dessous, on va être capable  d'afficher **si l'ensemble des conditions de la ressource sont remplies**.

```go
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
```

---

## 3. Validation

L'annotation suivante permet d'ajouter simplement des **validations sur les différents champs de votre  spécification**. 

Il en existe de différents types, en voici quelques exemples :

- **Rendre obligatoire un champ** :  `//+kubebuilder:validation:Required`
- **Rendre optionel un champ** : `// +kubebuilder:validation:Optional`
- **Définir une longueur minimum à la valeur saisie** : `//+kubebuilder:validation:MinLength=8`
- **Définir une longueur maximum à la valeur saisie** : `//+kubebuilder:validation:MaxLength=20`
- **S'assurer que la valeur fait bien parti d'une certaine liste de possibilités** : `// +kubebuilder:validation:Enum=earth;wind;fire`
- **Pour définir  le nombre minimum d'éléments dans un tableau** : `//+kubebuilder:validation:MinItems=2`
- Pour une valeur numérique, **s'assurer que la valeur saisie est le multiple d'un certain nombre** : `//+kubebuilder:validation:MultipleOf=15`

Et là encore j'en oublie plein, pour voir tous les autres existants, allez sur **[la documentation de KubeBuilder](https://book.kubebuilder.io/reference/markers/crd-validation.html)**. Mais ça vous montre quand même ce qui est possible de faire avec!

---

## 4. Default

La derrnière annotation qui vous sera bien utile est celle pour pouvoir  **définir une valeur par défaut à un champ**.

En effet, si certains de vos champs sont optionnels, vous aimerez peut être définir une valeur par défaut.

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

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

---

## Conclusion
Maintenant que vous connaissez ces annotations, vous êtes en mesure de pouvoir mieux configurer votre opérateur et notamment de gérer un certain nombre de vérifications sans créer un webhook.

Dans la prochaine partie de cette série, nous allons parler sortir un peu du tutoriel pour parler d'un opérateur déjà existant : **Crossplane**!

J'espère que ça vous aidera et si jamais vous avez des questions, quelles qu'elles soient *(Il n'y a jamais de questions bêtes!)* ou des points qui ne sont pas clairs pour vous, n'hésitez pas à laisser un message dans les commentaires ou à me joindre directement sur [LinkedIn](https://www.linkedin.com/in/maxime-guilbert-14b6a5128/) *(même pour parler d'autres sujets!)*.

---

<center><h3>Vous voulez me supporter?</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>