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 avecespace
, 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 benja . É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 Flyounet (site web personnel) . Évalué à 1.
En effet, j'ai bien le même comportement avec
echo "" | ansible-lint
[^] # Re: TERM, pipe, stdin
Posté par Flyounet (site web personnel) . Évalué à 1.
Ton idée allait dans la bonne direction :
Cependant,
/dev/null
n'était pas ce qu'il me fallait, il s'agissait de/dev/fd/1
:Un énorme merci pour m'avoir résolu ce problème.
[^] # Re: TERM, pipe, stdin
Posté par Cyril Brulebois (site web personnel) . Évalué à 3.
Attention à
/dev/fd/N
, une option peut être de préférer/proc/self/fd/N
. Cela dépend beaucoup de l'environnement dans lequel on se trouve, un peu plus de détails dans les commentaires du commit 6b2229c6c60d0486 dans systemd.git.Debian Consultant @ DEBAMAX
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.