Beispiel #1
0
    def __init__(
        self,
        environment=None,
        boto3_credentials=None,
        stack_names=None,
        config=None,
        config_path=None,
        region="us-east-1",
        force_stacks=None,
    ):
        """Instantiate class."""
        if not boto3_credentials:
            boto3_credentials = {}

        self._boto3_test_client = MutableMap()
        self._boto3_test_stubber = MutableMap()

        # used during init process
        self.__boto3_credentials = boto3_credentials
        self.s3_stubber = self.add_stubber("s3", region=region)

        super(MockCFNginContext, self).__init__(
            environment=environment,
            boto3_credentials=boto3_credentials,
            stack_names=stack_names,
            config=config,
            config_path=config_path,
            region=region,
            force_stacks=force_stacks,
        )
Beispiel #2
0
    def test_update_termination_protection(self):
        """Test update_termination_protection."""
        stack_name = 'fake-stack'
        test_cases = [
            MutableMap(aws=False, defined=True, expected=True),
            MutableMap(aws=True, defined=False, expected=False),
            MutableMap(aws=True, defined=True, expected=None),
            MutableMap(aws=False, defined=False, expected=None)
        ]

        for test in test_cases:
            self.stubber.add_response(
                'describe_stacks', {
                    'Stacks': [
                        generate_describe_stacks_stack(
                            stack_name, termination_protection=test.aws)
                    ]
                }, {'StackName': stack_name})
            if isinstance(test.expected, bool):
                self.stubber.add_response(
                    'update_termination_protection', {'StackId': stack_name}, {
                        'EnableTerminationProtection': test.expected,
                        'StackName': stack_name
                    })
            with self.stubber:
                self.provider.update_termination_protection(
                    stack_name, test.defined)
            self.stubber.assert_no_pending_responses()
Beispiel #3
0
    def handle(cls, value, context=None, provider=None, **kwargs):
        """Return the data from ``hook_data``.

        Args:
            value (str): Parameter(s) given to this lookup.
            context (:class:`runway.cfngin.context.Context`): Context instance.
            provider (:class:`runway.cfngin.providers.base.BaseProvider`):
                Provider instance.

        """
        try:
            query, args = cls.parse(value)
        except ValueError:
            query, args = cls.legacy_parse(value)

        hook_data = MutableMap(**context.hook_data)

        # TODO use context.hook_data directly in next major release
        result = hook_data.find(query, args.get('default'))

        if isinstance(result, BaseAWSObject) and \
                args.get('get') and \
                not args.get('load'):
            args['load'] = 'troposphere'

        if not result:
            raise ValueError('Could not find a value for "%s"' % value)

        if result == args.get('default'):
            # assume default value has already been processed so no need to
            # use these
            args.pop('load', None)
            args.pop('get', None)

        return cls.format_results(result, **args)
Beispiel #4
0
    def test_update_termination_protection(self):
        """Test update_termination_protection."""
        stack_name = "fake-stack"
        test_cases = [
            MutableMap(aws=False, defined=True, expected=True),
            MutableMap(aws=True, defined=False, expected=False),
            MutableMap(aws=True, defined=True, expected=None),
            MutableMap(aws=False, defined=False, expected=None),
        ]

        for test in test_cases:
            self.stubber.add_response(
                "describe_stacks",
                {
                    "Stacks": [
                        generate_describe_stacks_stack(
                            stack_name, termination_protection=test.aws)
                    ]
                },
                {"StackName": stack_name},
            )
            if isinstance(test.expected, bool):
                self.stubber.add_response(
                    "update_termination_protection",
                    {"StackId": stack_name},
                    {
                        "EnableTerminationProtection": test.expected,
                        "StackName": stack_name,
                    },
                )
            with self.stubber:
                self.provider.update_termination_protection(
                    stack_name, test.defined)
            self.stubber.assert_no_pending_responses()
