mirror of
https://github.com/ansible/ansible.git
synced 2026-05-28 04:32:20 -04:00
Merge 789bbe7715 into ba21909655
This commit is contained in:
commit
2749adc80d
12 changed files with 123 additions and 4 deletions
2
changelogs/fragments/environment_cli.yml
Normal file
2
changelogs/fragments/environment_cli.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- CLI - Tools that execute tasks can now pass one or more ``-E`` options to define key-value pairs (k=v or YAML/JSON mapping) to be added to the ``environment`` task keyword for all hosts.
|
||||
|
|
@ -19,13 +19,14 @@ import yaml
|
|||
import ansible
|
||||
from ansible import constants as C
|
||||
from ansible._internal import _templating
|
||||
from ansible._internal._datatag._tags import TrustedAsTemplate, Origin
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils.common.yaml import HAS_LIBYAML, yaml_load
|
||||
from ansible.release import __version__
|
||||
from ansible.parsing.splitter import parse_kv
|
||||
from ansible.parsing.utils.yaml import from_yaml
|
||||
from ansible.utils.path import unfrackpath
|
||||
|
||||
from ansible._internal._datatag._tags import TrustedAsTemplate, Origin
|
||||
|
||||
|
||||
#
|
||||
# Special purpose OptionParsers
|
||||
|
|
@ -233,6 +234,46 @@ def maybe_unfrack_path(beacon):
|
|||
return inner
|
||||
|
||||
|
||||
def parse_env_vars(value: str) -> dict[str, str]:
|
||||
"""
|
||||
Parse command line args to set the 'environment' keyword
|
||||
"""
|
||||
|
||||
if value.startswith('{'):
|
||||
data = from_yaml(value)
|
||||
|
||||
elif value.startswith('@'):
|
||||
filename = unfrackpath(value[1:])
|
||||
try:
|
||||
with open(filename, errors='strict') as f:
|
||||
data = from_yaml(f.read(), file_name=filename)
|
||||
except OSError as e:
|
||||
raise ValueError(f"Cannot access environment file {filename!r}") from e
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Cannot decode file {filename!r}") from e
|
||||
|
||||
elif '=' in value:
|
||||
data = parse_kv(value)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unable to parse environment option, not YAML/JSON nor k=v pairs nor a file: {value!r}")
|
||||
|
||||
if not isinstance(data, dict):
|
||||
raise TypeError(f"Error while parsing environment values, expected a dictionary, got a {type(data)!r}")
|
||||
|
||||
final = {}
|
||||
for k, v in data.items():
|
||||
if not isinstance(k, str):
|
||||
raise TypeError(f"Environment key is required to be a string, but {k!r} is a {type(k)!r} instead.")
|
||||
try:
|
||||
final[k] = TrustedAsTemplate().tag(str(v))
|
||||
except UnicodeError as e:
|
||||
raise ValueError(f"Environment values are required to be strings, {k!r}'s value could not be converted.") from e
|
||||
|
||||
# TODO: add Origin
|
||||
return final
|
||||
|
||||
|
||||
def _git_repo_info(repo_path):
|
||||
""" returns a string containing git branch, commit id and commit date """
|
||||
result = None
|
||||
|
|
@ -502,6 +543,8 @@ def add_runtask_options(parser):
|
|||
"""Add options for commands that run a task"""
|
||||
parser.add_argument('-e', '--extra-vars', dest="extra_vars", action="append", type=maybe_unfrack_path('@'),
|
||||
help="set additional variables as key=value or YAML/JSON, if filename prepend with @", default=[])
|
||||
parser.add_argument('-E', '--environment', dest='environment', action='append', default=[], type=parse_env_vars,
|
||||
help="Set environment variables (key=value or YAML/JSON formatted or @filename.yml) when executing a task on the target.")
|
||||
|
||||
|
||||
def add_tasknoplay_options(parser):
|
||||
|
|
|
|||
|
|
@ -271,6 +271,8 @@ class PullCLI(CLI):
|
|||
repo_opts, limit_opts)
|
||||
for ev in context.CLIARGS['extra_vars']:
|
||||
cmd += ' -e %s' % shlex.quote(ev)
|
||||
for env_var in context.CLIARGS.get('environment', []):
|
||||
cmd += ' ' + shlex.join(('-E', str(dict(env_var))))
|
||||
|
||||
# Nap?
|
||||
if context.CLIARGS['sleep']:
|
||||
|
|
@ -318,6 +320,8 @@ class PullCLI(CLI):
|
|||
|
||||
for ev in context.CLIARGS['extra_vars']:
|
||||
cmd += ' -e %s' % shlex.quote(ev)
|
||||
for env_var in context.CLIARGS.get('environment', []):
|
||||
cmd += ' -E %s' % shlex.quote(str(dict(env_var)))
|
||||
|
||||
if context.CLIARGS['become_ask_pass']:
|
||||
cmd += ' --ask-become-pass'
|
||||
|
|
|
|||
|
|
@ -2032,6 +2032,17 @@ TARGET_LOG_INFO:
|
|||
vars:
|
||||
- name: ansible_target_log_info
|
||||
version_added: "2.17"
|
||||
TASK_ENVIRONMENT:
|
||||
name: Task environment
|
||||
description:
|
||||
- Environment variables to be provided for the task upon execution on the target.
|
||||
- This can ONLY affects module execution, it does not affect any other type of plugin nor Ansible itself nor its configuration.
|
||||
- This is not a recommended way to pass in confidential data.
|
||||
type: list
|
||||
keyword:
|
||||
- name: environment
|
||||
cli:
|
||||
- name: environment
|
||||
TASK_TIMEOUT:
|
||||
name: Task Timeout
|
||||
default: 0
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ from __future__ import annotations
|
|||
import functools as _functools
|
||||
import pathlib as _pathlib
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible import context
|
||||
from ansible import context, constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.errors import AnsibleParserError, AnsibleAssertionError, AnsibleValueOmittedError
|
||||
from ansible.module_utils.common.collections import is_sequence
|
||||
|
|
@ -173,6 +172,16 @@ class Play(Base, Taggable, CollectionSearch):
|
|||
ds['remote_user'] = ds['user']
|
||||
del ds['user']
|
||||
|
||||
# prepend possible CLI environment vars (tuple of immutable dicts), these are not a default values
|
||||
cli = C.config.get_config_value('TASK_ENVIRONMENT')
|
||||
if cli:
|
||||
play_env = ds.get('environment')
|
||||
ds['environment'] = [dict(d) for d in cli] # from ImmutableDict
|
||||
if play_env:
|
||||
if not isinstance(play_env, list):
|
||||
play_env = [play_env]
|
||||
ds['environment'].extend(play_env)
|
||||
|
||||
return super(Play, self).preprocess_data(ds)
|
||||
|
||||
def _load(self, attr: str, ds: object) -> list[Block]:
|
||||
|
|
|
|||
|
|
@ -28,3 +28,7 @@ ansible localhost -m assert -a '{"that": "ansible_facts.distribution is defined"
|
|||
ansible --flush-cache localhost -m debug -a "msg={{ ansible_facts }}" | grep '"msg": {}'
|
||||
# test meta end_play
|
||||
ansible localhost -m include_role -a name=end_play
|
||||
# test environment variable setting with -E option (basic KEY=VALUE)
|
||||
ansible localhost -m gather_facts -a 'gather_subset="env"' -E 'TEST_ENV_VAR=ansible_test_value' | grep '"TEST_ENV_VAR": "ansible_test_value"'
|
||||
# test environment variable setting with -E option (YAML/JSON format)
|
||||
ansible localhost -m gather_facts -a 'gather_subset="env"' -E '{"TEST_JSON_VAR": "json_test_value"}' | grep '"TEST_JSON_VAR": "json_test_value"'
|
||||
|
|
|
|||
|
|
@ -14,3 +14,8 @@ if grep -q "ERROR" err.txt; then
|
|||
echo "Failed to execute end_play"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# test environment variable setting with -E option (basic KEY=VALUE)
|
||||
echo 'gather_facts gather_subset=env' | ansible-console localhost -E 'TEST_CONSOLE_VAR=console_test_value' | grep '"TEST_CONSOLE_VAR": "console_test_value"'
|
||||
# test environment variable setting with -E option (JSON format)
|
||||
echo 'gather_facts gather_subset=env' | ansible-console localhost -E '{"TEST_CONSOLE_JSON": "console_json_value"}' | grep '"TEST_CONSOLE_JSON": "console_json_value"'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
- hosts: localhost
|
||||
gather_facts: false
|
||||
module_defaults:
|
||||
gather_facts:
|
||||
gather_subset: ['env']
|
||||
tasks:
|
||||
- name: Verify all environment variables are accessible via ansible_env
|
||||
assert:
|
||||
that:
|
||||
- ansible_env.TEST_PULL is defined
|
||||
- ansible_env.TEST_PULL == "pull_test_value"
|
||||
fail_msg: "Environment variables not accessible via -E option in ansible-pull"
|
||||
|
|
@ -142,3 +142,11 @@ pass_tests
|
|||
if [ "${ORIG_CONFIG}" != "" ]; then
|
||||
export ANSIBLE_CONFIG="${ORIG_CONFIG}"
|
||||
fi
|
||||
|
||||
# test environment variable setting with -E option (basic KEY=VALUE)
|
||||
ansible-pull -d "${pull_dir}" -U "${repo_dir}" env_var_test.yml \
|
||||
-e 'ansble_python_interpreter={{ansible_playbook_python}}' -E 'TEST_PULL=pull_test_value' "$@"
|
||||
|
||||
# test environment variable setting with -E option (JSON format)
|
||||
ansible-pull -d "${pull_dir}" -U "${repo_dir}" env_var_test.yml \
|
||||
-e 'ansble_python_interpreter={{ansible_playbook_python}}' -E '{"TEST_PULL": "pull_test_value"}' "$@"
|
||||
|
|
|
|||
10
test/integration/targets/playbook/env_var_test.yml
Normal file
10
test/integration/targets/playbook/env_var_test.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
- hosts: localhost
|
||||
gather_facts: yes
|
||||
tasks:
|
||||
- name: Verify all environment variables are accessible via ansible_env
|
||||
assert:
|
||||
that:
|
||||
- ansible_env.TEST_PLAYBOOK is defined
|
||||
- ansible_env.TEST_PLAYBOOK == "playbook_test_value"
|
||||
fail_msg: "Environment variables not accessible via -E option"
|
||||
1
test/integration/targets/playbook/playbook_env_vars.yml
Normal file
1
test/integration/targets/playbook/playbook_env_vars.yml
Normal file
|
|
@ -0,0 +1 @@
|
|||
TEST_PLAYBOOK: playbook_test_value
|
||||
|
|
@ -90,3 +90,12 @@ ansible-playbook -i ../../inventory vars_files_null.yml -v "$@"
|
|||
|
||||
# test vars_files: filename.yml
|
||||
ansible-playbook -i ../../inventory vars_files_string.yml -v "$@"
|
||||
|
||||
# test environment variable setting with -E option (basic KEY=VALUE)
|
||||
ansible-playbook -i ../../inventory env_var_test.yml -E '@playbook_env_vars.yml' "$@"
|
||||
|
||||
# test environment variable setting with -E option (basic KEY=VALUE)
|
||||
ansible-playbook -i ../../inventory env_var_test.yml -E 'TEST_PLAYBOOK=playbook_test_value' "$@"
|
||||
|
||||
# test environment variable setting with -E option (JSON format)
|
||||
ansible-playbook -i ../../inventory env_var_test.yml -E '{"TEST_PLAYBOOK": "playbook_test_value"}' "$@"
|
||||
|
|
|
|||
Loading…
Reference in a new issue