diff --git a/remoteappmanager/docker/container_manager.py b/remoteappmanager/docker/container_manager.py index 8177f114c..b0c01bc26 100644 --- a/remoteappmanager/docker/container_manager.py +++ b/remoteappmanager/docker/container_manager.py @@ -55,7 +55,12 @@ def __init__(self, docker_config, *args, **kwargs): super().__init__(*args, **kwargs) @gen.coroutine - def start_container(self, user_name, image_name, mapping_id, volumes): + def start_container(self, + user_name, + image_name, + mapping_id, + volumes, + environment=None): """Starts a container using the given image name. Parameters @@ -71,6 +76,9 @@ def start_container(self, user_name, image_name, mapping_id, volumes): (i.e. configuration). volumes: dict or None {volume_source: {'bind': volume_target, 'mode': volume_mode} + environment: dict or None + Contains additional keyvalue pairs that will be exported + as environment variables inside the container. Return ------ @@ -80,12 +88,16 @@ def start_container(self, user_name, image_name, mapping_id, volumes): if image_name in self._start_pending: return None + if environment is None: + environment = {} + try: self._start_pending.add(image_name) result = yield self._start_container(user_name, image_name, mapping_id, - volumes) + volumes, + environment) finally: self._start_pending.remove(image_name) @@ -204,7 +216,12 @@ def image(self, image_id_or_name): # Private @gen.coroutine - def _start_container(self, user_name, image_name, mapping_id, volumes): + def _start_container(self, + user_name, + image_name, + mapping_id, + volumes, + environment): """Helper method that performs the physical operation of starting the container. @@ -272,7 +289,9 @@ def _start_container(self, user_name, image_name, mapping_id, volumes): create_kwargs = dict( image=image_name, name=container_name, - environment=_get_container_env(user_name, container_url_id), + environment=_get_container_env(user_name, + container_url_id, + environment), volumes=volume_targets, labels=_get_container_labels(user_name, mapping_id, @@ -473,7 +492,7 @@ def _docker_client_default(self): return AsyncDockerClient(**self.docker_config) -def _get_container_env(user_name, url_id): +def _get_container_env(user_name, url_id, environment): """Introduces the environment variables that are available at container startup time. @@ -486,12 +505,20 @@ def _get_container_env(user_name, url_id): A string containing the container identifier that will be used in the user-exposed URL. + environment: dict + Additional environment keys to add to the final result. + Note that these will not take precedence. + Return ------ a dictionary containing the envvars to export. """ - return dict( + result = {} + if environment: + result.update(environment) + + result.update(dict( # Username used to login to jupyterhub. Generally an email address. JPY_USER=user_name, # The base url. We use this one because the JPY username might @@ -501,7 +528,8 @@ def _get_container_env(user_name, url_id): USER=_unix_user(user_name), # The identifier that will be used for the URL. URL_ID=url_id, - ) + )) + return result def _get_container_labels(user_name, mapping_id, url_id): diff --git a/remoteappmanager/docker/tests/test_container_manager.py b/remoteappmanager/docker/tests/test_container_manager.py index 2e16010be..57f061b4b 100644 --- a/remoteappmanager/docker/tests/test_container_manager.py +++ b/remoteappmanager/docker/tests/test_container_manager.py @@ -209,3 +209,23 @@ def raiser(*args, **kwargs): self.assertTrue(self.mock_docker_client.stop.called) self.assertTrue(self.mock_docker_client.remove_container.called) + + @gen_test + def test_start_container_with_environment(self): + mock_client = self.mock_docker_client + + environment = { + "FOO": "bar" + } + + yield self.manager.start_container( + "username", + "image_name1", + "mapping_id", + None, + environment + ) + + self.assertEqual( + mock_client.create_container.call_args[1]["environment"]["FOO"], + "bar")