Exemple #1
0
    def sanity_check(self):
        self.find_missing_env()
        if all(item is not NotSpecified for item in (self.params_json, self.params_yaml)):
            raise BadStack("Please don't have both params_json and params_yaml")
        if all(item is NotSpecified for item in (self.stack_json, self.stack_yaml)):
            raise BadStack("Could not find either stack_json or stack_yaml")

        # Hack for sanity check
        for var in self.nested_vars():
            if hasattr(var, 'stack') and not isinstance(var.stack, six.string_types):
                if not var.stack.cloudformation.status.exists:
                    var._resolved = "YYY_RESOLVED_BY_MISSING_STACK_YYY"
                elif var.stack.output_yet_to_be_deployed(var):
                    var._resolved = "YYY_RESOLVED_BY_MISSING_OUTPUT_YYY"

        matches = re.findall("XXX_[A-Z0-9_]+_XXX", json.dumps(self.params_json_obj))
        for var in self.nested_vars():
            if hasattr(var, "_resolved"):
                var._resolved = None

        if matches:
            raise BadStack("Found placeholders in the generated params file", stack=self.name, found=matches)
        if self.cloudformation.status.failed:
            raise BadStack("Stack is in a failed state, this means it probably has to be deleted first....", stack=self.stack_name)

        self.validate_template_params()
    def wait(self,
             timeout=1200,
             rollback_is_failure=False,
             may_not_exist=True):
        status = self.status
        if not status.exists and may_not_exist:
            return status

        last = datetime.datetime.now(pytz.utc)
        if status.failed:
            raise BadStack(
                "Stack is in a failed state, it must be deleted first",
                name=self.stack_name,
                status=status)

        for _ in hp.until(timeout, step=15):
            if status.exists and status.complete:
                break

            log.info("Waiting for %s - %s", self.stack_name, status.name)
            if status.exists and not status.complete:
                status = self.status
            else:
                break

            description = self.description()

            events = []
            while True:
                try:
                    with self.ignore_throttling_error():
                        response = self.conn.describe_stack_events(
                            StackName=self.stack_name)
                        events = response['StackEvents']
                        break
                except Throttled:
                    log.info("Was throttled, waiting a bit")
                    time.sleep(1)

            next_last = events[0]['Timestamp']
            for event in events:
                if event['Timestamp'] > last:
                    reason = event.get('ResourceStatusReason', '')
                    log.info("%s - %s %s (%s) %s", self.stack_name,
                             event['ResourceType'], event['LogicalResourceId'],
                             event['ResourceStatus'], reason)
            last = next_last

        status = self.status
        if status.failed or (rollback_is_failure
                             and status.is_rollback) or not status.complete:
            raise BadStack("Stack failed to complete", final_status=status)

        return status
Exemple #3
0
    def validate_template_params(self):
        """ Validate stack template and stack params against CloudFormation """
        validation = self.validate_template()

        _defined_params = lambda obj: set([x['ParameterKey'] for x in obj])
        template_params = _defined_params(validation.get('Parameters', []))
        defaulted_params = _defined_params(x for x in validation.get('Parameters', []) if 'DefaultValue' in x)
        required_params = template_params - defaulted_params
        stack_params = _defined_params(json.loads(self.params_json_raw))
        if stack_params > template_params:
            raise BadStack("Parameters not defined in template provided", stack=self.name, requires=list(template_params), additional=list(stack_params - template_params))
        if required_params > stack_params:
            raise BadStack("Parameters required by template missing", stack=self.name, defined=list(required_params), missing=list(required_params - stack_params))
        return validation
Exemple #4
0
    def create_or_update(self):
        """Create or update the stack, return True if the stack actually changed"""
        log.info("Creating or updating the stack (%s)", self.stack_name)
        status = self.cloudformation.wait(may_not_exist=True)
        tags = self.tags or None
        role = self.bespin.credentials.account_role_arn(self.role_name)
        if tags and type(tags) is not dict and hasattr(self.tags, "as_dict"):
            tags = tags.as_dict()

        if not status.exists:
            log.info("No existing stack, making one now")
            if self.bespin.dry_run:
                log.info("DRYRUN: Would create stack")
                log.info("Would use following stack:")
                print(self.dumped_stack_obj)
            else:
                return self.cloudformation.create(self.dumped_stack_obj, self.params_json_obj, tags, self.dumped_policy_obj, role, self.termination_protection)
        elif status.complete:
            log.info("Found existing stack, doing an update")
            if self.bespin.dry_run:
                log.info("DRYRUN: Would update stack")
                log.info("Would use following stack:")
                print(self.dumped_stack_obj)
            else:
                return self.cloudformation.update(self.dumped_stack_obj, self.params_json_obj, tags, self.dumped_policy_obj, role, self.termination_protection)
        else:
            raise BadStack("Stack could not be updated", name=self.stack_name, status=status.name)

        return False
Exemple #5
0
    def wait(self, environment):
        endpoint = self.endpoint().resolve()
        while endpoint.endswith("/"):
            endpoint = endpoint[:-1]
        while endpoint.endswith("."):
            endpoint = endpoint[:-1]

        while self.check_url.startswith("/"):
            self.check_url = self.check_url[1:]

        url = endpoint + '/' + self.check_url
        expected = self.expect.format(**environment)

        log.info("Asking server for version till we match %s", expected)
        for _ in hp.until(self.timeout_after, step=15):
            log.info("Asking %s", url)
            try:
                res = requests.get(url)
                result = res.text
                status = res.status_code
            except requests.exceptions.ConnectionError as error:
                log.warning("Failed to ask server\terror=%s", error)
            else:
                log.info("\tgot back (%s) '%s'", status, result)
                if fnmatch.fnmatch(result, expected):
                    log.info("Deployment successful!")
                    return

        raise BadStack(
            "Timedout waiting for the app to give back the correct version")