Elegant way to handle from_json filter failure with default value

这一生的挚爱 提交于 2019-12-24 15:45:13

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!