用默认值处理from_json过滤器失败的好方法

时间:2019-03-27 18:21:18

标签: ansible

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

我有一个角色很普通的任务,用来在sonatype nexus存储库管理器中调用不同的groovy脚本

- 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进一步详细说明我的脚本调用是否导致错误,更改或正常状态。一些常规脚本完全“可感知”,并且会在result中返回一个转义的json字符串,我可以使用该字符串检查错误/更改。但是(暂时)其他一些脚本不是“可感知的”(或者我自己无法更改),它们会在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,我将接受所有建议。

2 个答案:

答案 0 :(得分:1)

(这是从上面的评论中复制/粘贴的)

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

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

过滤器位于filter_plugins/下您的角色中,因此分发不成问题。您甚至可以创建空的ansible角色,这些角色仅定义过滤器,然后将它们包含在其他角色中(通过meta/main.yml)以在其中使用。

答案 1 :(得分:1)

我不确定它是否满足“优雅”的阈值,但我使用了自定义测试插件和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