def import_template(migration_environment, aws_stack_name, template_path):
    """
    Saves a template imported from AWS CloudFormation.

    :param path: The absolute path to the file which stores the template.
    :type path: str
    :param body: The body of the imported template.
    :type region: str or dict
    :raises: UnsupportedTemplateFileTypeError
    """
    abs_template_path = os.path.join(
        migration_environment.environment_config.sceptre_dir, template_path)

    logging.getLogger(__name__).debug(
        "%s - Preparing to Import CloudFormation to %s",
        os.path.basename(template_path).split(".")[0], abs_template_path)

    response = migration_environment.connection_manager.call(
        service='cloudformation',
        command='get_template',
        kwargs={
            'StackName': aws_stack_name,
            'TemplateStage': 'Original'
        })

    _write_template(
        abs_template_path,
        _normalize_template_for_write(response['TemplateBody'],
                                      os.path.splitext(template_path)[1]))

    template = Template(abs_template_path, [])
    template.relative_template_path = template_path
    return template
    def test_import_config__empty_stack(self, mock_isfile, mock_open,
                                        mock_print):
        self.migration_environment.connection_manager\
            .call.return_value = {'Stacks': [
                {
                }
            ]}

        mock_isfile.return_value = False
        fake_template = Template('fake-path', [])
        fake_template.relative_template_path = 'fake-relative-path'
        config.import_config(migration_environment=self.migration_environment,
                             aws_stack_name="fake-aws-stack-name",
                             config_path="environment-path/fake-stack",
                             template=fake_template)

        mock_open.assert_called_with(
            "fake-spectre-dir/config/environment-path/fake-stack.yaml", 'w')

        mock_print.assert_has_calls([
            call("template_path: fake-relative-path",
                 file=mock_open.return_value.__enter__.return_value),
            call("stack_name: fake-aws-stack-name",
                 file=mock_open.return_value.__enter__.return_value)
        ],
                                    any_order=False)
Example #3
0
    def setup_method(self, test_method):
        self.region = "region"
        self.bucket_name = "bucket_name"
        self.environment_path = "environment_path"
        self.stack_name = "stack_name"

        self.connection_manager = Mock(spec=ConnectionManager)
        self.connection_manager.create_bucket_lock = threading.Lock()

        self.template = Template(path="/folder/template.py",
                                 sceptre_user_data={})
def test_render_jinja_template_j2_environment_config(mock_environment, stack_group_config, expected_keys):
    filename = "vpc.j2"
    sceptre_user_data = {"vpc_id": "10.0.0.0/16"}
    template = Template(path=filename, sceptre_user_data=sceptre_user_data, stack_group_config=stack_group_config)
    jinja_template_dir = os.path.join(
        os.getcwd(),
        "tests/fixtures/templates"
    )
    template._render_jinja_template(
        template_dir=jinja_template_dir,
        filename=filename,
        jinja_vars={"sceptre_user_data": sceptre_user_data}
    )
    assert list(mock_environment.call_args.kwargs) == expected_keys
def test_render_jinja_template(filename, sceptre_user_data, expected):
    jinja_template_dir = os.path.join(
        os.getcwd(),
        "tests/fixtures/templates"
    )
    template = Template(path=filename, sceptre_user_data=sceptre_user_data, stack_group_config={})
    result = template._render_jinja_template(
        template_dir=jinja_template_dir,
        filename=filename,
        jinja_vars={"sceptre_user_data": sceptre_user_data}
    )
    expected_yaml = yaml.safe_load(expected)
    result_yaml = yaml.safe_load(result)
    assert expected_yaml == result_yaml
