Exemple #1
0
    def test_must_init_class(self, get_container_dirs_mock,
                             get_entrypoint_mock, get_image_mock,
                             make_request_mock):

        request = make_request_mock.return_value = "somerequest"
        entry = get_entrypoint_mock.return_value = "entrypoint"
        image = get_image_mock.return_value = "imagename"
        container_dirs = get_container_dirs_mock.return_value = {
            "source_dir": "/mysource",
            "manifest_dir": "/mymanifest",
            "artifacts_dir": "/myartifacts",
            "scratch_dir": "/myscratch",
        }

        container = LambdaBuildContainer(
            "protocol",
            "language",
            "dependency",
            "application",
            "/foo/source",
            "/bar/manifest.txt",
            "runtime",
            optimizations="optimizations",
            options="options",
            log_level="log-level",
            mode="mode",
        )

        self.assertEqual(container.image, image)
        self.assertEqual(container.executable_name, "lambda-builders")
        self.assertEqual(container._entrypoint, entry)
        self.assertEqual(container._cmd, [])
        self.assertEqual(container._working_dir, container_dirs["source_dir"])
        self.assertEqual(container._host_dir,
                         str(pathlib.Path("/foo/source").resolve()))
        self.assertEqual(container._env_vars,
                         {"LAMBDA_BUILDERS_LOG_LEVEL": "log-level"})
        self.assertEqual(
            container._additional_volumes,
            {
                str(pathlib.Path("/bar").resolve()): {
                    "bind": container_dirs["manifest_dir"],
                    "mode": "ro"
                }
            },
        )

        self.assertEqual(container._exposed_ports, None)
        self.assertEqual(container._memory_limit_mb, None)
        self.assertEqual(container._network_id, None)
        self.assertEqual(container._container_opts, None)

        make_request_mock.assert_called_once()
        get_entrypoint_mock.assert_called_once_with(request)
        get_image_mock.assert_called_once_with("runtime")
        get_container_dirs_mock.assert_called_once_with(
            str(pathlib.Path("/foo/source").resolve()),
            str(pathlib.Path("/bar").resolve()))
    def test_must_skip_on_empty_input(self):

        input = None
        mapping = {"/known/path": "/first"}

        expected = None
        result = LambdaBuildContainer._convert_to_container_dirs(input, mapping)

        self.assertEqual(result, expected)
    def test_must_skip_unknown_paths(self):

        input = ["/known/path", "/unknown/path"]
        mapping = {"/known/path": "/first"}

        expected = ["/first", "/unknown/path"]
        result = LambdaBuildContainer._convert_to_container_dirs(input, mapping)

        self.assertEqual(result, expected)
    def test_must_work_on_abs_and_relative_paths(self):

        input = [".", "../foo", "/some/abs/path"]
        mapping = {str(pathlib.Path(".").resolve()): "/first", "../foo": "/second", "/some/abs/path": "/third"}

        expected = ["/first", "/second", "/third"]
        result = LambdaBuildContainer._convert_to_container_dirs(input, mapping)

        self.assertEqual(result, expected)
Exemple #5
0
    def _build_function_on_container(
            self,  # pylint: disable=too-many-locals
            config,
            source_dir,
            artifacts_dir,
            scratch_dir,
            manifest_path,
            runtime):

        # If we are printing debug logs in SAM CLI, the builder library should also print debug logs
        log_level = LOG.getEffectiveLevel()

        container = LambdaBuildContainer(lambda_builders_protocol_version,
                                         config.language,
                                         config.dependency_manager,
                                         config.application_framework,
                                         source_dir,
                                         manifest_path,
                                         runtime,
                                         log_level=log_level,
                                         optimizations=None,
                                         options=None)

        try:
            try:
                self._container_manager.run(container)
            except docker.errors.APIError as ex:
                if "executable file not found in $PATH" in str(ex):
                    raise UnsupportedBuilderLibraryVersionError(
                        container.image,
                        "{} executable not found in container".format(
                            container.executable_name))

            # Container's output provides status of whether the build succeeded or failed
            # stdout contains the result of JSON-RPC call
            stdout_stream = io.BytesIO()
            # stderr contains logs printed by the builder. Stream it directly to terminal
            stderr_stream = osutils.stderr()
            container.wait_for_logs(stdout=stdout_stream, stderr=stderr_stream)

            stdout_data = stdout_stream.getvalue().decode('utf-8')
            LOG.debug("Build inside container returned response %s",
                      stdout_data)

            response = self._parse_builder_response(stdout_data,
                                                    container.image)

            # Request is successful. Now copy the artifacts back to the host
            LOG.debug(
                "Build inside container was successful. Copying artifacts from container to host"
            )

            # "/." is a Docker thing that instructions the copy command to download contents of the folder only
            result_dir_in_container = response["result"]["artifacts_dir"] + "/."
            container.copy(result_dir_in_container, artifacts_dir)
        finally:
            self._container_manager.stop(container)

        LOG.debug("Build inside container succeeded")
        return artifacts_dir
