forked from CCCHH/ansible-infra
Vendor Galaxy Roles and Collections
This commit is contained in:
parent
c1e1897cda
commit
2aed20393f
3553 changed files with 387444 additions and 2 deletions
117
ansible_collections/community/sops/plugins/modules/load_vars.py
Normal file
117
ansible_collections/community/sops/plugins/modules/load_vars.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Felix Fontein (@felixfontein)
|
||||
module: load_vars
|
||||
short_description: Load SOPS-encrypted variables from files, dynamically within a task
|
||||
version_added: '0.1.0'
|
||||
description:
|
||||
- Loads SOPS-encrypted YAML/JSON variables dynamically from a file during task runtime.
|
||||
- To assign included variables to a different host than C(inventory_hostname), use C(delegate_to) and set C(delegate_facts=true).
|
||||
options:
|
||||
file:
|
||||
description:
|
||||
- The file name from which variables should be loaded.
|
||||
- If the path is relative, it will look for the file in C(vars/) subdirectory of a role or relative to playbook.
|
||||
type: path
|
||||
name:
|
||||
description:
|
||||
- The name of a variable into which assign the included vars.
|
||||
- If omitted (V(null)) they will be made top level vars.
|
||||
type: str
|
||||
expressions:
|
||||
description:
|
||||
- This option controls how Jinja2 expressions in values in the loaded file are handled.
|
||||
- If set to V(ignore), expressions will not be evaluated, but treated as regular strings.
|
||||
- If set to V(evaluate-on-load), expressions will be evaluated on execution of this module, in other words, when the
|
||||
file is loaded.
|
||||
- If set to V(lazy-evaluation), expressions will be lazily evaluated. This requires ansible-core 2.19 or newer
|
||||
and is the same behavior than M(ansible.builtin.include_vars). V(lazy-evaluation) has been added in community.sops 2.2.0.
|
||||
type: str
|
||||
default: ignore
|
||||
choices:
|
||||
- ignore
|
||||
- evaluate-on-load
|
||||
- lazy-evaluation
|
||||
extends_documentation_fragment:
|
||||
- community.sops.sops
|
||||
- community.sops.attributes
|
||||
- community.sops.attributes.facts
|
||||
- community.sops.attributes.flow
|
||||
attributes:
|
||||
action:
|
||||
support: full
|
||||
async:
|
||||
support: none
|
||||
details:
|
||||
- This action runs completely on the controller.
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: N/A
|
||||
details:
|
||||
- This action does not modify state.
|
||||
facts:
|
||||
support: full
|
||||
idempotent:
|
||||
support: N/A
|
||||
details:
|
||||
- The action has no C(changed) state.
|
||||
seealso:
|
||||
- module: ansible.builtin.set_fact
|
||||
- module: ansible.builtin.include_vars
|
||||
- ref: playbooks_delegation
|
||||
description: More information related to task delegation.
|
||||
- plugin: community.sops.sops
|
||||
plugin_type: lookup
|
||||
description: The sops lookup can be used decrypt SOPS-encrypted files.
|
||||
- plugin: community.sops.decrypt
|
||||
plugin_type: filter
|
||||
description: The decrypt filter can be used to descrypt SOPS-encrypted in-memory data.
|
||||
- plugin: community.sops.sops
|
||||
plugin_type: vars
|
||||
description: The sops vars plugin can be used to load SOPS-encrypted host or group variables.
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
---
|
||||
- name: Include variables of stuff.sops.yaml into the 'stuff' variable
|
||||
community.sops.load_vars:
|
||||
file: stuff.sops.yaml
|
||||
name: stuff
|
||||
expressions: evaluate-on-load # interpret Jinja2 expressions in stuf.sops.yaml on load-time!
|
||||
|
||||
- name: Conditionally decide to load in variables into 'plans' when x is 0, otherwise do not
|
||||
community.sops.load_vars:
|
||||
file: contingency_plan.sops.yaml
|
||||
name: plans
|
||||
expressions: ignore # do not interpret possible Jinja2 expressions
|
||||
when: x == 0
|
||||
|
||||
- name: Load variables into the global namespace
|
||||
community.sops.load_vars:
|
||||
file: contingency_plan.sops.yaml
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
ansible_facts:
|
||||
description: Variables that were included and their values.
|
||||
returned: success
|
||||
type: dict
|
||||
sample: {'variable': 'value'}
|
||||
ansible_included_var_files:
|
||||
description: A list of files that were successfully included.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample: [/path/to/file.sops.yaml]
|
||||
"""
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020, Felix Fontein <felix@fontein.de>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Felix Fontein (@felixfontein)
|
||||
module: sops_encrypt
|
||||
short_description: Encrypt data with SOPS
|
||||
version_added: '0.1.0'
|
||||
description:
|
||||
- Allows to encrypt binary data (Base64 encoded), text data, JSON or YAML data with SOPS.
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- The SOPS encrypt file.
|
||||
type: path
|
||||
required: true
|
||||
force:
|
||||
description:
|
||||
- Force rewriting the encrypted file.
|
||||
type: bool
|
||||
default: false
|
||||
content_text:
|
||||
description:
|
||||
- The data to encrypt. Must be a Unicode text.
|
||||
- Please note that the module might not be idempotent if the text can be parsed as JSON or YAML.
|
||||
- Exactly one of O(content_text), O(content_binary), O(content_json), and O(content_yaml) must be specified.
|
||||
type: str
|
||||
content_binary:
|
||||
description:
|
||||
- The data to encrypt. Must be L(Base64 encoded,https://en.wikipedia.org/wiki/Base64) binary data.
|
||||
- Please note that the module might not be idempotent if the data can be parsed as JSON or YAML.
|
||||
- Exactly one of O(content_text), O(content_binary), O(content_json), and O(content_yaml) must be specified.
|
||||
type: str
|
||||
content_json:
|
||||
description:
|
||||
- The data to encrypt. Must be a JSON dictionary.
|
||||
- Exactly one of O(content_text), O(content_binary), O(content_json), and O(content_yaml) must be specified.
|
||||
type: dict
|
||||
content_yaml:
|
||||
description:
|
||||
- The data to encrypt. Must be a YAML dictionary.
|
||||
- Please note that Ansible only allows to pass data that can be represented as a JSON dictionary.
|
||||
- Exactly one of O(content_text), O(content_binary), O(content_json), and O(content_yaml) must be specified.
|
||||
type: dict
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.files
|
||||
- community.sops.sops
|
||||
- community.sops.sops.encrypt_specific
|
||||
- community.sops.attributes
|
||||
- community.sops.attributes.files
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: none
|
||||
safe_file_operations:
|
||||
support: full
|
||||
idempotent:
|
||||
support: full
|
||||
seealso:
|
||||
- plugin: community.sops.sops
|
||||
plugin_type: lookup
|
||||
description: The sops lookup can be used decrypt SOPS-encrypted files.
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
---
|
||||
- name: Encrypt a secret text
|
||||
community.sops.sops_encrypt:
|
||||
path: text-data.sops
|
||||
content_text: This is a secret text.
|
||||
|
||||
- name: Encrypt the contents of a file
|
||||
community.sops.sops_encrypt:
|
||||
path: binary-data.sops
|
||||
content_binary: "{{ lookup('ansible.builtin.file', '/path/to/file', rstrip=false) | b64encode }}"
|
||||
|
||||
- name: Encrypt some datastructure as YAML
|
||||
community.sops.sops_encrypt:
|
||||
path: stuff.sops.yaml
|
||||
content_yaml: "{{ result }}"
|
||||
"""
|
||||
|
||||
RETURN = r"""#"""
|
||||
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
|
||||
from ansible_collections.community.sops.plugins.module_utils.io import write_file
|
||||
from ansible_collections.community.sops.plugins.module_utils.sops import Sops, SopsError, get_sops_argument_spec
|
||||
|
||||
try:
|
||||
import yaml
|
||||
YAML_IMP_ERR = None
|
||||
HAS_YAML = True
|
||||
except ImportError:
|
||||
YAML_IMP_ERR = traceback.format_exc()
|
||||
HAS_YAML = False
|
||||
yaml = None
|
||||
|
||||
|
||||
def get_data_type(module):
|
||||
if module.params['content_text'] is not None:
|
||||
return 'binary'
|
||||
if module.params['content_binary'] is not None:
|
||||
return 'binary'
|
||||
if module.params['content_json'] is not None:
|
||||
return 'json'
|
||||
if module.params['content_yaml'] is not None:
|
||||
return 'yaml'
|
||||
module.fail_json(msg='Internal error: unknown content type')
|
||||
|
||||
|
||||
def compare_encoded_content(module, binary_data, content):
|
||||
if module.params['content_text'] is not None:
|
||||
return content == module.params['content_text'].encode('utf-8')
|
||||
if module.params['content_binary'] is not None:
|
||||
return content == binary_data
|
||||
if module.params['content_json'] is not None:
|
||||
# Compare JSON
|
||||
try:
|
||||
return json.loads(content) == module.params['content_json']
|
||||
except Exception:
|
||||
# Treat parsing errors as content not equal
|
||||
return False
|
||||
if module.params['content_yaml'] is not None:
|
||||
# Compare YAML
|
||||
try:
|
||||
return yaml.safe_load(content) == module.params['content_yaml']
|
||||
except Exception:
|
||||
# Treat parsing errors as content not equal
|
||||
return False
|
||||
module.fail_json(msg='Internal error: unknown content type')
|
||||
|
||||
|
||||
def get_encoded_type_content(module, binary_data):
|
||||
if module.params['content_text'] is not None:
|
||||
return 'binary', module.params['content_text'].encode('utf-8')
|
||||
if module.params['content_binary'] is not None:
|
||||
return 'binary', binary_data
|
||||
if module.params['content_json'] is not None:
|
||||
return 'json', json.dumps(module.params['content_json']).encode('utf-8')
|
||||
if module.params['content_yaml'] is not None:
|
||||
return 'yaml', yaml.safe_dump(module.params['content_yaml']).encode('utf-8')
|
||||
module.fail_json(msg='Internal error: unknown content type')
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
path=dict(type='path', required=True),
|
||||
force=dict(type='bool', default=False),
|
||||
content_text=dict(type='str', no_log=True),
|
||||
content_binary=dict(type='str', no_log=True),
|
||||
content_json=dict(type='dict', no_log=True),
|
||||
content_yaml=dict(type='dict', no_log=True),
|
||||
)
|
||||
argument_spec.update(get_sops_argument_spec(add_encrypt_specific=True))
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
('content_text', 'content_binary', 'content_json', 'content_yaml'),
|
||||
],
|
||||
required_one_of=[
|
||||
('content_text', 'content_binary', 'content_json', 'content_yaml'),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
add_file_common_args=True,
|
||||
)
|
||||
|
||||
# Check YAML
|
||||
if module.params['content_yaml'] is not None and not HAS_YAML:
|
||||
module.fail_json(msg=missing_required_lib('pyyaml'), exception=YAML_IMP_ERR)
|
||||
|
||||
# Decode binary data
|
||||
binary_data = None
|
||||
if module.params['content_binary'] is not None:
|
||||
try:
|
||||
binary_data = base64.b64decode(module.params['content_binary'])
|
||||
except Exception as e:
|
||||
module.fail_json(msg='Cannot decode Base64 encoded data: {0}'.format(e))
|
||||
|
||||
path = module.params['path']
|
||||
directory = os.path.dirname(path) or None
|
||||
changed = False
|
||||
|
||||
def get_option_value(argument_name):
|
||||
return module.params.get(argument_name)
|
||||
|
||||
try:
|
||||
if module.params['force'] or not os.path.exists(path):
|
||||
# Simply encrypt
|
||||
changed = True
|
||||
else:
|
||||
# Change detection: check if encrypted data equals new data
|
||||
decrypted_content = Sops.decrypt(
|
||||
path, decode_output=False, output_type=get_data_type(module), rstrip=False,
|
||||
get_option_value=get_option_value, module=module,
|
||||
)
|
||||
if not compare_encoded_content(module, binary_data, decrypted_content):
|
||||
changed = True
|
||||
|
||||
if changed and not module.check_mode:
|
||||
input_type, input_data = get_encoded_type_content(module, binary_data)
|
||||
output_type = None
|
||||
if path.endswith('.json'):
|
||||
output_type = 'json'
|
||||
elif path.endswith(('.yml', '.yaml')):
|
||||
output_type = 'yaml'
|
||||
data = Sops.encrypt(
|
||||
data=input_data, cwd=directory, input_type=input_type, output_type=output_type,
|
||||
filename=os.path.relpath(path, directory) if directory is not None else path,
|
||||
get_option_value=get_option_value, module=module,
|
||||
)
|
||||
write_file(module, data)
|
||||
except SopsError as e:
|
||||
module.fail_json(msg=to_text(e))
|
||||
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
changed = module.set_fs_attributes_if_different(file_args, changed)
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue