【问题标题】:How can I assign the default Ansible value when environment variable does not exist?当环境变量不存在时,如何分配默认的 Ansible 值?
【发布时间】:2018-05-26 17:00:36
【问题描述】:

我的目标是拥有一个灵活的 Ansible 角色,其中可以按此优先顺序(从大到小)提供变量的值:

  1. 从命令行使用“--extra-vars”
  2. 来自环境变量
  3. 来自角色的默认值

1 & 3 与 Ansible 变量 (http://docs.ansible.com/ansible/latest/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable) 记录的优先顺序相匹配,因此我将注意力集中在环境变量上。使用lookup('env',...) 插件,我可以读取vars/main.yml 中的环境变量,并且优先顺序正是我想要的。

但是,当没有定义环境变量时,lookup 插件返回一个空字符串。这意味着变量被分配了空字符串而不是保持未定义,以便可以分配默认值。

剧本 (var-exp.yml)

- name: Variable experiment
  hosts: all

  tasks:
  - import_role:
      name: ansible-role-variable-experiment

角色(ansible-role-variable-experiment)

tasks/main.yml

- name: Display value of 'location'
  debug:
    msg: 'location is {{ location }}'

defaults/main.yml

location: from-defaults-main-yml

vars/main.yml

# If used, this will override the value in defaults/main.yml, as expected
# location: from-vars-main-yml

# Since lookup returns '' when the environment variable doesn't exist,
# location gets set to '' instead of being left undefined so that the
# default can be used:
# location: "{{ lookup('env', 'LOCATION' ) }}"  # -> location == ''

# When the environment variable does not exist, all of these options generate 
# some value assigned to location so that the default cannot be assigned:
# location: "{{ lookup('env', 'LOCATION' ) | default(None, true) }}"  # -> location == ''
# location: "{{ lookup('env', 'LOCATION' ) | default(omit, true) }}"  # -> location == __omit_place_holder__3e8bdbb6cebc653a758afca99607fcf9ec1f99f4
# location: "{{ lookup('env', 'LOCATION' ) | default('undefined') }}"  # -> location == ''
# location: "{{ lookup('env', 'LOCATION' ) | reject('undefined') }}"  # -> location == <generator object select_or_reject at 0x10caaef00>

# When the environment variable does not exist, these generate a recursive 
# loop that crashes the play:
# location: "{{ lookup('env', 'LOCATION' ) | default(location) }}"
# location: "{{ lookup('env', 'LOCATION' ) | default(location, true) }}"

执行示例

带环境变量:

LOCATION=from-env-variable ansible-playbook ./var-exp.yml

没有环境变量:

ansible-playbook ./var-exp.yml

我无法找到一种干净的方式来实现我的目标。我已经能够想出一种“解决”这个问题的方法:

vars/main.yml

default_location: from-default-array-in-vars-main-yml

location: "{{ lookup('env', 'LOCATION' ) | default(default_location, true) }}"

虽然这似乎完成了我想要的,但现在我在“vars”区域而不是“defaults”区域中定义“defaults”。

Ansible 文档说“如果您正在做的事情看起来太复杂,那可能就是这样。”

所以,我的问题是:有没有更简单/更好/正确的方法来完成这个?或者,我是否在当前实现 Ansible 2.4.2(或lookup('env') 插件)的方式上遇到了限制?

【问题讨论】:

    标签: ansible environment-variables


    【解决方案1】:

    没有办法使用单个变量名来做到这一点。

    我能想到的唯一解决方法是使用set_fact:

    - set_fact:
        location: "{{ lookup('env', 'LOCATION' ) | ternary (lookup('env', 'LOCATION' ), omit) }}"
    

    这样,如果环境变量LOCATION 不存在/为空,任务将不会分配值(omit),并且将使用角色的默认值(即不会被覆盖)。

    您可以在调用角色之前将其放置在 pre_tasks 中,或者在角色顶部 tasks/main.yml


    理由:

    您带来的整个问题是在整个过程中使用单个变量名,但是:

    • Ansible 中的变量是惰性求值的
    • 在vars 部分定义变量后,它的优先级高于defaults 中定义的变量(尽管稍后会进行评估 - 查找插件与问题无关)
    • 无法在 Ansible 中取消定义变量

    Ansible 处理变量的“优先链”(在运行任何任务之前),一旦遇到定义,它就会停止。在评估实际值时,在执行时没有进一步“返回”。


    备注:

     我已经想出了一种“解决”这个问题的方法:

    [ ]

    现在我在“vars”区域而不是“defaults”区域中定义“defaults”。

    但你不必这样做。无需将您的default_location 变量放入vars。可以在defaults中定义(值在执行时使用)。

    【讨论】:

    • 首先,谢谢您——您的解决方法确实有效。
    • 恐怕我不理解“单个变量名”的问题。我想要的是能够在角色的任务中使用变量(比如说path_to_file_destination)(因为无论定义的路径如何,过程都是相同的)。但是,我想(1)在角色中定义一个默认路径,(2)让使用这个角色的人能够在环境变量中定义这个路径,(3)让他们通过提供临时覆盖这些值中的任何一个 - -额外变量。你是说我应该为每个变量使用不同命名的变量吗?
    • 如果你使用location和default_location——你使用两个变量没有问题。如果您坚持使用单个变量名 (locacion),则会出现覆盖默认优先级的问题。
    • 啊。知道了。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    • 2022-12-31
    • 1970-01-01
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多