atex 0.9__py3-none-any.whl → 0.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. atex/aggregator/__init__.py +60 -0
  2. atex/{orchestrator/aggregator.py → aggregator/json.py} +6 -21
  3. atex/cli/__init__.py +11 -1
  4. atex/cli/libvirt.py +3 -2
  5. atex/cli/testingfarm.py +48 -3
  6. atex/connection/podman.py +2 -4
  7. atex/connection/ssh.py +7 -14
  8. atex/executor/executor.py +18 -17
  9. atex/executor/scripts.py +5 -3
  10. atex/executor/testcontrol.py +1 -1
  11. atex/orchestrator/__init__.py +76 -3
  12. atex/orchestrator/{orchestrator.py → adhoc.py} +183 -103
  13. atex/{provision → provisioner}/__init__.py +49 -37
  14. atex/{provision → provisioner}/libvirt/libvirt.py +21 -14
  15. atex/{provision → provisioner}/libvirt/locking.py +3 -1
  16. atex/provisioner/podman/__init__.py +2 -0
  17. atex/provisioner/podman/podman.py +169 -0
  18. atex/{provision → provisioner}/testingfarm/api.py +53 -44
  19. atex/{provision → provisioner}/testingfarm/testingfarm.py +17 -23
  20. atex/util/log.py +62 -67
  21. atex/util/subprocess.py +46 -12
  22. atex/util/threads.py +7 -0
  23. atex-0.10.dist-info/METADATA +86 -0
  24. atex-0.10.dist-info/RECORD +44 -0
  25. atex/provision/podman/__init__.py +0 -1
  26. atex/provision/podman/podman.py +0 -274
  27. atex-0.9.dist-info/METADATA +0 -178
  28. atex-0.9.dist-info/RECORD +0 -43
  29. /atex/{provision → provisioner}/libvirt/VM_PROVISION +0 -0
  30. /atex/{provision → provisioner}/libvirt/__init__.py +0 -0
  31. /atex/{provision → provisioner}/libvirt/setup-libvirt.sh +0 -0
  32. /atex/{provision → provisioner}/testingfarm/__init__.py +0 -0
  33. {atex-0.9.dist-info → atex-0.10.dist-info}/WHEEL +0 -0
  34. {atex-0.9.dist-info → atex-0.10.dist-info}/entry_points.txt +0 -0
  35. {atex-0.9.dist-info → atex-0.10.dist-info}/licenses/COPYING.txt +0 -0
