\newpage

# Mes réalisations

Ce projet m'a amené à participer sur de nombreux sujets. En voici deux qui, je 
l'espère, illustreront bien ce que j'ai rencontré.

Je parlerais de **livraison continue** (CI/cd) et d'**états Terraform**.

## Réalisation 1&nbsp;: Livraison continue

### Contexte

Durant le court laps de temps accordé au projet, nous arrivions à la fin, nous 
étions en manque de ressources humaines&nbsp;: 

* le travail de l'équipe n'était **pas toujours qualitatif**,
* parfois cela **ne répondait pas au besoin énoncé**,
* d'autres fois **une ressource prenait plus de temps que prévu** sur sa tâche.

Il en résulta **peu de moyens pour répondre à tous les besoins** du projet. 
Cela raccourcissait le temps des tâches restantes.

En pareille situation il a fallu **faire avec l'existant** afin de créér une 
chaîne de publication logicielle. J'ai pris en charge cette tâche.

### Analyse

À ce moment du projet nous n'avions que peu d'éléments&nbsp;: 

* un dépôt applicatif (nommé **upstream**),
* un dépôt contenant les charts Helm qui utilisent les images publiées du 
dépôt applicatif (nommé **charts**),
* et un dépôt contenant l'infrastructure de production (nommé **infra**).

Chaque dépôt peut utiliser l'intégration continue de Gitlab, appelée Gitlab CI 
et utilisant des pipelines.

\newpage

On souhaite **créer une chaîne d'intégration continue** (CI, Continuous 
Integration) pour passer d'un dépôt à l'autre suivant **une chaîne**, comme le 
montre le schéma suivant&nbsp;: 

![Schéma de la chaîne des dépôts](./media/schema_chaine_depots.png){height=40%}

Cela fait penser au chapitre précédent où nous parlions du [sujet de la 
promotion logicielle](#collab). Suivant **quelle(s) règle(s)** pouvons-nous 
**passer d'un dépôt à l'autre**, d'une version à l'autre ou (in)valider le 
fonctionnement&nbsp;?

Après plusieurs heures de réflexions, de dessins en tous genres et du recul, 
j'ai constaté que&nbsp;: 

* quoiqu'il arrive la **pipeline va lancer des tests** sur le code,
* et dans la **situation où l'on pose une étiquette** (appelé **tag** sur un 
dépôt Git), c'est qu'on souhaite valider - indirectement - le travail effectué.

\newpage

Ainsi dans la situation où une étiquette apparaît, la pipeline diffère 
légèrement. Il y a donc globalement **2 règles à suivre**, représentées par le 
schéma suivant&nbsp;: 

![Schéma des 2 règles principales pour la CI d'un dépôt](./media/schema_regles_ci.png){height=50%}

Comme le résultat positif des tests engendre la compilation puis la 
publication d'une image sur un registre, une idée apparaît donc&nbsp;: 
**informer les autres dépôts que l'image est disponible**&nbsp;!

### Solution

Suivant l'idée précédente qui consiste à informer les autres dépôts de la 
publication récente d'une version, il a fallu trouver une solution.

Nous sommes sur Gitlab, avons des dépôts Git, et des pipelines qui se lancent 
quand un commit est effectué. Dans la situation où une étiquette est posée, 
pourquoi ne pas **utiliser Git lui-même à l'aide d'un commit sur le dépôt 
suivant**&nbsp;? Ainsi, sans outils supplémentaires, nous pouvons lier les 
dépôts entre eux.

En suivant cette logique, nous obtenons le schéma représentant la pipeline de 
chaque dépôt&nbsp;: 

```{=latex}
\begin{sidewaysfigure}
   \includegraphics{./media/schema_3_pipelines.png}
\caption{Schéma des 3 pipelines}
\end{sidewaysfigure}
```

\newpage

Ce qui donne le savant mélange suivant&nbsp;: 

![Schéma de toutes les pipelines imbriquées](./media/schema_toutes_pipelines.png){height=73%}

\newpage

Ainsi nous avons, par exemple, le code suivant permettant d'informer le dépôt **charts** qu'une nouvelle étiquette a été posée sur le dépôt **upstream** une fois le backend et le frontend publiés sur le registre Docker de Gitlab&nbsp;: 

```bash {.numberLines}
inform-charts:
  stage: inform-next
  variables:
    BACKEND_CHARTFILE_PATH: charts/backend/Chart.yaml
    FRONTEND_CHARTFILE_PATH: charts/frontend/Chart.yaml
  script:
    - |
      if ! [ -z "$COMMIT_TAG" ]; then
        # Configure git user
        git config --global user.email "git@dossmann.net"
        git config --global user.name "CI Pipeline"
        # Get CHARTS repository
        git clone https://blankoworld:${ACCESS_TOKEN}@${CHARTS_REPOSITORY}
        cd charts
        # Update files
        sed -i "s/^version: \".*\"/version: \"${COMMIT_TAG}\"/" \
          "${BACKEND_CHARTFILE_PATH}"
        sed -i "s/^appVersion: \".*\"/appVersion:\"${COMMIT_TAG}-back\"/" \
          "${BACKEND_CHARTFILE_PATH}"
        sed -i "s/^version: \".*\"/version: \"${COMMIT_TAG}\"/" 
          "${FRONTEND_CHARTFILE_PATH}"
        sed -i "s/^appVersion: \".*\"/appVersion:\"${COMMIT_TAG}-front\"/" \
          "${FRONTEND_CHARTFILE_PATH}"
        # Add them to git staged area
        git add "${BACKEND_CHARTFILE_PATH}" "${FRONTEND_CHARTFILE_PATH}"
        # Commit and push result (to launch CHARTS CI for new code)
        git commit -m "chore(release): Update back/front image version"
        git tag $COMMIT_TAG
        git push origin main --tags
      fi
  needs: [push-backend,push-frontend]
```

**Le code a dû être modifié (raccourcissement des lignes) pour les fins de 
rédaction du présent document**.

Les **ligne 27 à 29** posent un tag sur le dépôt **charts** en mettant 
simplement à jour les versions utilisées du backend et du frontend dans les 
charts Helm sur le registre Gitlab.

### Résultats

Après plusieurs points de détails corrigés, nous avons pu **automatiser 
correctement la chaîne de production logicielle** de la **phase de 
développement** jusqu'à **la production** en passant **par une validation 
manuelle** après déploiement en environnement de pré-production **via 
l'intégration continue de Gitlab** (Gitlab CI).

Ceci nous a permis de facilement **travailler sur 2 environnements** 
distincts&nbsp;: 

* l'environnement de **pré-production**,
* et l'environnement de **production**.

### Limites

Cette technique, bien qu'efficace, a ses limites&nbsp;: 

* pour fonctionner sans développer plus, il a fallu **utiliser le même numéro 
de version sur tous les dépôts**&nbsp;: on ne fait que passer le tag courant 
au dépôt suivant. **Que faire si les numéros de version dérivent&nbsp;?**,
* plus on ajoute de dépôts dans la chaîne, plus il y aura de code à faire et 
plus il y aura de **complexité à chaîner les dépôts**.

Trouver une solution temporaire et efficace est - potentiellement - facile. 
Mais avoir **une solution pérenne demande de l'expérience et plus de temps**.

### Amélioration(s) possibles(s)

Cette expérience a été enrichissante. Bien que ce système ait répondu à nos 
besoins du moment, je pense qu'il serait envisageable de procéder autrement.

Dans un premier temps, **utiliser des dépôts indépendants** avec leur propre 
pipeline qui **teste** puis **publie** sur des **dépôts utilisables** par 
d'autres projets. Puis **chaque dépôt pourrait avoir un script de montée de 
version** pour définir les contraintes spécifiques au projet, au dernier 
numéro de version, etc.

Ainsi un dépôt d'infrastructure, par exemple, pourrait utiliser des versions 
spécifiques de chacun de ses dépôts indépendants. Et ce serait à lui d'aller 
regarder quelle est la dernière version d'un module ou d'une application. Et 
d'agir en conséquence&nbsp;: lancer une batterie de tests puis intégrer la 
nouvelle version disponible.

Ceci éviterait d'ajouter une quantité astronomique de code Gitlab dans le 
dépôt initial (celui de l'application par exemple). Et cela **laisse la 
responsabilité aux personnes suivantes** (qui utilisent le dépôt initial) plutôt 
qu'aux personnes qui s'occupent du code dans le dépôt initial.

\newpage

## Réalisation 2&nbsp;: États Terraform

### Contexte

Pendant la création du dépôt **infra** contenant - entre autre - 
l'infrastructure de notre environnement de production, **nous avons subis 
quelques pertes des états Terraform** posés sur le registre Gitlab.

Non seulement **les états Terraform étaient continuellement cassés** par les 
collaborateurs, mais l'ignorance de ces derniers sur le fonctionnement de 
Terraform nous a mis **une belle épine dans le pied**.

Il a fallu agir. Sur mon temps libre - au grand dam de ma famille - j'ai 
décidé d'y regarder de plus près.

### Analyse

Comme nous avons plusieurs environnements, l'état Terraform va décrire chaque 
fois un environnement. Nous aurons besoin au minimum des états Terraform 
suivants&nbsp;: 

* un état **gitlab-ci** pour les pipelines de test,
* un état **staging** pour l'environnement de pré-production,
* un état **prod** pour l'environnement de production,
* et éventuellement **un état Terraform par développeur**.

C'est ce dernier point que je cherchais à résoudre&nbsp;: **faciliter le 
travail du développeur** sur Terraform **pour éviter toute bévue**.

Nous constatons également&nbsp;: 

* le **manque de documentation** par le développeur pour savoir comment 
procéder pour travailler sur Terraform,
* que **trop d'erreurs manuelles** sont effectués lors de l'utilisation des 
commandes Terraform.

C'est ce qui **nécessite une simplification** et/ou un changement.

### Solution

Dans le Logiciel Libre, dans la plupart des dépôts, nous retrouvons un fichier 
**Makefile** qui décrit les différentes étapes de compilation d'une 
application, de son installation ou de la création d'une image.

Pourquoi ne pas partir sur cette solution habituelle et l'adapter à notre 
cas&nbsp;?

Ainsi l'idée serait de **reprendre les commandes habituelles de Terraform**, à 
savoir&nbsp;: 

* **init**,
* **plan**,
* **apply**,
* **destroy**,
* **fmt**,
* et **graph**.

J'ai imaginé mettre à disposition les mots-clés suivants avec le fichier 
**Makefile**&nbsp;: 

* **make init** - pour **initialiser l'état Terraform**,
* **make plan** - pour **vérifier les changements attendus** entre l'état Terraform local et l'environnement distant,
* **make apply** - pour **appliquer les changements**,
* **make destroy** - pour **supprimer l'ensemble des ressources distantes**,
* **make format** - pour **formater le contenu des fichiers \*.tf** trouvés,
* **make graph** - pour **générer une image vectorielle des dépendances entre 
ressources** du projet,
* et **make clean** - pour **nettoyer les fichiers Terraform** non nécessaires.

**Ces commandes ne résolvent pas le problème en tant que tel**. C'est ce qui 
est derrière qui va résoudre le souci&nbsp;: **des scripts qui vérifient que 
tout soit OK** avant d'appliquer les commandes.

Ainsi l'**utilisation d'un fichier .env**, non disponible dans le dépôt de 
code, permet de charger les informations utiles au choix d'un état Terraform 
et de l'environnement distant à atteindre pour travailler.

La solution réside dans le fait que des scripts soient lancés sous chacune des 
commandes make et qu'ils vérifient l'existence et le contenu du fichier 
**.env**.

S'ajoute à cela une **documentation dans le fichier README.md**.

\newpage

Exemple de script avec **make init** pour bien comprendre de quoi il est 
question&nbsp;: 

```bash {.numberLines}
#!/usr/bin/env bash
#
# init.sh
#
# Initialize for a Developer

ENV_FILE="${PWD}/.env"
BACKEND_HTTP_FILE="${PWD}/backend.hcl"

# Need variables in this file
source "${ENV_FILE}" \
  || echo "Fichier ${ENV_FILE} manquant." \
  || exit 1

echo "[INFO] 'USERNAME': ${TF_HTTP_USERNAME}"

if ! [[ -f "${BACKEND_HTTP_FILE}" ]]; then
  echo "[ERR] ${BACKEND_HTTP_FILE} manquant!"
  exit 1
fi

terraform init \
  -reconfigure \
  -backend-config=${BACKEND_HTTP_FILE} $@
```

Et le contenu du fichier **env.example**, partagé aux développeurs comme d'un 
template pour fabriquer le fichier **.env**&nbsp;: 

```bash
# TOKEN DOIT avoir permissions read/write
export TF_HTTP_USERNAME="PseudoGitlab"
export TF_STATE_NAME="dev"
# Cf.https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
export TF_HTTP_PASSWORD="monPersonnalAccessTokenGitlab"
# Backend HTTP
export TF_HTTP_ADDRESS="<myurl>/state/${TF_STATE_NAME}"
export TF_HTTP_LOCK_ADDRESS="<myurl>/state/${TF_STATE_NAME}/lock"
export TF_HTTP_UNLOCK_ADDRESS="<myurl>/state/${TF_STATE_NAME}/lock"
# Accès AWS
export AWS_ACCESS_KEY_ID="AWS-access-key-id"
export AWS_SECRET_ACCESS_KEY="secret-AWS-access-key"
```

Pour faciliter la création de nouveaux dépôts utilisant Terraform, j'ai choisi 
d'inclure ce travail dans un [dépôt dit **template** sur 
Gitlab](https://gitlab.com/devu42/templates/terraform).

### Résultats

**À l'usage le travail sur Terraform s'est amélioré**. En procédant ainsi, 
toute erreur se produisant sur Terraform n'affectait pas les autres 
collaborateurs. **Chacun avait son état Terraform**.

La **documentation a permis** aux développeurs **de comprendre facilement** 
quoi changer pour travailler. Et ils se sont sentis moins perdus.

### Limites

Toutefois, procéder ainsi a quelques limites&nbsp;: 

* **toute modification** sur les éléments du Makefile, les scripts, la 
documentation, etc. **doit être faite sur CHAQUE dépôt**. Ainsi plus on a de 
dépôt, plus on consomme de temps à mettre à jour (avec des oublis possibles),
* **si le template évolue**, les dépôts ayant utilisé le template **n'ont plus 
les mises à jour**. On pourrait imaginer faire un **git rebase, mais cela 
casserait l'historique des dépôts** ou bien demanderait d'effectuer du travail 
sur une branche Git à part.

**C'est très limitant**.

### Amélioration(s) possibles(s)

**Cette solution** a fait ses preuves. Elle **a été utile au moment où nous en 
avions grandement besoin**. Elle permet de prendre de l'expérience à ce sujet.

Nous pourrions imaginer améliorer cela avec&nbsp;: 

* **une forme de dépendance du fichier Makefile** et des scripts avec le dépôt 
**template** utilisé (par exemple via une commande de mise à jour fournie),
* avoir une possibilité de **choisir entre la commande Terraform et OpenTofu** 
par l'utilisation d'une variable d'environnement,
* et **permettre l'ajout d'arguments** aux commandes **make apply**, voire 
ajouter une commande **make apply-autoapprove**.

Il est aussi possible qu'il existe une commande de type **wrapper** (qui 
utilise Terraform et ses options) pour en faciliter l'usage, tel que 
**kubectx** pour Kubernetes.

## Conclusion

Deux expériences variées et différentes qui s'imbriquent pourtant dans le 
processus de production logicielle et de livraison continue. Il y a beaucoup 
de choses à dire sur chacun des sujets, tellement ils sont passionnants. Ils 
amènent également à prendre des initiatives et tester localement des outils. 
Nous parlerons d'ailleurs dans le prochain chapitre d'une situation de travail 
ayant amené à faire une recherche. Nous pourrons ainsi voir plus en détails le 
processus de travail suivi afin d'aboutir à ce résultat.
