From a8a889a00fcd1471252479c7bcb54b67db37aced Mon Sep 17 00:00:00 2001 From: Felipe Martin Date: Sun, 5 Jan 2020 18:16:35 +0100 Subject: [PATCH] Added basic docker action --- jeeves/core/actions/__init__.py | 5 ++- jeeves/core/actions/docker.py | 74 +++++++++++++++++++++++++++++++++ pyproject.toml | 1 + setup.cfg | 2 +- 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 jeeves/core/actions/docker.py diff --git a/jeeves/core/actions/__init__.py b/jeeves/core/actions/__init__.py index fbb127d..2a6a827 100644 --- a/jeeves/core/actions/__init__.py +++ b/jeeves/core/actions/__init__.py @@ -1 +1,4 @@ -PROVIDED_ACTIONS = ["jeeves.core.actions.shell:ScriptAction"] +PROVIDED_ACTIONS = [ + "jeeves.core.actions.shell:ScriptAction", + "jeeves.core.actions.docker:DockerAction", +] diff --git a/jeeves/core/actions/docker.py b/jeeves/core/actions/docker.py new file mode 100644 index 0000000..9a8c10c --- /dev/null +++ b/jeeves/core/actions/docker.py @@ -0,0 +1,74 @@ +from typing import Text + +import docker +import pydantic +import requests + +from jeeves.core.objects import Result +from .base import Action + + +class DockerAction(Action): + """ + .. automethod:: _run_container + """ + + id = "contrib/docker" + verbose_name = "Execute docker container" + + class Parameters(pydantic.BaseModel): + """ + +----------------+------+-----------+----------------------------------------------+ + | Parameter name | Type | Mandatory | Description | + +================+======+===========+==============================================+ + | ``image`` | text | no | Image to run (defaults to ``DEFAULT_IMAGE``) | + | ``command`` | text | yes | The command to be executed | + +----------------+------+-----------+----------------------------------------------+ + """ + + image: Text = "alpine:latest" + command: Text + remove_container: bool = True + + def _run_container(self): + """ + """ + pass + + def execute(self, **kwargs): + workspace = kwargs.get("workspace") + image = self.parameters.image + command = self.parameters.command + environment = {"WORKSPACE_PATH": "/workspace"} + + client = docker.from_env() + + self.logger.info("Pulling image...") + try: + client.images.get(image) + except docker.errors.ImageNotFound: + self.logger.error("Image does not exist") + return Result(success=False) + + self.logger.info("Execute command in container...") + container = client.containers.run( + image=image, + command=command, + detach=True, + environment=environment, + volumes={"/workspace": {"bind": str(workspace.path), "mode": "rw"}}, + ) + + try: + result = container.wait(timeout=30, condition="not-running") + logs = container.logs() + success = result["StatusCode"] == 0 + except requests.exceptions.ReadTimeout: + success = False + logs = container.logs() + + if self.parameters.remove_container: + self.logger.info("Removing container") + container.remove() + + return Result(success=success, output=logs) diff --git a/pyproject.toml b/pyproject.toml index c3c5d12..6c4fbfb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ jinja2 = "^2.10" pydantic = "^0.32.2" click = "^7.0" toml = "^0.10.0" +docker = "^4.1.0" [tool.poetry.dev-dependencies] black = {version = "^18.3-alpha.0", allow-prereleases = true} diff --git a/setup.cfg b/setup.cfg index 4004ff6..007f4ac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,6 +11,6 @@ include_trailing_comma = True length_sort = 1 lines_between_types = 0 line_length = 88 -known_third_party = click,django,factory,pydantic,pytest,toml +known_third_party = click,django,docker,factory,pydantic,pytest,requests,toml sections = FUTURE, STDLIB, DJANGO, THIRDPARTY, FIRSTPARTY, LOCALFOLDER no_lines_before = LOCALFOLDER