def test_layers_created_from_template_resources(self):
        resources = {
            "Layer": {"Type": "AWS::Lambda::LayerVersion", "Properties": {"Content": {"Bucket": "bucket"}}},
            "ServerlessLayer": {"Type": "AWS::Serverless::LayerVersion", "Properties": {"ContentUri": "/somepath"}},
        }

        list_of_layers = [
            {"Ref": "Layer"},
            {"Ref": "ServerlessLayer"},
            "arn:aws:lambda:region:account-id:layer:layer-name:1",
            {"NonRef": "Something"},
        ]
        actual = SamFunctionProvider._parse_layer_info(
            Mock(stack_path=STACK_PATH, location="template.yaml", resources=resources), list_of_layers
        )

        for (actual_layer, expected_layer) in zip(
            actual,
            [
                LayerVersion("Layer", ".", stack_path=STACK_PATH),
                LayerVersion("ServerlessLayer", "/somepath", stack_path=STACK_PATH),
                LayerVersion("arn:aws:lambda:region:account-id:layer:layer-name:1", None, stack_path=STACK_PATH),
            ],
        ):
            self.assertEqual(actual_layer, expected_layer)
    def download(self, layer: LayerVersion, force=False) -> LayerVersion:
        """
        Download a given layer to the local cache.

        Parameters
        ----------
        layer samcli.commands.local.lib.provider.Layer
            Layer representing the layer to be downloaded.
        force bool
            True to download the layer even if it exists already on the system

        Returns
        -------
        Path
            Path object that represents where the layer is download to
        """
        if layer.is_defined_within_template:
            LOG.info("%s is a local Layer in the template", layer.name)
            # the template file containing the layer might not be in the same directory as root template file
            # therefore we need to join the path of template directory and codeuri in case codeuri is a relative path.
            try:
                stack = next(stack for stack in self._stacks
                             if stack.stack_path == layer.stack_path)
            except StopIteration as ex:
                raise RuntimeError(
                    f"Cannot find stack that matches layer's stack_path {layer.stack_path}"
                ) from ex

            codeuri = (layer.codeuri
                       if os.path.isabs(layer.codeuri) else os.path.normpath(
                           os.path.join(os.path.dirname(stack.location),
                                        layer.codeuri)))
            layer.codeuri = resolve_code_path(self.cwd, codeuri)
            return layer

        layer_path = Path(self.layer_cache).resolve().joinpath(layer.name)
        is_layer_downloaded = self._is_layer_cached(layer_path)
        layer.codeuri = str(layer_path)

        if is_layer_downloaded and not force:
            LOG.info("%s is already cached. Skipping download", layer.arn)
            return layer

        layer_zip_path = layer.codeuri + ".zip"
        layer_zip_uri = self._fetch_layer_uri(layer)
        unzip_from_uri(
            layer_zip_uri,
            layer_zip_path,
            unzip_output_dir=layer.codeuri,
            progressbar_label="Downloading {}".format(layer.layer_arn),
        )

        return layer
Beispiel #3
0
    def test_layer_version_raises_unsupported_intrinsic(self):
        intrinsic_arn = {
            "Fn::Sub": ["arn:aws:lambda:region:account-id:layer:{layer_name}:1", {"layer_name": "layer-name"}]
        }

        with self.assertRaises(UnsupportedIntrinsic):
            LayerVersion(intrinsic_arn, ".")
Beispiel #4
0
    def setUp(self):
        self.manager_mock = Mock()

        self.name = "name"
        self.lang = "runtime"
        self.handler = "handler"
        self.code_path = "code-path"

        self.layer1_code_path = "layer1-code-path"
        self.layer1_arn = "layer1-arn"
        self.layers = [
            LayerVersion(arn=self.layer1_arn,
                         codeuri=self.layer1_code_path,
                         compatible_runtimes=self.lang)
        ]
        self.imageuri = None
        self.packagetype = ZIP
        self.imageconfig = None
        self.func_config = FunctionConfig(
            self.name,
            self.lang,
            self.handler,
            self.imageuri,
            self.imageconfig,
            self.packagetype,
            self.code_path,
            self.layers,
        )
