Ansible "template error" 在格式化字符串中使用变量

Ansible "template error" using variable in formatted string

这个 ansible 任务应该检查是否安装了特定版本的 npm 包,如果没有,则安装它。我正在使用的 when 条件涉及带有已定义变量 pm2_version 的格式化字符串,但由于某种原因,我在测试中尝试使用该变量导致了错误。

我希望表达式 f'pm2@{pm2_version}' 应该创建一个格式化的字符串,但显然这种期望是不正确的。在这里使用变量的正确方法是什么?

相关任务如下:

- name: List installed packages
  shell: 'npm list -g --depth=0'
  args:
    executable: /bin/bash
  environment:
    PATH: "{{ ansible_env.HOME }}/.nvm/versions/node/{{node_version}}/bin:{{ ansible_env.PATH }}"
  register: npm_package_list

- name: Install pm2
  shell: 'npm i -g pm2@{{pm2_version}}'
  args:
    executable: /bin/bash
  environment:
    PATH: '{{ ansible_env.HOME }}/.nvm/versions/node/{{node_version}}/bin:{{ ansible_env.PATH }}'
    when: npm_package_list.stdout.find(f'pm2@{pm2_version}') == -1

但是 运行 剧本,我收到以下错误:

FAILED! => {"msg": "The conditional check 'npm_package_list.stdout.find(f'pm2@{pm2_version}') == -1' failed. The error was: template error while templating string: expected token ',', got 'string'. 

你想要做的是 Ansible 中的 anti-pattern。
Ansible是基于idempotency,也就是说,在你的情况下,如果npm包已经安装在请求的版本,Ansible什么都不做,return你一个ok status 而它会 return 你一个 changed status 以防它必须安装它。

但是为此,您必须使用专用 npm module:

- npm:
    name: "{{ 'pm2@%s' | format(pm2_version) }}"
    global: yes
    executable: >- 
      {{ ansible_env.HOME }}/.nvm/versions/node/{{ node_version }}/bin/npm

不过,您的两个(也许三个)问题是:

  • Jinja 没有实现 Python 的所有功能,因此有些功能无法使用,例如 find 字符串
  • 同理,不支持f-strings
  • (可能是问题中的拼写错误)您的 when 关闭错误缩进。

为了在字符串中找到子字符串,您可以使用 in,或者在您的情况下 not in 测试。
为了获得 f-string 格式,您可以使用 format filter,或者只使用字符串连接 ~.

所以,你的情况应该是这样的:

when: "'pm2@%s' | format(pm2_version) not in npm_package_list.stdout"

when: "'pm2@' ~ pm2_version not in npm_package_list.stdout"

给定任务:

- debug:
    var: "'pm2@%s' | format(pm2_version) not in npm_package_list.stdout"
  vars:
    pm2_version: 1.2.4
    npm_package_list:
      stdout: pm2@1.2.3

产生:

ok: [localhost] => 
  '''pm2@%s'' | format(pm2_version) not in npm_package_list.stdout': true