Exemple #6
0
    def test_must_return_dirs(self):
        source_dir = "source"
        manifest_dir = "manifest"

        result = LambdaBuildContainer._get_container_dirs(
            source_dir, manifest_dir)

        self.assertEquals(
            result, {
                "source_dir": "/tmp/samcli/source",
                "manifest_dir": "/tmp/samcli/manifest",
                "artifacts_dir": "/tmp/samcli/artifacts",
                "scratch_dir": "/tmp/samcli/scratch",
            })
    def test_must_make_request_object_string(self):

        container_dirs = {
            "source_dir": "source_dir",
            "artifacts_dir": "artifacts_dir",
            "scratch_dir": "scratch_dir",
            "manifest_dir": "manifest_dir",
        }

        result = LambdaBuildContainer._make_request(
            "protocol",
            "language",
            "dependency",
            "application",
            container_dirs,
            "manifest_file_name",
            "runtime",
            "optimizations",
            "options",
            "executable_search_paths",
            "mode",
        )

        self.maxDiff = None  # Print whole json diff
        self.assertEqual(
            json.loads(result),
            {
                "jsonschema": "2.0",
                "id": 1,
                "method": "LambdaBuilder.build",
                "params": {
                    "__protocol_version": "protocol",
                    "capability": {
                        "language": "language",
                        "dependency_manager": "dependency",
                        "application_framework": "application",
                    },
                    "source_dir": "source_dir",
                    "artifacts_dir": "artifacts_dir",
                    "scratch_dir": "scratch_dir",
                    "manifest_path": "manifest_dir/manifest_file_name",
                    "runtime": "runtime",
                    "optimizations": "optimizations",
                    "options": "options",
                    "executable_search_paths": "executable_search_paths",
                    "mode": "mode",
                },
            },
        )
    def test_must_override_manifest_if_equal_to_source(self):
        source_dir = "/home/source"
        manifest_dir = "/home/source"

        result = LambdaBuildContainer._get_container_dirs(source_dir, manifest_dir)

        self.assertEqual(
            result,
            {
                # When source & manifest directories are the same, manifest_dir must be equal to source
                "source_dir": "/tmp/samcli/source",
                "manifest_dir": "/tmp/samcli/source",
                "artifacts_dir": "/tmp/samcli/artifacts",
                "scratch_dir": "/tmp/samcli/scratch",
            },
        )
Exemple #9
0
 def test_must_get_entrypoint(self):
     self.assertEquals(["lambda-builders", "requestjson"],
                       LambdaBuildContainer._get_entrypoint("requestjson"))
Exemple #10
0
 def test_must_get_image_name(self):
     self.assertEquals("lambci/lambda:build-myruntime",
                       LambdaBuildContainer._get_image("myruntime"))
 def test_must_get_image_name(self, runtime, expected_image_name):
     self.assertEqual(expected_image_name, LambdaBuildContainer._get_image(runtime))
    def _build_function_on_container(
        self,  # pylint: disable=too-many-locals
        config: CONFIG,
        source_dir: str,
        artifacts_dir: str,
        scratch_dir: str,
        manifest_path: str,
        runtime: str,
        options: Optional[Dict],
        container_env_vars: Optional[Dict] = None,
    ) -> str:
        # _build_function_on_container() is only called when self._container_manager if not None
        if not self._container_manager:
            raise RuntimeError(
                "_build_function_on_container() is called when self._container_manager is None."
            )

        if not self._container_manager.is_docker_reachable:
            raise BuildInsideContainerError(
                "Docker is unreachable. Docker needs to be running to build inside a container."
            )

        container_build_supported, reason = supports_build_in_container(config)
        if not container_build_supported:
            raise ContainerBuildNotSupported(reason)

        # If we are printing debug logs in SAM CLI, the builder library should also print debug logs
        log_level = LOG.getEffectiveLevel()

        container_env_vars = container_env_vars or {}

        container = LambdaBuildContainer(
            lambda_builders_protocol_version,
            config.language,
            config.dependency_manager,
            config.application_framework,
            source_dir,
            manifest_path,
            runtime,
            log_level=log_level,
            optimizations=None,
            options=options,
            executable_search_paths=config.executable_search_paths,
            mode=self._mode,
            env_vars=container_env_vars,
        )

        try:
            try:
                self._container_manager.run(container)
            except docker.errors.APIError as ex:
                if "executable file not found in $PATH" in str(ex):
                    raise UnsupportedBuilderLibraryVersionError(
                        container.image,
                        "{} executable not found in container".format(
                            container.executable_name)) from ex

            # Container's output provides status of whether the build succeeded or failed
            # stdout contains the result of JSON-RPC call
            stdout_stream = io.BytesIO()
            # stderr contains logs printed by the builder. Stream it directly to terminal
            stderr_stream = osutils.stderr()
            container.wait_for_logs(stdout=stdout_stream, stderr=stderr_stream)

            stdout_data = stdout_stream.getvalue().decode("utf-8")
            LOG.debug("Build inside container returned response %s",
                      stdout_data)

            response = self._parse_builder_response(stdout_data,
                                                    container.image)

            # Request is successful. Now copy the artifacts back to the host
            LOG.debug(
                "Build inside container was successful. Copying artifacts from container to host"
            )

            # "/." is a Docker thing that instructions the copy command to download contents of the folder only
            result_dir_in_container = response["result"]["artifacts_dir"] + "/."
            container.copy(result_dir_in_container, artifacts_dir)
        finally:
            self._container_manager.stop(container)

        LOG.debug("Build inside container succeeded")
        return artifacts_dir