Beispiel #5
0
    def test_pre_process_resolve(self, yaml_fixtures):
        """Test that pre-process resolution only resolves specific vars."""
        raw_config = deepcopy(
            yaml_fixtures["config.runway.yml"]["deployments"])
        raw_vars = deepcopy(yaml_fixtures["config.runway.variables.yml"])
        deployment = DeploymentDefinition.from_list(raw_config)[0]
        raw_context = {"env_vars": os.environ.copy()}
        raw_context["env_vars"].update(ENV_VARS)
        deployment.resolve(
            MutableMap(**raw_context),
            variables=MutableMap(**raw_vars),
            pre_process=True,
        )

        # check resolved variables for pre_process
        assert deployment.account_id != 123456789101
        assert deployment.assume_role["arn"] == "arn:aws:iam::role/some-role"
        assert deployment.env_vars == {"MY_USERNAME": "******"}
        assert deployment.regions == ["us-east-1"]

        assert not deployment.parallel_regions, "not set in test config, should be None"

        # these should be unresolved at this point
        for attr in ["environments", "module_options"]:
            with pytest.raises(UnresolvedVariable):
                getattr(deployment, attr)
Beispiel #6
0
    def __init__(self, ctx, parameters=None, sys_path=None):
        """Instantiate class.

        Args:
            ctx (runway.context.Context): Runway context object.
            parameters (Optional[Dict[str. Any]]): Parameters from Runway.
            sys_path (Optional[str]): Working directory.

        """
        self.__ctx = ctx
        self._env_file_name = None
        self.concurrency = ctx.max_concurrent_cfngin_stacks
        self.interactive = ctx.is_interactive
        self.parameters = MutableMap()
        self.recreate_failed = ctx.is_noninteractive
        self.region = ctx.env_region
        self.sys_path = sys_path or os.getcwd()
        self.tail = ctx.debug

        self.parameters.update(self.env_file)

        if parameters:
            LOGGER.debug('Adding Runway parameters to CFNgin parameters')
            self.parameters.update(parameters)

        self._inject_common_parameters()
Beispiel #7
0
    def test_find_default(self):
        """Validate default value functionality."""
        mute_map = MutableMap(**VALUE)

        assert mute_map.find('NOT_VALID', 'default_val') == \
            'default_val', 'default should be used'
        assert mute_map.find('str_val', 'default_val') == \
            VALUE['str_val'], 'default should be ignored'
Beispiel #8
0
    def test_find_default(self):
        """Validate default value functionality."""
        mute_map = MutableMap(**VALUE)

        assert (mute_map.find(
            "NOT_VALID",
            "default_val") == "default_val"), "default should be used"
        assert (mute_map.find(
            "str_val",
            "default_val") == VALUE["str_val"]), "default should be ignored"
Beispiel #9
0
 def __init__(self, command=None, deploy_environment=None):
     """Instantiate class."""
     if not deploy_environment:
         deploy_environment = DeployEnvironment(environ={},
                                                explicit_name="test")
     super(MockRunwayContext,
           self).__init__(command=command,
                          deploy_environment=deploy_environment)
     self._boto3_test_client = MutableMap()
     self._boto3_test_stubber = MutableMap()
     self._use_concurrent = True
Beispiel #10
0
 def __init__(self,
              env_name,
              env_region,
              env_root,
              env_vars=None,
              command=None):
     """Instantiate class."""
     super(MockRunwayContext, self).__init__(env_name=env_name or 'test',
                                             env_region=env_region or 'us-east-1',
                                             env_root=env_root,
                                             env_vars=env_vars,
                                             command=command)
     self._boto3_test_client = MutableMap()
     self._boto3_test_stubber = MutableMap()
Beispiel #11
0
    def test_delete(self):
        """Validate that keys can be deleted.

        Uses dot and bracket notation.

        Also tests `get` method.

        """
        mute_map = MutableMap(**VALUE)
        del mute_map.str_val
        del mute_map['dict_val']

        assert not mute_map.get('str_val')
        assert not mute_map.get('dict_val')
