Exemplo n.º 1
0
    def test_must_raise_user_exception_on_invalid_imageuri(self, invoke_context_mock):
        invoke_context_mock.side_effect = InvalidIntermediateImageError("invalid imageuri")

        with self.assertRaises(UserException) as context:
            self.call_cli()

        msg = str(context.exception)
        expected = "invalid imageuri"
        self.assertEqual(msg, expected)
Exemplo n.º 2
0
    def build(self, runtime, packagetype, image, layers, stream=None):
        """
        Build the image if one is not already on the system that matches the runtime and layers

        Parameters
        ----------
        runtime str
            Name of the Lambda runtime
        packagetype str
            Packagetype for the Lambda
        image str
            Pre-defined invocation image.
        layers list(samcli.commands.local.lib.provider.Layer)
            List of layers

        Returns
        -------
        str
            The image to be used (REPOSITORY:TAG)
        """
        image_name = None

        if packagetype == IMAGE:
            image_name = image
        elif packagetype == ZIP:
            image_name = f"{self._INVOKE_REPO_PREFIX}-{runtime}:latest"

        if not image_name:
            raise InvalidIntermediateImageError(
                f"Invalid PackageType, PackageType needs to be one of [{ZIP}, {IMAGE}]"
            )

        if image:
            self.skip_pull_image = True

        # Default image tag to be the base image with a tag of 'rapid' instead of latest.
        # If the image name had a digest, removing the @ so that a valid image name can be constructed
        # to use for the local invoke image name.
        image_tag = f"{image_name.split(':')[0].replace('@', '')}:rapid-{version}"

        downloaded_layers = []

        if layers and packagetype == ZIP:
            downloaded_layers = self.layer_downloader.download_all(
                layers, self.force_image_build)

            docker_image_version = self._generate_docker_image_version(
                downloaded_layers, runtime)
            image_tag = f"{self._SAM_CLI_REPO_NAME}:{docker_image_version}"

        image_not_found = False

        # If we are not using layers, build anyways to ensure any updates to rapid get added
        try:
            self.docker_client.images.get(image_tag)
        except docker.errors.ImageNotFound:
            LOG.info("Image was not found.")
            image_not_found = True

        if (self.force_image_build or image_not_found
                or any(layer.is_defined_within_template
                       for layer in downloaded_layers) or not runtime):
            stream_writer = stream or StreamWriter(sys.stderr)
            stream_writer.write("Building image...")
            stream_writer.flush()
            self._build_image(image if image else image_name,
                              image_tag,
                              downloaded_layers,
                              stream=stream_writer)

        return image_tag
