mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-10-01 22:13:21 -07:00
Add filters to_yaml and to_nice_yaml (#10784)
* Add filters to_yaml and to_nice_yaml. * Allow to redact sensitive values. * Add basic tests. * Work around https://github.com/ansible/ansible/issues/85783. * Cleanup.
This commit is contained in:
parent
3574b3fa93
commit
062b63bda5
10 changed files with 532 additions and 0 deletions
89
plugins/filter/to_nice_yaml.yml
Normal file
89
plugins/filter/to_nice_yaml.yml
Normal file
|
@ -0,0 +1,89 @@
|
|||
# Copyright (c) Contributors to the Ansible project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
DOCUMENTATION:
|
||||
name: to_nice_yaml
|
||||
author:
|
||||
- Ansible Core Team
|
||||
- Felix Fontein (@felixfontein)
|
||||
version_added: 11.3.0
|
||||
short_description: Convert variable to YAML string
|
||||
description:
|
||||
- Converts an Ansible variable into a YAML string representation, without preserving vaulted strings as P(ansible.builtin.to_yaml#filter).
|
||||
- This filter functions as a wrapper to the L(Python PyYAML library, https://pypi.org/project/PyYAML/)'s C(yaml.dump) function.
|
||||
positional: _input
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- A variable or expression that returns a data structure.
|
||||
type: raw
|
||||
required: true
|
||||
indent:
|
||||
description:
|
||||
- Number of spaces to indent Python structures, mainly used for display to humans.
|
||||
type: integer
|
||||
default: 2
|
||||
sort_keys:
|
||||
description:
|
||||
- Affects sorting of dictionary keys.
|
||||
default: true
|
||||
type: bool
|
||||
default_style:
|
||||
description:
|
||||
- Indicates the style of the scalar.
|
||||
choices:
|
||||
- ''
|
||||
- "'"
|
||||
- '"'
|
||||
- '|'
|
||||
- '>'
|
||||
type: string
|
||||
canonical:
|
||||
description:
|
||||
- If set to V(true), export tag type to the output.
|
||||
type: bool
|
||||
width:
|
||||
description:
|
||||
- Set the preferred line width.
|
||||
type: integer
|
||||
line_break:
|
||||
description:
|
||||
- Specify the line break.
|
||||
type: string
|
||||
encoding:
|
||||
description:
|
||||
- Specify the output encoding.
|
||||
type: string
|
||||
explicit_start:
|
||||
description:
|
||||
- If set to V(true), adds an explicit start using C(---).
|
||||
type: bool
|
||||
explicit_end:
|
||||
description:
|
||||
- If set to V(true), adds an explicit end using C(...).
|
||||
type: bool
|
||||
redact_sensitive_values:
|
||||
description:
|
||||
- If set to V(true), vaulted strings are replaced by V(<redacted>) instead of being decrypted.
|
||||
- With future ansible-core versions, this can extend to other strings tagged as sensitive.
|
||||
- B(Note) that with ansible-core 2.18 and before this might not yield the expected result
|
||||
since these versions of ansible-core strip the vault information away from strings that are
|
||||
part of more complex data structures specified in C(vars).
|
||||
type: bool
|
||||
default: false
|
||||
notes:
|
||||
- More options may be available, see L(PyYAML documentation, https://pyyaml.org/wiki/PyYAMLDocumentation) for details.
|
||||
- >-
|
||||
These parameters to C(yaml.dump) are not accepted, as they are overridden internally: O(ignore:allow_unicode).
|
||||
|
||||
EXAMPLES: |
|
||||
---
|
||||
# Dump variable in a template to create a YAML document
|
||||
value: "{{ github_workflow | community.general.to_nice_yaml }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description:
|
||||
- The YAML serialized string representing the variable structure inputted.
|
||||
type: string
|
113
plugins/filter/to_yaml.py
Normal file
113
plugins/filter/to_yaml.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
# Copyright (c) Contributors to the Ansible project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
from collections.abc import Mapping, Set
|
||||
|
||||
from yaml import dump
|
||||
try:
|
||||
from yaml.cyaml import CSafeDumper as SafeDumper
|
||||
except ImportError:
|
||||
from yaml import SafeDumper
|
||||
|
||||
from ansible.module_utils.common.collections import is_sequence
|
||||
try:
|
||||
# This is ansible-core 2.19+
|
||||
from ansible.utils.vars import transform_to_native_types
|
||||
from ansible.parsing.vault import VaultHelper, VaultLib
|
||||
except ImportError:
|
||||
transform_to_native_types = None
|
||||
|
||||
from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode
|
||||
from ansible.utils.unsafe_proxy import AnsibleUnsafe
|
||||
|
||||
|
||||
def _to_native_types_compat(value: t.Any, *, redact_value: str | None) -> t.Any:
|
||||
"""Compatibility function for ansible-core 2.18 and before."""
|
||||
if value is None:
|
||||
return value
|
||||
if isinstance(value, AnsibleUnsafe):
|
||||
# This only works up to ansible-core 2.18:
|
||||
return _to_native_types_compat(value._strip_unsafe(), redact_value=redact_value)
|
||||
# But that's fine, since this code path isn't taken on ansible-core 2.19+ anyway.
|
||||
if isinstance(value, Mapping):
|
||||
return {
|
||||
_to_native_types_compat(key, redact_value=redact_value): _to_native_types_compat(val, redact_value=redact_value)
|
||||
for key, val in value.items()
|
||||
}
|
||||
if isinstance(value, Set):
|
||||
return {_to_native_types_compat(elt, redact_value=redact_value) for elt in value}
|
||||
if is_sequence(value):
|
||||
return [_to_native_types_compat(elt, redact_value=redact_value) for elt in value]
|
||||
if isinstance(value, AnsibleVaultEncryptedUnicode):
|
||||
if redact_value is not None:
|
||||
return redact_value
|
||||
# This only works up to ansible-core 2.18:
|
||||
return value.data
|
||||
# But that's fine, since this code path isn't taken on ansible-core 2.19+ anyway.
|
||||
if isinstance(value, bytes):
|
||||
return bytes(value)
|
||||
if isinstance(value, str):
|
||||
return str(value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _to_native_types(value: t.Any, *, redact: bool) -> t.Any:
|
||||
if isinstance(value, Mapping):
|
||||
return {_to_native_types(k, redact=redact): _to_native_types(v, redact=redact) for k, v in value.items()}
|
||||
if is_sequence(value):
|
||||
return [_to_native_types(e, redact=redact) for e in value]
|
||||
if redact:
|
||||
ciphertext = VaultHelper.get_ciphertext(value, with_tags=False)
|
||||
if ciphertext and VaultLib.is_encrypted(ciphertext):
|
||||
return "<redacted>"
|
||||
return transform_to_native_types(value, redact=redact)
|
||||
|
||||
|
||||
def remove_all_tags(value: t.Any, *, redact_sensitive_values: bool = False) -> t.Any:
|
||||
"""
|
||||
Remove all tags from all values in the input.
|
||||
|
||||
If ``redact_sensitive_values`` is ``True``, all sensitive values will be redacted.
|
||||
"""
|
||||
if transform_to_native_types is not None:
|
||||
return _to_native_types(value, redact=redact_sensitive_values)
|
||||
|
||||
return _to_native_types_compat(
|
||||
value,
|
||||
redact_value="<redacted>" if redact_sensitive_values else None, # same string as in ansible-core 2.19 by transform_to_native_types()
|
||||
)
|
||||
|
||||
|
||||
def to_yaml(value: t.Any, *, redact_sensitive_values: bool = False, default_flow_style: bool | None = None, **kwargs) -> str:
|
||||
"""Serialize input as terse flow-style YAML."""
|
||||
return dump(
|
||||
remove_all_tags(value, redact_sensitive_values=redact_sensitive_values),
|
||||
Dumper=SafeDumper,
|
||||
allow_unicode=True,
|
||||
default_flow_style=default_flow_style,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def to_nice_yaml(value: t.Any, *, redact_sensitive_values: bool = False, indent: int = 2, default_flow_style: bool = False, **kwargs) -> str:
|
||||
"""Serialize input as verbose multi-line YAML."""
|
||||
return to_yaml(
|
||||
value,
|
||||
redact_sensitive_values=redact_sensitive_values,
|
||||
default_flow_style=default_flow_style,
|
||||
indent=indent,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'to_yaml': to_yaml,
|
||||
'to_nice_yaml': to_nice_yaml,
|
||||
}
|
92
plugins/filter/to_yaml.yml
Normal file
92
plugins/filter/to_yaml.yml
Normal file
|
@ -0,0 +1,92 @@
|
|||
# Copyright (c) Contributors to the Ansible project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
DOCUMENTATION:
|
||||
name: to_yaml
|
||||
author:
|
||||
- Ansible Core Team
|
||||
- Felix Fontein (@felixfontein)
|
||||
version_added: 11.3.0
|
||||
short_description: Convert variable to YAML string
|
||||
description:
|
||||
- Converts an Ansible variable into a YAML string representation, without preserving vaulted strings as P(ansible.builtin.to_yaml#filter).
|
||||
- This filter functions as a wrapper to the L(Python PyYAML library, https://pypi.org/project/PyYAML/)'s C(yaml.dump) function.
|
||||
positional: _input
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- A variable or expression that returns a data structure.
|
||||
type: raw
|
||||
required: true
|
||||
indent:
|
||||
description:
|
||||
- Number of spaces to indent Python structures, mainly used for display to humans.
|
||||
type: integer
|
||||
sort_keys:
|
||||
description:
|
||||
- Affects sorting of dictionary keys.
|
||||
default: true
|
||||
type: bool
|
||||
default_style:
|
||||
description:
|
||||
- Indicates the style of the scalar.
|
||||
choices:
|
||||
- ''
|
||||
- "'"
|
||||
- '"'
|
||||
- '|'
|
||||
- '>'
|
||||
type: string
|
||||
canonical:
|
||||
description:
|
||||
- If set to V(true), export tag type to the output.
|
||||
type: bool
|
||||
width:
|
||||
description:
|
||||
- Set the preferred line width.
|
||||
type: integer
|
||||
line_break:
|
||||
description:
|
||||
- Specify the line break.
|
||||
type: string
|
||||
encoding:
|
||||
description:
|
||||
- Specify the output encoding.
|
||||
type: string
|
||||
explicit_start:
|
||||
description:
|
||||
- If set to V(true), adds an explicit start using C(---).
|
||||
type: bool
|
||||
explicit_end:
|
||||
description:
|
||||
- If set to V(true), adds an explicit end using C(...).
|
||||
type: bool
|
||||
redact_sensitive_values:
|
||||
description:
|
||||
- If set to V(true), vaulted strings are replaced by V(<redacted>) instead of being decrypted.
|
||||
- With future ansible-core versions, this can extend to other strings tagged as sensitive.
|
||||
- B(Note) that with ansible-core 2.18 and before this might not yield the expected result
|
||||
since these versions of ansible-core strip the vault information away from strings that are
|
||||
part of more complex data structures specified in C(vars).
|
||||
type: bool
|
||||
default: false
|
||||
notes:
|
||||
- More options may be available, see L(PyYAML documentation, https://pyyaml.org/wiki/PyYAMLDocumentation) for details.
|
||||
- >-
|
||||
These parameters to C(yaml.dump) are not accepted, as they are overridden internally: O(ignore:allow_unicode).
|
||||
|
||||
EXAMPLES: |
|
||||
---
|
||||
# Dump variable in a template to create a YAML document
|
||||
value: "{{ github_workflow | community.general.to_yaml }}"
|
||||
|
||||
---
|
||||
# Same as above but 'prettier' (equivalent to community.general.to_nice_yaml filter)
|
||||
value: "{{ docker_config | community.general.to_yaml(indent=2) }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description:
|
||||
- The YAML serialized string representing the variable structure inputted.
|
||||
type: string
|
Loading…
Add table
Add a link
Reference in a new issue