\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.

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=50%}\

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 de recul, j'ai constaté que&nbsp;: 

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

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 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 suivant représentant la pipeline de chaque dépôt&nbsp;: 

![Schéma des 3 pipelines](./media/schema_3_pipelines.png){width=100%}\

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

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

Exemple de code 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 "$CI_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:${CHARTS_ACCESS_TOKEN}@${CHARTS_REPOSITORY}
        cd charts
        # Update files
        sed -i "s/^version: \".*\"/version: \"${CI_COMMIT_TAG}\"/" "${BACKEND_CHARTFILE_PATH}"
        sed -i "s/^appVersion: \".*\"/appVersion: \"${CI_COMMIT_TAG}-backend\"/" "${BACKEND_CHARTFILE_PATH}"
        sed -i "s/^version: \".*\"/version: \"${CI_COMMIT_TAG}\"/" "${FRONTEND_CHARTFILE_PATH}"
        sed -i "s/^appVersion: \".*\"/appVersion: \"${CI_COMMIT_TAG}-frontend\"/" "${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 backend/frontend image version"
        git tag $CI_COMMIT_TAG
        git push origin main --tags
      fi
  needs: [push-backend,push-frontend]
```

La ligne 23 à 25 posent un tag sur le dépôt **charts** en mettant simplement à jour les versions utilisées pour les charts Helm.

### Résultats

Après plusieurs points de détails, 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 complexiter à 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é à ceux qui ont besoin du dépôt initial plutôt qu'à la/les personne(s) qui s'occupent du dépôt initial.

## 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**.

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 de mettre en place 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 fait 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érience 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. Ces sujets amènent également à prendre des initiatives et tester localement des outils choisis. 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étail le processus de travail dans son ensemble.
