示例#1
0
文件: default.py 项目: ttaub/stacker
    def get_stack_info(self, stack_name):
        """ Get the template and parameters of the stack currently in AWS

        Returns [ template, parameters ]
        """
        try:
            stacks = retry_on_throttling(
                self.cloudformation.describe_stacks,
                kwargs=dict(StackName=stack_name))
        except botocore.exceptions.ClientError as e:
            if "does not exist" not in e.message:
                raise
            raise exceptions.StackDoesNotExist(stack_name)

        try:
            template = retry_on_throttling(
                self.cloudformation.get_template,
                kwargs=dict(StackName=stack_name))['TemplateBody']
        except botocore.exceptions.ClientError as e:
            if "does not exist" not in e.message:
                raise
            raise exceptions.StackDoesNotExist(stack_name)

        stack = stacks['Stacks'][0]
        parameters = dict()
        if 'Parameters' in stack:
            for p in stack['Parameters']:
                parameters[p['ParameterKey']] = p['ParameterValue']

        return [json.dumps(template), parameters]
示例#2
0
 def get_stack(self, stack_name, **kwargs):
     try:
         return self.cloudformation.describe_stacks(
             StackName=stack_name)['Stacks'][0]
     except botocore.exceptions.ClientError as e:
         if "does not exist" not in e.message:
             raise
         raise exceptions.StackDoesNotExist(stack_name)
示例#3
0
    def get_stack_info(self, stack):
        """ Get the template and parameters of the stack currently in AWS

        Returns [ template, parameters ]
        """
        stack_name = stack['StackId']

        try:
            template = self.cloudformation.get_template(
                StackName=stack_name)['TemplateBody']
        except botocore.exceptions.ClientError as e:
            if "does not exist" not in e.message:
                raise
            raise exceptions.StackDoesNotExist(stack_name)

        parameters = self.params_as_dict(stack.get('Parameters', []))

        return [json.dumps(template), parameters]
示例#4
0
    def get_stack_changes(self, stack, template, parameters,
                          tags, **kwargs):
        """Get the changes from a ChangeSet.

        Args:
            stack (:class:`stacker.stack.Stack`): the stack to get changes
            template (:class:`stacker.providers.base.Template`): A Template
                object to compaired to.
            parameters (list): A list of dictionaries that defines the
                parameter list to be applied to the Cloudformation stack.
            tags (list): A list of dictionaries that defines the tags
                that should be applied to the Cloudformation stack.

        Returns:
            dict: Stack outputs with inferred changes.

        """
        try:
            stack_details = self.get_stack(stack.fqn)
            # handling for orphaned changeset temp stacks
            if self.get_stack_status(
                    stack_details) == self.REVIEW_STATUS:
                raise exceptions.StackDoesNotExist(stack.fqn)
            _old_template, old_params = self.get_stack_info(
                stack_details
            )
            old_template = parse_cloudformation_template(_old_template)
            change_type = 'UPDATE'
        except exceptions.StackDoesNotExist:
            old_params = {}
            old_template = {}
            change_type = 'CREATE'

        changes, change_set_id = create_change_set(
            self.cloudformation, stack.fqn, template, parameters, tags,
            change_type, service_role=self.service_role, **kwargs
        )
        new_parameters_as_dict = self.params_as_dict(
            [x
             if 'ParameterValue' in x
             else {'ParameterKey': x['ParameterKey'],
                   'ParameterValue': old_params[x['ParameterKey']]}
             for x in parameters]
        )
        params_diff = diff_parameters(old_params, new_parameters_as_dict)

        if changes or params_diff:
            ui.lock()
            try:
                if self.interactive:
                    output_summary(stack.fqn, 'changes', changes,
                                   params_diff,
                                   replacements_only=self.replacements_only)
                    output_full_changeset(full_changeset=changes,
                                          params_diff=params_diff,
                                          fqn=stack.fqn)
                else:
                    output_full_changeset(full_changeset=changes,
                                          params_diff=params_diff,
                                          answer='y', fqn=stack.fqn)
            finally:
                ui.unlock()

        self.cloudformation.delete_change_set(
            ChangeSetName=change_set_id
        )

        # ensure current stack outputs are loaded
        self.get_outputs(stack.fqn)

        # infer which outputs may have changed
        refs_to_invalidate = []
        for change in changes:
            resc_change = change.get('ResourceChange', {})
            if resc_change.get('Type') == 'Add':
                continue  # we don't care about anything new
            # scope of changes that can invalidate a change
            if resc_change and (resc_change.get('Replacement') == 'True' or
                                'Properties' in resc_change['Scope']):
                logger.debug('%s added to invalidation list for %s',
                             resc_change['LogicalResourceId'], stack.fqn)
                refs_to_invalidate.append(resc_change['LogicalResourceId'])

        # invalidate cached outputs with inferred changes
        for output, props in old_template.get('Outputs', {}).items():
            if any(r in str(props['Value']) for r in refs_to_invalidate):
                self._outputs[stack.fqn].pop(output)
                logger.debug('Removed %s from the outputs of %s',
                             output, stack.fqn)

        # push values for new + invalidated outputs to outputs
        for output_name, output_params in \
                stack.blueprint.get_output_definitions().items():
            if output_name not in self._outputs[stack.fqn]:
                self._outputs[stack.fqn][output_name] = (
                    '<inferred-change: {}.{}={}>'.format(
                        stack.fqn, output_name,
                        str(output_params['Value'])
                    )
                )

        # when creating a changeset for a new stack, CFN creates a temporary
        # stack with a status of REVIEW_IN_PROGRESS. this is only removed if
        # the changeset is executed or it is manually deleted.
        if change_type == 'CREATE':
            try:
                temp_stack = self.get_stack(stack.fqn)
                if self.is_stack_in_review(temp_stack):
                    logger.debug('Removing temporary stack that is created '
                                 'with a ChangeSet of type "CREATE"')
                    self.destroy_stack(temp_stack)
            except exceptions.StackDoesNotExist:
                # not an issue if the stack was already cleaned up
                logger.debug('Stack does not exist: %s', stack.fqn)

        return self.get_outputs(stack.fqn)