diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6dfbce037..af40b7bc7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,7 +57,9 @@ jobs: python-version: ${{ matrix.python-version }} - run: pip install '.[test]' - run: pytest --cov --disable-warnings - - uses: codecov/codecov-action@v3 + - name: Upload coverage report to codecov + if: ${{ matrix.os == 'ubuntu-20.04' && matrix.python-version == '3.12' }} + uses: codecov/codecov-action@v3 # End-to-end tests # diff --git a/.gitignore b/.gitignore index 5fe8a3d5d..e1b6a7318 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ venv/ .cache/ .mypy_cache/ .pytest_cache/ +.python-version .vagrant* diff --git a/CHANGELOG.md b/CHANGELOG.md index 8801ea2d6..7ebb24dde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ +# v3.1.1 + +- Improve errors with 2.x style `@decorator` (vs `@decorator()`) functions +- Document adding custom connectors (@simonhammes) +- Add basic API example to docs (@pirate) +- Fix sphinx warnings (@simonhammes) +- Fix force & pull arguments in `git.worktree` operation +- Fix `server.reboot` reconnection (@wackou) +- Fix chroot/local connector non-utf file gets (@evoldstad) +- Fix `AptSources` fact to parse components in order & with digits (@rsfzi) + +# v3.1 + +Here's pyinfra 3.1 - a release primarily driven by contributors new and old - a HUGE THANK YOU to all of you who dedicate time to work on pushing pyinfra forward. New stuff: + +- Add `zfs` operations (`dataset`, `snapshot`, `volume`, `filesystem`) facts (`Pools`, `Datasets`, `Filesystems`, `Snapshots`, `Volumes`) (@taliaferro) +- Add `flatpak` operations (`packages`) and facts (`FlatpakPackage`, `FlatpakPackages`) (@JustScreaMy) +- Add `jinja_env_kwargs` argument to `files.template` operation (@DonDebonair) +- Add using dictionaries as `@terraform` output (map from group -> hosts) +- Add default `@terraform` output key - `pyinfra_inventory.value`, promote connector to beta +- Add support for multiple keys in each `server.authorized_keys` file (@matthijskooijman) +- Add print all dependency versions with `--support` flag (@kytta) + +Fixes: + +- Fix when `ssh_hostname` is set as override data, don't do inventory hostname check +- Fix `apt.AptSources` parsing special characters (@CondensedTea) +- Fix `server.reboot` connection detection (@bauen1 + @lemmi) +- Fix systemd flagging of sockets running (@bauen1) +- Fix mysql dump quoting (@simonhammes) +- Fix tilde expansion in files facts (@simonhammes) +- Fix host lookup check with SSH alias config (@simonhammes) +- Fix crontab comparison (@andrew-d) + +Docs/internal tweaks: + +- Improve operations documentation (@bauen1) +- Default to local machine if `user_name` set in systecmt (@bauen1) +- Improve efficiency of Docker operations (@apecnascimento) +- Shallow copy `host.data` data to mutation + # v3.0.2 - Fix `OperationMeta.did_change`: this is now a function as originally designed diff --git a/docs/api/connectors.md b/docs/api/connectors.md index 4291ba0d0..3ed6e870c 100644 --- a/docs/api/connectors.md +++ b/docs/api/connectors.md @@ -86,3 +86,14 @@ class LocalConnector(BaseConnector): bool: indicating success or failure. """ ``` + +## pyproject.toml + +In order for pyinfra to gain knowledge about your connector, you need to add the following snippet to your connector's `pyproject.toml`: + +```toml +[project.entry-points.'pyinfra.connectors'] +# Key = Entry point name +# Value = module_path:class_name +custom = 'pyinfra_custom_connector.connector:LoggingConnector' +``` diff --git a/docs/api/deploys.md b/docs/api/deploys.md index 357d01cad..80d67de80 100644 --- a/docs/api/deploys.md +++ b/docs/api/deploys.md @@ -34,7 +34,7 @@ install_mariadb() ## Global Arguments -Deploys accept the same [global arguments as as operations](../deploys.html#global-arguments) and they apply to every operation call within that deploy. Taking the above example: +Deploys accept the same [global arguments as operations](../arguments) and they apply to every operation call within that deploy. Taking the above example: ```py diff --git a/docs/api/index.rst b/docs/api/index.rst index 74b5be56a..d2faac8ab 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -3,4 +3,64 @@ Using the API In addition to :doc:`the pyinfra CLI <../cli>`, pyinfra provides a full Python API. As of ``v3`` this API can be considered mostly stable. See the :doc:`./reference`. -`An example of how to use the API can be found here `_. Note: this is not yet tested and pending future updates. +You can also reference `pyinfra's own main.py `_, and the `pyinfra API source code `_. + +Full Example +============ + +`A full example of how to use the API can be found here `_. (Note: this is not yet tested and is pending future updates) + +Basic Localhost Example +======================= + +.. code:: python + + from pyinfra.api import Config, Inventory, State + from pyinfra.api.connect import connect_all + from pyinfra.api.operation import add_op + from pyinfra.api.operations import run_ops + from pyinfra.api.facts import get_facts + from pyinfra.facts.server import Os + from pyinfra.operations import server + + # Define your inventory (@local means execute on localhost using subprocess) + # https://docs.pyinfra.com/en/3.x/apidoc/pyinfra.api.inventory.html + inventory = Inventory((["@local"], {})) + + # Define any config you need + # https://docs.pyinfra.com/en/3.x/apidoc/pyinfra.api.config.html + config = Config(SUDO=True) + + # Set up the state object + # https://docs.pyinfra.com/en/3.x/apidoc/pyinfra.api.state.html + state = State(inventory=inventory, config=config) + + # Connect to all the hosts + connect_all(state) + + # Start adding operations + result1 = add_op( + state, + server.user, + user="pyinfra", + home="/home/pyinfra", + shell="/bin/bash", + ) + result2 = add_op( + state, + server.shell, + name="Run some shell commands", + commands=["whoami", "echo $PATH", "bash --version"] + ) + + # And finally we run the ops + run_ops(state) + + # add_op returns an OperationMeta for each op, letting you access stdout, stderr, etc. after they run + host = state.hosts.inventory['@local'] + print(result1.changed, result1[host].stdout, result1[host].stderr) + print(result2.changed, result2[host].stdout, result2[host].stderr) + + # We can also get facts for all the hosts + # https://docs.pyinfra.com/en/3.x/apidoc/pyinfra.api.facts.html + print(get_facts(state, Os)) diff --git a/docs/cli.md b/docs/cli.md index 034c7b2c7..691776c35 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -46,7 +46,7 @@ By default pyinfra only prints high level information (this host connected, this ## Inventory -When using pyinfra inventory can be provided directly via the command line or [defined in a file](./deploys.html#inventory). Both support the full range of [connectors](./connectors) and multiple hosts. Some CLI examples: +When using pyinfra inventory can be provided directly via the command line or [defined in a file](./inventory-data). Both support the full range of [connectors](./connectors) and multiple hosts. Some CLI examples: ```sh # Load inventory hosts from a file diff --git a/docs/compatibility.md b/docs/compatibility.md index 665d80934..a85c4b5d2 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -39,12 +39,36 @@ pyinfra aims to be compatible with all Unix-like operating systems and is curren * HardenedBSD 11 * DragonflyBSD 5 + OpenSUSE (leap15 + tumbleweed) -+ macOS 10.15 (with [`@local` connector](connectors.html#local)) -+ Docker (with [`@docker` connector](connectors.html#docker)) ++ macOS 10.15 (with [`@local` connector](./connectors/local)) ++ Docker (with [`@docker` connector](./connectors/docker)) In general, the only requirement on the remote side is shell access. POSIX commands are used where possible for facts and operations, so most of the ``server`` and ``files`` operations should work anywhere POSIX. +## Upgrading pyinfra from ``2.x`` -> ``3.x`` + +- Rename `_use_sudo_password` argument to `_sudo_password` +- Deploy decorator must be called: + +```py +# Old, 2.x decorator +@deploy +def mydeploy(): + ... + +# New, 3.x decorator +@deploy() +def mydeploy(): + ... +``` + +- Remove `@winrm` connector, will come back as [`pyinfra-windows`](https://github.com/pyinfra-dev/pyinfra-windows) + +## Upgrading pyinfra from ``1.x`` -> ``2.x`` + +- Python 2.7 (finally!), 3.5 support dropped, Python 3.6 is now the minimum required version +- The "deploy directory" concept has been removed - everything now executes from the current working directory which removes the ambiguous magic v1 used to pick a deploy directory. A new --chdir CLI flag has been added to set the working directory before pyinfra executes + ## Upgrading pyinfra from ``0.x`` -> ``1.x`` The move to `v1` removes a lot of old legacy functionality in pyinfra - there will be warnings written to the user in CLI mode if any of these are encountered. The full list can be seen [on the changelog](https://github.com/Fizzadar/pyinfra/blob/master/CHANGELOG.md#v1). In addition to these removals, `v1` brings a few major changes and deprecates the old methods. Again, pyinfra will output warnings when these are encountered, and support will be removed in `v2`. diff --git a/docs/conf.py b/docs/conf.py index b47dcabad..43e165380 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,6 +46,8 @@ "primary_doc_version": "3.x", } +myst_heading_anchors = 3 + templates_path = ["templates"] html_favicon = "static/logo_small.png" diff --git a/docs/contributing.md b/docs/contributing.md index 57254fca4..7540b5b87 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,12 +2,12 @@ 🎉 Thank you for taking the time to contribute to pyinfra! 🎉 -Third party pull requests help expand pyinfra's functionality and are essential to it's continued growth. This guide should help get you started adding additional operations, facts and other functionality to pyinfra. +Third party pull requests help expand pyinfra's functionality and are essential to its continued growth. This guide should help get you started adding additional operations, facts and other functionality to pyinfra. ## Guides + [How to write operations](api/operations) -+ [How to write facts](api/facts) ++ [How to write facts](api/operations.md#facts) + [How to write connectors](api/connectors) + [API reference](api/reference) @@ -23,7 +23,7 @@ Third party pull requests help expand pyinfra's functionality and are essential # python -m venv / pyenv virtualenv / virtualenv # Clone the repo -git clone git@github.com:Fizzadar/pyinfra.git +git clone git@github.com:pyinfra-dev/pyinfra.git # Install the package in editable mode with development requirements cd pyinfra diff --git a/docs/install.md b/docs/install.md index f257ea0f9..e43cbbb3c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,3 +1,8 @@ +--- +# Ignore warning about this page not being included in any toctree +orphan: true +--- + # Installation ## Pip diff --git a/docs/support.md b/docs/support.md index 0d182871e..61b8afb3a 100644 --- a/docs/support.md +++ b/docs/support.md @@ -5,16 +5,16 @@ Need a hand with using pyinfra or have some questions? You can find official pyi + Chat in [the `pyinfra` Matrix room](https://matrix.to/#/#pyinfra:matrix.org) + Ask questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/pyinfra) (be sure to use the `pyinfra` tag!) -### I think I've got a bug! +## I think I've got a bug! + Please have a quick scan of the [the compatibility page](./compatibility) to see if the issue is referenced there + Check [the issue list](https://github.com/Fizzadar/pyinfra/issues) to see if anyone else has this issue + Submit [a new bug report](https://github.com/Fizzadar/pyinfra/issues/new/choose) -### I have an idea! +## I have an idea! Please consider [submitting a feature request](https://github.com/Fizzadar/pyinfra/issues/new/choose). -### I'd like to contribute! +## I'd like to contribute! Fantastic! Please see [the contributing docs](./contributing). diff --git a/docs/using-operations.rst b/docs/using-operations.rst index 752625303..a1cb7df1f 100644 --- a/docs/using-operations.rst +++ b/docs/using-operations.rst @@ -143,10 +143,27 @@ All operations return an operation meta object which provides information about user="myuser", ) + create_otheruser = server.user( + name="Create user otheruser", + user="otheruser", + ) + server.shell( - name="Bootstrap user", + name="Bootstrap myuser", commands=["..."], - _if=create_user, + _if=create_user.did_change, + ) + + # A list can be provided to run an operation if **all** functions return true + server.shell( + commands=["echo 'Both myuser and otheruser changed'"], + _if=[create_user.did_change, create_otheruser.did_change], + ) + + # You can also build your own lamba functions to achieve, e.g. an OR condition + server.shell( + commands=["echo 'myuser or otheruser changed'"], + _if=lambda: create_user.did_change() or create_otheruser.did_change(), ) Operation Output diff --git a/docs/utils.py b/docs/utils.py index 17a3d2aa0..9fe347634 100644 --- a/docs/utils.py +++ b/docs/utils.py @@ -7,7 +7,7 @@ def title_line(char, string): def format_doc_line(line): # Bold the : part of each line - line = re.sub(r"\+ ([0-9a-z_\/]+)(.*)", r"+ **\1**\2", line) + line = re.sub(r"\+ ([0-9a-z_\/\*]+)(.*)", r"+ **\1**\2", line) # Strip the first 4 characters (first indent from docstring) return line[4:] diff --git a/pyinfra/api/arguments.py b/pyinfra/api/arguments.py index e4eee7d7a..ad26b79d4 100644 --- a/pyinfra/api/arguments.py +++ b/pyinfra/api/arguments.py @@ -137,7 +137,7 @@ def generate_env(config: "Config", value: dict) -> dict: ), "_chdir": ArgumentMeta( "Directory to switch to before executing the command.", - default=lambda _: "", + default=lambda _: None, ), "_env": ArgumentMeta( "Dictionary of environment variables to set.", diff --git a/pyinfra/api/deploy.py b/pyinfra/api/deploy.py index b5b356f5a..b65d7a302 100644 --- a/pyinfra/api/deploy.py +++ b/pyinfra/api/deploy.py @@ -23,7 +23,7 @@ from pyinfra.api.state import State -def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs): +def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs) -> None: """ Prepare & add an deploy to pyinfra.state by executing it on all hosts. @@ -54,12 +54,22 @@ def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs) P = ParamSpec("P") -def deploy(name: Optional[str] = None, data_defaults=None): +def deploy( + name: Optional[str] = None, data_defaults: Optional[dict] = None +) -> Callable[[Callable[P, Any]], PyinfraOperation[P]]: """ Decorator that takes a deploy function (normally from a pyinfra_* package) and wraps any operations called inside with any deploy-wide kwargs/data. """ + if name and not isinstance(name, str): + raise PyinfraError( + ( + "The `deploy` decorator must be called, ie `@deploy()`, " + "see: https://docs.pyinfra.com/en/3.x/compatibility.html#upgrading-pyinfra-from-2-x-3-x" # noqa + ) + ) + def decorator(func: Callable[P, Any]) -> PyinfraOperation[P]: func.deploy_name = name or func.__name__ # type: ignore[attr-defined] if data_defaults: diff --git a/pyinfra/api/host.py b/pyinfra/api/host.py index c77ff7532..d16538c2a 100644 --- a/pyinfra/api/host.py +++ b/pyinfra/api/host.py @@ -1,6 +1,7 @@ from __future__ import annotations from contextlib import contextmanager +from copy import copy from typing import ( TYPE_CHECKING, Any, @@ -41,7 +42,6 @@ def extract_callable_datas( # the data is stored on the state temporarily. if callable(data): data = data() - yield data @@ -66,7 +66,10 @@ def __init__(self, host: "Host", *datas): def __getattr__(self, key: str): for data in extract_callable_datas(self.datas): try: - return data[key] + # Take a shallow copy of the object here, we don't want modifications + # to host.data. to stick, instead setting host.data. = is the + # correct way to achieve this (see __setattr__). + return copy(data[key]) except KeyError: pass @@ -215,17 +218,19 @@ def style_print_prefix(self, *args, **kwargs) -> str: self.print_prefix_padding, ) - def log(self, message, log_func=logger.info): + def log(self, message: str, log_func: Callable[[str], Any] = logger.info) -> None: log_func(f"{self.print_prefix}{message}") - def log_styled(self, message, log_func=logger.info, **kwargs): + def log_styled( + self, message: str, log_func: Callable[[str], Any] = logger.info, **kwargs + ) -> None: message_styled = click.style(message, **kwargs) self.log(message_styled, log_func=log_func) def get_deploy_data(self): return self.current_op_deploy_data or self.current_deploy_data or {} - def noop(self, description): + def noop(self, description: str) -> None: """ Log a description for a noop operation. """ @@ -403,12 +408,13 @@ def disconnect(self): # Disconnect is an optional function for connectors if needed disconnect_func = getattr(self.connector, "disconnect", None) if disconnect_func: - return disconnect_func() + disconnect_func() # TODO: consider whether this should be here! remove_any_sudo_askpass_file(self) self.state.trigger_callbacks("host_disconnect", self) + self.connected = False def run_shell_command(self, *args, **kwargs) -> tuple[bool, CommandOutput]: """ diff --git a/pyinfra/api/util.py b/pyinfra/api/util.py index 0aba51b49..2a49d1bba 100644 --- a/pyinfra/api/util.py +++ b/pyinfra/api/util.py @@ -139,12 +139,13 @@ def get_operation_order_from_stack(state: "State"): return line_numbers -def get_template(filename_or_io: str | IO): +def get_template(filename_or_io: str | IO, jinja_env_kwargs: dict[str, Any] | None = None): """ Gets a jinja2 ``Template`` object for the input filename or string, with caching based on the filename of the template, or the SHA1 of the input string. """ - + if jinja_env_kwargs is None: + jinja_env_kwargs = {} file_data = get_file_io(filename_or_io, mode="r") cache_key = file_data.cache_key @@ -158,6 +159,7 @@ def get_template(filename_or_io: str | IO): undefined=StrictUndefined, keep_trailing_newline=True, loader=FileSystemLoader(getcwd()), + **jinja_env_kwargs, ).from_string(template_string) if cache_key: diff --git a/pyinfra/connectors/chroot.py b/pyinfra/connectors/chroot.py index a8858a970..4b575f2ea 100644 --- a/pyinfra/connectors/chroot.py +++ b/pyinfra/connectors/chroot.py @@ -174,7 +174,7 @@ def get_file( ) # Load the temporary file and write it to our file or IO object - with open(temp_filename, encoding="utf-8") as temp_f: + with open(temp_filename, "rb") as temp_f: with get_file_io(filename_or_io, "wb") as file_io: data = temp_f.read() data_bytes: bytes diff --git a/pyinfra/connectors/local.py b/pyinfra/connectors/local.py index ab12197e1..d7a32b722 100644 --- a/pyinfra/connectors/local.py +++ b/pyinfra/connectors/local.py @@ -184,7 +184,7 @@ def get_file( raise IOError(output.stderr) # Load our file or IO object and write it to the temporary file - with open(temp_filename, encoding="utf-8") as temp_f: + with open(temp_filename, "rb") as temp_f: with get_file_io(filename_or_io, "wb") as file_io: data_bytes: bytes diff --git a/pyinfra/connectors/ssh.py b/pyinfra/connectors/ssh.py index 5cb461c6c..34f2daea8 100644 --- a/pyinfra/connectors/ssh.py +++ b/pyinfra/connectors/ssh.py @@ -264,6 +264,9 @@ def _connect(self) -> None: f"Host key for {e.hostname} does not match.", ) + def disconnect(self) -> None: + self.get_sftp_connection.cache.clear() + def run_shell_command( self, command: StringCommand, @@ -606,7 +609,7 @@ def rsync( rsync_flags=" ".join(flags), ssh_flags=" ".join(ssh_flags), remote_rsync_command=remote_rsync_command, - user=user, + user=user or "", hostname=hostname, src=src, dest=dest, diff --git a/pyinfra/connectors/terraform.py b/pyinfra/connectors/terraform.py index 2d043469b..dbfc02bed 100644 --- a/pyinfra/connectors/terraform.py +++ b/pyinfra/connectors/terraform.py @@ -10,16 +10,15 @@ @memoize def show_warning(): - logger.warning("The @terraform connector is in alpha!") + logger.warning("The @terraform connector is in beta!") def _flatten_dict_gen(d, parent_key, sep): for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k + yield new_key, v if isinstance(v, dict): yield from _flatten_dict(v, new_key, sep=sep).items() - else: - yield new_key, v def _flatten_dict(d: dict, parent_key: str = "", sep: str = "."): @@ -82,7 +81,9 @@ def make_names_data(name=None): show_warning() if not name: - name = "" + # This is the default which allows one to create a Terraform output + # "pyinfra" and directly call: pyinfra @terraform ... + name = "pyinfra_inventory.value" with progress_spinner({"fetch terraform output"}): tf_output_raw = local.shell("terraform output -json") @@ -96,27 +97,36 @@ def make_names_data(name=None): keys = "\n".join(f" - {k}" for k in tf_output.keys()) raise InventoryError(f"No Terraform output with key: `{name}`, valid keys:\n{keys}") - if not isinstance(tf_output_value, list): + if not isinstance(tf_output_value, (list, dict)): raise InventoryError( "Invalid Terraform output type, should be `list`, got " f"`{type(tf_output_value).__name__}`", ) - for ssh_target in tf_output_value: - if isinstance(ssh_target, dict): - name = ssh_target.pop("name", ssh_target.get("ssh_hostname")) - if name is None: - raise InventoryError( - "Invalid Terraform list item, missing `name` or `ssh_hostname` keys", - ) - yield f"@terraform/{name}", ssh_target, ["@terraform"] - - elif isinstance(ssh_target, str): - data = {"ssh_hostname": ssh_target} - yield f"@terraform/{ssh_target}", data, ["@terraform"] + if isinstance(tf_output_value, list): + tf_output_value = { + "all": tf_output_value, + } - else: + for group_name, hosts in tf_output_value.items(): + if not isinstance(hosts, list): raise InventoryError( - "Invalid Terraform list item, should be `dict` or `str` got " - f"`{type(ssh_target).__name__}`", + "Invalid Terraform map value type, all values should be `list`, got " + f"`{type(hosts).__name__}`", ) + for host in hosts: + if isinstance(host, dict): + name = host.pop("name", host.get("ssh_hostname")) + if name is None: + raise InventoryError( + "Invalid Terraform list item, missing `name` or `ssh_hostname` keys", + ) + yield f"@terraform/{name}", host, ["@terraform", group_name] + elif isinstance(host, str): + data = {"ssh_hostname": host} + yield f"@terraform/{host}", data, ["@terraform", group_name] + else: + raise InventoryError( + "Invalid Terraform list item, should be `dict` or `str` got " + f"`{type(host).__name__}`", + ) diff --git a/pyinfra/facts/apt.py b/pyinfra/facts/apt.py index a496b3dd3..600e0967d 100644 --- a/pyinfra/facts/apt.py +++ b/pyinfra/facts/apt.py @@ -2,14 +2,38 @@ import re +from typing_extensions import TypedDict + from pyinfra.api import FactBase from .gpg import GpgFactBase from .util import make_cat_files_command +def noninteractive_apt(command: str, force=False): + args = ["DEBIAN_FRONTEND=noninteractive apt-get -y"] + + if force: + args.append("--force-yes") + + args.extend( + ( + '-o Dpkg::Options::="--force-confdef"', + '-o Dpkg::Options::="--force-confold"', + command, + ), + ) + + return " ".join(args) + + +APT_CHANGES_RE = re.compile( + r"^(\d+) upgraded, (\d+) newly installed, (\d+) to remove and (\d+) not upgraded.$" +) + + def parse_apt_repo(name): - regex = r"^(deb(?:-src)?)(?:\s+\[([^\]]+)\])?\s+([^\s]+)\s+([a-z-]+)\s+([a-z-\s]*)$" + regex = r"^(deb(?:-src)?)(?:\s+\[([^\]]+)\])?\s+([^\s]+)\s+([^\s]+)\s+([a-z-\s\d]*)$" matches = re.match(regex, name) @@ -32,7 +56,7 @@ def parse_apt_repo(name): "type": matches.group(1), "url": matches.group(3), "distribution": matches.group(4), - "components": set(matches.group(5).split()), + "components": list(matches.group(5).split()), } @@ -94,3 +118,39 @@ def command(self) -> str: def requires_command(self) -> str: return "apt-key" + + +class AptSimulationDict(TypedDict): + upgraded: int + newly_installed: int + removed: int + not_upgraded: int + + +class SimulateOperationWillChange(FactBase[AptSimulationDict]): + """ + Simulate an 'apt-get' operation and try to detect if any changes would be performed. + """ + + def command(self, command: str) -> str: + # LC_ALL=C: Ensure the output is in english, as we want to parse it + return "LC_ALL=C " + noninteractive_apt(f"{command} --dry-run") + + def requires_command(self, command: str) -> str: + return "apt-get" + + def process(self, output) -> AptSimulationDict: + # We are looking for a line similar to + # "3 upgraded, 0 newly installed, 0 to remove and 0 not upgraded." + for line in output: + result = APT_CHANGES_RE.match(line) + if result is not None: + return { + "upgraded": int(result[1]), + "newly_installed": int(result[2]), + "removed": int(result[3]), + "not_upgraded": int(result[4]), + } + + # We did not find the line we expected: + raise Exception("Did not find proposed changes in output") diff --git a/pyinfra/facts/files.py b/pyinfra/facts/files.py index 4d55a9cc6..1f1bca6dd 100644 --- a/pyinfra/facts/files.py +++ b/pyinfra/facts/files.py @@ -5,6 +5,7 @@ from __future__ import annotations import re +import shlex import stat from datetime import datetime from typing import TYPE_CHECKING, List, Optional, Tuple, Union @@ -111,13 +112,19 @@ class File(FactBase[Union[FileDict, Literal[False], None]]): type = "file" def command(self, path): + if path.startswith("~/"): + # Do not quote leading tilde to ensure that it gets properly expanded by the shell + path = f"~/{shlex.quote(path[2:])}" + else: + path = QuoteString(path) + return make_formatted_string_command( ( # only stat if the path exists (file or symlink) "! (test -e {0} || test -L {0} ) || " "( {linux_stat_command} {0} 2> /dev/null || {bsd_stat_command} {0} )" ), - QuoteString(path), + path, linux_stat_command=LINUX_STAT_COMMAND, bsd_stat_command=BSD_STAT_COMMAND, ) diff --git a/pyinfra/facts/flatpak.py b/pyinfra/facts/flatpak.py new file mode 100644 index 000000000..3c1cdf62e --- /dev/null +++ b/pyinfra/facts/flatpak.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import re + +from pyinfra.api import FactBase + + +class FlatpakBaseFact(FactBase): + abstract = True + + def requires_command(self, *args, **kwargs) -> str: + return "flatpak" + + +class FlatpakPackage(FlatpakBaseFact): + """ + Returns information for an installed flatpak package + + .. code:: python + + { + "id": "org.signal.Signal", + "ref": "app/org.signal.Signal/x86_64/stable", + "version": "7.12.0" + } + """ + + default = dict + _regexes = { + "id": "^[ ]+ID:[ ]+(.*)$", + "ref": r"^[ ]+Ref:[ ]+(.*)$", + "version": r"^[ ]+Version:[ ]+([\w\d.-]+).*$", + } + + def command(self, package): + return f"flatpak info {package}" + + def process(self, output): + data = {} + for line in output: + for regex_name, regex in self._regexes.items(): + matches = re.match(regex, line) + if matches: + data[regex_name] = matches.group(1) + + return data + + +class FlatpakPackages(FlatpakBaseFact): + """ + Returns a list of installed flatpak packages: + + .. code:: python + + [ + "org.gnome.Platform", + "org.kde.Platform", + "org.kde.Sdk", + "org.libreoffice.LibreOffice", + "org.videolan.VLC" + ] + """ + + default = list + + def command(self): + return "flatpak list --columns=application" + + def process(self, output): + return [flatpak for flatpak in output[1:]] diff --git a/pyinfra/facts/hardware.py b/pyinfra/facts/hardware.py index 155dbc6c2..6da623ea5 100644 --- a/pyinfra/facts/hardware.py +++ b/pyinfra/facts/hardware.py @@ -109,6 +109,7 @@ class NetworkDevices(FactBase): ``ipv6_addresses`` facts for easier-to-use shortcuts to get device addresses. .. code:: python + "enp1s0": { "ether": "12:34:56:78:9A:BC", "mtu": 1500, diff --git a/pyinfra/facts/selinux.py b/pyinfra/facts/selinux.py index b2685658f..65a6f49d2 100644 --- a/pyinfra/facts/selinux.py +++ b/pyinfra/facts/selinux.py @@ -58,7 +58,8 @@ def process(self, output): class FileContextMapping(FactBase): """ Returns structured SELinux file context data for the specified target path prefix - using the same format as :ref:`selinux.FileContext`. If there is no mapping, it returns ``{}`` + using the same format as :ref:`facts:selinux.FileContext`. + If there is no mapping, it returns ``{}`` Note: This fact requires root privileges. """ @@ -85,6 +86,7 @@ class SEPorts(FactBase): Note: This fact requires root privileges. .. code:: python + { "tcp": { 22: "ssh_port_t", ...}, "udp": { ...} diff --git a/pyinfra/facts/systemd.py b/pyinfra/facts/systemd.py index d1b75facb..07e6d1bcd 100644 --- a/pyinfra/facts/systemd.py +++ b/pyinfra/facts/systemd.py @@ -32,6 +32,9 @@ def _make_systemctl_cmd(user_mode=False, machine=None, user_name=None): systemctl_cmd.append("--machine={1}@{0}".format(machine, user_name)) else: systemctl_cmd.append("--machine={0}".format(machine)) + elif user_name is not None: + # If only the user is given, assume that the connection should be made to the local machine + systemctl_cmd.append("--machine={0}@.host".format(user_name)) return StringCommand(*systemctl_cmd) @@ -58,7 +61,7 @@ def requires_command(self, *args, **kwargs) -> str: default = dict state_key = "SubState" - state_values = ["running", "waiting", "exited"] + state_values = ["running", "waiting", "exited", "listening"] def command( self, diff --git a/pyinfra/facts/zfs.py b/pyinfra/facts/zfs.py new file mode 100644 index 000000000..a48dfcddc --- /dev/null +++ b/pyinfra/facts/zfs.py @@ -0,0 +1,57 @@ +""" +Manage ZFS filesystems. +""" + +from pyinfra.api import FactBase, ShortFactBase + + +def _process_zfs_props_table(output): + datasets: dict = {} + for line in output: + dataset, property, value, source = tuple(line.split("\t")) + if dataset not in datasets: + datasets[dataset] = {} + datasets[dataset][property] = value + return datasets + + +class Pools(FactBase): + def command(self): + return "zpool get -H all" + + @staticmethod + def process(output): + return _process_zfs_props_table(output) + + +class Datasets(FactBase): + def command(self): + return "zfs get -H all" + + @staticmethod + def process(output): + return _process_zfs_props_table(output) + + +class Filesystems(ShortFactBase): + fact = Datasets + + @staticmethod + def process_data(data): + return {name: props for name, props in data.items() if props.get("type") == "filesystem"} + + +class Snapshots(ShortFactBase): + fact = Datasets + + @staticmethod + def process_data(data): + return {name: props for name, props in data.items() if props.get("type") == "snapshot"} + + +class Volumes(ShortFactBase): + fact = Datasets + + @staticmethod + def process_data(data): + return {name: props for name, props in data.items() if props.get("type") == "volume"} diff --git a/pyinfra/operations/apt.py b/pyinfra/operations/apt.py index 53717ea6e..c4212dd3d 100644 --- a/pyinfra/operations/apt.py +++ b/pyinfra/operations/apt.py @@ -9,7 +9,13 @@ from pyinfra import host from pyinfra.api import OperationError, operation -from pyinfra.facts.apt import AptKeys, AptSources, parse_apt_repo +from pyinfra.facts.apt import ( + AptKeys, + AptSources, + SimulateOperationWillChange, + noninteractive_apt, + parse_apt_repo, +) from pyinfra.facts.deb import DebPackage, DebPackages from pyinfra.facts.files import File from pyinfra.facts.gpg import GpgKey @@ -21,21 +27,22 @@ APT_UPDATE_FILENAME = "/var/lib/apt/periodic/update-success-stamp" -def noninteractive_apt(command: str, force=False): - args = ["DEBIAN_FRONTEND=noninteractive apt-get -y"] - - if force: - args.append("--force-yes") - - args.extend( - ( - '-o Dpkg::Options::="--force-confdef"', - '-o Dpkg::Options::="--force-confold"', - command, - ), - ) - - return " ".join(args) +def _simulate_then_perform(command: str): + changes = host.get_fact(SimulateOperationWillChange, command) + + if not changes: + # Simulating apt-get command failed, so the actual + # operation will probably fail too: + yield noninteractive_apt(command) + elif ( + changes["upgraded"] == 0 + and changes["newly_installed"] == 0 + and changes["removed"] == 0 + and changes["not_upgraded"] == 0 + ): + host.noop(f"{command} skipped, no changes would be performed") + else: + yield noninteractive_apt(command) @operation() @@ -53,7 +60,8 @@ def key(src: str | None = None, keyserver: str | None = None, keyid: str | list[ .. warning:: ``apt-key`` is deprecated in Debian, it is recommended NOT to use this operation and instead follow the instructions here: - https://wiki.debian.org/DebianRepository/UseThirdParty + + https://wiki.debian.org/DebianRepository/UseThirdParty **Examples:** @@ -320,7 +328,7 @@ def update(cache_time: int | None = None): _update = update # noqa: E305 -@operation(is_idempotent=False) +@operation() def upgrade(auto_remove: bool = False): """ Upgrades all apt packages. @@ -348,13 +356,13 @@ def upgrade(auto_remove: bool = False): if auto_remove: command.append("--autoremove") - yield noninteractive_apt(" ".join(command)) + yield from _simulate_then_perform(" ".join(command)) _upgrade = upgrade # noqa: E305 (for use below where update is a kwarg) -@operation(is_idempotent=False) +@operation() def dist_upgrade(): """ Updates all apt packages, employing dist-upgrade. @@ -368,7 +376,7 @@ def dist_upgrade(): ) """ - yield noninteractive_apt("dist-upgrade") + yield from _simulate_then_perform("dist-upgrade") @operation() diff --git a/pyinfra/operations/docker.py b/pyinfra/operations/docker.py index 67a421032..f92e08c61 100644 --- a/pyinfra/operations/docker.py +++ b/pyinfra/operations/docker.py @@ -4,7 +4,7 @@ from pyinfra import host from pyinfra.api import operation -from pyinfra.facts.docker import DockerContainers, DockerNetworks, DockerVolumes +from pyinfra.facts.docker import DockerContainer, DockerNetwork, DockerVolume from .util.docker import handle_docker @@ -68,7 +68,7 @@ def container( ) """ - existent_container = [c for c in host.get_fact(DockerContainers) if container in c["Name"]] + existent_container = host.get_fact(DockerContainer, object_id=container) if force: if existent_container: @@ -183,7 +183,7 @@ def volume(volume, driver="", labels=None, present=True): ) """ - existent_volume = [v for v in host.get_fact(DockerVolumes) if v["Name"] == volume] + existent_volume = host.get_fact(DockerVolume, object_id=volume) if present: @@ -257,7 +257,7 @@ def network( present=True, ) """ - existent_network = [n for n in host.get_fact(DockerNetworks) if n["Name"] == network] + existent_network = host.get_fact(DockerNetwork, object_id=network) if present: if existent_network: diff --git a/pyinfra/operations/files.py b/pyinfra/operations/files.py index 9657e9896..c7ffc94de 100644 --- a/pyinfra/operations/files.py +++ b/pyinfra/operations/files.py @@ -256,7 +256,7 @@ def line( change bits of lines, see ``files.replace``. Regex line escaping: - If matching special characters (eg a crontab line containing *), remember to escape + If matching special characters (eg a crontab line containing ``*``), remember to escape it first using Python's ``re.escape``. Backup: @@ -523,7 +523,7 @@ def sync( + mode: permissions of the files + dir_mode: permissions of the directories + delete: delete remote files not present locally - + exclude: string or list/tuple of strings to match & exclude files (eg *.pyc) + + exclude: string or list/tuple of strings to match & exclude files (eg ``*.pyc``) + exclude_dir: string or list/tuple of strings to match & exclude directories (eg node_modules) + add_deploy_dir: interpret src as relative to deploy directory instead of current directory @@ -915,7 +915,8 @@ def template( user: str | None = None, group: str | None = None, mode: str | None = None, - create_remote_dir=True, + create_remote_dir: bool = True, + jinja_env_kwargs: dict[str, Any] | None = None, **data, ): ''' @@ -927,12 +928,18 @@ def template( + group: group to own the files + mode: permissions of the files + create_remote_dir: create the remote directory if it doesn't exist + + jinja_env_kwargs: keyword arguments to be passed into the jinja Environment() ``create_remote_dir``: If the remote directory does not exist it will be created using the same user & group as passed to ``files.put``. The mode will *not* be copied over, if this is required call ``files.directory`` separately. + ``jinja_env_kwargs``: + To have more control over how jinja2 renders your template, you can pass + a dict with arguments that will be passed as keyword args to the jinja2 + `Environment() `_. + Notes: Common convention is to store templates in a "templates" directory and have a filename suffix with '.j2' (for jinja2). @@ -1002,7 +1009,7 @@ def template( # Render and make file-like it's output try: - output = get_template(src).render(data) + output = get_template(src, jinja_env_kwargs).render(data) except (TemplateRuntimeError, TemplateSyntaxError, UndefinedError) as e: trace_frames = [ frame diff --git a/pyinfra/operations/flatpak.py b/pyinfra/operations/flatpak.py new file mode 100644 index 000000000..3a99152e7 --- /dev/null +++ b/pyinfra/operations/flatpak.py @@ -0,0 +1,79 @@ +""" +Manage flatpak packages. See https://www.flatpak.org/ +""" + +from __future__ import annotations + +from pyinfra import host +from pyinfra.api import operation +from pyinfra.facts.flatpak import FlatpakPackages + + +@operation() +def packages( + packages: str | list[str] | None = None, + present=True, +): + """ + Install/remove a flatpak package + + + packages: List of packages + + present: whether the package should be installed + + **Examples:** + + .. code:: python + + # Install vlc flatpak + flatpak.package( + name="Install vlc", + packages="org.videolan.VLC", + ) + + # Install multiple flatpaks + flatpak.package( + name="Install vlc and kodi", + packages=["org.videolan.VLC", "tv.kodi.Kodi"], + ) + + # Remove vlc + flatpak.package( + name="Remove vlc", + packages="org.videolan.VLC", + present=False, + ) + """ + + if packages is None: + return + + if isinstance(packages, str): + packages = [packages] + + flatpak_packages = host.get_fact(FlatpakPackages) + + install_packages = [] + remove_packages = [] + + for package in packages: + # it's installed + if package in flatpak_packages: + if not present: + # we don't want it + remove_packages.append(package) + + # it's not installed + if package not in flatpak_packages: + # we want it + if present: + install_packages.append(package) + + # we don't want it + else: + host.noop(f"flatpak package {package} is not installed") + + if install_packages: + yield " ".join(["flatpak", "install", "--noninteractive"] + install_packages) + + if remove_packages: + yield " ".join(["flatpak", "uninstall", "--noninteractive"] + remove_packages) diff --git a/pyinfra/operations/git.py b/pyinfra/operations/git.py index e163b6134..5d42511d0 100644 --- a/pyinfra/operations/git.py +++ b/pyinfra/operations/git.py @@ -184,7 +184,7 @@ def worktree( + from_remote_branch: a 2-tuple ``(remote, branch)`` that identifies a remote branch + present: whether the working tree should exist + assume_repo_exists: whether to assume the main repo exists - + force: remove unclean working tree if should not exist + + force: whether to use ``--force`` when adding/removing worktrees + user: chown files to this user after + group: chown files to this group after @@ -205,6 +205,14 @@ def worktree( commitish="4e091aa0" ) + git.worktree( + name="Create a worktree from the tag `4e091aa0`, even if already registered", + repo="/usr/local/src/pyinfra/master", + worktree="/usr/local/src/pyinfra/2.x", + commitish="2.x", + force=True + ) + git.worktree( name="Create a worktree with a new local branch `v1.0`", repo="/usr/local/src/pyinfra/master", @@ -250,6 +258,15 @@ def worktree( commitish="v1.0" ) + git.worktree( + name="Idempotent worktree creation, never pulls", + repo="/usr/local/src/pyinfra/master", + worktree="/usr/local/src/pyinfra/hotfix", + new_branch="v1.0", + commitish="v1.0", + pull=False + ) + git.worktree( name="Pull an existing worktree already linked to a tracking branch", repo="/usr/local/src/pyinfra/master", @@ -295,6 +312,9 @@ def worktree( elif detached: command_parts.append("--detach") + if force: + command_parts.append("--force") + command_parts.append(worktree) if commitish: @@ -317,9 +337,12 @@ def worktree( # It exists and we still want it => pull/rebase it elif host.get_fact(Directory, path=worktree) and present: + if not pull: + host.noop("Pull is disabled") + # pull the worktree only if it's already linked to a tracking branch or # if a remote branch is set - if host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch: + elif host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch: command = "cd {0} && git pull".format(worktree) if rebase: diff --git a/pyinfra/operations/mysql.py b/pyinfra/operations/mysql.py index 3603d61df..4c0303a41 100644 --- a/pyinfra/operations/mysql.py +++ b/pyinfra/operations/mysql.py @@ -554,7 +554,7 @@ def dump( ) """ - yield "{0} > {1}".format( + yield StringCommand( make_mysql_command( executable="mysqldump", database=database, @@ -563,7 +563,8 @@ def dump( host=mysql_host, port=mysql_port, ), - dest, + ">", + QuoteString(dest), ) @@ -595,7 +596,7 @@ def load( ) """ - commands_bits = [ + yield StringCommand( make_mysql_command( database=database, user=mysql_user, @@ -605,5 +606,4 @@ def load( ), "<", QuoteString(src), - ] - yield StringCommand(*commands_bits) + ) diff --git a/pyinfra/operations/postgres.py b/pyinfra/operations/postgres.py index 0a38e6a2b..4767be449 100644 --- a/pyinfra/operations/postgres.py +++ b/pyinfra/operations/postgres.py @@ -16,7 +16,7 @@ from __future__ import annotations from pyinfra import host -from pyinfra.api import MaskString, StringCommand, operation +from pyinfra.api import MaskString, QuoteString, StringCommand, operation from pyinfra.facts.postgres import ( PostgresDatabases, PostgresRoles, @@ -302,7 +302,7 @@ def dump( port=psql_port, ), ">", - dest, + QuoteString(dest), ) @@ -345,5 +345,5 @@ def load( port=psql_port, ), "<", - src, + QuoteString(src), ) diff --git a/pyinfra/operations/server.py b/pyinfra/operations/server.py index 09baa350f..d9fccab5f 100644 --- a/pyinfra/operations/server.py +++ b/pyinfra/operations/server.py @@ -89,12 +89,12 @@ def wait_and_reconnect(state, host): # pragma: no cover sleep(delay) max_retries = round(reboot_timeout / interval) - host.connection = None # remove the connection object + host.disconnect() # make sure we are properly disconnected retries = 0 while True: host.connect(show_errors=False) - if host.connection: + if host.connected: break if retries > max_retries: @@ -723,11 +723,11 @@ def comma_sep(value): if any( ( special_time != existing_crontab.get("special_time"), - minute != existing_crontab.get("minute"), - hour != existing_crontab.get("hour"), - month != existing_crontab.get("month"), - day_of_week != existing_crontab.get("day_of_week"), - day_of_month != existing_crontab.get("day_of_month"), + try_int(minute) != existing_crontab.get("minute"), + try_int(hour) != existing_crontab.get("hour"), + try_int(month) != existing_crontab.get("month"), + try_int(day_of_week) != existing_crontab.get("day_of_week"), + try_int(day_of_month) != existing_crontab.get("day_of_month"), existing_crontab_command != command, ), ): @@ -881,11 +881,11 @@ def read_any_pub_key_file(key): if path.exists(try_path): with open(try_path, "r") as f: - return f.read().strip() + return [key.strip() for key in f.readlines()] - return key.strip() + return [key.strip()] - public_keys = list(map(read_any_pub_key_file, public_keys)) + public_keys = [key for key_or_file in public_keys for key in read_any_pub_key_file(key_or_file)] # Ensure .ssh directory # note that this always outputs commands unless the SSH user has access to the diff --git a/pyinfra/operations/zfs.py b/pyinfra/operations/zfs.py new file mode 100644 index 000000000..8c3c39f23 --- /dev/null +++ b/pyinfra/operations/zfs.py @@ -0,0 +1,175 @@ +""" +Manage ZFS filesystems. +""" + +from pyinfra import host +from pyinfra.api import operation +from pyinfra.facts.zfs import Datasets, Snapshots + + +@operation() +def dataset( + dataset_name, + present=True, + recursive=False, + sparse=None, + volume_size=None, + properties={}, + **extra_props, +): + """ + Create, destroy or set properties on a ZFS dataset (e.g. filesystem, + volume, snapshot). + + + dataset_name: name of the filesystem to operate on + + present: whether the named filesystem should exist + + recursive: whether to create parent datasets, or destroy child datasets + + sparse: for volumes, whether to create a sparse volume with no allocation + + volume_size: the size of the volume + + properties: the ZFS properties that should be set on the dataset. + + **extra_props: additional props; merged with `properties` for convenience + + **Examples:** + + .. code:: python + + zfs.dataset( + "tank/srv", + mountpoint="/srv", + compression="lz4", + properties={"com.sun:auto_snapshot": "true"} + ) + zfs.dataset("tank/vm-disks/db_srv_04", volume_size="32G") # creates a volume + zfs.dataset("tank/home@old_version", present=False) + + """ + + noop_msg = "{0} is already {1}".format(dataset_name, "present" if present else "absent") + + properties.update(extra_props) + + datasets = host.get_fact(Datasets) + + existing_dataset = datasets.get(dataset_name) + + if present and not existing_dataset: + args = ["-o {0}={1}".format(prop, value) for prop, value in properties.items()] + if recursive: + args.append("-p") + if sparse: + args.append("-s") + if volume_size: + args.append("-V {0}".format(volume_size)) + + args.sort() # dicts are unordered, so make sure the test results are deterministic + + yield "zfs create {0} {1}".format(" ".join(args), dataset_name) + + elif present and existing_dataset: + prop_args = [ + "{0}={1}".format(prop, value) + for prop, value in properties.items() - existing_dataset.items() + ] + prop_args.sort() + if prop_args: + yield "zfs set {0} {1}".format(" ".join(prop_args), dataset_name) + else: + host.noop(noop_msg) + + elif existing_dataset and not present: + recursive_arg = "-r" if recursive else "" + yield "zfs destroy {0} {1}".format(recursive_arg, dataset_name) + + else: + host.noop(noop_msg) + + +@operation() +def snapshot(snapshot_name, present=True, recursive=False, properties={}, **extra_props): + """ + Create or destroy a ZFS snapshot, or modify its properties. + + + dataset_name: name of the filesystem to operate on + + present: whether the named filesystem should exist + + recursive: whether to snapshot child datasets + + properties: the ZFS properties that should be set on the snapshot. + + **extra_props: additional props; merged with `properties` for convenience + + **Examples:** + + .. code:: python + + zfs.snapshot("tank/home@weekly_backup") + + """ + properties.update(extra_props) + snapshots = host.get_fact(Snapshots) + + if snapshot_name in snapshots or not present: + yield from dataset._inner(snapshot_name, present=present, properties=properties) + + else: + args = ["-o {0}={1}".format(prop, value) for prop, value in properties.items()] + if recursive: + args.append("-r") + yield "zfs snap {0} {1}".format(" ".join(args), snapshot_name) + + +@operation() +def volume( + volume_name, size, sparse=False, present=True, recursive=False, properties={}, **extra_props +): + """ + Create or destroy a ZFS volume, or modify its properties. + + + volume_name: name of the volume to operate on + + size: the size of the volume + + sparse: create a sparse volume + + present: whether the named volume should exist + + recursive: whether to create parent datasets or destroy child datasets + + properties: the ZFS properties that should be set on the snapshot. + + **extra_props: additional props; merged with `properties` for convenience + + **Examples:** + + .. code:: python + + zfs.volume("tank/vm-disks/db_srv_04", "32G") + + """ + properties.update(extra_props) + yield from dataset._inner( + volume_name, + volume_size=size, + present=present, + sparse=sparse, + recursive=recursive, + properties=properties, + ) + + +@operation() +def filesystem(fs_name, present=True, recursive=False, properties={}, **extra_props): + """ + Create or destroy a ZFS filesystem, or modify its properties. + + + fs_name: name of the volume to operate on + + present: whether the named volume should exist + + recursive: whether to create parent datasets or destroy child datasets + + properties: the ZFS properties that should be set on the snapshot. + + **extra_props: additional props; merged with `properties` for convenience + + **Examples:** + + .. code:: python + + zfs.filesystem("tank/vm-disks/db_srv_04", "32G") + + """ + properties.update(extra_props) + yield from dataset._inner( + fs_name, + present=present, + recursive=recursive, + properties=properties, + ) diff --git a/pyinfra_cli/inventory.py b/pyinfra_cli/inventory.py index 543d6b75f..95105a4d1 100644 --- a/pyinfra_cli/inventory.py +++ b/pyinfra_cli/inventory.py @@ -6,6 +6,7 @@ from pyinfra import logger from pyinfra.api.inventory import Inventory +from pyinfra.connectors.sshuserclient.client import get_ssh_config from pyinfra.context import ctx_inventory from .exceptions import CliError @@ -88,7 +89,34 @@ def _resolves_to_host(maybe_host: str) -> bool: socket.getaddrinfo(maybe_host, port=None) return True except socket.gaierror: - return False + alias = _get_ssh_alias(maybe_host) + if not alias: + return False + + try: + socket.getaddrinfo(alias, port=None) + return True + except socket.gaierror: + return False + + +def _get_ssh_alias(maybe_host: str) -> Optional[str]: + logger.debug('Checking if "%s" is an SSH alias', maybe_host) + + # Note this does not cover the case where `host.data.ssh_config_file` is used + ssh_config = get_ssh_config() + + if ssh_config is None: + logger.debug("Could not load SSH config") + return None + + options = ssh_config.lookup(maybe_host) + alias = options.get("hostname") + + if alias is None or maybe_host == alias: + return None + + return alias def make_inventory( @@ -105,7 +133,11 @@ def make_inventory( # (1) an inventory file is a common use case and (2) no other option can have a comma or an @ # symbol in them. is_path_or_host_list_or_connector = ( - path.exists(inventory) or "," in inventory or "@" in inventory + path.exists(inventory) + or "," in inventory + or "@" in inventory + # Special case: passing an arbitrary name and specifying --data ssh_hostname=a.b.c + or (override_data is not None and "ssh_hostname" in override_data) ) if not is_path_or_host_list_or_connector: # Next, try loading the inventory from a python function. This happens before checking for a diff --git a/pyinfra_cli/prints.py b/pyinfra_cli/prints.py index 519ecf797..51673c2f6 100644 --- a/pyinfra_cli/prints.py +++ b/pyinfra_cli/prints.py @@ -131,6 +131,10 @@ def print_facts(facts): def print_support_info(): + from importlib.metadata import PackageNotFoundError, requires, version + + from packaging.requirements import Requirement + click.echo( """ If you are having issues with pyinfra or wish to make feature requests, please @@ -144,6 +148,18 @@ def print_support_info(): click.echo(" Release: {0}".format(platform.uname()[2]), err=True) click.echo(" Machine: {0}".format(platform.uname()[4]), err=True) click.echo(" pyinfra: v{0}".format(__version__), err=True) + + for requirement_string in sorted(requires("pyinfra") or []): + requirement = Requirement(requirement_string) + try: + click.echo( + " {0}: v{1}".format(requirement.name, version(requirement.name)), + err=True, + ) + except PackageNotFoundError: + # package not installed in this environment + continue + click.echo(" Executable: {0}".format(sys.argv[0]), err=True) click.echo( " Python: {0} ({1}, {2})".format( diff --git a/pyinfra_cli/util.py b/pyinfra_cli/util.py index 6416da280..a166b4116 100644 --- a/pyinfra_cli/util.py +++ b/pyinfra_cli/util.py @@ -180,13 +180,13 @@ def try_import_module_attribute(path, prefix=None, raise_for_none=True): if module is None: if raise_for_none: - raise CliError(f"No such module: {possible_modules[-1]}") + raise CliError(f"No such module: {possible_modules[0]}") return attr = getattr(module, attr_name, None) if attr is None: if raise_for_none: - raise CliError(f"No such attribute in module {possible_modules[-1]}: {attr_name}") + raise CliError(f"No such attribute in module {possible_modules[0]}: {attr_name}") return return attr diff --git a/scripts/dev-lint.sh b/scripts/dev-lint.sh index 7f4405407..00b40e418 100755 --- a/scripts/dev-lint.sh +++ b/scripts/dev-lint.sh @@ -1,6 +1,14 @@ #!/usr/bin/env bash -set -euxo pipefail +set -euo pipefail +echo "Execute black..." +black ./ + +echo "Execute flake8..." flake8 + +echo "Execute mypy..." mypy + +echo "Linting complete!" diff --git a/scripts/dev-test.sh b/scripts/dev-test.sh index 97ddbafdb..ea7379290 100755 --- a/scripts/dev-test.sh +++ b/scripts/dev-test.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash -set -euxo pipefail +set -euo pipefail +echo "Execute pytest..." pytest + +echo "Tests complete!" diff --git a/scripts/generate_connectors_docs.py b/scripts/generate_connectors_docs.py index 48018a1dd..0a1306bcd 100644 --- a/scripts/generate_connectors_docs.py +++ b/scripts/generate_connectors_docs.py @@ -36,6 +36,7 @@ def build_connectors_docs(): lines.append(title_line("~", "Examples")) lines.append("") lines.append(cleandoc(examples_doc)) + lines.append("") else: data_title = "Usage" lines.append(data_title) diff --git a/scripts/spellcheck.sh b/scripts/spellcheck.sh new file mode 100755 index 000000000..a5c9e0f11 --- /dev/null +++ b/scripts/spellcheck.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +flake8 --select=SC --ignore= diff --git a/setup.cfg b/setup.cfg index dce845c26..0c0b0ce9b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,9 +4,7 @@ universal = 1 [flake8] exclude = .git,venv,build -ignore = W503,C815,C816,E704 -# See: https://github.com/PyCQA/pycodestyle/issues/373 -extend-ignore = E203 +ignore = W503,C815,C816,E704,E203,SC max-line-length = 100 # flake8-quotes diff --git a/tests/facts/apt.AptSources/component_with_number.json b/tests/facts/apt.AptSources/component_with_number.json new file mode 100644 index 000000000..97b3989aa --- /dev/null +++ b/tests/facts/apt.AptSources/component_with_number.json @@ -0,0 +1,19 @@ +{ + "output": [ + "deb http://archive.ubuntu.com/ubuntu trusty restricted pi4" + ], + "command": "(! test -f /etc/apt/sources.list || cat /etc/apt/sources.list) && (cat /etc/apt/sources.list.d/*.list || true)", + "requires_command": "apt", + "fact": [ + { + "url": "http://archive.ubuntu.com/ubuntu", + "distribution": "trusty", + "type": "deb", + "components": [ + "restricted", + "pi4" + ], + "options": {} + } + ] +} diff --git a/tests/facts/apt.AptSources/sources.json b/tests/facts/apt.AptSources/sources.json index 8d85f3e48..2b5365f28 100644 --- a/tests/facts/apt.AptSources/sources.json +++ b/tests/facts/apt.AptSources/sources.json @@ -2,6 +2,7 @@ "output": [ "deb http://archive.ubuntu.com/ubuntu trusty restricted", "deb-src [arch=amd64,i386] http://archive.ubuntu.com/ubuntu trusty main", + "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse", "nope" ], "command": "(! test -f /etc/apt/sources.list || cat /etc/apt/sources.list) && (cat /etc/apt/sources.list.d/*.list || true)", @@ -26,6 +27,18 @@ "options": { "arch": ["amd64", "i386"] } + }, + { + "url": "https://repo.mongodb.org/apt/ubuntu", + "distribution": "jammy/mongodb-org/7.0", + "type": "deb", + "components": [ + "multiverse" + ], + "options": { + "arch": ["amd64", "arm64"], + "signed-by": "/usr/share/keyrings/mongodb-server-7.0.gpg" + } } ] } diff --git a/tests/facts/apt.SimulateOperationWillChange/upgrade-nochanges.json b/tests/facts/apt.SimulateOperationWillChange/upgrade-nochanges.json new file mode 100644 index 000000000..b926d665b --- /dev/null +++ b/tests/facts/apt.SimulateOperationWillChange/upgrade-nochanges.json @@ -0,0 +1,22 @@ +{ + "arg": "upgrade", + "command": "LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade --dry-run", + "requires_command": "apt-get", + "output": [ + "NOTE: This is only a simulation!", + "apt-get needs root privileges for real execution.", + "Keep also in mind that locking is deactivated,", + "so don't depend on the relevance to the real current situation!", + "Reading package lists... Done", + "Building dependency tree... Done", + "Reading state information... Done", + "Calculating upgrade... Done", + "0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded." + ], + "fact": { + "upgraded": 0, + "newly_installed": 0, + "removed": 0, + "not_upgraded": 0 + } +} diff --git a/tests/facts/apt.SimulateOperationWillChange/upgrade.json b/tests/facts/apt.SimulateOperationWillChange/upgrade.json new file mode 100644 index 000000000..07108f177 --- /dev/null +++ b/tests/facts/apt.SimulateOperationWillChange/upgrade.json @@ -0,0 +1,57 @@ +{ + "arg": "upgrade", + "command": "LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade --dry-run", + "requires_command": "apt-get", + "output": [ + "NOTE: This is only a simulation!", + " apt-get needs root privileges for real execution.", + " Keep also in mind that locking is deactivated,", + " so don't depend on the relevance to the real current situation!", + "Reading package lists... Done", + "Building dependency tree... Done", + "Reading state information... Done", + "Calculating upgrade... Done", + "The following packages will be upgraded:", + " apache2-bin firefox-esr gir1.2-javascriptcoregtk-4.0 gir1.2-javascriptcoregtk-4.1 gir1.2-webkit2-4.0", + " gir1.2-webkit2-4.1 libgsf-1-114 libgsf-1-common libgsf-bin libjavascriptcoregtk-4.0-18", + " libjavascriptcoregtk-4.1-0 libjavascriptcoregtk-6.0-1 libwebkit2gtk-4.0-37 libwebkit2gtk-4.1-0", + " libwebkitgtk-6.0-4", + "15 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.", + "Inst apache2-bin [2.4.62-1~deb12u1] (2.4.62-1~deb12u2 Debian-Security:12/stable-security [amd64])", + "Inst firefox-esr [128.3.0esr-1~deb12u1] (128.3.1esr-1~deb12u1 Debian-Security:12/stable-security [amd64])", + "Inst gir1.2-webkit2-4.0 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst gir1.2-javascriptcoregtk-4.0 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst libwebkit2gtk-4.0-37 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst libjavascriptcoregtk-4.0-18 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Inst gir1.2-webkit2-4.1 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst gir1.2-javascriptcoregtk-4.1 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst libwebkit2gtk-4.1-0 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst libjavascriptcoregtk-4.1-0 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Inst libgsf-1-common [1.14.50-1] (1.14.50-1+deb12u1 Debian-Security:12/stable-security [all])", + "Inst libgsf-1-114 [1.14.50-1] (1.14.50-1+deb12u1 Debian-Security:12/stable-security [amd64])", + "Inst libgsf-bin [1.14.50-1] (1.14.50-1+deb12u1 Debian-Security:12/stable-security [amd64])", + "Inst libwebkitgtk-6.0-4 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64]) []", + "Inst libjavascriptcoregtk-6.0-1 [2.44.3-1~deb12u1] (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf apache2-bin (2.4.62-1~deb12u2 Debian-Security:12/stable-security [amd64])", + "Conf firefox-esr (128.3.1esr-1~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf gir1.2-webkit2-4.0 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf gir1.2-javascriptcoregtk-4.0 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libwebkit2gtk-4.0-37 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libjavascriptcoregtk-4.0-18 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf gir1.2-webkit2-4.1 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf gir1.2-javascriptcoregtk-4.1 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libwebkit2gtk-4.1-0 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libjavascriptcoregtk-4.1-0 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libgsf-1-common (1.14.50-1+deb12u1 Debian-Security:12/stable-security [all])", + "Conf libgsf-1-114 (1.14.50-1+deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libgsf-bin (1.14.50-1+deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libwebkitgtk-6.0-4 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])", + "Conf libjavascriptcoregtk-6.0-1 (2.46.0-2~deb12u1 Debian-Security:12/stable-security [amd64])" + ], + "fact": { + "upgraded": 15, + "newly_installed": 0, + "removed": 0, + "not_upgraded": 0 + } +} diff --git a/tests/facts/files.File/tilde.json b/tests/facts/files.File/tilde.json new file mode 100644 index 000000000..5a0252354 --- /dev/null +++ b/tests/facts/files.File/tilde.json @@ -0,0 +1,16 @@ +{ + "arg": "~/.bashrc", + "command": "! (test -e ~/.bashrc || test -L ~/.bashrc ) || ( stat -c 'user=%U group=%G mode=%A atime=%X mtime=%Y ctime=%Z size=%s %N' ~/.bashrc 2> /dev/null || stat -f 'user=%Su group=%Sg mode=%Sp atime=%a mtime=%m ctime=%c size=%z %N%SY' ~/.bashrc )", + "output": [ + "user=pyinfra group=pyinfra mode=-rw-r--r-- atime=1723483091 mtime=1722760187 ctime=1722760187 size=4310 '/home/pyinfra/.bashrc'" + ], + "fact": { + "user": "pyinfra", + "group": "pyinfra", + "mode": 644, + "atime": "2024-08-12T17:18:11", + "mtime": "2024-08-04T08:29:47", + "ctime": "2024-08-04T08:29:47", + "size": 4310 + } +} diff --git a/tests/facts/files.File/tilde_with_space.json b/tests/facts/files.File/tilde_with_space.json new file mode 100644 index 000000000..3cabae5f9 --- /dev/null +++ b/tests/facts/files.File/tilde_with_space.json @@ -0,0 +1,16 @@ +{ + "arg": "~/My Documents/file", + "command": "! (test -e ~/'My Documents/file' || test -L ~/'My Documents/file' ) || ( stat -c 'user=%U group=%G mode=%A atime=%X mtime=%Y ctime=%Z size=%s %N' ~/'My Documents/file' 2> /dev/null || stat -f 'user=%Su group=%Sg mode=%Sp atime=%a mtime=%m ctime=%c size=%z %N%SY' ~/'My Documents/file' )", + "output": [ + "user=pyinfra group=pyinfra mode=-rw-r--r-- atime=1723484281 mtime=1723484281 ctime=1723484281 size=0 '/home/pyinfra/My Documents/file'" + ], + "fact": { + "user": "pyinfra", + "group": "pyinfra", + "mode": 644, + "atime": "2024-08-12T17:38:01", + "mtime": "2024-08-12T17:38:01", + "ctime": "2024-08-12T17:38:01", + "size": 0 + } +} diff --git a/tests/facts/flatpak.FlatpakPackage/kodi.json b/tests/facts/flatpak.FlatpakPackage/kodi.json new file mode 100644 index 000000000..68c64c530 --- /dev/null +++ b/tests/facts/flatpak.FlatpakPackage/kodi.json @@ -0,0 +1,32 @@ +{ + "arg": "tv.kodi.Kodi", + "command": "flatpak info tv.kodi.Kodi", + "requires_command": "flatpak", + "output": [ + "", + "Kodi - Ultimate entertainment center", + "", + " ID: tv.kodi.Kodi", + " Ref: app/tv.kodi.Kodi/x86_64/stable", + " Arch: x86_64", + " Branch: stable", + " Version: 21.0-Omega", + " License: GPL-2.0-only GPL-2.0-or-later LGPL-2.1-or-later MIT BSD-3-Clause BSD-4-Clause", + " Origin: flathub", + " Collection: org.flathub.Stable", + "Installation: system", + " Installed: 514.7 MB", + " Runtime: org.freedesktop.Platform/x86_64/23.08", + " Sdk: org.freedesktop.Sdk/x86_64/23.08", + "", + " Commit: 03934475318a3a17476eed0f73e2d8339194373109f324868fc22484cbd7276e", + " Parent: cb0195ac49050d0e16c840244de4c99061453102a270f6f930f3186a25bcd562", + " Subject: Update addons (787f64c4)", + " Date: 2024-06-01 18:22:30 +0000" + ], + "fact": { + "id": "tv.kodi.Kodi", + "ref": "app/tv.kodi.Kodi/x86_64/stable", + "version": "21.0-Omega" + } +} diff --git a/tests/facts/flatpak.FlatpakPackage/not_found.json b/tests/facts/flatpak.FlatpakPackage/not_found.json new file mode 100644 index 000000000..731b7de9b --- /dev/null +++ b/tests/facts/flatpak.FlatpakPackage/not_found.json @@ -0,0 +1,9 @@ +{ + "arg": "not_found.test.test", + "command": "flatpak info not_found.test.test", + "requires_command": "flatpak", + "output": [ + "error: not_found.test.test/*unspecified*/*unspecified* not installed" + ], + "fact": {} +} diff --git a/tests/facts/flatpak.FlatpakPackage/vlc.json b/tests/facts/flatpak.FlatpakPackage/vlc.json new file mode 100644 index 000000000..e23e659bf --- /dev/null +++ b/tests/facts/flatpak.FlatpakPackage/vlc.json @@ -0,0 +1,32 @@ +{ + "arg": "org.videolan.VLC", + "command": "flatpak info org.videolan.VLC", + "requires_command": "flatpak", + "output": [ + "", + "VLC - VLC media player, the open-source multimedia player", + "", + " ID: org.videolan.VLC", + " Ref: app/org.videolan.VLC/x86_64/stable", + " Arch: x86_64", + " Branch: stable", + " Version: 3.0.21", + " License: GPL-2.0+", + " Origin: flathub", + " Collection: org.flathub.Stable", + "Installation: system", + " Installed: 98.9 MB", + " Runtime: org.kde.Platform/x86_64/5.15-23.08", + " Sdk: org.kde.Sdk/x86_64/5.15-23.08", + "", + " Commit: 00fce8e80caa0b5a74e4fd8baeda39e5450f406e9992586a578ec991258c927c", + " Parent: 4905cf9d41d96f96b967ea4150a7f5d06930158f125fd41603936ddcd006aaaa", + " Subject: Update VLC to 3.0.21 and some deps (c6a6bb62)", + " Date: 2024-06-09 13:21:31 +0000" + ], + "fact": { + "id": "org.videolan.VLC", + "ref": "app/org.videolan.VLC/x86_64/stable", + "version": "3.0.21" + } +} diff --git a/tests/facts/flatpak.FlatpakPackages/packages.json b/tests/facts/flatpak.FlatpakPackages/packages.json new file mode 100644 index 000000000..d724931cd --- /dev/null +++ b/tests/facts/flatpak.FlatpakPackages/packages.json @@ -0,0 +1,17 @@ +{ + "command": "flatpak list --columns=application", + "requires_command": "flatpak", + "output": [ + "Application ID", + "com.bitwarden.desktop", + "com.mattermost.Desktop", + "org.videolan.VLC", + "tv.kodi.Kodi" + ], + "fact": [ + "com.bitwarden.desktop", + "com.mattermost.Desktop", + "org.videolan.VLC", + "tv.kodi.Kodi" + ] +} diff --git a/tests/facts/mysql.MysqlDatabases/multiple_custom_connection.json b/tests/facts/mysql.MysqlDatabases/multiple_custom_connection.json index d40378798..f266bb578 100644 --- a/tests/facts/mysql.MysqlDatabases/multiple_custom_connection.json +++ b/tests/facts/mysql.MysqlDatabases/multiple_custom_connection.json @@ -1,9 +1,9 @@ { "arg": ["myuser", "mypassword", "myhost", "myport"], - "command": [ - "mysql -u\"myuser\" -p\"mypassword\" -hmyhost -Pmyport -Be 'SELECT * FROM information_schema.SCHEMATA'", - "mysql -u\"myuser\" *** -hmyhost -Pmyport -Be 'SELECT * FROM information_schema.SCHEMATA'" - ], + "command": { + "raw": "mysql -u\"myuser\" -p\"mypassword\" -hmyhost -Pmyport -Be 'SELECT * FROM information_schema.SCHEMATA'", + "masked": "mysql -u\"myuser\" *** -hmyhost -Pmyport -Be 'SELECT * FROM information_schema.SCHEMATA'" + }, "requires_command": "mysql", "output": [ "CATALOG_NAME\tSCHEMA_NAME\tDEFAULT_CHARACTER_SET_NAME\tDEFAULT_COLLATION_NAME\tSQL_PATH", diff --git a/tests/facts/postgresql.PostgresqlRoles/multiple_custom_connection.json b/tests/facts/postgresql.PostgresqlRoles/multiple_custom_connection.json index a2bda1f44..df70c593b 100644 --- a/tests/facts/postgresql.PostgresqlRoles/multiple_custom_connection.json +++ b/tests/facts/postgresql.PostgresqlRoles/multiple_custom_connection.json @@ -1,9 +1,9 @@ { "arg": ["myuser", "mypassword", "myhost", "myport"], - "command": [ - "PGPASSWORD=\"mypassword\" psql -U myuser -h myhost -p myport -Ac 'SELECT * FROM pg_catalog.pg_roles'", - "*** psql -U myuser -h myhost -p myport -Ac 'SELECT * FROM pg_catalog.pg_roles'" - ], + "command": { + "raw": "PGPASSWORD=\"mypassword\" psql -U myuser -h myhost -p myport -Ac 'SELECT * FROM pg_catalog.pg_roles'", + "masked": "*** psql -U myuser -h myhost -p myport -Ac 'SELECT * FROM pg_catalog.pg_roles'" + }, "requires_command": "psql", "output": [ "rolname|rolsuper|rolinherit|rolcreaterole|rolcreatedb|rolcanlogin|rolreplication|rolconnlimit|rolpassword|rolvaliduntil|rolbypassrls|rolconfig|oid", diff --git a/tests/facts/zfs.Datasets/datasets.json b/tests/facts/zfs.Datasets/datasets.json new file mode 100644 index 000000000..f9a8782bd --- /dev/null +++ b/tests/facts/zfs.Datasets/datasets.json @@ -0,0 +1,33 @@ +{ + "command": "zfs get -H all", + "output": [ + "tank\ttype\tfilesystem\t-", + "tank\tsize\t2.71T\t-", + "tank\tfeature@encryption\tenabled\tlocal", + "tank\tcom.sun:auto_snapshot\ttrue\t-", + "tank/home\ttype\tfilesystem\t-", + "tank/home\tcom.sun:auto_snapshot\ttrue\tinherited from tank", + "tank/home@old\ttype\tsnapshot\t-", + "tank/home@old\tencryption\toff\tdefault", + "tank/myvol\ttype\tvolume\t-" + ], + "fact": { + "tank": { + "type": "filesystem", + "size": "2.71T", + "feature@encryption": "enabled", + "com.sun:auto_snapshot": "true" + }, + "tank/home": { + "type": "filesystem", + "com.sun:auto_snapshot": "true" + }, + "tank/myvol": { + "type": "volume" + }, + "tank/home@old": { + "type": "snapshot", + "encryption": "off" + } + } +} diff --git a/tests/facts/zfs.Filesystems/filesystems.json b/tests/facts/zfs.Filesystems/filesystems.json new file mode 100644 index 000000000..b9034e6b5 --- /dev/null +++ b/tests/facts/zfs.Filesystems/filesystems.json @@ -0,0 +1,26 @@ +{ + "command": "zfs get -H all", + "output": [ + "tank\ttype\tfilesystem\t-", + "tank\tsize\t2.71T\t-", + "tank\tfeature@encryption\tenabled\tlocal", + "tank\tcom.sun:auto_snapshot\ttrue\t-", + "tank/home\ttype\tfilesystem\t-", + "tank/home\tcom.sun:auto_snapshot\ttrue\tinherited from tank", + "tank/home@old\ttype\tsnapshot\t-", + "tank/home@old\tencryption\toff\tdefault", + "tank/myvol\ttype\tvolume\t-" + ], + "fact": { + "tank": { + "type": "filesystem", + "size": "2.71T", + "feature@encryption": "enabled", + "com.sun:auto_snapshot": "true" + }, + "tank/home": { + "type": "filesystem", + "com.sun:auto_snapshot": "true" + } + } +} diff --git a/tests/facts/zfs.Pools/pools.json b/tests/facts/zfs.Pools/pools.json new file mode 100644 index 000000000..0f88a47cb --- /dev/null +++ b/tests/facts/zfs.Pools/pools.json @@ -0,0 +1,13 @@ +{ + "command": "zpool get -H all", + "output": [ + "tank\tcompression\tlz4\t-", + "tank\tashift\t12\tlocal" + ], + "fact": { + "tank": { + "compression": "lz4", + "ashift": "12" + } + } +} diff --git a/tests/facts/zfs.Snapshots/snapshots.json b/tests/facts/zfs.Snapshots/snapshots.json new file mode 100644 index 000000000..d20beba7c --- /dev/null +++ b/tests/facts/zfs.Snapshots/snapshots.json @@ -0,0 +1,20 @@ +{ + "command": "zfs get -H all", + "output": [ + "tank\ttype\tfilesystem\t-", + "tank\tsize\t2.71T\t-", + "tank\tfeature@encryption\tenabled\tlocal", + "tank\tcom.sun:auto_snapshot\ttrue\t-", + "tank/home\ttype\tfilesystem\t-", + "tank/home\tcom.sun:auto_snapshot\ttrue\tinherited from tank", + "tank/home@old\ttype\tsnapshot\t-", + "tank/home@old\tencryption\toff\tdefault", + "tank/myvol\ttype\tvolume\t-" + ], + "fact": { + "tank/home@old": { + "type": "snapshot", + "encryption": "off" + } + } +} diff --git a/tests/facts/zfs.Volumes/volumes.json b/tests/facts/zfs.Volumes/volumes.json new file mode 100644 index 000000000..eca7f7716 --- /dev/null +++ b/tests/facts/zfs.Volumes/volumes.json @@ -0,0 +1,19 @@ +{ + "command": "zfs get -H all", + "output": [ + "tank\ttype\tfilesystem\t-", + "tank\tsize\t2.71T\t-", + "tank\tfeature@encryption\tenabled\tlocal", + "tank\tcom.sun:auto_snapshot\ttrue\t-", + "tank/home\ttype\tfilesystem\t-", + "tank/home\tcom.sun:auto_snapshot\ttrue\tinherited from tank", + "tank/home@old\ttype\tsnapshot\t-", + "tank/home@old\tencryption\toff\tdefault", + "tank/myvol\ttype\tvolume\t-" + ], + "fact": { + "tank/myvol": { + "type": "volume" + } + } +} diff --git a/tests/operations/apt.deb/add.json b/tests/operations/apt.deb/add.json index fa3c4c3c8..48e0d5682 100644 --- a/tests/operations/apt.deb/add.json +++ b/tests/operations/apt.deb/add.json @@ -6,7 +6,7 @@ "path=_tempfile_": "" }, "deb.DebPackage": { - "name=_tempfile_": { + "package=_tempfile_": { "name": "test", "version": 0 } diff --git a/tests/operations/apt.deb/download_add.json b/tests/operations/apt.deb/download_add.json index b2f25b9fa..e64d0a6b0 100644 --- a/tests/operations/apt.deb/download_add.json +++ b/tests/operations/apt.deb/download_add.json @@ -6,7 +6,7 @@ "path=_tempfile_": null }, "deb.DebPackage": { - "name=_tempfile_": null + "package=_tempfile_": null }, "deb.DebPackages": {}, "server.Which": { diff --git a/tests/operations/apt.deb/remove_no_exist.json b/tests/operations/apt.deb/remove_no_exist.json index 9940cabac..29c96ffcb 100644 --- a/tests/operations/apt.deb/remove_no_exist.json +++ b/tests/operations/apt.deb/remove_no_exist.json @@ -9,7 +9,7 @@ "path=_tempfile_": "" }, "deb.DebPackage": { - "name=_tempfile_": { + "package=_tempfile_": { "name": "test", "version": 0 } diff --git a/tests/operations/apt.dist_upgrade/dist_upgrade.json b/tests/operations/apt.dist_upgrade/dist_upgrade.json index 8f6337836..d6eb50229 100644 --- a/tests/operations/apt.dist_upgrade/dist_upgrade.json +++ b/tests/operations/apt.dist_upgrade/dist_upgrade.json @@ -3,5 +3,9 @@ "commands": [ "DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" dist-upgrade" ], - "idempotent": false + "facts": { + "apt.SimulateOperationWillChange": { + "command=dist-upgrade": {} + } + } } diff --git a/tests/operations/apt.packages/update_upgrade.json b/tests/operations/apt.packages/update_upgrade.json index 102da98db..210017ac7 100644 --- a/tests/operations/apt.packages/update_upgrade.json +++ b/tests/operations/apt.packages/update_upgrade.json @@ -4,12 +4,13 @@ "upgrade": true }, "facts": { - "deb.DebPackages": {} + "deb.DebPackages": {}, + "apt.SimulateOperationWillChange": { + "command=upgrade": {} + } }, "commands": [ "apt-get update", "DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade" - ], - "idempotent": false, - "disable_idempotent_warning_reason": "package upgrades are always executed" + ] } diff --git a/tests/operations/apt.repo/add.json b/tests/operations/apt.repo/add.json index 75baccafc..d201f2e79 100644 --- a/tests/operations/apt.repo/add.json +++ b/tests/operations/apt.repo/add.json @@ -3,7 +3,7 @@ "facts": { "apt.AptSources": [], "files.FindInFile": { - "path=/etc/apt/sources.list, pattern=^.*deb http://archive\\.canonical\\.com/ubuntu hardy partner.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=/etc/apt/sources.list, pattern=^.*deb http://archive\\.canonical\\.com/ubuntu hardy partner.*$": [] } }, "commands": [ diff --git a/tests/operations/apt.repo/add_existing.json b/tests/operations/apt.repo/add_existing.json index 1d6737082..77f131c55 100644 --- a/tests/operations/apt.repo/add_existing.json +++ b/tests/operations/apt.repo/add_existing.json @@ -5,7 +5,7 @@ "type": "deb", "url": "http://archive.canonical.com/ubuntu", "distribution": "hardy", - "components": ["set:", "partner"], + "components": ["partner"], "options": {} }] }, diff --git a/tests/operations/apt.repo/add_filename.json b/tests/operations/apt.repo/add_filename.json index a25472c12..206ca7b22 100644 --- a/tests/operations/apt.repo/add_filename.json +++ b/tests/operations/apt.repo/add_filename.json @@ -6,7 +6,7 @@ "facts": { "apt.AptSources": [], "files.FindInFile": { - "path=/etc/apt/sources.list.d/somefile.list, pattern=^.*deb http://archive\\.canonical\\.com/ubuntu hardy partner.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=/etc/apt/sources.list.d/somefile.list, pattern=^.*deb http://archive\\.canonical\\.com/ubuntu hardy partner.*$": [] } }, "commands": [ diff --git a/tests/operations/apt.repo/remove.json b/tests/operations/apt.repo/remove.json index 1bd98b33e..0bc0e5d88 100644 --- a/tests/operations/apt.repo/remove.json +++ b/tests/operations/apt.repo/remove.json @@ -8,7 +8,7 @@ "type": "deb", "url": "http://archive.canonical.com/ubuntu", "distribution": "hardy", - "components": ["set:", "partner"], + "components": ["partner"], "options": {} }], "files.FindInFile": { diff --git a/tests/operations/apt.upgrade/upgrade.json b/tests/operations/apt.upgrade/upgrade.json index 4ff2527de..01f1a122b 100644 --- a/tests/operations/apt.upgrade/upgrade.json +++ b/tests/operations/apt.upgrade/upgrade.json @@ -1,5 +1,15 @@ { "args": [], + "facts": { + "apt.SimulateOperationWillChange": { + "command=upgrade": { + "upgraded": 15, + "newly_installed": 0, + "removed": 0, + "not_upgraded": 0 + } + } + }, "commands": [ "DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade" ], diff --git a/tests/operations/apt.upgrade/upgrade_autoremove.json b/tests/operations/apt.upgrade/upgrade_autoremove.json index 01a39c5dd..74c6f9175 100644 --- a/tests/operations/apt.upgrade/upgrade_autoremove.json +++ b/tests/operations/apt.upgrade/upgrade_autoremove.json @@ -3,5 +3,9 @@ "commands": [ "DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade --autoremove" ], - "idempotent": false + "facts": { + "apt.SimulateOperationWillChange": { + "command=upgrade --autoremove": {} + } + } } diff --git a/tests/operations/bsdinit.service/disabled.json b/tests/operations/bsdinit.service/disabled.json index eec109982..bc99ebd52 100644 --- a/tests/operations/bsdinit.service/disabled.json +++ b/tests/operations/bsdinit.service/disabled.json @@ -9,7 +9,7 @@ "nginx": true }, "files.FindInFile": { - "path=/etc/rc.conf.local, pattern=^nginx_enable=.*$, interpolate_variables=False": [ + "interpolate_variables=False, path=/etc/rc.conf.local, pattern=^nginx_enable=.*$": [ "nginx_enable=\"YES\"" ] } diff --git a/tests/operations/bsdinit.service/enabled.json b/tests/operations/bsdinit.service/enabled.json index f21eeea9a..43d2d836a 100644 --- a/tests/operations/bsdinit.service/enabled.json +++ b/tests/operations/bsdinit.service/enabled.json @@ -9,8 +9,8 @@ "nginx": true }, "files.FindInFile": { - "path=/etc/rc.conf.local, pattern=^nginx_enable=.*$, interpolate_variables=False": [], - "path=/etc/rc.conf.local, pattern=^.*nginx_enable=\"YES\".*$, interpolate_variables=False": [] + "interpolate_variables=False, path=/etc/rc.conf.local, pattern=^nginx_enable=.*$": [], + "interpolate_variables=False, path=/etc/rc.conf.local, pattern=^nginx_enable=\"YES\"$": [] } }, "commands": [ diff --git a/tests/operations/dnf.repo/add.json b/tests/operations/dnf.repo/add.json index f17e23eeb..e4c932e9f 100644 --- a/tests/operations/dnf.repo/add.json +++ b/tests/operations/dnf.repo/add.json @@ -11,7 +11,8 @@ "path=/etc/yum.repos.d/somerepo.repo": {} }, "files.Directory": { - "path=/etc/yum.repos.d": true + "path=/etc/yum.repos.d": true, + "path=/etc/yum.repos.d/somerepo.repo": null } }, "commands": [[ diff --git a/tests/operations/docker.container/add_and_start_no_existent_container.json b/tests/operations/docker.container/add_and_start_no_existent_container.json index d6c67cddc..e16b515e2 100644 --- a/tests/operations/docker.container/add_and_start_no_existent_container.json +++ b/tests/operations/docker.container/add_and_start_no_existent_container.json @@ -9,7 +9,9 @@ "start": true }, "facts": { - "docker.DockerContainers": [] + "docker.DockerContainer": { + "object_id=nginx": [] + } }, "commands": [ "docker container create --name nginx -p 80:80 nginx:alpine ; docker container start nginx" diff --git a/tests/operations/docker.container/add_existent_container.json b/tests/operations/docker.container/add_existent_container.json index 31810d8a7..4473baebc 100644 --- a/tests/operations/docker.container/add_existent_container.json +++ b/tests/operations/docker.container/add_existent_container.json @@ -9,234 +9,239 @@ "force": true }, "facts": { - "docker.DockerContainers": [ - { - "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", - "Created": "2024-05-26T22: 01: 24.10525839Z", - "Path": "/docker-entrypoint.sh", - "Args": [ - "nginx", - "-g", - "daemon off;" - ], - "State": { - "Status": "running", - "Running": "True", - "Paused": "False", - "Restarting": "False", - "OOMKilled": "False", - "Dead": "False", - "Pid": 8407, - "ExitCode": 0, - "Error": "", - "StartedAt": "2024-05-26T22: 01: 24.502384646Z", - "FinishedAt": "0001-01-01T00: 00: 00Z" - }, - "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", - "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", - "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", - "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", - "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", - "Name": "/nginx", - "RestartCount": 0, - "Driver": "overlay2", - "Platform": "linux", - "MountLabel": "", - "ProcessLabel": "", - "AppArmorProfile": "", - "ExecIDs": "None", - "HostConfig": { - "Binds": "None", - "ContainerIDFile": "", - "LogConfig": { - "Type": "json-file", - "Config": {} - }, - "NetworkMode": "bridge", - "PortBindings": { - "80/tcp": [ - { - "HostIp": "", - "HostPort": "80" - } - ] - }, - "RestartPolicy": { - "Name": "no", - "MaximumRetryCount": 0 - }, - "AutoRemove": "False", - "VolumeDriver": "", - "VolumesFrom": "None", - "ConsoleSize": [ - 0, - 0 - ], - "CapAdd": "None", - "CapDrop": "None", - "CgroupnsMode": "private", - "Dns": [], - "DnsOptions": [], - "DnsSearch": [], - "ExtraHosts": "None", - "GroupAdd": "None", - "IpcMode": "private", - "Cgroup": "", - "Links": "None", - "OomScoreAdj": 0, - "PidMode": "", - "Privileged": "False", - "PublishAllPorts": "False", - "ReadonlyRootfs": "False", - "SecurityOpt": "None", - "UTSMode": "", - "UsernsMode": "", - "ShmSize": 67108864, - "Runtime": "runc", - "Isolation": "", - "CpuShares": 0, - "Memory": 0, - "NanoCpus": 0, - "CgroupParent": "", - "BlkioWeight": 0, - "BlkioWeightDevice": [], - "BlkioDeviceReadBps": [], - "BlkioDeviceWriteBps": [], - "BlkioDeviceReadIOps": [], - "BlkioDeviceWriteIOps": [], - "CpuPeriod": 0, - "CpuQuota": 0, - "CpuRealtimePeriod": 0, - "CpuRealtimeRuntime": 0, - "CpusetCpus": "", - "CpusetMems": "", - "Devices": [], - "DeviceCgroupRules": "None", - "DeviceRequests": "None", - "MemoryReservation": 0, - "MemorySwap": 0, - "MemorySwappiness": "None", - "OomKillDisable": "None", - "PidsLimit": "None", - "Ulimits": [], - "CpuCount": 0, - "CpuPercent": 0, - "IOMaximumIOps": 0, - "IOMaximumBandwidth": 0, - "MaskedPaths": [ - "/proc/asound", - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - "/sys/devices/virtual/powercap" - ], - "ReadonlyPaths": [ - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger" - ] - }, - "GraphDriver": { - "Data": { - "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", - "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", - "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", - "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" - }, - "Name": "overlay2" - }, - "Mounts": [], - "Config": { - "Hostname": "9bb5a79e7c4d", - "Domainname": "", - "User": "", - "AttachStdin": "False", - "AttachStdout": "True", - "AttachStderr": "True", - "ExposedPorts": { - "80/tcp": {} - }, - "Tty": "False", - "OpenStdin": "False", - "StdinOnce": "False", - "Env": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "NGINX_VERSION=1.25.5", - "NJS_VERSION=0.8.4", - "NJS_RELEASE=3~bookworm", - "PKG_RELEASE=1~bookworm" - ], - "Cmd": [ - "nginx", - "-g", - "daemon off;" - ], - "Image": "nginx", - "Volumes": "None", - "WorkingDir": "", - "Entrypoint": [ - "/docker-entrypoint.sh" - ], - "OnBuild": "None", - "Labels": { - "maintainer": "NGINX Docker Maintainers " - }, - "StopSignal": "SIGQUIT" - }, - "NetworkSettings": { - "Bridge": "", - "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", - "SandboxKey": "/var/run/docker/netns/30102172778c", - "Ports": { - "80/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "80" - } - ] - }, - "HairpinMode": "False", - "LinkLocalIPv6Address": "", - "LinkLocalIPv6PrefixLen": 0, - "SecondaryIPAddresses": "None", - "SecondaryIPv6Addresses": "None", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "MacAddress": "02: 42:ac: 11: 00: 02", - "Networks": { - "bridge": { - "IPAMConfig": "None", - "Links": "None", - "Aliases": "None", - "MacAddress": "02: 42:ac: 11: 00: 02", - "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "DriverOpts": "None", - "DNSNames": "None" + "docker.DockerContainer": + { + "object_id=nginx": [ + + { + "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", + "Created": "2024-05-26T22: 01: 24.10525839Z", + "Path": "/docker-entrypoint.sh", + "Args": [ + "nginx", + "-g", + "daemon off;" + ], + "State": { + "Status": "running", + "Running": "True", + "Paused": "False", + "Restarting": "False", + "OOMKilled": "False", + "Dead": "False", + "Pid": 8407, + "ExitCode": 0, + "Error": "", + "StartedAt": "2024-05-26T22: 01: 24.502384646Z", + "FinishedAt": "0001-01-01T00: 00: 00Z" + }, + "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", + "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", + "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", + "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", + "Name": "/nginx", + "RestartCount": 0, + "Driver": "overlay2", + "Platform": "linux", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "ExecIDs": "None", + "HostConfig": { + "Binds": "None", + "ContainerIDFile": "", + "LogConfig": { + "Type": "json-file", + "Config": {} + }, + "NetworkMode": "bridge", + "PortBindings": { + "80/tcp": [ + { + "HostIp": "", + "HostPort": "80" + } + ] + }, + "RestartPolicy": { + "Name": "no", + "MaximumRetryCount": 0 + }, + "AutoRemove": "False", + "VolumeDriver": "", + "VolumesFrom": "None", + "ConsoleSize": [ + 0, + 0 + ], + "CapAdd": "None", + "CapDrop": "None", + "CgroupnsMode": "private", + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": "None", + "GroupAdd": "None", + "IpcMode": "private", + "Cgroup": "", + "Links": "None", + "OomScoreAdj": 0, + "PidMode": "", + "Privileged": "False", + "PublishAllPorts": "False", + "ReadonlyRootfs": "False", + "SecurityOpt": "None", + "UTSMode": "", + "UsernsMode": "", + "ShmSize": 67108864, + "Runtime": "runc", + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": [], + "BlkioDeviceReadBps": [], + "BlkioDeviceWriteBps": [], + "BlkioDeviceReadIOps": [], + "BlkioDeviceWriteIOps": [], + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DeviceCgroupRules": "None", + "DeviceRequests": "None", + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": "None", + "OomKillDisable": "None", + "PidsLimit": "None", + "Ulimits": [], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "MaskedPaths": [ + "/proc/asound", + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi", + "/sys/firmware", + "/sys/devices/virtual/powercap" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "GraphDriver": { + "Data": { + "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", + "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", + "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", + "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" + }, + "Name": "overlay2" + }, + "Mounts": [], + "Config": { + "Hostname": "9bb5a79e7c4d", + "Domainname": "", + "User": "", + "AttachStdin": "False", + "AttachStdout": "True", + "AttachStderr": "True", + "ExposedPorts": { + "80/tcp": {} + }, + "Tty": "False", + "OpenStdin": "False", + "StdinOnce": "False", + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "NGINX_VERSION=1.25.5", + "NJS_VERSION=0.8.4", + "NJS_RELEASE=3~bookworm", + "PKG_RELEASE=1~bookworm" + ], + "Cmd": [ + "nginx", + "-g", + "daemon off;" + ], + "Image": "nginx", + "Volumes": "None", + "WorkingDir": "", + "Entrypoint": [ + "/docker-entrypoint.sh" + ], + "OnBuild": "None", + "Labels": { + "maintainer": "NGINX Docker Maintainers " + }, + "StopSignal": "SIGQUIT" + }, + "NetworkSettings": { + "Bridge": "", + "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", + "SandboxKey": "/var/run/docker/netns/30102172778c", + "Ports": { + "80/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "80" + } + ] + }, + "HairpinMode": "False", + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "SecondaryIPAddresses": "None", + "SecondaryIPv6Addresses": "None", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02: 42:ac: 11: 00: 02", + "Networks": { + "bridge": { + "IPAMConfig": "None", + "Links": "None", + "Aliases": "None", + "MacAddress": "02: 42:ac: 11: 00: 02", + "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "DriverOpts": "None", + "DNSNames": "None" + } + } + } + } - } - } + ] } - ] }, "commands": [ "docker container rm -f nginx", diff --git a/tests/operations/docker.container/add_no_existent_container.json b/tests/operations/docker.container/add_no_existent_container.json index 990beace3..fdaa8ffba 100644 --- a/tests/operations/docker.container/add_no_existent_container.json +++ b/tests/operations/docker.container/add_no_existent_container.json @@ -9,7 +9,9 @@ "start": false }, "facts": { - "docker.DockerContainers": [] + "docker.DockerContainer": { + "object_id=nginx": [] + } }, "commands": [ "docker container create --name nginx -p 80:80 nginx:alpine" diff --git a/tests/operations/docker.container/remove_container.json b/tests/operations/docker.container/remove_container.json index a7e5fb739..6f753fe83 100644 --- a/tests/operations/docker.container/remove_container.json +++ b/tests/operations/docker.container/remove_container.json @@ -8,234 +8,239 @@ "present": false }, "facts": { - "docker.DockerContainers": [ - { - "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", - "Created": "2024-05-26T22: 01: 24.10525839Z", - "Path": "/docker-entrypoint.sh", - "Args": [ - "nginx", - "-g", - "daemon off;" - ], - "State": { - "Status": "running", - "Running": "True", - "Paused": "False", - "Restarting": "False", - "OOMKilled": "False", - "Dead": "False", - "Pid": 8407, - "ExitCode": 0, - "Error": "", - "StartedAt": "2024-05-26T22: 01: 24.502384646Z", - "FinishedAt": "0001-01-01T00: 00: 00Z" - }, - "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", - "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", - "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", - "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", - "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", - "Name": "/nginx", - "RestartCount": 0, - "Driver": "overlay2", - "Platform": "linux", - "MountLabel": "", - "ProcessLabel": "", - "AppArmorProfile": "", - "ExecIDs": "None", - "HostConfig": { - "Binds": "None", - "ContainerIDFile": "", - "LogConfig": { - "Type": "json-file", - "Config": {} - }, - "NetworkMode": "bridge", - "PortBindings": { - "80/tcp": [ - { - "HostIp": "", - "HostPort": "80" - } - ] - }, - "RestartPolicy": { - "Name": "no", - "MaximumRetryCount": 0 - }, - "AutoRemove": "False", - "VolumeDriver": "", - "VolumesFrom": "None", - "ConsoleSize": [ - 0, - 0 - ], - "CapAdd": "None", - "CapDrop": "None", - "CgroupnsMode": "private", - "Dns": [], - "DnsOptions": [], - "DnsSearch": [], - "ExtraHosts": "None", - "GroupAdd": "None", - "IpcMode": "private", - "Cgroup": "", - "Links": "None", - "OomScoreAdj": 0, - "PidMode": "", - "Privileged": "False", - "PublishAllPorts": "False", - "ReadonlyRootfs": "False", - "SecurityOpt": "None", - "UTSMode": "", - "UsernsMode": "", - "ShmSize": 67108864, - "Runtime": "runc", - "Isolation": "", - "CpuShares": 0, - "Memory": 0, - "NanoCpus": 0, - "CgroupParent": "", - "BlkioWeight": 0, - "BlkioWeightDevice": [], - "BlkioDeviceReadBps": [], - "BlkioDeviceWriteBps": [], - "BlkioDeviceReadIOps": [], - "BlkioDeviceWriteIOps": [], - "CpuPeriod": 0, - "CpuQuota": 0, - "CpuRealtimePeriod": 0, - "CpuRealtimeRuntime": 0, - "CpusetCpus": "", - "CpusetMems": "", - "Devices": [], - "DeviceCgroupRules": "None", - "DeviceRequests": "None", - "MemoryReservation": 0, - "MemorySwap": 0, - "MemorySwappiness": "None", - "OomKillDisable": "None", - "PidsLimit": "None", - "Ulimits": [], - "CpuCount": 0, - "CpuPercent": 0, - "IOMaximumIOps": 0, - "IOMaximumBandwidth": 0, - "MaskedPaths": [ - "/proc/asound", - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - "/sys/devices/virtual/powercap" - ], - "ReadonlyPaths": [ - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger" - ] - }, - "GraphDriver": { - "Data": { - "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", - "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", - "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", - "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" - }, - "Name": "overlay2" - }, - "Mounts": [], - "Config": { - "Hostname": "9bb5a79e7c4d", - "Domainname": "", - "User": "", - "AttachStdin": "False", - "AttachStdout": "True", - "AttachStderr": "True", - "ExposedPorts": { - "80/tcp": {} - }, - "Tty": "False", - "OpenStdin": "False", - "StdinOnce": "False", - "Env": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "NGINX_VERSION=1.25.5", - "NJS_VERSION=0.8.4", - "NJS_RELEASE=3~bookworm", - "PKG_RELEASE=1~bookworm" - ], - "Cmd": [ - "nginx", - "-g", - "daemon off;" - ], - "Image": "nginx", - "Volumes": "None", - "WorkingDir": "", - "Entrypoint": [ - "/docker-entrypoint.sh" - ], - "OnBuild": "None", - "Labels": { - "maintainer": "NGINX Docker Maintainers " - }, - "StopSignal": "SIGQUIT" - }, - "NetworkSettings": { - "Bridge": "", - "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", - "SandboxKey": "/var/run/docker/netns/30102172778c", - "Ports": { - "80/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "80" - } - ] - }, - "HairpinMode": "False", - "LinkLocalIPv6Address": "", - "LinkLocalIPv6PrefixLen": 0, - "SecondaryIPAddresses": "None", - "SecondaryIPv6Addresses": "None", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "MacAddress": "02: 42:ac: 11: 00: 02", - "Networks": { - "bridge": { - "IPAMConfig": "None", - "Links": "None", - "Aliases": "None", - "MacAddress": "02: 42:ac: 11: 00: 02", - "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "DriverOpts": "None", - "DNSNames": "None" + "docker.DockerContainer": + { + "object_id=nginx": [ + + { + "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", + "Created": "2024-05-26T22: 01: 24.10525839Z", + "Path": "/docker-entrypoint.sh", + "Args": [ + "nginx", + "-g", + "daemon off;" + ], + "State": { + "Status": "running", + "Running": "True", + "Paused": "False", + "Restarting": "False", + "OOMKilled": "False", + "Dead": "False", + "Pid": 8407, + "ExitCode": 0, + "Error": "", + "StartedAt": "2024-05-26T22: 01: 24.502384646Z", + "FinishedAt": "0001-01-01T00: 00: 00Z" + }, + "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", + "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", + "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", + "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", + "Name": "/nginx", + "RestartCount": 0, + "Driver": "overlay2", + "Platform": "linux", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "ExecIDs": "None", + "HostConfig": { + "Binds": "None", + "ContainerIDFile": "", + "LogConfig": { + "Type": "json-file", + "Config": {} + }, + "NetworkMode": "bridge", + "PortBindings": { + "80/tcp": [ + { + "HostIp": "", + "HostPort": "80" + } + ] + }, + "RestartPolicy": { + "Name": "no", + "MaximumRetryCount": 0 + }, + "AutoRemove": "False", + "VolumeDriver": "", + "VolumesFrom": "None", + "ConsoleSize": [ + 0, + 0 + ], + "CapAdd": "None", + "CapDrop": "None", + "CgroupnsMode": "private", + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": "None", + "GroupAdd": "None", + "IpcMode": "private", + "Cgroup": "", + "Links": "None", + "OomScoreAdj": 0, + "PidMode": "", + "Privileged": "False", + "PublishAllPorts": "False", + "ReadonlyRootfs": "False", + "SecurityOpt": "None", + "UTSMode": "", + "UsernsMode": "", + "ShmSize": 67108864, + "Runtime": "runc", + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": [], + "BlkioDeviceReadBps": [], + "BlkioDeviceWriteBps": [], + "BlkioDeviceReadIOps": [], + "BlkioDeviceWriteIOps": [], + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DeviceCgroupRules": "None", + "DeviceRequests": "None", + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": "None", + "OomKillDisable": "None", + "PidsLimit": "None", + "Ulimits": [], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "MaskedPaths": [ + "/proc/asound", + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi", + "/sys/firmware", + "/sys/devices/virtual/powercap" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "GraphDriver": { + "Data": { + "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", + "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", + "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", + "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" + }, + "Name": "overlay2" + }, + "Mounts": [], + "Config": { + "Hostname": "9bb5a79e7c4d", + "Domainname": "", + "User": "", + "AttachStdin": "False", + "AttachStdout": "True", + "AttachStderr": "True", + "ExposedPorts": { + "80/tcp": {} + }, + "Tty": "False", + "OpenStdin": "False", + "StdinOnce": "False", + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "NGINX_VERSION=1.25.5", + "NJS_VERSION=0.8.4", + "NJS_RELEASE=3~bookworm", + "PKG_RELEASE=1~bookworm" + ], + "Cmd": [ + "nginx", + "-g", + "daemon off;" + ], + "Image": "nginx", + "Volumes": "None", + "WorkingDir": "", + "Entrypoint": [ + "/docker-entrypoint.sh" + ], + "OnBuild": "None", + "Labels": { + "maintainer": "NGINX Docker Maintainers " + }, + "StopSignal": "SIGQUIT" + }, + "NetworkSettings": { + "Bridge": "", + "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", + "SandboxKey": "/var/run/docker/netns/30102172778c", + "Ports": { + "80/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "80" + } + ] + }, + "HairpinMode": "False", + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "SecondaryIPAddresses": "None", + "SecondaryIPv6Addresses": "None", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02: 42:ac: 11: 00: 02", + "Networks": { + "bridge": { + "IPAMConfig": "None", + "Links": "None", + "Aliases": "None", + "MacAddress": "02: 42:ac: 11: 00: 02", + "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "DriverOpts": "None", + "DNSNames": "None" + } + } + } + } - } - } + ] } - ] }, "commands": [ "docker container rm -f nginx" diff --git a/tests/operations/docker.container/start_container.json b/tests/operations/docker.container/start_container.json index 46b4587cd..f16a01634 100644 --- a/tests/operations/docker.container/start_container.json +++ b/tests/operations/docker.container/start_container.json @@ -8,234 +8,237 @@ "start": true }, "facts": { - "docker.DockerContainers": [ - { - "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", - "Created": "2024-05-26T22: 01: 24.10525839Z", - "Path": "/docker-entrypoint.sh", - "Args": [ - "nginx", - "-g", - "daemon off;" - ], - "State": { - "Status": "exited", - "Running": "True", - "Paused": "False", - "Restarting": "False", - "OOMKilled": "False", - "Dead": "False", - "Pid": 8407, - "ExitCode": 0, - "Error": "", - "StartedAt": "2024-05-26T22: 01: 24.502384646Z", - "FinishedAt": "0001-01-01T00: 00: 00Z" - }, - "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", - "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", - "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", - "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", - "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", - "Name": "/nginx", - "RestartCount": 0, - "Driver": "overlay2", - "Platform": "linux", - "MountLabel": "", - "ProcessLabel": "", - "AppArmorProfile": "", - "ExecIDs": "None", - "HostConfig": { - "Binds": "None", - "ContainerIDFile": "", - "LogConfig": { - "Type": "json-file", - "Config": {} - }, - "NetworkMode": "bridge", - "PortBindings": { - "80/tcp": [ - { - "HostIp": "", - "HostPort": "80" - } - ] - }, - "RestartPolicy": { - "Name": "no", - "MaximumRetryCount": 0 - }, - "AutoRemove": "False", - "VolumeDriver": "", - "VolumesFrom": "None", - "ConsoleSize": [ - 0, - 0 - ], - "CapAdd": "None", - "CapDrop": "None", - "CgroupnsMode": "private", - "Dns": [], - "DnsOptions": [], - "DnsSearch": [], - "ExtraHosts": "None", - "GroupAdd": "None", - "IpcMode": "private", - "Cgroup": "", - "Links": "None", - "OomScoreAdj": 0, - "PidMode": "", - "Privileged": "False", - "PublishAllPorts": "False", - "ReadonlyRootfs": "False", - "SecurityOpt": "None", - "UTSMode": "", - "UsernsMode": "", - "ShmSize": 67108864, - "Runtime": "runc", - "Isolation": "", - "CpuShares": 0, - "Memory": 0, - "NanoCpus": 0, - "CgroupParent": "", - "BlkioWeight": 0, - "BlkioWeightDevice": [], - "BlkioDeviceReadBps": [], - "BlkioDeviceWriteBps": [], - "BlkioDeviceReadIOps": [], - "BlkioDeviceWriteIOps": [], - "CpuPeriod": 0, - "CpuQuota": 0, - "CpuRealtimePeriod": 0, - "CpuRealtimeRuntime": 0, - "CpusetCpus": "", - "CpusetMems": "", - "Devices": [], - "DeviceCgroupRules": "None", - "DeviceRequests": "None", - "MemoryReservation": 0, - "MemorySwap": 0, - "MemorySwappiness": "None", - "OomKillDisable": "None", - "PidsLimit": "None", - "Ulimits": [], - "CpuCount": 0, - "CpuPercent": 0, - "IOMaximumIOps": 0, - "IOMaximumBandwidth": 0, - "MaskedPaths": [ - "/proc/asound", - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - "/sys/devices/virtual/powercap" - ], - "ReadonlyPaths": [ - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger" - ] - }, - "GraphDriver": { - "Data": { - "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", - "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", - "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", - "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" - }, - "Name": "overlay2" - }, - "Mounts": [], - "Config": { - "Hostname": "9bb5a79e7c4d", - "Domainname": "", - "User": "", - "AttachStdin": "False", - "AttachStdout": "True", - "AttachStderr": "True", - "ExposedPorts": { - "80/tcp": {} - }, - "Tty": "False", - "OpenStdin": "False", - "StdinOnce": "False", - "Env": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "NGINX_VERSION=1.25.5", - "NJS_VERSION=0.8.4", - "NJS_RELEASE=3~bookworm", - "PKG_RELEASE=1~bookworm" - ], - "Cmd": [ + "docker.DockerContainer": { + + "object_id=nginx" :[ + { + "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", + "Created": "2024-05-26T22: 01: 24.10525839Z", + "Path": "/docker-entrypoint.sh", + "Args": [ "nginx", "-g", "daemon off;" ], - "Image": "nginx", - "Volumes": "None", - "WorkingDir": "", - "Entrypoint": [ - "/docker-entrypoint.sh" - ], - "OnBuild": "None", - "Labels": { - "maintainer": "NGINX Docker Maintainers " + "State": { + "Status": "exited", + "Running": "True", + "Paused": "False", + "Restarting": "False", + "OOMKilled": "False", + "Dead": "False", + "Pid": 8407, + "ExitCode": 0, + "Error": "", + "StartedAt": "2024-05-26T22: 01: 24.502384646Z", + "FinishedAt": "0001-01-01T00: 00: 00Z" }, - "StopSignal": "SIGQUIT" - }, - "NetworkSettings": { - "Bridge": "", - "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", - "SandboxKey": "/var/run/docker/netns/30102172778c", - "Ports": { - "80/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "80" - } + "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", + "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", + "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", + "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", + "Name": "/nginx", + "RestartCount": 0, + "Driver": "overlay2", + "Platform": "linux", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "ExecIDs": "None", + "HostConfig": { + "Binds": "None", + "ContainerIDFile": "", + "LogConfig": { + "Type": "json-file", + "Config": {} + }, + "NetworkMode": "bridge", + "PortBindings": { + "80/tcp": [ + { + "HostIp": "", + "HostPort": "80" + } + ] + }, + "RestartPolicy": { + "Name": "no", + "MaximumRetryCount": 0 + }, + "AutoRemove": "False", + "VolumeDriver": "", + "VolumesFrom": "None", + "ConsoleSize": [ + 0, + 0 + ], + "CapAdd": "None", + "CapDrop": "None", + "CgroupnsMode": "private", + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": "None", + "GroupAdd": "None", + "IpcMode": "private", + "Cgroup": "", + "Links": "None", + "OomScoreAdj": 0, + "PidMode": "", + "Privileged": "False", + "PublishAllPorts": "False", + "ReadonlyRootfs": "False", + "SecurityOpt": "None", + "UTSMode": "", + "UsernsMode": "", + "ShmSize": 67108864, + "Runtime": "runc", + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": [], + "BlkioDeviceReadBps": [], + "BlkioDeviceWriteBps": [], + "BlkioDeviceReadIOps": [], + "BlkioDeviceWriteIOps": [], + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DeviceCgroupRules": "None", + "DeviceRequests": "None", + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": "None", + "OomKillDisable": "None", + "PidsLimit": "None", + "Ulimits": [], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "MaskedPaths": [ + "/proc/asound", + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi", + "/sys/firmware", + "/sys/devices/virtual/powercap" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" ] }, - "HairpinMode": "False", - "LinkLocalIPv6Address": "", - "LinkLocalIPv6PrefixLen": 0, - "SecondaryIPAddresses": "None", - "SecondaryIPv6Addresses": "None", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "MacAddress": "02: 42:ac: 11: 00: 02", - "Networks": { - "bridge": { - "IPAMConfig": "None", - "Links": "None", - "Aliases": "None", - "MacAddress": "02: 42:ac: 11: 00: 02", - "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "DriverOpts": "None", - "DNSNames": "None" + "GraphDriver": { + "Data": { + "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", + "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", + "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", + "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" + }, + "Name": "overlay2" + }, + "Mounts": [], + "Config": { + "Hostname": "9bb5a79e7c4d", + "Domainname": "", + "User": "", + "AttachStdin": "False", + "AttachStdout": "True", + "AttachStderr": "True", + "ExposedPorts": { + "80/tcp": {} + }, + "Tty": "False", + "OpenStdin": "False", + "StdinOnce": "False", + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "NGINX_VERSION=1.25.5", + "NJS_VERSION=0.8.4", + "NJS_RELEASE=3~bookworm", + "PKG_RELEASE=1~bookworm" + ], + "Cmd": [ + "nginx", + "-g", + "daemon off;" + ], + "Image": "nginx", + "Volumes": "None", + "WorkingDir": "", + "Entrypoint": [ + "/docker-entrypoint.sh" + ], + "OnBuild": "None", + "Labels": { + "maintainer": "NGINX Docker Maintainers " + }, + "StopSignal": "SIGQUIT" + }, + "NetworkSettings": { + "Bridge": "", + "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", + "SandboxKey": "/var/run/docker/netns/30102172778c", + "Ports": { + "80/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "80" + } + ] + }, + "HairpinMode": "False", + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "SecondaryIPAddresses": "None", + "SecondaryIPv6Addresses": "None", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02: 42:ac: 11: 00: 02", + "Networks": { + "bridge": { + "IPAMConfig": "None", + "Links": "None", + "Aliases": "None", + "MacAddress": "02: 42:ac: 11: 00: 02", + "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "DriverOpts": "None", + "DNSNames": "None" + } } } } - } - ] + ] + } }, "commands": [ "docker container start nginx" diff --git a/tests/operations/docker.container/stop_container.json b/tests/operations/docker.container/stop_container.json index ec53c0091..af9c57b87 100644 --- a/tests/operations/docker.container/stop_container.json +++ b/tests/operations/docker.container/stop_container.json @@ -9,234 +9,238 @@ "start": false }, "facts": { - "docker.DockerContainers": [ - { - "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", - "Created": "2024-05-26T22: 01: 24.10525839Z", - "Path": "/docker-entrypoint.sh", - "Args": [ - "nginx", - "-g", - "daemon off;" - ], - "State": { - "Status": "running", - "Running": "True", - "Paused": "False", - "Restarting": "False", - "OOMKilled": "False", - "Dead": "False", - "Pid": 8407, - "ExitCode": 0, - "Error": "", - "StartedAt": "2024-05-26T22: 01: 24.502384646Z", - "FinishedAt": "0001-01-01T00: 00: 00Z" - }, - "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", - "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", - "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", - "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", - "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", - "Name": "/nginx", - "RestartCount": 0, - "Driver": "overlay2", - "Platform": "linux", - "MountLabel": "", - "ProcessLabel": "", - "AppArmorProfile": "", - "ExecIDs": "None", - "HostConfig": { - "Binds": "None", - "ContainerIDFile": "", - "LogConfig": { - "Type": "json-file", - "Config": {} - }, - "NetworkMode": "bridge", - "PortBindings": { - "80/tcp": [ - { - "HostIp": "", - "HostPort": "80" - } - ] - }, - "RestartPolicy": { - "Name": "no", - "MaximumRetryCount": 0 - }, - "AutoRemove": "False", - "VolumeDriver": "", - "VolumesFrom": "None", - "ConsoleSize": [ - 0, - 0 - ], - "CapAdd": "None", - "CapDrop": "None", - "CgroupnsMode": "private", - "Dns": [], - "DnsOptions": [], - "DnsSearch": [], - "ExtraHosts": "None", - "GroupAdd": "None", - "IpcMode": "private", - "Cgroup": "", - "Links": "None", - "OomScoreAdj": 0, - "PidMode": "", - "Privileged": "False", - "PublishAllPorts": "False", - "ReadonlyRootfs": "False", - "SecurityOpt": "None", - "UTSMode": "", - "UsernsMode": "", - "ShmSize": 67108864, - "Runtime": "runc", - "Isolation": "", - "CpuShares": 0, - "Memory": 0, - "NanoCpus": 0, - "CgroupParent": "", - "BlkioWeight": 0, - "BlkioWeightDevice": [], - "BlkioDeviceReadBps": [], - "BlkioDeviceWriteBps": [], - "BlkioDeviceReadIOps": [], - "BlkioDeviceWriteIOps": [], - "CpuPeriod": 0, - "CpuQuota": 0, - "CpuRealtimePeriod": 0, - "CpuRealtimeRuntime": 0, - "CpusetCpus": "", - "CpusetMems": "", - "Devices": [], - "DeviceCgroupRules": "None", - "DeviceRequests": "None", - "MemoryReservation": 0, - "MemorySwap": 0, - "MemorySwappiness": "None", - "OomKillDisable": "None", - "PidsLimit": "None", - "Ulimits": [], - "CpuCount": 0, - "CpuPercent": 0, - "IOMaximumIOps": 0, - "IOMaximumBandwidth": 0, - "MaskedPaths": [ - "/proc/asound", - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - "/sys/devices/virtual/powercap" - ], - "ReadonlyPaths": [ - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger" - ] - }, - "GraphDriver": { - "Data": { - "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", - "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", - "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", - "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" - }, - "Name": "overlay2" - }, - "Mounts": [], - "Config": { - "Hostname": "9bb5a79e7c4d", - "Domainname": "", - "User": "", - "AttachStdin": "False", - "AttachStdout": "True", - "AttachStderr": "True", - "ExposedPorts": { - "80/tcp": {} - }, - "Tty": "False", - "OpenStdin": "False", - "StdinOnce": "False", - "Env": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "NGINX_VERSION=1.25.5", - "NJS_VERSION=0.8.4", - "NJS_RELEASE=3~bookworm", - "PKG_RELEASE=1~bookworm" - ], - "Cmd": [ + "docker.DockerContainer": + { + "object_id=nginx": [ + { + "Id": "9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4", + "Created": "2024-05-26T22: 01: 24.10525839Z", + "Path": "/docker-entrypoint.sh", + "Args": [ "nginx", "-g", "daemon off;" ], - "Image": "nginx", - "Volumes": "None", - "WorkingDir": "", - "Entrypoint": [ - "/docker-entrypoint.sh" - ], - "OnBuild": "None", - "Labels": { - "maintainer": "NGINX Docker Maintainers " + "State": { + "Status": "running", + "Running": "True", + "Paused": "False", + "Restarting": "False", + "OOMKilled": "False", + "Dead": "False", + "Pid": 8407, + "ExitCode": 0, + "Error": "", + "StartedAt": "2024-05-26T22: 01: 24.502384646Z", + "FinishedAt": "0001-01-01T00: 00: 00Z" }, - "StopSignal": "SIGQUIT" - }, - "NetworkSettings": { - "Bridge": "", - "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", - "SandboxKey": "/var/run/docker/netns/30102172778c", - "Ports": { - "80/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "80" - } + "Image": "sha256:e784f4560448b14a66f55c26e1b4dad2c2877cc73d001b7cd0b18e24a700a070", + "ResolvConfPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hostname", + "HostsPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/hosts", + "LogPath": "/var/lib/docker/containers/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4/9bb5a79e7c4da9ad38e7183aa7c943ee41dcbbab70e4832fe29b2788ab88d4b4-json.log", + "Name": "/nginx", + "RestartCount": 0, + "Driver": "overlay2", + "Platform": "linux", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "ExecIDs": "None", + "HostConfig": { + "Binds": "None", + "ContainerIDFile": "", + "LogConfig": { + "Type": "json-file", + "Config": {} + }, + "NetworkMode": "bridge", + "PortBindings": { + "80/tcp": [ + { + "HostIp": "", + "HostPort": "80" + } + ] + }, + "RestartPolicy": { + "Name": "no", + "MaximumRetryCount": 0 + }, + "AutoRemove": "False", + "VolumeDriver": "", + "VolumesFrom": "None", + "ConsoleSize": [ + 0, + 0 + ], + "CapAdd": "None", + "CapDrop": "None", + "CgroupnsMode": "private", + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": "None", + "GroupAdd": "None", + "IpcMode": "private", + "Cgroup": "", + "Links": "None", + "OomScoreAdj": 0, + "PidMode": "", + "Privileged": "False", + "PublishAllPorts": "False", + "ReadonlyRootfs": "False", + "SecurityOpt": "None", + "UTSMode": "", + "UsernsMode": "", + "ShmSize": 67108864, + "Runtime": "runc", + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "", + "BlkioWeight": 0, + "BlkioWeightDevice": [], + "BlkioDeviceReadBps": [], + "BlkioDeviceWriteBps": [], + "BlkioDeviceReadIOps": [], + "BlkioDeviceWriteIOps": [], + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DeviceCgroupRules": "None", + "DeviceRequests": "None", + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": "None", + "OomKillDisable": "None", + "PidsLimit": "None", + "Ulimits": [], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "MaskedPaths": [ + "/proc/asound", + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi", + "/sys/firmware", + "/sys/devices/virtual/powercap" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" ] }, - "HairpinMode": "False", - "LinkLocalIPv6Address": "", - "LinkLocalIPv6PrefixLen": 0, - "SecondaryIPAddresses": "None", - "SecondaryIPv6Addresses": "None", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "MacAddress": "02: 42:ac: 11: 00: 02", - "Networks": { - "bridge": { - "IPAMConfig": "None", - "Links": "None", - "Aliases": "None", - "MacAddress": "02: 42:ac: 11: 00: 02", - "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", - "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", - "Gateway": "172.17.0.1", - "IPAddress": "172.17.0.2", - "IPPrefixLen": 16, - "IPv6Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "DriverOpts": "None", - "DNSNames": "None" + "GraphDriver": { + "Data": { + "LowerDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca-init/diff:/var/lib/docker/overlay2/21b7dde68322832abacbc35a1fdfe6a75bacb0ef4b55d5341b29c0be312d457e/diff:/var/lib/docker/overlay2/7b978a86e25dd74d51c81795b8af92c4ae3c0cd8d5d34602e70dfe3be604aebe/diff:/var/lib/docker/overlay2/483058275ebfbe7dbc3e5bb15f2d7c45ec3f61a935b2a098d82f16cf8ba5231b/diff:/var/lib/docker/overlay2/87438f28df76ef0175c7ccd134f23f70e83fd85870f693735d6757bf6cb88f98/diff:/var/lib/docker/overlay2/ac61c96de3b3fd3644979b81c98d1071e6a063bfd2dcfb9c73cae30e064efdb8/diff:/var/lib/docker/overlay2/d744f3a16ab1a2cfd7889959279e6cc693199e5c1099d48f3c0794f62b045cc7/diff:/var/lib/docker/overlay2/572ed1139aed78c5873bc6ca8f8172b10b9876062f9a385c653e17244d88e421/diff", + "MergedDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/merged", + "UpperDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/diff", + "WorkDir": "/var/lib/docker/overlay2/ee7c8aaf7d9d84d989ea529b2cb1b4aea63c378ef0527ce8562f6d4d7530e1ca/work" + }, + "Name": "overlay2" + }, + "Mounts": [], + "Config": { + "Hostname": "9bb5a79e7c4d", + "Domainname": "", + "User": "", + "AttachStdin": "False", + "AttachStdout": "True", + "AttachStderr": "True", + "ExposedPorts": { + "80/tcp": {} + }, + "Tty": "False", + "OpenStdin": "False", + "StdinOnce": "False", + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "NGINX_VERSION=1.25.5", + "NJS_VERSION=0.8.4", + "NJS_RELEASE=3~bookworm", + "PKG_RELEASE=1~bookworm" + ], + "Cmd": [ + "nginx", + "-g", + "daemon off;" + ], + "Image": "nginx", + "Volumes": "None", + "WorkingDir": "", + "Entrypoint": [ + "/docker-entrypoint.sh" + ], + "OnBuild": "None", + "Labels": { + "maintainer": "NGINX Docker Maintainers " + }, + "StopSignal": "SIGQUIT" + }, + "NetworkSettings": { + "Bridge": "", + "SandboxID": "30102172778c8c2268fa443d0c29ac898beaa07c6ca3d73a93751a21e0812f14", + "SandboxKey": "/var/run/docker/netns/30102172778c", + "Ports": { + "80/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "80" + } + ] + }, + "HairpinMode": "False", + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "SecondaryIPAddresses": "None", + "SecondaryIPv6Addresses": "None", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "MacAddress": "02: 42:ac: 11: 00: 02", + "Networks": { + "bridge": { + "IPAMConfig": "None", + "Links": "None", + "Aliases": "None", + "MacAddress": "02: 42:ac: 11: 00: 02", + "NetworkID": "9602c89bf5d2a3b55665598ec99ec803cd54e0df0c2a25a511d59ecc2dc047b5", + "EndpointID": "2e0e98fd4346d9ad61d78294bd97e7c27f512a88c402115163a0df73bf83cad4", + "Gateway": "172.17.0.1", + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "DriverOpts": "None", + "DNSNames": "None" + } } } } - } - ] + ] + } + }, "commands": [ "docker container stop nginx" diff --git a/tests/operations/docker.network/add_network.json b/tests/operations/docker.network/add_network.json index 3b90decd0..9257a0740 100644 --- a/tests/operations/docker.network/add_network.json +++ b/tests/operations/docker.network/add_network.json @@ -6,7 +6,9 @@ "present": true }, "facts": { - "docker.DockerNetworks": [] + "docker.DockerNetwork": { + "object_id=nginx": [] + } }, "commands": [ "docker network create nginx --ingress --attachable" diff --git a/tests/operations/docker.network/add_network_with_gateway.json b/tests/operations/docker.network/add_network_with_gateway.json index 7757ac61f..e408ef4c4 100644 --- a/tests/operations/docker.network/add_network_with_gateway.json +++ b/tests/operations/docker.network/add_network_with_gateway.json @@ -5,7 +5,9 @@ "present": true }, "facts": { - "docker.DockerNetworks": [] + "docker.DockerNetwork": { + "object_id=nginx": [] + } }, "commands": [ "docker network create nginx --gateway 192.168.0.1" diff --git a/tests/operations/docker.network/add_network_with_ip_range.json b/tests/operations/docker.network/add_network_with_ip_range.json index 6bf1ef2b9..b378decca 100644 --- a/tests/operations/docker.network/add_network_with_ip_range.json +++ b/tests/operations/docker.network/add_network_with_ip_range.json @@ -5,7 +5,9 @@ "present": true }, "facts": { - "docker.DockerNetworks": [] + "docker.DockerNetwork": { + "object_id=nginx": [] + } }, "commands": [ "docker network create nginx --ip-range 192.168.0.100/24" diff --git a/tests/operations/docker.network/add_network_with_opts.json b/tests/operations/docker.network/add_network_with_opts.json index 08d1ffb61..f9d1b2fe2 100644 --- a/tests/operations/docker.network/add_network_with_opts.json +++ b/tests/operations/docker.network/add_network_with_opts.json @@ -6,7 +6,9 @@ "present": true }, "facts": { - "docker.DockerNetworks": [] + "docker.DockerNetwork": { + "object_id=nginx": [] + } }, "commands": [ "docker network create nginx --opt opta=A --opt optB=B --ipam-opt ipam_opta=IPAM_A --ipam-opt imap_optB=IPAM_B" diff --git a/tests/operations/docker.volume/add_volume.json b/tests/operations/docker.volume/add_volume.json index 304512d39..716de264c 100644 --- a/tests/operations/docker.volume/add_volume.json +++ b/tests/operations/docker.volume/add_volume.json @@ -4,7 +4,9 @@ "present": true }, "facts": { - "docker.DockerVolumes": [] + "docker.DockerVolume": { + "object_id=nginx_volume": [] + } }, "commands": [ "docker volume create nginx_volume" diff --git a/tests/operations/docker.volume/add_volume_with_driver.json b/tests/operations/docker.volume/add_volume_with_driver.json index aafd50b32..51a3c42e6 100644 --- a/tests/operations/docker.volume/add_volume_with_driver.json +++ b/tests/operations/docker.volume/add_volume_with_driver.json @@ -5,7 +5,9 @@ "present": true }, "facts": { - "docker.DockerVolumes": [] + "docker.DockerVolume": { + "object_id=nginx_volume": [] + } }, "commands": [ "docker volume create nginx_volume -d ceph" diff --git a/tests/operations/docker.volume/add_volume_with_labels.json b/tests/operations/docker.volume/add_volume_with_labels.json index a4662a5e9..9995d0b63 100644 --- a/tests/operations/docker.volume/add_volume_with_labels.json +++ b/tests/operations/docker.volume/add_volume_with_labels.json @@ -5,7 +5,9 @@ "present": true }, "facts": { - "docker.DockerVolumes": [] + "docker.DockerVolume": { + "object_id=nginx_volume": [] + } }, "commands": [ "docker volume create nginx_volume --label environment=test --label team=a" diff --git a/tests/operations/files.block/add_existing_block_different_content.json b/tests/operations/files.block/add_existing_block_different_content.json index 3c29332d6..97fb59960 100644 --- a/tests/operations/files.block/add_existing_block_different_content.json +++ b/tests/operations/files.block/add_existing_block_different_content.json @@ -8,7 +8,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": ["previously was something else"] + "begin=None, end=None, marker=None, path=/home/someone/something": ["previously was something else"] } }, "commands": [ diff --git a/tests/operations/files.block/add_existing_block_different_content_and_backup.json b/tests/operations/files.block/add_existing_block_different_content_and_backup.json index a871a98cd..8a5900d5f 100644 --- a/tests/operations/files.block/add_existing_block_different_content_and_backup.json +++ b/tests/operations/files.block/add_existing_block_different_content_and_backup.json @@ -9,7 +9,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": ["previously was something else"] + "begin=None, end=None, marker=None, path=/home/someone/something": ["previously was something else"] } }, "commands": [ diff --git a/tests/operations/files.block/add_existing_block_same_content.json b/tests/operations/files.block/add_existing_block_same_content.json index d6ef21326..7b5774b14 100644 --- a/tests/operations/files.block/add_existing_block_same_content.json +++ b/tests/operations/files.block/add_existing_block_same_content.json @@ -8,7 +8,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": ["the one true string"] + "begin=None, end=None, marker=None, path=/home/someone/something": ["the one true string"] } }, "commands": [], diff --git a/tests/operations/files.block/add_line_given_but_before_eq_after.json b/tests/operations/files.block/add_line_given_but_before_eq_after.json index 901e0a3c4..b3f94edcf 100644 --- a/tests/operations/files.block/add_line_given_but_before_eq_after.json +++ b/tests/operations/files.block/add_line_given_but_before_eq_after.json @@ -9,7 +9,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": null + "begin=None, end=None, marker=None, path=/home/someone/something": null } }, "exception": { diff --git a/tests/operations/files.block/add_no_content_provided.json b/tests/operations/files.block/add_no_content_provided.json index fc50d36f9..2e1342dd7 100644 --- a/tests/operations/files.block/add_no_content_provided.json +++ b/tests/operations/files.block/add_no_content_provided.json @@ -3,7 +3,7 @@ "args": ["/home/someone/something"], "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": null + "begin=None, end=None, marker=None, path=/home/someone/something": null } }, "exception": { diff --git a/tests/operations/files.block/add_no_existing_block_and_no_line.json b/tests/operations/files.block/add_no_existing_block_and_no_line.json index c32a5f6b9..921e0292f 100644 --- a/tests/operations/files.block/add_no_existing_block_and_no_line.json +++ b/tests/operations/files.block/add_no_existing_block_and_no_line.json @@ -8,7 +8,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": null + "begin=None, end=None, marker=None, path=/home/someone/something": null } }, "commands": [ diff --git a/tests/operations/files.block/add_no_existing_block_line_provided.json b/tests/operations/files.block/add_no_existing_block_line_provided.json index f7b9bbfb5..157452bcd 100644 --- a/tests/operations/files.block/add_no_existing_block_line_provided.json +++ b/tests/operations/files.block/add_no_existing_block_line_provided.json @@ -8,7 +8,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": [] + "begin=None, end=None, marker=None, path=/home/someone/something": [] } }, "commands": [ diff --git a/tests/operations/files.block/add_no_existing_block_line_provided_escape_regex.json b/tests/operations/files.block/add_no_existing_block_line_provided_escape_regex.json index cd9b1adae..0b261f795 100644 --- a/tests/operations/files.block/add_no_existing_block_line_provided_escape_regex.json +++ b/tests/operations/files.block/add_no_existing_block_line_provided_escape_regex.json @@ -9,7 +9,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": [] + "begin=None, end=None, marker=None, path=/home/someone/something": [] } }, "commands": [ diff --git a/tests/operations/files.block/add_no_existing_file.json b/tests/operations/files.block/add_no_existing_file.json index 358c1ddca..2cbbecec3 100644 --- a/tests/operations/files.block/add_no_existing_file.json +++ b/tests/operations/files.block/add_no_existing_file.json @@ -8,7 +8,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": null + "begin=None, end=None, marker=None, path=/home/someone/something": null } }, "commands": [ diff --git a/tests/operations/files.block/add_no_line_given_but_before_ne_after.json b/tests/operations/files.block/add_no_line_given_but_before_ne_after.json index 57e4f335d..1fea58d8e 100644 --- a/tests/operations/files.block/add_no_line_given_but_before_ne_after.json +++ b/tests/operations/files.block/add_no_line_given_but_before_ne_after.json @@ -8,7 +8,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": null + "begin=None, end=None, marker=None, path=/home/someone/something": null } }, "exception": { diff --git a/tests/operations/files.block/remove_but_content_not_none.json b/tests/operations/files.block/remove_but_content_not_none.json index d0b273c67..41e0f6a30 100644 --- a/tests/operations/files.block/remove_but_content_not_none.json +++ b/tests/operations/files.block/remove_but_content_not_none.json @@ -7,7 +7,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": ["some existing content"] + "begin=None, end=None, marker=None, path=/home/someone/something": ["some existing content"] } }, "commands": [ diff --git a/tests/operations/files.block/remove_existing_block.json b/tests/operations/files.block/remove_existing_block.json index 02d32e964..1c8464f72 100644 --- a/tests/operations/files.block/remove_existing_block.json +++ b/tests/operations/files.block/remove_existing_block.json @@ -6,7 +6,7 @@ }, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": ["existing content"] + "begin=None, end=None, marker=None, path=/home/someone/something": ["existing content"] } }, "commands": [ diff --git a/tests/operations/files.block/remove_no_existing_block.json b/tests/operations/files.block/remove_no_existing_block.json index 738ed52c6..535035f81 100644 --- a/tests/operations/files.block/remove_no_existing_block.json +++ b/tests/operations/files.block/remove_no_existing_block.json @@ -4,7 +4,7 @@ "kwargs": {"present": false}, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": [] + "begin=None, end=None, marker=None, path=/home/someone/something": [] } }, "commands": [], diff --git a/tests/operations/files.block/remove_no_file.json b/tests/operations/files.block/remove_no_file.json index 4ece19de6..8f9916558 100644 --- a/tests/operations/files.block/remove_no_file.json +++ b/tests/operations/files.block/remove_no_file.json @@ -4,7 +4,7 @@ "kwargs": {"present": false}, "facts": { "files.Block": { - "path=/home/someone/something, marker=None, begin=None, end=None": null + "begin=None, end=None, marker=None, path=/home/someone/something": null } }, "commands": [], diff --git a/tests/operations/files.directory/add_with_spaces.json b/tests/operations/files.directory/add_with_spaces.json index 46336a47b..1687ad802 100644 --- a/tests/operations/files.directory/add_with_spaces.json +++ b/tests/operations/files.directory/add_with_spaces.json @@ -8,7 +8,7 @@ }, "facts": { "files.Directory": { - "path=testdir": null + "path=test dir": null } }, "commands": [ diff --git a/tests/operations/files.line/add.json b/tests/operations/files.line/add.json index 13082cbed..5c6f109f6 100644 --- a/tests/operations/files.line/add.json +++ b/tests/operations/files.line/add.json @@ -2,7 +2,7 @@ "args": ["somefile", "match_line"], "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [] } }, "commands": [ diff --git a/tests/operations/files.line/add_backup.json b/tests/operations/files.line/add_backup.json index e36757c43..3d7c1d640 100644 --- a/tests/operations/files.line/add_backup.json +++ b/tests/operations/files.line/add_backup.json @@ -5,7 +5,7 @@ }, "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [] } }, "commands": [ diff --git a/tests/operations/files.line/add_existing.json b/tests/operations/files.line/add_existing.json index e671c6d0f..0a754f9d5 100644 --- a/tests/operations/files.line/add_existing.json +++ b/tests/operations/files.line/add_existing.json @@ -6,7 +6,7 @@ }, "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": ["replace_line"] + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": ["replace_line"] } }, "commands": [], diff --git a/tests/operations/files.line/add_replace.json b/tests/operations/files.line/add_replace.json index 2c5634806..225849684 100644 --- a/tests/operations/files.line/add_replace.json +++ b/tests/operations/files.line/add_replace.json @@ -7,7 +7,7 @@ "facts": { "files.FindInFile": { "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [], - "interpolate_variables=False, path=somefile, pattern=^.*replace_line.*$": [] + "interpolate_variables=False, path=somefile, pattern=^replace_line$": [] } }, "commands": [ diff --git a/tests/operations/files.line/add_replace_noop.json b/tests/operations/files.line/add_replace_noop.json index 43988cc70..217372ecd 100644 --- a/tests/operations/files.line/add_replace_noop.json +++ b/tests/operations/files.line/add_replace_noop.json @@ -6,8 +6,8 @@ }, "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": [], - "path=somefile, pattern=^replace_line$, interpolate_variables=False": ["replace_line"] + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [], + "interpolate_variables=False, path=somefile, pattern=^replace_line$": ["replace_line"] } }, "commands": [], diff --git a/tests/operations/files.line/edit.json b/tests/operations/files.line/edit.json index 1c4f1a413..f250b94d4 100644 --- a/tests/operations/files.line/edit.json +++ b/tests/operations/files.line/edit.json @@ -6,7 +6,7 @@ }, "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": [ + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [ "match_line" ] } diff --git a/tests/operations/files.line/edit_backup.json b/tests/operations/files.line/edit_backup.json index 280020305..d7dda5f29 100644 --- a/tests/operations/files.line/edit_backup.json +++ b/tests/operations/files.line/edit_backup.json @@ -7,7 +7,7 @@ }, "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": [ + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [ "match_line" ] } diff --git a/tests/operations/files.line/pathlib_edit.json b/tests/operations/files.line/pathlib_edit.json index bba7ddd04..4fe4d9090 100644 --- a/tests/operations/files.line/pathlib_edit.json +++ b/tests/operations/files.line/pathlib_edit.json @@ -6,7 +6,7 @@ }, "facts": { "files.FindInFile": { - "path=somefile, pattern=^.*match_line.*$, interpolate_variables=False": [ + "interpolate_variables=False, path=somefile, pattern=^.*match_line.*$": [ "match_line" ] } diff --git a/tests/operations/files.put/copy_local_permissions.json b/tests/operations/files.put/copy_local_permissions.json index f4a40f3e2..7ee952f17 100644 --- a/tests/operations/files.put/copy_local_permissions.json +++ b/tests/operations/files.put/copy_local_permissions.json @@ -11,9 +11,11 @@ }, "facts": { "files.File": { - "path=/home/some\\ file.txt": null + "path=/somefile.txt": null, + "path=/somefile.txt/somefile.txt": null }, "files.Directory": { + "path=/somefile.txt": false, "path=/": true } }, diff --git a/tests/operations/files.put/create_directory.json b/tests/operations/files.put/create_directory.json index 8f548d4e8..6c6c54de1 100644 --- a/tests/operations/files.put/create_directory.json +++ b/tests/operations/files.put/create_directory.json @@ -16,7 +16,8 @@ "path=/home/somefile.txt": null }, "files.Directory": { - "path=/home": null + "path=/home": null, + "path=/home/somefile.txt": null } }, "commands": [ diff --git a/tests/operations/files.put/no_remote.json b/tests/operations/files.put/no_remote.json index fa0a38d61..25058ba61 100644 --- a/tests/operations/files.put/no_remote.json +++ b/tests/operations/files.put/no_remote.json @@ -16,7 +16,8 @@ "path=/home/somefile.txt": null }, "files.Directory": { - "path=/home": true + "path=/home": true, + "path=/home/somefile.txt": null } }, "commands": [ diff --git a/tests/operations/files.put/path_with_spaces.json b/tests/operations/files.put/path_with_spaces.json index 7cfda2a13..1f4a30d75 100644 --- a/tests/operations/files.put/path_with_spaces.json +++ b/tests/operations/files.put/path_with_spaces.json @@ -8,10 +8,11 @@ }, "facts": { "files.File": { - "path=/home/some\\ file.txt": null + "path=/home/some file.txt": null }, "files.Directory": { - "path=/home": true + "path=/home": true, + "path=/home/some file.txt": false } }, "commands": [ diff --git a/tests/operations/files.put/path_with_spaces_already_escaped.json b/tests/operations/files.put/path_with_spaces_already_escaped.json index 9ed33594c..8779b278f 100644 --- a/tests/operations/files.put/path_with_spaces_already_escaped.json +++ b/tests/operations/files.put/path_with_spaces_already_escaped.json @@ -11,7 +11,8 @@ "path=/home/some\\ file.txt": null }, "files.Directory": { - "path=/home": true + "path=/home": true, + "path=/home/some\\ file.txt": false } }, "commands": [ diff --git a/tests/operations/files.put/pathlib_with_spaces.json b/tests/operations/files.put/pathlib_with_spaces.json index f9fec2de2..04418cbeb 100644 --- a/tests/operations/files.put/pathlib_with_spaces.json +++ b/tests/operations/files.put/pathlib_with_spaces.json @@ -9,10 +9,11 @@ }, "facts": { "files.File": { - "path=/home/some\\ file.txt": null + "path=/home/some file.txt": null }, "files.Directory": { - "path=/home": true + "path=/home": true, + "path=/home/some file.txt": null } }, "commands": [ diff --git a/tests/operations/files.put/upload_to_directory.json b/tests/operations/files.put/upload_to_directory.json index 9a2111724..6abf455a5 100644 --- a/tests/operations/files.put/upload_to_directory.json +++ b/tests/operations/files.put/upload_to_directory.json @@ -13,6 +13,7 @@ }, "facts": { "files.File": { + "path=/home": false, "path=/home/somefile.txt": null }, "files.Directory": { diff --git a/tests/operations/files.replace/no_match.json b/tests/operations/files.replace/no_match.json index 4e9cf7aaa..6cb44215c 100644 --- a/tests/operations/files.replace/no_match.json +++ b/tests/operations/files.replace/no_match.json @@ -2,7 +2,7 @@ "args": ["filename", "match", "replace"], "facts": { "files.FindInFile": { - "path=filename, pattern=match, interpolate_variables=False": [] + "interpolate_variables=False, path=filename, pattern=match": [] } }, "commands": [], diff --git a/tests/operations/files.replace/replace_single_quotes_interpolate.json b/tests/operations/files.replace/replace_single_quotes_interpolate.json index 6ef4dc354..cb354da4d 100644 --- a/tests/operations/files.replace/replace_single_quotes_interpolate.json +++ b/tests/operations/files.replace/replace_single_quotes_interpolate.json @@ -5,7 +5,7 @@ }, "facts": { "files.FindInFile": { - "path=filename, pattern='single quote match', interpolate_variables=True": ["'single quote match' rest of line"] + "interpolate_variables=True, path=filename, pattern='single quote match'": ["'single quote match' rest of line"] } }, "commands": [ diff --git a/tests/operations/files.replace/simple_interpolate.json b/tests/operations/files.replace/simple_interpolate.json index bb74fd2e4..e8372ef73 100644 --- a/tests/operations/files.replace/simple_interpolate.json +++ b/tests/operations/files.replace/simple_interpolate.json @@ -6,7 +6,7 @@ }, "facts": { "files.FindInFile": { - "path=filename, pattern=match, interpolate_variables=True": ["matching line"] + "interpolate_variables=True, path=filename, pattern=match": ["matching line"] } }, "commands": [ diff --git a/tests/operations/files.sync/sync_delete_posix.json b/tests/operations/files.sync/sync_delete_posix.json index 669c17c97..c86ed8d03 100644 --- a/tests/operations/files.sync/sync_delete_posix.json +++ b/tests/operations/files.sync/sync_delete_posix.json @@ -41,11 +41,14 @@ "path=/home/somedir/underthat/yet-another-file.txt": null }, "files.Directory": { + "path=/home/somedir/somefile.txt": null, + "path=/home/somedir/anotherfile.txt": null, + "path=/home/somedir/underthat/yet-another-file.txt": null, "path=/home/somedir": null, "path=/home/somedir/underthat": null }, "files.FindFiles": { - "path=/home/somedir": [ + "path=/home/somedir, quote_path=True": [ "/home/somedir/deleteme.txt", "/home/somedir/nodelete.pyc" ], diff --git a/tests/operations/files.sync/sync_delete_windows.json b/tests/operations/files.sync/sync_delete_windows.json index 0059a98e1..c2de52731 100644 --- a/tests/operations/files.sync/sync_delete_windows.json +++ b/tests/operations/files.sync/sync_delete_windows.json @@ -51,10 +51,14 @@ "files.Directory": { "path=/home/somedir": null, "path=/home/somedir/underthat": null, - "path=/home/somedir/underthat/evendeeper": null + "path=/home/somedir/underthat/evendeeper": null, + "path=/home/somedir/somefile.txt": null, + "path=/home/somedir/anotherfile.txt": null, + "path=/home/somedir/underthat/yet-another-file.txt": null, + "path=/home/somedir/underthat/evendeeper/a-very-deep-file.txt": null }, "files.FindFiles": { - "path=/home/somedir": [ + "path=/home/somedir, quote_path=True": [ "/home/somedir/deleteme.txt", "/home/somedir/nodelete.pyc" ], diff --git a/tests/operations/files.sync/sync_destination_link.json b/tests/operations/files.sync/sync_destination_link.json index ce13083dc..af89c6e97 100644 --- a/tests/operations/files.sync/sync_destination_link.json +++ b/tests/operations/files.sync/sync_destination_link.json @@ -53,7 +53,10 @@ "path=/home/some-actual-dir": { "mode": 755 }, - "path=/home/somedir/underthat": null + "path=/home/somedir/underthat": null, + "path=/home/somedir/somefile.txt": null, + "path=/home/somedir/anotherfile.txt": null, + "path=/home/somedir/underthat/yet-another-file.txt": null }, "files.FindFiles": { "path=/home/somedir": [ diff --git a/tests/operations/files.sync/sync_exclude_multiple.json b/tests/operations/files.sync/sync_exclude_multiple.json index 499b2bd10..3c4586739 100644 --- a/tests/operations/files.sync/sync_exclude_multiple.json +++ b/tests/operations/files.sync/sync_exclude_multiple.json @@ -60,6 +60,7 @@ }, "facts": { "files.File": { + "path=/home/somedir": false, "path=/home/somedir/somefile.txt": null, "path=/home/somedir/build.log": null, "path=/home/somedir/local_ignore.pyc": null, @@ -71,7 +72,9 @@ "path=/home/somedir": { "mode": 755 }, - "path=/home/somedir/underthat": null + "path=/home/somedir/underthat": null, + "path=/home/somedir/somefile.txt": null, + "path=/home/somedir/underthat/another_file.txt": null }, "files.FindFiles": { "path=/home/somedir": [ @@ -80,7 +83,9 @@ ], "path=/home/somedir/underthat": [] }, - "files.Link": {} + "files.Link": { + "path=/home/somedir": false + } }, "commands": [ "mkdir -p /home/somedir/underthat", diff --git a/tests/operations/files.sync/sync_no_destination_exist.json b/tests/operations/files.sync/sync_no_destination_exist.json index d6ed414ef..f6a98d09f 100644 --- a/tests/operations/files.sync/sync_no_destination_exist.json +++ b/tests/operations/files.sync/sync_no_destination_exist.json @@ -26,13 +26,17 @@ }, "files.Directory": { "path=/home/somedir": null, - "path=/home/somedir/underthat": null + "path=/home/somedir/underthat": null, + "path=/home/somedir/somefile.txt": null, + "path=/home/somedir/underthat/another-file.txt": null }, "files.FindFiles": { "path=/home/somedir": [], "path=/home/somedir/underthat": [] }, - "files.Link": {}, + "files.Link": { + "path=/home/somedir": null + }, "files.Sha1File": { "path=/home/somedir/somefile.txt": "ac2cd59a622114712b5b21081763c54bf0caacb8" } diff --git a/tests/operations/files.sync/sync_partial_exists.json b/tests/operations/files.sync/sync_partial_exists.json index 4117f0047..5e3ffbecb 100644 --- a/tests/operations/files.sync/sync_partial_exists.json +++ b/tests/operations/files.sync/sync_partial_exists.json @@ -35,7 +35,9 @@ }, "path=/home/somedir/underthat": { "mode": 777 - } + }, + "path=/home/somedir/anotherfile.txt": null, + "path=/home/somedir/underthat/yet-another-file.txt": null }, "files.FindFiles": { "path=/home/somedir": [ diff --git a/tests/operations/files.sync/sync_special_chars.json b/tests/operations/files.sync/sync_special_chars.json index 49d460b62..3d9cf8b6e 100644 --- a/tests/operations/files.sync/sync_special_chars.json +++ b/tests/operations/files.sync/sync_special_chars.json @@ -17,7 +17,8 @@ "path=/home/somedir/yet () another {} file $$ __.txt": null }, "files.Directory": { - "path=/home/somedir": null + "path=/home/somedir": null, + "path=/home/somedir/yet () another {} file $$ __.txt": null }, "files.FindFiles": { "path=/home/somedir": [] diff --git a/tests/operations/files.template/no_remote.json b/tests/operations/files.template/no_remote.json index e11335529..753930d15 100644 --- a/tests/operations/files.template/no_remote.json +++ b/tests/operations/files.template/no_remote.json @@ -16,7 +16,8 @@ "path=/home/somefile.txt.j2": null }, "files.Directory": { - "path=/home": true + "path=/home": true, + "path=/home/somefile.txt.j2": null } }, "commands": [ diff --git a/tests/operations/flatpak.packages/install_package.json b/tests/operations/flatpak.packages/install_package.json new file mode 100644 index 000000000..2c06e70b4 --- /dev/null +++ b/tests/operations/flatpak.packages/install_package.json @@ -0,0 +1,10 @@ +{ + "kwargs": { + "packages": ["org.videolan.VLC"] + }, + "facts": { + "flatpak.FlatpakPackage": {}, + "flatpak.FlatpakPackages": [] + }, + "commands": ["flatpak install --noninteractive org.videolan.VLC"] +} diff --git a/tests/operations/flatpak.packages/install_packages.json b/tests/operations/flatpak.packages/install_packages.json new file mode 100644 index 000000000..31f96078c --- /dev/null +++ b/tests/operations/flatpak.packages/install_packages.json @@ -0,0 +1,16 @@ +{ + "kwargs": { + "packages": ["org.videolan.VLC", "tv.kodi.Kodi"] + }, + "facts": { + "flatpak.FlatpakPackage": { + "package=org.videolan.VLC": { + "id": "org.videolan.VLC", + "ref": "app/org.videolan.VLC/x86_64/stable", + "version": "3.0.21" + } + }, + "flatpak.FlatpakPackages": ["org.videolan.VLC"] + }, + "commands": ["flatpak install --noninteractive tv.kodi.Kodi"] +} diff --git a/tests/operations/flatpak.packages/remove_packages.json b/tests/operations/flatpak.packages/remove_packages.json new file mode 100644 index 000000000..9d7165ba4 --- /dev/null +++ b/tests/operations/flatpak.packages/remove_packages.json @@ -0,0 +1,12 @@ +{ + "kwargs": { + "packages": ["org.videolan.VLC", "tv.kodi.Kodi"], + "present": false + }, + "facts": { + "flatpak.FlatpakPackage": {}, + "flatpak.FlatpakPackages": ["org.videolan.VLC"] + }, + "commands": ["flatpak uninstall --noninteractive org.videolan.VLC"], + "noop_description": "flatpak package tv.kodi.Kodi is not installed" +} diff --git a/tests/operations/git.repo/clone_keyscan.json b/tests/operations/git.repo/clone_keyscan.json index 456a85217..ce473cfbb 100644 --- a/tests/operations/git.repo/clone_keyscan.json +++ b/tests/operations/git.repo/clone_keyscan.json @@ -15,9 +15,7 @@ } }, "files.FindInFile": { - "path=~/.ssh/known_hosts, interpolate_variables=False": { - "github.com": [] - } + "interpolate_variables=False, path=/home/pyinfra/.ssh/known_hosts, pattern=github.com": [] } }, "commands": [ diff --git a/tests/operations/git.worktree/create_force.json b/tests/operations/git.worktree/create_force.json new file mode 100644 index 000000000..d72efbdb6 --- /dev/null +++ b/tests/operations/git.worktree/create_force.json @@ -0,0 +1,19 @@ +{ + "args": ["/home/myworktree"], + "kwargs": { + "repo": "/home/mainrepo", + "force": true + }, + "facts": { + "files.Directory": { + "path=/home/mainrepo": {}, + "path=/home/mainrepo/.git": { + "mode": 0 + }, + "path=/home/myworktree": null + } + }, + "commands": [ + "cd /home/mainrepo && git worktree add --force /home/myworktree" + ] +} diff --git a/tests/operations/git.worktree/pull_disable.json b/tests/operations/git.worktree/pull_disable.json new file mode 100644 index 000000000..8f8d73d97 --- /dev/null +++ b/tests/operations/git.worktree/pull_disable.json @@ -0,0 +1,18 @@ +{ + "args": ["/home/myworktree"], + "kwargs": { + "pull": false + }, + "facts": { + "files.Directory": { + "path=/home/myworktree": { + "mode": 0 + } + }, + "git.GitTrackingBranch" : { + "repo=/home/myworktree": "origin/master" + } + }, + "commands": [], + "noop_description": "Pull is disabled" +} diff --git a/tests/operations/mysql.database/add_privileges.json b/tests/operations/mysql.database/add_privileges.json index 2b7227c41..48b272b56 100644 --- a/tests/operations/mysql.database/add_privileges.json +++ b/tests/operations/mysql.database/add_privileges.json @@ -9,7 +9,7 @@ "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None": {} }, "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "localhost": {} } } diff --git a/tests/operations/mysql.dump/dump.json b/tests/operations/mysql.dump/dump.json index d5889ad1d..b851f811e 100644 --- a/tests/operations/mysql.dump/dump.json +++ b/tests/operations/mysql.dump/dump.json @@ -6,7 +6,10 @@ "mysql_password": "somepass" }, "commands": [ - "mysqldump somedb -u\"root\" *** > somefile" + { + "raw": "mysqldump somedb -u\"root\" -p\"somepass\" > somefile", + "masked": "mysqldump somedb -u\"root\" *** > somefile" + } ], "idempotent": false } diff --git a/tests/operations/mysql.load/load.json b/tests/operations/mysql.load/load.json index 5c2255368..8d432972d 100644 --- a/tests/operations/mysql.load/load.json +++ b/tests/operations/mysql.load/load.json @@ -6,6 +6,5 @@ }, "commands": [ "mysql somedb -u\"root\" < somefile" - ], - "idempotent": false + ] } diff --git a/tests/operations/mysql.privileges/add.json b/tests/operations/mysql.privileges/add.json index 691c480ea..10b6663f3 100644 --- a/tests/operations/mysql.privileges/add.json +++ b/tests/operations/mysql.privileges/add.json @@ -2,7 +2,7 @@ "args": ["someuser", ["SELECT", "INSERT", "DELETE"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "DELETE"] } } diff --git a/tests/operations/mysql.privileges/add_all_with_grant_option.json b/tests/operations/mysql.privileges/add_all_with_grant_option.json index 0c212a4ad..8fb49ab76 100644 --- a/tests/operations/mysql.privileges/add_all_with_grant_option.json +++ b/tests/operations/mysql.privileges/add_all_with_grant_option.json @@ -5,7 +5,7 @@ }, "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:"] } } diff --git a/tests/operations/mysql.privileges/add_database.json b/tests/operations/mysql.privileges/add_database.json index 2cf4e01db..a7563140c 100644 --- a/tests/operations/mysql.privileges/add_database.json +++ b/tests/operations/mysql.privileges/add_database.json @@ -5,7 +5,7 @@ }, "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:"] } } diff --git a/tests/operations/mysql.privileges/add_delete.json b/tests/operations/mysql.privileges/add_delete.json index e4bdc917e..517b43f19 100644 --- a/tests/operations/mysql.privileges/add_delete.json +++ b/tests/operations/mysql.privileges/add_delete.json @@ -2,7 +2,7 @@ "args": ["someuser", ["SELECT", "INSERT", "DELETE"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "DELETE", "UPDATE"] } } diff --git a/tests/operations/mysql.privileges/add_delete_usage.json b/tests/operations/mysql.privileges/add_delete_usage.json index 4e5aba7bb..e117c8041 100644 --- a/tests/operations/mysql.privileges/add_delete_usage.json +++ b/tests/operations/mysql.privileges/add_delete_usage.json @@ -2,7 +2,7 @@ "args": ["someuser", ["SELECT"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "USAGE"] } } diff --git a/tests/operations/mysql.privileges/add_existing.json b/tests/operations/mysql.privileges/add_existing.json index 5582bd3c5..2a56ac436 100644 --- a/tests/operations/mysql.privileges/add_existing.json +++ b/tests/operations/mysql.privileges/add_existing.json @@ -2,7 +2,7 @@ "args": ["someuser", ["SELECT", "INSERT"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "SELECT", "INSERT"] } } diff --git a/tests/operations/mysql.privileges/add_table.json b/tests/operations/mysql.privileges/add_table.json index 7e432024a..e698e534f 100644 --- a/tests/operations/mysql.privileges/add_table.json +++ b/tests/operations/mysql.privileges/add_table.json @@ -6,7 +6,7 @@ }, "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:"] } } diff --git a/tests/operations/mysql.privileges/add_table_invalid.json b/tests/operations/mysql.privileges/add_table_invalid.json index 1ff6b617e..43f63991f 100644 --- a/tests/operations/mysql.privileges/add_table_invalid.json +++ b/tests/operations/mysql.privileges/add_table_invalid.json @@ -5,7 +5,7 @@ }, "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": [] } } diff --git a/tests/operations/mysql.privileges/add_with_grant_option.json b/tests/operations/mysql.privileges/add_with_grant_option.json index bfc0f6a13..174901eac 100644 --- a/tests/operations/mysql.privileges/add_with_grant_option.json +++ b/tests/operations/mysql.privileges/add_with_grant_option.json @@ -5,7 +5,7 @@ }, "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:"] } } diff --git a/tests/operations/mysql.privileges/delete.json b/tests/operations/mysql.privileges/delete.json index 2899c37fb..0f0ce3086 100644 --- a/tests/operations/mysql.privileges/delete.json +++ b/tests/operations/mysql.privileges/delete.json @@ -2,7 +2,7 @@ "args": ["someuser", ["DELETE"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "SELECT", "INSERT", "DELETE"] } } diff --git a/tests/operations/mysql.privileges/delete_all.json b/tests/operations/mysql.privileges/delete_all.json index 53894ad3d..497be8363 100644 --- a/tests/operations/mysql.privileges/delete_all.json +++ b/tests/operations/mysql.privileges/delete_all.json @@ -2,7 +2,7 @@ "args": ["someuser", ["DELETE"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "ALL"] } } diff --git a/tests/operations/mysql.privileges/delete_all_with_grant_option.json b/tests/operations/mysql.privileges/delete_all_with_grant_option.json index 3fed332ef..8ddff4ba9 100644 --- a/tests/operations/mysql.privileges/delete_all_with_grant_option.json +++ b/tests/operations/mysql.privileges/delete_all_with_grant_option.json @@ -2,7 +2,7 @@ "args": ["someuser", []], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "ALL", "GRANT OPTION"] } } diff --git a/tests/operations/mysql.privileges/delete_with_grant_option.json b/tests/operations/mysql.privileges/delete_with_grant_option.json index ea0de2b16..4c7fa20ad 100644 --- a/tests/operations/mysql.privileges/delete_with_grant_option.json +++ b/tests/operations/mysql.privileges/delete_with_grant_option.json @@ -2,7 +2,7 @@ "args": ["someuser", ["SELECT"]], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "SELECT", "GRANT OPTION"] } } diff --git a/tests/operations/mysql.privileges/remove_noop.json b/tests/operations/mysql.privileges/remove_noop.json index 1e86a83c7..f354b7117 100644 --- a/tests/operations/mysql.privileges/remove_noop.json +++ b/tests/operations/mysql.privileges/remove_noop.json @@ -2,7 +2,7 @@ "args": ["someuser", []], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:"] } } diff --git a/tests/operations/mysql.privileges/remove_usage.json b/tests/operations/mysql.privileges/remove_usage.json index 0dcead5ca..05250157d 100644 --- a/tests/operations/mysql.privileges/remove_usage.json +++ b/tests/operations/mysql.privileges/remove_usage.json @@ -2,7 +2,7 @@ "args": ["someuser", []], "facts": { "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localhost": { + "hostname=localhost, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": { "*.*": ["set:", "USAGE"] } } diff --git a/tests/operations/mysql.sql/execute_mysql_kwargs.json b/tests/operations/mysql.sql/execute_mysql_kwargs.json index 94f6db46e..9de5fac6e 100644 --- a/tests/operations/mysql.sql/execute_mysql_kwargs.json +++ b/tests/operations/mysql.sql/execute_mysql_kwargs.json @@ -7,10 +7,10 @@ "mysql_port": 100 }, "commands": [ - [ - "mysql -u\"someuser\" -p\"somepass\" -h127.0.0.1 -P100 -Be 'SELECT 1'", - "mysql -u\"someuser\" *** -h127.0.0.1 -P100 -Be 'SELECT 1'" - ] + { + "raw": "mysql -u\"someuser\" -p\"somepass\" -h127.0.0.1 -P100 -Be 'SELECT 1'", + "masked": "mysql -u\"someuser\" *** -h127.0.0.1 -P100 -Be 'SELECT 1'" + } ], "idempotent": false } diff --git a/tests/operations/mysql.user/add_password.json b/tests/operations/mysql.user/add_password.json index e5b9ecbf8..5b590e04b 100644 --- a/tests/operations/mysql.user/add_password.json +++ b/tests/operations/mysql.user/add_password.json @@ -10,9 +10,9 @@ } }, "commands": [ - [ - "mysql -Be 'CREATE USER \"someuser\"@\"localwhat\" IDENTIFIED BY \"mypass\"'", - "mysql -Be 'CREATE USER \"someuser\"@\"localwhat\" ***'" - ] + { + "raw": "mysql -Be 'CREATE USER \"someuser\"@\"localwhat\" IDENTIFIED BY \"mypass\"'", + "masked": "mysql -Be 'CREATE USER \"someuser\"@\"localwhat\" ***'" + } ] } diff --git a/tests/operations/mysql.user/add_privileges.json b/tests/operations/mysql.user/add_privileges.json index 3ca4d7064..f51a2ab37 100644 --- a/tests/operations/mysql.user/add_privileges.json +++ b/tests/operations/mysql.user/add_privileges.json @@ -9,7 +9,7 @@ "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None": {} }, "mysql.MysqlUserGrants": { - "mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser, hostname=localwhat": {} + "hostname=localwhat, mysql_host=None, mysql_password=None, mysql_port=None, mysql_user=None, user=someuser": {} } }, "commands": [ diff --git a/tests/operations/pkg.packages/pkg_add_packages.json b/tests/operations/pkg.packages/pkg_add_packages.json index 9c7dfcec1..35a55a503 100644 --- a/tests/operations/pkg.packages/pkg_add_packages.json +++ b/tests/operations/pkg.packages/pkg_add_packages.json @@ -10,6 +10,9 @@ }, "files.File": { "path=/etc/installurl": false + }, + "server.Which": { + "command=pkg": null } }, "commands": [ diff --git a/tests/operations/pkg.packages/pkg_add_packages_with_installurl.json b/tests/operations/pkg.packages/pkg_add_packages_with_installurl.json index 4202f8cfc..98a2992c3 100644 --- a/tests/operations/pkg.packages/pkg_add_packages_with_installurl.json +++ b/tests/operations/pkg.packages/pkg_add_packages_with_installurl.json @@ -12,6 +12,9 @@ "path=/etc/installurl": { "size": 0 } + }, + "server.Which": { + "command=pkg": null } }, "commands": [ diff --git a/tests/operations/postgresql.dump/dump.json b/tests/operations/postgresql.dump/dump.json index b1ab39e5d..96446f551 100644 --- a/tests/operations/postgresql.dump/dump.json +++ b/tests/operations/postgresql.dump/dump.json @@ -8,10 +8,10 @@ "psql_port": 3306 }, "commands": [ - [ - "PGPASSWORD=\"somepass\" pg_dump -d somedb -U root -h localhost -p 3306 > somefile", - "*** pg_dump -d somedb -U root -h localhost -p 3306 > somefile" - ] + { + "raw": "PGPASSWORD=\"somepass\" pg_dump -d somedb -U root -h localhost -p 3306 > somefile", + "masked": "*** pg_dump -d somedb -U root -h localhost -p 3306 > somefile" + } ], "idempotent": false } diff --git a/tests/operations/postgresql.load/load.json b/tests/operations/postgresql.load/load.json index 14bb4f341..066e15762 100644 --- a/tests/operations/postgresql.load/load.json +++ b/tests/operations/postgresql.load/load.json @@ -6,10 +6,10 @@ "psql_password": "somepass" }, "commands": [ - [ - "PGPASSWORD=\"somepass\" psql -d somedb -U root < somefile", - "*** psql -d somedb -U root < somefile" - ] + { + "raw": "PGPASSWORD=\"somepass\" psql -d somedb -U root < somefile", + "masked": "*** psql -d somedb -U root < somefile" + } ], "idempotent": false } diff --git a/tests/operations/postgresql.role/user_add.json b/tests/operations/postgresql.role/user_add.json index 2c995eb98..e927648bf 100644 --- a/tests/operations/postgresql.role/user_add.json +++ b/tests/operations/postgresql.role/user_add.json @@ -10,9 +10,9 @@ } }, "commands": [ - [ - "psql -Ac 'CREATE ROLE \"testuser\" LOGIN CONNECTION LIMIT 15 PASSWORD '\"'\"'abc'\"'\"''", - "psql -Ac 'CREATE ROLE \"testuser\" LOGIN CONNECTION LIMIT 15 ***'" - ] + { + "raw": "psql -Ac 'CREATE ROLE \"testuser\" LOGIN CONNECTION LIMIT 15 PASSWORD '\"'\"'abc'\"'\"''", + "masked":"psql -Ac 'CREATE ROLE \"testuser\" LOGIN CONNECTION LIMIT 15 ***'" + } ] } diff --git a/tests/operations/runit.service/disable.json b/tests/operations/runit.service/disable.json index 0a57d861b..c2e5c68e6 100644 --- a/tests/operations/runit.service/disable.json +++ b/tests/operations/runit.service/disable.json @@ -15,7 +15,8 @@ } }, "files.File": { - "path=/var/service/nginx/down": null + "path=/var/service/nginx/down": null, + "path=/etc/sv/nginx/down": null }, "runit.RunitStatus": { "service=nginx, svdir=/var/service": {"nginx": true} diff --git a/tests/operations/runit.service/enable.json b/tests/operations/runit.service/enable.json index 3d6a2e4da..62c21dc70 100644 --- a/tests/operations/runit.service/enable.json +++ b/tests/operations/runit.service/enable.json @@ -18,7 +18,8 @@ } }, "files.File": { - "path=/var/service/nginx/down": null + "path=/var/service/nginx/down": null, + "path=/etc/sv/nginx/down": null }, "runit.RunitStatus": { "service=nginx, svdir=/var/service": {"nginx": true} diff --git a/tests/operations/selinux.port/add_different.json b/tests/operations/selinux.port/add_different.json index 75f0d197d..e126cc763 100644 --- a/tests/operations/selinux.port/add_different.json +++ b/tests/operations/selinux.port/add_different.json @@ -10,7 +10,7 @@ } }, "server.Which": { - "sepolicy": null + "command=sepolicy": null } }, "commands": [ diff --git a/tests/operations/selinux.port/add_different_direct.json b/tests/operations/selinux.port/add_different_direct.json index 431b66201..15dd7b683 100644 --- a/tests/operations/selinux.port/add_different_direct.json +++ b/tests/operations/selinux.port/add_different_direct.json @@ -5,7 +5,7 @@ }, "facts": { "selinux.SEPort": { - "protocol=tcp, port=22": "xray_port_t" + "port=22, protocol=tcp": "xray_port_t" }, "server.Which": { "command=sepolicy": "/usr/bin/sepolicy" diff --git a/tests/operations/selinux.port/add_not_existing.json b/tests/operations/selinux.port/add_not_existing.json index e620fdf91..ac8d04244 100644 --- a/tests/operations/selinux.port/add_not_existing.json +++ b/tests/operations/selinux.port/add_not_existing.json @@ -8,7 +8,7 @@ "tcp": {"22": ""} }, "server.Which": { - "sepolicy": null + "command=sepolicy": null } }, "commands": [ diff --git a/tests/operations/selinux.port/add_not_existing_direct.json b/tests/operations/selinux.port/add_not_existing_direct.json index 96ebbc4ad..a51c2541b 100644 --- a/tests/operations/selinux.port/add_not_existing_direct.json +++ b/tests/operations/selinux.port/add_not_existing_direct.json @@ -5,7 +5,7 @@ }, "facts": { "selinux.SEPort": { - "protocol=tcp, port=22": "" + "port=22, protocol=tcp": "" }, "server.Which": { "command=sepolicy": "/usr/bin/sepolicy" diff --git a/tests/operations/selinux.port/add_not_existing_protocol.json b/tests/operations/selinux.port/add_not_existing_protocol.json index 3ce50c0e0..b8d0184b6 100644 --- a/tests/operations/selinux.port/add_not_existing_protocol.json +++ b/tests/operations/selinux.port/add_not_existing_protocol.json @@ -8,7 +8,7 @@ "udp": {"53": "dns_port_t"} }, "server.Which": { - "sepolicy": null + "command=sepolicy": null } }, "commands": ["semanage port -a -t ssh_port_t -p tcp 22"] diff --git a/tests/operations/selinux.port/add_same.json b/tests/operations/selinux.port/add_same.json index fc4f1ec5c..4d85b56af 100644 --- a/tests/operations/selinux.port/add_same.json +++ b/tests/operations/selinux.port/add_same.json @@ -8,7 +8,7 @@ "tcp": {"22": "ssh_port_t"} }, "server.Which": { - "sepolicy": null + "command=sepolicy": null } }, "commands": [], diff --git a/tests/operations/selinux.port/add_same_direct.json b/tests/operations/selinux.port/add_same_direct.json index 0cc8428e9..78ff51fcd 100644 --- a/tests/operations/selinux.port/add_same_direct.json +++ b/tests/operations/selinux.port/add_same_direct.json @@ -5,7 +5,7 @@ }, "facts": { "selinux.SEPort": { - "protocol=tcp, port=22": "ssh_port_t" + "port=22, protocol=tcp": "ssh_port_t" }, "server.Which": { "command=sepolicy": "/usr/bin/sepolicy" diff --git a/tests/operations/selinux.port/remove_existing.json b/tests/operations/selinux.port/remove_existing.json index 112a43e4c..416c2404a 100644 --- a/tests/operations/selinux.port/remove_existing.json +++ b/tests/operations/selinux.port/remove_existing.json @@ -8,7 +8,7 @@ "tcp": {"22": "ssh_port_t"} }, "server.Which": { - "sepolicy": null + "command=sepolicy": null } }, "commands": [ diff --git a/tests/operations/selinux.port/remove_existing_direct.json b/tests/operations/selinux.port/remove_existing_direct.json index 43d91e3b2..9ec1a6997 100644 --- a/tests/operations/selinux.port/remove_existing_direct.json +++ b/tests/operations/selinux.port/remove_existing_direct.json @@ -5,7 +5,7 @@ }, "facts": { "selinux.SEPort": { - "protocol=tcp, port=22": "ssh_port_t" + "port=22, protocol=tcp": "ssh_port_t" }, "server.Which": { "command=sepolicy": "/usr/bin/sepolicy" diff --git a/tests/operations/selinux.port/remove_not_existing.json b/tests/operations/selinux.port/remove_not_existing.json index 6eb3cc90c..118fe3252 100644 --- a/tests/operations/selinux.port/remove_not_existing.json +++ b/tests/operations/selinux.port/remove_not_existing.json @@ -5,10 +5,10 @@ }, "facts": { "selinux.SEPorts": { - "tcp": {"22": ""} + "port=22, protocol=tcp": {"22": ""} }, "server.Which": { - "sepolicy": null + "command=sepolicy": null } }, "commands": [], diff --git a/tests/operations/selinux.port/remove_not_existing_direct.json b/tests/operations/selinux.port/remove_not_existing_direct.json index 18353deae..c3a8f13bd 100644 --- a/tests/operations/selinux.port/remove_not_existing_direct.json +++ b/tests/operations/selinux.port/remove_not_existing_direct.json @@ -5,7 +5,7 @@ }, "facts": { "selinux.SEPort": { - "protocol=tcp, port=22": "" + "port=22, protocol=tcp": "" }, "server.Which": { "command=sepolicy": "/usr/bin/sepolicy" diff --git a/tests/operations/server.crontab/add_existing_int.json b/tests/operations/server.crontab/add_existing_int.json new file mode 100644 index 000000000..235b91f8a --- /dev/null +++ b/tests/operations/server.crontab/add_existing_int.json @@ -0,0 +1,22 @@ +{ + "args": ["this_is_a_command"], + "kwargs": { + "minute": "0", + "hour": "0" + }, + "facts": { + "server.Crontab": { + "user=None": { + "this_is_a_command": { + "minute": 0, + "hour": 0, + "month": "*", + "day_of_week": "*", + "day_of_month": "*" + } + } + } + }, + "commands": [], + "noop_description": "crontab this_is_a_command exists" +} diff --git a/tests/operations/server.hostname/set_hostname_bsd.json b/tests/operations/server.hostname/set_hostname_bsd.json index f00f27bce..967adc142 100644 --- a/tests/operations/server.hostname/set_hostname_bsd.json +++ b/tests/operations/server.hostname/set_hostname_bsd.json @@ -7,7 +7,8 @@ "path=/etc/myname": {} }, "files.Directory": { - "path=/etc": true + "path=/etc": true, + "path=/etc/myname": false }, "server.Which": { "command=hostnamectl": null diff --git a/tests/operations/server.hostname/set_hostname_linux.json b/tests/operations/server.hostname/set_hostname_linux.json index 8cc29604e..40b550709 100644 --- a/tests/operations/server.hostname/set_hostname_linux.json +++ b/tests/operations/server.hostname/set_hostname_linux.json @@ -7,7 +7,9 @@ "path=/etc/hostname": {} }, "files.Directory": { - "path=/etc": true + "path=/etc": true, + "path=/etc/myname": false, + "path=/etc/hostname": false }, "server.Which": { "command=hostnamectl": null diff --git a/tests/operations/server.locale/add.json b/tests/operations/server.locale/add.json index 477a9d964..2b38e566d 100644 --- a/tests/operations/server.locale/add.json +++ b/tests/operations/server.locale/add.json @@ -4,11 +4,14 @@ ], "facts": { "files.FindInFile": { - "path=/etc/locale.gen, pattern=^.*fr_FR.UTF-8[[:space:]]\\+.*$": [ + "interpolate_variables=False, path=/etc/locale.gen, pattern=^.*fr_FR.UTF-8[[:space:]]\\+.*$": [ "# fr_FR.UTF-8 UTF-8" ], "interpolate_variables=False, path=/etc/locale.gen, pattern=^.*fr_FR.UTF-8[[:space:]]\\+.*": [ "# fr_FR.UTF-8 UTF-8" + ], + "interpolate_variables=False, path=/etc/locale.gen, pattern=^# fr_FR.UTF-8 UTF-8$": [ + "# fr_FR.UTF-8 UTF-8" ] }, "server.Locales": [ @@ -27,4 +30,4 @@ ], "idempotent": false, "disable_idempotent_warning_reason": "localgen is always executed" -} \ No newline at end of file +} diff --git a/tests/operations/server.locale/remove.json b/tests/operations/server.locale/remove.json index dc9fedad3..ae2d65dbe 100644 --- a/tests/operations/server.locale/remove.json +++ b/tests/operations/server.locale/remove.json @@ -4,11 +4,14 @@ ], "facts": { "files.FindInFile": { - "path=/etc/locale.gen, pattern=^.*fr_FR.UTF-8[[:space:]]\\+.*$": [ + "interpolate_variables=False, path=/etc/locale.gen, pattern=^.*fr_FR.UTF-8[[:space:]]\\+.*$": [ "# fr_FR.UTF-8 UTF-8" ], "interpolate_variables=False, path=/etc/locale.gen, pattern=^.*fr_FR.UTF-8[[:space:]]\\+.*": [ "# fr_FR.UTF-8 UTF-8" + ], + "interpolate_variables=False, path=/etc/locale.gen, pattern=^# fr_FR.UTF-8 UTF-8$": [ + "# fr_FR.UTF-8 UTF-8" ] }, "server.Locales": [ @@ -28,4 +31,4 @@ ], "idempotent": false, "disable_idempotent_warning_reason": "localgen is always executed" -} \ No newline at end of file +} diff --git a/tests/operations/server.packages/add_apk_packages.json b/tests/operations/server.packages/add_apk_packages.json index 0abbdd486..00331dff6 100644 --- a/tests/operations/server.packages/add_apk_packages.json +++ b/tests/operations/server.packages/add_apk_packages.json @@ -6,7 +6,8 @@ ]], "facts": { "server.Which": { - "command=apk": "/sbin/apk" + "command=apk": "/sbin/apk", + "command=zypper": null }, "apk.ApkPackages": { "something": [""] diff --git a/tests/operations/server.packages/add_dnf_packages.json b/tests/operations/server.packages/add_dnf_packages.json index b4a665f8b..ef22e06b6 100644 --- a/tests/operations/server.packages/add_dnf_packages.json +++ b/tests/operations/server.packages/add_dnf_packages.json @@ -9,15 +9,16 @@ "command=apk": null, "command=apt": null, "command=brew": null, + "command=zypper": null, "command=dnf": "/sbin/dnf" }, "rpm.RpmPackages": { "something": [""] }, "rpm.RpmPackageProvides": { - "name=git": null, - "name=python-devel": null, - "name=something": null + "package=git": null, + "package=python-devel": null, + "package=something": null } }, "commands": [ diff --git a/tests/operations/server.packages/add_yum_packages.json b/tests/operations/server.packages/add_yum_packages.json index 9488a5244..845d30b79 100644 --- a/tests/operations/server.packages/add_yum_packages.json +++ b/tests/operations/server.packages/add_yum_packages.json @@ -12,15 +12,17 @@ "command=dnf": null, "command=pacman": null, "command=xbps": null, + "command=zypper": null, + "command=xbps-install": null, "command=yum": "/sbin/yum" }, "rpm.RpmPackages": { "something": [""] }, "rpm.RpmPackageProvides": { - "name=git": null, - "name=python-devel": null, - "name=something": null + "package=git": null, + "package=python-devel": null, + "package=something": null } }, "commands": [ diff --git a/tests/operations/server.script/upload_run.json b/tests/operations/server.script/upload_run.json index 171912574..5ce13d9a4 100644 --- a/tests/operations/server.script/upload_run.json +++ b/tests/operations/server.script/upload_run.json @@ -10,7 +10,9 @@ "files.File": { "path=_tempfile_": null }, - "files.Directory": {} + "files.Directory": { + "path=_tempfile_": null + } }, "commands": [ ["upload", "/somescript.sh", "_tempfile_"], diff --git a/tests/operations/server.script_template/script.json b/tests/operations/server.script_template/script.json index ff200b857..25a3571d4 100644 --- a/tests/operations/server.script_template/script.json +++ b/tests/operations/server.script_template/script.json @@ -10,7 +10,9 @@ "files.File": { "path=_tempfile_": null }, - "files.Directory": {} + "files.Directory": { + "path=_tempfile_": null + } }, "commands": [ ["upload", "_test_data_", "_tempfile_"], diff --git a/tests/operations/server.security_limit/set.json b/tests/operations/server.security_limit/set.json index d59acdf1b..e7b9538f1 100644 --- a/tests/operations/server.security_limit/set.json +++ b/tests/operations/server.security_limit/set.json @@ -5,7 +5,12 @@ "item": "memlock", "value": "unlimited" }, - "facts": {"files.FindInFile": {}}, + "facts": { + "files.FindInFile": { + "interpolate_variables=False, path=/etc/security/limits.conf, pattern=^root[[:space:]]+hard[[:space:]]+memlock.*$": [], + "interpolate_variables=False, path=/etc/security/limits.conf, pattern=^root\thard\tmemlock\tunlimited$": [] + } + }, "commands": [ "echo 'root\thard\tmemlock\tunlimited' >> /etc/security/limits.conf" ] diff --git a/tests/operations/server.service/invalid.json b/tests/operations/server.service/invalid.json index 8a541ceff..d8b0fd902 100644 --- a/tests/operations/server.service/invalid.json +++ b/tests/operations/server.service/invalid.json @@ -3,7 +3,10 @@ "facts": { "server.Which": { "command=systemctl": false, - "command=initctl": false + "command=initctl": false, + "command=rc-service": false, + "command=sv": false, + "command=service": false }, "files.Directory": { "path=/etc/init.d": false, diff --git a/tests/operations/server.service/start_initd.json b/tests/operations/server.service/start_initd.json index d8d6bc479..541fdb4a0 100644 --- a/tests/operations/server.service/start_initd.json +++ b/tests/operations/server.service/start_initd.json @@ -6,7 +6,10 @@ }, "server.Which": { "command=systemctl": false, - "command=initctl": false + "command=initctl": false, + "command=rc-service": false, + "command=sv": false, + "command=service": false }, "files.Directory": { "path=/etc/init.d": true, diff --git a/tests/operations/server.service/start_initd_service.json b/tests/operations/server.service/start_initd_service.json index e4b0d7b1c..d3c000c82 100644 --- a/tests/operations/server.service/start_initd_service.json +++ b/tests/operations/server.service/start_initd_service.json @@ -7,7 +7,15 @@ "server.Which": { "command=systemctl": false, "command=initctl": false, - "command=service": true + "command=rc-service": false, + "command=sv": false, + "command=service": false + }, + "files.Link": { + "path=/etc/init.d": false + }, + "files.Directory": { + "path=/etc/init.d": true } }, "commands": [ diff --git a/tests/operations/server.service/start_rcd.json b/tests/operations/server.service/start_rcd.json index fca2bf2ad..f10673108 100644 --- a/tests/operations/server.service/start_rcd.json +++ b/tests/operations/server.service/start_rcd.json @@ -7,7 +7,10 @@ }, "server.Which": { "command=systemctl": false, - "command=initctl": false + "command=initctl": false, + "command=rc-service": false, + "command=sv": false, + "command=service": false }, "files.Directory": { "path=/etc/init.d": false, diff --git a/tests/operations/server.service/start_runit.json b/tests/operations/server.service/start_runit.json index ac87a084f..a20673f4f 100644 --- a/tests/operations/server.service/start_runit.json +++ b/tests/operations/server.service/start_runit.json @@ -2,7 +2,11 @@ "args": ["nginx"], "facts": { "server.Which": { - "command=sv": true + "command=sv": true, + "command=systemctl": false, + "command=rc-service": false, + "command=initctl": false, + "command=service": false }, "runit.RunitManaged": { "service=nginx, svdir=/var/service": ["nginx"] diff --git a/tests/operations/server.service/start_systemd.json b/tests/operations/server.service/start_systemd.json index c4207a0d4..d0b46614b 100644 --- a/tests/operations/server.service/start_systemd.json +++ b/tests/operations/server.service/start_systemd.json @@ -2,7 +2,7 @@ "args": ["nginx.service"], "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['nginx.service']": { + "machine=None, services=['nginx.service'], user_mode=False, user_name=None": { "nginx.service": false } }, diff --git a/tests/operations/server.service/start_upstart.json b/tests/operations/server.service/start_upstart.json index eba0f7e19..d2123ad3e 100644 --- a/tests/operations/server.service/start_upstart.json +++ b/tests/operations/server.service/start_upstart.json @@ -6,7 +6,10 @@ }, "server.Which": { "command=systemctl": false, - "command=initctl": true + "command=initctl": true, + "command=rc-service": false, + "command=sv": false, + "command=service": false }, "files.Directory": { "path=/etc/init.d": false, diff --git a/tests/operations/server.sysctl/set_persist.json b/tests/operations/server.sysctl/set_persist.json index 8add2212b..9cf62b138 100644 --- a/tests/operations/server.sysctl/set_persist.json +++ b/tests/operations/server.sysctl/set_persist.json @@ -8,8 +8,8 @@ "keys=['kern.max-files']": {} }, "files.FindInFile": { - "path=/etc/sysctl.conf, pattern=^.*kern.max-files[[:space:]]*=[[:space:]]*2000.*$, interpolate_variables=False": [], - "path=/etc/sysctl.conf, pattern=^.*kern.max-files = 2000.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=/etc/sysctl.conf, pattern=^.*kern.max-files[[:space:]]*=[[:space:]]*2000.*$": [], + "interpolate_variables=False, path=/etc/sysctl.conf, pattern=^kern.max-files = 2000$": [] } }, "commands": [ diff --git a/tests/operations/server.user/add_group_exists.json b/tests/operations/server.user/add_group_exists.json index 54b20c6ea..d1985f858 100644 --- a/tests/operations/server.user/add_group_exists.json +++ b/tests/operations/server.user/add_group_exists.json @@ -5,7 +5,9 @@ "server.Groups": [ "someuser" ], - "files.Directory": {}, + "files.Directory": { + "path=/home/someuser": null + }, "server.Os": "Linux" }, "commands": [ diff --git a/tests/operations/server.user/add_invalid_home_is_file.json b/tests/operations/server.user/add_invalid_home_is_file.json index aef43018c..4e789ea9b 100644 --- a/tests/operations/server.user/add_invalid_home_is_file.json +++ b/tests/operations/server.user/add_invalid_home_is_file.json @@ -10,7 +10,9 @@ "files.Directory": { "path=homedir": false }, - "files.Link": {} + "files.Link": { + "path=homedir": false + } }, "exception": { "name": "OperationError", diff --git a/tests/operations/server.user/key_files.json b/tests/operations/server.user/key_files.json index deab0974c..9332929cc 100644 --- a/tests/operations/server.user/key_files.json +++ b/tests/operations/server.user/key_files.json @@ -39,9 +39,9 @@ } }, "files.FindInFile": { - "path=homedir/.ssh/authorized_keys, pattern=^.*abc.*$, interpolate_variables=False": [], - "path=homedir/.ssh/authorized_keys, pattern=^.*somekeydata.*$, interpolate_variables=False": ["somekeydata"], - "path=homedir/.ssh/authorized_keys, pattern=^.*someotherkeydata.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=homedir/.ssh/authorized_keys, pattern=^.*abc.*$": [], + "interpolate_variables=False, path=homedir/.ssh/authorized_keys, pattern=^.*somekeydata.*$": ["somekeydata"], + "interpolate_variables=False, path=homedir/.ssh/authorized_keys, pattern=^.*someotherkeydata.*$": [] }, "server.Groups": {} }, diff --git a/tests/operations/server.user/keys.json b/tests/operations/server.user/keys.json index f39b22e8e..ab12e3131 100644 --- a/tests/operations/server.user/keys.json +++ b/tests/operations/server.user/keys.json @@ -32,7 +32,7 @@ } }, "files.FindInFile": { - "path=homedir/.ssh/authorized_keys, pattern=^.*abc.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=homedir/.ssh/authorized_keys, pattern=^.*abc.*$": [] }, "server.Groups": {} }, diff --git a/tests/operations/server.user/keys_nohome.json b/tests/operations/server.user/keys_nohome.json index 3d2847730..a1b34beb3 100644 --- a/tests/operations/server.user/keys_nohome.json +++ b/tests/operations/server.user/keys_nohome.json @@ -31,7 +31,7 @@ } }, "files.FindInFile": { - "path=/root/.ssh/authorized_keys, pattern=^.*abc.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=/root/.ssh/authorized_keys, pattern=^.*abc.*$": [] }, "server.Groups": {} }, diff --git a/tests/operations/server.user/keys_single.json b/tests/operations/server.user/keys_single.json index a72dcfeba..bcf1069ea 100644 --- a/tests/operations/server.user/keys_single.json +++ b/tests/operations/server.user/keys_single.json @@ -32,7 +32,7 @@ } }, "files.FindInFile": { - "path=homedir/.ssh/authorized_keys, pattern=^.*abc.*$, interpolate_variables=False": [] + "interpolate_variables=False, path=homedir/.ssh/authorized_keys, pattern=^.*abc.*$": [] }, "server.Groups": {} }, diff --git a/tests/operations/ssh.download/download.json b/tests/operations/ssh.download/download.json index 4b2fc6a7b..8dac6df17 100644 --- a/tests/operations/ssh.download/download.json +++ b/tests/operations/ssh.download/download.json @@ -15,7 +15,7 @@ "path=local_filename": null }, "files.FindInFile": { - "path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] + "interpolate_variables=False, path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] } }, "commands": [ diff --git a/tests/operations/ssh.keyscan/scan_not_present.json b/tests/operations/ssh.keyscan/scan_not_present.json index 189768540..de6f9f224 100644 --- a/tests/operations/ssh.keyscan/scan_not_present.json +++ b/tests/operations/ssh.keyscan/scan_not_present.json @@ -8,7 +8,7 @@ } }, "files.FindInFile": { - "path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [] + "interpolate_variables=False, path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [] } }, "commands": [ diff --git a/tests/operations/ssh.keyscan/scan_present.json b/tests/operations/ssh.keyscan/scan_present.json index 1de2188cc..4c931a252 100644 --- a/tests/operations/ssh.keyscan/scan_present.json +++ b/tests/operations/ssh.keyscan/scan_present.json @@ -8,7 +8,7 @@ } }, "files.FindInFile": { - "path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] + "interpolate_variables=False, path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] } }, "commands": [], diff --git a/tests/operations/ssh.keyscan/scan_present_force.json b/tests/operations/ssh.keyscan/scan_present_force.json index 372fc5e27..62fcdbaa7 100644 --- a/tests/operations/ssh.keyscan/scan_present_force.json +++ b/tests/operations/ssh.keyscan/scan_present_force.json @@ -11,7 +11,7 @@ } }, "files.FindInFile": { - "path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] + "interpolate_variables=False, path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] } }, "commands": [ diff --git a/tests/operations/ssh.upload/upload.json b/tests/operations/ssh.upload/upload.json index a4875d986..f36cf001c 100644 --- a/tests/operations/ssh.upload/upload.json +++ b/tests/operations/ssh.upload/upload.json @@ -12,7 +12,7 @@ } }, "files.FindInFile": { - "path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] + "interpolate_variables=False, path=/home/pyinfra/.ssh/known_hosts, pattern=remote-host.net": [true] } }, "commands": [ diff --git a/tests/operations/systemd.service/daemon_reload.json b/tests/operations/systemd.service/daemon_reload.json index 5b303f838..1f730203c 100644 --- a/tests/operations/systemd.service/daemon_reload.json +++ b/tests/operations/systemd.service/daemon_reload.json @@ -5,7 +5,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/disabled.json b/tests/operations/systemd.service/disabled.json index cfa3a35ba..454249c72 100644 --- a/tests/operations/systemd.service/disabled.json +++ b/tests/operations/systemd.service/disabled.json @@ -5,12 +5,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/enabled.json b/tests/operations/systemd.service/enabled.json index 416789f5a..00cddc6da 100644 --- a/tests/operations/systemd.service/enabled.json +++ b/tests/operations/systemd.service/enabled.json @@ -5,12 +5,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/machine_daemon_reload.json b/tests/operations/systemd.service/machine_daemon_reload.json index 365f0a9d8..7d6887a48 100644 --- a/tests/operations/systemd.service/machine_daemon_reload.json +++ b/tests/operations/systemd.service/machine_daemon_reload.json @@ -7,7 +7,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=testmachine, user_name=None, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/machine_disabled.json b/tests/operations/systemd.service/machine_disabled.json index 536175103..a5c653ed8 100644 --- a/tests/operations/systemd.service/machine_disabled.json +++ b/tests/operations/systemd.service/machine_disabled.json @@ -6,12 +6,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=testmachine, user_name=None, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=False, machine=testmachine, user_name=None, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/machine_enabled.json b/tests/operations/systemd.service/machine_enabled.json index 0d6b97939..d770b5456 100644 --- a/tests/operations/systemd.service/machine_enabled.json +++ b/tests/operations/systemd.service/machine_enabled.json @@ -6,12 +6,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=testmachine, user_name=None, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=False, machine=testmachine, user_name=None, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/machine_start.json b/tests/operations/systemd.service/machine_start.json index fa6d4672e..9dab7d9c7 100644 --- a/tests/operations/systemd.service/machine_start.json +++ b/tests/operations/systemd.service/machine_start.json @@ -6,7 +6,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=testmachine, user_name=None, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/running_ambiguous_unit.json b/tests/operations/systemd.service/running_ambiguous_unit.json index 444dc521c..bcf8d5638 100644 --- a/tests/operations/systemd.service/running_ambiguous_unit.json +++ b/tests/operations/systemd.service/running_ambiguous_unit.json @@ -2,7 +2,7 @@ "args": ["redis-server"], "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/running_noop.json b/tests/operations/systemd.service/running_noop.json index 69c50281b..ec228ed58 100644 --- a/tests/operations/systemd.service/running_noop.json +++ b/tests/operations/systemd.service/running_noop.json @@ -2,7 +2,7 @@ "args": ["redis-server.service"], "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/running_timer.json b/tests/operations/systemd.service/running_timer.json index 9d4552237..f83949170 100644 --- a/tests/operations/systemd.service/running_timer.json +++ b/tests/operations/systemd.service/running_timer.json @@ -2,7 +2,7 @@ "args": ["some-timer.timer"], "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['some-timer.timer']": { + "machine=None, services=['some-timer.timer'], user_mode=False, user_name=None": { "some-timer.timer": false } } diff --git a/tests/operations/systemd.service/start.json b/tests/operations/systemd.service/start.json index d16e8ed0c..680de98fc 100644 --- a/tests/operations/systemd.service/start.json +++ b/tests/operations/systemd.service/start.json @@ -2,7 +2,7 @@ "args": ["redis-server.service"], "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/stopped_noop.json b/tests/operations/systemd.service/stopped_noop.json index 9e8ed98ac..e66e17be4 100644 --- a/tests/operations/systemd.service/stopped_noop.json +++ b/tests/operations/systemd.service/stopped_noop.json @@ -5,7 +5,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=False, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/timer.json b/tests/operations/systemd.service/timer.json index 3805d7402..6fc720a78 100644 --- a/tests/operations/systemd.service/timer.json +++ b/tests/operations/systemd.service/timer.json @@ -2,7 +2,7 @@ "args": ["logrotate.timer"], "facts": { "systemd.SystemdStatus": { - "user_mode=False, machine=None, user_name=None, services=['logrotate.timer']": { + "machine=None, services=['logrotate.timer'], user_mode=False, user_name=None": { "logrotate.timer": false } } diff --git a/tests/operations/systemd.service/user_daemon_reload.json b/tests/operations/systemd.service/user_daemon_reload.json index f1e38e5d5..3525af285 100644 --- a/tests/operations/systemd.service/user_daemon_reload.json +++ b/tests/operations/systemd.service/user_daemon_reload.json @@ -6,7 +6,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=True, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/user_disabled.json b/tests/operations/systemd.service/user_disabled.json index 8f1c0f7ee..efe50fa74 100644 --- a/tests/operations/systemd.service/user_disabled.json +++ b/tests/operations/systemd.service/user_disabled.json @@ -6,12 +6,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=True, user_name=None": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=True, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=True, user_name=None": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/user_enabled.json b/tests/operations/systemd.service/user_enabled.json index 46d73b652..ddde7994f 100644 --- a/tests/operations/systemd.service/user_enabled.json +++ b/tests/operations/systemd.service/user_enabled.json @@ -6,12 +6,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=True, user_name=None": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=True, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=True, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/user_machine_daemon_reload.json b/tests/operations/systemd.service/user_machine_daemon_reload.json index 6f9b81422..8f26776c8 100644 --- a/tests/operations/systemd.service/user_machine_daemon_reload.json +++ b/tests/operations/systemd.service/user_machine_daemon_reload.json @@ -8,7 +8,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=testmachine, user_name=testuser, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=True, user_name=testuser": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/user_machine_disabled.json b/tests/operations/systemd.service/user_machine_disabled.json index 75322843f..4591143ee 100644 --- a/tests/operations/systemd.service/user_machine_disabled.json +++ b/tests/operations/systemd.service/user_machine_disabled.json @@ -8,12 +8,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=testmachine, user_name=testuser, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=True, user_name=testuser": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=True, machine=testmachine, user_name=testuser, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=True, user_name=testuser": { "redis-server.service": true } } diff --git a/tests/operations/systemd.service/user_machine_enabled.json b/tests/operations/systemd.service/user_machine_enabled.json index 6e176b790..a23f33cc1 100644 --- a/tests/operations/systemd.service/user_machine_enabled.json +++ b/tests/operations/systemd.service/user_machine_enabled.json @@ -8,12 +8,12 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=testmachine, user_name=testuser, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=True, user_name=testuser": { "redis-server.service": true } }, "systemd.SystemdEnabled": { - "user_mode=True, machine=testmachine, user_name=testuser, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=True, user_name=testuser": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/user_machine_start.json b/tests/operations/systemd.service/user_machine_start.json index 0372346d6..2add8387b 100644 --- a/tests/operations/systemd.service/user_machine_start.json +++ b/tests/operations/systemd.service/user_machine_start.json @@ -7,7 +7,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=testmachine, user_name=testuser, services=['redis-server.service']": { + "machine=testmachine, services=['redis-server.service'], user_mode=True, user_name=testuser": { "redis-server.service": false } } diff --git a/tests/operations/systemd.service/user_start.json b/tests/operations/systemd.service/user_start.json index 8961d218f..74df03e35 100644 --- a/tests/operations/systemd.service/user_start.json +++ b/tests/operations/systemd.service/user_start.json @@ -5,7 +5,7 @@ }, "facts": { "systemd.SystemdStatus": { - "user_mode=True, machine=None, user_name=None, services=['redis-server.service']": { + "machine=None, services=['redis-server.service'], user_mode=True, user_name=None": { "redis-server.service": false } } diff --git a/tests/operations/upstart.service/disabled.json b/tests/operations/upstart.service/disabled.json index 01977640e..493c5aa1d 100644 --- a/tests/operations/upstart.service/disabled.json +++ b/tests/operations/upstart.service/disabled.json @@ -11,7 +11,8 @@ "path=/etc/init/redis-server.override": null }, "files.Directory": { - "path=/etc/init": {"mode": 777} + "path=/etc/init": {"mode": 777}, + "path=/etc/init/redis-server.override": null } }, "commands": [ diff --git a/tests/operations/yum.repo/add.json b/tests/operations/yum.repo/add.json index f17e23eeb..e4c932e9f 100644 --- a/tests/operations/yum.repo/add.json +++ b/tests/operations/yum.repo/add.json @@ -11,7 +11,8 @@ "path=/etc/yum.repos.d/somerepo.repo": {} }, "files.Directory": { - "path=/etc/yum.repos.d": true + "path=/etc/yum.repos.d": true, + "path=/etc/yum.repos.d/somerepo.repo": null } }, "commands": [[ diff --git a/tests/operations/zfs.dataset/create.json b/tests/operations/zfs.dataset/create.json new file mode 100644 index 000000000..e0853d7a6 --- /dev/null +++ b/tests/operations/zfs.dataset/create.json @@ -0,0 +1,18 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": true, + "mountpoint": "/home/james", + "properties": { + "com.sun:auto_snapshot": "true" + } + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [ + "zfs create -o com.sun:auto_snapshot=true -o mountpoint=/home/james tank/home/james" + ] +} diff --git a/tests/operations/zfs.dataset/create_noop.json b/tests/operations/zfs.dataset/create_noop.json new file mode 100644 index 000000000..f85c29654 --- /dev/null +++ b/tests/operations/zfs.dataset/create_noop.json @@ -0,0 +1,18 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": true, + "compression": "lz4" + }, + "facts": { + "zfs.Datasets": { + "tank/home/james": { + "compression": "lz4" + } + } + }, + "commands": [], + "noop_description": "tank/home/james is already present" +} diff --git a/tests/operations/zfs.dataset/create_recursive.json b/tests/operations/zfs.dataset/create_recursive.json new file mode 100644 index 000000000..bdb00334a --- /dev/null +++ b/tests/operations/zfs.dataset/create_recursive.json @@ -0,0 +1,16 @@ +{ + "args": [ + "tank/myzvol" + ], + "kwargs": { + "present": true, + "recursive": true, + "compression": "lz4" + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [ + "zfs create -o compression=lz4 -p tank/myzvol" + ] +} diff --git a/tests/operations/zfs.dataset/create_sparse.json b/tests/operations/zfs.dataset/create_sparse.json new file mode 100644 index 000000000..4929add40 --- /dev/null +++ b/tests/operations/zfs.dataset/create_sparse.json @@ -0,0 +1,17 @@ +{ + "args": [ + "tank/myzvol" + ], + "kwargs": { + "present": true, + "sparse": true, + "compression": "lz4", + "volume_size": "5G" + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [ + "zfs create -V 5G -o compression=lz4 -s tank/myzvol" + ] +} diff --git a/tests/operations/zfs.dataset/create_volume.json b/tests/operations/zfs.dataset/create_volume.json new file mode 100644 index 000000000..7b0bb0bae --- /dev/null +++ b/tests/operations/zfs.dataset/create_volume.json @@ -0,0 +1,16 @@ +{ + "args": [ + "tank/myzvol" + ], + "kwargs": { + "present": true, + "compression": "lz4", + "volume_size": "5G" + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [ + "zfs create -V 5G -o compression=lz4 tank/myzvol" + ] +} diff --git a/tests/operations/zfs.dataset/delete.json b/tests/operations/zfs.dataset/delete.json new file mode 100644 index 000000000..051027aed --- /dev/null +++ b/tests/operations/zfs.dataset/delete.json @@ -0,0 +1,18 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": false + }, + "facts": { + "zfs.Datasets": { + "tank/home/james": { + "mountpoint": "/home/james" + } + } + }, + "commands": [ + "zfs destroy tank/home/james" + ] +} diff --git a/tests/operations/zfs.dataset/delete_noop.json b/tests/operations/zfs.dataset/delete_noop.json new file mode 100644 index 000000000..b7391b6a3 --- /dev/null +++ b/tests/operations/zfs.dataset/delete_noop.json @@ -0,0 +1,13 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": false + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [], + "noop_description": "tank/home/james is already absent" +} diff --git a/tests/operations/zfs.dataset/delete_recursive.json b/tests/operations/zfs.dataset/delete_recursive.json new file mode 100644 index 000000000..126bdeee7 --- /dev/null +++ b/tests/operations/zfs.dataset/delete_recursive.json @@ -0,0 +1,22 @@ +{ + "args": [ + "tank/home" + ], + "kwargs": { + "present": false, + "recursive": true + }, + "facts": { + "zfs.Datasets": { + "tank/home": { + "mountpoint": "/home" + }, + "tank/home/james": { + "mountpoint": "/home/james" + } + } + }, + "commands": [ + "zfs destroy -r tank/home" + ] +} diff --git a/tests/operations/zfs.dataset/set_props.json b/tests/operations/zfs.dataset/set_props.json new file mode 100644 index 000000000..154498428 --- /dev/null +++ b/tests/operations/zfs.dataset/set_props.json @@ -0,0 +1,25 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": true, + "mountpoint": "/home/james", + "compression": "lz4", + "properties": { + "com.sun:auto_snapshot": "true" + } + }, + "facts": { + "zfs.Datasets": { + "tank/home/james": { + "compression": "zstd", + "mountpoint": "/home/james", + "com.sun:auto_snapshot": "false" + } + } + }, + "commands": [ + "zfs set com.sun:auto_snapshot=true compression=lz4 tank/home/james" + ] +} diff --git a/tests/operations/zfs.filesystem/create.json b/tests/operations/zfs.filesystem/create.json new file mode 100644 index 000000000..e0853d7a6 --- /dev/null +++ b/tests/operations/zfs.filesystem/create.json @@ -0,0 +1,18 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": true, + "mountpoint": "/home/james", + "properties": { + "com.sun:auto_snapshot": "true" + } + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [ + "zfs create -o com.sun:auto_snapshot=true -o mountpoint=/home/james tank/home/james" + ] +} diff --git a/tests/operations/zfs.filesystem/delete.json b/tests/operations/zfs.filesystem/delete.json new file mode 100644 index 000000000..051027aed --- /dev/null +++ b/tests/operations/zfs.filesystem/delete.json @@ -0,0 +1,18 @@ +{ + "args": [ + "tank/home/james" + ], + "kwargs": { + "present": false + }, + "facts": { + "zfs.Datasets": { + "tank/home/james": { + "mountpoint": "/home/james" + } + } + }, + "commands": [ + "zfs destroy tank/home/james" + ] +} diff --git a/tests/operations/zfs.snapshot/create.json b/tests/operations/zfs.snapshot/create.json new file mode 100644 index 000000000..4e775e999 --- /dev/null +++ b/tests/operations/zfs.snapshot/create.json @@ -0,0 +1,17 @@ +{ + "args": [ + "tank/home/james@2024-02-29" + ], + "kwargs": { + "present": true, + "properties": { + "com.example:created_timestamp": "1583020799" + } + }, + "facts": { + "zfs.Snapshots": {} + }, + "commands": [ + "zfs snap -o com.example:created_timestamp=1583020799 tank/home/james@2024-02-29" + ] +} diff --git a/tests/operations/zfs.volume/create.json b/tests/operations/zfs.volume/create.json new file mode 100644 index 000000000..0364a74cf --- /dev/null +++ b/tests/operations/zfs.volume/create.json @@ -0,0 +1,15 @@ +{ + "args": [ + "tank/myzvol", "5G" + ], + "kwargs": { + "present": true, + "compression": "lz4" + }, + "facts": { + "zfs.Datasets": {} + }, + "commands": [ + "zfs create -V 5G -o compression=lz4 tank/myzvol" + ] +} diff --git a/tests/operations/zypper.repo/add.json b/tests/operations/zypper.repo/add.json index aece3c369..ec3f37d56 100644 --- a/tests/operations/zypper.repo/add.json +++ b/tests/operations/zypper.repo/add.json @@ -10,7 +10,8 @@ "path=/etc/zypp/repos.d/somerepo.repo": {} }, "files.Directory": { - "path=/etc/zypp/repos.d": true + "path=/etc/zypp/repos.d": true, + "path=/etc/zypp/repos.d/somerepo.repo": null } }, "commands": [[ diff --git a/tests/test_api/test_api_inventory.py b/tests/test_api/test_api_inventory.py index 8e132bc7b..f441155ec 100644 --- a/tests/test_api/test_api_inventory.py +++ b/tests/test_api/test_api_inventory.py @@ -51,3 +51,24 @@ def test_create_inventory_override_data(self): assert inventory.get_host("somehost").data.override_data == "override_data" assert inventory.get_host("anotherhost").data.override_data == "override_data" + + def test_inventory_group_data_not_shared(self): + group_data = {"test": {}} + hosts = ["hosthost", "anotherhost"] + + inventory = Inventory( + (hosts, {}), + group=(hosts, group_data), + ) + + hosthost = inventory.get_host("hosthost") + + # Test that modifying host.data. *does not* stick (both on the same + # host and also other hosts). + hosthost.data.test["hi"] = "no" + assert hosthost.data.test == {} + assert inventory.get_host("anotherhost").data.test == {} + + # Test that setting host.data. *does* persist + hosthost.data.somethingelse = {"hello": "world"} + assert hosthost.data.somethingelse == {"hello": "world"} diff --git a/tests/test_cli/test_cli_exceptions.py b/tests/test_cli/test_cli_exceptions.py index f6fb4f733..7cac5f15a 100644 --- a/tests/test_cli/test_cli_exceptions.py +++ b/tests/test_cli/test_cli_exceptions.py @@ -38,13 +38,13 @@ def test_invalid_fact(self): def test_no_fact_module(self): self.assert_cli_exception( ["my-server.net", "fact", "not_a_module.SomeFact"], - "No such module: pyinfra.facts.not_a_module", + "No such module: not_a_module", ) def test_no_fact_cls(self): self.assert_cli_exception( ["my-server.net", "fact", "server.NotAFact"], - "No such attribute in module pyinfra.facts.server: NotAFact", + "No such attribute in module server: NotAFact", ) diff --git a/tests/test_cli/test_cli_util.py b/tests/test_cli/test_cli_util.py index b1ddadda5..776510f14 100644 --- a/tests/test_cli/test_cli_util.py +++ b/tests/test_cli/test_cli_util.py @@ -30,15 +30,13 @@ def test_json_encode_set(self): def test_setup_no_module(self): with self.assertRaises(CliError) as context: get_func_and_args(("no.op",)) - assert context.exception.message == "No such module: pyinfra.operations.no" + assert context.exception.message == "No such module: no" def test_setup_no_op(self): with self.assertRaises(CliError) as context: get_func_and_args(("server.no",)) - assert ( - context.exception.message == "No such attribute in module pyinfra.operations.server: no" - ) + assert context.exception.message == "No such attribute in module server: no" def test_setup_op_and_args(self): commands = ("pyinfra.operations.server.user", "one", "two", "hello=world") diff --git a/tests/test_connectors/test_terraform.py b/tests/test_connectors/test_terraform.py index 8a634e8c5..c072c72ab 100644 --- a/tests/test_connectors/test_terraform.py +++ b/tests/test_connectors/test_terraform.py @@ -20,9 +20,9 @@ def test_make_names_data_no_output(self, fake_shell): with self.assertRaises(InventoryError) as context: list(TerraformInventoryConnector.make_names_data("output_key")) - assert ( - context.exception.args[0] - == "No Terraform output with key: `output_key`, valid keys:\n - hello.world" + assert context.exception.args[0] == ( + "No Terraform output with key: `output_key`, " + "valid keys:\n - hello\n - hello.world" ) @patch("pyinfra.connectors.terraform.local.shell") @@ -58,7 +58,7 @@ def test_make_names_data(self, fake_shell): ( "@terraform/somehost", {"ssh_hostname": "somehost"}, - ["@terraform"], + ["@terraform", "all"], ), ] @@ -71,7 +71,7 @@ def test_make_names_data_nested(self, fake_shell): ( "@terraform/somehost", {"ssh_hostname": "somehost"}, - ["@terraform"], + ["@terraform", "all"], ), ] @@ -88,7 +88,7 @@ def test_make_names_data_dict(self, fake_shell): ( "@terraform/a name", {"ssh_hostname": "hostname"}, - ["@terraform"], + ["@terraform", "all"], ), ] diff --git a/tests/util.py b/tests/util.py index 82951ba4f..d3766a6d2 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,7 +1,7 @@ import json import os from datetime import datetime -from inspect import getfullargspec +from inspect import getcallargs, getfullargspec from io import open from os import listdir, path from pathlib import Path @@ -16,7 +16,7 @@ def get_command_string(command): masked_value = command.get_masked_value() if value == masked_value: return value - return [value, masked_value] + return {"raw": value, "masked": masked_value} def make_inventory(hosts=("somehost", "anotherhost"), **kwargs): @@ -194,24 +194,30 @@ def _get_fact_key(fact_cls): def _check_fact_args(fact_cls, kwargs): # Check that the arguments we're going to use to fake a fact are all actual arguments in # the fact class, otherwise the test will hide a bug in the underlying operation. - real_args = getfullargspec(fact_cls.command) + real_args = getfullargspec(fact_cls.command).args + for key in kwargs.keys(): assert ( - key in real_args.args + key in real_args ), f"Argument {key} is not a real argument in the `{fact_cls}.command` method" - def get_fact(self, fact_cls, **kwargs): + def get_fact(self, fact_cls, *args, **kwargs): fact_key = self._get_fact_key(fact_cls) fact = getattr(self.fact, fact_key, None) if fact is None: - raise KeyError("Missing test fact data: {0}".format(fact_key)) + raise KeyError(f"Missing test fact: {fact_key}") + + # This does the same thing that pyinfra.apit.facts._handle_fact_kwargs does + if args or kwargs: + # Merges args & kwargs into a single kwargs dictionary + kwargs = getcallargs(fact_cls().command, *args, **kwargs) + if kwargs: self._check_fact_args(fact_cls, kwargs) - fact_ordered_keys = {_sort_kwargs_str(key): value for key, value in fact.items()} - kwargs_str = _sort_kwargs_str(get_kwargs_str(kwargs)) + kwargs_str = get_kwargs_str(kwargs) if kwargs_str not in fact: - print("Possible missing fact key: {0}".format(kwargs_str)) - return fact_ordered_keys.get(kwargs_str) + raise KeyError(f"Missing test fact key: {fact_key} -> {kwargs_str}") + return fact.get(kwargs_str) return fact