Ansible:在插值时如何仅将其中一个变量视为必需变量



假设我们需要从两个变量之一中插入一个值。如果未定义第一个,请使用第二个的值。

- set_fact:
result: >-
var1 | default(var2)

虽然我们也不能接受结果为空,所以我们需要确保至少定义了var1var2中的一个(尽管不是两个)。在这种情况下,我们愿意做这样的事情:

- set_fact:
result: >-
var1 | default(var2 | mandatory)

它看起来不错,但它不起作用。唉,如果var2没有定义,即使var1很好,Ansible 也会引发异常。是否定义var1并不重要,Ansible 只是检查是否定义了var2,而不考虑如果它实际上不打算分配默认值,则不需要此检查。

坦率地说,从我的角度来看,如果定义了var1,则永远不应该进行此var2 | mandatory,但 Ansible 不是一种编程语言,因此我知道它以另一种方式工作。

我唯一的想法是确保事先定义了其中一个变量,因此它如下所示:

- assert:
that:
- (var1 | mandatory) or (var2 | mandatory)
- set_fact:
result: >-
var1 | default(var2)

虽然我不喜欢这样,但由于两个原因,他们在这里。

  1. 我必须添加一个完整的附加步骤(assert)。
  2. 我不喜欢var2没有在set_fact步骤中检查。当然,Ansible 在上一步中检查了它们,但看起来result仍然可能会获得null值,这是不希望的。即使它只是看起来那样,它仍然让我有点恼火。

我想要的是找到一些替代mandatory过滤器的替代方案,但我需要只有在未定义时才考虑这个"准mandatory"var1

有没有办法用一些简短而整洁的成语而不是这两个步骤来解决它?类似于 Perl 中的$var1 // $var2 // any_way_to_raise_an_exception('Neither $var1 nor #var2 is defined, accept our condolences');。:-)

对于我尝试过的情况,它适用于一组外部参数,我还发现mandatory接受未记录的消息,当它死亡时

- debug:
msg: the value is {{ ((var1|default(var2))|mandatory("need var1 or var2") }}
并优先于var1

,如果提供,则使用var2,如果两者都不是,则与fatal: [localhost]: FAILED! => {"msg": "need var1 or var2"}一起死亡

问题是你不必要的mandatory介绍。没有这个,Ansible 的行为是正确的,除非你(非常不明智地)禁用了error_on_undefined_vars

- hosts: localhost
vars:
foo: eh
bar: bee
tasks:
# foo is set, so we get its value
- debug:
msg: "{{ foo | default(bar) }}"
# baz is not set, so we get bar
- debug:
msg: "{{ baz | default(bar) }}"
# foo is set, so we get its value
- debug:
msg: "{{ foo | default(quux) }}"
# neither baz nor quux is set, so we get an error
- debug:
msg: "{{ baz | default(quux) }}"

输出:

TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "eh"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "bee"
}
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "eh"
}
TASK [debug] *******************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'quux' is undefinednnThe error appears to be in '/home/ec2-user/test.yml': line 20, column 7, but maynbe elsewhere in the file depending on the exact syntax problem.nnThe offending line appears to be:nn    # neither baz nor quux is set, so we get an errorn    - debug:n      ^ heren"}

如果禁用了error_on_undefined_vars,则应使用mandatory作为最终筛选器,而不是将其放在非必需值上:

- debug:
msg: "{{ baz | default(quux) | mandatory }}"

或者,如果你要求设置一个和正好一个值,则无法使用assert(因为你正在施加非标准要求),但你应该使用defined测试,而不是mandatory筛选器。

- hosts: localhost
vars:
foo: eh
bar: bee
tasks:
- assert:
that: "[foo, baz] | select('defined') | length == 1"
- assert:
that: "[foo, bar] | select('defined') | length == 1"
ignore_errors: true
- assert:
that: "[baz, quux] | select('defined') | length == 1"
TASK [assert] ******************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
TASK [assert] ******************************************************************
fatal: [localhost]: FAILED! => {
"assertion": "[foo, bar] | select('defined') | length == 1",
"changed": false,
"evaluated_to": false,
"msg": "Assertion failed"
}
...ignoring
TASK [assert] ******************************************************************
fatal: [localhost]: FAILED! => {
"assertion": "[baz, quux] | select('defined') | length == 1",
"changed": false,
"evaluated_to": false,
"msg": "Assertion failed"
}

旁注:如果未定义var1var2,则可以声明默认值。例如

result: "{{ var1|default(var2)|default('default') }}"

但是,问题的用例是:如果未定义var1var2,则失败。


:"不能接受结果为空,因此我们需要确保至少定义了 var1 或 var2 中的一个。

答:断言没有按照您期望的方式工作。如果未定义var1,它将失败。例如

- debug:
var: var1
- debug:
var: var2
- assert:
that: var1|mandatory or var2|mandatory
TASK [debug] *********************************************************************************
ok: [localhost] => 
var1: VARIABLE IS NOT DEFINED!
TASK [debug] *********************************************************************************
ok: [localhost] => 
var2: test
TASK [assert] ********************************************************************************
fatal: [localhost]: FAILED! => 
msg: 'The conditional check ''(var1|mandatory) or (var2|mandatory)'' failed. The error was: Mandatory variable ''var1''  not defined.'

相反,下面的任务可以执行您想要的操作

- assert:
that: (var1|default('')|length > 0) or
(var2|default('')|length > 0)

问:">简短整洁的成语而不是两步?像exception('Neither $var1 nor #var2 is defined')

答:实际上,默认过滤器会这样做。下面的例外说:'var2' is undefined.(我们知道,只有当var1未定义时,才需要var2。为什么要重复它?

- debug:
var: var1
- debug:
var: var2
- set_fact:
result: "{{ var1|default(var2) }}"
TASK [debug] *********************************************************************************
ok: [localhost] => 
var1: VARIABLE IS NOT DEFINED!
TASK [debug] *********************************************************************************
ok: [localhost] => 
var2: VARIABLE IS NOT DEFINED!
TASK [set_fact] ******************************************************************************
fatal: [localhost]: FAILED! => 
msg: |-
The task includes an option with an undefined variable. The error was: 'var2' is undefined

最新更新