From fca5b611d8d2a525c5e763d6c11fddd3ab189292 Mon Sep 17 00:00:00 2001 From: Roman Plevka Date: Tue, 20 Dec 2022 16:03:17 +0100 Subject: [PATCH] support passing additional env vars to host (#176) * support passing additional env vars to host * Update README with the environment argument example fix some typos * Update args_file parsing to behave correctly with eventual cli args. Update README --- README.md | 14 +++++++++++--- broker/commands.py | 8 ++++---- broker/helpers.py | 29 +++++++++++++++++------------ broker/providers/container.py | 7 ++++++- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 05599809..981f6af1 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,15 @@ A username can still be provided when using a token to authenticate. This user w # CLI Usage **Checking out a VM or container** +You can pass in any arbitrary arguments you want. ``` broker checkout --workflow "workflow-name" --workflow-arg1 something --workflow-arg2 else ``` -You can pass in any arbitrary arguments you want. Broker can also checkout multiple VMs at once by specifying a count. +Passing environment variables to the target container. +``` +broker checkout --nick rhel7 --environment "VAR1=val1,VAR2=val2" +``` +Broker can also checkout multiple VMs at once by specifying a count. ``` broker checkout --nick rhel7 --count 3 ``` @@ -57,6 +62,9 @@ broker checkout --nick rhel7 --AnsibleTower testing ``` If you have more complex data structures you need to pass in, you can do that in two ways. You can populate a json or yaml file where the top-level keys will become broker arguments and their nested data structures become values. +Note: + The json and yaml files need to use the supported suffix ('json', 'yaml', '.yml') in order to be properly recognized. + Any eventual arbitrary arguments passed to CLI will be combined with those in the passed argument file with the CLI ones taking precedence. ``` broker checkout --nick rhel7 --args-file tests/data/broker_args.json ``` @@ -204,11 +212,11 @@ from broker import Broker ``` The Broker class largely accepts the same arguments as you would pass via the CLI. One key difference is that you need to use underscores instead of dashes. For example, a checkout at the CLI that looks like this ``` -broker checkout --nick rhel7 --args-file tests/data/broker_args.json +broker checkout --nick rhel7 --args-file tests/data/broker_args.json --environment="VAR1=val1,VAR2=val2" ``` could look like this in an API usage ```python -rhel7_host = Broker(nick="rhel7", args_file="tests/data/broker_args.json").checkout() +rhel7_host = Broker(nick="rhel7", args_file="tests/data/broker_args.json", environment={"VAR1": "val1", "VAR2": "val2"}).checkout() ``` Broker will carry out its usual actions and package the resulting host in a Host object. This host object will also include some basic functionality, like the ability to execute ssh commands on the host. Executed ssh command results are packaged in a Results object containing status (return code), stdout, and stderr. diff --git a/broker/commands.py b/broker/commands.py index 7c7f7614..0c660b69 100644 --- a/broker/commands.py +++ b/broker/commands.py @@ -130,7 +130,7 @@ def cli(version): @click.option( "--args-file", type=click.Path(exists=True), - help="A json or yaml file mappng arguments to values", + help="A json or yaml file mapping arguments to values", ) @provider_options @click.pass_context @@ -146,7 +146,7 @@ def checkout(ctx, background, nick, count, args_file, **kwargs): :param nick: shortcut for arguments saved in settings.yaml, passed in as a string - :param args_file: this broker argument wil be replaced with the contents of the file passed in + :param args_file: this broker argument will be replaced with the contents of the file passed in """ broker_args = helpers.clean_dict(kwargs) if nick: @@ -342,7 +342,7 @@ def duplicate(vm, background, count, all_, filter): @click.option( "--args-file", type=click.Path(exists=True), - help="A json or yaml file mappng arguments to values", + help="A json or yaml file mapping arguments to values", ) @provider_options @click.pass_context @@ -362,7 +362,7 @@ def execute(ctx, background, nick, output_format, artifacts, args_file, **kwargs :param artifacts: AnsibleTower provider specific option for choosing what to return - :param args_file: this broker argument wil be replaced with the contents of the file passed in + :param args_file: this broker argument will be replaced with the contents of the file passed in """ broker_args = helpers.clean_dict(kwargs) if nick: diff --git a/broker/helpers.py b/broker/helpers.py index 7c0492e6..911788c3 100644 --- a/broker/helpers.py +++ b/broker/helpers.py @@ -180,25 +180,30 @@ def resolve_file_args(broker_args): then attempt to resolve them. If not resolved, keep arg/value pair intact. """ final_args = {} - for key, val in broker_args.items(): + # parse the eventual args_file first + if val := broker_args.pop('args_file', None): if isinstance(val, Path) or ( isinstance(val, str) and val[-4:] in ("json", "yaml", ".yml") ): if data := load_file(val): - if key == "args_file": - if isinstance(data, dict): - final_args.update(data) - elif isinstance(data, list): - for d in data: - final_args.update(d) - else: - final_args[key] = data - elif key == "args_file": + if isinstance(data, dict): + final_args.update(data) + elif isinstance(data, list): + for d in data: + final_args.update(d) + else: raise exceptions.BrokerError(f"No data loaded from {val}") + + for key, val in broker_args.items(): + if isinstance(val, Path) or ( + isinstance(val, str) and val[-4:] in ("json", "yaml", ".yml") + ): + if data := load_file(val): + final_args.update({key: data}) else: - final_args[key] = val + final_arg.update({key: val}) else: - final_args[key] = val + final_args.update({key: val}) return final_args diff --git a/broker/providers/container.py b/broker/providers/container.py index 56364f06..43f7e9ce 100644 --- a/broker/providers/container.py +++ b/broker/providers/container.py @@ -266,8 +266,13 @@ def run_container(self, container_host, **kwargs): if not kwargs.get("name"): kwargs["name"] = self._gen_name() kwargs["ports"] = self._port_mapping(container_host, **kwargs) + + envars = kwargs.get('environment', {}) + if isinstance(envars, str): + envars = {var.split('=')[0]: var.split('=')[1] for var in envars.split(',')} # add some context information about the container's requester - envars, origin = {}, helpers.find_origin() + origin = helpers.find_origin() + if "for" in origin: origin = origin.split()[-1] envars["BROKER_ORIGIN"] = origin[0]