예제 #1
0
    def test_export_basic_output(self):
        """Should be able to create and export a simple CloudFormation Output object."""
        output = Output(Description="Stuff we need",
                        Value=6644,
                        Export={"Name": "ImportantThingy"})
        exported = output.export("yaml")

        assert exported == dedent("""
        ---
        Description: Stuff we need
        Export:
          Name: ImportantThingy
        Value: 6644
        """)
예제 #2
0
    def test_cannot_merge_if_two_outputs_have_the_same_export_name(self):
        # Setup
        export_name = "SpecialExportedValue"

        source_output = Output(Value=123, Export={"Name": export_name})
        source = Stack(Outputs={"SourceOutput": source_output})

        target_output = Output(Value=987, Export={"Name": export_name})
        target = Stack(Outputs={"TargetOutput": target_output})

        # Exercise & Verify
        with pytest.raises(StackMergeError) as excinfo:
            target.merge_stack(source)

        assert "the target stack already has exports" in str(
            excinfo.value).lower()
예제 #3
0
    def test_dont_create_export_name_for_output_when_it_is_not_set(self):
        # Setup
        name = "SomeItemName"
        stack = Stack(Outputs={name: (Output(Value="HelloWorld"))})

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        new_output = new_stack.Outputs[self.STACK_PREFIX + name]
        assert getattr(new_output, "Export", None) is None
예제 #4
0
    def test_prefix_export_name_for_output(self):
        # Setup
        name = "SomeItemName"
        export_name = "SomeGloballyScopedValue"
        output = Output(Value="HelloWorld", Export={"Name": export_name})
        stack = Stack(Outputs={name: output})

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert new_stack.Outputs[
            self.STACK_PREFIX +
            name]["Export"]["Name"] == self.STACK_PREFIX + export_name
예제 #5
0
def generate_stack_template():
    stack = Stack()

    stack.Resources["WebServer"] = create_ec2_instance("webserver")

    stack.Resources["DatabaseServer"] = dbserver = create_ec2_instance(
        "dbserver", "t2.medium")
    dbserver.DeletionPolicy = "Retain"

    stack.Outputs["DatabaseServerIp"] = Output(
        Description=f"Internal IP address for the database server",
        Value=GetAtt(dbserver, "PrivateIp"),
    )

    stack.tag(application="api-service",
              environment="test",
              owner=os.environ.get("USER"))

    return stack.export("yaml")
