Ejemplo n.º 1
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
Ejemplo n.º 2
0
    def test_must_return_sys_stderr(self):

        expected_stderr = sys.stderr

        if sys.version_info.major > 2:
            expected_stderr = sys.stderr.buffer

        self.assertEqual(expected_stderr, osutils.stderr())
Ejemplo n.º 3
0
    def test_must_return_sys_stderr(self):

        expected_stderr = sys.stderr

        if sys.version_info.major > 2:
            expected_stderr = sys.stderr.buffer

        self.assertEquals(expected_stderr, osutils.stderr())
Ejemplo n.º 4
0
    def stderr(self):
        """
        Returns stderr stream to output Lambda function errors to

        :return File like object: Stream where the stderr of the function is sent to
        """
        if self._log_file_handle:
            return self._log_file_handle

        return osutils.stderr()
Ejemplo n.º 5
0
    def stderr(self) -> StreamWriter:
        """
        Returns stream writer for stderr to output Lambda function errors to

        Returns
        -------
        samcli.lib.utils.stream_writer.StreamWriter
            Stream writer for stderr
        """
        stream = self._log_file_handle if self._log_file_handle else osutils.stderr()
        return StreamWriter(stream, self._is_debugging)
Ejemplo n.º 6
0
    def stderr(self):
        """
        Returns stream writer for stderr to output Lambda function errors to

        Returns
        -------
        samcli.lib.utils.stream_writer.StreamWriter
            Stream writer for stderr
        """
        stream = self._log_file_handle if self._log_file_handle else osutils.stderr()
        return StreamWriter(stream, self._is_debugging)
Ejemplo n.º 7
0
 def __init__(self,
              docker_client,
              ecr_client,
              ecr_repo,
              tag="latest",
              stream=stderr()):
     self.docker_client = docker_client if docker_client else docker.from_env(
     )
     self.ecr_client = ecr_client
     self.ecr_repo = ecr_repo
     self.tag = tag
     self.auth_config = {}
     self.stream = StreamWriter(stream=stream, auto_flush=True)
Ejemplo n.º 8
0
    def __init__(self,
                 resources_to_build,
                 build_dir,
                 base_dir,
                 cache_dir,
                 cached=False,
                 is_building_specific_resource=False,
                 manifest_path_override=None,
                 container_manager=None,
                 parallel=False,
                 mode=None,
                 stream_writer=None,
                 docker_client=None):
        """
        Initialize the class

        Parameters
        ----------
        resources_to_build: Iterator
            Iterator that can vend out resources available in the SAM template

        build_dir : str
            Path to the directory where we will be storing built artifacts

        base_dir : str
            Path to a folder. Use this folder as the root to resolve relative source code paths against

        cache_dir : str
            Path to a the directory where we will be caching built artifacts

        cached:
            Optional. Set to True to build each function with cache to improve performance

        is_building_specific_resource : boolean
            Whether customer requested to build a specific resource alone in isolation,
            by specifying function_identifier to the build command.
            Ex: sam build MyServerlessFunction

        container_manager : samcli.local.docker.manager.ContainerManager
            Optional. If provided, we will attempt to build inside a Docker Container

        parallel : bool
            Optional. Set to True to build each function in parallel to improve performance

        mode : str
            Optional, name of the build mode to use ex: 'debug'
        """
        self._resources_to_build = resources_to_build
        self._build_dir = build_dir
        self._base_dir = base_dir
        self._cache_dir = cache_dir
        self._cached = cached
        self._manifest_path_override = manifest_path_override
        self._is_building_specific_resource = is_building_specific_resource

        self._container_manager = container_manager
        self._parallel = parallel
        self._mode = mode
        self._stream_writer = stream_writer if stream_writer else StreamWriter(
            osutils.stderr())
        self._docker_client = docker_client if docker_client else docker.from_env(
        )

        self._deprecated_runtimes = {
            "nodejs4.3", "nodejs6.10", "nodejs8.10", "dotnetcore2.0"
        }
        self._colored = Colored()
Ejemplo n.º 9
0
    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