Exemplo n.º 3
0
class TestCli(TestCase):
    def setUp(self):
        self.function_id = "id"
        self.template = "template"
        self.eventfile = "eventfile"
        self.env_vars = "env-vars"
        self.container_env_vars = "debug-env-vars"
        self.debug_ports = [123]
        self.debug_args = "args"
        self.debugger_path = "/test/path"
        self.docker_volume_basedir = "basedir"
        self.docker_network = "network"
        self.log_file = "logfile"
        self.skip_pull_image = True
        self.no_event = True
        self.parameter_overrides = {}
        self.layer_cache_basedir = "/some/layers/path"
        self.force_image_build = True
        self.shutdown = False
        self.region_name = "region"
        self.profile = "profile"

    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_cli_must_setup_context_and_invoke(self, get_event_mock,
                                               InvokeContextMock):
        event_data = "data"
        get_event_mock.return_value = event_data

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        # Mock the __enter__ method to return a object inside a context manager
        context_mock = Mock()
        InvokeContextMock.return_value.__enter__.return_value = context_mock

        invoke_cli(
            ctx=ctx_mock,
            function_identifier=self.function_id,
            template=self.template,
            event=self.eventfile,
            no_event=self.no_event,
            env_vars=self.env_vars,
            debug_port=self.debug_ports,
            debug_args=self.debug_args,
            debugger_path=self.debugger_path,
            container_env_vars=self.container_env_vars,
            docker_volume_basedir=self.docker_volume_basedir,
            docker_network=self.docker_network,
            log_file=self.log_file,
            skip_pull_image=self.skip_pull_image,
            parameter_overrides=self.parameter_overrides,
            layer_cache_basedir=self.layer_cache_basedir,
            force_image_build=self.force_image_build,
            shutdown=self.shutdown,
        )

        InvokeContextMock.assert_called_with(
            template_file=self.template,
            function_identifier=self.function_id,
            env_vars_file=self.env_vars,
            docker_volume_basedir=self.docker_volume_basedir,
            docker_network=self.docker_network,
            log_file=self.log_file,
            skip_pull_image=self.skip_pull_image,
            debug_ports=self.debug_ports,
            debug_args=self.debug_args,
            debugger_path=self.debugger_path,
            container_env_vars_file=self.container_env_vars,
            parameter_overrides=self.parameter_overrides,
            layer_cache_basedir=self.layer_cache_basedir,
            force_image_build=self.force_image_build,
            shutdown=self.shutdown,
            aws_region=self.region_name,
            aws_profile=self.profile,
        )

        context_mock.local_lambda_runner.invoke.assert_called_with(
            context_mock.function_identifier,
            event=event_data,
            stdout=context_mock.stdout,
            stderr=context_mock.stderr)
        get_event_mock.assert_called_with(self.eventfile)

    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_cli_must_invoke_with_no_event(self, get_event_mock,
                                           InvokeContextMock):
        self.event = None

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        # Mock the __enter__ method to return a object inside a context manager
        context_mock = Mock()
        InvokeContextMock.return_value.__enter__.return_value = context_mock
        invoke_cli(
            ctx=ctx_mock,
            function_identifier=self.function_id,
            template=self.template,
            event=self.event,
            no_event=self.no_event,
            env_vars=self.env_vars,
            debug_port=self.debug_ports,
            debug_args=self.debug_args,
            debugger_path=self.debugger_path,
            container_env_vars=self.container_env_vars,
            docker_volume_basedir=self.docker_volume_basedir,
            docker_network=self.docker_network,
            log_file=self.log_file,
            skip_pull_image=self.skip_pull_image,
            parameter_overrides=self.parameter_overrides,
            layer_cache_basedir=self.layer_cache_basedir,
            force_image_build=self.force_image_build,
            shutdown=self.shutdown,
        )

        InvokeContextMock.assert_called_with(
            template_file=self.template,
            function_identifier=self.function_id,
            env_vars_file=self.env_vars,
            docker_volume_basedir=self.docker_volume_basedir,
            docker_network=self.docker_network,
            log_file=self.log_file,
            skip_pull_image=self.skip_pull_image,
            debug_ports=self.debug_ports,
            debug_args=self.debug_args,
            debugger_path=self.debugger_path,
            container_env_vars_file=self.container_env_vars,
            parameter_overrides=self.parameter_overrides,
            layer_cache_basedir=self.layer_cache_basedir,
            force_image_build=self.force_image_build,
            shutdown=self.shutdown,
            aws_region=self.region_name,
            aws_profile=self.profile,
        )

        get_event_mock.assert_not_called()
        context_mock.local_lambda_runner.invoke.assert_called_with(
            context_mock.function_identifier,
            event="{}",
            stdout=context_mock.stdout,
            stderr=context_mock.stderr)

    @parameterized.expand([
        param(FunctionNotFound("not found"),
              "Function id not found in template"),
        param(DockerImagePullFailedException("Failed to pull image"),
              "Failed to pull image"),
    ])
    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_must_raise_user_exception_on_function_not_found(
            self, side_effect_exception, expected_exectpion_message,
            get_event_mock, InvokeContextMock):
        event_data = "data"
        get_event_mock.return_value = event_data

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        # Mock the __enter__ method to return a object inside a context manager
        context_mock = Mock()
        InvokeContextMock.return_value.__enter__.return_value = context_mock

        context_mock.local_lambda_runner.invoke.side_effect = side_effect_exception

        with self.assertRaises(UserException) as ex_ctx:

            invoke_cli(
                ctx=ctx_mock,
                function_identifier=self.function_id,
                template=self.template,
                event=self.eventfile,
                no_event=self.no_event,
                env_vars=self.env_vars,
                debug_port=self.debug_ports,
                debug_args=self.debug_args,
                debugger_path=self.debugger_path,
                container_env_vars=self.container_env_vars,
                docker_volume_basedir=self.docker_volume_basedir,
                docker_network=self.docker_network,
                log_file=self.log_file,
                skip_pull_image=self.skip_pull_image,
                parameter_overrides=self.parameter_overrides,
                layer_cache_basedir=self.layer_cache_basedir,
                force_image_build=self.force_image_build,
                shutdown=self.shutdown,
            )

        msg = str(ex_ctx.exception)
        self.assertEqual(msg, expected_exectpion_message)

    @parameterized.expand([
        param(
            InvalidIntermediateImageError(
                "ImageUri not set to a reference-able image for Function: MyFunction"
            ),
            "ImageUri not set to a reference-able image for Function: MyFunction",
        ),
    ])
    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_must_raise_user_exception_on_function_local_invoke_image_not_found_for_IMAGE_packagetype(
            self, side_effect_exception, expected_exectpion_message,
            get_event_mock, InvokeContextMock):
        event_data = "data"
        get_event_mock.return_value = event_data

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        # Mock the __enter__ method to return a object inside a context manager
        context_mock = Mock()
        InvokeContextMock.return_value.__enter__.return_value = context_mock

        context_mock.local_lambda_runner.invoke.side_effect = side_effect_exception

        with self.assertRaises(UserException) as ex_ctx:

            invoke_cli(
                ctx=ctx_mock,
                function_identifier=self.function_id,
                template=self.template,
                event=self.eventfile,
                no_event=self.no_event,
                env_vars=self.env_vars,
                debug_port=self.debug_ports,
                debug_args=self.debug_args,
                debugger_path=self.debugger_path,
                container_env_vars=self.container_env_vars,
                docker_volume_basedir=self.docker_volume_basedir,
                docker_network=self.docker_network,
                log_file=self.log_file,
                skip_pull_image=self.skip_pull_image,
                parameter_overrides=self.parameter_overrides,
                layer_cache_basedir=self.layer_cache_basedir,
                force_image_build=self.force_image_build,
                shutdown=self.shutdown,
            )

        msg = str(ex_ctx.exception)
        self.assertEqual(msg, expected_exectpion_message)

    @parameterized.expand([
        (InvalidSamDocumentException("bad template"), "bad template"),
        (
            InvalidLayerReference(),
            "Layer References need to be of type "
            "'AWS::Serverless::LayerVersion' or 'AWS::Lambda::LayerVersion'",
        ),
        (DebuggingNotSupported("Debugging not supported"),
         "Debugging not supported"),
    ])
    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_must_raise_user_exception_on_invalid_sam_template(
            self, exeception_to_raise, execption_message, get_event_mock,
            InvokeContextMock):
        event_data = "data"
        get_event_mock.return_value = event_data

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        InvokeContextMock.side_effect = exeception_to_raise

        with self.assertRaises(UserException) as ex_ctx:

            invoke_cli(
                ctx=ctx_mock,
                function_identifier=self.function_id,
                template=self.template,
                event=self.eventfile,
                no_event=self.no_event,
                env_vars=self.env_vars,
                debug_port=self.debug_ports,
                debug_args=self.debug_args,
                debugger_path=self.debugger_path,
                container_env_vars=self.container_env_vars,
                docker_volume_basedir=self.docker_volume_basedir,
                docker_network=self.docker_network,
                log_file=self.log_file,
                skip_pull_image=self.skip_pull_image,
                parameter_overrides=self.parameter_overrides,
                layer_cache_basedir=self.layer_cache_basedir,
                force_image_build=self.force_image_build,
                shutdown=self.shutdown,
            )

        msg = str(ex_ctx.exception)
        self.assertEqual(msg, execption_message)

    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_must_raise_user_exception_on_invalid_env_vars(
            self, get_event_mock, InvokeContextMock):
        event_data = "data"
        get_event_mock.return_value = event_data

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        InvokeContextMock.side_effect = OverridesNotWellDefinedError(
            "bad env vars")

        with self.assertRaises(UserException) as ex_ctx:

            invoke_cli(
                ctx=ctx_mock,
                function_identifier=self.function_id,
                template=self.template,
                event=self.eventfile,
                no_event=self.no_event,
                env_vars=self.env_vars,
                debug_port=self.debug_ports,
                debug_args=self.debug_args,
                debugger_path=self.debugger_path,
                container_env_vars=self.container_env_vars,
                docker_volume_basedir=self.docker_volume_basedir,
                docker_network=self.docker_network,
                log_file=self.log_file,
                skip_pull_image=self.skip_pull_image,
                parameter_overrides=self.parameter_overrides,
                layer_cache_basedir=self.layer_cache_basedir,
                force_image_build=self.force_image_build,
                shutdown=self.shutdown,
            )

        msg = str(ex_ctx.exception)
        self.assertEqual(msg, "bad env vars")

    @parameterized.expand([
        param(
            ContainerNotStartableException(
                "Container cannot be started, no free ports on host"),
            "Container cannot be started, no free ports on host",
        ),
    ])
    @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext")
    @patch("samcli.commands.local.invoke.cli._get_event")
    def test_must_raise_user_exception_on_function_no_free_ports(
            self, side_effect_exception, expected_exectpion_message,
            get_event_mock, InvokeContextMock):
        event_data = "data"
        get_event_mock.return_value = event_data

        ctx_mock = Mock()
        ctx_mock.region = self.region_name
        ctx_mock.profile = self.profile

        # Mock the __enter__ method to return a object inside a context manager
        context_mock = Mock()
        InvokeContextMock.return_value.__enter__.return_value = context_mock

        context_mock.local_lambda_runner.invoke.side_effect = side_effect_exception

        with self.assertRaises(UserException) as ex_ctx:

            invoke_cli(
                ctx=ctx_mock,
                function_identifier=self.function_id,
                template=self.template,
                event=self.eventfile,
                no_event=self.no_event,
                env_vars=self.env_vars,
                debug_port=self.debug_ports,
                debug_args=self.debug_args,
                debugger_path=self.debugger_path,
                container_env_vars=self.container_env_vars,
                docker_volume_basedir=self.docker_volume_basedir,
                docker_network=self.docker_network,
                log_file=self.log_file,
                skip_pull_image=self.skip_pull_image,
                parameter_overrides=self.parameter_overrides,
                layer_cache_basedir=self.layer_cache_basedir,
                force_image_build=self.force_image_build,
                shutdown=self.shutdown,
            )

        msg = str(ex_ctx.exception)
        self.assertEqual(msg, expected_exectpion_message)