Beispiel #5
0
    def setUp(self):
        self.manager_mock = Mock()
        lambda_image_mock = Mock()
        self.runtime = WarmLambdaRuntime(self.manager_mock, lambda_image_mock)

        self.observer_mock = Mock()
        self.runtime._observer = self.observer_mock

        self.lang = "runtime"
        self.handler = "handler"
        self.imageuri = None
        self.imageconfig = None

        self.func1_name = "func1_name"
        self.func1_code_path = "func1_code_path"

        self.func2_name = "func2_name"
        self.func2_code_path = "func2_code_path"

        self.common_layer_code_path = "layer1-code-path"
        self.common_layer_arn = "layer1-arn"
        self.common_layers = [
            LayerVersion(arn=self.common_layer_arn,
                         codeuri=self.common_layer_code_path,
                         compatible_runtimes=self.lang)
        ]

        self.func_config1 = FunctionConfig(
            self.func1_name,
            self.lang,
            self.handler,
            self.imageuri,
            self.imageconfig,
            ZIP,
            self.func1_code_path,
            self.common_layers,
        )
        self.func_config2 = FunctionConfig(
            self.func2_name,
            self.lang,
            self.handler,
            self.imageuri,
            self.imageconfig,
            IMAGE,
            self.func2_code_path,
            self.common_layers,
        )

        self.func1_container_mock = Mock()
        self.func2_container_mock = Mock()
        self.runtime._containers = {
            self.func1_name: self.func1_container_mock,
            self.func2_name: self.func2_container_mock,
        }
Beispiel #6
0
    def test_must_ignore_opt_in_AmazonLinux1803_layer(self):
        resources = {}

        list_of_layers = [
            "arn:aws:lambda:region:account-id:layer:layer-name:1",
            "arn:aws:lambda:::awslayer:AmazonLinux1803",
        ]
        actual = SamFunctionProvider._parse_layer_info(list_of_layers, resources)

        for (actual_layer, expected_layer) in zip(
            actual, [LayerVersion("arn:aws:lambda:region:account-id:layer:layer-name:1", None)]
        ):
            self.assertEqual(actual_layer, expected_layer)
Beispiel #7
0
    def download(self, layer: LayerVersion, force=False) -> LayerVersion:
        """
        Download a given layer to the local cache.

        Parameters
        ----------
        layer samcli.commands.local.lib.provider.Layer
            Layer representing the layer to be downloaded.
        force bool
            True to download the layer even if it exists already on the system

        Returns
        -------
        Path
            Path object that represents where the layer is download to
        """
        if layer.is_defined_within_template:
            LOG.info("%s is a local Layer in the template", layer.name)
            layer.codeuri = resolve_code_path(self.cwd, layer.codeuri)
            return layer

        layer_path = Path(self.layer_cache).resolve().joinpath(layer.name)
        is_layer_downloaded = self._is_layer_cached(layer_path)
        layer.codeuri = str(layer_path)

        if is_layer_downloaded and not force:
            LOG.info("%s is already cached. Skipping download", layer.arn)
            return layer

        layer_zip_path = layer.codeuri + ".zip"
        layer_zip_uri = self._fetch_layer_uri(layer)
        unzip_from_uri(
            layer_zip_uri,
            layer_zip_path,
            unzip_output_dir=layer.codeuri,
            progressbar_label="Downloading {}".format(layer.layer_arn),
        )

        return layer
    def test_must_ignore_opt_in_AmazonLinux1803_layer(self):
        resources = {}

        list_of_layers = [
            "arn:aws:lambda:region:account-id:layer:layer-name:1",
            "arn:aws:lambda:::awslayer:AmazonLinux1803",
        ]
        actual = SamFunctionProvider._parse_layer_info(
            Mock(stack_path=STACK_PATH, location="template.yaml", resources=resources), list_of_layers
        )

        for (actual_layer, expected_layer) in zip(
            actual, [LayerVersion("arn:aws:lambda:region:account-id:layer:layer-name:1", None, stack_path=STACK_PATH)]
        ):
            self.assertEqual(actual_layer, expected_layer)
Beispiel #9
0
    def test_codeuri_is_setable(self):
        layer_version = LayerVersion(
            "arn:aws:lambda:region:account-id:layer:layer-name:1", None)
        layer_version.codeuri = "./some_value"

        self.assertEqual(layer_version.codeuri, "./some_value")
Beispiel #10
0
    def test_layer_build_method_returned(self):
        layer_version = LayerVersion(
            "arn:aws:lambda:region:account-id:layer:layer-name:1", None,
            {"BuildMethod": "dummy_build_method"})

        self.assertEqual(layer_version.build_method, "dummy_build_method")