Example #6
0
 def setup_method(self, test_method):
     self.patcher_connection_manager = patch(
         "sceptre.plan.actions.ConnectionManager")
     self.mock_ConnectionManager = self.patcher_connection_manager.start()
     self.stack = Stack(name='prod/app/stack',
                        project_code=sentinel.project_code,
                        template_path=sentinel.template_path,
                        region=sentinel.region,
                        profile=sentinel.profile,
                        parameters={"key1": "val1"},
                        sceptre_user_data=sentinel.sceptre_user_data,
                        hooks={},
                        s3_details=None,
                        dependencies=sentinel.dependencies,
                        role_arn=sentinel.role_arn,
                        protected=False,
                        tags={"tag1": "val1"},
                        external_name=sentinel.external_name,
                        notifications=[sentinel.notification],
                        on_failure=sentinel.on_failure,
                        stack_timeout=sentinel.stack_timeout)
     self.actions = StackActions(self.stack)
     self.template = Template("fixtures/templates",
                              self.stack.sceptre_user_data,
                              self.actions.connection_manager,
                              self.stack.s3_details)
     self.stack._template = self.template
Example #7
0
    def template(self):
        """
        Returns the CloudFormation Template used to create the Stack.

        :returns: The Stack's template.
        :rtype: Template
        """
        if self._template is None:
            if self.template_path:
                handler_config = {
                    "type": "file",
                    "path": self.template_path
                }
            else:
                handler_config = self.template_handler_config

            self._template = Template(
                name=self.name,
                handler_config=handler_config,
                sceptre_user_data=self.sceptre_user_data,
                stack_group_config=self.stack_group_config,
                s3_details=self.s3_details,
                connection_manager=self.connection_manager
            )
        return self._template
 def test_import_config__exists(self, mock_isfile):
     mock_isfile.return_value = True
     with pytest.raises(ImportFailureError):
         config.import_config(
             migration_environment=self.migration_environment,
             aws_stack_name="fake-aws-stack-name",
             config_path="environment-path/fake-stack",
             template=Template('fake-path', []))
Example #9
0
    def setup_method(self, test_method):
        self.region = "region"
        self.bucket_name = "bucket_name"
        self.stack_group_path = "stack_group_path"
        self.stack_name = "stack_name"

        connection_manager = Mock(spec=ConnectionManager)
        connection_manager.create_bucket_lock = threading.Lock()

        self.template = Template(
            name="template_name",
            handler_config={"type": "file", "path": "/folder/template.py"},
            sceptre_user_data={},
            stack_group_config={
                "project_path": "projects"
            },
            connection_manager=connection_manager,
        )
Example #10
0
    def test_initialise_template_default_handler_type(self):
        template = Template(
            name="template_name",
            handler_config={"path": "/folder/template.py"},
            sceptre_user_data={},
            stack_group_config={},
            connection_manager={},
        )

        assert template.handler_config == {"type": "file", "path": "/folder/template.py"}
Example #11
0
    def template(self):
        """
        Returns the CloudFormation Template used to create the Stack.

        :returns: The Stack's template.
        :rtype: str
        """
        if self._template is None:
            self._template = Template(
                path=self.template_path,
                sceptre_user_data=self.sceptre_user_data,
                s3_details=self.s3_details,
                connection_manager=self.connection_manager)
        return self._template
Example #12
0
    def template(self):
        """
        Returns the CloudFormation Template used to create the Stack.

        :returns: The Stack's template.
        :rtype: str
        """
        self.connection_manager = ConnectionManager(
            self.region, self.profile, self.external_name
        )
        if self._template is None:
            self._template = Template(
                path=self.template_path,
                sceptre_user_data=self.sceptre_user_data,
                s3_details=self.s3_details,
                connection_manager=self.connection_manager
            )
        return self._template
 def __init__(self,
              connection_manager,
              environment_config,
              import_stack_list=[]):
     self.logger = logging.getLogger(__name__)
     self.connection_manager = connection_manager
     self.environment_config = environment_config
     self.import_stack_list = import_stack_list
     self._reversed_env_config = {
         str(v): "{{ var." + str(k) + " }}"
         for k, v in self.environment_config['user_variables'].items()
     }
     self._config_re_pattern = '|'.join([
         re.escape(str(config_value)) for config_value in
         self.environment_config['user_variables'].values()
     ])
     self._reverse_resolver_list = None
     self.config_path = ""
     self.aws_stack_name = ""
     self.aws_stack = {}
     self.template = Template("", {})
    def resolve(self):
        """
        Retrieves the parameter value from SSM Parameter Store.

        :returns: parameter value
        :rtype: str
        """
        if self._resolver_started:
            return self._resolved_value
        else:
            self._resolver_started = True
            if self.argument:
                # Generate template data using the sceptre template submodule
                if self._template is None:
                    self._template = Template(
                        path=self.argument,
                        sceptre_user_data=self.stack.sceptre_user_data,
                        s3_details=self.stack.s3_details,
                        connection_manager=self.stack.connection_manager)
                self._resolved_value = self._template.body
                return self._resolved_value
            else:
                raise ValueError("No template path given.")
Example #15
0
class TestTemplate(object):

    def setup_method(self, test_method):
        self.region = "region"
        self.bucket_name = "bucket_name"
        self.stack_group_path = "stack_group_path"
        self.stack_name = "stack_name"

        connection_manager = Mock(spec=ConnectionManager)
        connection_manager.create_bucket_lock = threading.Lock()

        self.template = Template(
            path="/folder/template.py",
            sceptre_user_data={},
            stack_group_config={},
            connection_manager=connection_manager,
        )

    def test_initialise_template(self):
        assert self.template.path == "/folder/template.py"
        assert self.template.name == "template"
        assert self.template.sceptre_user_data == {}
        assert self.template._body is None

    def test_repr(self):
        representation = self.template.__repr__()
        assert representation == "sceptre.template.Template(" \
            "name='template', path='/folder/template.py'"\
            ", sceptre_user_data={}, s3_details=None)"

    def test_body_with_cache(self):
        self.template._body = sentinel.body
        body = self.template.body
        assert body == sentinel.body

    @freeze_time("2012-01-01")
    @patch("sceptre.template.Template._bucket_exists")
    def test_upload_to_s3_with_valid_s3_details(self, mock_bucket_exists):
        self.template._body = '{"template": "mock"}'
        mock_bucket_exists.return_value = True
        self.template.s3_details = {
            "bucket_name": "bucket-name",
            "bucket_key": "bucket-key"
        }

        self.template.upload_to_s3()

        get_bucket_location_call, put_object_call = self.template.connection_manager.call.call_args_list
        get_bucket_location_call.assert_called_once_with(
            service="s3",
            command="get_bucket_location",
            kwargs={
                "Bucket": "bucket-name"
            }
        )
        put_object_call.assert_called_once_with(
            service="s3",
            command="put_object",
            kwargs={
                "Bucket": "bucket-name",
                "Key": "bucket-key",
                "Body": '{"template": "mock"}',
                "ServerSideEncryption": "AES256"
            }
        )

    def test_domain_from_region(self):
        assert self.template._domain_from_region("us-east-1") == "com"
        assert self.template._domain_from_region("cn-north-1") == "com.cn"
        assert self.template._domain_from_region("cn-northwest-1") == "com.cn"

    def test_bucket_exists_with_bucket_that_exists(self):
        # connection_manager.call doesn't raise an exception, mimicing the
        # behaviour when head_bucket successfully executes.
        self.template.s3_details = {
            "bucket_name": "bucket-name",
            "bucket_key": "bucket-key"
        }

        assert self.template._bucket_exists() is True

    def test_create_bucket_with_unreadable_bucket(self):
        self.template.connection_manager.region = "eu-west-1"
        self.template.s3_details = {
            "bucket_name": "bucket-name",
            "bucket_key": "bucket-key"
        }

        self.template.connection_manager.call.side_effect = ClientError(
            {
                "Error": {
                    "Code": 500,
                    "Message": "Bucket Unreadable"
                }
            },
            sentinel.operation
        )
        with pytest.raises(ClientError) as e:
            self.template._create_bucket()
            assert e.value.response["Error"]["Code"] == 500
            assert e.value.response["Error"]["Message"] == "Bucket Unreadable"

    def test_bucket_exists_with_non_existent_bucket(self):
        # connection_manager.call is called twice, and should throw the
        # Not Found ClientError only for the first call.
        self.template.s3_details = {
            "bucket_name": "bucket-name",
            "bucket_key": "bucket-key"
        }

        self.template.connection_manager.call.side_effect = [
            ClientError(
                {
                    "Error": {
                        "Code": 404,
                        "Message": "Not Found"
                    }
                },
                sentinel.operation
            ),
            None
        ]

        existance = self.template._bucket_exists()

        assert existance is False

    def test_create_bucket_in_us_east_1(self):
        # connection_manager.call is called twice, and should throw the
        # Not Found ClientError only for the first call.
        self.template.connection_manager.region = "us-east-1"
        self.template.s3_details = {
            "bucket_name": "bucket-name",
            "bucket_key": "bucket-key"
        }

        self.template._create_bucket()

        self.template.connection_manager.call.assert_any_call(
            service="s3",
            command="create_bucket",
            kwargs={"Bucket": "bucket-name"}
        )

    @patch("sceptre.template.Template.upload_to_s3")
    def test_get_boto_call_parameter_with_s3_details(self, mock_upload_to_s3):
        # self.stack._template = Mock(spec=Template)
        mock_upload_to_s3.return_value = sentinel.template_url
        self.template.s3_details = {
            "bucket_name": sentinel.bucket_name,
            "bucket_key": sentinel.bucket_key
        }

        boto_parameter = self.template.get_boto_call_parameter()

        assert boto_parameter == {"TemplateURL": sentinel.template_url}

    def test_get_template_details_without_upload(self):
        self.template.s3_details = None
        self.template._body = sentinel.body
        boto_parameter = self.template.get_boto_call_parameter()

        assert boto_parameter == {"TemplateBody": sentinel.body}

    def test_body_with_json_template(self):
        self.template.name = "vpc"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc.json"
        )
        output = self.template.body
        output_dict = json.loads(output)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output_dict = json.loads(f.read())
        assert output_dict == expected_output_dict

    def test_body_with_yaml_template(self):
        self.template.name = "vpc"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc.yaml"
        )
        output = self.template.body
        output_dict = yaml.safe_load(output)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output_dict = json.loads(f.read())
        assert output_dict == expected_output_dict

    def test_body_with_generic_template(self):
        self.template.name = "vpc"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc.template"
        )
        output = self.template.body
        output_dict = json.loads(output)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output_dict = json.loads(f.read())
        assert output_dict == expected_output_dict

    def test_body_with_chdir_template(self):
        self.template.sceptre_user_data = None
        self.template.name = "chdir"
        current_dir = os.getcwd()
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/chdir.py"
        )
        try:
            json.loads(self.template.body)
        except ValueError:
            assert False
        finally:
            os.chdir(current_dir)

    def test_body_with_missing_file(self):
        self.template.path = "incorrect/template/path.py"
        with pytest.raises(IOError):
            self.template.body

    def test_body_with_python_template(self):
        self.template.sceptre_user_data = None
        self.template.name = "vpc"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc.py"
        )
        actual_output = json.loads(self.template.body)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output = json.loads(f.read())
        assert actual_output == expected_output

    def test_body_with_python_template_with_sgt(self):
        self.template.sceptre_user_data = None
        self.template.name = "vpc_sgt"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc_sgt.py"
        )
        actual_output = json.loads(self.template.body)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output = json.loads(f.read())
        assert actual_output == expected_output

    def test_body_injects_sceptre_user_data(self):
        self.template.sceptre_user_data = {
            "cidr_block": "10.0.0.0/16"
        }
        self.template.name = "vpc_sud"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc_sud.py"
        )

        actual_output = json.loads(self.template.body)
        with open("tests/fixtures/templates/compiled_vpc_sud.json", "r") as f:
            expected_output = json.loads(f.read())
        assert actual_output == expected_output

    def test_body_injects_sceptre_user_data_incorrect_function(self):
        self.template.sceptre_user_data = {
            "cidr_block": "10.0.0.0/16"
        }
        self.template.name = "vpc_sud_incorrect_function"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc_sud_incorrect_function.py"
        )
        with pytest.raises(TemplateSceptreHandlerError):
            self.template.body

    def test_body_injects_sceptre_user_data_incorrect_handler(self):
        self.template.sceptre_user_data = {
            "cidr_block": "10.0.0.0/16"
        }
        self.template.name = "vpc_sud_incorrect_handler"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc_sud_incorrect_handler.py"
        )
        with pytest.raises(TypeError):
            self.template.body

    def test_body_with_incorrect_filetype(self):
        self.template.path = (
            "path/to/something.ext"
        )
        with pytest.raises(UnsupportedTemplateFileTypeError):
            self.template.body