Exemplo n.º 4
0
    def invoke(self, function_name, event, stdout=None, stderr=None):
        """
        Find the Lambda function with given name and invoke it. Pass the given event to the function and return
        response through the given streams.

        This function will block until either the function completes or times out.

        Parameters
        ----------
        function_name str
            Name of the Lambda function to invoke
        event str
            Event data passed to the function. Must be a valid JSON String.
        stdout samcli.lib.utils.stream_writer.StreamWriter
            Stream writer to write the output of the Lambda function to.
        stderr samcli.lib.utils.stream_writer.StreamWriter
            Stream writer to write the Lambda runtime logs to.

        Raises
        ------
        FunctionNotfound
            When we cannot find a function with the given name
        """

        # Generate the correct configuration based on given inputs
        function = self.provider.get(function_name)

        if not function:
            all_functions = [f.name for f in self.provider.get_all()]
            available_function_message = "{} not found. Possible options in your template: {}".format(
                function_name, all_functions)
            LOG.info(available_function_message)
            raise FunctionNotFound(
                "Unable to find a Function with name '{}'".format(
                    function_name))

        LOG.debug("Found one Lambda function with name '%s'", function_name)
        if function.packagetype == ZIP:
            LOG.info("Invoking %s (%s)", function.handler, function.runtime)
        elif function.packagetype == IMAGE:
            if not function.imageuri:
                raise InvalidIntermediateImageError(
                    f"ImageUri not provided for Function: {function_name} of PackageType: {function.packagetype}"
                )
            LOG.info("Invoking Container created from %s", function.imageuri)
        config = self.get_invoke_config(function)

        # Invoke the function
        try:
            self.local_runtime.invoke(config,
                                      event,
                                      debug_context=self.debug_context,
                                      stdout=stdout,
                                      stderr=stderr)
        except ContainerResponseException:
            # NOTE(sriram-mv): This should still result in a exit code zero to avoid regressions.
            LOG.info("No response from invoke container for %s", function.name)
        except OSError as os_error:
            # pylint: disable=no-member
            if hasattr(os_error, "winerror") and os_error.winerror == 1314:
                raise NoPrivilegeException(
                    "Administrator, Windows Developer Mode, "
                    "or SeCreateSymbolicLinkPrivilege is required to create symbolic link for files: {}, {}"
                    .format(os_error.filename,
                            os_error.filename2)) from os_error

            raise