From da1a615f65306c2cc8e44b7066b40ac0c0ad457b Mon Sep 17 00:00:00 2001 From: Vasek Sraier Date: Thu, 4 Mar 2021 17:11:21 +0100 Subject: [PATCH] integration testing: initial Podman API code --- manager/integration/Dockerfile | 3 ++ manager/integration/runner.py | 99 ++++++++++++++++++++++++++++++++++ manager/poetry.lock | 61 +++++++++++++++++++-- manager/pyproject.toml | 1 + 4 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 manager/integration/Dockerfile create mode 100644 manager/integration/runner.py diff --git a/manager/integration/Dockerfile b/manager/integration/Dockerfile new file mode 100644 index 000000000..0605685f8 --- /dev/null +++ b/manager/integration/Dockerfile @@ -0,0 +1,3 @@ +FROM debian:latest + +CMD sleep 20 && echo "hello" && exit 5 diff --git a/manager/integration/runner.py b/manager/integration/runner.py new file mode 100644 index 000000000..1cea09249 --- /dev/null +++ b/manager/integration/runner.py @@ -0,0 +1,99 @@ +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() diff --git a/manager/poetry.lock b/manager/poetry.lock index 5e3e2a910..2ea8f77f1 100644 --- a/manager/poetry.lock +++ b/manager/poetry.lock @@ -108,6 +108,14 @@ typing-extensions = ">=3.7.4" 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" @@ -224,11 +232,11 @@ gitdb = ">=4.0.1,<5" [[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" @@ -650,6 +658,24 @@ category = "dev" 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" @@ -809,6 +835,19 @@ category = "main" 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" @@ -873,7 +912,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake [metadata] lock-version = "1.1" python-versions = "^3.6.12" -content-hash = "f88e69a2748e50bd111eeda6d8e56c7cdbcea2f419830a697a54e5e8502a1403" +content-hash = "3c88907317606698cea3e6abbf6e5d84a68a32f942f90e3115f8a02f88539051" [metadata.files] aiohttp = [ @@ -942,6 +981,10 @@ bandit = [ 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"}, @@ -1038,8 +1081,8 @@ gitpython = [ {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"}, @@ -1319,6 +1362,10 @@ regex = [ {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"}, ] @@ -1434,6 +1481,10 @@ typing-extensions = [ {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"}, diff --git a/manager/pyproject.toml b/manager/pyproject.toml index 7841f3821..dab0a5dd1 100644 --- a/manager/pyproject.toml +++ b/manager/pyproject.toml @@ -22,6 +22,7 @@ tox = "^3.21.4" tox-pyenv = "^1.1.0" poethepoet = "^0.9.0" prospector = {extras = ["with_mypy", "with_bandit"], version = "^1.3.1"} +requests = "^2.25.1" [tool.poe.tasks] run = { cmd = "python -m knot_resolver_manager", help = "Run the manager" } -- 2.47.3