class TestContainerManager_pull_image(TestCase): def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.api = Mock() self.mock_docker_client.api.pull = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) def test_must_pull_and_print_progress_dots(self): stream = io.StringIO() pull_result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] self.mock_docker_client.api.pull.return_value = pull_result expected_stream_output = "\nFetching {} Docker container image...{}\n".format( self.image_name, "." * len(pull_result) # Progress bar will print one dot per response from pull API ) self.manager.pull_image(self.image_name, stream=stream) self.mock_docker_client.api.pull.assert_called_with(self.image_name, stream=True, decode=True) self.assertEqual(stream.getvalue(), expected_stream_output) def test_must_raise_if_image_not_found(self): msg = "some error" self.mock_docker_client.api.pull.side_effect = APIError(msg) with self.assertRaises(DockerImagePullFailedException) as context: self.manager.pull_image("imagename") ex = context.exception self.assertEqual(str(ex), msg)
class TestContainerManager(TestCase): """ Verifies functionality of ContainerManager by calling Docker APIs """ IMAGE = "busybox" # small sized Linux container @classmethod def setUpClass(cls): # Make sure we start with a clean slate docker_client = docker.from_env() TestContainerManager._remove_image(docker_client) def setUp(self): self.manager = ContainerManager() self.docker_client = docker.from_env() def tearDown(self): self._remove_image(self.docker_client) def test_pull_image(self): # Image should not exist self.assertFalse(self.manager.has_image(self.IMAGE)) # Pull the image self.manager.pull_image(self.IMAGE) # Image should be available now self.assertTrue(self.manager.has_image(self.IMAGE)) @classmethod def _remove_image(cls, docker_client): try: docker_client.images.remove(cls.IMAGE) except docker.errors.ImageNotFound: pass
def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.images = Mock() self.mock_docker_client.images.get = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client)
def test_must_call_delete_on_container(self): manager = ContainerManager() container = Mock() container.delete = Mock() manager.stop(container) container.delete.assert_called_with()
def test_must_call_delete_on_container(self): manager = ContainerManager() container = Mock() container.delete = Mock() manager.stop(container) container.delete.assert_called_with()
def setUp(self): self.mock_docker_client = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) self.image_name = "image name" self.container_mock = Mock() self.container_mock.image = self.image_name self.container_mock.start = Mock() self.container_mock.create = Mock() self.container_mock.is_created = Mock()
def setUp(self): self.ping_mock = Mock() self.docker_client_mock = Mock() self.docker_client_mock.ping = self.ping_mock self.manager = ContainerManager(docker_client=self.docker_client_mock)
def __enter__(self): self._template_dict = get_template_data(self._template_file) self._function_provider = SamFunctionProvider( self._template_dict, self._parameter_overrides) self._layer_provider = SamLayerProvider(self._template_dict, self._parameter_overrides) if not self._base_dir: # Base directory, if not provided, is the directory containing the template self._base_dir = str( pathlib.Path(self._template_file).resolve().parent) self._build_dir = self._setup_build_dir(self._build_dir, self._clean) if self._cached: cache_path = pathlib.Path(self._cache_dir) cache_path.mkdir(mode=self._BUILD_DIR_PERMISSIONS, parents=True, exist_ok=True) self._cache_dir = str(cache_path.resolve()) if self._use_container: self._container_manager = ContainerManager( docker_network_id=self._docker_network, skip_pull_image=self._skip_pull_image) return self
def test_must_invoke(self): input_event = '"some data"' expected_env_vars = { "var1": "override_value1", "var2": "shell_env_value2" } manager = ContainerManager() local_runtime = LambdaRuntime(manager) runner = LocalLambdaRunner(local_runtime, self.mock_function_provider, self.cwd, self.env_var_overrides, debug_args=None, debug_port=None, aws_profile=None) # Append the real AWS credentials to the expected values. creds = runner.get_aws_creds() # default value for creds is not configured by the test. But coming from a downstream class expected_env_vars["AWS_SECRET_ACCESS_KEY"] = creds.get("secret", "defaultsecret") expected_env_vars["AWS_ACCESS_KEY_ID"] = creds.get("key", "defaultkey") expected_env_vars["AWS_REGION"] = creds.get("region", "us-east-1") stdout_stream = io.BytesIO() stderr_stream = io.BytesIO() runner.invoke(self.function_name, input_event, stdout=stdout_stream, stderr=stderr_stream) # stderr is where the Lambda container runtime logs are available. It usually contains requestId, start time # etc. So it is non-zero in size self.assertGreater(len(stderr_stream.getvalue().strip()), 0, "stderr stream must contain data") # This should contain all the environment variables passed to the function actual_output = json.loads(stdout_stream.getvalue().strip().decode('utf-8')) for key, value in expected_env_vars.items(): self.assertTrue(key in actual_output, "Key '{}' must be in function output".format(key)) self.assertEquals(actual_output.get(key), value)
def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.images = Mock() self.mock_docker_client.images.get = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client)
def setUp(self): self.code_dir = { "echo": nodejs_lambda(ECHO_CODE), "sleep": nodejs_lambda(SLEEP_CODE), "envvar": nodejs_lambda(GET_ENV_VAR) } self.container_manager = ContainerManager() self.runtime = LambdaRuntime(self.container_manager)
class TestContainerManager_has_image(TestCase): def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.images = Mock() self.mock_docker_client.images.get = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) def test_must_find_an_image(self): self.assertTrue(self.manager.has_image(self.image_name)) def test_must_not_find_image(self): self.mock_docker_client.images.get.side_effect = ImageNotFound("test") self.assertFalse(self.manager.has_image(self.image_name))
def setUp(self): self.host = "127.0.0.1" self.port = random.randint(30000, 40000) # get a random port self.url = "http://{}:{}".format(self.host, self.port) self.code_abs_path = nodejs_lambda(API_GATEWAY_ECHO_EVENT) # Let's convert this absolute path to relative path. Let the parent be the CWD, and codeuri be the folder self.cwd = os.path.dirname(self.code_abs_path) self.code_uri = os.path.relpath(self.code_abs_path, self.cwd) # Get relative path with respect to CWD # Setup a static file in the directory self.static_dir = "mystaticdir" self.static_file_name = "myfile.txt" self.static_file_content = "This is a static file" self._setup_static_file( os.path.join(self.cwd, self.static_dir), # Create static directory with in cwd self.static_file_name, self.static_file_content, ) # Create one Lambda function self.function_name = "name" self.function = provider.Function( name=self.function_name, runtime="nodejs4.3", memory=256, timeout=5, handler="index.handler", codeuri=self.code_uri, environment={}, rolearn=None, layers=[], ) self.mock_function_provider = Mock() self.mock_function_provider.get.return_value = self.function # Setup two APIs pointing to the same function routes = [ Route(path="/get", methods=["GET"], function_name=self.function_name), Route(path="/post", methods=["POST"], function_name=self.function_name), ] api = Api(routes=routes) self.api_provider_mock = Mock() self.api_provider_mock.get_all.return_value = api # Now wire up the Lambda invoker and pass it through the context self.lambda_invoke_context_mock = Mock() manager = ContainerManager() layer_downloader = LayerDownloader("./", "./") lambda_image = LambdaImage(layer_downloader, False, False) local_runtime = LambdaRuntime(manager, lambda_image) lambda_runner = LocalLambdaRunner(local_runtime, self.mock_function_provider, self.cwd, debug_context=None) self.lambda_invoke_context_mock.local_lambda_runner = lambda_runner self.lambda_invoke_context_mock.get_cwd.return_value = self.cwd
class TestContainerManager_has_image(TestCase): def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.images = Mock() self.mock_docker_client.images.get = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) def test_must_find_an_image(self): self.assertTrue(self.manager.has_image(self.image_name)) def test_must_not_find_image(self): self.mock_docker_client.images.get.side_effect = ImageNotFound("test") self.assertFalse(self.manager.has_image(self.image_name))
def setUp(self): self.mock_docker_client = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) self.image_name = "image name" self.container_mock = Mock() self.container_mock.image = self.image_name self.container_mock.start = Mock() self.container_mock.create = Mock() self.container_mock.is_created = Mock()
def setUp(self): self.code_dir = { "echo": nodejs_lambda(ECHO_CODE), "sleep": nodejs_lambda(SLEEP_CODE), "envvar": nodejs_lambda(GET_ENV_VAR) } self.container_manager = ContainerManager() layer_downloader = LayerDownloader("./", "./") self.lambda_image = LambdaImage(layer_downloader, False, False) self.runtime = LambdaRuntime(self.container_manager, self.lambda_image)
def make_service(function_provider, cwd): port = random_port() manager = ContainerManager() layer_downloader = LayerDownloader("./", "./") image_builder = LambdaImage(layer_downloader, False, False) local_runtime = LambdaRuntime(manager, image_builder) lambda_runner = LocalLambdaRunner(local_runtime=local_runtime, function_provider=function_provider, cwd=cwd) service = LocalLambdaInvokeService(lambda_runner, port=port, host="127.0.0.1") scheme = "http" url = "{}://127.0.0.1:{}".format(scheme, port) return service, port, url, scheme
class TestContainerManager_pull_image(TestCase): def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.api = Mock() self.mock_docker_client.api.pull = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) def test_must_pull_and_print_progress_dots(self): stream = io.StringIO() pull_result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] self.mock_docker_client.api.pull.return_value = pull_result expected_stream_output = "\nFetching {} Docker container image...{}\n".format( self.image_name, '.' * len(pull_result) # Progress bar will print one dot per response from pull API ) self.manager.pull_image(self.image_name, stream=stream) self.mock_docker_client.api.pull.assert_called_with(self.image_name, stream=True, decode=True) self.assertEquals(stream.getvalue(), expected_stream_output) def test_must_raise_if_image_not_found(self): msg = "some error" self.mock_docker_client.api.pull.side_effect = APIError(msg) with self.assertRaises(DockerImagePullFailedException) as context: self.manager.pull_image("imagename") ex = context.exception self.assertEquals(str(ex), msg)
def make_service(function_provider, cwd): port = random_port() manager = ContainerManager() local_runtime = LambdaRuntime(manager) lambda_runner = LocalLambdaRunner(local_runtime=local_runtime, function_provider=function_provider, cwd=cwd) service = LocalLambdaInvokeService(lambda_runner, port=port, host='127.0.0.1') scheme = "http" url = '{}://127.0.0.1:{}'.format(scheme, port) return service, port, url, scheme
def setUp(self): self.code_dir = nodejs_lambda(SLEEP_CODE) Input = namedtuple('Input', ["timeout", "sleep", "check_stdout"]) self.inputs = [ Input(sleep=1, timeout=10, check_stdout=True), Input(sleep=2, timeout=10, check_stdout=True), Input(sleep=3, timeout=10, check_stdout=True), Input(sleep=5, timeout=10, check_stdout=True), Input(sleep=8, timeout=10, check_stdout=True), Input(sleep=13, timeout=12, check_stdout=False), # Must timeout Input(sleep=21, timeout=20, check_stdout=False), # Must timeout. So stdout will be empty ] random.shuffle(self.inputs) container_manager = ContainerManager() self.runtime = LambdaRuntime(container_manager)
def _get_container_manager(docker_network, skip_pull_image): """ Creates a ContainerManager with specified options Parameters ---------- docker_network str Docker network identifier skip_pull_image bool Should the manager skip pulling the image Returns ------- samcli.local.docker.manager.ContainerManager Object representing Docker container manager """ return ContainerManager(docker_network_id=docker_network, skip_pull_image=skip_pull_image)
def setUp(self): self.code_dir = nodejs_lambda(SLEEP_CODE) Input = namedtuple("Input", ["timeout", "sleep", "check_stdout"]) self.inputs = [ Input(sleep=1, timeout=10, check_stdout=True), Input(sleep=2, timeout=10, check_stdout=True), Input(sleep=3, timeout=10, check_stdout=True), Input(sleep=5, timeout=10, check_stdout=True), Input(sleep=8, timeout=10, check_stdout=True), Input(sleep=13, timeout=12, check_stdout=False), # Must timeout Input(sleep=21, timeout=20, check_stdout=False), # Must timeout. So stdout will be empty ] random.shuffle(self.inputs) container_manager = ContainerManager() layer_downloader = LayerDownloader("./", "./") self.lambda_image = LambdaImage(layer_downloader, False, False) self.runtime = LambdaRuntime(container_manager, self.lambda_image)
def local_lambda_runner(self): """ Returns an instance of the runner capable of running Lambda functions locally :return samcli.commands.local.lib.local_lambda.LocalLambdaRunner: Runner configured to run Lambda functions locally """ container_manager = ContainerManager(docker_network_id=self._docker_network, skip_pull_image=self._skip_pull_image) lambda_runtime = LambdaRuntime(container_manager) return LocalLambdaRunner(local_runtime=lambda_runtime, function_provider=self._function_provider, cwd=self.get_cwd(), env_vars_values=self._env_vars_value, debug_context=self._debug_context, aws_profile=self._aws_profile, aws_region=self._aws_region)
def __enter__(self): self._template_dict = get_template_data(self._template_file) self._function_provider = SamFunctionProvider( self._template_dict, self._parameter_overrides) if not self._base_dir: # Base directory, if not provided, is the directory containing the template self._base_dir = str( pathlib.Path(self._template_file).resolve().parent) self._build_dir = self._setup_build_dir(self._build_dir, self._clean) if self._use_container: self._container_manager = ContainerManager( docker_network_id=self._docker_network, skip_pull_image=self._skip_pull_image) return self
def __enter__(self) -> "BuildContext": self._stacks, remote_stack_full_paths = SamLocalStackProvider.get_stacks( self._template_file, parameter_overrides=self._parameter_overrides) if remote_stack_full_paths: LOG.warning( "Below nested stacks(s) specify non-local URL(s), which are unsupported:\n%s\n" "Skipping building resources inside these nested stacks.", "\n".join([ f"- {full_path}" for full_path in remote_stack_full_paths ]), ) # Note(xinhol): self._use_raw_codeuri is added temporarily to fix issue #2717 # when base_dir is provided, codeuri should not be resolved based on template file path. # we will refactor to make all path resolution inside providers intead of in multiple places self._function_provider = SamFunctionProvider(self.stacks, self._use_raw_codeuri) self._layer_provider = SamLayerProvider(self.stacks, self._use_raw_codeuri) if not self._base_dir: # Base directory, if not provided, is the directory containing the template self._base_dir = str( pathlib.Path(self._template_file).resolve().parent) self._build_dir = self._setup_build_dir(self._build_dir, self._clean) if self._cached: cache_path = pathlib.Path(self._cache_dir) cache_path.mkdir(mode=self._BUILD_DIR_PERMISSIONS, parents=True, exist_ok=True) self._cache_dir = str(cache_path.resolve()) if self._use_container: self._container_manager = ContainerManager( docker_network_id=self._docker_network, skip_pull_image=self._skip_pull_image) return self
def _get_container_manager(docker_network, skip_pull_image, shutdown): """ Creates a ContainerManager with specified options Parameters ---------- docker_network str Docker network identifier skip_pull_image bool Should the manager skip pulling the image shutdown bool Should SHUTDOWN events be sent when tearing down image Returns ------- samcli.local.docker.manager.ContainerManager Object representing Docker container manager """ return ContainerManager(docker_network_id=docker_network, skip_pull_image=skip_pull_image, do_shutdown_event=shutdown)
def local_lambda_runner(self): """ Returns an instance of the runner capable of running Lambda functions locally :return samcli.commands.local.lib.local_lambda.LocalLambdaRunner: Runner configured to run Lambda functions locally """ container_manager = ContainerManager( docker_network_id=self._docker_network, skip_pull_image=self._skip_pull_image) layer_downloader = LayerDownloader(self._layer_cache_basedir, self.get_cwd()) image_builder = LambdaImage(layer_downloader, self._skip_pull_image, self._force_image_build) lambda_runtime = LambdaRuntime(container_manager, image_builder) return LocalLambdaRunner(local_runtime=lambda_runtime, function_provider=self._function_provider, cwd=self.get_cwd(), env_vars_values=self._env_vars_value, debug_context=self._debug_context)
class TestContainerManager_run(TestCase): def setUp(self): self.mock_docker_client = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) self.image_name = "image name" self.container_mock = Mock() self.container_mock.image = self.image_name self.container_mock.start = Mock() self.container_mock.create = Mock() self.container_mock.is_created = Mock() def test_must_error_with_warm(self): with self.assertRaises(ValueError): self.manager.run(self.container_mock, warm=True) def test_must_pull_image_and_run_container(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image doesn't exist. self.manager.has_image.return_value = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_pull_image_if_image_exist_and_no_skip(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_not_pull_image_if_image_is_samcli_lambda_image(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.container_mock.image = "samcli/lambda" self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with("samcli/lambda") self.manager.pull_image.assert_not_called() self.container_mock.start.assert_called_with(input_data=input_data) def test_must_not_pull_image_if_asked_to_skip(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, skip pulling self.manager.skip_pull_image = True self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) # Must not call pull_image self.manager.pull_image.assert_not_called() self.container_mock.start.assert_called_with(input_data=input_data) def test_must_fail_if_image_pull_failed_and_image_does_not_exist(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock(side_effect=DockerImagePullFailedException("Failed to pull image")) # Assume the image exist. self.manager.has_image.return_value = False # And, don't skip pulling => Pull again self.manager.skip_pull_image = False with self.assertRaises(DockerImagePullFailedException): self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_not_called() def test_must_run_if_image_pull_failed_and_image_does_exist(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock(side_effect=DockerImagePullFailedException("Failed to pull image")) # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_create_container_if_not_exists(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume container does NOT exist self.container_mock.is_created.return_value = False self.manager.run(self.container_mock, input_data) # Container should be created self.container_mock.create.assert_called_with() def test_must_not_create_container_if_it_already_exists(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume container does NOT exist self.container_mock.is_created.return_value = True self.manager.run(self.container_mock, input_data) # Container should be created self.container_mock.create.assert_not_called()
class TestContainerManager_run(TestCase): def setUp(self): self.mock_docker_client = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) self.image_name = "image name" self.container_mock = Mock() self.container_mock.image = self.image_name self.container_mock.start = Mock() self.container_mock.create = Mock() self.container_mock.is_created = Mock() def test_must_error_with_warm(self): with self.assertRaises(ValueError): self.manager.run(self.container_mock, warm=True) def test_must_pull_image_and_run_container(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image doesn't exist. self.manager.has_image.return_value = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_pull_image_if_image_exist_and_no_skip(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_not_pull_image_if_asked_to_skip(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, skip pulling self.manager.skip_pull_image = True self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) # Must not call pull_image self.manager.pull_image.assert_not_called() self.container_mock.start.assert_called_with(input_data=input_data) def test_must_create_container_if_not_exists(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume container does NOT exist self.container_mock.is_created.return_value = False self.manager.run(self.container_mock, input_data) # Container should be created self.container_mock.create.assert_called_with() def test_must_not_create_container_if_it_already_exists(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume container does NOT exist self.container_mock.is_created.return_value = True self.manager.run(self.container_mock, input_data) # Container should be created self.container_mock.create.assert_not_called()
def setUp(self): self.manager = ContainerManager() self.docker_client = docker.from_env()
def test_must_initialize_with_default_value(self): manager = ContainerManager() self.assertFalse(manager.skip_pull_image)
def setUpClass(cls): manager = ContainerManager() if not manager.has_image(cls.IMAGE_NAME): manager.pull_image(cls.IMAGE_NAME)
class TestContainerManager_pull_image(TestCase): def setUp(self): self.image_name = "image name" self.mock_docker_client = Mock() self.mock_docker_client.api = Mock() self.mock_docker_client.api.pull = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) def test_must_pull_and_print_progress_dots(self): stream = io.StringIO() pull_result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] self.mock_docker_client.api.pull.return_value = pull_result expected_stream_output = "\nFetching {} Docker container image...{}\n".format( self.image_name, "." * len(pull_result ) # Progress bar will print one dot per response from pull API ) self.manager.pull_image(self.image_name, stream=stream) self.mock_docker_client.api.pull.assert_called_with(self.image_name, stream=True, decode=True) self.assertEqual(stream.getvalue(), expected_stream_output) def test_must_raise_if_image_not_found(self): msg = "some error" self.mock_docker_client.api.pull.side_effect = APIError(msg) with self.assertRaises(DockerImagePullFailedException) as context: self.manager.pull_image("imagename") ex = context.exception self.assertEqual(str(ex), msg) @patch("samcli.local.docker.manager.threading") def test_multiple_image_pulls_must_use_locks(self, mock_threading): self.mock_docker_client.api.pull.return_value = [1, 2, 3] # mock general lock mock_lock = MagicMock() self.manager._lock = mock_lock # mock locks per image mock_image1_lock = MagicMock() mock_image2_lock = MagicMock() mock_threading.Lock.side_effect = [mock_image1_lock, mock_image2_lock] # pull 2 different images for multiple times self.manager.pull_image("image1") self.manager.pull_image("image1") self.manager.pull_image("image2") # assert that image1 lock have been used twice and image2 lock only used once mock_image1_lock.assert_has_calls( 2 * [call.__enter__(), call.__exit__(ANY, ANY, ANY)], any_order=True) mock_image2_lock.assert_has_calls( [call.__enter__(), call.__exit__(ANY, ANY, ANY)]) # assert that general lock have been used three times for all the image pulls mock_lock.assert_has_calls( 3 * [call.__enter__(), call.__exit__(ANY, ANY, ANY)], any_order=True)
class TestContainerManager_run(TestCase): def setUp(self): self.mock_docker_client = Mock() self.manager = ContainerManager(docker_client=self.mock_docker_client) self.image_name = "image name" self.container_mock = Mock() self.container_mock.image = self.image_name self.container_mock.start = Mock() self.container_mock.create = Mock() self.container_mock.is_created = Mock() def test_must_error_with_warm(self): with self.assertRaises(ValueError): self.manager.run(self.container_mock, warm=True) def test_must_pull_image_and_run_container(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image doesn't exist. self.manager.has_image.return_value = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_pull_image_if_image_exist_and_no_skip(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_not_pull_image_if_image_is_samcli_lambda_image(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.container_mock.image = "samcli/lambda" self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with("samcli/lambda") self.manager.pull_image.assert_not_called() self.container_mock.start.assert_called_with(input_data=input_data) def test_must_not_pull_image_if_image_is_rapid_image(self): input_data = "input data" rapid_image_name = "Mock_image_name:rapid-1.0.0" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.container_mock.image = rapid_image_name self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(rapid_image_name) self.manager.pull_image.assert_not_called() self.container_mock.start.assert_called_with(input_data=input_data) def test_must_not_pull_image_if_asked_to_skip(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume the image exist. self.manager.has_image.return_value = True # And, skip pulling self.manager.skip_pull_image = True self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) # Must not call pull_image self.manager.pull_image.assert_not_called() self.container_mock.start.assert_called_with(input_data=input_data) def test_must_fail_if_image_pull_failed_and_image_does_not_exist(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock( side_effect=DockerImagePullFailedException("Failed to pull image")) # Assume the image exist. self.manager.has_image.return_value = False # And, don't skip pulling => Pull again self.manager.skip_pull_image = False with self.assertRaises(DockerImagePullFailedException): self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_not_called() def test_must_run_if_image_pull_failed_and_image_does_exist(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock( side_effect=DockerImagePullFailedException("Failed to pull image")) # Assume the image exist. self.manager.has_image.return_value = True # And, don't skip pulling => Pull again self.manager.skip_pull_image = False self.manager.run(self.container_mock, input_data) self.manager.has_image.assert_called_with(self.image_name) self.manager.pull_image.assert_called_with(self.image_name) self.container_mock.start.assert_called_with(input_data=input_data) def test_must_create_container_if_not_exists(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume container does NOT exist self.container_mock.is_created.return_value = False self.manager.run(self.container_mock, input_data) # Container should be created self.container_mock.create.assert_called_with() def test_must_not_create_container_if_it_already_exists(self): input_data = "input data" self.manager.has_image = Mock() self.manager.pull_image = Mock() # Assume container does NOT exist self.container_mock.is_created.return_value = True self.manager.run(self.container_mock, input_data) # Container should be created self.container_mock.create.assert_not_called()
def setUpClass(cls): manager = ContainerManager() if not manager.has_image(cls.IMAGE_NAME): manager.pull_image(cls.IMAGE_NAME)
def setUp(self): self.manager = ContainerManager() self.docker_client = docker.from_env()