Möchte man Anwendungen (Binaries) oder Skripte global aus jedem Verzeichnis heraus aufrufen können, gibt es grundsätzlich zwei Möglichkeiten:
- Ablegen in einem Ordner, der in $PATH bereits hinterlegt ist. Hier gibt es verschiedene Möglichkeiten. /usr/local/bin ist explizit für Programme ausgelegt, die nicht über die Paketverwaltung installiert wurden.
- Man erstellt einen eigenen Ordner, der wiederum an die $PATH Variable angehängt wird.
#1 ist der bevorzugte Weg, wenn die Anwendung systemweit genutzt werden soll und root-Rechte zur Verfügung stehen. Im folgenden Artikel möchten wir uns Szenario 2 anschauen. Händisch würde man die mit einer Zeile wie
export PATH="$PATH:/home/vagrant/bin"
in der .bashrc lösen und anschließend die Datei mit source neu laden. Oder alternativ /etc/environment, falls der Pfad global verfügbar sein soll. In Ansible gestaltet sich dies etwas komplexer.
$PATH für manuelle Nutzung anpassen
Soll $PATH für nachfolgende Anwendungen oder Nutzer verändert werden, kann man den Pfad einfach in der jeweiligen Datei (z.B. /etc/environment) ersetzen. Hier verwenden wir dafür die Ansible-Variable „bin“, die entsprechend gesetzt sein muss:
- name: Add {{bin}} to path
become: yes
lineinfile: >
dest=/etc/environment
state=present
backrefs=yes
regexp='PATH=(["]*)((?!.*?{{bin}}).*?)(["]*)$'
line="PATH=\1\2:{{bin}}\3"
Veränderten $PATH in Ansible nutzen
Wenn wir uns danach per SSH auf der Maschine einloggen, sehen wir /home/vagrant/bin in der Ausgabe von echo $PATH. Doch in Ansible findet dies leider keine Anwendung für die darauffolgenden Befehle, wie folgendes Beispiel zeigt:
vars:
- bin: /home/vagrant/bin
tasks:
- name: Test task
file:
path: "{{bin}}"
state: directory
- name: Add {{bin}} to path
become: yes
lineinfile: >
dest=/etc/environment
state=present
backrefs=yes
regexp='PATH=(["]*)((?!.*?{{bin}}).*?)(["]*)$'
line="PATH=\1\2:{{bin}}\3"
- name: Check path
shell: echo $PATH
- name: Download exa
unarchive:
src: https://github.com/ogham/exa/releases/download/v0.8.0/exa-linux-x86_64-0.8.0.zip
dest: "{{bin}}"
remote_src: yes
- name: Test exa from PATH
shell: exa-linux-x86_64 --version
Das zu erwartende Ergebnis wäre: Die Binary „exa“ wird heruntergeladen, in /home/vagrant/bin entpackt und zeigt uns am Schluss die Version 0.8.0 an, da der Pfad zuvor global in $PATH hinterlegt wurde. Stattdessen ist eine Fehlermeldung zu sehen:
fatal: [default]: FAILED! => {"changed": false, "cmd": "'/bin/bash -l' -c 'exa-linux-x86_64 --version'", "msg": "[Errno 2] No such file or directory", "rc": 2}
Lässt man sich $PATH ausgeben, fehlt der Ordner. Dies liegt daran, dass Ansible eine eigene Shell startet. Darüber hinaus handelt es sich um keine interaktive Login-Shell, die sich anders verhält. Beispielsweise wird die .bashrc nicht ausgeführt.
Lösung für Ansible
Um dieses Problem zu lösen, gibt es zwei Möglichkeiten. Die wohl beste ist, einfach den vollen Pfad anzugeben. Das heißt: Zu beginn definiert man eine Variable, legt den Ordner an und lädt darin die jeweiligen Binaries. In diesem Beispiel ist das die Variable „bin“ aus obigem Beispiel. Sie wird dem exa-Beispielaufruf vorangestellt:
- name: Test exa from PATH
shell: "{{ bin }}/exa-linux-x86_64 --version"
Nun erhalten wir die Version der Applikation:
"stdout_lines": ["exa v0.8.0"]
Alternativ könnte man auch die Variable über Ansibles eigene Umgebungsvariablen setzen:
- name: Test exa from PATH
shell: exa-linux-x86_64 --version
environment:
PATH: "{{ ansible_env.PATH }}:{{bin}}"
Dies gilt allerdings nur für den jeweiligen Task. Möchte man den Pfad in mehreren Tasks verwenden, müsste er jeweils erneut gesetzt werden. Das Angeben eines absoluten Pfades erscheint mir daher einfacher.