diff --git a/changelogs/fragments/py3-consul_kv.yaml b/changelogs/fragments/py3-consul_kv.yaml new file mode 100644 index 00000000000..0aa198a2cc2 --- /dev/null +++ b/changelogs/fragments/py3-consul_kv.yaml @@ -0,0 +1,2 @@ +bugfixes: +- Fix for consul_kv idempotence on Python3 https://github.com/ansible/ansible/issues/35893 diff --git a/lib/ansible/modules/clustering/consul_kv.py b/lib/ansible/modules/clustering/consul_kv.py index 967dadc969b..f284b3d6e97 100644 --- a/lib/ansible/modules/clustering/consul_kv.py +++ b/lib/ansible/modules/clustering/consul_kv.py @@ -1,6 +1,7 @@ #!/usr/bin/python # # (c) 2015, Steve Gargan +# (c) 2017, 2018 Genome Research Ltd. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function @@ -28,7 +29,8 @@ requirements: - requests version_added: "2.0" author: -- Steve Gargan (@sgargan) + - Steve Gargan (@sgargan) + - Colin Nolan (@colin-nolan) options: state: description: @@ -122,6 +124,8 @@ EXAMPLES = ''' state: acquire ''' +from ansible.module_utils._text import to_text + try: import consul from requests.exceptions import ConnectionError @@ -132,6 +136,27 @@ except ImportError: from ansible.module_utils.basic import AnsibleModule +def _has_value_changed(consul_client, key, target_value): + """ + Uses the given Consul client to determine if the value associated to the given key is different to the given target + value. + :param consul_client: Consul connected client + :param key: key in Consul + :param target_value: value to be associated to the key + :return: tuple where the first element is the value of the "X-Consul-Index" header and the second is `True` if the + value has changed (i.e. the stored value is not the target value) + """ + index, existing = consul_client.kv.get(key) + if not existing: + return index, True + try: + changed = to_text(existing['Value'], errors='surrogate_or_strict') != target_value + return index, changed + except UnicodeError: + # Existing value was not decodable but all values we set are valid utf-8 + return index, True + + def execute(module): state = module.params.get('state') @@ -157,9 +182,8 @@ def lock(module, state): msg='%s of lock for %s requested but no session supplied' % (state, key)) - index, existing = consul_api.kv.get(key) + index, changed = _has_value_changed(consul_api, key, value) - changed = not existing or (existing and existing['Value'] != value) if changed and not module.check_mode: if state == 'acquire': changed = consul_api.kv.put(key, value, @@ -184,14 +208,14 @@ def add_value(module): key = module.params.get('key') value = module.params.get('value') - index, existing = consul_api.kv.get(key) + index, changed = _has_value_changed(consul_api, key, value) - changed = not existing or (existing and existing['Value'] != value) if changed and not module.check_mode: changed = consul_api.kv.put(key, value, cas=module.params.get('cas'), flags=module.params.get('flags')) + stored = None if module.params.get('retrieve'): index, stored = consul_api.kv.get(key)