问题
I'm seeking for your ideas to elegantly handle a from_json filter failure when it happens.
I have a generic task in an ansible role that I use to call different groovy script in sonatype nexus repository manager (the full role is available on 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
The scripts I call all return a json map
{
"name": "name of the script"
"result": "whatever was used as a groovy return statement"
}
Note: there is no way to add anything else to this map from groovy, I can only push data back to ansible in the result
Now, I want to use result to further detail if my script call leads to an error, a change or an ok status in ansible. Some groovy scripts are fully "ansible aware" and will return in result an escaped json string I can use to check for error/changes. But (for time being...) some other scripts are not "ansible aware" (or I cannot change them myself) and will return in result a simple string (without any usable information in most cases).
Now my real problem: If I get a json result I want to use it to check for failure or change. If its not a json result, I'll just rely on the http 200 for a success (until the script can be fixed).
I was almost there with the following options to my task:
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
Unfortunatelly, when result is a simple string, from_json will fire an error (Expecting value: line 1 column 1 (char 0)) before the default can be applied and my playbook ends there.
My current workaround is to add another condition to check if result starts with a { before trying to read json but I'm not really happy with this (as the json string might still be corrupted and still lead to an error)
If any of you have experience/idea on how to gracefully handle this json decode error with a default value or to nicelly check if a string can be decoded as json in ansible, I'll take all suggestions.
回答1:
I found out that writing complex stuff in failed_when/changed_when can get easily out of hand. Have you tried creating a filter and then do something like failed_when: script_run | my_role_failed ?
https://gist.github.com/tuxfight3r/37048ba536575277f5f4d26813d69489
filters live inside your ansible role, under filter_plugins/, so distribution shouldnt be an issue. You can go as far as creating empty ansible roles that only define filters and then include them in other roles (through meta/main.yml) to be used there.
回答2:
I'm not sure it meets the "elegant" threshold, but I used a custom test plugin, and a jinja2 {{ <something> if <test> else <somethingelse> }} expression to solve this problem:
# 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
Output:
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
来源:https://stackoverflow.com/questions/55384113/elegant-way-to-handle-from-json-filter-failure-with-default-value