def _build_lambda_image(self, function_name: str, metadata: Dict) -> str: """ Build an Lambda image Parameters ---------- function_name str Name of the function (logical id or function name) metadata dict Dictionary representing the Metadata attached to the Resource in the template Returns ------- str The full tag (org/repo:tag) of the image that was built """ LOG.info("Building image for %s function", function_name) dockerfile = cast(str, metadata.get("Dockerfile")) docker_context = cast(str, metadata.get("DockerContext")) # Have a default tag if not present. tag = metadata.get("DockerTag", "latest") docker_tag = f"{function_name.lower()}:{tag}" docker_build_args = metadata.get("DockerBuildArgs", {}) if not isinstance(docker_build_args, dict): raise DockerBuildFailed("DockerBuildArgs needs to be a dictionary!") docker_context_dir = pathlib.Path(self._base_dir, docker_context).resolve() if not is_docker_reachable(self._docker_client): raise DockerConnectionError(msg=f"Building image for {function_name} requires Docker. is Docker running?") if os.environ.get("SAM_BUILD_MODE") and isinstance(docker_build_args, dict): docker_build_args["SAM_BUILD_MODE"] = os.environ.get("SAM_BUILD_MODE") docker_tag = "-".join([docker_tag, docker_build_args["SAM_BUILD_MODE"]]) if isinstance(docker_build_args, dict): LOG.info("Setting DockerBuildArgs: %s for %s function", docker_build_args, function_name) build_logs = self._docker_client.api.build( path=str(docker_context_dir), dockerfile=dockerfile, tag=docker_tag, buildargs=docker_build_args, decode=True, ) # The Docker-py low level api will stream logs back but if an exception is raised by the api # this is raised when accessing the generator. So we need to wrap accessing build_logs in a try: except. try: self._stream_lambda_image_build_logs(build_logs, function_name) except docker.errors.APIError as e: if e.is_server_error and "Cannot locate specified Dockerfile" in e.explanation: raise DockerfileOutSideOfContext(e.explanation) from e # Not sure what else can be raise that we should be catching but re-raising for now raise return docker_tag
def is_docker_reachable(self): """ Checks if Docker daemon is running. This is required for us to invoke the function locally Returns ------- bool True, if Docker is available, False otherwise """ return utils.is_docker_reachable(self.docker_client)