Beispiel #11
0
    def test_layer_arn_returned(self):
        layer_version = LayerVersion(
            "arn:aws:lambda:region:account-id:layer:layer-name:1", None)

        self.assertEqual(layer_version.layer_arn,
                         "arn:aws:lambda:region:account-id:layer:layer-name")
Beispiel #12
0
 def test_invalid_arn(self, arn):
     with self.assertRaises(InvalidLayerVersionArn):
         LayerVersion(arn, None)
Beispiel #13
0
class TestSamLayerProvider(TestCase):
    TEMPLATE = {
        "Resources": {
            "ServerlessLayer": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
                "Metadata": {
                    "BuildMethod": "python3.8"
                },
            },
            "LambdaLayer": {
                "Type": "AWS::Lambda::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "Content": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
                "Metadata": {
                    "BuildMethod": "python3.8"
                },
            },
            "ServerlessLayerNoBuild": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "LambdaLayerNoBuild": {
                "Type": "AWS::Lambda::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "Content": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "ServerlessLayerS3Content": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "s3://dummy-bucket/my-layer.zip",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "LambdaLayerS3Content": {
                "Type": "AWS::Lambda::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "Content": {
                        "S3Bucket": "dummy-bucket",
                        "S3Key": "layer.zip"
                    },
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "SamFunc": {
                "Type": "AWS::Serverless::Function",
                "Properties": {
                    # CodeUri is unsupported S3 location
                    "CodeUri": "s3://bucket/key",
                    "Runtime": "nodejs4.3",
                    "Handler": "index.handler",
                },
            },
            "ChildStack": {
                "Type": "AWS::Serverless::Application",
                "Properties": {
                    "Location": "./child.yaml",
                },
            },
        }
    }

    CHILD_TEMPLATE = {
        "Resources": {
            "SamLayerInChild": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "PyLayer",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
                "Metadata": {
                    "BuildMethod": "python3.8"
                },
            },
        }
    }

    def setUp(self):
        self.parameter_overrides = {}
        root_stack = Stack("", "", "template.yaml", self.parameter_overrides,
                           self.TEMPLATE)
        child_stack = Stack("", "ChildStack", "./child/template.yaml", None,
                            self.CHILD_TEMPLATE)
        with patch("samcli.lib.providers.sam_stack_provider.get_template_data"
                   ) as get_template_data_mock:
            get_template_data_mock.side_effect = lambda t: {
                "template.yaml": self.TEMPLATE,
                "./child/template.yaml": self.CHILD_TEMPLATE,
            }
            self.provider = SamLayerProvider([root_stack, child_stack])

    @parameterized.expand([
        (
            "ServerlessLayer",
            LayerVersion(
                "ServerlessLayer",
                "PyLayer",
                ["python3.8", "python3.6"],
                {"BuildMethod": "python3.8"},
                stack_path="",
            ),
        ),
        (
            "LambdaLayer",
            LayerVersion(
                "LambdaLayer",
                "PyLayer",
                ["python3.8", "python3.6"],
                {"BuildMethod": "python3.8"},
                stack_path="",
            ),
        ),
        (
            "ServerlessLayerNoBuild",
            LayerVersion("ServerlessLayerNoBuild",
                         "PyLayer", ["python3.8", "python3.6"],
                         None,
                         stack_path=""),
        ),
        (
            "LambdaLayerNoBuild",
            LayerVersion("LambdaLayerNoBuild",
                         "PyLayer", ["python3.8", "python3.6"],
                         None,
                         stack_path=""),
        ),
        ("ServerlessLayerS3Content",
         None),  # codeuri is a s3 location, ignored
        ("LambdaLayerS3Content", None),  # codeuri is a s3 location, ignored
        (
            posixpath.join("ChildStack", "SamLayerInChild"),
            LayerVersion(
                "SamLayerInChild",
                os.path.join("child", "PyLayer"),
                ["python3.8", "python3.6"],
                {"BuildMethod": "python3.8"},
                stack_path="ChildStack",
            ),
        ),
    ])
    def test_get_must_return_each_layer(self, name, expected_output):
        actual = self.provider.get(name)
        self.assertEqual(expected_output, actual)

    def test_get_all_must_return_all_layers(self):
        result = [
            posixpath.join(f.stack_path, f.arn)
            for f in self.provider.get_all()
        ]
        expected = [
            "ServerlessLayer",
            "LambdaLayer",
            "ServerlessLayerNoBuild",
            "LambdaLayerNoBuild",
            posixpath.join("ChildStack", "SamLayerInChild"),
        ]

        self.assertEqual(expected, result)

    def test_provider_ignores_non_layer_resource(self):
        self.assertIsNone(self.provider.get("SamFunc"))

    def test_fails_with_empty_name(self):
        with self.assertRaises(ValueError):
            self.provider.get("")
