Forum Programmation.shell [Résolu] Ai-je détecté un bug ou bien suis-je mauvais avec GNU Bash et ansible-lint ?

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
2
29
jan.
2021

Sommaire

Bonjour,

La question est posée, cependant, laissez moi vous conter toute l'histoire :

Il était une fois un travailleur sympa qui voulait aider ses collègues (qui débutent) en leur faisant un petit script pour utiliser un linter sur leurs sources. Les sources étant des roles et playbooks Ansible, il trouve fort intéressant d'utiliser ansible-lint. Pour l'encapsulage Gnu Bash est largement suffisant. Hélas les résultats ne sont pas ceux à quoi il s'attendaient.

Le soucis

Lorsque ansible-lint est appelé dans une boucle while read ... done le résultat affiché de la commande n'est pas correcte. Il l'est dans tout autre cas.

Un point sur les versions

  • Environnement 1 :
    • RHEL : 7.9
    • Gnu Bash : 4.2.46
    • ansible-lint : 3.5.1
    • ansible : 2.9.25 (python 2.7.5)
  • Environnement 2 :
    • Ubuntu : 18.04
    • Gnu Bash : 4.4.20
    • ansible-lint : 3.4.20
    • ansible : 2.9.17 (python 2.7.17)

Hélas, il n'est pas ici question de pouvoir changer les versions installées sur ces systèmes. En tout cas, pas à court terme.

Fichier yaml d'exemple

Le fichier suivant (que l'on pourra facilement appeler test.yml) comporte une erreur dupiclate entry. Dans les fichiers des collègues du travailleur, c'était involontaire, mais pas ici, puisque c'est dans ce cas que les soucis se posent.

g_test_1:
    children:
        g_test_2:
    vars:
        test: "titi"

g_test_2:
    vars:
        test: "toto"

g_test_1:
    children:
        g_test_2:
    vars:
        test: "titi"

Example avec résulat correcte

$: ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml
[WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.
[WARNING]: While constructing a mapping from <unicode string>, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.

Le résultat s'affiche sur deux lignes (c'est à dire que [WARNING] à only. ne représente qu'une ligne).

Example avec résultat incorrecte

 $: while read a; do ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml; done <<< "1"
[WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1,
found a duplicate dict key (g_test_1). Using last defined value only.
[WARNING]: While constructing a mapping from <unicode string>, line 2, column
1, found a duplicate dict key (g_test_1). Using last defined value only.

Comprenez que la valeur donnée ici au while read n'est pas ce qui est utilisé dans le script. Mais l'effet est identique. J'aurais tout aussi pu bien mettre echo '1' | while read a; do ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml; done

L'affichage peut sembler identique, cependant l'effet dépend de la longueur du path et de la clef. Ci dessous un exemple avec des données un peu plus proche de ce que rencontre l'utilisateur :

[WARNING]: While constructing a mapping from
/srv/nfs/userdata/myuser/dev/_Ansible_/project-playbooks-
2021/new_ansible_role/inventory/grps_ap_unk.yml, line 12, column 1, found a
duplicate dict key (g_grps_ap_unk_en_tst_vs_2). Using last defined value only.

Dans ce cas le résultat au travers du while read prend 4 lignes (au lieu d'une).

Encore deux exemples qui fonctionnent

$: for i in 1; do read a <<< "1"; ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml; done
[WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.
[WARNING]: While constructing a mapping from <unicode string>, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.

$: while true; do ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml; break; done
[WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.
[WARNING]: While constructing a mapping from <unicode string>, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.

Analyse

D'après les tests effectués, il semble que le problème ne survient que dans une boucle while read, un while sans le read fonctionne, de même qu'un for.
En outre appeler un read juste avant l'appel ansible-lint ne change rien.
Le while read <variablename> a été agrémenté de différents paramètres pour tester (comme -a, -e, -r et même -s). Il fût même question de tester un IFS=$'\n', sans aucun changement.

Questions

  • Ami visiteur/codeur toi qui a souvent la joie de découvrir comment résoudre les problèmes d'autrui, voudrais-tu bien aider un travailleur (qui voudrait aider ses collègues) ?
  • Est-ce réellement un bug ?
  • Auprès de qui l'ouvrir ? Gnu Bash ? ansible ? ansible-lint ? RedHat ? Ubuntu ?
  • Est-ce que le susdit travailleur va devoir se tailler son problème en pointe et se le carrer ?
  • Oui avec le for ça fonctionne, mais ça oblige à tricher pour les fichiers/chemins avec espace, doit-il vraiment en passer par là ?

Remerciements

Merci ami visiteur/codeur, car si tu n'as pas la réponse tu as déjà perdu ton temps à lire ceci. Et ça ami visiteur/codeur ça me touche (enfin pas moi, mais le travailleur).

  • # TERM, pipe, stdin

    Posté par  . Évalué à 7.

    Ca n'a pas grand chose avec le while en fait, c'est simplement que le fait que tu utilises un pipe.

    Exemple:

    $ echo -e "a\nb" | while read a ; do read b; echo "A $a B $b"; done
    A a B b

    - > les deux read héritent du pipe comme stdin, ils lisent effectivement depuis le même "fichier", chacun une ligne à son tour…

    Toute commande à droite du | (ou le sucre bash <<<"1" qu'il vaut mieux éviter amha) à son stdin remplacé par l'output du pipe.

    Tu aurais le même comportement en utilisant echo "" | ansible....

    Car il y a probablement une logique dans ansible qui teste si le stdin est un terminal ou un pas, et en fonction change l'output (couleurs, etc.). Aussi, dans le cas ou stdin est un terminal, ansible va probablement consulter la valeur de $TERM et/ou $COLUMNS. On va dire c'est une feature, notabug… Dans tous les cas, tu peux essayer de lancer ansible pour avoir quelques chose de plus homogène: env COLUMNS=1000 TERM=xterm ANSIBLE_COLORS=FALSE ansible-lint ... < /dev/null

    • [^] # Re: TERM, pipe, stdin

      Posté par  (site web personnel) . Évalué à 1.

      En effet, j'ai bien le même comportement avec echo "" | ansible-lint

      $: echo '' | ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml
      [WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1,
      found a duplicate dict key (g_test_1). Using last defined value only.
      [WARNING]: While constructing a mapping from <unicode string>, line 2, column
      1, found a duplicate dict key (g_test_1). Using last defined value only.
    • [^] # Re: TERM, pipe, stdin

      Posté par  (site web personnel) . Évalué à 1.

      Ton idée allait dans la bonne direction :

      $: echo '' | ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml < /dev/null
      [WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1,
      found a duplicate dict key (g_test_1). Using last defined value only.
      [WARNING]: While constructing a mapping from <unicode string>, line 2, column
      1, found a duplicate dict key (g_test_1). Using last defined value only.

      Cependant, /dev/null n'était pas ce qu'il me fallait, il s'agissait de /dev/fd/1 :

      $: echo '' | ANSIBLE_NOCOLOR=true ansible-lint -R -p /tmp/test.yml < /dev/fd/1
      [WARNING]: While constructing a mapping from /tmp/test.yml, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.
      [WARNING]: While constructing a mapping from <unicode string>, line 2, column 1, found a duplicate dict key (g_test_1). Using last defined value only.

      Un énorme merci pour m'avoir résolu ce problème.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.