Beispiel #12
0
    def test_resolve(self, yaml_fixtures):
        """Test full resolution of variable attributes."""
        raw_config = deepcopy(yaml_fixtures["config.runway.yml"]["tests"])
        raw_vars = deepcopy(yaml_fixtures["config.runway.variables.yml"])
        test = ConfigTestDefinition.from_list(raw_config)[0]
        raw_context = {"env_vars": os.environ.copy()}
        raw_context["env_vars"].update(ENV_VARS)
        test.resolve(MutableMap(**raw_context),
                     variables=MutableMap(**raw_vars))

        assert test.args == {"commands": ['echo "My name is test"']}
        assert test.name == "hello_world"
        assert isinstance(test.required, bool)
        assert test.required
        assert test.type == "script"
Beispiel #13
0
    def __init__(self, context, provider, **kwargs):
        """Instantiate class.

        Args:
            context (:class:`runway.cfngin.context.Context`): Context instance.
                (passed in by CFNgin)
            provider (:class:`runway.cfngin.providers.base.BaseProvider`):
                Provider instance. (passed in by CFNgin)

        """
        kwargs.setdefault('ttl', 300)
        super(Certificate, self).__init__(context, provider, **kwargs)

        self.template_description = self.get_template_description()
        self.stack_name = self.args.get('stack_name',
                                        kwargs['domain'].replace('.', '-'))

        self.properties = MutableMap(
            **{
                'DomainName': self.args.domain,
                'SubjectAlternativeNames': self.args.get('alt_names', []),
                'Tags': self.tags,
                'ValidationMethod': 'DNS'
            })
        self.blueprint = self._create_blueprint()

        session = self.context.get_session()
        self.acm_client = session.client('acm')
        self.r53_client = session.client('route53')
        self.stack = self.generate_stack(variables={
            'ValidateRecordTTL': self.args.ttl,
            'DomainName': self.args.domain
        })
Beispiel #14
0
    def __init__(self, context, provider, **kwargs):
        """Instantiate class.

        Args:
            context (:class:`runway.cfngin.context.Context`): Context instance.
                (passed in by CFNgin)
            provider (:class:`runway.cfngin.providers.base.BaseProvider`):
                Provider instance. (passed in by CFNgin)

        """
        kwargs.setdefault("ttl", 300)
        super(Certificate, self).__init__(context, provider, **kwargs)

        self.template_description = self.get_template_description()
        self.stack_name = self.args.get("stack_name",
                                        kwargs["domain"].replace(".", "-"))

        self.properties = MutableMap(
            **{
                "DomainName": self.args.domain,
                "SubjectAlternativeNames": self.args.get("alt_names", []),
                "Tags": self.tags,
                "ValidationMethod": "DNS",
            })
        self.blueprint = self._create_blueprint()

        session = self.context.get_session()
        self.acm_client = session.client("acm")
        self.r53_client = session.client("route53")
        self.stack = self.generate_stack(variables={
            "ValidateRecordTTL": self.args.ttl,
            "DomainName": self.args.domain,
        })
def test_validate_environment(env_def, strict, expected, expected_logs, caplog,
                              runway_context):
    """Test validate_environment."""
    mock_module = MutableMap(name='test_module')
    caplog.set_level(logging.DEBUG, logger='runway')
    assert validate_environment(runway_context, mock_module, env_def, strict) \
        is expected
    # all() does not give an output that can be used for troubleshooting failures
    for log in expected_logs:
        assert log in caplog.messages
