mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 12:50:22 -07:00
make hash_params more robust in the face of many corner cases (#18701)
* make hash_params more robust in the face of many corner cases Fixes #18680 Alternative fix to #18681 * add test case for role.hash_params * Add role.hash_params test for more types A set, a generator/iterable, and a Container that is not Iterable.
This commit is contained in:
parent
8e375913b0
commit
5f5ea06ca4
2 changed files with 139 additions and 21 deletions
|
@ -19,10 +19,10 @@
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.compat.six import iteritems
|
||||
|
||||
import collections
|
||||
import os
|
||||
|
||||
from ansible.compat.six import iteritems, binary_type, text_type
|
||||
from ansible.errors import AnsibleError, AnsibleParserError
|
||||
from ansible.playbook.attribute import FieldAttribute
|
||||
from ansible.playbook.base import Base
|
||||
|
@ -41,25 +41,54 @@ __all__ = ['Role', 'hash_params']
|
|||
# the role due to the fact that it would require the use of self
|
||||
# in a static method. This is also used in the base class for
|
||||
# strategies (ansible/plugins/strategy/__init__.py)
|
||||
|
||||
def hash_params(params):
|
||||
if not isinstance(params, dict):
|
||||
if isinstance(params, list):
|
||||
return frozenset(params)
|
||||
"""
|
||||
Construct a data structure of parameters that is hashable.
|
||||
|
||||
This requires changing any mutable data structures into immutable ones.
|
||||
We chose a frozenset because role parameters have to be unique.
|
||||
|
||||
.. warning:: this does not handle unhashable scalars. Two things
|
||||
mitigate that limitation:
|
||||
|
||||
1) There shouldn't be any unhashable scalars specified in the yaml
|
||||
2) Our only choice would be to return an error anyway.
|
||||
"""
|
||||
# Any container is unhashable if it contains unhashable items (for
|
||||
# instance, tuple() is a Hashable subclass but if it contains a dict, it
|
||||
# cannot be hashed)
|
||||
if isinstance(params, collections.Container) and not isinstance(params, (text_type, binary_type)):
|
||||
if isinstance(params, collections.Mapping):
|
||||
try:
|
||||
# Optimistically hope the contents are all hashable
|
||||
new_params = frozenset(params.items())
|
||||
except TypeError:
|
||||
new_params = set()
|
||||
for k, v in params.items():
|
||||
# Hash each entry individually
|
||||
new_params.update((k, hash_params(v)))
|
||||
new_params = frozenset(new_params)
|
||||
|
||||
elif isinstance(params, (collections.Set, collections.Sequence)):
|
||||
try:
|
||||
# Optimistically hope the contents are all hashable
|
||||
new_params = frozenset(params)
|
||||
except TypeError:
|
||||
new_params = set()
|
||||
for v in params:
|
||||
# Hash each entry individually
|
||||
new_params.update(hash_params(v))
|
||||
new_params = frozenset(new_params)
|
||||
else:
|
||||
return params
|
||||
else:
|
||||
s = set()
|
||||
for k,v in iteritems(params):
|
||||
if isinstance(v, dict):
|
||||
s.update((k, hash_params(v)))
|
||||
elif isinstance(v, list):
|
||||
things = []
|
||||
for item in v:
|
||||
things.append(hash_params(item))
|
||||
s.update((k, tuple(things)))
|
||||
else:
|
||||
s.update((k, v))
|
||||
return frozenset(s)
|
||||
# This is just a guess.
|
||||
new_params = frozenset(params)
|
||||
return new_params
|
||||
|
||||
# Note: We do not handle unhashable scalars but our only choice would be
|
||||
# to raise an error there anyway.
|
||||
return frozenset((params,))
|
||||
|
||||
|
||||
class Role(Base, Become, Conditional, Taggable):
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue