使用默认值处理from_json过滤器故障的优雅方法



我正在寻找您的想法,以便在发生from_json过滤器故障时优雅地处理它。

我有一个 ansible 角色的通用任务,我用它来调用 sonatype nexus 存储库管理器中的不同 groovy 脚本(完整角色在 github 上可用)

- name: Calling Groovy script {{ script_name }}
uri:
url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}
{{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/{{ script_name }}/run"
user: 'admin'
password: "{{ current_nexus_admin_password }}"
headers:
Content-Type: "text/plain"
method: POST
force_basic_auth: yes
validate_certs: "{{ nexus_api_validate_certs }}"
body: "{{ args | to_json }}"
register: script_run

我调用的脚本都返回一个 json 映射

{
"name": "name of the script",
"result": "whatever was used as a groovy return statement"
}

注意:没有办法从 groovy 向这张地图添加任何其他内容,我只能在result中将数据推送回 ansible

我想使用result来进一步详细说明我的脚本调用是否会导致 ansible 中的错误、更改或正常状态。一些时髦的脚本是完全"ansible 感知"的,并且会result返回一个转义的 json 字符串,我可以用来检查错误/更改。 下面是一个半虚构的例子:

{
"name": "create_repos_from_list",
"result": "{"changed": false, "error": false, "action_details": [{"name": "my_registry", "format": "docker", "type": "hosted", "status": "no change"}]}" 
}

但是(暂时...)其他一些脚本不是"ansible aware"(或者我自己无法更改它们),并且会result一个简单的字符串返回(在大多数情况下没有任何可用信息)。

现在我真正的问题:如果我得到一个 json 结果,我想用它来检查失败或更改。如果它不是 json 结果,我将依靠 http 200 来取得成功(直到脚本可以修复)。

我几乎在那里为我的任务提供了以下选项:

failed_when: >-
script_run.status != 200
or
(script_run.json.result | from_json | default({})).error | default(false) | bool
changed_when: >-
(script_run.json.result | from_json | default({})).changed | default(false) | bool

不幸的是,当result是一个简单的字符串时,from_json会触发一个错误(Expecting value: line 1 column 1 (char 0)),然后才能应用默认值,我的剧本到此结束。

我目前的解决方法是在尝试读取 json 之前添加另一个条件来检查result是否以{开头,但我对此并不满意(因为 json 字符串可能仍然损坏并且仍然导致错误)

如果你们中的任何人对如何使用默认值优雅地处理此 json 解码错误或很好地检查字符串是否可以在 ansible 中解码为 json 有经验/想法,我会接受所有建议。

我发现用failed_when/changed_when写复杂的东西很容易失控。您是否尝试过创建过滤器,然后执行类似failed_when: script_run | my_role_failed的操作?

https://gist.github.com/tuxfight3r/37048ba536575277f5f4d26813d69489

过滤器存在于您的 Ansible 角色中,在filter_plugins/下,因此分发应该不是问题。您可以创建仅定义过滤器的空 ansible 角色,然后将它们包含在要在那里使用的其他角色(通过meta/main.yml)中。

我不确定它是否符合"优雅"阈值,但我使用了一个自定义测试插件和一个 jinja2{{ <something> if <test> else <somethingelse> }}表达式来解决这个问题:

# test_plugins/is_json.py
import json
def is_json(input_string):
''' Tests if input_string is valid JSON
Returns a bool
'''
try:
json.loads(input_string)
return True
except:
return False
class TestModule(object):
def tests(self):
return {
'json': is_json,
}
# test_json_test.yaml
---
- name: 'Test json test'
hosts: localhost
tasks:
- name: 'Give us an empty dict on invalid json'
with_items:
- ' {"somekey": "somevalue"} ' # valid JSON
#  ^-extra space here to ensure Ansible treats this as a string
- '{"somekey": "somevalue"}' # valid JSON
- ' {     }' # valid
- ' [     ]' # valid
- '[]' # valid
- "I'm not valid JSON" # invalid JSON
debug:
msg: '{{ item if item is json else "{}" }}'
#                        ^^^^-our test

输出:

PLAY [Test json test] *************
TASK [Gathering Facts] ************
ok: [localhost]
TASK [Give us an empty dict on invalid json] ************
ok: [localhost] => (item= {"somekey": "somevalue"} ) => {
"msg": " {"somekey": "somevalue"} "
}
ok: [localhost] => (item={'somekey': 'somevalue'}) => {
"msg": {
"somekey": "somevalue"
}
}
ok: [localhost] => (item= {     }) => {
"msg": " {     }"
}
ok: [localhost] => (item= [     ]) => {
"msg": " [     ]"
}
ok: [localhost] => (item=[]) => {
"msg": []
}
ok: [localhost] => (item=I'm not valid JSON) => {
"msg": {}
}
PLAY RECAP ****************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

最新更新