Beispiel #16
0
    def test_resolve(self, yaml_fixtures):
        """Test full resolution of variable attributes."""
        raw_config = deepcopy(
            yaml_fixtures['config.runway.yml']['deployments'][0]['modules']
        )
        raw_vars = deepcopy(yaml_fixtures['config.runway.variables.yml'])
        module = ModuleDefinition.from_list(raw_config)[0]
        raw_context = {'env_vars': os.environ.copy()}
        raw_context['env_vars'].update(ENV_VARS)
        module.resolve(MutableMap(**raw_context),
                       variables=MutableMap(**raw_vars))

        assert module.child_modules == []
        assert not module.class_path
        assert module.env_vars == {'MY_USERNAME_MODULE': 'test'}
        assert module.environments == {'module_test_param': 'test'}
        assert module.name == '${var test_path}app.cfn'
        assert module.options == {'sample_module_option': 'test.module.options'}
        assert module.path == 'sampleapp.cfn'
        assert module.tags == {}
Beispiel #17
0
    def test_resolve(self, yaml_fixtures):
        """Test full resolution of variable attributes."""
        raw_config = deepcopy(
            yaml_fixtures["config.runway.yml"]["deployments"][0]["modules"])
        raw_vars = deepcopy(yaml_fixtures["config.runway.variables.yml"])
        module = ModuleDefinition.from_list(raw_config)[0]
        raw_context = {"env_vars": os.environ.copy()}
        raw_context["env_vars"].update(ENV_VARS)
        module.resolve(MutableMap(**raw_context),
                       variables=MutableMap(**raw_vars))

        assert module.child_modules == []
        assert not module.class_path
        assert module.env_vars == {"MY_USERNAME_MODULE": "test"}
        assert module.environments == {"module_test_param": "test"}
        assert module.name == "${var test_path}app.cfn"
        assert module.options == {
            "sample_module_option": "test.module.options"
        }
        assert module.path == "sampleapp.cfn"
        assert module.tags == {}
Beispiel #18
0
    def test_bool(self):
        """Validates the bool value.

        Also tests setting an attr using bracket notation.

        """
        mute_map = MutableMap()

        assert not mute_map

        mute_map['str_val'] = 'test'

        assert mute_map
Beispiel #19
0
    def test_find(self):
        """Validate the `find` method with and without `ignore_cache`.

        Also tests the `clear_found_cache` method and setting an attr value
        using dot notation.

        """
        mute_map = MutableMap(**VALUE)

        assert mute_map.find('str_val') == VALUE['str_val']

        mute_map.str_val = 'new_val'

        assert mute_map.find('str_val') == VALUE['str_val']
        assert mute_map.find('str_val', ignore_cache=True) == 'new_val'

        mute_map.clear_found_cache()

        assert mute_map.find('str_val') == 'new_val'
Beispiel #20
0
    def test_resolve(self, yaml_fixtures):
        """Test full resolution of variable attributes."""
        raw_config = deepcopy(yaml_fixtures['config.runway.yml']['deployments'])
        raw_vars = deepcopy(yaml_fixtures['config.runway.variables.yml'])
        deployment = DeploymentDefinition.from_list(raw_config)[0]
        raw_context = {'env_vars': os.environ.copy()}
        raw_context['env_vars'].update(ENV_VARS)
        deployment.resolve(MutableMap(**raw_context),
                           variables=MutableMap(**raw_vars))

        assert deployment.regions == ['us-east-1']
        assert not deployment.account_id == 123456789101
        assert deployment.assume_role['arn'] == 'arn:aws:iam::role/some-role'
        assert deployment.env_vars == {'MY_USERNAME': '******'}
        assert deployment.environments == {
            'test_param': 'lab value for ${envvar AWS_REGION}'
        }
        assert deployment.module_options == {
            'deployment_option': 'test.deployment.module_options'
        }
        assert deployment.regions == ['us-east-1']

        assert not deployment.parallel_regions, 'not set in test config, should be None'
Beispiel #21
0
    def test_pre_process_resolve(self, yaml_fixtures):
        """Test that pre-process resolution only resolves specific vars."""
        raw_config = deepcopy(yaml_fixtures['config.runway.yml']['deployments'])
        raw_vars = deepcopy(yaml_fixtures['config.runway.variables.yml'])
        deployment = DeploymentDefinition.from_list(raw_config)[0]
        raw_context = {'env_vars': os.environ.copy()}
        raw_context['env_vars'].update(ENV_VARS)
        deployment.resolve(MutableMap(**raw_context),
                           variables=MutableMap(**raw_vars),
                           pre_process=True)

        # check resolved variables for pre_process
        assert not deployment.account_id == 123456789101
        assert deployment.assume_role['arn'] == 'arn:aws:iam::role/some-role'
        assert deployment.env_vars == {'MY_USERNAME': '******'}
        assert deployment.regions == ['us-east-1']

        assert not deployment.parallel_regions, 'not set in test config, should be None'

        # these should be unresolved at this point
        for attr in ['environments', 'module_options']:
            with pytest.raises(UnresolvedVariable):
                getattr(deployment, attr)
Beispiel #22
0
    def __init__(self,
                 environment=None,
                 boto3_credentials=None,
                 stack_names=None,
                 config=None,
                 config_path=None,
                 region=None,
                 force_stacks=None):
        """Instantiate class.

        Args:
            boto3_credentials (Optional[Dict[str, str]]): Credentials to use
                when creating a boto3 session from context.
            environment (dict): A dictionary used to pass in information about
                the environment. Useful for templating.
            stack_names (list): A list of stack_names to operate on. If not
                passed, usually all stacks defined in the config will be
                operated on.
            config (:class:`runway.cfngin.config.Config`): The CFNgin
                configuration being operated on.
            config_path (str): Path to the config file that was provided.
            region (str): Name of an AWS region if provided as a CLI argument.
            force_stacks (list): A list of stacks to force work on. Used to
                work on locked stacks.

        """
        self.__boto3_credentials = boto3_credentials
        self._bucket_name = None
        self._persistent_graph = None
        self._persistent_graph_lock_code = None
        self._persistent_graph_lock_tag = 'cfngin_lock_code'
        self._s3_bucket_verified = None
        self._stacks = None
        self._targets = None
        self._upload_to_s3 = None
        # TODO load the config from context instead of taking it as an arg
        self.config = config or Config()
        # TODO set this value when provisioning a Config object in context
        # set to a fake location for the time being but this should be set
        # by all runtime entry points. the only time the fake value should be
        # used is during tests.
        self.config_path = config_path or './'
        self.bucket_region = self.config.cfngin_bucket_region or region
        self.environment = environment
        self.force_stacks = force_stacks or []
        self.hook_data = MutableMap()
        self.region = region
        self.s3_conn = self.get_session(region=self.bucket_region).client('s3')
        self.stack_names = stack_names or []
Beispiel #23
0
    def __init__(self, **kwargs):
        """Instantiate class."""
        super(MockRunwayConfig, self).__init__()
        self._kwargs = kwargs
        self.deployments = []
        self.future = MagicMock()
        self.tests = []
        self.ignore_git_branch = False
        self.variables = MutableMap()

        # classmethods
        self.find_config_file = MagicMock(name='find_config_file',
                                          return_value='./runway.yml')
        self.load_from_file = MagicMock(name='load_from_file',
                                        return_value=self)
Beispiel #24
0
    def test_resolve(self, yaml_fixtures):
        """Test full resolution of variable attributes."""
        raw_config = deepcopy(
            yaml_fixtures["config.runway.yml"]["deployments"])
        raw_vars = deepcopy(yaml_fixtures["config.runway.variables.yml"])
        deployment = DeploymentDefinition.from_list(raw_config)[0]
        raw_context = {"env_vars": os.environ.copy()}
        raw_context["env_vars"].update(ENV_VARS)
        deployment.resolve(MutableMap(**raw_context),
                           variables=MutableMap(**raw_vars))

        assert deployment.regions == ["us-east-1"]
        assert deployment.account_id != 123456789101
        assert deployment.assume_role["arn"] == "arn:aws:iam::role/some-role"
        assert deployment.env_vars == {"MY_USERNAME": "******"}
        assert deployment.environments == {
            "test_param": "lab value for ${envvar AWS_REGION}"
        }
        assert deployment.module_options == {
            "deployment_option": "test.deployment.module_options"
        }
        assert deployment.regions == ["us-east-1"]

        assert not deployment.parallel_regions, "not set in test config, should be None"
