We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Allow the deployment and use of development versions of Avocado
**Is your feature request related to a problem? Please describe.**
Epic issue https://github.com/avocado-framework/avocado/issues/4934 covers aspects such as creating and publishing (AFAICT) packages for every Avocado release, for every Python version, and how they will be deployed and installed on `DeploymentSpawner`s.

But, this doesn't address the development time needs, where unreleased code may be needed in both the job side (say running out of a GIT checkout), and on the isolated spawner environment.

**Describe the solution you'd like**
There should be a way to signal a preference for given wheels to be used, instead of the latest release ones. 

**Describe alternatives you've considered**
Building the wheels is probably going to be a separate step (such as the current `make whee`, and It will probably be necessary to refer to them by a local (non HTTP(s)) uri.   Bonus point for an all-in-one `--mirror-this-repo-as-a-wheel-and-deploy-it` kind of feature, but I think this will be too complex.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `avocado/plugins/spawners/podman.py`
Content:
```
1 import asyncio
2 import json
3 import logging
4 import os
5 import subprocess
6 
7 from avocado.core.plugin_interfaces import CLI, DeploymentSpawner, Init
8 from avocado.core.settings import settings
9 from avocado.core.spawners.common import SpawnerMixin, SpawnMethod
10 from avocado.core.version import VERSION
11 from avocado.utils import distro
12 from avocado.utils.asset import Asset
13 from avocado.utils.podman import Podman, PodmanException
14 
15 LOG = logging.getLogger(__name__)
16 
17 
18 ENTRY_POINT_CMD = "/tmp/avocado-runner"
19 
20 
21 class PodmanSpawnerInit(Init):
22 
23     description = 'Podman (container) based spawner initialization'
24 
25     def initialize(self):
26         section = 'spawner.podman'
27 
28         help_msg = 'Path to the podman binary'
29         settings.register_option(
30             section=section,
31             key='bin',
32             help_msg=help_msg,
33             default='/usr/bin/podman')
34 
35         this_distro = distro.detect()
36         if this_distro != distro.UNKNOWN_DISTRO:
37             default_distro = '{0}:{1}'.format(this_distro.name,
38                                               this_distro.version)
39         else:
40             default_distro = 'fedora:latest'
41         help_msg = ('Image name to use when creating the container. '
42                     'The first default choice is a container image '
43                     'matching the current OS. If unable to detect, '
44                     'default becomes the latest Fedora release. Default '
45                     'on this system: {0}'.format(default_distro))
46         settings.register_option(
47             section=section,
48             key='image',
49             help_msg=help_msg,
50             default=default_distro)
51 
52 
53 class PodmanCLI(CLI):
54 
55     name = 'podman'
56     description = 'podman spawner command line options for "run"'
57 
58     def configure(self, parser):
59         super().configure(parser)
60         parser = parser.subcommands.choices.get('run', None)
61         if parser is None:
62             return
63 
64         parser = parser.add_argument_group('podman spawner specific options')
65         settings.add_argparser_to_option(namespace='spawner.podman.bin',
66                                          parser=parser,
67                                          long_arg='--spawner-podman-bin',
68                                          metavar='PODMAN_BIN')
69 
70         settings.add_argparser_to_option(namespace='spawner.podman.image',
71                                          parser=parser,
72                                          long_arg='--spawner-podman-image',
73                                          metavar='CONTAINER_IMAGE')
74 
75     def run(self, config):
76         pass
77 
78 
79 class PodmanSpawner(DeploymentSpawner, SpawnerMixin):
80 
81     description = 'Podman (container) based spawner'
82     METHODS = [SpawnMethod.STANDALONE_EXECUTABLE]
83 
84     _PYTHON_VERSIONS_CACHE = {}
85 
86     def is_task_alive(self, runtime_task):
87         if runtime_task.spawner_handle is None:
88             return False
89         podman_bin = self.config.get('spawner.podman.bin')
90         cmd = [podman_bin, "ps", "--all", "--format={{.State}}",
91                "--filter=id=%s" % runtime_task.spawner_handle]
92         process = subprocess.Popen(cmd,
93                                    stdin=subprocess.DEVNULL,
94                                    stdout=subprocess.PIPE,
95                                    stderr=subprocess.DEVNULL)
96         out, _ = process.communicate()
97         # FIXME: check how podman 2.x is reporting valid "OK" states
98         return out.startswith(b'Up ')
99 
100     def _fetch_asset(self, url):
101         cachedirs = self.config.get('datadir.paths.cache_dirs')
102         asset = Asset(url, cache_dirs=cachedirs)
103         return asset.fetch()
104 
105     def get_eggs_paths(self, py_major, py_minor):
106         """Return the basic eggs needed to bootstrap Avocado.
107 
108         This will return a tuple with the current location and where this
109         should be deployed.
110         """
111         result = []
112         # Setuptools
113         # For now let's pin to setuptools 59.2.
114         # TODO: Automatically get latest setuptools version.
115         eggs = [f"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg",
116                 f"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg"]
117         for url in eggs:
118             path = self._fetch_asset(url)
119             to = os.path.join('/tmp/', os.path.basename(path))
120             result.append((path, to))
121         return result
122 
123     @property
124     async def python_version(self):
125         image = self.config.get('spawner.podman.image')
126         if image not in self._PYTHON_VERSIONS_CACHE:
127             if not self.podman:
128                 msg = "Cannot get Python version: self.podman not defined."
129                 LOG.debug(msg)
130                 return None, None, None
131             result = await self.podman.get_python_version(image)
132             self._PYTHON_VERSIONS_CACHE[image] = result
133         return self._PYTHON_VERSIONS_CACHE[image]
134 
135     async def deploy_artifacts(self):
136         pass
137 
138     async def deploy_avocado(self, where):
139         # Deploy all the eggs to container inside /tmp/
140         major, minor, _ = await self.python_version
141         eggs = self.get_eggs_paths(major, minor)
142 
143         for egg, to in eggs:
144             await self.podman.copy_to_container(where, egg, to)
145 
146     async def _create_container_for_task(self, runtime_task, env_args,
147                                          test_output=None):
148         mount_status_server_socket = False
149         mounted_status_server_socket = '/tmp/.status_server.sock'
150         status_server_uri = runtime_task.task.status_services[0].uri
151         if ':' not in status_server_uri:
152             # a unix domain socket is being used
153             mount_status_server_socket = True
154             runtime_task.task.status_services[0].uri = mounted_status_server_socket
155 
156         _, _, python_binary = await self.python_version
157         entry_point_args = [python_binary,
158                             '-m',
159                             'avocado.core.nrunner',
160                             'task-run']
161 
162         task = runtime_task.task
163         entry_point_args.extend(task.get_command_args())
164         entry_point = json.dumps(entry_point_args)
165         entry_point_arg = "--entrypoint=" + entry_point
166 
167         if mount_status_server_socket:
168             status_server_opts = (
169                 "--privileged",
170                 "-v", "%s:%s" % (status_server_uri,
171                                  mounted_status_server_socket)
172             )
173         else:
174             status_server_opts = ("--net=host", )
175 
176         output_opts = ()
177         if test_output:
178             podman_output = runtime_task.task.runnable.output_dir
179             output_opts = ("-v", "%s:%s" % (test_output,
180                                             os.path.expanduser(podman_output)))
181 
182         image = self.config.get('spawner.podman.image')
183 
184         envs = [f"-e={k}={v}" for k, v in env_args.items()]
185         try:
186             # pylint: disable=W0201
187             _, stdout, _ = await self.podman.execute("create",
188                                                      *status_server_opts,
189                                                      *output_opts,
190                                                      entry_point_arg,
191                                                      *envs,
192                                                      image)
193         except PodmanException as ex:
194             msg = f"Could not create podman container: {ex}"
195             runtime_task.status = msg
196             return False
197 
198         return stdout.decode().strip()
199 
200     async def spawn_task(self, runtime_task):
201         self.create_task_output_dir(runtime_task)
202         podman_bin = self.config.get('spawner.podman.bin')
203         try:
204             # pylint: disable=W0201
205             self.podman = Podman(podman_bin)
206         except PodmanException as ex:
207             runtime_task.status = str(ex)
208             return False
209 
210         major, minor, _ = await self.python_version
211         # Return only the "to" location
212         eggs = self.get_eggs_paths(major, minor)
213         destination_eggs = ":".join(map(lambda egg: str(egg[1]), eggs))
214         env_args = {'PYTHONPATH': destination_eggs}
215         output_dir_path = self.task_output_dir(runtime_task)
216         container_id = await self._create_container_for_task(runtime_task,
217                                                              env_args,
218                                                              output_dir_path)
219 
220         runtime_task.spawner_handle = container_id
221 
222         await self.deploy_avocado(container_id)
223 
224         try:
225             # pylint: disable=W0201
226             returncode, _, _ = await self.podman.start(container_id)
227         except PodmanException as ex:
228             msg = f"Could not start container: {ex}"
229             runtime_task.status = msg
230             LOG.error(msg)
231             return False
232 
233         return returncode == 0
234 
235     def create_task_output_dir(self, runtime_task):
236         output_dir_path = self.task_output_dir(runtime_task)
237         output_podman_path = '~/avocado/job-results/spawner/task'
238 
239         os.makedirs(output_dir_path, exist_ok=True)
240         runtime_task.task.setup_output_dir(output_podman_path)
241 
242     async def wait_task(self, runtime_task):
243         while True:
244             if not self.is_task_alive(runtime_task):
245                 return
246             await asyncio.sleep(0.1)
247 
248     @staticmethod
249     async def check_task_requirements(runtime_task):
250         """Check the runtime task requirements needed to be able to run"""
251         # right now, limit the check to the runner availability.
252         if runtime_task.task.runnable.pick_runner_command() is None:
253             return False
254         return True
255
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/avocado/plugins/spawners/podman.py b/avocado/plugins/spawners/podman.py
--- a/avocado/plugins/spawners/podman.py
+++ b/avocado/plugins/spawners/podman.py
@@ -49,6 +49,16 @@
             help_msg=help_msg,
             default=default_distro)
 
+        help_msg = ('Avocado egg path to be used during initial bootstrap '
+                    'of avocado inside the isolated environment. By default, '
+                    'Avocado will try to download (or get from cache) an '
+                    'egg from its repository.')
+
+        settings.register_option(section=section,
+                                 key='avocado_spawner_egg',
+                                 help_msg=help_msg,
+                                 default=None)
+
 
 class PodmanCLI(CLI):
 
@@ -72,6 +82,13 @@
                                          long_arg='--spawner-podman-image',
                                          metavar='CONTAINER_IMAGE')
 
+        namespace = 'spawner.podman.avocado_spawner_egg'
+        long_arg = '--spawner-podman-avocado-egg'
+        settings.add_argparser_to_option(namespace=namespace,
+                                         parser=parser,
+                                         long_arg=long_arg,
+                                         metavar='AVOCADO_EGG')
+
     def run(self, config):
         pass
 
@@ -112,8 +129,14 @@
         # Setuptools
         # For now let's pin to setuptools 59.2.
         # TODO: Automatically get latest setuptools version.
-        eggs = [f"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg",
-                f"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg"]
+        eggs = [f"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg"]
+        local_egg = self.config.get('spawner.podman.avocado_spawner_egg')
+        if local_egg:
+            eggs.append(local_egg)
+        else:
+            remote_egg = f"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg"
+            eggs.append(remote_egg)
+
         for url in eggs:
             path = self._fetch_asset(url)
             to = os.path.join('/tmp/', os.path.basename(path))

{"golden_diff": "diff --git a/avocado/plugins/spawners/podman.py b/avocado/plugins/spawners/podman.py\n--- a/avocado/plugins/spawners/podman.py\n+++ b/avocado/plugins/spawners/podman.py\n@@ -49,6 +49,16 @@\n             help_msg=help_msg,\n             default=default_distro)\n \n+        help_msg = ('Avocado egg path to be used during initial bootstrap '\n+                    'of avocado inside the isolated environment. By default, '\n+                    'Avocado will try to download (or get from cache) an '\n+                    'egg from its repository.')\n+\n+        settings.register_option(section=section,\n+                                 key='avocado_spawner_egg',\n+                                 help_msg=help_msg,\n+                                 default=None)\n+\n \n class PodmanCLI(CLI):\n \n@@ -72,6 +82,13 @@\n                                          long_arg='--spawner-podman-image',\n                                          metavar='CONTAINER_IMAGE')\n \n+        namespace = 'spawner.podman.avocado_spawner_egg'\n+        long_arg = '--spawner-podman-avocado-egg'\n+        settings.add_argparser_to_option(namespace=namespace,\n+                                         parser=parser,\n+                                         long_arg=long_arg,\n+                                         metavar='AVOCADO_EGG')\n+\n     def run(self, config):\n         pass\n \n@@ -112,8 +129,14 @@\n         # Setuptools\n         # For now let's pin to setuptools 59.2.\n         # TODO: Automatically get latest setuptools version.\n-        eggs = [f\"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg\",\n-                f\"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg\"]\n+        eggs = [f\"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg\"]\n+        local_egg = self.config.get('spawner.podman.avocado_spawner_egg')\n+        if local_egg:\n+            eggs.append(local_egg)\n+        else:\n+            remote_egg = f\"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg\"\n+            eggs.append(remote_egg)\n+\n         for url in eggs:\n             path = self._fetch_asset(url)\n             to = os.path.join('/tmp/', os.path.basename(path))\n", "issue": "Allow the deployment and use of development versions of Avocado\n**Is your feature request related to a problem? Please describe.**\r\nEpic issue https://github.com/avocado-framework/avocado/issues/4934 covers aspects such as creating and publishing (AFAICT) packages for every Avocado release, for every Python version, and how they will be deployed and installed on `DeploymentSpawner`s.\r\n\r\nBut, this doesn't address the development time needs, where unreleased code may be needed in both the job side (say running out of a GIT checkout), and on the isolated spawner environment.\r\n\r\n**Describe the solution you'd like**\r\nThere should be a way to signal a preference for given wheels to be used, instead of the latest release ones. \r\n\r\n**Describe alternatives you've considered**\r\nBuilding the wheels is probably going to be a separate step (such as the current `make whee`, and It will probably be necessary to refer to them by a local (non HTTP(s)) uri.   Bonus point for an all-in-one `--mirror-this-repo-as-a-wheel-and-deploy-it` kind of feature, but I think this will be too complex.\r\n\n", "before_files": [{"content": "import asyncio\nimport json\nimport logging\nimport os\nimport subprocess\n\nfrom avocado.core.plugin_interfaces import CLI, DeploymentSpawner, Init\nfrom avocado.core.settings import settings\nfrom avocado.core.spawners.common import SpawnerMixin, SpawnMethod\nfrom avocado.core.version import VERSION\nfrom avocado.utils import distro\nfrom avocado.utils.asset import Asset\nfrom avocado.utils.podman import Podman, PodmanException\n\nLOG = logging.getLogger(__name__)\n\n\nENTRY_POINT_CMD = \"/tmp/avocado-runner\"\n\n\nclass PodmanSpawnerInit(Init):\n\n    description = 'Podman (container) based spawner initialization'\n\n    def initialize(self):\n        section = 'spawner.podman'\n\n        help_msg = 'Path to the podman binary'\n        settings.register_option(\n            section=section,\n            key='bin',\n            help_msg=help_msg,\n            default='/usr/bin/podman')\n\n        this_distro = distro.detect()\n        if this_distro != distro.UNKNOWN_DISTRO:\n            default_distro = '{0}:{1}'.format(this_distro.name,\n                                              this_distro.version)\n        else:\n            default_distro = 'fedora:latest'\n        help_msg = ('Image name to use when creating the container. '\n                    'The first default choice is a container image '\n                    'matching the current OS. If unable to detect, '\n                    'default becomes the latest Fedora release. Default '\n                    'on this system: {0}'.format(default_distro))\n        settings.register_option(\n            section=section,\n            key='image',\n            help_msg=help_msg,\n            default=default_distro)\n\n\nclass PodmanCLI(CLI):\n\n    name = 'podman'\n    description = 'podman spawner command line options for \"run\"'\n\n    def configure(self, parser):\n        super().configure(parser)\n        parser = parser.subcommands.choices.get('run', None)\n        if parser is None:\n            return\n\n        parser = parser.add_argument_group('podman spawner specific options')\n        settings.add_argparser_to_option(namespace='spawner.podman.bin',\n                                         parser=parser,\n                                         long_arg='--spawner-podman-bin',\n                                         metavar='PODMAN_BIN')\n\n        settings.add_argparser_to_option(namespace='spawner.podman.image',\n                                         parser=parser,\n                                         long_arg='--spawner-podman-image',\n                                         metavar='CONTAINER_IMAGE')\n\n    def run(self, config):\n        pass\n\n\nclass PodmanSpawner(DeploymentSpawner, SpawnerMixin):\n\n    description = 'Podman (container) based spawner'\n    METHODS = [SpawnMethod.STANDALONE_EXECUTABLE]\n\n    _PYTHON_VERSIONS_CACHE = {}\n\n    def is_task_alive(self, runtime_task):\n        if runtime_task.spawner_handle is None:\n            return False\n        podman_bin = self.config.get('spawner.podman.bin')\n        cmd = [podman_bin, \"ps\", \"--all\", \"--format={{.State}}\",\n               \"--filter=id=%s\" % runtime_task.spawner_handle]\n        process = subprocess.Popen(cmd,\n                                   stdin=subprocess.DEVNULL,\n                                   stdout=subprocess.PIPE,\n                                   stderr=subprocess.DEVNULL)\n        out, _ = process.communicate()\n        # FIXME: check how podman 2.x is reporting valid \"OK\" states\n        return out.startswith(b'Up ')\n\n    def _fetch_asset(self, url):\n        cachedirs = self.config.get('datadir.paths.cache_dirs')\n        asset = Asset(url, cache_dirs=cachedirs)\n        return asset.fetch()\n\n    def get_eggs_paths(self, py_major, py_minor):\n        \"\"\"Return the basic eggs needed to bootstrap Avocado.\n\n        This will return a tuple with the current location and where this\n        should be deployed.\n        \"\"\"\n        result = []\n        # Setuptools\n        # For now let's pin to setuptools 59.2.\n        # TODO: Automatically get latest setuptools version.\n        eggs = [f\"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg\",\n                f\"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg\"]\n        for url in eggs:\n            path = self._fetch_asset(url)\n            to = os.path.join('/tmp/', os.path.basename(path))\n            result.append((path, to))\n        return result\n\n    @property\n    async def python_version(self):\n        image = self.config.get('spawner.podman.image')\n        if image not in self._PYTHON_VERSIONS_CACHE:\n            if not self.podman:\n                msg = \"Cannot get Python version: self.podman not defined.\"\n                LOG.debug(msg)\n                return None, None, None\n            result = await self.podman.get_python_version(image)\n            self._PYTHON_VERSIONS_CACHE[image] = result\n        return self._PYTHON_VERSIONS_CACHE[image]\n\n    async def deploy_artifacts(self):\n        pass\n\n    async def deploy_avocado(self, where):\n        # Deploy all the eggs to container inside /tmp/\n        major, minor, _ = await self.python_version\n        eggs = self.get_eggs_paths(major, minor)\n\n        for egg, to in eggs:\n            await self.podman.copy_to_container(where, egg, to)\n\n    async def _create_container_for_task(self, runtime_task, env_args,\n                                         test_output=None):\n        mount_status_server_socket = False\n        mounted_status_server_socket = '/tmp/.status_server.sock'\n        status_server_uri = runtime_task.task.status_services[0].uri\n        if ':' not in status_server_uri:\n            # a unix domain socket is being used\n            mount_status_server_socket = True\n            runtime_task.task.status_services[0].uri = mounted_status_server_socket\n\n        _, _, python_binary = await self.python_version\n        entry_point_args = [python_binary,\n                            '-m',\n                            'avocado.core.nrunner',\n                            'task-run']\n\n        task = runtime_task.task\n        entry_point_args.extend(task.get_command_args())\n        entry_point = json.dumps(entry_point_args)\n        entry_point_arg = \"--entrypoint=\" + entry_point\n\n        if mount_status_server_socket:\n            status_server_opts = (\n                \"--privileged\",\n                \"-v\", \"%s:%s\" % (status_server_uri,\n                                 mounted_status_server_socket)\n            )\n        else:\n            status_server_opts = (\"--net=host\", )\n\n        output_opts = ()\n        if test_output:\n            podman_output = runtime_task.task.runnable.output_dir\n            output_opts = (\"-v\", \"%s:%s\" % (test_output,\n                                            os.path.expanduser(podman_output)))\n\n        image = self.config.get('spawner.podman.image')\n\n        envs = [f\"-e={k}={v}\" for k, v in env_args.items()]\n        try:\n            # pylint: disable=W0201\n            _, stdout, _ = await self.podman.execute(\"create\",\n                                                     *status_server_opts,\n                                                     *output_opts,\n                                                     entry_point_arg,\n                                                     *envs,\n                                                     image)\n        except PodmanException as ex:\n            msg = f\"Could not create podman container: {ex}\"\n            runtime_task.status = msg\n            return False\n\n        return stdout.decode().strip()\n\n    async def spawn_task(self, runtime_task):\n        self.create_task_output_dir(runtime_task)\n        podman_bin = self.config.get('spawner.podman.bin')\n        try:\n            # pylint: disable=W0201\n            self.podman = Podman(podman_bin)\n        except PodmanException as ex:\n            runtime_task.status = str(ex)\n            return False\n\n        major, minor, _ = await self.python_version\n        # Return only the \"to\" location\n        eggs = self.get_eggs_paths(major, minor)\n        destination_eggs = \":\".join(map(lambda egg: str(egg[1]), eggs))\n        env_args = {'PYTHONPATH': destination_eggs}\n        output_dir_path = self.task_output_dir(runtime_task)\n        container_id = await self._create_container_for_task(runtime_task,\n                                                             env_args,\n                                                             output_dir_path)\n\n        runtime_task.spawner_handle = container_id\n\n        await self.deploy_avocado(container_id)\n\n        try:\n            # pylint: disable=W0201\n            returncode, _, _ = await self.podman.start(container_id)\n        except PodmanException as ex:\n            msg = f\"Could not start container: {ex}\"\n            runtime_task.status = msg\n            LOG.error(msg)\n            return False\n\n        return returncode == 0\n\n    def create_task_output_dir(self, runtime_task):\n        output_dir_path = self.task_output_dir(runtime_task)\n        output_podman_path = '~/avocado/job-results/spawner/task'\n\n        os.makedirs(output_dir_path, exist_ok=True)\n        runtime_task.task.setup_output_dir(output_podman_path)\n\n    async def wait_task(self, runtime_task):\n        while True:\n            if not self.is_task_alive(runtime_task):\n                return\n            await asyncio.sleep(0.1)\n\n    @staticmethod\n    async def check_task_requirements(runtime_task):\n        \"\"\"Check the runtime task requirements needed to be able to run\"\"\"\n        # right now, limit the check to the runner availability.\n        if runtime_task.task.runnable.pick_runner_command() is None:\n            return False\n        return True\n", "path": "avocado/plugins/spawners/podman.py"}], "after_files": [{"content": "import asyncio\nimport json\nimport logging\nimport os\nimport subprocess\n\nfrom avocado.core.plugin_interfaces import CLI, DeploymentSpawner, Init\nfrom avocado.core.settings import settings\nfrom avocado.core.spawners.common import SpawnerMixin, SpawnMethod\nfrom avocado.core.version import VERSION\nfrom avocado.utils import distro\nfrom avocado.utils.asset import Asset\nfrom avocado.utils.podman import Podman, PodmanException\n\nLOG = logging.getLogger(__name__)\n\n\nENTRY_POINT_CMD = \"/tmp/avocado-runner\"\n\n\nclass PodmanSpawnerInit(Init):\n\n    description = 'Podman (container) based spawner initialization'\n\n    def initialize(self):\n        section = 'spawner.podman'\n\n        help_msg = 'Path to the podman binary'\n        settings.register_option(\n            section=section,\n            key='bin',\n            help_msg=help_msg,\n            default='/usr/bin/podman')\n\n        this_distro = distro.detect()\n        if this_distro != distro.UNKNOWN_DISTRO:\n            default_distro = '{0}:{1}'.format(this_distro.name,\n                                              this_distro.version)\n        else:\n            default_distro = 'fedora:latest'\n        help_msg = ('Image name to use when creating the container. '\n                    'The first default choice is a container image '\n                    'matching the current OS. If unable to detect, '\n                    'default becomes the latest Fedora release. Default '\n                    'on this system: {0}'.format(default_distro))\n        settings.register_option(\n            section=section,\n            key='image',\n            help_msg=help_msg,\n            default=default_distro)\n\n        help_msg = ('Avocado egg path to be used during initial bootstrap '\n                    'of avocado inside the isolated environment. By default, '\n                    'Avocado will try to download (or get from cache) an '\n                    'egg from its repository.')\n\n        settings.register_option(section=section,\n                                 key='avocado_spawner_egg',\n                                 help_msg=help_msg,\n                                 default=None)\n\n\nclass PodmanCLI(CLI):\n\n    name = 'podman'\n    description = 'podman spawner command line options for \"run\"'\n\n    def configure(self, parser):\n        super().configure(parser)\n        parser = parser.subcommands.choices.get('run', None)\n        if parser is None:\n            return\n\n        parser = parser.add_argument_group('podman spawner specific options')\n        settings.add_argparser_to_option(namespace='spawner.podman.bin',\n                                         parser=parser,\n                                         long_arg='--spawner-podman-bin',\n                                         metavar='PODMAN_BIN')\n\n        settings.add_argparser_to_option(namespace='spawner.podman.image',\n                                         parser=parser,\n                                         long_arg='--spawner-podman-image',\n                                         metavar='CONTAINER_IMAGE')\n\n        namespace = 'spawner.podman.avocado_spawner_egg'\n        long_arg = '--spawner-podman-avocado-egg'\n        settings.add_argparser_to_option(namespace=namespace,\n                                         parser=parser,\n                                         long_arg=long_arg,\n                                         metavar='AVOCADO_EGG')\n\n    def run(self, config):\n        pass\n\n\nclass PodmanSpawner(DeploymentSpawner, SpawnerMixin):\n\n    description = 'Podman (container) based spawner'\n    METHODS = [SpawnMethod.STANDALONE_EXECUTABLE]\n\n    _PYTHON_VERSIONS_CACHE = {}\n\n    def is_task_alive(self, runtime_task):\n        if runtime_task.spawner_handle is None:\n            return False\n        podman_bin = self.config.get('spawner.podman.bin')\n        cmd = [podman_bin, \"ps\", \"--all\", \"--format={{.State}}\",\n               \"--filter=id=%s\" % runtime_task.spawner_handle]\n        process = subprocess.Popen(cmd,\n                                   stdin=subprocess.DEVNULL,\n                                   stdout=subprocess.PIPE,\n                                   stderr=subprocess.DEVNULL)\n        out, _ = process.communicate()\n        # FIXME: check how podman 2.x is reporting valid \"OK\" states\n        return out.startswith(b'Up ')\n\n    def _fetch_asset(self, url):\n        cachedirs = self.config.get('datadir.paths.cache_dirs')\n        asset = Asset(url, cache_dirs=cachedirs)\n        return asset.fetch()\n\n    def get_eggs_paths(self, py_major, py_minor):\n        \"\"\"Return the basic eggs needed to bootstrap Avocado.\n\n        This will return a tuple with the current location and where this\n        should be deployed.\n        \"\"\"\n        result = []\n        # Setuptools\n        # For now let's pin to setuptools 59.2.\n        # TODO: Automatically get latest setuptools version.\n        eggs = [f\"https://github.com/avocado-framework/setuptools/releases/download/v59.2.0/setuptools-59.2.0-py{py_major}.{py_minor}.egg\"]\n        local_egg = self.config.get('spawner.podman.avocado_spawner_egg')\n        if local_egg:\n            eggs.append(local_egg)\n        else:\n            remote_egg = f\"https://github.com/avocado-framework/avocado/releases/download/{VERSION}/avocado_framework-{VERSION}-py{py_major}.{py_minor}.egg\"\n            eggs.append(remote_egg)\n\n        for url in eggs:\n            path = self._fetch_asset(url)\n            to = os.path.join('/tmp/', os.path.basename(path))\n            result.append((path, to))\n        return result\n\n    @property\n    async def python_version(self):\n        image = self.config.get('spawner.podman.image')\n        if image not in self._PYTHON_VERSIONS_CACHE:\n            if not self.podman:\n                msg = \"Cannot get Python version: self.podman not defined.\"\n                LOG.debug(msg)\n                return None, None, None\n            result = await self.podman.get_python_version(image)\n            self._PYTHON_VERSIONS_CACHE[image] = result\n        return self._PYTHON_VERSIONS_CACHE[image]\n\n    async def deploy_artifacts(self):\n        pass\n\n    async def deploy_avocado(self, where):\n        # Deploy all the eggs to container inside /tmp/\n        major, minor, _ = await self.python_version\n        eggs = self.get_eggs_paths(major, minor)\n\n        for egg, to in eggs:\n            await self.podman.copy_to_container(where, egg, to)\n\n    async def _create_container_for_task(self, runtime_task, env_args,\n                                         test_output=None):\n        mount_status_server_socket = False\n        mounted_status_server_socket = '/tmp/.status_server.sock'\n        status_server_uri = runtime_task.task.status_services[0].uri\n        if ':' not in status_server_uri:\n            # a unix domain socket is being used\n            mount_status_server_socket = True\n            runtime_task.task.status_services[0].uri = mounted_status_server_socket\n\n        _, _, python_binary = await self.python_version\n        entry_point_args = [python_binary,\n                            '-m',\n                            'avocado.core.nrunner',\n                            'task-run']\n\n        task = runtime_task.task\n        entry_point_args.extend(task.get_command_args())\n        entry_point = json.dumps(entry_point_args)\n        entry_point_arg = \"--entrypoint=\" + entry_point\n\n        if mount_status_server_socket:\n            status_server_opts = (\n                \"--privileged\",\n                \"-v\", \"%s:%s\" % (status_server_uri,\n                                 mounted_status_server_socket)\n            )\n        else:\n            status_server_opts = (\"--net=host\", )\n\n        output_opts = ()\n        if test_output:\n            podman_output = runtime_task.task.runnable.output_dir\n            output_opts = (\"-v\", \"%s:%s\" % (test_output,\n                                            os.path.expanduser(podman_output)))\n\n        image = self.config.get('spawner.podman.image')\n\n        envs = [f\"-e={k}={v}\" for k, v in env_args.items()]\n        try:\n            # pylint: disable=W0201\n            _, stdout, _ = await self.podman.execute(\"create\",\n                                                     *status_server_opts,\n                                                     *output_opts,\n                                                     entry_point_arg,\n                                                     *envs,\n                                                     image)\n        except PodmanException as ex:\n            msg = f\"Could not create podman container: {ex}\"\n            runtime_task.status = msg\n            return False\n\n        return stdout.decode().strip()\n\n    async def spawn_task(self, runtime_task):\n        self.create_task_output_dir(runtime_task)\n        podman_bin = self.config.get('spawner.podman.bin')\n        try:\n            # pylint: disable=W0201\n            self.podman = Podman(podman_bin)\n        except PodmanException as ex:\n            runtime_task.status = str(ex)\n            return False\n\n        major, minor, _ = await self.python_version\n        # Return only the \"to\" location\n        eggs = self.get_eggs_paths(major, minor)\n        destination_eggs = \":\".join(map(lambda egg: str(egg[1]), eggs))\n        env_args = {'PYTHONPATH': destination_eggs}\n        output_dir_path = self.task_output_dir(runtime_task)\n        container_id = await self._create_container_for_task(runtime_task,\n                                                             env_args,\n                                                             output_dir_path)\n\n        runtime_task.spawner_handle = container_id\n\n        await self.deploy_avocado(container_id)\n\n        try:\n            # pylint: disable=W0201\n            returncode, _, _ = await self.podman.start(container_id)\n        except PodmanException as ex:\n            msg = f\"Could not start container: {ex}\"\n            runtime_task.status = msg\n            LOG.error(msg)\n            return False\n\n        return returncode == 0\n\n    def create_task_output_dir(self, runtime_task):\n        output_dir_path = self.task_output_dir(runtime_task)\n        output_podman_path = '~/avocado/job-results/spawner/task'\n\n        os.makedirs(output_dir_path, exist_ok=True)\n        runtime_task.task.setup_output_dir(output_podman_path)\n\n    async def wait_task(self, runtime_task):\n        while True:\n            if not self.is_task_alive(runtime_task):\n                return\n            await asyncio.sleep(0.1)\n\n    @staticmethod\n    async def check_task_requirements(runtime_task):\n        \"\"\"Check the runtime task requirements needed to be able to run\"\"\"\n        # right now, limit the check to the runner availability.\n        if runtime_task.task.runnable.pick_runner_command() is None:\n            return False\n        return True\n", "path": "avocado/plugins/spawners/podman.py"}]}