# Cue 0.7 <!-- .element: style="font-size: 2.5em" --> ## Mise à jour de Cue 0.7 <!-- .element: style="font-size: 1em" --> ## sur env-ng et trackbone <!-- .element: style="font-size: 1em" --> --- ## Sommaire 1. Évolutions constatées 2. Problème de dépendances des taches flow 3. Problème de concurrence avec cue flow 4. Rappel sur la fermeture (close) des définitions 5. Problème du modulo --- ## Pourquoi ? * Cue 0.4 n'est pas thread safe -> bloque nos travaux de parallélisation des taches par zone * Upgrade en Go 1.20 --- ## Évolutions constatées * Refonte du calcul des dépendances sur les taches flow * Durcissement sur les struct close, il semble que le moteur avait des "tolérances", aujourd'hui il ne laisse rien passer * Changement de l'affichage des float : précision à 1 chiffre après la virgule automatique --- ## Problème de dépendances des taches flow ---- ### Rappel sur les tasks flow * des "pre" et "post" task *->* moteur "Cue Flow" * `$id` et `$after` *->* ❌ ~~moteur "Cue Flow"~~ :warning: Cue Flow n'est pas "Cue Tool.Task" ---- Avec trackbone, on charge ce qui se trouve sous les branches * `run.pre_tasks` * `run.pre_apply_tasks` * `run.pre_plan_tasks` * `run.pre_destroy_tasks` * `run.post_tasks` * `run.post_apply_tasks` * `run.post_plan_tasks` * `run.post_destroy_tasks` ---- ### Problème dépendance `_cst_id` Utilisé pour injecter des dépendances auto-magiquement pour les jobs d’un certain type. C’est défini dans `custom_tasks.cue` ---- ``` cue #RDSDBSecret: { vault.#Read // Custom Task ID _cst_id: "RDSDBSecret" // Interface namespace: string // The values of the DB secret data: { // Applications should use the private IP private_ip: string port: string // The DB name name: string username: string password: string ca: string } } ``` ---- **`custom_tasks.cue`** ``` cue run: [=~"_tasks$"]: [string]: { _cst_id: *"" | string if _cst_id == #RDSDBSecret._cst_id { namespace: string url: run.pre_tasks["vault_\(infra_zone.name)"].url path: "secret/zones/\(zone.provider.type)/\(zone.name)/rds/\(namespace)/db" } } ``` ---- ### Problème `if _cst_id == #RDSDBSecret._cst_id` semble poser problème au calcul des dépendances de Cue Flow et provoque des dépendances illogiques. ---- ### Solution Pour retirer ce `if` on utilise un suffixe sur le job ``` cue run: [=~"_tasks$"]: [=~"_db$"]: #RDSDBSecret & { namespace: string url: run.pre_tasks["vault_\(infra_zone.name)"].url path: "secret/zones/\(zone.provider.type)/\(zone.name)/rds/\(namespace)/db" } ``` ---- ### Problème dépendance sur interpolation Lors d’une interpolation, il semble ne pas toujours voir la dépendance. ``` cue login: exec.#Run & { cmd: ["nix-shell", "--quiet", "--run", """ az login -u \(secret.data.username) -p \(secret.data.password) --allow-no-subscriptions --output table """, ] } ``` __je n'ai pas toujours reproduit ce bug 🤷‍♂️__ ---- ### Solution On extrait la dépendance pour la rendre explicite ``` cue login: exec.#Run & { let UserName = tasks.secret.data.username let Password = tasks.secret.data.password cmd: ["nix-shell", "--quiet", "--run", """ az login -u \(UserName) -p \(Password) --allow-no-subscriptions --output table """, ] } ``` ---- ### Problème dépendance sur une valeur concrète Si la dépendance est liée à un élément concret, il ne sera pas vu comme dépendance de la tache. ```cue url: pre_tasks["vault_infra"].url ``` `.url` est défini et ne dépend pas de l’exécution de la tache `vault_infra`. donc notre tache ne dépend pas de `vault_infra`. ---- ### Solution Pour résoudre ce problème on va lier explicitement la tache `vault_infra` à notre tache. ``` cue _vault_login: pre_tasks["vault_infra"] url: _vault_login.url ``` ---- ### Exemple Boucle de dépendance ```mermaid graph LR t0("run.pre_tasks.vault_infra [Ready]") t1("run.pre_tasks.write_environ [Waiting]") t1-->t14 t2("run.pre_tasks.write_plan [Waiting]") t2-->t8 t2-->t9 t2-->t17 t2-->t7 t3("run.pre_tasks.write_apply [Waiting]") t3-->t8 t3-->t9 t3-->t17 t3-->t7 t4("run.pre_tasks.write_plan_destroy [Waiting]") t4-->t8 t4-->t9 t4-->t17 t4-->t7 t5("run.pre_tasks.write_values [Waiting]") t5-->t8 t5-->t9 t5-->t17 t5-->t7 t6("run.pre_tasks.write_plan_tpl [Ready]") t7("run.pre_tasks.harbor_db [Waiting]") t7-->t11 t7-->t14 t7-->t8 t7-->t9 t7-->t17 t7-->t5 t7-->t10 t7-->t13 t7-->t12 t7-->t0 t7-->t18 t8("run.pre_tasks.harbor_bucket [Waiting]") t8-->t13 t8-->t14 t8-->t9 t8-->t17 t8-->t7 t8-->t5 t8-->t11 t8-->t10 t8-->t12 t8-->t0 t8-->t18 t9("run.pre_tasks.harbor_admin [Waiting]") t9-->t13 t10("run.pre_tasks.#quot;vault_infra-stg_approle#quot; [Waiting]") t10-->t0 t11("run.pre_tasks.#quot;vault_infra-stg#quot; [Waiting]") t11-->t10 t12("run.pre_tasks.#quot;vault_ocb-test06_approle#quot; [Waiting]") t12-->t0 t13("run.pre_tasks.#quot;vault_ocb-test06#quot; [Waiting]") t13-->t12 t14("run.pre_tasks.kubeconfig [Ready]") t15("run.pre_tasks.write_destroy [Waiting]") t15-->t8 t15-->t9 t15-->t17 t15-->t7 t16("run.pre_tasks.write_plan_destroy_tpl [Ready]") t17("run.pre_tasks.harbor_secrets [Waiting]") t17-->t13 t18("run.pre_tasks.#quot;k8s_ocb-test06#quot; [Waiting]") t18-->t14 t18-->t0 ``` --- ## Conséquences / Évolutions ---- ### Évolution - Vault ```cue url: pre_tasks["vault_\(zone.name)"].url ``` Devient ``` cue _vault_login: pre_tasks["vault_\(zone.name)"] url: _vault_login.url ``` ---- **script** ``` shell grep "vault.#Write" -rl | xargs \ sed -ri '/vault\.#Write/,+2 s/^(\s+)url:\s+(.*)\.url/\1_vault_login: \2\n\1url: _vault_login.url/' grep "vault.#Read" -rl | xargs \ sed -ri '/vault\.#Read/,+2 s/^(\s+)url:\s+(.*)\.url/\1_vault_login: \2\n\1url: _vault_login.url/' grep "vault.#List" -rl | xargs \ sed -ri '/vault\.#List/,+2 s/^(\s+)url:\s+(.*)\.url/\1_vault_login: \2\n\1url: _vault_login.url/' grep "vault.#Delete" -rl | xargs \ sed -ri '/vault\.#Delete/,+2 s/^(\s+)url:\s+(.*)\.url/\1_vault_login: \2\n\1url: _vault_login.url/' ``` ---- ### Évolution - Kube Config * Ajout du suffixe `_kubecfg` -> #KubectlAction ``` cue [=~"_kubecfg$"]: { $after: [for task_key, _ in run.pre_tasks if task_key =~ "^k8s_" {run.pre_tasks[task_key].$id}] context: *zone.name | string env: KUBECONFIG: run.pre_tasks.kubeconfig.abspath } ``` Exemple ``` cue run: pre_tasks: patchNS: #KubectlAction // devient run: pre_tasks: patchNS_kubecfg: #KubectlAction ``` ---- ### Évolution - DB * Ajout du suffixe `_db` pour `#RDSDBSecret` ``` cue [=~"_db$"]: #RDSDBSecret & { _vault_login: run.pre_tasks["vault_\(infra_zone.name)"] url: _vault_login.url namespace: string path: "secret/zones/\(zone.provider.type)/\(zone.name)/rds/\(namespace)/db" } ``` Exemple ``` cue let DB = run.pre_tasks.db.data // devient let DB = run.pre_tasks.rds_db.data ``` ---- Autre Exemple ``` cue admin_db_read: #RDSDBSecret & {namespace: "customers-manager"} // devient admin_read_db: #RDSDBSecret & {namespace: "customers-manager"} ``` ---- ### Évolution - Bucket * Ajout du suffixe `_bucket` pour `#S3Secret` ``` cue [=~"_bucket$"]: #S3Secret & { namespace: string if zone.product == "caascad" && zone.name != "corp-stg" { _vault_login: run.pre_tasks["vault_\(zone.name)"] url: _vault_login.url } if zone.name == "corp-stg" { _vault_login: run.pre_tasks["vault_corp"] url: _vault_login.url } path: *"secret/zones/\(zone.provider.type)/\(zone.name)/buckets/\(namespace)" | string if zone.product == "caascad" { path: "secret/bucket/\(namespace)" } } ``` ---- Exemple ``` cue let BucketUsages = run.pre_tasks.bucket_usages_read.data // devient let BucketUsages = run.pre_tasks.usages_read_bucket.data ``` --- ## Problème de concurence avec Cue Flow ``` ❯ trackbone plan -c fe_bastion -n8 -t staging panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x65206f] ``` _bug déjà présent sur cue 0.4_ ---- #### Explication Pour chaque tache, Cue Flow attribut un état, qui peut être `Waiting` -> `Ready` -> `Running` -> `Terminated`. * Une tache qui dépend d'une autre est `Waiting`, * Une tache qui n'a plus de dépendances (tous ses inputs sont concrets) est `Ready` ---- A chaque cycle Cue Flow * écrit les outputs des taches `Terminated` dans le contexte Cue * lit les inputs des taches `Ready` dans le même contexte Cue * passe les taches `Ready` à `Running` ---- * Il crée 1 thread par tache `Running` (ce n'est pas configurable) * Il écrit et lit dans le contexte Cue en même temps. 📛 Cue n'est pas thread safe 📛 ---- ### Solution (de contournement) J'ai ajouté un mutex dans trackbone, pour réduire le nombre d'accès parallèles au contexte. Mais comme flow fait également des écritures dans son coin J'ai ajouté des wait pour lui laisser le temps de faire sa tambouille :see_no_evil: --- ## Rappel sur la fermeture (close) des définitions ---- ### Quel est le résultat de ce code ? ``` cue package test #Test: { a: string } #TEST2: { #Test b: string } #TEST3: #Test & { b: string } ``` ---- ### Réponse ``` #TEST3.b: field not allowed: -:5:8 -:14:9 -:15:3 ``` ---- ### Une structure est ouverte On peut fermer une structure avec `close` ``` cue ouvert: { a: string } ferme: close({ a: string }) ``` ---- ### Une définition est fermée Les préfixes `#` ou `_#` représente une définition qui est fermé ``` cue #Ferme: { a: string } #Ouvert: { a: string ... } ``` On peut ouvrir une définition avec l’ellipse `...` ---- ### Fermer une structure ``` cue #Ferme: { a: string } ferme: #Ferme & { a: "ok 🤘" } ouvert: { #Ferme b: string } ``` --- ## Problème du modulo Changement de l'affichage des float Précision à 1 chiffre après la virgule automatique ---- ### Modulo ne retourne pas un entier 🤯 ``` cue let ThirdGroupByte = mod(div(RelativeClusterIndex, nbClusterPerGroup), 256) let GroupCidr = "10.0.\(ThirdGroupByte).0/24" ``` Cue 0.4 GroupCidr = "10.0.128.0/24" Cue 0.7 GroupCidr = "10.0.128.0.0/24" *Non ce n’est pas IPv5* ---- ### Explication * En Go: toutes les fonctions de Math retournent un float * Donc `mod`, `div`, etc en Cue retourne un float ---- #### En cue 0.4 L'interpolation retour la valeur la plus courte "2.5 -> 2.5" "2.0 -> 2" "0 -> 0" ---- #### En cue 0.7 L'interpolation retour la valeur avec une précision de 1 nombre après la virgule (*il me semble*) "2.5 -> 2.5" "2.0 -> 2.0" "0 -> 0.0" ---- ### Solution On fait la conversion en string manuellement ``` cue let ThirdGroupByte = mod(div(RelativeClusterIndex, nbClusterPerGroup), 256) let Str = strconv.FormatFloat(ThirdGroupByte, 102, 0, 64) let GroupCidr = "10.0.\(Str).0/24" ``` ###### Remarque: la syntaxe normale c'est strconv.FormatFloat(ThirdGroupByte, 'f', 0, 64) mais le paramètre 'f' est transformé en bytes au lieu de int à cause de cue. --- ## Conclusion / RAF * Une régression peut en cacher une autre :train: * Je recommande l'abandon de Cue Flow * Garder Cue pour * la validation de la configuration * la génération des inputs pour les jobs * le calcul des dépendances explicitement ---- ### RAF * Finalisation des tests * Upgrade de env-ng et trackbone en simultanée bientôt 🤞 * Traitements parallélisés par zone pour réduire la conso mémoire
{"type":"slide","slideOptions":{"transition":"slide","center":true}}