Beispiel #25
0
    def __init__(self, **kwargs):
        """Instantiate class."""
        super(MockRunwayConfig, self).__init__()
        self._kwargs = kwargs
        self.deployments = []
        self.future = MagicMock()
        self.tests = []
        self.ignore_git_branch = False
        self.runway_version = SpecifierSet(">=1.10", prereleases=True)
        self.variables = MutableMap()

        # classmethods
        self.find_config_file = MagicMock(name="find_config_file",
                                          return_value="./runway.yml")
        self.load_from_file = MagicMock(name="load_from_file",
                                        return_value=self)
Beispiel #26
0
    def _load_yaml(cls, value, **_):
        # type: (str, Any) -> MutableMap
        """Load a YAML string into a MutableMap.

        Args:
            value: YAML formatted string.

        Returns:
            MutableMap

        """
        if not isinstance(value, str):
            raise TypeError('value of type "%s" must of type "str" to use '
                            'the "load=yaml" argument.')
        result = yaml.safe_load(value)
        if isinstance(result, dict):
            return MutableMap(**result)
        return result
Beispiel #27
0
    def _load_json(cls, value, **_):
        # type: (str, Any) -> MutableMap
        """Load a JSON string into a MutableMap.

        Args:
            value: JSON formatted string.

        Returns:
            MutableMap

        """
        if not isinstance(value, str):
            raise TypeError('value of type "%s" must of type "str" to use '
                            'the "load=json" argument.')
        result = json.loads(value)
        if isinstance(result, dict):
            return MutableMap(**result)
        return result
Beispiel #28
0
    def env_file(self):
        """Contents of a CFNgin environment file.

        Returns:
            MutableMap

        """
        result = {}
        supported_names = ['{}.env'.format(self.__ctx.env_name),
                           '{}-{}.env'.format(self.__ctx.env_name,
                                              self.region)]
        for _, file_name in enumerate(supported_names):
            file_path = os.path.join(self.sys_path, file_name)
            if os.path.isfile(file_path):
                LOGGER.info('Found environment file: %s', file_path)
                self._env_file_name = file_path
                with open(file_path, 'r') as file_:
                    result.update(parse_environment(file_.read()))
        return MutableMap(**result)
Beispiel #29
0
    def __init__(self, context, provider, **kwargs):
        """Instantiate class.

        Args:
            context (:class:`runway.cfngin.context.Context`): Context instance.
                (passed in by CFNgin)
            provider (:class:`runway.cfngin.providers.base.BaseProvider`):
                Provider instance. (passed in by CFNgin)

        """
        kwargs.setdefault("tags", {})

        self.args = MutableMap(**kwargs)
        self.args.tags.update(context.tags)
        self.blueprint = None
        self.context = context
        self.provider = provider
        self.stack = None
        self.stack_name = "stack"
        self._deploy_action = HookBuildAction(self.context, self.provider)
        self._destroy_action = HookDestroyAction(self.context, self.provider)
Beispiel #30
0
    def _load_troposphere(cls, value, **_):
        # type: (BaseAWSObject, Any) -> MutableMap
        """Load a Troposphere resource into a MutableMap.

        Args:
            Value (troposphere.BaseAWSObject): Troposphere resource to
                contvert to a MutableMap for parsing.

        Returns:
            MutableMap

        """
        if not isinstance(value, BaseAWSObject):
            raise TypeError(
                'value of type "%s" must of type "troposphere.'
                'BaseAWSObject" to use the "load=troposphere" option.')
        if hasattr(value, "properties"):
            return MutableMap(**value.properties)
        raise NotImplementedError(
            '"load=troposphere" only supports BaseAWSObject with a "properties" object.'
        )