Esempio n. 1
0
    def delete(self, retain_resources=[]):
        """
        Delete a Stack, optionally retraining certain resources.
        Waits for Stack to delete and prints Events if deletion fails
        :param list retain_resources: List of LogicalIds to retain
        :raises StackError: if deletion fails
        """
        if not self.stack:
            raise ValidationError(f'Stack {self.config.stack_name} not found')
        if not StackStatus.is_deletable(self.stack):
            raise ValidationError(f'Stack {self.config.stack_name} is not in a deletable status: '
                                  f'{StackStatus.get_status(self.stack).name}')

        info(f'\nDeleting Stack {self.config.stack_name}')
        last_timestamp = self.get_last_timestamp()

        try:
            self.client.delete_stack(StackName=self.config.stack_name, RetainResources=retain_resources)

            self.client.get_waiter('stack_delete_complete').wait(
                StackName=self.config.stack_name,
                WaiterConfig={'Delay': 10, 'MaxAttempts': 360})

            info(f'\nDeletion of Stack {self.config.stack_name} successfully completed')
            self.stack = None
        except ClientError as ce:
            raise StackError(ce)
        except WaiterError as we:
            error(f'\nDeletion of Stack {self.config.stack_name} failed:\n')
            self.print_events(last_timestamp)
            raise StackError(we)
Esempio n. 2
0
    def execute_change_set(self):
        """
        Execute a ChangeSet, waiting for execution to complete and printing the details of Stack Events
        caused by this ChangeSet
        :raises StackError: If there is an error executing the ChangeSet
        """
        last_timestamp = self.get_last_timestamp()

        try:
            info(f'\nExecuting ChangeSet {self.change_set_name} for {self.config.stack_name}')

            self.client.execute_change_set(ChangeSetName=self.change_set_name, StackName=self.config.stack_name)

            waiter_name = 'stack_create_complete' if StackStatus.is_creatable(self.stack) else 'stack_update_complete'
            self.client.get_waiter(waiter_name).wait(StackName=self.config.stack_name,
                                                     WaiterConfig={'Delay': 10, 'MaxAttempts': 360})

            info(f'\nChangeSet {self.change_set_name} for {self.config.stack_name} successfully completed:\n')
            self.print_events(last_timestamp)

        except ClientError as ce:
            raise StackError(ce)
        except WaiterError as we:
            error(f'\nChangeSet {self.change_set_name} for {self.config.stack_name} failed:\n')
            self.print_events(last_timestamp)
            raise StackError(we)
Esempio n. 3
0
    def deploy(self):
        """
        Create a new Stack or update an existing Stack using a ChangeSet.
        This will wait for ChangeSet to be created and print the details,
        and if Auto-Approve is set, will then execute the ChangeSet and wait for that to complete.
        If there are no changes in the ChangeSet it will be automatically deleted.
        :raises StackError: If creating the ChangeSet or executing it fails
        """
        if not StackStatus.is_creatable(self.stack) and not StackStatus.is_updatable(self.stack):
            stack_status = StackStatus.get_status(self.stack)
            if stack_status == StackStatus.ROLLBACK_COMPLETE:
                warn(f'Deleting Stack {self.config.stack_name} in ROLLBACK_COMPLETE status before attempting to create')
                self.delete()
            else:
                raise ValidationError(f'Stack {self.config.stack_name} is not in a deployable status: '
                                      f'{stack_status.name}')

        info(f'\nCreating ChangeSet {self.change_set_name}\n')
        try:
            self.client.create_change_set(**self.build_change_set_args())
            if self.wait_for_change_set():
                if self.auto_approve:
                    self.execute_change_set()
                else:
                    self.pending_change_set()
        except ClientError as ce:
            raise StackError(ce)
Esempio n. 4
0
 def load_stack(self):
     """
     Load Description of stack to determine if it exists and the current status
     :return: Matching stack of None
     """
     try:
         stacks = self.client.describe_stacks(
             StackName=self.config.stack_name)['Stacks']
         stack = stacks[0]
         return stack
     except ClientError as ce:
         if ce.response['Error']['Code'] == 'ValidationError':
             return None
         else:
             raise StackError(ce)
Esempio n. 5
0
    def wait_for_change_set(self):
        """
        Wait for a ChangeSet to be created, printing the details when it is created.
        :return: True if ChangeSet created, False if there are no changes
        :raises StackError: If creation of ChangeSet fails for reason other than no changes
        """
        try:
            self.client.get_waiter('change_set_create_complete').wait(
                ChangeSetName=self.change_set_name,
                StackName=self.config.stack_name,
                WaiterConfig={
                    'Delay': 5,
                    'MaxAttempts': 120
                })
        except WaiterError as we:
            resp = we.last_response
            status = resp['Status']
            reason = resp['StatusReason']

            # See SAM CLI: https://github.com/awslabs/aws-sam-cli/blob/develop/samcli/lib/deploy/deployer.py#L272
            if (status == 'FAILED'
                    and "The submitted information didn't contain changes."
                    in reason or 'No updates are to be performed' in reason):
                warn('No changes to Stack {}'.format(self.config.stack_name))
                self.client.delete_change_set(
                    ChangeSetName=self.change_set_name,
                    StackName=self.config.stack_name)
                return False

            raise StackError(
                f'ChangeSet creation failed - Status: {status}, Reason: {reason}'
            )

        describe_change_set_response = self.client.describe_change_set(
            ChangeSetName=self.change_set_name,
            StackName=self.config.stack_name)

        data = [[
            change['ResourceChange']['Action'],
            change['ResourceChange']['LogicalResourceId'],
            change['ResourceChange']['ResourceType'],
            change['ResourceChange'].get('Replacement', '-')
        ] for change in describe_change_set_response['Changes']]
        table(data,
              ['Action', 'LogicalResourceId', 'ResourceType', 'Replacement'])
        return True