--- /dev/null
+import subprocess
+import signal
+import uuid
+from typing import Optional, List
+import shutil
+import pathlib
+import tarfile
+import os
+import time
+import sys
+import requests
+
+SOCKET_DIR = pathlib.Path("/dev/shm")
+
+class PodmanService:
+ def __init__(self):
+ self._process: Optional[subprocess.Popen] = None
+ def __enter__(self):
+ self._process = subprocess.Popen("podman system service tcp:localhost:13579 --log-level=info --time=0", shell=True)
+ time.sleep(0.5) # required to prevent connection
+ return PodmanServiceManager("http://localhost:13579")
+ def __exit__(self, ex_type, ex_value, ex_traceback):
+ failed_while_running = (self._process.poll() is not None)
+ self._process.send_signal(signal.SIGINT)
+
+ time.sleep(0.5) # fixes interleaved stacktraces with podman's output
+
+ if failed_while_running:
+ raise Exception("Failed to properly start the podman service", ex_value)
+
+class PodmanServiceManager:
+ """
+ Using HTTP Rest API new in version 2.0. Documentation here:
+ https://docs.podman.io/en/latest/_static/api.html
+ """
+ _API_VERSION = "v1.0.0"
+
+ def __init__(self, url):
+ self._url = url
+
+ def _create_url(self, path):
+ return self._url + '/' + PodmanServiceManager._API_VERSION + '/' + path
+
+ @staticmethod
+ def _create_tar_achive(directory: pathlib.Path, outfile: pathlib.Path):
+ with tarfile.open(str(outfile), "w:gz") as tar_handle:
+ for root, _, files in os.walk(str(directory)):
+ for file in files:
+ tar_handle.add(os.path.join(root, file))
+
+ def build_image(self, context_dir: pathlib.Path, image: str):
+ # create tar archive out of the context_dir (weird, but there is no other way to specify context)
+ tar = pathlib.Path("/tmp/context.tar.gz")
+ PodmanServiceManager._create_tar_achive(context_dir, tar)
+ try:
+ # send the API request
+ with open(tar, 'rb') as f:
+ response = requests.post(self._create_url('libpod/build'), params=[("t", image)], data=f)
+ response.raise_for_status()
+
+ finally:
+ # cleanup the tar file
+ tar.unlink()
+
+ def start_temporary_and_wait(self, image: str, command: List[str]) -> int:
+ # create the container
+ response = requests.post(self._create_url('libpod/containers/create'), json={
+ "command": command,
+ "image": image,
+ "remove": True,
+ }
+ )
+ response.raise_for_status()
+ container_id = response.json()['Id']
+
+ # start the container
+ response = requests.post(self._create_url(f'libpod/containers/{container_id}/start'))
+ response.raise_for_status()
+
+ # the container is doing something
+
+ # wait for the container
+ response = requests.post(self._create_url(f'libpod/containers/{container_id}/wait'), params=[('condition', 'exited')], timeout=None)
+ response.raise_for_status()
+ return int(response.text)
+
+
+
+
+def main():
+ with PodmanService() as manager:
+ IMAGE = "testenv"
+ manager.build_image(pathlib.Path("."), IMAGE)
+ res = manager.start_temporary_and_wait(IMAGE, ["bash", "-c", "exit 12"])
+ print("Exit code", res)
+
+
+if __name__ == "__main__":
+ main()
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
+[[package]]
+name = "certifi"
+version = "2020.12.5"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "dev"
+optional = false
+python-versions = "*"
+
[[package]]
name = "chardet"
version = "3.0.4"
[[package]]
name = "idna"
-version = "3.1"
+version = "2.10"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
-python-versions = ">=3.4"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "idna-ssl"
optional = false
python-versions = "*"
+[[package]]
+name = "requests"
+version = "2.25.1"
+description = "Python HTTP for Humans."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+chardet = ">=3.0.2,<5"
+idna = ">=2.5,<3"
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+
[[package]]
name = "requirements-detector"
version = "0.7"
optional = false
python-versions = "*"
+[[package]]
+name = "urllib3"
+version = "1.26.3"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
[[package]]
name = "virtualenv"
version = "20.4.2"
[metadata]
lock-version = "1.1"
python-versions = "^3.6.12"
-content-hash = "f88e69a2748e50bd111eeda6d8e56c7cdbcea2f419830a697a54e5e8502a1403"
+content-hash = "3c88907317606698cea3e6abbf6e5d84a68a32f942f90e3115f8a02f88539051"
[metadata.files]
aiohttp = [
black = [
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
]
+certifi = [
+ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
+ {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
+]
chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
{file = "GitPython-3.1.13.tar.gz", hash = "sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a"},
]
idna = [
- {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
- {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"},
+ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
+ {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
idna-ssl = [
{file = "idna-ssl-1.1.0.tar.gz", hash = "sha256:a933e3bb13da54383f9e8f35dc4f9cb9eb9b3b78c6b36f311254d6d0d92c6c7c"},
{file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"},
{file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"},
]
+requests = [
+ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
+ {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
+]
requirements-detector = [
{file = "requirements-detector-0.7.tar.gz", hash = "sha256:0d1e13e61ed243f9c3c86e6cbb19980bcb3a0e0619cde2ec1f3af70fdbee6f7b"},
]
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
{file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
]
+urllib3 = [
+ {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
+ {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
+]
virtualenv = [
{file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"},
{file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"},