diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a81c8ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,138 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..8a653ff --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] +python-language-server = {extras = ["all"],version = "*"} + +[packages] + +[requires] +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..7483a6a --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,195 @@ +{ + "_meta": { + "hash": { + "sha256": "00be801646ed78999c7a9321245796cdb44c060c00a84e2dc788e9609389d135" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "astroid": { + "hashes": [ + "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1", + "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38" + ], + "version": "==2.4.1" + }, + "autopep8": { + "hashes": [ + "sha256:152fd8fe47d02082be86e05001ec23d6f420086db56b17fc883f3f965fb34954" + ], + "version": "==1.5.2" + }, + "flake8": { + "hashes": [ + "sha256:c69ac1668e434d37a2d2880b3ca9aafd54b3a10a3ac1ab101d22f29e29cf8634", + "sha256:ccaa799ef9893cebe69fdfefed76865aeaefbb94cb8545617b2298786a4de9a5" + ], + "version": "==3.8.2" + }, + "isort": { + "hashes": [ + "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", + "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" + ], + "version": "==4.3.21" + }, + "jedi": { + "hashes": [ + "sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798", + "sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030" + ], + "version": "==0.17.0" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", + "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", + "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", + "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", + "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", + "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", + "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", + "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", + "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", + "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", + "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", + "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", + "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", + "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", + "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", + "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", + "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", + "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", + "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", + "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", + "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" + ], + "version": "==1.4.3" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "parso": { + "hashes": [ + "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0", + "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c" + ], + "version": "==0.7.0" + }, + "pluggy": { + "hashes": [ + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + ], + "version": "==0.13.1" + }, + "pycodestyle": { + "hashes": [ + "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", + "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + ], + "version": "==2.6.0" + }, + "pydocstyle": { + "hashes": [ + "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586", + "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5" + ], + "version": "==5.0.2" + }, + "pyflakes": { + "hashes": [ + "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", + "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + ], + "version": "==2.2.0" + }, + "pylint": { + "hashes": [ + "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c", + "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b" + ], + "version": "==2.5.2" + }, + "python-jsonrpc-server": { + "hashes": [ + "sha256:1f85f75f37f923149cc0aa078474b6df55b708e82ed819ca8846a65d7d0ada7f", + "sha256:c73bf5495c9dd4d2f902755bedeb6da5afe778e0beee82f0e195c4655352fe37" + ], + "version": "==0.3.4" + }, + "python-language-server": { + "extras": [ + "all" + ], + "hashes": [ + "sha256:3609ade1041066b55a351c371b1b1c9e7e61ba6446ec3716c6efdf928675bd13", + "sha256:636e6f09ea23658729fa135af737bd2d84fbbc850c9d99299eab60e9180ec0a9" + ], + "index": "pypi", + "version": "==0.33.0" + }, + "rope": { + "hashes": [ + "sha256:658ad6705f43dcf3d6df379da9486529cf30e02d9ea14c5682aa80eb33b649e1" + ], + "version": "==0.17.0" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "version": "==1.15.0" + }, + "snowballstemmer": { + "hashes": [ + "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", + "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" + ], + "version": "==2.0.0" + }, + "toml": { + "hashes": [ + "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", + "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + ], + "version": "==0.10.1" + }, + "ujson": { + "hashes": [ + "sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86" + ], + "markers": "platform_system != 'Windows'", + "version": "==1.35" + }, + "wrapt": { + "hashes": [ + "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" + ], + "version": "==1.12.1" + }, + "yapf": { + "hashes": [ + "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427", + "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9" + ], + "version": "==0.30.0" + } + } +} diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sandbox.py b/sandbox.py deleted file mode 100644 index 41f467d..0000000 --- a/sandbox.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Optional -from dataclasses import dataclass -from enum import Enum - - -class Language(Enum): - PYTHON = "Dockerfile.py" - JAVASCRIPT = "Dockerfile.js" - GOLANG = "Dockerfile.go" - RUST = "Dockerfile.rs" - - def __repr__(self): - return "<{}.{}>".format(self.__class__.__name__, self.name) - - -@dataclass(frozen=True) -class Sandbox: - """ - IDK what I'm going to do with this yet. - - Definitely something... - """ - language: Language - self_guided_project: Optional[str] - remote_dir: str - - def build(self): - raise NotImplementedError() diff --git a/sandboxes/__init__.py b/sandboxes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sandboxes/docker_files/Dockerfile.go b/sandboxes/docker_files/Dockerfile.go new file mode 100644 index 0000000..3a646f4 --- /dev/null +++ b/sandboxes/docker_files/Dockerfile.go @@ -0,0 +1,8 @@ +ARG PROJECT_ROOT + +FROM golang:latest +ADD ${PROJECT_ROOT} . + +RUN go build -o app +ENTRYPOINT app +# CMD ['go', 'run', '.'] \ No newline at end of file diff --git a/sandboxes/docker_files/Dockerfile.py b/sandboxes/docker_files/Dockerfile.py new file mode 100644 index 0000000..5483cbb --- /dev/null +++ b/sandboxes/docker_files/Dockerfile.py @@ -0,0 +1,4 @@ +FROM python:latest +COPY $PROJECT_ROOT . +RUN pip install . +CMD ["python", "-m", "unittest"] diff --git a/sandboxes/docker_files/__init__.py b/sandboxes/docker_files/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sandboxes/docker_files/tests/__init__.py b/sandboxes/docker_files/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sandboxes/docker_files/tests/test_go/main.go b/sandboxes/docker_files/tests/test_go/main.go new file mode 100644 index 0000000..c0c6965 --- /dev/null +++ b/sandboxes/docker_files/tests/test_go/main.go @@ -0,0 +1,9 @@ +package main + + +import "fmt" + + +func main() { + fmt.Println("Hello World!") +} diff --git a/sandboxes/docker_files/tests/test_sandbox.py b/sandboxes/docker_files/tests/test_sandbox.py new file mode 100644 index 0000000..6d9638d --- /dev/null +++ b/sandboxes/docker_files/tests/test_sandbox.py @@ -0,0 +1,21 @@ +import docker +import pytest + +from os.path import abspath, dirname, join + +from sandbox import Sandbox + + +class TestSandbox: + @pytest.fixture(scope="module") + def client(self) -> docker.APIClient: + return docker.DockerClient(base_url="unix:///var/run/docker.sock") + + def test_go_sample(self, client): + d_name = dirname(__file__) + box = Sandbox(client=client, + language="golang", + project_root=abspath(join(d_name, "test_go_sample"))) + + container = box.execute() + assert container.attach() == b"Hello World!\n" diff --git a/sandboxes/sandbox.py b/sandboxes/sandbox.py new file mode 100644 index 0000000..38952d0 --- /dev/null +++ b/sandboxes/sandbox.py @@ -0,0 +1,52 @@ +import docker +import rootpath + +from os.path import abspath, join +from dataclasses import dataclass +from typing import Dict + + +@dataclass +class Sandbox: + """ A data type to abstract the notion of a development "sandbox" + + This is an interface to test and run Dockerized software + on an arbitrary container runtime (either local or remote). + + Args: + client (docker.APIClient): Client for container runtime + langauge (str): Desired programming language of the sandbox + project_root (str): Global path to directory location in Docker build + context (same server as the runtime itself) + """ + SCHEMA: Dict[str, str] = { + "python": "docker_files/Dockerfile.py", + "golang": "docker_files/Dockerfile.go", + } + client: docker.APIClient + language: str + project_root: str + + def execute(self) -> docker.models.containers.Container: + """ Builds and runs associated Dockerfile on :client: + + Returns: + str: Output and error traces from the container at runtime + + Raises: + docker.errors.APIError: If the server returns any other error + docker.errors.BuildError: If there is an error in the build + docker.errors.ImageNotFound: If the image is not found on the + Docker server - this would generally indicate server-side + corruption + """ + dockerfile_path = abspath( + join(rootpath.detect(), Sandbox.SCHEMA[self.language])) + + with open(dockerfile_path, "r") as df: + image = self.client.images.build( + fileobj=df, + custom_context=True, + buildargs={"PROJECT_ROOT": self.project_root}) + + return self.client.containers.run(image, detach=True).attach() diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py deleted file mode 100644 index cb63764..0000000 --- a/tests/test_sandbox.py +++ /dev/null @@ -1,4 +0,0 @@ -import pytest - -def test_ok(): - assert True