def test_generate_dockerfile(self, docker_patch): docker_client_mock = Mock() docker_patch.from_env.return_value = docker_client_mock expected_docker_file = "FROM python\nADD --chown=sbx_user1051:495 layer1 /opt\n" layer_mock = Mock() layer_mock.name = "layer1" self.assertEquals(LambdaImage._generate_dockerfile("python", [layer_mock]), expected_docker_file)
def test_not_force_building_image_that_doesnt_already_exists(self, generate_docker_image_version_patch, build_image_patch): layer_downloader_mock = Mock() layer_downloader_mock.download_all.return_value = ["layers1"] generate_docker_image_version_patch.return_value = "image-version" docker_client_mock = Mock() docker_client_mock.images.get.side_effect = ImageNotFound("image not found") lambda_image = LambdaImage(layer_downloader_mock, False, False, docker_client=docker_client_mock) actual_image_id = lambda_image.build("python3.6", ["layers1"]) self.assertEquals(actual_image_id, "samcli/lambda:image-version") layer_downloader_mock.download_all.assert_called_once_with(["layers1"], False) generate_docker_image_version_patch.assert_called_once_with(["layers1"], "python3.6") docker_client_mock.images.get.assert_called_once_with("samcli/lambda:image-version") build_image_patch.assert_called_once_with("lambci/lambda:python3.6", "samcli/lambda:image-version", ["layers1"])
def test_generate_docker_image_version(self, hashlib_patch): haslib_sha256_mock = Mock() hashlib_patch.sha256.return_value = haslib_sha256_mock haslib_sha256_mock.hexdigest.return_value = "thisisahexdigestofshahash" layer_mock = Mock() layer_mock.name = 'layer1' image_version = LambdaImage._generate_docker_image_version([layer_mock], 'runtime') self.assertEquals(image_version, "runtime-thisisahexdigestofshahash") hashlib_patch.sha256.assert_called_once_with(b'layer1')
def test_not_building_image_that_already_exists(self, generate_docker_image_version_patch, build_image_patch): layer_downloader_mock = Mock() layer_mock = Mock() layer_mock.name = "layers1" layer_mock.is_defined_within_template = False layer_downloader_mock.download_all.return_value = [layer_mock] generate_docker_image_version_patch.return_value = "image-version" docker_client_mock = Mock() docker_client_mock.images.get.return_value = Mock() lambda_image = LambdaImage(layer_downloader_mock, False, False, docker_client=docker_client_mock) actual_image_id = lambda_image.build("python3.6", [layer_mock]) self.assertEquals(actual_image_id, "samcli/lambda:image-version") layer_downloader_mock.download_all.assert_called_once_with([layer_mock], False) generate_docker_image_version_patch.assert_called_once_with([layer_mock], "python3.6") docker_client_mock.images.get.assert_called_once_with("samcli/lambda:image-version") build_image_patch.assert_not_called()
def test_basic_creation(self): """ A docker container must be successfully created """ layer_downloader = LayerDownloader("./", "./") image_builder = LambdaImage(layer_downloader, False, False) container = LambdaContainer(self.runtime, self.handler, self.code_dir, self.layers, image_builder) self.assertIsNone(container.id, "Container must not have ID before creation") # Create the container and verify its properties with self._create(container): self.assertIsNotNone(container.id, "Container must have an ID") # Call Docker API to make sure container indeed exists actual_container = self.docker_client.containers.get(container.id) self.assertEquals(actual_container.status, "created") self.assertTrue( self.expected_docker_image in actual_container.image.tags, "Image name of the container must be " + self.expected_docker_image)
def test_build_image(self, generate_dockerfile_patch, path_patch, uuid_patch, create_tarball_patch): uuid_patch.uuid4.return_value = "uuid" generate_dockerfile_patch.return_value = "Dockerfile content" docker_full_path_mock = Mock() docker_full_path_mock.exists.return_value = True path_patch.return_value = docker_full_path_mock docker_client_mock = Mock() layer_downloader_mock = Mock() layer_downloader_mock.layer_cache = "cached layers" tarball_fileobj = Mock() create_tarball_patch.return_value.__enter__.return_value = tarball_fileobj layer_version1 = Mock() layer_version1.codeuri = "somevalue" layer_version1.name = "name" dockerfile_mock = Mock() m = mock_open(dockerfile_mock) with patch("samcli.local.docker.lambda_image.open", m): LambdaImage(layer_downloader_mock, True, False, docker_client=docker_client_mock)\ ._build_image("base_image", "docker_tag", [layer_version1]) handle = m() handle.write.assert_called_with("Dockerfile content") path_patch.assert_called_once_with("cached layers", "dockerfile_uuid") docker_client_mock.images.build.assert_called_once_with( fileobj=tarball_fileobj, rm=True, tag="docker_tag", pull=False, custom_context=True) docker_full_path_mock.unlink.assert_called_once()
def test_debug_port_is_created_on_host(self): layer_downloader = LayerDownloader("./", "./") image_builder = LambdaImage(layer_downloader, False, False) container = LambdaContainer(self.runtime, self.handler, self.code_dir, self.layers, image_builder, debug_options=self.debug_context) with self._create(container): container.start() # After container is started, query the container to make sure it is bound to the right ports port_binding = self.docker_client.api.port(container.id, self.debug_port) self.assertIsNotNone( port_binding, "Container must be bound to a port on host machine") self.assertEqual(1, len(port_binding), "Only one port must be bound to the container") self.assertEqual(port_binding[0]["HostPort"], str(self.debug_port))
def test_function_result_is_available_in_stdout_and_logs_in_stderr(self): # This is the JSON result from Lambda function # Convert to proper binary type to be compatible with Python 2 & 3 expected_output = b'{"a":"b"}' expected_stderr = b"**This string is printed from Lambda function**" layer_downloader = LayerDownloader("./", "./") image_builder = LambdaImage(layer_downloader, False) container = LambdaContainer(self.runtime, self.handler, self.code_dir, self.layers, image_builder) stdout_stream = io.BytesIO() stderr_stream = io.BytesIO() with self._create(container): container.start() container.wait_for_logs(stdout=stdout_stream, stderr=stderr_stream) function_output = stdout_stream.getvalue() function_stderr = stderr_stream.getvalue() self.assertEquals(function_output.strip(), expected_output) self.assertIn(expected_stderr, function_stderr)
def test_must_invoke(self): input_event = '"some data"' expected_env_vars = {"var1": "override_value1", "var2": "shell_env_value2"} manager = ContainerManager() layer_downloader = LayerDownloader("./", "./") lambda_image = LambdaImage(layer_downloader, False, False) local_runtime = LambdaRuntime(manager, lambda_image) runner = LocalLambdaRunner( local_runtime, self.mock_function_provider, self.cwd, self.env_var_overrides, debug_context=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() stdout_stream_writer = StreamWriter(stdout_stream) stderr_stream_writer = StreamWriter(stderr_stream) runner.invoke(self.function_name, input_event, stdout=stdout_stream_writer, stderr=stderr_stream_writer) # 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.assertEqual(actual_output.get(key), value)
def test_building_image_with_no_layers(self): docker_client_mock = Mock() lambda_image = LambdaImage("layer_downloader", False, False, docker_client=docker_client_mock) self.assertEquals(lambda_image.build("python3.6", []), "lambci/lambda:python3.6")
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
def test_building_image_with_no_layers(self): docker_client_mock = Mock() lambda_image = LambdaImage("layer_downloader", False, False, docker_client=docker_client_mock) self.assertEqual(lambda_image.build("python3.6", []), "lambci/lambda:python3.6")