예제 #6
0
class TestPrefixedNames:
    """Verify the object name prefixing functionality."""
    ATTRIBUTE_PARAMETRIZE_NAMES = 'stack_attribute,item'
    PREFIXABLE_ATTRIBUTE_EXAMPLES = [
        ("Parameters", Parameter(Type="String")),
        ("Resources", SimpleResource()),
        # TODO #87 Add Condition when we have a helper class
        # TODO #87 Add Mapping when we have a helper class
        # TODO #87 Add Metadata when we have a helper class. Consider whether we should retain the Metadata or just throw it away
    ]
    OUTPUT_EXAMPLES = [
        ("Outputs", Output(Value="HelloWorld")),
        ("Outputs",
         Output(Value="HelloWorld", Export={"Name":
                                            "SomeGloballyScopedValue"})),
    ]

    STACK_PREFIX = "NewScope"

    # Basic Prefixing Behaviour
    # -------------------------

    def test_return_value_is_a_new_stack(self):
        # Setup
        stack = Stack(Resources={"SomeName": SimpleResource()})

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert isinstance(new_stack, Stack)
        assert new_stack is not stack

    @given(aws_logical_name_strategy())
    def test_prefix_is_an_underscored_alphanumeric_string(self, prefix):
        # Setup
        stack = Stack(Resources={"SomeName": SimpleResource()})

        # Exercise & Verify
        _ = stack.with_prefixed_names(prefix)  # Should not throw an error

    def test_prefix_cannot_be_empty(self):
        # Setup
        stack = Stack(Resources={"SomeName": SimpleResource()})

        # Exercise & Verify
        with pytest.raises(ValueError) as excinfo:
            _ = stack.with_prefixed_names("")

        assert "empty" in str(excinfo.value).lower()

    def test_prefix_must_have_leading_capital(self):
        # Setup
        stack = Stack(Resources={"SomeName": SimpleResource()})

        # Exercise & Verify
        with pytest.raises(ValueError) as excinfo:
            _ = stack.with_prefixed_names(
                "lowercasedCamelsAreBactrianButInvalid")

        assert "uppercase" in str(excinfo.value).lower()

    @given(st.from_regex(re.compile(r"^[A-Z]\w*\W+\w*$", re.ASCII)))
    def test_prefix_cannot_contain_special_characters(self, prefix):
        # Setup
        stack = Stack(Resources={"SomeName": SimpleResource()})

        # Exercise & Verify
        with pytest.raises(ValueError) as excinfo:
            _ = stack.with_prefixed_names(prefix)

        assert "alphanumeric" in str(excinfo.value).lower()

    @pytest.mark.parametrize('prefix', [
        None,
        123,
        Stack(),
    ])
    def test_prefix_must_be_string(self, prefix):
        # Setup
        stack = Stack(Resources={"SomeName": SimpleResource()})

        # Exercise & Verify
        with pytest.raises(TypeError) as excinfo:
            _ = stack.with_prefixed_names(prefix)

        assert "string" in str(excinfo.value).lower()

    # Prefixable Dictionaries
    # -----------------------

    @pytest.mark.parametrize(ATTRIBUTE_PARAMETRIZE_NAMES,
                             PREFIXABLE_ATTRIBUTE_EXAMPLES)
    def test_item_is_prefixed_in_the_new_stack(self, stack_attribute, item):
        # Setup
        item_name = "SomeItemName"
        stack = Stack()
        stack[stack_attribute] = {item_name: item}

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        new_name = self.STACK_PREFIX + item_name

        assert len(new_stack[stack_attribute]) == 1
        assert item_name not in new_stack[stack_attribute]
        assert new_stack[stack_attribute][new_name] is item

    @pytest.mark.parametrize(ATTRIBUTE_PARAMETRIZE_NAMES, OUTPUT_EXAMPLES)
    def test_output_is_prefixed_in_the_new_stack_but_not_same_object(
            self, stack_attribute, item):
        # Setup
        item_name = "SomeItemName"
        stack = Stack()
        stack[stack_attribute] = {item_name: item}

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        new_name = self.STACK_PREFIX + item_name

        assert len(new_stack[stack_attribute]) == 1
        assert item_name not in new_stack[stack_attribute]

        new_item = new_stack[stack_attribute][new_name]
        assert new_item is not item
        assert getattr(new_item, "Description",
                       None) == getattr(item, "Description", None)
        assert getattr(new_item, "Value", None) is getattr(item, "Value", None), \
            "This should be the same because it might be a Reference function or some such"

    @pytest.mark.parametrize(ATTRIBUTE_PARAMETRIZE_NAMES,
                             PREFIXABLE_ATTRIBUTE_EXAMPLES + OUTPUT_EXAMPLES)
    def test_item_is_not_removed_from_original_stack(self, stack_attribute,
                                                     item):
        # Setup
        item_name = "SomeItemName"
        stack = Stack()
        stack[stack_attribute] = {item_name: item}

        # Exercise
        _ = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert len(stack[stack_attribute]) == 1
        assert stack[stack_attribute][item_name] is item
        assert stack[stack_attribute][item_name] == item

    # Special Cases
    # -------------

    def test_copy_cfn_template_version(self):
        """AWSTemplateFormatVersion should be a string which we copy across unchanged"""
        # Setup
        version_string = "WhatIfThisWaSemanticallyVersioned.1.0"
        stack = Stack(AWSTemplateFormatVersion=version_string)

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert new_stack.AWSTemplateFormatVersion == version_string
        assert stack.AWSTemplateFormatVersion == version_string, "Old stack should not be modified"

    def test_copy_sam_version_in_transform(self):
        """If set, Transform should be the version of the Serverless Application Model
        being used, which we copy across unchanged.
        """
        # Setup
        version_string = "AWS::Serverless-1999-12-31"
        stack = Stack(Transform=version_string)

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert new_stack.Transform == version_string
        assert stack.Transform == version_string, "Old stack should not be modified"

    def test_modify_content_of_description(self):
        # Setup
        stack = Stack(Description=LOREM_IPSUM)

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert self.STACK_PREFIX in new_stack.Description
        assert LOREM_IPSUM in new_stack.Description
        assert stack.Description == LOREM_IPSUM, "Old stack should not be modified"

    def test_create_description_when_it_is_not_set(self):
        # Setup
        stack = Stack()

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert self.STACK_PREFIX == new_stack.Description
        assert not hasattr(
            stack, "Description"
        ) or stack.Description is None, "Old stack should not be modified"

    def test_prefix_export_name_for_output(self):
        # Setup
        name = "SomeItemName"
        export_name = "SomeGloballyScopedValue"
        output = Output(Value="HelloWorld", Export={"Name": export_name})
        stack = Stack(Outputs={name: output})

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        assert new_stack.Outputs[
            self.STACK_PREFIX +
            name]["Export"]["Name"] == self.STACK_PREFIX + export_name

    def test_dont_create_export_name_for_output_when_it_is_not_set(self):
        # Setup
        name = "SomeItemName"
        stack = Stack(Outputs={name: (Output(Value="HelloWorld"))})

        # Exercise
        new_stack = stack.with_prefixed_names(self.STACK_PREFIX)

        # Verify
        new_output = new_stack.Outputs[self.STACK_PREFIX + name]
        assert getattr(new_output, "Export", None) is None
