如何在 Ansible 的 shell 配置中防止条件命令评估?

How to prevent conditioned command evaluation in Ansible's shell provisioning?

我有以下多机 Vagrant 设置:

Vagrant.configure(2) do |config|

  config.vm.define "xfcevm" do |xfcevm|
    xfcevm.vm.box = "generic/ubuntu1904"
    xfcevm.vm.hostname = "xfcevm"
    config.vm.provider "virtualbox" do |vb|
      vb.name = "ubuntu-xfce"
    end
  end

  config.vm.define "kdevm" do |kdevm|
    kdevm.vm.box = "generic/arch"
    kdevm.vm.hostname = "kdevm"
    config.vm.provider "virtualbox" do |vb|
      vb.name = "arch-kde"
    end
  end

  ## only Arch doesn't ship with Python installed
  config.vm.provision "shell", inline: "which python || sudo pacman --noconfirm -S python"

  config.vm.provider "virtualbox" do |vb|
    vb.gui = true
    vb.memory = "2048"
    vb.cpus = 1
    vb.customize ["modifyvm", :id, "--vram", "32"]
  end

  config.vm.provision "ansible" do |ansible|
    ansible.verbose = "v"
    ansible.compatibility_mode = "2.0"
    ansible.playbook = "setup.yml"
    ansible.inventory_path = "hosts"
  end

end

由于 Arch vagrant box 不包含 Python,所以我创建了一个内联 shell provision 命令来测试 Python 的存在(通过 which python),如果计算结果为 false,那么 Python 的 pacman 安装应该随之而来。对于回显输出,不应评估第二部分,运行 终端中的命令就是这种情况。

但是 shell provisioner 无论如何都会评估 || 之后的部分,无论 Python 是否存在。在 Ubuntu 的情况下,它引发了 pacman 未安装的明显错误:

$ vagrant up --provision
Bringing machine 'xfcevm' up with 'virtualbox' provider...
Bringing machine 'kdevm' up with 'virtualbox' provider...
==> xfcevm: Checking if box 'generic/ubuntu1904' version '1.9.34' is up to date...
==> xfcevm: Running provisioner: shell...
    xfcevm: Running: inline script
    xfcevm: sudo
    xfcevm: : 
    xfcevm: pacman: command not found
The SSH command responded with a non-zero exit status. Vagrant
assumes that this means the command failed. The output for this command
should be in the log above. Please read the output to determine what
went wrong.

用一个简单的 if 语句代替 ||:

也是如此
config.vm.provision "shell", inline: "if [ ! `which python` ]; then sudo pacman --noconfirm -S python; fi"

实际问题是两件事的结合:在 Arch 中使用 python 作为 Python3 的别名(与 Ubuntu 相反,其中 python 是别名对于 Python2) 以及 Ubuntu 不附带 Python2 的事实(我们不需要它用于 Ansible 目的,我们使用 Python3)。

所以解决方案是同时检查 pythonpython3:

config.vm.provision "shell", inline: "if [ ! `which python`] && [ ! `which python3` ]; then sudo pacman --noconfirm -S python; fi"

在我的机器上使用以下 Vagranfile 进行测试后:

 Vagrant.configure(2) do |config|
     config.vm.box = "generic/ubuntu1904"
     config.vm.hostname = "test"
     config.vm.network "private_network", type: "dhcp"
     config.vm.synced_folder ".", "/vagrant", disabled: true
     config.vm.provider "virtualbox" do |v|
         v.memory = 2048
         v.cpus = 2
     end
     config.vm.provision "default", type: "shell", inline: "which python", run: "always"
 end

这是 vagrant up 的结果(仅最后几行)

==> default: Running provisioner: default (shell)...
    default: Running: inline script
The SSH command responded with a non-zero exit status. Vagrant
assumes that this means the command failed. The output for this command
should be in the log above. Please read the output to determine what
went wrong.

交互式检查:

$ vagrant ssh
Last login: Wed Oct  9 15:33:22 2019 from 10.0.2.2
vagrant@test:~$ which python
vagrant@test:~$ echo $?
1
vagrant@test:~$ which python3
/usr/bin/python3
vagrant@test:~$ echo $?
0
vagrant@test:~$

结论:你得到的是完全连贯的。 python 在您的 ubuntu 图像中不存在,因此您的命令的其余部分是 运行。你的方案有问题,你需要另辟蹊径。

在您的上下文中,我会尝试 运行 ansible 中的所有内容。这里只是一个想法的例子,我没有测试,肯定可以大大改进

- name: Make sure machine can run ansible
  hosts: all
  gather_facts: false

  tasks:
    - block:
        - name: Try to ansible-ping the host. Consider python is not installed otherwise
          ping:

      rescue:
        - name: No python available, install with low-level and dirty command
          become: true
          become_method: sudo
          raw: pacman --noconfirm -S python