Example #16
0
class TestTemplate(object):
    def setup_method(self, test_method):
        self.region = "region"
        self.bucket_name = "bucket_name"
        self.environment_path = "environment_path"
        self.stack_name = "stack_name"

        self.connection_manager = Mock(spec=ConnectionManager)
        self.connection_manager.create_bucket_lock = threading.Lock()

        self.template = Template(path="/folder/template.py",
                                 sceptre_user_data={})

    def test_initialise_template(self):
        assert self.template.path == "/folder/template.py"
        assert self.template.name == "template"
        assert self.template.sceptre_user_data == {}
        assert self.template._body is None

    def test_repr(self):
        representation = self.template.__repr__()
        assert representation == "sceptre.template.Template(" \
            "name='template', path='/folder/template.py'"\
            ", sceptre_user_data={})"

    def test_body_with_cache(self):
        self.template._body = sentinel.body
        body = self.template.body
        assert body == sentinel.body

    @freeze_time("2012-01-01")
    @patch("sceptre.template.Template._bucket_exists")
    def test_upload_to_s3_with_valid_arguments(self, mock_bucket_exists):
        self.template._body = '{"template": "mock"}'
        mock_bucket_exists.return_value = True

        url = self.template.upload_to_s3(
            region="eu-west-1",
            bucket_name="bucket-name",
            key_prefix="/prefix/",
            environment_path="environment/path",
            stack_name="stack-name",
            connection_manager=self.connection_manager)

        expected_template_key = ("prefix/eu-west-1/environment/path/"
                                 "stack-name-2012-01-01-00-00-00-000000Z.json")

        self.connection_manager.call.assert_called_once_with(
            service="s3",
            command="put_object",
            kwargs={
                "Bucket": "bucket-name",
                "Key": expected_template_key,
                "Body": '{"template": "mock"}',
                "ServerSideEncryption": "AES256"
            })

        assert url == "https://bucket-name.s3.amazonaws.com/{0}".format(
            expected_template_key)

    def test_bucket_exists_with_bucket_that_exists(self):
        # connection_manager.call doesn't raise an exception, mimicing the
        # behaviour when head_bucket successfully executes.
        self.template._bucket_exists("bucket_name", self.connection_manager)

    def test_create_bucket_with_unreadable_bucket(self):
        self.connection_manager.call.side_effect = ClientError(
            {"Error": {
                "Code": 500,
                "Message": "Bucket Unreadable"
            }}, sentinel.operation)
        with pytest.raises(ClientError) as e:
            self.template._create_bucket("region", "bucket_name",
                                         self.connection_manager)
            assert e.value.response["Error"]["Code"] == 500
            assert e.value.response["Error"]["Message"] == "Bucket Unreadable"

    def test_bucket_exists_with_non_existent_bucket(self):
        # connection_manager.call is called twice, and should throw the
        # Not Found ClientError only for the first call.
        self.connection_manager.call.side_effect = [
            ClientError({"Error": {
                "Code": 404,
                "Message": "Not Found"
            }}, sentinel.operation), None
        ]

        existance = self.template._bucket_exists(self.bucket_name,
                                                 self.connection_manager)

        assert existance is False

    def test_create_bucket_in_us_east_1(self):
        # connection_manager.call is called twice, and should throw the
        # Not Found ClientError only for the first call.

        self.template._create_bucket("us-east-1", self.bucket_name,
                                     self.connection_manager)

        self.connection_manager.call.assert_any_call(
            service="s3",
            command="create_bucket",
            kwargs={"Bucket": self.bucket_name})

    def test_body_with_json_template(self):
        self.template.name = "vpc"
        self.template.path = os.path.join(os.getcwd(),
                                          "tests/fixtures/templates/vpc.json")
        output = self.template.body
        output_dict = json.loads(output)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output_dict = json.loads(f.read())
        assert output_dict == expected_output_dict

    def test_body_with_yaml_template(self):
        self.template.name = "vpc"
        self.template.path = os.path.join(os.getcwd(),
                                          "tests/fixtures/templates/vpc.yaml")
        output = self.template.body
        output_dict = yaml.load(output)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output_dict = json.loads(f.read())
        assert output_dict == expected_output_dict

    def test_body_with_missing_file(self):
        self.template.path = "incorrect/template/path.py"
        with pytest.raises(IOError):
            self.template.body

    def test_body_with_python_template(self):
        self.template.sceptre_user_data = None
        self.template.name = "vpc"
        self.template.path = os.path.join(os.getcwd(),
                                          "tests/fixtures/templates/vpc.py")
        actual_output = json.loads(self.template.body)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output = json.loads(f.read())
        assert actual_output == expected_output

    def test_body_with_python_template_with_sgt(self):
        self.template.sceptre_user_data = None
        self.template.name = "vpc_sgt"
        self.template.path = os.path.join(
            os.getcwd(), "tests/fixtures/templates/vpc_sgt.py")
        actual_output = json.loads(self.template.body)
        with open("tests/fixtures/templates/compiled_vpc.json", "r") as f:
            expected_output = json.loads(f.read())
        assert actual_output == expected_output

    def test_body_injects_sceptre_user_data(self):
        self.template.sceptre_user_data = {"cidr_block": "10.0.0.0/16"}
        self.template.name = "vpc_sud"
        self.template.path = os.path.join(
            os.getcwd(), "tests/fixtures/templates/vpc_sud.py")

        actual_output = json.loads(self.template.body)
        with open("tests/fixtures/templates/compiled_vpc_sud.json", "r") as f:
            expected_output = json.loads(f.read())
        assert actual_output == expected_output

    def test_body_injects_sceptre_user_data_incorrect_function(self):
        self.template.sceptre_user_data = {"cidr_block": "10.0.0.0/16"}
        self.template.name = "vpc_sud_incorrect_function"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc_sud_incorrect_function.py")
        with pytest.raises(TemplateSceptreHandlerError):
            self.template.body

    def test_body_injects_sceptre_user_data_incorrect_handler(self):
        self.template.sceptre_user_data = {"cidr_block": "10.0.0.0/16"}
        self.template.name = "vpc_sud_incorrect_handler"
        self.template.path = os.path.join(
            os.getcwd(),
            "tests/fixtures/templates/vpc_sud_incorrect_handler.py")
        with pytest.raises(TypeError):
            self.template.body

    def test_body_with_incorrect_filetype(self):
        self.template.path = ("path/to/something.ext")
        with pytest.raises(UnsupportedTemplateFileTypeError):
            self.template.body