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)
Beispiel #9
0
    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")