예제 #7
0
class TestMergeStack:
    """Verify the stack merge functionality."""
    PARAMETRIZE_NAMES = 'stack_attribute,item'
    MERGED_ATTRIBUTE_EXAMPLES = [
        ("Outputs", Output(Value="HelloWorld")),
        ("Parameters", Parameter(Type="String")),
        ("Resources", SimpleResource()),
        # TODO #87 Add Condition when we have a helper class
        # TODO #87 Add Mapping when we have a helper class
    ]

    # TODO #87 test_does_not_copy_metadata when we have a Metadata class

    def test_merge_returns_target_stack(self):
        # Setup
        source = Stack(Resources={"SomeResource": SimpleResource()})
        target = Stack()

        # Exercise
        result = target.merge_stack(source)

        # Verify
        assert target is result

    @pytest.mark.parametrize(PARAMETRIZE_NAMES, MERGED_ATTRIBUTE_EXAMPLES)
    def test_item_is_added_to_the_target_stack(self, stack_attribute, item):
        # Setup
        item_name = "SomeChildProperty"
        source = Stack()
        source[stack_attribute] = {item_name: item}
        target = Stack()

        # Exercise
        target.merge_stack(source)

        # Verify
        assert len(target[stack_attribute]) == 1
        assert target[stack_attribute][item_name] is item

    @pytest.mark.parametrize(PARAMETRIZE_NAMES, MERGED_ATTRIBUTE_EXAMPLES)
    def test_item_is_not_removed_from_source_stack(self, stack_attribute,
                                                   item):
        # Setup
        item_name = "SomeChildProperty"
        source = Stack()
        source[stack_attribute] = {item_name: item}
        target = Stack()

        # Exercise
        target.merge_stack(source)

        # Verify
        assert len(source[stack_attribute]) == 1
        assert source[stack_attribute][item_name] is item

    @pytest.mark.parametrize(PARAMETRIZE_NAMES, MERGED_ATTRIBUTE_EXAMPLES)
    def test_does_not_clobber_existing_items_in_target_stack(
            self, stack_attribute, item):
        # Setup
        item_name = "SomeChildProperty"
        source = Stack()
        source[stack_attribute] = {item_name: item}

        existing_item = copy(item)
        existing_item_name = "SomeOldItem"
        target = Stack()
        target[stack_attribute] = {existing_item_name: existing_item}

        # Exercise
        target.merge_stack(source)

        # Verify
        assert len(target[stack_attribute]) == 2
        assert target[stack_attribute][item_name] is item
        assert target[stack_attribute][existing_item_name] is existing_item

    def test_does_not_copy_description(self):
        # Setup
        source = Stack(Description="Source Description")
        original_description = "Target Description"
        target = Stack(Description=original_description)

        # Exercise
        target.merge_stack(source)

        # Verify
        assert target.Description == original_description

    def test_cannot_merge_if_template_version_is_different(self):
        source = Stack(AWSTemplateFormatVersion="123")
        target = Stack(AWSTemplateFormatVersion="456")

        # Exercise & Verify
        with pytest.raises(StackMergeError) as excinfo:
            target.merge_stack(source)

        assert "template version" in str(excinfo.value).lower()

    def test_cannot_merge_if_sam_transform_version_is_different(self):
        source = Stack(Transform="123")
        target = Stack(Transform="456")

        # Exercise & Verify
        with pytest.raises(StackMergeError) as excinfo:
            target.merge_stack(source)

        assert "transform version" in str(excinfo.value).lower()

    @pytest.mark.parametrize(PARAMETRIZE_NAMES, MERGED_ATTRIBUTE_EXAMPLES)
    def test_cannot_merge_if_logical_name_is_already_used_for_that_item_type(
            self, stack_attribute, item):
        # Setup
        item_name = "SomeChildProperty"
        source = Stack()
        source[stack_attribute] = {item_name: item}

        existing_item = copy(item)
        target = Stack()
        target[stack_attribute] = {item_name: existing_item}

        # Exercise & Verify
        with pytest.raises(StackMergeError) as excinfo:
            target.merge_stack(source)

        assert "in this stack already has an item with the logical name" in str(
            excinfo.value)

    def test_cannot_merge_if_two_outputs_have_the_same_export_name(self):
        # Setup
        export_name = "SpecialExportedValue"

        source_output = Output(Value=123, Export={"Name": export_name})
        source = Stack(Outputs={"SourceOutput": source_output})

        target_output = Output(Value=987, Export={"Name": export_name})
        target = Stack(Outputs={"TargetOutput": target_output})

        # Exercise & Verify
        with pytest.raises(StackMergeError) as excinfo:
            target.merge_stack(source)

        assert "the target stack already has exports" in str(
            excinfo.value).lower()