@@ -1,178 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: atex
3
- Version: 0.9
4
- Summary: Ad-hoc Test EXecutor
5
- Project-URL: Homepage, https://github.com/RHSecurityCompliance/atex
6
- License-Expression: GPL-3.0-or-later
7
- License-File: COPYING.txt
8
- Classifier: Operating System :: POSIX :: Linux
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Topic :: Software Development :: Testing
11
- Requires-Python: >=3.11
12
- Requires-Dist: fmf>=1.6
13
- Requires-Dist: pyyaml
14
- Requires-Dist: urllib3<3,>=2
15
- Description-Content-Type: text/markdown
16
-
17
- # ATEX = Ad-hoc Test EXecutor
18
-
19
- A collections of Python APIs to provision operating systems, collect
20
- and execute [FMF](https://github.com/teemtee/fmf/)-style tests, gather
21
- and organize their results and generate reports from those results.
22
-
23
- The name comes from a (fairly unique to FMF/TMT ecosystem) approach that
24
- allows provisioning a pool of systems and scheduling tests on them as one would
25
- on an ad-hoc pool of thread/process workers - once a worker becomes free,
26
- it receives a test to run.
27
- This is in contrast to splitting a large list of N tests onto M workers
28
- like N/M, which yields significant time penalties due to tests having
29
- very varies runtimes.
30
-
31
- Above all, this project is meant to be a toolbox, not a silver-plate solution.
32
- Use its Python APIs to build a CLI tool for your specific use case.
33
- The CLI tool provided here is just for demonstration / testing, not for serious
34
- use - we want to avoid huge modular CLIs for Every Possible Scenario. That's
35
- the job of the Python API. Any CLI should be simple by nature.
36
-
37
- ---
38
-
39
- THIS PROJECT IS HEAVILY WIP, THINGS WILL MOVE AROUND, CHANGE AND OTHERWISE
40
- BREAK. DO NOT USE IT (for now).
41
-
42
- ---
43
-
44
- ## License
45
-
46
- Unless specified otherwise, any content within this repository is distributed
47
- under the GNU GPLv3 license, see the [COPYING.txt](COPYING.txt) file for more.
48
-
49
- ## Testing this project
50
-
51
- There are some limited sanity tests provided via `pytest`, although:
52
-
53
- * Some require additional variables (ie. Testing Farm) and will ERROR
54
- without them.
55
- * Some take a long time (ie. Testing Farm) due to system provisioning
56
- taking a long time, so install `pytest-xdist` and run with a large `-n`.
57
-
58
- Currently, the recommended approach is to split the execution:
59
-
60
- ```
61
- # synchronously, because podman CLI has concurrency issues
62
- pytest tests/provision/test_podman.py
63
-
64
- # in parallel, because provisioning takes a long time
65
- export TESTING_FARM_API_TOKEN=...
66
- export TESTING_FARM_COMPOSE=...
67
- pytest -n 20 tests/provision/test_podman.py
68
-
69
- # fast enough for synchronous execution
70
- pytest tests/fmf
71
- ```
72
-
73
- ## Parallelism and cleanup
74
-
75
- There are effectively 3 methods of running things in parallel in Python:
76
-
77
- - `threading.Thread` (and related `concurrent.futures` classes)
78
- - `multiprocessing.Process` (and related `concurrent.futures` classes)
79
- - `asyncio`
80
-
81
- and there is no clear winner (in terms of cleanup on `SIGTERM` or Ctrl-C):
82
-
83
- - `Thread` has signal handlers only in the main thread and is unable to
84
- interrupt any running threads without super ugly workarounds like `sleep(1)`
85
- in every thread, checking some "pls exit" variable
86
- - `Process` is too heavyweight and makes sharing native Python objects hard,
87
- but it does handle signals in each process individually
88
- - `asyncio` handles interrupting perfectly (every `try`/`except`/`finally`
89
- completes just fine, `KeyboardInterrupt` is raised in every async context),
90
- but async python is still (3.14) too weird and unsupported
91
- - `asyncio` effectively re-implements `subprocess` with a slightly different
92
- API, same with `asyncio.Transport` and derivatives reimplementing `socket`
93
- - 3rd party libraries like `requests` or `urllib3` don't support it, one needs
94
- to resort to spawning these in separate threads anyway
95
- - same with `os.*` functions and syscalls
96
- - every thing exposed via API needs to have 2 copies - async and non-async,
97
- making it unbearable
98
- - other stdlib bugs, ie. "large" reads returning BlockingIOError sometimes
99
-
100
- The approach chosen by this project was to use `threading.Thread`, and
101
- implement thread safety for classes and their functions that need it.
102
- For example:
103
-
104
- ```python
105
- class MachineReserver:
106
- def __init__(self):
107
- self.lock = threading.RLock()
108
- self.job = None
109
- self.proc = None
110
-
111
- def reserve(self, ...):
112
- try:
113
- ...
114
- job = schedule_new_job_on_external_service()
115
- with self.lock:
116
- self.job = job
117
- ...
118
- while not reserved(self.job):
119
- time.sleep(60)
120
- ...
121
- with self.lock:
122
- self.proc = subprocess.Popen(["ssh", f"{user}@{host}", ...)
123
- ...
124
- return machine
125
- except Exception:
126
- self.abort()
127
- raise
128
-
129
- def abort(self):
130
- with self.lock:
131
- if self.job:
132
- cancel_external_service(self.job)
133
- self.job = None
134
- if self.proc:
135
- self.proc.kill()
136
- self.proc = None
137
- ```
138
-
139
- Here, it is expected for `.reserve()` to be called in a long-running thread that
140
- provisions a new machine on some external service, waits for it to be installed
141
- and reserved, connects an ssh session to it and returns it back.
142
-
143
- But equally, `.abort()` can be called from an external thread and clean up any
144
- non-pythonic resources (external jobs, processes, temporary files, etc.) at
145
- which point **we don't care what happens to .reserve()**, it will probably fail
146
- with some exception, but doesn't do any harm.
147
-
148
- Here is where `daemon=True` threads come in handy - we can simply call `.abort()`
149
- from a `KeyboardInterrupt` (or `SIGTERM`) handle in the main thread, and just
150
- exit, automatically killing any leftover threads that are uselessly sleeping.
151
- (Realistically, we might want to spawn new threads to run many `.abort()`s in
152
- parallel, but the main thread can wait for those just fine.)
153
-
154
- It is not perfect, but it's probably the best Python can do.
155
-
156
- Note that races can still occur between a resource being reserved and written
157
- to `self.*` for `.abort()` to free, so resource de-allocation is not 100%
158
- guaranteed, but single-threaded interrupting has the same issue.
159
- Do have fallbacks (ie. max reserve times on the external service).
160
-
161
- Also note that `.reserve()` and `.abort()` could be also called by a context
162
- manager as `__enter__` and `__exit__`, ie. by a non-threaded caller (running
163
- everything in the main thread).
164
-
165
-
166
- ## Unsorted notes
167
-
168
- TODO: codestyle from contest
169
-
170
- ```
171
- - this is not tmt, the goal is to make a python toolbox *for* making runcontest
172
- style tools easily, not to replace those tools with tmt-style CLI syntax
173
-
174
- - the whole point is to make usecase-targeted easy-to-use tools that don't
175
- intimidate users with 1 KB long command line, and runcontest is a nice example
176
-
177
- - TL;DR - use a modular pythonic approach, not a gluetool-style long CLI
178
- ```
atex-0.9.dist-info/RECORD DELETED
@@ -1,43 +0,0 @@
1
- atex/__init__.py,sha256=LdX67gprtHYeAkjLhFPKzpc7ECv2rHxUbHKDGbGXO1c,517
2
- atex/fmf.py,sha256=gkJXIaRO7_KvwJR-V6Tc1NVn4a9Hq7hoBLQLhxYIdbg,8834
3
- atex/cli/__init__.py,sha256=erHv68SsybRbdgJ60013y9jVqY1ec-cb9T9ThPCJ_HY,2408
4
- atex/cli/fmf.py,sha256=HfbTgFbCwK4Nuyq6vtGutcq_4-4kj-tmoqzXUn3AYtY,3573
5
- atex/cli/libvirt.py,sha256=vGcan9u0Nor_DaEjgOj3Yzjtlq4WNVLr5oYMb8zFb6M,3743
6
- atex/cli/testingfarm.py,sha256=DHMupwU3AjbkwkzvzdhlxtsuBnxVaba5i8T8g9sKRug,7569
7
- atex/connection/__init__.py,sha256=dj8ZBcEspom7Z_UjecfLGBRNvLZ3dyGR9q19i_B4xpY,3880
8
- atex/connection/podman.py,sha256=JUsi4jOkNFGsEHCqUQF003R2yRfJPsMnUSBEZM3H3Kk,1953
9
- atex/connection/ssh.py,sha256=c8v01mj0pf6VU8z2cLLCvPGPmGUZp8wQsTG441k3Qy4,13674
10
- atex/executor/__init__.py,sha256=XCfhi7QDELjey7N1uzhMjc46Kp1Jsd5bOCf52I27SCE,85
11
- atex/executor/duration.py,sha256=x06sItKOZi6XA8KszQwZGpIb1Z_L-HWqIwZKo2SDo0s,1759
12
- atex/executor/executor.py,sha256=OcKBXQ77OktmzPWAbdCb81Yj44Jr4SEkwW_CxdEhFP4,15674
13
- atex/executor/reporter.py,sha256=MceFmHFt0bTEClBZbRI1WnFbfMhR0e1noOzcu7gjKuQ,3403
14
- atex/executor/scripts.py,sha256=6xqP-m_9UCVb3z0tCWIMczimz_cTeCIJucSp0AGYsRw,5567
15
- atex/executor/testcontrol.py,sha256=O4V4jRx7lZv-El1i9jkPha-aWzjq_8m6loK7sNGVD14,12695
16
- atex/orchestrator/__init__.py,sha256=GSHtWoZjJrLiatjUHsMAkYB8VebSQD9ApQw0ymCrGTQ,212
17
- atex/orchestrator/aggregator.py,sha256=naypN8T42iPau_4eLeXak-INR1_vsYhP_QSp3xLM_FA,3837
18
- atex/orchestrator/orchestrator.py,sha256=qAwka7MqpAWA6mLgsMl1m1HpQP2mERcaAASNvJwgfTM,14570
19
- atex/provision/__init__.py,sha256=Pj_JnDaJA8DzKRGmLcc2JiwPsuQc2TYlE-hDR56SwTY,4083
20
- atex/provision/libvirt/VM_PROVISION,sha256=7pkZ-ozgTyK4qNGC-E-HUznr4IhbosWSASbB72Gknl8,2664
21
- atex/provision/libvirt/__init__.py,sha256=pKG5IpZSC2IHs5wL2ecQx_fd9AzAXEbZmDzA7RyZsfM,119
22
- atex/provision/libvirt/libvirt.py,sha256=7wux-zhQoNODJoPB4iMqPlGEJe7rEEZeLSOtMU3TB0M,18160
23
- atex/provision/libvirt/locking.py,sha256=e9zbtR0uzJZBAzucfhs9VQaDCx-jp0pWJi1pb7JPK4w,5630
24
- atex/provision/libvirt/setup-libvirt.sh,sha256=oCMy9SCnbC_QuAzO2sFwvB5ui1kMQ6uviHsgdXyoFXc,2428
25
- atex/provision/podman/__init__.py,sha256=V9miIDQV-CyDVcbf2-1qtDbXDhhZzJza5oXas-JxI8o,66
26
- atex/provision/podman/podman.py,sha256=FJK4uLDuHBj8_3Wlg4a-4JgGIRGyLX4v7UVcxEorHbA,9578
27
- atex/provision/testingfarm/__init__.py,sha256=kZncgLGdRCR4FMaRQr2GTwJ8vjlA-24ri8JO2ueZJuw,113
28
- atex/provision/testingfarm/api.py,sha256=9xeoIN28KumJyMUEUoyEUZcIxVH9G34Wecyy3bp2Cmg,21612
29
- atex/provision/testingfarm/testingfarm.py,sha256=-Q33FAretSGPWdmNJUywJZVfXYGtsAfLP5uqRZTGfQQ,8631
30
- atex/util/__init__.py,sha256=cWHFbtQ4mDlKe6lXyPDWRmWJOTcHDGfVuW_-GYa8hB0,1473
31
- atex/util/dedent.py,sha256=SEuJMtLzqz3dQ7g7qyZzEJ9VYynVlk52tQCJY-FveXo,603
32
- atex/util/libvirt.py,sha256=kDZmT6xLYEZkQNLZY98gJ2M48DDWXxHF8rQY9PnjB3U,660
33
- atex/util/log.py,sha256=70f6YiF2RwNUfE5BXZMSQonzfI_uxQoD7_S4bRD_Btw,2466
34
- atex/util/named_mapping.py,sha256=UBMe9TetjV-DGPhjYjJ42YtC40FVPKAAEROXl9MA5fo,4700
35
- atex/util/path.py,sha256=x-kXqiWCVodfZWbEwtC5A8LFvutpDIPYv2m0boZSlXU,504
36
- atex/util/ssh_keygen.py,sha256=9yuSl2yBV7pG3Qfsf9tossVC00nbIUrAeLdbwTykpjk,384
37
- atex/util/subprocess.py,sha256=IQT9QHe2kMaaO_XPSry-DwObYstGsq6_QdwdbhYDjko,1826
38
- atex/util/threads.py,sha256=46-5nV-qJqi1YZ4qEshmZXGUxr8j9_9xT9eEpkjRr5I,3355
39
- atex-0.9.dist-info/METADATA,sha256=iLGFHVxeK9KWrPaRdL4hkbNkb-OgJkeohHhxYz90tAI,6857
40
- atex-0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- atex-0.9.dist-info/entry_points.txt,sha256=pLqJdcfeyQTgup2h6dWb6SvkHhtOl-W5Eg9zV8moK0o,39
42
- atex-0.9.dist-info/licenses/COPYING.txt,sha256=oEuj51jdmbXcCUy7pZ-KE0BNcJTR1okudRp5zQ0yWnU,670
43
- atex-0.9.dist-info/RECORD,,
File without changes
File without changes
File without changes
File without changes