From 6f1bdee9ee86129434beadfa9829a7cb1ed294fc Mon Sep 17 00:00:00 2001 From: Jacob Callahan Date: Wed, 12 Jul 2023 14:05:15 -0400 Subject: [PATCH] Going full into pyproject.toml Switch all relevant config files to pyproject.toml Also added in pre-commit checks for black and ruff Bumped out python version 3.9 Added in dependabot config for actions, since others are largely unpinned --- .github/dependabot.yml | 14 ++++ .github/workflows/codeql-analysis.yml | 14 ++-- .github/workflows/python-publish.yml | 4 +- .pre-commit-config.yaml | 12 +++ MANIFEST.in | 1 - broker/binds/beaker.py | 20 ++--- broker/binds/containers.py | 14 +--- broker/broker.py | 31 ++------ broker/commands.py | 49 ++++-------- broker/helpers.py | 26 ++----- broker/hosts.py | 12 +-- broker/logger.py | 7 +- broker/providers/__init__.py | 13 +--- broker/providers/ansible_tower.py | 103 +++++++------------------- broker/providers/beaker.py | 12 +-- broker/providers/container.py | 20 ++--- broker/session.py | 8 +- broker/settings.py | 12 ++- ruff.toml => pyproject.toml | 89 +++++++++++++++++++++- pytest.ini | 3 - setup.cfg | 53 ------------- setup.py | 4 - tests/functional/test_containers.py | 24 ++---- tests/functional/test_rh_beaker.py | 13 ++-- tests/functional/test_satlab.py | 16 ++-- tests/providers/test_beaker.py | 4 +- tests/providers/test_container.py | 4 +- tests/test_broker.py | 4 +- tests/test_helpers.py | 18 +---- tests/test_settings.py | 12 +-- 30 files changed, 232 insertions(+), 384 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .pre-commit-config.yaml delete mode 100644 MANIFEST.in rename ruff.toml => pyproject.toml (68%) delete mode 100644 pytest.ini delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0b3ba9da --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# This is the configuration file for Dependabot. You can find configuration information below. +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# Note: Dependabot has a configurable max open PR limit of 5 + +version: 2 +updates: + + # Maintain dependencies for our GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + labels: + - "dependencies" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bde2096e..19d0d428 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,11 +20,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11"] steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -35,14 +35,12 @@ jobs: uses: github/codeql-action/analyze@v2 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - name: Run Ruff Checks - uses: chartboost/ruff-action@v1 - with: - src: ./broker + - name: Pre Commit Checks + uses: pre-commit/action@v3.0.0 - name: Setup Temp Directory run: mkdir broker_dir @@ -52,7 +50,7 @@ jobs: BROKER_DIRECTORY: "${{ github.workspace }}/broker_dir" run: | pip install -U pip - pip install -U .[test,docker] + pip install -U .[dev,docker] ls -l "$BROKER_DIRECTORY" broker --version pytest -v tests/ --ignore tests/functional diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 127a33e5..e7062f93 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -2,7 +2,7 @@ name: PythonPackage on: push: - tags: + tags: - "*" jobs: @@ -12,7 +12,7 @@ jobs: strategy: matrix: # build/push in lowest support python version - python-version: [ 3.9 ] + python-version: [ 3.10 ] steps: - uses: actions/checkout@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..218e7286 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +# configuration for pre-commit git hooks +repos: +- repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.0.277 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bb3ec5f0..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include README.md diff --git a/broker/binds/beaker.py b/broker/binds/beaker.py index dc7152a5..ba616c18 100644 --- a/broker/binds/beaker.py +++ b/broker/binds/beaker.py @@ -92,9 +92,7 @@ def _exec_command(self, *cmd_args, **cmd_kwargs): ) if result.status != 0 and raise_on_error: raise BeakerBindError( - f"Beaker command failed:\n" - f"Command={' '.join(exec_cmd)}\n" - f"Result={result}", + f"Beaker command failed:\nCommand={' '.join(exec_cmd)}\nResult={result}", ) logger.debug(f"Beaker command result: {result.stdout}") return result @@ -120,9 +118,7 @@ def job_watch(self, job_id): def job_results(self, job_id, format="beaker-results-xml", pretty=False): """Get the results of a job in the specified format.""" job_id = f"J:{job_id}" if not job_id.startswith("J:") else job_id - return self._exec_command( - "job-results", job_id, format=format, prettyxml=pretty - ) + return self._exec_command("job-results", job_id, format=format, prettyxml=pretty) def job_clone(self, job_id, wait=False, **kwargs): """Clone a job by the specified job id.""" @@ -171,9 +167,7 @@ def system_list(self, **kwargs): """ # convert the flags passed in kwargs to arguments args = [ - f"--{key}" - for key in ("available", "free", "removed", "mine") - if kwargs.pop(key, False) + f"--{key}" for key in ("available", "free", "removed", "mine") if kwargs.pop(key, False) ] return self._exec_command("system-list", *args, **kwargs) @@ -207,9 +201,7 @@ def execute_job(self, job, max_wait="24h"): time.sleep(60) result = self.job_results(job_id, pretty=True) if 'result="Pass"' in result.stdout: - return _curate_job_info( - _elementree_to_dict(ET.fromstring(result.stdout)) - ) + return _curate_job_info(_elementree_to_dict(ET.fromstring(result.stdout))) elif 'result="Fail"' in result.stdout or "Exception: " in result.stdout: raise BeakerBindError(f"Job {job_id} failed:\n{result}") elif 'result="Warn"' in result.stdout: @@ -249,8 +241,6 @@ def jobid_from_system(self, system_hostname): """Return the job id for the current reservation on the system.""" for job_id in json.loads(self.job_list(mine=True).stdout): job_result = self.job_results(job_id, pretty=True) - job_detail = _curate_job_info( - _elementree_to_dict(ET.fromstring(job_result.stdout)) - ) + job_detail = _curate_job_info(_elementree_to_dict(ET.fromstring(job_result.stdout))) if job_detail["hostname"] == system_hostname: return job_id diff --git a/broker/binds/containers.py b/broker/binds/containers.py index 061f3547..cf291e1c 100644 --- a/broker/binds/containers.py +++ b/broker/binds/containers.py @@ -39,9 +39,7 @@ def image_info(self, name): "id": image.short_id, "tags": image.tags, "size": image.attrs["Size"], - "config": { - k: v for k, v in image.attrs["Config"].items() if k != "Env" - }, + "config": {k: v for k, v in image.attrs["Config"].items() if k != "Env"}, } def create_container(self, image, command=None, **kwargs): @@ -51,9 +49,7 @@ def create_container(self, image, command=None, **kwargs): def execute(self, image, command=None, remove=True, **kwargs): """Run a container and return the raw result.""" - return self.client.containers.run( - image, command=command, remove=remove, **kwargs - ).decode() + return self.client.containers.run(image, command=command, remove=remove, **kwargs).decode() def remove_container(self, container=None): """Remove a container from the container host.""" @@ -102,11 +98,7 @@ def __init__(self, **kwargs): if self.host == "localhost": self.uri = "unix:///run/user/1000/podman/podman.sock" else: - self.uri = ( - "http+ssh://{username}@{host}:{port}/run/podman/podman.sock".format( - **kwargs - ) - ) + self.uri = "http+ssh://{username}@{host}:{port}/run/podman/podman.sock".format(**kwargs) def _sanitize_create_args(self, kwargs): from podman.domain.containers_create import CreateMixin diff --git a/broker/broker.py b/broker/broker.py index 4819f002..da5374b2 100644 --- a/broker/broker.py +++ b/broker/broker.py @@ -69,8 +69,7 @@ def mp_split(*args, **kwargs): max_workers_count = self.MAX_WORKERS or count with self.EXECUTOR(max_workers=max_workers_count) as workers: completed_futures = as_completed( - workers.submit(self.func, instance, *args, **kwargs) - for _ in range(count) + workers.submit(self.func, instance, *args, **kwargs) for _ in range(count) ) for f in completed_futures: results.extend(f.result()) @@ -122,9 +121,7 @@ def _act(self, provider, method, checkout=False): } ) method_obj = getattr(provider_inst, method) - logger.debug( - f"On {provider_inst=} executing {method_obj=} with params {self._kwargs=}." - ) + logger.debug(f"On {provider_inst=} executing {method_obj=} with params {self._kwargs=}.") result = method_obj(**self._kwargs) logger.debug(f"Action {result=}") if result and checkout: @@ -238,9 +235,7 @@ def checkin(self, sequential=False, host=None, in_context=False): hosts = [hosts] if in_context: hosts = [ - host - for host in hosts - if not getattr(host, "_skip_context_checkin", False) + host for host in hosts if not getattr(host, "_skip_context_checkin", False) ] if not hosts: logger.debug("Checkin called with no hosts, taking no action") @@ -255,9 +250,7 @@ def checkin(self, sequential=False, host=None, in_context=False): for completed in completed_checkins: _host = completed.result() self._hosts = [h for h in self._hosts if h.to_dict() != _host.to_dict()] - logger.debug( - f"Completed checkin process for {_host.hostname or _host.name}" - ) + logger.debug(f"Completed checkin process for {_host.hostname or _host.name}") helpers.update_inventory(remove=[h.hostname for h in hosts]) def _extend(self, host): @@ -297,9 +290,7 @@ def extend(self, sequential=False, host=None): return with ThreadPoolExecutor(max_workers=1 if sequential else len(hosts)) as workers: - completed_extends = as_completed( - workers.submit(self._extend, _host) for _host in hosts - ) + completed_extends = as_completed(workers.submit(self._extend, _host) for _host in hosts) for completed in completed_extends: _host = completed.result() logger.info(f"Completed extend for {_host.hostname or _host.name}") @@ -320,9 +311,7 @@ def sync_inventory(provider): prov_inventory = PROVIDERS[provider](**instance).get_inventory(additional_arg) curr_inventory = [ hostname if (hostname := host.get("hostname")) else host.get("name") - for host in helpers.load_inventory( - filter=f'@inv._broker_provider == "{provider}"' - ) + for host in helpers.load_inventory(filter=f'@inv._broker_provider == "{provider}"') ] helpers.update_inventory(add=prov_inventory, remove=curr_inventory) @@ -390,18 +379,14 @@ def multi_manager(cls, **multi_dict): all_hosts.extend(broker_inst._hosts) # run setup on all hosts in parallel with ThreadPoolExecutor(max_workers=len(all_hosts)) as workers: - completed_setups = as_completed( - workers.submit(host.setup) for host in all_hosts - ) + completed_setups = as_completed(workers.submit(host.setup) for host in all_hosts) for completed in completed_setups: completed.result() # yield control to the user yield {name: broker._hosts for name, broker in broker_instances.items()} # teardown all hosts in parallel with ThreadPoolExecutor(max_workers=len(all_hosts)) as workers: - completed_teardowns = as_completed( - workers.submit(host.teardown) for host in all_hosts - ) + completed_teardowns = as_completed(workers.submit(host.teardown) for host in all_hosts) for completed in completed_teardowns: completed.result() # checkin all hosts in parallel diff --git a/broker/commands.py b/broker/commands.py index d706eb41..42579096 100644 --- a/broker/commands.py +++ b/broker/commands.py @@ -165,9 +165,9 @@ def cli(version): import requests latest_version = Version( - requests.get("https://pypi.org/pypi/broker/json", timeout=60).json()[ - "info" - ]["version"] + requests.get("https://pypi.org/pypi/broker/json", timeout=60).json()["info"][ + "version" + ] ) if latest_version > Version(broker_version): click.secho( @@ -187,9 +187,7 @@ def cli(version): @loggedcli(context_settings={"allow_extra_args": True, "ignore_unknown_options": True}) @click.option("-b", "--background", is_flag=True, help="Run checkout in the background") @click.option("-n", "--nick", type=str, help="Use a nickname defined in your settings") -@click.option( - "-c", "--count", type=int, help="Number of times broker repeats the checkout" -) +@click.option("-c", "--count", type=int, help="Number of times broker repeats the checkout") @click.option( "--args-file", type=click.Path(exists=True), @@ -238,9 +236,7 @@ def providers(): @click.option("-b", "--background", is_flag=True, help="Run checkin in the background") @click.option("--all", "all_", is_flag=True, help="Select all VMs") @click.option("--sequential", is_flag=True, help="Run checkins sequentially") -@click.option( - "--filter", type=str, help="Checkin only what matches the specified filter" -) +@click.option("--filter", type=str, help="Checkin only what matches the specified filter") def checkin(vm, background, all_, sequential, filter): """Checkin or "remove" a VM or series of VM broker instances. @@ -251,12 +247,7 @@ def checkin(vm, background, all_, sequential, filter): inventory = helpers.load_inventory(filter=filter) to_remove = [] for num, host in enumerate(inventory): - if ( - str(num) in vm - or host.get("hostname") in vm - or host.get("name") in vm - or all_ - ): + if str(num) in vm or host.get("hostname") in vm or host.get("name") in vm or all_: to_remove.append(Broker().reconstruct_host(host)) Broker(hosts=to_remove).checkin(sequential=sequential) @@ -268,9 +259,7 @@ def checkin(vm, background, all_, sequential, filter): type=str, help="Class-style name of a supported broker provider. (AnsibleTower)", ) -@click.option( - "--filter", type=str, help="Display only what matches the specified filter" -) +@click.option("--filter", type=str, help="Display only what matches the specified filter") def inventory(details, sync, filter): """Get a list of all VMs you've checked out showing hostname and local id. @@ -297,9 +286,7 @@ def inventory(details, sync, filter): @click.option("-b", "--background", is_flag=True, help="Run extend in the background") @click.option("--all", "all_", is_flag=True, help="Select all VMs") @click.option("--sequential", is_flag=True, help="Run extends sequentially") -@click.option( - "--filter", type=str, help="Extend only what matches the specified filter" -) +@click.option("--filter", type=str, help="Extend only what matches the specified filter") @provider_options def extend(vm, background, all_, sequential, filter, **kwargs): """Extend a host's lease time. @@ -319,16 +306,10 @@ def extend(vm, background, all_, sequential, filter, **kwargs): @loggedcli() @click.argument("vm", type=str, nargs=-1) -@click.option( - "-b", "--background", is_flag=True, help="Run duplicate in the background" -) -@click.option( - "-c", "--count", type=int, help="Number of times broker repeats the duplicate" -) +@click.option("-b", "--background", is_flag=True, help="Run duplicate in the background") +@click.option("-c", "--count", type=int, help="Number of times broker repeats the duplicate") @click.option("--all", "all_", is_flag=True, help="Select all VMs") -@click.option( - "--filter", type=str, help="Duplicate only what matches the specified filter" -) +@click.option("--filter", type=str, help="Duplicate only what matches the specified filter") def duplicate(vm, background, count, all_, filter): """Duplicate a broker-procured vm. @@ -347,17 +328,13 @@ def duplicate(vm, background, count, all_, filter): broker_inst = Broker(**broker_args) broker_inst.checkout() else: - logger.warning( - f"Unable to duplicate {host['hostname']}, no _broker_args found" - ) + logger.warning(f"Unable to duplicate {host['hostname']}, no _broker_args found") @loggedcli(context_settings={"allow_extra_args": True, "ignore_unknown_options": True}) @click.option("-b", "--background", is_flag=True, help="Run execute in the background") @click.option("--nick", type=str, help="Use a nickname defined in your settings") -@click.option( - "--output-format", "-o", type=click.Choice(["log", "raw", "yaml"]), default="log" -) +@click.option("--output-format", "-o", type=click.Choice(["log", "raw", "yaml"]), default="log") @click.option( "--artifacts", type=click.Choice(["merge", "last"]), diff --git a/broker/helpers.py b/broker/helpers.py index ee96f0c7..51124ba8 100644 --- a/broker/helpers.py +++ b/broker/helpers.py @@ -126,18 +126,14 @@ def dict_from_paths(source_dict, paths): def eval_filter(filter_list, raw_filter, filter_key="inv"): """Run each filter through an eval to get the results.""" - filter_list = [ - MockStub(item) if isinstance(item, dict) else item for item in filter_list - ] + filter_list = [MockStub(item) if isinstance(item, dict) else item for item in filter_list] for raw_f in raw_filter.split("|"): if f"@{filter_key}[" in raw_f: # perform a list filter on the inventory filter_list = eval( # noqa: S307 raw_f.replace(f"@{filter_key}", filter_key), {filter_key: filter_list} ) - filter_list = ( - filter_list if isinstance(filter_list, list) else [filter_list] - ) + filter_list = filter_list if isinstance(filter_list, list) else [filter_list] elif f"@{filter_key}" in raw_f: # perform an attribute filter on each host filter_list = list( @@ -189,9 +185,7 @@ def resolve_file_args(broker_args): final_args = {} # parse the eventual args_file first if val := broker_args.pop("args_file", None): - if isinstance(val, Path) or ( - isinstance(val, str) and val[-4:] in ("json", "yaml", ".yml") - ): + if isinstance(val, Path) or (isinstance(val, str) and val[-4:] in ("json", "yaml", ".yml")): if data := load_file(val): if isinstance(data, dict): final_args.update(data) @@ -202,9 +196,7 @@ def resolve_file_args(broker_args): raise exceptions.BrokerError(f"No data loaded from {val}") for key, val in broker_args.items(): - if isinstance(val, Path) or ( - isinstance(val, str) and val[-4:] in ("json", "yaml", ".yml") - ): + if isinstance(val, Path) or (isinstance(val, str) and val[-4:] in ("json", "yaml", ".yml")): if data := load_file(val): final_args.update({key: data}) else: @@ -374,11 +366,7 @@ def __hash__(self): The hash value is computed using the hash value of all hashable attributes of the object. """ return hash( - tuple( - kp - for kp in self.__dict__.items() - if isinstance(kp[1], collections.abc.Hashable) - ) + tuple(kp for kp in self.__dict__.items() if isinstance(kp[1], collections.abc.Hashable)) ) @@ -518,9 +506,7 @@ def find_origin(): """ prev, jenkins_url = None, os.environ.get("BUILD_URL") for frame in inspect.stack(): - if frame.function == "checkout" and frame.filename.endswith( - "broker/commands.py" - ): + if frame.function == "checkout" and frame.filename.endswith("broker/commands.py"): return f"broker_cli:{getpass.getuser()}", jenkins_url if frame.function.startswith("test_"): return f"{frame.function}:{frame.filename}", jenkins_url diff --git a/broker/hosts.py b/broker/hosts.py index 565fec64..d45fcb94 100644 --- a/broker/hosts.py +++ b/broker/hosts.py @@ -50,17 +50,13 @@ def __init__(self, **kwargs): import inspect if any(f.function == "reconstruct_host" for f in inspect.stack()): - logger.debug( - "Ignoring missing hostname and ip for checkin reconstruction." - ) + logger.debug("Ignoring missing hostname and ip for checkin reconstruction.") else: raise HostError("Host must be constructed with a hostname or ip") self.name = kwargs.pop("name", None) self.username = kwargs.pop("username", settings.HOST_USERNAME) self.password = kwargs.pop("password", settings.HOST_PASSWORD) - self.timeout = kwargs.pop( - "connection_timeout", settings.HOST_CONNECTION_TIMEOUT - ) + self.timeout = kwargs.pop("connection_timeout", settings.HOST_CONNECTION_TIMEOUT) self.port = kwargs.pop("port", settings.HOST_SSH_PORT) self.key_filename = kwargs.pop("key_filename", settings.HOST_SSH_KEY_FILENAME) self.__dict__.update(kwargs) # Make every other kwarg an attribute @@ -88,9 +84,7 @@ def session(self): self.connect() return self._session - def connect( - self, username=None, password=None, timeout=None, port=22, key_filename=None - ): + def connect(self, username=None, password=None, timeout=None, port=22, key_filename=None): """Connect to the host using SSH. Args: diff --git a/broker/logger.py b/broker/logger.py index 3ebb1b93..fedf2abc 100644 --- a/broker/logger.py +++ b/broker/logger.py @@ -67,9 +67,7 @@ def patch(cls, name): func = getattr(cls, name) def the_patch(self, *args, **kwargs): - awx_log.log( - LOG_LEVEL.TRACE.value, f"Calling {self=} {func=}(*{args=}, **{kwargs=}" - ) + awx_log.log(LOG_LEVEL.TRACE.value, f"Calling {self=} {func=}(*{args=}, **{kwargs=}") retval = func(self, *args, **kwargs) awx_log.log( LOG_LEVEL.TRACE.value, @@ -96,8 +94,7 @@ def formatter_factory(log_level, color=True): """Create a logzero formatter based on the log level.""" log_fmt = "%(color)s[%(levelname)s %(asctime)s]%(end_color)s %(message)s" debug_fmt = ( - "%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]" - "%(end_color)s %(message)s" + "%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s" ) formatter = logzero.LogFormatter( fmt=debug_fmt if log_level <= LOG_LEVEL.DEBUG else log_fmt, color=color diff --git a/broker/providers/__init__.py b/broker/providers/__init__.py index 2540d2bd..a84bbcdb 100644 --- a/broker/providers/__init__.py +++ b/broker/providers/__init__.py @@ -42,9 +42,7 @@ def get_inventory(self, **inventory_opts): # populate a list of all provider module names _provider_imports = [ - f.stem - for f in Path(__file__).parent.glob("*.py") - if f.is_file() and f.stem != "__init__" + f.stem for f in Path(__file__).parent.glob("*.py") if f.is_file() and f.stem != "__init__" ] # ProviderName: ProviderClassObject @@ -74,9 +72,7 @@ def __new__(cls, name, bases, attrs): new_cls, isinstance(param.default, bool), ) - logger.debug( - f"Registered help option {name} for provider {name}" - ) + logger.debug(f"Registered help option {name} for provider {name}") elif hasattr(obj, "_as_action"): for action in obj._as_action: PROVIDER_ACTIONS[action] = (new_cls, attr) @@ -139,10 +135,7 @@ def _validate_settings(self, instance_name=None): if instance_name in candidate: instance = candidate break - elif ( - candidate.values()[0].get("default") - or len(fresh_settings.instances) == 1 - ): + elif candidate.values()[0].get("default") or len(fresh_settings.instances) == 1: instance = candidate self.instance, *_ = instance # store the instance name on the provider fresh_settings.update(inst_vals := instance.values()[0]) diff --git a/broker/providers/ansible_tower.py b/broker/providers/ansible_tower.py index a67918a9..1b17f01d 100644 --- a/broker/providers/ansible_tower.py +++ b/broker/providers/ansible_tower.py @@ -26,9 +26,7 @@ @cache -def get_awxkit_and_uname( - config=None, root=None, url=None, token=None, uname=None, pword=None -): +def get_awxkit_and_uname(config=None, root=None, url=None, token=None, uname=None, pword=None): """Return an awxkit api object and resolved username.""" # Prefer token if its set, otherwise use username/password # auth paths for the API taken from: @@ -43,9 +41,7 @@ def get_awxkit_and_uname( logger.info("Using token authentication") config.token = token try: - root.connection.login( - username=None, password=None, token=token, auth_type="Bearer" - ) + root.connection.login(username=None, password=None, token=token, auth_type="Bearer") except awxkit.exceptions.Unauthorized as err: raise exceptions.AuthenticationError(err.args[0]) from err versions = root.get().available_versions @@ -109,9 +105,7 @@ class AnsibleTower(Provider): type=str, help="AnsibleTower inventory to checkout a host on", ), - click.option( - "--workflow", type=str, help="Name of a workflow used to checkout a host" - ), + click.option("--workflow", type=str, help="Name of a workflow used to checkout a host"), ] _execute_options = [ click.option( @@ -120,9 +114,7 @@ class AnsibleTower(Provider): help="AnsibleTower inventory to execute against", ), click.option("--workflow", type=str, help="Name of a workflow to execute"), - click.option( - "--job-template", type=str, help="Name of a job template to execute" - ), + click.option("--job-template", type=str, help="Name of a job template to execute"), ] _extend_options = [ click.option( @@ -148,9 +140,7 @@ def __init__(self, **kwargs): self.uname = settings.ANSIBLETOWER.get("username") self.pword = settings.ANSIBLETOWER.get("password") self.token = settings.ANSIBLETOWER.get("token") - self._inventory = ( - kwargs.get("tower_inventory") or settings.ANSIBLETOWER.inventory - ) + self._inventory = kwargs.get("tower_inventory") or settings.ANSIBLETOWER.inventory # Init the class itself config = kwargs.get("config") root = kwargs.get("root") @@ -271,9 +261,7 @@ def _merge_artifacts(self, at_object, strategy="last", artifacts=None): children = at_object.get_related("workflow_nodes").results # filter out children with no associated job children = list( - filter( - lambda child: getattr(child.summary_fields, "job", None), children - ) + filter(lambda child: getattr(child.summary_fields, "job", None), children) ) children.sort(key=lambda child: child.summary_fields.job.id) if strategy == "last": @@ -286,8 +274,7 @@ def _merge_artifacts(self, at_object, strategy="last", artifacts=None): if child_obj: child_obj = child_obj.pop() artifacts = ( - self._merge_artifacts(child_obj, strategy, artifacts) - or artifacts + self._merge_artifacts(child_obj, strategy, artifacts) or artifacts ) else: logger.warning( @@ -303,14 +290,10 @@ def _get_failure_messages(self, workflow): children = workflow.get_related("workflow_nodes").results # filter out children with no associated job children = list( - filter( - lambda child: getattr(child.summary_fields, "job", None), children - ) + filter(lambda child: getattr(child.summary_fields, "job", None), children) ) # filter out children that didn't fail - children = list( - filter(lambda child: child.summary_fields.job.failed, children) - ) + children = list(filter(lambda child: child.summary_fields.job.failed, children)) children.sort(key=lambda child: child.summary_fields.job.id) for child in children[::-1]: if child.type == "workflow_job_node": @@ -334,9 +317,7 @@ def _get_failure_messages(self, workflow): # get all failed job_events for each job (filter failed=true) failed_events = [ ev - for ev in child_obj.get_related( - "job_events", page_size=200 - ).results + for ev in child_obj.get_related("job_events", page_size=200).results if ev.failed ] # find the one(s) with event_data['res']['msg'] @@ -363,10 +344,7 @@ def _get_failure_messages(self, workflow): def _get_expire_date(self, host_id): try: time_stamp = ( - self.v2.hosts.get(id=host_id) - .results[0] - .related.ansible_facts.get() - .expire_date + self.v2.hosts.get(id=host_id).results[0].related.ansible_facts.get().expire_date ) return str(datetime.fromtimestamp(int(time_stamp))) except Exception as err: # noqa: BLE001 @@ -391,9 +369,7 @@ def _compile_host_info(self, host): if expire_time: host_info["expire_time"] = expire_time try: - create_job = self.v2.jobs.get( - id=host.get_related("job_events").results[0].job - ) + create_job = self.v2.jobs.get(id=host.get_related("job_events").results[0].job) create_job = create_job.results[0].get_related("source_workflow_job") host_info["_broker_args"]["workflow"] = create_job.name except IndexError: @@ -406,18 +382,12 @@ def _compile_host_info(self, host): ).summary_fields.source_workflow_job.name except Exception as err: # noqa: BLE001 logger.debug(f"Tell Jake that the exception here is: {err}!") - logger.warning( - f"Unable to determine workflow for {host_info['hostname']}" - ) + logger.warning(f"Unable to determine workflow for {host_info['hostname']}") else: return host_info create_vars = json.loads(create_job.extra_vars) host_info["_broker_args"].update( - { - arg: val - for arg, val in create_vars.items() - if val and isinstance(val, str) - } + {arg: val for arg, val in create_vars.items() if val and isinstance(val, str)} ) # temporary workaround for OSP hosts that have lost their hostname if not host_info["hostname"] and host.variables.get("openstack"): @@ -448,9 +418,7 @@ def construct_host(self, provider_params, host_classes, **kwargs): misc_attrs = {} # used later to add misc attributes to host object if provider_params: job = provider_params - job_attrs = self._merge_artifacts( - job, strategy=kwargs.get("strategy", "last") - ) + job_attrs = self._merge_artifacts(job, strategy=kwargs.get("strategy", "last")) # pull information about the job arguments job_extra_vars = json.loads(job.extra_vars) # and update them if they have resolved values @@ -476,9 +444,7 @@ def construct_host(self, provider_params, host_classes, **kwargs): if not hostname: logger.warning(f"No hostname found in job attributes:\n{job_attrs}") logger.debug(f"hostname: {hostname}, name: {name}, host type: {host_type}") - host_inst = host_classes[host_type]( - **{**kwargs, "hostname": hostname, "name": name} - ) + host_inst = host_classes[host_type](**{**kwargs, "hostname": hostname, "name": name}) else: host_inst = host_classes[kwargs.get("type")](**kwargs) self._set_attributes(host_inst, broker_args=kwargs, misc_attrs=misc_attrs) @@ -520,28 +486,21 @@ def execute(self, **kwargs): # noqa: PLR0912 - Possible TODO refactor payload = {} if inventory := kwargs.pop("inventory", None): payload["inventory"] = inventory - logger.info( - f"Using tower inventory: {self._translate_inventory(inventory)}" - ) + logger.info(f"Using tower inventory: {self._translate_inventory(inventory)}") elif self.inventory: payload["inventory"] = self.inventory - logger.info( - f"Using tower inventory: {self._translate_inventory(self.inventory)}" - ) + logger.info(f"Using tower inventory: {self._translate_inventory(self.inventory)}") else: logger.info("No inventory specified, Ansible Tower will use a default.") payload["extra_vars"] = str(kwargs) logger.debug( - f"Launching {subject}: {url_parser.urljoin(self.url, str(target.url))}\n" - f"{payload=}" + f"Launching {subject}: {url_parser.urljoin(self.url, str(target.url))}\n{payload=}" ) job = target.launch(payload=payload) job_number = job.url.rstrip("/").split("/")[-1] job_api_url = url_parser.urljoin(self.url, str(job.url)) if self._is_aap: - job_ui_url = url_parser.urljoin( - self.url, f"/#/jobs/{subject}/{job_number}/output" - ) + job_ui_url = url_parser.urljoin(self.url, f"/#/jobs/{subject}/{job_number}/output") else: job_ui_url = url_parser.urljoin(self.url, f"/#/{subject}s/{job_number}") helpers.emit(api_url=job_api_url, ui_url=job_ui_url) @@ -592,8 +551,7 @@ def extend(self, target_vm, new_expire_time=None): return self.execute( workflow=settings.ANSIBLETOWER.extend_workflow, target_vm=target_vm.name, - new_expire_time=new_expire_time - or settings.ANSIBLETOWER.get("new_expire_time"), + new_expire_time=new_expire_time or settings.ANSIBLETOWER.get("new_expire_time"), ) def provider_help( @@ -620,9 +578,7 @@ def provider_help( elif workflows: workflows = [ workflow.name - for workflow in self.v2.workflow_job_templates.get( - page_size=1000 - ).results + for workflow in self.v2.workflow_job_templates.get(page_size=1000).results if workflow.summary_fields.user_capabilities.get("start") ] if res_filter := kwargs.get("results_filter"): @@ -635,10 +591,7 @@ def provider_help( inv = {"Name": inv.name, "ID": inv.id, "Description": inv.description} logger.info(f"Accepted additional nick fields:\n{helpers.yaml_format(inv)}") elif inventories: - inv = [ - inv.name - for inv in self.v2.inventory.get(kind="", page_size=1000).results - ] + inv = [inv.name for inv in self.v2.inventory.get(kind="", page_size=1000).results] if res_filter := kwargs.get("results_filter"): inv = eval_filter(inv, res_filter, "res") inv = inv if isinstance(inv, list) else [inv] @@ -661,18 +614,16 @@ def provider_help( if res_filter := kwargs.get("results_filter"): job_templates = eval_filter(job_templates, res_filter, "res") job_templates = ( - job_templates - if isinstance(job_templates, list) - else [job_templates] + job_templates if isinstance(job_templates, list) else [job_templates] ) job_templates = "\n".join(job_templates[:results_limit]) logger.info(f"Available job templates:\n{job_templates}") elif templates: templates = list( set( - self.execute(workflow="list-templates", artifacts="last")[ - "data_out" - ]["list_templates"] + self.execute(workflow="list-templates", artifacts="last")["data_out"][ + "list_templates" + ] ) ) templates.sort(reverse=True) diff --git a/broker/providers/beaker.py b/broker/providers/beaker.py index ab4eb333..955c3d22 100644 --- a/broker/providers/beaker.py +++ b/broker/providers/beaker.py @@ -103,17 +103,13 @@ def construct_host(self, provider_params, host_classes, **kwargs): :return: constructed broker host object """ - logger.debug( - f"constructing with {provider_params=}\n{host_classes=}\n{kwargs=}" - ) + logger.debug(f"constructing with {provider_params=}\n{host_classes=}\n{kwargs=}") if not provider_params: host_inst = host_classes[kwargs.get("type", "host")](**kwargs) # cont_inst = self._cont_inst_by_name(host_inst.name) self._set_attributes(host_inst, broker_args=kwargs) else: - host_info = self._compile_host_info( - provider_params["hostname"], broker_info=False - ) + host_info = self._compile_host_info(provider_params["hostname"], broker_info=False) host_inst = host_classes[kwargs.get("type", "host")](**provider_params) self._set_attributes(host_inst, broker_args=kwargs, misc_attrs=host_info) return host_inst @@ -149,9 +145,7 @@ def release(self, host_name, job_id): def extend(self, host_name, extend_duration=99): """Extend the duration of a Beaker reservation.""" try: - Host(hostname=host_name).execute( - f"/usr/bin/extendtesttime.sh {extend_duration}" - ) + Host(hostname=host_name).execute(f"/usr/bin/extendtesttime.sh {extend_duration}") except BrokerError as err: raise ProviderError( f"Failed to extend host {host_name}: {err}\n" diff --git a/broker/providers/container.py b/broker/providers/container.py index 8de6b3e3..d0df7598 100644 --- a/broker/providers/container.py +++ b/broker/providers/container.py @@ -29,17 +29,13 @@ def container_info(container_inst): def _host_release(): caller_host = inspect.stack()[1][0].f_locals["host"] if not caller_host._cont_inst: - caller_host._cont_inst = caller_host._prov_inst._cont_inst_by_name( - caller_host.name - ) + caller_host._cont_inst = caller_host._prov_inst._cont_inst_by_name(caller_host.name) caller_host._cont_inst.remove(v=True, force=True) caller_host._checked_in = True @cache -def get_runtime( - runtime_cls=None, host=None, username=None, password=None, port=None, timeout=None -): +def get_runtime(runtime_cls=None, host=None, username=None, password=None, port=None, timeout=None): """Return a runtime instance.""" return runtime_cls( host=host, @@ -173,9 +169,7 @@ def _port_mapping(self, image, **kwargs): elif settings.container.auto_map_ports: mapping = { k: v or None - for k, v in self.runtime.image_info(image)["config"][ - "ExposedPorts" - ].items() + for k, v in self.runtime.image_info(image)["config"]["ExposedPorts"].items() } return mapping @@ -195,9 +189,7 @@ def construct_host(self, provider_params, host_classes, **kwargs): :return: broker object of constructed host instance """ - logger.debug( - f"constructing with {provider_params=}\n{host_classes=}\n{kwargs=}" - ) + logger.debug(f"constructing with {provider_params=}\n{host_classes=}\n{kwargs=}") if not provider_params: host_inst = host_classes[kwargs.get("type", "host")](**kwargs) cont_inst = self._cont_inst_by_name(host_inst.name) @@ -213,9 +205,7 @@ def construct_host(self, provider_params, host_classes, **kwargs): raise Exception(f"Could not determine container hostname:\n{cont_attrs}") name = cont_attrs["name"] logger.debug(f"hostname: {hostname}, name: {name}, host type: host") - host_inst = host_classes["host"]( - **{**kwargs, "hostname": hostname, "name": name} - ) + host_inst = host_classes["host"](**{**kwargs, "hostname": hostname, "name": name}) self._set_attributes(host_inst, broker_args=kwargs, cont_inst=cont_inst) return host_inst diff --git a/broker/session.py b/broker/session.py index ef8fbe73..61c14f8b 100644 --- a/broker/session.py +++ b/broker/session.py @@ -302,9 +302,7 @@ def sftp_write(self, source, destination=None, ensure_dir=True): destination = destination or f"{source[0].parent}/" # Files need to be added to a tarfile with helpers.temporary_tar(source) as tar: - logger.debug( - f"{self._cont_inst.hostname} adding file(s) {source} to {destination}" - ) + logger.debug(f"{self._cont_inst.hostname} adding file(s) {source} to {destination}") if ensure_dir: if destination.endswith("/"): self.run(f"mkdir -m 666 -p {destination}") @@ -334,9 +332,7 @@ def sftp_read(self, source, destination=None, return_data=False): destination.write_bytes(f.read()) else: logger.warning("More than one member was found in the tar file.") - tar.extractall( - destination.parent if destination.is_file() else destination - ) + tar.extractall(destination.parent if destination.is_file() else destination) def shell(self, pty=False): """Create and return an interactive shell instance.""" diff --git a/broker/settings.py b/broker/settings.py index 5cc0891a..4396f8fa 100644 --- a/broker/settings.py +++ b/broker/settings.py @@ -22,7 +22,9 @@ def init_settings(settings_path, interactive=False): """Initialize the broker settings file.""" - raw_url = "https://raw.githubusercontent.com/SatelliteQE/broker/master/broker_settings.yaml.example" + raw_url = ( + "https://raw.githubusercontent.com/SatelliteQE/broker/master/broker_settings.yaml.example" + ) if ( not interactive or click.prompt( @@ -46,9 +48,7 @@ def init_settings(settings_path, interactive=False): fg="yellow", ) else: - raise ConfigurationError( - f"Broker settings file not found at {settings_path.absolute()}." - ) + raise ConfigurationError(f"Broker settings file not found at {settings_path.absolute()}.") interative_mode = False @@ -75,9 +75,7 @@ def init_settings(settings_path, interactive=False): inventory_path = BROKER_DIRECTORY.joinpath("inventory.yaml") if not settings_path.exists(): - click.secho( - f"Broker settings file not found at {settings_path.absolute()}.", fg="red" - ) + click.secho(f"Broker settings file not found at {settings_path.absolute()}.", fg="red") init_settings(settings_path, interactive=interative_mode) validators = [ diff --git a/ruff.toml b/pyproject.toml similarity index 68% rename from ruff.toml rename to pyproject.toml index c8c92406..c2e57d9a 100644 --- a/ruff.toml +++ b/pyproject.toml @@ -1,3 +1,84 @@ +[build-system] +requires = ["setuptools", "setuptools-scm", "wheel", "twine"] +build-backend = "setuptools.build_meta" + +[project] +name = "broker" +description = "The infrastructure middleman." +readme = "README.md" +requires-python = ">=3.10" +license = {file = "LICENSE", name = "GNU General Public License v3"} +keywords = ["broker", "AnsibleTower", "docker", "podman", "beaker"] +authors = [ + {name = "Jacob J Callahan", email = "jacob.callahan05@gmail.com"} +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Natural Language :: English", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "awxkit", + "click", + "dynaconf<4.0.0", + "logzero", + "pyyaml", + "setuptools", + "ssh2-python", +] +dynamic = ["version"] # dynamic fields to update on build - version via setuptools_scm + +[project.urls] +Repository = "https://github.com/SatelliteQE/broker" + +[project.optional-dependencies] +dev = [ + "pre-commit", + "pytest", + "ruff" +] +docker = [ + "docker", + "paramiko" +] +podman = ["podman-py"] +beaker = ["beaker-client"] + +[project.scripts] +broker = "broker.commands:cli" + +[tool.setuptools] +platforms = ["any"] +zip-safe = false +include-package-data = true + +[tool.setuptools.packages.find] +include = ["broker"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = ["-v", "-l", "--color=yes", "--code-highlight=yes"] + +[tool.black] +line-length = 100 +target-version = ["py310", "py311"] +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.venv + | build + | dist + | tests/data +)/ +''' + +[tool.ruff] +exclude = ["tests/"] target-version = "py311" fixable = ["ALL"] @@ -81,18 +162,18 @@ ignore = [ "D107", # Missing docstring in __init__ ] -[flake8-pytest-style] +[tool.ruff.flake8-pytest-style] fixture-parentheses = false -[isort] +[tool.ruff.isort] force-sort-within-sections = true known-first-party = [ "broker", ] combine-as-imports = true -[per-file-ignores] +[tool.ruff.per-file-ignores] # None at this time -[mccabe] +[tool.ruff.mccabe] max-complexity = 25 diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index e2c5a324..00000000 --- a/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -testpaths = tests -addopts = -v diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index de91fc01..00000000 --- a/setup.cfg +++ /dev/null @@ -1,53 +0,0 @@ -[metadata] -name = broker -description = The infrastructure middleman. -long_description = file: README.md -long_description_content_type = text/markdown -author = Jacob J Callahan -author_email = jacob.callahan05@gmail.com -url = https://github.com/SatelliteQE/broker -license = GNU General Public License v3 -keywords = broker, AnsibleTower -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Developers - License :: OSI Approved :: GNU General Public License v3 (GPLv3) - Natural Language :: English - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - -[options] -install_requires = - awxkit - click - dynaconf>=3.1.0 - logzero - pyyaml - setuptools - ssh2-python -packages = find: -zip_safe = False - -[options.extras_require] -test = - pytest - ruff -setup = - setuptools - setuptools-scm - wheel - twine -docker = - docker - paramiko -podman = podman-py -beaker = beaker-client - -[options.entry_points] -console_scripts = - broker = broker.commands:cli - -[flake8] -max-line-length = 110 diff --git a/setup.py b/setup.py deleted file mode 100644 index 76755a44..00000000 --- a/setup.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python -from setuptools import setup - -setup(use_scm_version=True) diff --git a/tests/functional/test_containers.py b/tests/functional/test_containers.py index 26a8086a..61cd5d91 100644 --- a/tests/functional/test_containers.py +++ b/tests/functional/test_containers.py @@ -23,9 +23,7 @@ def temp_inventory(): """Temporarily move the local inventory, then move it back when done""" backup_path = inventory_path.rename(f"{inventory_path.absolute()}.bak") yield - CliRunner().invoke( - cli, ["checkin", "--all", "--filter", "_broker_provider {tailed_file}") @@ -116,9 +108,7 @@ def test_container_e2e_mp(): res = c_host.execute(f"ls {remote_dir}") assert str(loc_settings_path) in res.stdout with NamedTemporaryFile() as tmp: - c_host.session.sftp_read( - f"{remote_dir}/{loc_settings_path.name}", tmp.file.name - ) + c_host.session.sftp_read(f"{remote_dir}/{loc_settings_path.name}", tmp.file.name) data = c_host.session.sftp_read( f"{remote_dir}/{loc_settings_path.name}", return_data=True ) @@ -128,9 +118,7 @@ def test_container_e2e_mp(): assert ( loc_settings_path.read_bytes() == data ), "Local file is different from the received one (return_data=True)" - assert ( - data == Path(tmp.file.name).read_bytes() - ), "Received files do not match" + assert data == Path(tmp.file.name).read_bytes(), "Received files do not match" def test_broker_multi_manager(): diff --git a/tests/functional/test_rh_beaker.py b/tests/functional/test_rh_beaker.py index 1460eea0..5aa39daf 100644 --- a/tests/functional/test_rh_beaker.py +++ b/tests/functional/test_rh_beaker.py @@ -23,9 +23,7 @@ def temp_inventory(): """Temporarily move the local inventory, then move it back when done""" backup_path = inventory_path.rename(f"{inventory_path.absolute()}.bak") yield - CliRunner().invoke( - cli, ["checkin", "--all", "--filter", '@inv._broker_provider == "Beaker"'] - ) + CliRunner().invoke(cli, ["checkin", "--all", "--filter", '@inv._broker_provider == "Beaker"']) inventory_path.unlink() backup_path.rename(inventory_path) @@ -66,6 +64,7 @@ def test_jobs_list(): # ----- Broker API Tests ----- + def test_beaker_host(): with Broker(job_xml="tests/data/beaker/test_job.xml") as r_host: res = r_host.execute("hostname") @@ -76,9 +75,7 @@ def test_beaker_host(): assert str(settings_path.name) in res.stdout with NamedTemporaryFile() as tmp: r_host.session.sftp_read(f"{remote_dir}/{settings_path.name}", tmp.file.name) - data = r_host.session.sftp_read( - f"{remote_dir}/{settings_path.name}", return_data=True - ) + data = r_host.session.sftp_read(f"{remote_dir}/{settings_path.name}", return_data=True) assert ( settings_path.read_bytes() == Path(tmp.file.name).read_bytes() ), "Local file is different from the received one" @@ -91,5 +88,5 @@ def test_beaker_host(): r_host.execute(f"echo 'hello world' > {tailed_file}") with r_host.session.tail_file(tailed_file) as tf: r_host.execute(f"echo 'this is a new line' >> {tailed_file}") - assert 'this is a new line' in tf.stdout - assert 'hello world' not in tf.stdout + assert "this is a new line" in tf.stdout + assert "hello world" not in tf.stdout diff --git a/tests/functional/test_satlab.py b/tests/functional/test_satlab.py index ad214d92..dc48736c 100644 --- a/tests/functional/test_satlab.py +++ b/tests/functional/test_satlab.py @@ -23,9 +23,7 @@ def temp_inventory(): """Temporarily move the local inventory, then move it back when done""" backup_path = inventory_path.rename(f"{inventory_path.absolute()}.bak") yield - CliRunner().invoke( - cli, ["checkin", "--all", "--filter", "_broker_provider {tailed_file}") with r_host.session.tail_file(tailed_file) as tf: r_host.execute(f"echo 'this is a new line' >> {tailed_file}") - assert 'this is a new line' in tf.stdout - assert 'hello world' not in tf.stdout + assert "this is a new line" in tf.stdout + assert "hello world" not in tf.stdout def test_tower_host_mp(): @@ -121,8 +118,7 @@ def test_tower_host_mp(): r_hosts[0].session.remote_copy( source=f"{remote_dir}/{loc_settings_path.name}", dest_host=r_hosts[1], - dest_path=f"/root/{loc_settings_path.name}" + dest_path=f"/root/{loc_settings_path.name}", ) res = r_hosts[1].execute(f"ls /root") assert loc_settings_path.name in res.stdout - diff --git a/tests/providers/test_beaker.py b/tests/providers/test_beaker.py index e3db1928..7f2d1908 100644 --- a/tests/providers/test_beaker.py +++ b/tests/providers/test_beaker.py @@ -21,6 +21,7 @@ class BeakerBindStub(MockStub): - self.runtime.job_list(**kwargs).stdout.splitlines() - self.runtime.user_systems() """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.job_id = "1234567" @@ -39,7 +40,7 @@ def system_details_curated(self, host): "reservation_id": "1267", "reserved_on": "2023-01-01 00:00:00", "expires_on": "2025-01-01 00:00:00", - "reserved_for": "anotheruser " + "reserved_for": "anotheruser ", } def execute_job(self, job_xml, max_wait): @@ -49,7 +50,6 @@ def user_systems(self): return ["test.example.com", "test2.example.com"] - @pytest.fixture def bind_stub(): return BeakerBindStub() diff --git a/tests/providers/test_container.py b/tests/providers/test_container.py index 6ac9d216..9ab3aae6 100644 --- a/tests/providers/test_container.py +++ b/tests/providers/test_container.py @@ -22,9 +22,7 @@ class ContainerApiStub(MockStub): def __init__(self, **kwargs): in_dict = { "images": [MockStub({"tags": "ch-d:ubi8"})], # self.runtime.images - "containers": [ - MockStub({"tags": "f37d3058317f"}) - ], # self.runtime.containers + "containers": [MockStub({"tags": "f37d3058317f"})], # self.runtime.containers "name": "f37d3058317f", # self.runtime.get_attrs(cont_inst)["name"] } if "job_id" in kwargs: diff --git a/tests/test_broker.py b/tests/test_broker.py index 894a12d6..cbcab8de 100644 --- a/tests/test_broker.py +++ b/tests/test_broker.py @@ -66,9 +66,7 @@ def test_broker_checkin_n_sync_empty_hostname(): assert hosts[0].hostname is None broker_inst = broker.Broker(hosts=hosts) broker_inst.checkin() - assert ( - not broker_inst.from_inventory() - ), "Host was not removed from inventory after checkin" + assert not broker_inst.from_inventory(), "Host was not removed from inventory after checkin" def test_mp_checkout(): diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 30b3b39f..548303bf 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -134,25 +134,15 @@ def test_eval_filter_chain(fake_inventory): filtered = helpers.eval_filter(fake_inventory, "@inv[:3] | 'sat-jenkins' in @inv.name") assert len(filtered) == 1 + def test_dict_from_paths_nested(): source_dict = { "person": { "name": "John", "age": 30, - "address": { - "street": "123 Main St", - "city": "Anytown", - "state": "CA", - "zip": "12345" - } + "address": {"street": "123 Main St", "city": "Anytown", "state": "CA", "zip": "12345"}, } } - paths = { - "person_name": "person/name", - "person_zip": "person/address/zip" - } + paths = {"person_name": "person/name", "person_zip": "person/address/zip"} result = helpers.dict_from_paths(source_dict, paths) - assert result == { - "person_name": "John", - "person_zip": "12345" - } + assert result == {"person_name": "John", "person_zip": "12345"} diff --git a/tests/test_settings.py b/tests/test_settings.py index 1926babf..771c4c6b 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -36,9 +36,7 @@ def test_nested_envar(set_envars): assert test_provider.foo == "baz" -@pytest.mark.parametrize( - "set_envars", [("BROKER_TESTPROVIDER__foo", "envar")], indirect=True -) +@pytest.mark.parametrize("set_envars", [("BROKER_TESTPROVIDER__foo", "envar")], indirect=True) def test_default_envar(set_envars): """Set a top-level instance value via environment variable then verify that the value is not overriden when the provider is selected by default. @@ -48,9 +46,7 @@ def test_default_envar(set_envars): assert test_provider.foo == "envar" -@pytest.mark.parametrize( - "set_envars", [("BROKER_TESTPROVIDER__foo", "override me")], indirect=True -) +@pytest.mark.parametrize("set_envars", [("BROKER_TESTPROVIDER__foo", "override me")], indirect=True) def test_nondefault_envar(set_envars): """Set a top-level instance value via environment variable then verify that the value has been overriden when the provider is specified. @@ -60,9 +56,7 @@ def test_nondefault_envar(set_envars): assert test_provider.foo == "baz" -@pytest.mark.parametrize( - "set_envars", [("VAULT_ENABLED_FOR_DYNACONF", "1")], indirect=True -) +@pytest.mark.parametrize("set_envars", [("VAULT_ENABLED_FOR_DYNACONF", "1")], indirect=True) def test_purge_vault_envars(set_envars): """Set dynaconf vault envars and verify that they have no effect""" sys.modules.pop("broker.settings")