From 1aa83862f0d4f4bb3124928151501ebec4635096 Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Mon, 2 May 2022 18:34:14 +0200 Subject: [PATCH 1/2] src/vagrant/__init__.py: Wire global-status Add support for running "vagrant global-status" and its corresponding test. Signed-off-by: Arnaud Patard --- src/vagrant/__init__.py | 47 ++++++++++++++++++++++++++++++++++++++++ tests/test_vagrant.py | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/vagrant/__init__.py b/src/vagrant/__init__.py index b762f92..6b8282e 100644 --- a/src/vagrant/__init__.py +++ b/src/vagrant/__init__.py @@ -138,6 +138,9 @@ def get_vagrant_executable(): # Classes for listings of Statuses, Boxes, and Plugins Status = collections.namedtuple("Status", ["name", "state", "provider"]) +GlobalStatus = collections.namedtuple( + "GlobalStatus", ["id", "state", "provider", "home"] +) Box = collections.namedtuple("Box", ["name", "provider", "version"]) Plugin = collections.namedtuple("Plugin", ["name", "version", "system"]) @@ -525,6 +528,24 @@ def status(self, vm_name=None): output = self._run_vagrant_command(["status", "--machine-readable", vm_name]) return self._parse_status(output) + def global_status(self, prune=False): + """ + Return the results of a `vagrant global-status` call as a list of one or more + GlobalStatus objects. A GlobalStatus contains the following attributes: + + - id: The VM id. + - state: The state of the underlying guest machine (i.e. VM). + - provider: the name of the VM provider, e.g. 'virtualbox'. None + if no provider is output by vagrant. + - home: the path to the machine vagrantfile for the VM + """ + # machine-readable output are CSV lines + cmd = ["global-status", "--machine-readable"] + if prune is True: + cmd.append("--prune") + output = self._run_vagrant_command(cmd) + return self._parse_global_status(output) + def _normalize_status(self, status, provider): """ Normalise VM status to cope with state name being different @@ -562,6 +583,32 @@ def _parse_status(self, output): return statuses + def _parse_global_status(self, output): + """ + Unit testing is so much easier when Vagrant is removed from the + equation. + """ + parsed = self._parse_machine_readable_output(output) + statuses = [] + vm_id = state = provider = home = None + for timestamp, target, kind, data in parsed: + if kind == "machine-id": + vm_id = data + elif kind == "provider-name": + provider = data + elif kind == "machine-home": + home = data + elif kind == "state": + state = data + if vm_id and provider and home and state: + state = self._normalize_status(state, provider) + status = GlobalStatus( + id=vm_id, state=state, provider=provider, home=home + ) + statuses.append(status) + vm_id = state = provider = home = None + return statuses + def conf(self, ssh_config=None, vm_name=None): """ Parse ssh_config into a dict containing the keys defined in ssh_config, diff --git a/tests/test_vagrant.py b/tests/test_vagrant.py index 91cd383..d35089d 100644 --- a/tests/test_vagrant.py +++ b/tests/test_vagrant.py @@ -222,6 +222,54 @@ def test_parse_status(vm_dir): ) +def test_parse_global_status(vm_dir): + """ + Test the parsing the output of the `vagrant global-status` command. + """ + listing = """1651503808,,metadata,machine-count,2 +1651503808,,machine-id,9ec0e5d +1651503808,,provider-name,libvirt +1651503808,,machine-home,/tmp +1651503808,,state,preparing +1651503808,,machine-id,61395ad +1651503808,,provider-name,libvirt +1651503808,,machine-home,/home/rtp/.cache/molecule/sbd/default +1651503808,,state,running +1651504022,,ui,info,id +1651504022,,ui,info,name +1651504022,,ui,info,provider +1651504022,,ui,info,state +1651504022,,ui,info,directory +1651504022,,ui,info, +1651504022,,ui,info,------------------------------------------------------------------------------------------------------------- +1651504022,,ui,info,9ec0e5d +1651504022,,ui,info,bionic +1651504022,,ui,info,libvirt +1651504022,,ui,info,preparing +1651504022,,ui,info,/tmp +1651504022,,ui,info, +1651504022,,ui,info,61395ad +1651504022,,ui,info,instance-sbd-default +1651504022,,ui,info,libvirt +1651504022,,ui,info,running +1651504022,,ui,info,/home/rtp/.cache/molecule/sbd/default +1651504022,,ui,info, +11651504022,,ui,info, \\nThe above shows information about all known Vagrant environments\\non this machine... +""" + # Can compare tuples to GlobalStatus class b/c GlobalStatus is a collections.namedtuple. + goal = [ + ("9ec0e5d", "preparing", "libvirt", "/tmp"), + ("61395ad", "running", "libvirt", "/home/rtp/.cache/molecule/sbd/default"), + ] + v = vagrant.Vagrant(vm_dir) + parsed = v._parse_global_status(listing) + assert ( + goal == parsed + ), "The parsing of the test listing did not match the goal.\nlisting={!r}\ngoal={!r}\nparsed_listing={!r}".format( + listing, goal, parsed + ) + + def test_parse_aws_status(vm_dir): """ Test the parsing the output of the `vagrant status` command for an aws instance. From 1de96a742b9bb72778e2bf1215c850f1c1445973 Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Mon, 2 May 2022 18:49:39 +0200 Subject: [PATCH 2/2] .github/workflows/tox.yml: Set PYTEST_REQPASS Ensure the CI is running the correct number of tests. Signed-off-by: Arnaud Patard --- .github/workflows/tox.yml | 1 + setup.cfg | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 658ecb8..9cc4cb1 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -49,6 +49,7 @@ jobs: TOX_PARALLEL_NO_SPINNER: 1 TOXENV: ${{ matrix.tox_env }} FORCE_COLOR: 1 + PYTEST_REQPASS: 24 steps: - name: Check vagrant presence diff --git a/setup.cfg b/setup.cfg index d79cffb..773b195 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,6 +39,7 @@ test = coverage>=6.3 pytest-cov>=3.0.0 pytest>=7.0.0 + pytest-plus>=0.2 [options.packages.find] where = src