From 6aedc9c7cf605589d8ceee777007e3715a957333 Mon Sep 17 00:00:00 2001 From: Florian Aucomte <33633200+faucomte97@users.noreply.github.com> Date: Wed, 31 Oct 2018 13:43:36 +0000 Subject: [PATCH] Docker for local mode (#875) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Reduce image sizes for docker images * bump version * Merge branch 'development' into docker_for_local_mode # Conflicts: # version.txt * working docker in local mode! * expose external port to aimmo-game * detach aimmo-game-worker running from aimmo-game process * delete previously running containers before creating new ones * make docker container naming consistent with k8s * make docker host os agnostic * remove unnecessary log statement * only re install deps if they have changed * change hosts * add Portainer instructions to usage, and small intro paragraph * try to get host.docker.internal working on both linux and mac * Merge branch 'docker_for_local_mode' of https://github.com/ocadotechnology/aimmo into docker_for_local_mode * get port from env variable * os dependent host * use a template for container kwargs * pass CONTAINER_TEMPLATE into worker * provide default for LOCALHOST_IP * provide defaults for environment variables to do with docker * fix tests by mocking docker * Adding docker install to travis to try and fix tests. * Remove logs and adjust blank lines. * Added settings to make Docker for local work for Ubuntu machines. * Merge branch 'development' into docker_for_local_mode * Remove extra whitespaces * Merge branch 'docker_for_local_mode' of https://github.com/ocadotechnology/aimmo into docker_for_local_mode * Merge branch 'development' of https://github.com/ocadotechnology/aimmo into docker_for_local_mode * Bumped up version by minor change. * Move docker logic into a separate file Since docker setup is not related to minikube anymore * inject env variables for aimmo-game * Merge branch 'development' into docker_for_local_mode # Conflicts: # version.txt * remove newline in version file * remove docker entrypoint scripts * change experimental settings back to original values * add time.sleep back to run script * Code review changes * Docstring changes and delete stopped aimmo containers as well * change port setting back to original value * fix docker version * add missing quote * Don’t fix versions in Pipfile as they are in the Pipfile.lock * Update aimmo-game/Pipfile.lock * Conform to pep8 * remove bash and curl from dockerfile * Merge branch 'development' into docker_for_local_mode # Conflicts: # aimmo-game/Pipfile # aimmo-game/Pipfile.lock # aimmo-game/service.py # version.txt --- .travis.yml | 1 + aimmo-game-creator/Dockerfile | 6 +- aimmo-game-creator/Pipfile | 1 + aimmo-game-creator/Pipfile.lock | 112 ++++++++++++------ aimmo-game-creator/game_manager.py | 37 ++++-- aimmo-game-creator/service.py | 3 +- aimmo-game-worker/Dockerfile | 6 +- aimmo-game-worker/service.py | 1 - aimmo-game/Dockerfile | 8 +- aimmo-game/Pipfile | 3 +- aimmo-game/Pipfile.lock | 90 ++++++++++---- aimmo-game/service.py | 5 +- .../worker_managers/local_worker_manager.py | 27 +++-- .../test_local_worker_manager.py | 7 +- aimmo-game/tests/test_socketio.py | 9 +- aimmo_runner/docker_scripts.py | 102 ++++++++++++++++ aimmo_runner/minikube.py | 56 +-------- aimmo_runner/runner.py | 6 +- aimmo_runner/setup.py | 2 +- docs/usage.md | 6 + integration_tests/tests/test_integration.py | 4 +- minikube_requirements.txt | 2 +- setup.py | 2 +- version.txt | 2 +- 24 files changed, 342 insertions(+), 156 deletions(-) create mode 100644 aimmo_runner/docker_scripts.py diff --git a/.travis.yml b/.travis.yml index 7ad36e98e..6fe79b6ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ jobs: install: - pip install . - pip install coveralls + - pip install docker - pushd game_frontend - yarn - node djangoBundler.js diff --git a/aimmo-game-creator/Dockerfile b/aimmo-game-creator/Dockerfile index fe7ee7372..0117cf090 100644 --- a/aimmo-game-creator/Dockerfile +++ b/aimmo-game-creator/Dockerfile @@ -2,12 +2,14 @@ FROM python:2-alpine MAINTAINER code@ocado.com -COPY . . - RUN apk add --no-cache gcc musl-dev python-dev libffi-dev openssl-dev RUN pip install pipenv +COPY ["Pipfile", "Pipfile.lock", "setup.py", "./"] + RUN pipenv install --system --deploy +COPY . . + CMD ["python", "./service.py"] diff --git a/aimmo-game-creator/Pipfile b/aimmo-game-creator/Pipfile index 385fceb4f..99ce8d0a6 100644 --- a/aimmo-game-creator/Pipfile +++ b/aimmo-game-creator/Pipfile @@ -6,6 +6,7 @@ name = "pypi" [packages] kubernetes = "*" aimmo-game-creator = {editable = true, path = "."} +docker = "*" [requires] python_version = "2.7" diff --git a/aimmo-game-creator/Pipfile.lock b/aimmo-game-creator/Pipfile.lock index 74ffacef7..dea398248 100644 --- a/aimmo-game-creator/Pipfile.lock +++ b/aimmo-game-creator/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "58d4a5d007d207c103524baa2cca08cadcf5f979c0e807c767f0ef10f4a42dd5" + "sha256": "055a0f8b0f2d76d025788afd11d00a5e36ac7fc3539c01a3b7d7e1831293d033" }, "pipfile-spec": 6, "requires": { @@ -34,6 +34,13 @@ ], "version": "==0.24.0" }, + "backports.ssl-match-hostname": { + "hashes": [ + "sha256:502ad98707319f4a51fa2ca1c677bd659008d27ded9f6380c79e8932e38dcdf2" + ], + "markers": "python_version < '3.5'", + "version": "==3.5.0.1" + }, "cachetools": { "hashes": [ "sha256:90f1d559512fc073483fe573ef5ceb39bf6ad3d39edc98dc55178a2b2b176fa3", @@ -123,6 +130,21 @@ ], "version": "==1.15.0" }, + "docker": { + "hashes": [ + "sha256:6c4da20ef40e8d3eaf650f1488d91452b9a1128045481d7169fd34665ffa90ee", + "sha256:bc693be5a84b3b9e5aaf156068c5c0a445ee5138c638c3fbc857133bf67ebe07" + ], + "index": "pypi", + "version": "==3.5.0" + }, + "docker-pycreds": { + "hashes": [ + "sha256:0a941b290764ea7286bd77f54c0ace43b86a8acd6eb9ead3de9840af52384079", + "sha256:8b0e956c8d206f832b06aa93a710ba2c3bcbacb5a314449c040b0b814355bbff" + ], + "version": "==0.3.0" + }, "enum34": { "hashes": [ "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", @@ -149,27 +171,27 @@ }, "greenlet": { "hashes": [ - "sha256:0411b5bf0de5ec11060925fd811ad49073fa19f995bcf408839eb619b59bb9f7", - "sha256:131f4ed14f0fd28d2a9fa50f79a57d5ed1c8f742d3ccac3d773fee09ef6fe217", - "sha256:13510d32f8db72a0b3e1720dbf8cba5c4eecdf07abc4cb631982f51256c453d1", - "sha256:31dc4d77ef04ab0460d024786f51466dbbc274fda7c8aad0885a6df5ff8d642e", - "sha256:35021d9fecea53b21e4defec0ff3ad69a8e2b75aca1ceddd444a5ba71216547e", - "sha256:426a8ef9e3b97c27e841648241c2862442c13c91ec4a48c4a72b262ccf30add9", - "sha256:58217698193fb94f3e6ff57eed0ae20381a8d06c2bc10151f76c06bb449a3a19", - "sha256:5f45adbbb69281845981bb4e0a4efb8a405f10f3cd6c349cb4a5db3357c6bf93", - "sha256:5fdb524767288f7ad161d2182f7ed6cafc0a283363728dcd04b9485f6411547c", - "sha256:71fbee1f7ef3fb42efa3761a8faefc796e7e425f528de536cfb4c9de03bde885", - "sha256:80bd314157851d06f7db7ca527082dbb0ee97afefb529cdcd59f7a5950927ba0", - "sha256:b843c9ef6aed54a2649887f55959da0031595ccfaf7e7a0ba7aa681ffeaa0aa1", - "sha256:c6a05ef8125503d2d282ccf1448e3599b8a6bd805c3cdee79760fa3da0ea090e", - "sha256:deeda2769a52db840efe5bf7bdf7cefa0ae17b43a844a3259d39fb9465c8b008", - "sha256:e66f8b09eec1afdcab947d3a1d65b87b25fde39e9172ae1bec562488335633b4", - "sha256:e8db93045414980dbada8908d49dbbc0aa134277da3ff613b3e548cb275bdd37", - "sha256:f1cc268a15ade58d9a0c04569fe6613e19b8b0345b64453064e2c3c6d79051af", - "sha256:fe3001b6a4f3f3582a865b9e5081cc548b973ec20320f297f5e2d46860e9c703", - "sha256:fe85bf7adb26eb47ad53a1bae5d35a28df16b2b93b89042a3a28746617a4738d" - ], - "version": "==0.4.14" + "sha256:000546ad01e6389e98626c1367be58efa613fa82a1be98b0c6fc24b563acc6d0", + "sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28", + "sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8", + "sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304", + "sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0", + "sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214", + "sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043", + "sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6", + "sha256:8b4572c334593d449113f9dc8d19b93b7b271bdbe90ba7509eb178923327b625", + "sha256:9416443e219356e3c31f1f918a91badf2e37acf297e2fa13d24d1cc2380f8fbc", + "sha256:9854f612e1b59ec66804931df5add3b2d5ef0067748ea29dc60f0efdcda9a638", + "sha256:99a26afdb82ea83a265137a398f570402aa1f2b5dfb4ac3300c026931817b163", + "sha256:a19bf883b3384957e4a4a13e6bd1ae3d85ae87f4beb5957e35b0be287f12f4e4", + "sha256:a9f145660588187ff835c55a7d2ddf6abfc570c2651c276d3d4be8a2766db490", + "sha256:ac57fcdcfb0b73bb3203b58a14501abb7e5ff9ea5e2edfa06bb03035f0cff248", + "sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939", + "sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87", + "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720", + "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656" + ], + "version": "==0.4.15" }, "idna": { "hashes": [ @@ -183,7 +205,7 @@ "sha256:64b28eec5e78e7510698f6d4da08800a5c575caa4a286c93d651c5d3ff7b6794", "sha256:b146c751ea45cad6188dd6cf2d9b757f6f4f8d6ffb96a023e6f2e26eea02a72c" ], - "markers": "python_version < '3'", + "markers": "python_version == '2.7'", "version": "==1.0.22" }, "kubernetes": { @@ -210,23 +232,45 @@ }, "pyasn1": { "hashes": [ + "sha256:0ad0fe0593dde1e599cac0bf65bb1a4ec663032f0bc68ee44850db4251e8c501", + "sha256:13794d835643ee970b2c059dbfe4eb5d751e16c693c8baee61c526abd209e5c7", + "sha256:49a8ed515f26913049113820b462f698e6ed26df62c389dafb6fa3685ddca8de", + "sha256:74ac8521a0480f228549be20bea555ae35678f0e754c2fbc6f1576b0959bec43", + "sha256:89399ca8ecd4524f974e926d4ef9e7a787903e01f0a9cdff3131ad1361792fe5", + "sha256:8f291e0338d519a1a0d07f0b9d03c9265f6be26eb32fdd21af6d3259d14ea49c", "sha256:b9d3abc5031e61927c82d4d96c1cec1e55676c1a991623cfed28faea73cdd7ca", + "sha256:d3bbd726c1a760d4ca596a4d450c380b81737612fe0182f5bb3caebc17461fd9", + "sha256:dea873d6c907c1cf1341fd88742a61efce33227d7743cb37564ab7d7e77dd9fd", + "sha256:ded5eea5cb88bc1ce9aa074b5a3092f95ce4741887e317e9b49c7ece75d7ea0e", + "sha256:e8b69ea2200d42201cbedd486eedb8980f320d4534f83ce2fb468e96aa5545d0", + "sha256:edad117649643230493aeb4955456ce19ab4b12e94489dde6f7094cdb5a3c87e", "sha256:f58f2a3d12fd754aa123e9fa74fb7345333000a035f3921dbdaa08597aa53137" ], "version": "==0.4.4" }, "pyasn1-modules": { "hashes": [ + "sha256:077250b34432520430bc1c80dcbda4e354090785567c33ded35faa6df8d24753", + "sha256:0da2f947e8ad2697e86fe5fd0e55a4093a2fd79d839c9e19c34e28097db7002c", + "sha256:35ff894a0b5df8e28b700126b2869c7dcfb2b2db5bc82e5d5e82547069241553", + "sha256:44688b94841349648b1e1a5a7a3d96e6596d5d4f21d0b59a82307e153c4dc74b", + "sha256:833716dde880a7f2f2ccdeea9a096842626981ff2a477d8b318c0906367ac11b", "sha256:a0cf3e1842e7c60fde97cb22d275eb6f9524f5c5250489e292529de841417547", - "sha256:a38a8811ea784c0136abfdba73963876328f66172db21a05a82f9515909bfb4e" + "sha256:a38a8811ea784c0136abfdba73963876328f66172db21a05a82f9515909bfb4e", + "sha256:a728bb9502d1fdc104c66f24a176b6a70a32e89d1d8a5b55c959233ed51c67be", + "sha256:c30a098435ea0989c37005a971843e9d3966c7f6d056ddbf052e5061c06e3291", + "sha256:c355a45b32c5bc1d9893eceb704b0cfcd1126f91b5a7b9ee64c1c05383283381", + "sha256:e64679de1940f41ead5170fce364d54e7b9e2e862f064727b6bcb5cee753b7a2", + "sha256:ed71d20225c356881c29f0b1d7a0d6521563a389d9478e8f95d798cc5ba07b88", + "sha256:f183f0940b9f5ed2ad9d04c80cab2451440fa9af4fc959d85113fadd2e777962" ], "version": "==0.2.2" }, "pycparser": { "hashes": [ - "sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226" + "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" ], - "version": "==2.18" + "version": "==2.19" }, "pyjwt": { "hashes": [ @@ -268,16 +312,17 @@ "requests-oauthlib": { "hashes": [ "sha256:8886bfec5ad7afb391ed5443b1f697c6f4ae98d0e5620839d8b4499c032ada3f", - "sha256:e21232e2465808c0e892e0e4dbb8c2faafec16ac6dc067dd546e9b466f3deac8" + "sha256:e21232e2465808c0e892e0e4dbb8c2faafec16ac6dc067dd546e9b466f3deac8", + "sha256:fe3282f48fb134ee0035712159f5429215459407f6d5484013343031ff1a400d" ], "version": "==1.0.0" }, "rsa": { "hashes": [ - "sha256:25df4e10c263fb88b5ace923dd84bf9aa7f5019687b5e55382ffcdb8bede9db5", - "sha256:43f682fea81c452c98d09fc316aae12de6d30c4b5c84226642cf8f8fd1c93abd" + "sha256:14ba45700ff1ec9eeb206a2ce76b32814958a98e372006c8fb76ba820211be66", + "sha256:1a836406405730121ae9823e19c6e806c62bbad73f890574fff50efa4122c487" ], - "version": "==3.4.2" + "version": "==4.0" }, "six": { "hashes": [ @@ -291,15 +336,14 @@ "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" ], - "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'", "version": "==1.23" }, "websocket-client": { "hashes": [ - "sha256:03763384c530b331ec3822d0b52ffdc28c3aeb8a900ac8c98b2ceea3128a7b4e", - "sha256:3c9924675eaf0b27ae22feeeab4741bb4149b94820bd3a143eeaf8b62f64d821" + "sha256:c42b71b68f9ef151433d6dcc6a7cb98ac72d2ad1e3a74981ca22bc5d9134f166", + "sha256:f5889b1d0a994258cfcbc8f2dc3e457f6fc7b32a8d74873033d12e4eab4bdf63" ], - "version": "==0.52.0" + "version": "==0.53.0" } }, "develop": {} diff --git a/aimmo-game-creator/game_manager.py b/aimmo-game-creator/game_manager.py index 28bac8af2..7017df6bb 100644 --- a/aimmo-game-creator/game_manager.py +++ b/aimmo-game-creator/game_manager.py @@ -8,6 +8,8 @@ from eventlet.greenpool import GreenPool from eventlet.semaphore import Semaphore import kubernetes +import docker +import json LOGGER = logging.getLogger(__name__) @@ -131,7 +133,7 @@ def _parallel_map(self, func, *iterable_args): class LocalGameManager(GameManager): """Manages games running on local host""" - host = "127.0.0.1" + host = os.environ.get('LOCALHOST_IP', '127.0.0.1') game_directory = os.path.join( os.path.dirname(__file__), "../aimmo-game/", @@ -143,19 +145,27 @@ def __init__(self, *args, **kwargs): super(LocalGameManager, self).__init__(*args, **kwargs) def create_game(self, game_id, game_data): - assert(game_id not in self.games) - port = str(6001 + int(game_id) * 1000) - process_args = [ - "python", - self.game_service_path, - self.host, - port, - ] - env = os.environ.copy() + def setup_container_environment_variables(template, game_data): + template['environment'].update(game_data) + template['environment']['GAME_ID'] = game_id + template['environment']['PYTHONUNBUFFERED'] = 0 + template['environment']['WORKER_MANAGER'] = 'local' + template['environment']['EXTERNAL_PORT'] = port + template['environment']['CONTAINER_TEMPLATE'] = os.environ['CONTAINER_TEMPLATE'] + + assert (game_id not in self.games) game_data = {str(k): str(v) for k, v in game_data.items()} - env.update(game_data) - env['GAME_ID'] = game_id - self.games[game_id] = subprocess.Popen(process_args, cwd=self.game_directory, env=env) + port = str(6001 + int(game_id) * 1000) + client = docker.from_env() + + template = json.loads(os.environ.get('CONTAINER_TEMPLATE', '{}')) + setup_container_environment_variables(template, game_data) + template['ports'] = {"{}/tcp".format(port): ('0.0.0.0', port)} + + self.games[game_id] = client.containers.run( + name="aimmo-game-{}".format(game_id), + image='ocadotechnology/aimmo-game:test', + **template) game_url = "http://{}:{}".format(self.host, port) LOGGER.info("Game started - {}, listening at {}".format(game_id, game_url)) @@ -233,6 +243,7 @@ def _create_game_rc(self, game_id, environment_variables): environment_variables['GAME_URL'] = 'http://game-{}'.format(game_id) environment_variables['IMAGE_SUFFIX'] = os.environ.get('IMAGE_SUFFIX', 'latest') environment_variables['K8S_NAMESPACE'] = K8S_NAMESPACE + environment_variables['WORKER_MANAGER'] = 'kubernetes' rc = self._make_rc(environment_variables, game_id) self.api.create_namespaced_replication_controller(K8S_NAMESPACE, rc) diff --git a/aimmo-game-creator/service.py b/aimmo-game-creator/service.py index 9f168d2db..73f294af6 100755 --- a/aimmo-game-creator/service.py +++ b/aimmo-game-creator/service.py @@ -8,8 +8,9 @@ def main(): logging.basicConfig(level=logging.DEBUG) game_manager_class = GAME_MANAGERS[os.environ.get('GAME_MANAGER', 'local')] + host = os.environ.get('LOCALHOST_IP', 'localhost') game_manager = game_manager_class(os.environ.get('GAME_API_URL', - 'http://localhost:8000/aimmo/api/games/')) + "http://{}:8000/aimmo/api/games/".format(host))) game_manager.run() diff --git a/aimmo-game-worker/Dockerfile b/aimmo-game-worker/Dockerfile index d55128482..ac1f877e7 100644 --- a/aimmo-game-worker/Dockerfile +++ b/aimmo-game-worker/Dockerfile @@ -2,12 +2,14 @@ FROM python:2-alpine MAINTAINER code@ocado.com -COPY . . - RUN apk add --no-cache gcc musl-dev python-dev libffi-dev openssl-dev RUN pip install pipenv +COPY ["Pipfile", "Pipfile.lock", "setup.py", "./"] + RUN pipenv install --system --deploy +COPY . . + CMD python service.py 0.0.0.0 5000 $DATA_URL diff --git a/aimmo-game-worker/service.py b/aimmo-game-worker/service.py index d079b2aa5..ef6e5f3b9 100755 --- a/aimmo-game-worker/service.py +++ b/aimmo-game-worker/service.py @@ -17,7 +17,6 @@ def get_code_and_options(): - LOGGER.info('Data url: ' + DATA_URL) data = requests.get(DATA_URL).json() return data['code'], data['options'] diff --git a/aimmo-game/Dockerfile b/aimmo-game/Dockerfile index 26ae067cd..a737dc62d 100644 --- a/aimmo-game/Dockerfile +++ b/aimmo-game/Dockerfile @@ -4,12 +4,14 @@ MAINTAINER code@ocado.com ENV WORKER_MANAGER=kubernetes -COPY . . - RUN apk add --no-cache gcc musl-dev python-dev libffi-dev openssl-dev RUN pip install pipenv +COPY ["Pipfile", "Pipfile.lock", "setup.py", "./"] + RUN pipenv install --system --deploy -CMD ["python", "./service.py", "0.0.0.0", "5000"] +COPY . . + +CMD ["python", "./service.py", "0.0.0.0"] diff --git a/aimmo-game/Pipfile b/aimmo-game/Pipfile index 11e3805c3..c98af800c 100644 --- a/aimmo-game/Pipfile +++ b/aimmo-game/Pipfile @@ -4,8 +4,9 @@ verify_ssl = true name = "pypi" [packages] -kubernetes = "*" aimmo-game = {editable = true, path = "."} +docker = "*" +kubernetes = "*" prometheus-client = "*" [requires] diff --git a/aimmo-game/Pipfile.lock b/aimmo-game/Pipfile.lock index db98bf95e..6668365a9 100644 --- a/aimmo-game/Pipfile.lock +++ b/aimmo-game/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "12fed2c19f40b11d4cd5cd378d1ef3e5324eea755a782fcdec3a96c1cd7e6912" + "sha256": "d926f064ac627d0f29b0d62258b5fd3c0fc9355ab729d228e491b74b485991ce" }, "pipfile-spec": 6, "requires": { @@ -18,11 +18,10 @@ "default": { "adal": { "hashes": [ - "sha256:534ab04df7ab7c30bc7fe9526c3120e50b9496982f6c85001b05fd7cf4134eb7", - "sha256:a2a2f7e4a2d2e2014e3d5ff9f6d614af280c879a1dbf96bb64d92d85a814a645" + "sha256:ba52913c38d76b4a4d88eaab41a5763d056ab6d073f106e0605b051ab930f5c1", + "sha256:bf79392b8e9e5e82aa6acac3835ba58bbac0ccf7e15befa215863f83d5f6a007" ], - "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", - "version": "==1.1.0" + "version": "==1.2.0" }, "aimmo-game": { "editable": true, @@ -35,6 +34,13 @@ ], "version": "==0.24.0" }, + "backports.ssl-match-hostname": { + "hashes": [ + "sha256:502ad98707319f4a51fa2ca1c677bd659008d27ded9f6380c79e8932e38dcdf2" + ], + "markers": "python_version < '3.5'", + "version": "==3.5.0.1" + }, "cachetools": { "hashes": [ "sha256:90f1d559512fc073483fe573ef5ceb39bf6ad3d39edc98dc55178a2b2b176fa3", @@ -84,7 +90,6 @@ "sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f", "sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb" ], - "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", "version": "==1.11.5" }, "chardet": { @@ -99,7 +104,6 @@ "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" ], - "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", "version": "==7.0" }, "cryptography": { @@ -124,7 +128,6 @@ "sha256:e79ab4485b99eacb2166f3212218dd858258f374855e1568f728462b0e6ee0d9", "sha256:f995d3667301e1754c57b04e0bae6f0fa9d710697a9f8d6712e8cca02550910f" ], - "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", "version": "==2.3.1" }, "dnspython": { @@ -134,6 +137,21 @@ ], "version": "==1.15.0" }, + "docker": { + "hashes": [ + "sha256:31421f16c01ffbd1ea7353c7e7cd7540bf2e5906d6173eb51c8fea4e0ea38b19", + "sha256:fbe82af9b94ccced752527c8de07fa20267f9634b48674ba478a0bb4000a0b1e" + ], + "index": "pypi", + "version": "==3.5.1" + }, + "docker-pycreds": { + "hashes": [ + "sha256:0a941b290764ea7286bd77f54c0ace43b86a8acd6eb9ead3de9840af52384079", + "sha256:8b0e956c8d206f832b06aa93a710ba2c3bcbacb5a314449c040b0b814355bbff" + ], + "version": "==0.3.0" + }, "enum34": { "hashes": [ "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", @@ -141,7 +159,7 @@ "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1" ], - "markers": "python_version < '3'", + "markers": "python_version < '3.4'", "version": "==1.1.6" }, "eventlet": { @@ -208,14 +226,15 @@ "sha256:64b28eec5e78e7510698f6d4da08800a5c575caa4a286c93d651c5d3ff7b6794", "sha256:b146c751ea45cad6188dd6cf2d9b757f6f4f8d6ffb96a023e6f2e26eea02a72c" ], - "markers": "python_version < '3'", + "markers": "python_version < '3.3'", "version": "==1.0.22" }, "itsdangerous": { "hashes": [ - "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" ], - "version": "==0.24" + "version": "==1.1.0" }, "jinja2": { "hashes": [ @@ -261,15 +280,37 @@ }, "pyasn1": { "hashes": [ + "sha256:0ad0fe0593dde1e599cac0bf65bb1a4ec663032f0bc68ee44850db4251e8c501", + "sha256:13794d835643ee970b2c059dbfe4eb5d751e16c693c8baee61c526abd209e5c7", + "sha256:49a8ed515f26913049113820b462f698e6ed26df62c389dafb6fa3685ddca8de", + "sha256:74ac8521a0480f228549be20bea555ae35678f0e754c2fbc6f1576b0959bec43", + "sha256:89399ca8ecd4524f974e926d4ef9e7a787903e01f0a9cdff3131ad1361792fe5", + "sha256:8f291e0338d519a1a0d07f0b9d03c9265f6be26eb32fdd21af6d3259d14ea49c", "sha256:b9d3abc5031e61927c82d4d96c1cec1e55676c1a991623cfed28faea73cdd7ca", + "sha256:d3bbd726c1a760d4ca596a4d450c380b81737612fe0182f5bb3caebc17461fd9", + "sha256:dea873d6c907c1cf1341fd88742a61efce33227d7743cb37564ab7d7e77dd9fd", + "sha256:ded5eea5cb88bc1ce9aa074b5a3092f95ce4741887e317e9b49c7ece75d7ea0e", + "sha256:e8b69ea2200d42201cbedd486eedb8980f320d4534f83ce2fb468e96aa5545d0", + "sha256:edad117649643230493aeb4955456ce19ab4b12e94489dde6f7094cdb5a3c87e", "sha256:f58f2a3d12fd754aa123e9fa74fb7345333000a035f3921dbdaa08597aa53137" ], "version": "==0.4.4" }, "pyasn1-modules": { "hashes": [ + "sha256:077250b34432520430bc1c80dcbda4e354090785567c33ded35faa6df8d24753", + "sha256:0da2f947e8ad2697e86fe5fd0e55a4093a2fd79d839c9e19c34e28097db7002c", + "sha256:35ff894a0b5df8e28b700126b2869c7dcfb2b2db5bc82e5d5e82547069241553", + "sha256:44688b94841349648b1e1a5a7a3d96e6596d5d4f21d0b59a82307e153c4dc74b", + "sha256:833716dde880a7f2f2ccdeea9a096842626981ff2a477d8b318c0906367ac11b", "sha256:a0cf3e1842e7c60fde97cb22d275eb6f9524f5c5250489e292529de841417547", - "sha256:a38a8811ea784c0136abfdba73963876328f66172db21a05a82f9515909bfb4e" + "sha256:a38a8811ea784c0136abfdba73963876328f66172db21a05a82f9515909bfb4e", + "sha256:a728bb9502d1fdc104c66f24a176b6a70a32e89d1d8a5b55c959233ed51c67be", + "sha256:c30a098435ea0989c37005a971843e9d3966c7f6d056ddbf052e5061c06e3291", + "sha256:c355a45b32c5bc1d9893eceb704b0cfcd1126f91b5a7b9ee64c1c05383283381", + "sha256:e64679de1940f41ead5170fce364d54e7b9e2e862f064727b6bcb5cee753b7a2", + "sha256:ed71d20225c356881c29f0b1d7a0d6521563a389d9478e8f95d798cc5ba07b88", + "sha256:f183f0940b9f5ed2ad9d04c80cab2451440fa9af4fc959d85113fadd2e777962" ], "version": "==0.2.2" }, @@ -277,7 +318,6 @@ "hashes": [ "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" ], - "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", "version": "==2.19" }, "pyjwt": { @@ -289,10 +329,10 @@ }, "python-dateutil": { "hashes": [ - "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", - "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" + "sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93", + "sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02" ], - "version": "==2.7.3" + "version": "==2.7.5" }, "python-engineio": { "hashes": [ @@ -326,15 +366,16 @@ }, "requests": { "hashes": [ - "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", - "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" + "sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c", + "sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279" ], - "version": "==2.19.1" + "version": "==2.20.0" }, "requests-oauthlib": { "hashes": [ "sha256:8886bfec5ad7afb391ed5443b1f697c6f4ae98d0e5620839d8b4499c032ada3f", - "sha256:e21232e2465808c0e892e0e4dbb8c2faafec16ac6dc067dd546e9b466f3deac8" + "sha256:e21232e2465808c0e892e0e4dbb8c2faafec16ac6dc067dd546e9b466f3deac8", + "sha256:fe3282f48fb134ee0035712159f5429215459407f6d5484013343031ff1a400d" ], "version": "==1.0.0" }, @@ -354,11 +395,10 @@ }, "urllib3": { "hashes": [ - "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", - "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" + "sha256:41c3db2fc01e5b907288010dec72f9d0a74e37d6994e6eb56849f59fea2265ae", + "sha256:8819bba37a02d143296a4d032373c4dd4aca11f6d4c9973335ca75f9c8475f59" ], - "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'", - "version": "==1.23" + "version": "==1.24" }, "websocket-client": { "hashes": [ diff --git a/aimmo-game/service.py b/aimmo-game/service.py index c95bd9aaa..283d970b4 100644 --- a/aimmo-game/service.py +++ b/aimmo-game/service.py @@ -143,8 +143,11 @@ def run_game(port): if __name__ == '__main__': logging.basicConfig(level=logging.INFO) - host, port = sys.argv[1], int(sys.argv[2]) + host = sys.argv[1] socket_app = socketio.Middleware(socketio_server, flask_app, socketio_path=os.environ.get('SOCKETIO_RESOURCE', 'socket.io')) + + port = int(os.environ['EXTERNAL_PORT']) + run_game(port) eventlet.wsgi.server(eventlet.listen((host, port)), socket_app, debug=False) diff --git a/aimmo-game/simulation/worker_managers/local_worker_manager.py b/aimmo-game/simulation/worker_managers/local_worker_manager.py index 709d78893..5012b742a 100644 --- a/aimmo-game/simulation/worker_managers/local_worker_manager.py +++ b/aimmo-game/simulation/worker_managers/local_worker_manager.py @@ -3,6 +3,8 @@ import logging import os import subprocess +import docker +import json from .worker_manager import WorkerManager @@ -12,7 +14,7 @@ class LocalWorkerManager(WorkerManager): """Relies on them already being created already.""" - host = '127.0.0.1' + host = os.environ.get('LOCALHOST_IP', '127.0.0.1') worker_directory = os.path.join( os.path.dirname(__file__), '../../../aimmo-game-worker/', @@ -20,20 +22,29 @@ class LocalWorkerManager(WorkerManager): def __init__(self, *args, **kwargs): self.workers = {} - game_id = os.environ['GAME_ID'] - self.port_counter = itertools.count(1989 + int(game_id) * 10000) + self.game_id = os.environ['GAME_ID'] + self.port_counter = itertools.count(1989 + int(self.game_id) * 10000) + self.client = docker.from_env() super(LocalWorkerManager, self).__init__(*args, **kwargs) def create_worker(self, player_id): assert(player_id not in self.workers) port = self.port_counter.next() + env = json.loads(os.environ.get('CONTAINER_TEMPLATE', '{}')) data_url = 'http://{}:{}/player/{}'.format(self.host, self.port, player_id) - - process = subprocess.Popen(['python', 'service.py', self.host, str(port), data_url], - cwd=self.worker_directory) - atexit.register(process.kill) - self.workers[player_id] = process + env['DATA_URL'] = data_url + env['PORT'] = port + + container = self.client.containers.run( + name="aimmo-{}-worker-{}".format(self.game_id, player_id), + image='ocadotechnology/aimmo-game-worker:test', + publish_all_ports=True, + environment=env, + network_mode='host', + detach=True, + ports={"{}/tcp".format(port): port}) + self.workers[player_id] = container worker_url = 'http://%s:%d' % ( self.host, port, diff --git a/aimmo-game/tests/test_simulation/worker_managers/test_local_worker_manager.py b/aimmo-game/tests/test_simulation/worker_managers/test_local_worker_manager.py index 077628f3c..26242a8db 100644 --- a/aimmo-game/tests/test_simulation/worker_managers/test_local_worker_manager.py +++ b/aimmo-game/tests/test_simulation/worker_managers/test_local_worker_manager.py @@ -1,4 +1,5 @@ import os +import mock from unittest import TestCase from urlparse import urlparse @@ -7,7 +8,8 @@ class TestLocalWorkerManager(TestCase): - def test_local_worker_ports_do_not_conflict(self): + @mock.patch('docker.from_env') + def test_local_worker_ports_do_not_conflict(self, docker_from_env): os.environ['GAME_ID'] = '1' worker_manager1 = LocalWorkerManager() local_worker1 = worker_manager1.create_worker(1) @@ -21,7 +23,8 @@ def test_local_worker_ports_do_not_conflict(self): self.assertEquals(url1.port, 11989) self.assertEquals(url2.port, 21989) - def test_local_workers_in_the_same_game_do_not_have_port_conflicts(self): + @mock.patch('docker.from_env') + def test_local_workers_in_the_same_game_do_not_have_port_conflicts(self, docker_from_env): os.environ['GAME_ID'] = '1' worker_manager = LocalWorkerManager() local_worker1 = worker_manager.create_worker(1) diff --git a/aimmo-game/tests/test_socketio.py b/aimmo-game/tests/test_socketio.py index 88bc98b66..76ef0f422 100644 --- a/aimmo-game/tests/test_socketio.py +++ b/aimmo-game/tests/test_socketio.py @@ -2,6 +2,7 @@ import random import string import mock +import os import service from simulation.worker_managers.local_worker_manager import LocalWorkerManager @@ -25,7 +26,9 @@ def wrapper(*args, **kwargs): class TestSocketIO(TestCase): + def setUp(self): + os.environ['GAME_ID'] = '1' self.environ = {'QUERY_STRING': 'avatar_id=1&EIO=3&transport=polling&t=MJhoMgb'} self.game_api = self.create_game_api() self.mocked_mappings = self.game_api._socket_session_id_to_player_id @@ -34,8 +37,12 @@ def setUp(self): string.digits) for _ in range(19)) + def tearDown(self): + del os.environ['GAME_ID'] + + @mock.patch('docker.from_env') @mock.patch('service.flask_app') - def create_game_api(self, flask_app): + def create_game_api(self, flask_app, docker_from_env): game_runner = GameRunner(worker_manager_class=LocalWorkerManager, game_state_generator=lambda avatar_manager: MockGameState(), django_api_url='http://test', diff --git a/aimmo_runner/docker_scripts.py b/aimmo_runner/docker_scripts.py new file mode 100644 index 000000000..4c899b00a --- /dev/null +++ b/aimmo_runner/docker_scripts.py @@ -0,0 +1,102 @@ +import docker +import platform +import re +from shell_api import (run_command, create_test_bin, BASE_DIR) +import json +import os + + +def vm_none_enabled(raw_env_settings): + """ + Check if the VM driver is enabled or not. This is important to see where the environment variables live. + + :param raw_env_settings: String that is returned by the 'minikube docker-env' command. + :return: Boolean value indicating if enabled or not. + """ + return False if 'driver does not support' in raw_env_settings else True + + +def create_docker_client(raw_env_settings): + """ + Create a docker client using the python SDK. + + :param raw_env_settings: String that is returned by the 'minikube docker-env' command. + :return: + """ + if vm_none_enabled(raw_env_settings): + matches = re.finditer(r'^export (.+)="(.+)"$', raw_env_settings, re.MULTILINE) + env_variables = dict([(m.group(1), m.group(2)) for m in matches]) + + return docker.from_env( + environment=env_variables, + version='auto', + ) + else: + # VM driver is set + return docker.from_env( + version='auto' + ) + + +def build_docker_images(minikube=None): + """ + Find environment settings and builds docker images for each directory. + + :param minikube: Executable command to run in terminal. + """ + print('Building docker images') + if minikube: + raw_env_settings = run_command([minikube, 'docker-env', '--shell="bash"'], True) + client = create_docker_client(raw_env_settings) + else: + client = docker.from_env(version='auto') + + directories = ('aimmo-game', 'aimmo-game-creator', 'aimmo-game-worker') + for dir in directories: + path = os.path.join(BASE_DIR, dir) + tag = 'ocadotechnology/%s:test' % dir + print("Building %s..." % tag) + client.images.build( + path=path, + tag=tag, + encoding='gzip' + ) + + +def delete_containers(): + """Delete any containers starting with 'aimmo'.""" + client = docker.from_env(version='auto') + + containers = [container for container in client.containers.list(all=True) if container.name.startswith('aimmo')] + for container in containers: + container.remove(force=True) + + +def start_game_creator(): + """Start an aimmo-game-creator docker container.""" + os_name = platform.system() + client = docker.from_env(version='auto') + template = { + 'detach': True, + 'tty': True, + 'environment': { + 'FLASK_ENV': 'development', + 'WORKER_MANAGER': 'local' + }, + 'volumes': { + '/var/run/docker.sock': {'bind': '/var/run/docker.sock', 'mode': 'rw'} + } + } + if os_name == 'Linux': + template['environment']['LOCALHOST_IP'] = '127.0.0.1' + template['network_mode'] = 'host' + else: + template['environment']['LOCALHOST_IP'] = 'host.docker.internal' + + template['environment']['CONTAINER_TEMPLATE'] = json.dumps(template) + kwargs = template.copy() + client.containers.run( + name='aimmo-game-creator', + image='ocadotechnology/aimmo-game-creator:test', + **kwargs + ) diff --git a/aimmo_runner/minikube.py b/aimmo_runner/minikube.py index e4eb659ed..af3771b99 100644 --- a/aimmo_runner/minikube.py +++ b/aimmo_runner/minikube.py @@ -3,14 +3,13 @@ from subprocess import CalledProcessError -import docker import kubernetes import os -import re import platform import yaml import socket from shell_api import (run_command, create_test_bin, BASE_DIR) +from docker_scripts import build_docker_images MINIKUBE_EXECUTABLE = "minikube" @@ -83,59 +82,6 @@ def start_cluster(minikube): run_command([minikube, 'start', '--memory=2048', '--cpus=2']) -def create_docker_client(raw_env_settings): - """ - Creates a docker client using the python SDK. - :param raw_env_settings: String that is returned by the 'minikube docker-env' command. - :return: - """ - if vm_none_enabled(raw_env_settings): - matches = re.finditer(r'^export (.+)="(.+)"$', raw_env_settings, re.MULTILINE) - env_variables = dict([(m.group(1), m.group(2)) for m in matches]) - - return docker.from_env( - environment=env_variables, - version='auto', - ) - else: - # VM driver is set - return docker.from_env( - version='auto' - ) - - -def vm_none_enabled(raw_env_settings): - """ - Check if the VM driver is enabled or not. This is important to see where - the environment variables live. - :param raw_env_settings: String that is returned by the 'minikube docker-env' command. - :return: Boolean value indicating if enabled or not. - """ - return False if 'driver does not support' in raw_env_settings else True - - -def build_docker_images(minikube): - """ - Finds environment settings and builds docker images for each directory. - :param minikube: Executable command to run in terminal. - """ - print('Building docker images') - raw_env_settings = run_command([minikube, 'docker-env', '--shell="bash"'], True) - - client = create_docker_client(raw_env_settings) - - directories = ('aimmo-game', 'aimmo-game-creator', 'aimmo-game-worker') - for dir in directories: - path = os.path.join(BASE_DIR, dir) - tag = 'ocadotechnology/%s:test' % dir - print("Building %s..." % tag) - client.images.build( - path=path, - tag=tag, - encoding='gzip' - ) - - def delete_components(api_instance, extensions_api_instance): for rc in api_instance.list_namespaced_replication_controller('default').items: api_instance.delete_namespaced_replication_controller( diff --git a/aimmo_runner/runner.py b/aimmo_runner/runner.py index 13e1868c2..d07c77c72 100644 --- a/aimmo_runner/runner.py +++ b/aimmo_runner/runner.py @@ -5,6 +5,7 @@ import time from django.conf import settings from shell_api import log, run_command, run_command_async +import docker_scripts sys.path.append("/home/travis/build/ocadotechnology/aimmo") @@ -67,9 +68,10 @@ def run(use_minikube, server_wait=True, capture_output=False, test_env=False): os.environ['AIMMO_MODE'] = 'minikube' else: time.sleep(2) - game = run_command_async(['python', _SERVICE_PY, '127.0.0.1', '5000'], capture_output=capture_output) - PROCESSES.append(game) os.environ['AIMMO_MODE'] = 'threads' + docker_scripts.delete_containers() + docker_scripts.build_docker_images() + docker_scripts.start_game_creator() os.environ['NODE_ENV'] = 'development' if settings.DEBUG else 'production' server = run_command_async(['python', _MANAGE_PY, 'runserver'] + server_args, capture_output=capture_output) diff --git a/aimmo_runner/setup.py b/aimmo_runner/setup.py index 2e6180e68..999c4b9d0 100644 --- a/aimmo_runner/setup.py +++ b/aimmo_runner/setup.py @@ -19,7 +19,7 @@ 'hypothesis', 'flask-cors', 'psutil', - 'docker', + 'docker >= 3.5, < 3.6', 'kubernetes == 5.0.0', ], zip_safe=False, diff --git a/docs/usage.md b/docs/usage.md index 677e9a8ac..deea64057 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,4 +1,7 @@ # Usage + +This setup process will allow you to run AI:MMO locally via [docker](https://www.docker.com/) or a [kubernetes](https://kubernetes.io/) cluster, and to be able to contribute towards the project. + - [Mac setup](#mac-setup) - [Ubuntu/Debian setup](#ubuntu-setup) - [Windows setup](#windows-setup) @@ -32,6 +35,7 @@ The game should now be set up to run locally (If you ran the setup script, it wi * `curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.9.4/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/` * Alter your `/etc/hosts` file by adding the following line to the end of the file: `192.168.99.100 local.aimmo.codeforlife.education`. You may need to use sudo for this step as the file is protected. * Get the latest Unity bundle release from the [aimmo-unity](https://github.com/ocadotechnology/aimmo-unity) repo. +* Follow the instructions for [deploying Portainer](https://portainer.io/install.html), to create a dashboard for your local docker containers. #### To run AI:MMO: @@ -65,6 +69,7 @@ The game should now be set up to run locally (If you ran the setup script, it wi * To install [Docker](https://www.docker.com/), either use `sudo apt-get install docker-ce` to install a fixed version of the latest release, or follow the Ubuntu install instructions on the [Docker website](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository). * Alter your `/etc/hosts` file by adding the following line to the end of the file: `192.168.99.100 local.aimmo.codeforlife.education`. You may need to use sudo for this step as the file is protected. * Get the latest Unity bundle release from the [aimmo-unity](https://github.com/ocadotechnology/aimmo-unity) repo. +* Follow the instructions for [deploying Portainer](https://portainer.io/install.html), to create a dashboard for your local docker containers. #### To run AI:MMO: @@ -85,6 +90,7 @@ The game should now be set up to run locally. If you wish to be able to run the * Next, [download chocolatey](https://chocolatey.org/) and run `choco install kubernetes-cli`. * Then follow the [docker installation instructions for Windows](https://docs.docker.com/docker-for-windows/). * Alter your `/etc/hosts` file by adding the following line to the end of the file: `192.168.99.100 local.aimmo.codeforlife.education`. You may need admin privileges for this step as the file is protected. +* Follow the instructions for [deploying Portainer](https://portainer.io/install.html), to create a dashboard for your local docker containers. #### To run AI:MMO: diff --git a/integration_tests/tests/test_integration.py b/integration_tests/tests/test_integration.py index dbefca91f..c36f4e7eb 100644 --- a/integration_tests/tests/test_integration.py +++ b/integration_tests/tests/test_integration.py @@ -1,5 +1,6 @@ import os import logging +import mock import unittest from django.test.client import Client import psutil @@ -33,7 +34,8 @@ def tearDown(self): parent.terminate() - def test_superuser_authentication(self): + @mock.patch('docker.from_env') + def test_superuser_authentication(self, docker_from_env): """ A test that will run on a clean & empty database, create all migrations, new browser session and passes a CSRF token with the POST input request. diff --git a/minikube_requirements.txt b/minikube_requirements.txt index 4534f8b89..ad3d99ec1 100644 --- a/minikube_requirements.txt +++ b/minikube_requirements.txt @@ -1,2 +1,2 @@ -docker >= 2.7, < 2.8 +docker >= 3.5, < 3.6 kubernetes == 5.0.0 diff --git a/setup.py b/setup.py index 15d04e3ae..e43e4efaa 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ tests_require=[ 'httmock', 'mock == 2.0.0', - 'docker == 2.7.0', + 'docker >= 3.5, < 3.6', 'kubernetes == 5.0.0', 'PyYAML == 3.12', ], diff --git a/version.txt b/version.txt index 28af839c0..9325c3ccd 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.2.5 \ No newline at end of file +0.3.0 \ No newline at end of file