Beispiel #14
0
class TestSamLayerProvider(TestCase):
    TEMPLATE = {
        "Resources": {
            "ServerlessLayer": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
                "Metadata": {"BuildMethod": "python3.8"},
            },
            "LambdaLayer": {
                "Type": "AWS::Lambda::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "Content": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
                "Metadata": {"BuildMethod": "python3.8"},
            },
            "ServerlessLayerNoBuild": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "LambdaLayerNoBuild": {
                "Type": "AWS::Lambda::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "Content": "PyLayer/",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "ServerlessLayerS3Content": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "ContentUri": "s3://dummy-bucket/my-layer.zip",
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "LambdaLayerS3Content": {
                "Type": "AWS::Lambda::LayerVersion",
                "Properties": {
                    "LayerName": "Layer1",
                    "Content": {"S3Bucket": "dummy-bucket", "S3Key": "layer.zip"},
                    "CompatibleRuntimes": ["python3.8", "python3.6"],
                },
            },
            "SamFunc": {
                "Type": "AWS::Serverless::Function",
                "Properties": {
                    # CodeUri is unsupported S3 location
                    "CodeUri": "s3://bucket/key",
                    "Runtime": "nodejs4.3",
                    "Handler": "index.handler",
                },
            },
        }
    }

    def setUp(self):
        self.parameter_overrides = {}
        self.provider = SamLayerProvider(self.TEMPLATE, parameter_overrides=self.parameter_overrides)

    @parameterized.expand(
        [
            ("ServerlessLayer", LayerVersion("ServerlessLayer", "PyLayer/", {"BuildMethod": "python3.8"})),
            ("LambdaLayer", LayerVersion("LambdaLayer", "PyLayer/", {"BuildMethod": "python3.8"})),
            ("ServerlessLayerNoBuild", LayerVersion("ServerlessLayerNoBuild", "PyLayer/", None)),
            ("LambdaLayerNoBuild", LayerVersion("LambdaLayerNoBuild", "PyLayer/", None)),
            ("ServerlessLayerS3Content", LayerVersion("ServerlessLayerS3Content", ".", None)),
            ("LambdaLayerS3Content", LayerVersion("LambdaLayerS3Content", ".", None)),
        ]
    )
    def test_get_must_return_each_layer(self, name, expected_output):
        actual = self.provider.get(name)
        self.assertEqual(actual, expected_output)

    def test_get_all_must_return_all_layers(self):
        result = [f.arn for f in self.provider.get_all()]
        expected = [
            "ServerlessLayer",
            "LambdaLayer",
            "ServerlessLayerNoBuild",
            "LambdaLayerNoBuild",
            "ServerlessLayerS3Content",
            "LambdaLayerS3Content",
        ]

        self.assertEqual(result, expected)

    def test_provider_ignores_non_layer_resource(self):
        self.assertIsNone(self.provider.get("SamFunc"))

    def test_fails_with_empty_name(self):
        with self.assertRaises(ValueError):
            self.provider.get("")
Beispiel #15
0
    def test_name_is_computed(self):
        layer_version = LayerVersion(
            "arn:aws:lambda:region:account-id:layer:layer-name:1", None)

        self.assertEqual(layer_version.name, "layer-name-1-8cebcd0539")
Beispiel #16
0
    def test_layer_version_is_defined_in_template(self):
        layer_version = LayerVersion(
            "arn:aws:lambda:region:account-id:layer:layer-name:1", ".")

        self.assertTrue(layer_version.is_defined_within_template)
Beispiel #17
0
 def test_invalid_arn(self, arn):
     layer = LayerVersion(
         arn, None)  # creation of layer does not raise exception
     with self.assertRaises(InvalidLayerVersionArn):
         layer.version, layer.name