def test_validate(): s = Stack(name='teststack') aws_env = AWSEnv(regions=['us-east-1']) with default_region('us-east-1'): cfn_client = aws_env.client('cloudformation', region='us-east-1') stubber = Stubber(cfn_client) stubber.add_response('validate_template', {}, {'TemplateBody': ANY}) stubber.add_response('validate_template', {}, {'TemplateURL': ANY}) with stubber: s.validate() s.validate(url='noprotocol://nothing')
def test_validate(): s = Stack(name="teststack") aws_env = AWSEnv(regions=["us-east-1"]) with default_region("us-east-1"): cfn_client = aws_env.client("cloudformation", region="us-east-1") stubber = Stubber(cfn_client) stubber.add_response("validate_template", {}, {"TemplateBody": ANY}) stubber.add_response("validate_template", {}, {"TemplateURL": ANY}) with stubber: s.validate() s.validate(url="noprotocol://nothing")
def execute_for_stack(self, stack: Stack) -> int: """Execute application for a given stack and return exit status. :param Stack: the stack on which the application executes """ assert self.args is not None try: if self.args.command in ("push", "update"): # Synchronize resources to the S3 bucket s3 = self.aws_env.client("s3") with tempfile.TemporaryDirectory() as tempd: # Push data associated with CFNMain and then all data # related to the stack self.create_data_dir(root_dir=tempd) stack.create_data_dir(root_dir=tempd) if self.s3_data_key is not None: # synchronize data to the bucket before creating the stack for f in find(tempd): with open(f, "rb") as fd: subkey = os.path.relpath(f, tempd).replace( "\\", "/") logging.info( "Upload %s to %s:%s%s", subkey, self.s3_bucket, self.s3_data_key, subkey, ) s3.put_object( Bucket=self.s3_bucket, Body=fd, ServerSideEncryption="AES256", Key=self.s3_data_key + subkey, ) if self.s3_template_key is not None: logging.info("Upload template to %s:%s", self.s3_bucket, self.s3_template_key) s3.put_object( Bucket=self.s3_bucket, Body=stack.body.encode("utf-8"), ServerSideEncryption="AES256", Key=self.s3_template_key, ) logging.info("Validate template for stack %s" % stack.name) try: stack.validate(url=self.s3_template_url) except Exception: logging.error("Invalid cloud formation template") logging.error(stack.body) raise if stack.exists(): changeset_name = "changeset%s" % int(time.time()) logging.info("Push changeset: %s" % changeset_name) stack.create_change_set(changeset_name, url=self.s3_template_url) result = stack.describe_change_set(changeset_name) while result["Status"] in ("CREATE_PENDING", "CREATE_IN_PROGRESS"): time.sleep(1.0) result = stack.describe_change_set(changeset_name) if result["Status"] == "FAILED": change_executed = False if ("The submitted information didn't contain changes" in result["StatusReason"]): logging.warning(result["StatusReason"]) change_executed = True else: logging.error(result["StatusReason"]) stack.delete_change_set(changeset_name) if not change_executed: return 1 else: for el in result["Changes"]: if "ResourceChange" not in el: continue logging.info( "%-8s %-32s: (replacement:%s)", el["ResourceChange"].get("Action"), el["ResourceChange"].get("LogicalResourceId"), el["ResourceChange"].get("Replacement", "n/a"), ) if self.args.apply_changeset: ask = input("Apply change (y/N): ") if ask[0] in "Yy": stack.execute_change_set( changeset_name=changeset_name, wait=self.args.wait_stack_creation, ) return 0 else: logging.info("Create new stack") stack.create(url=self.s3_template_url, wait=self.args.wait_stack_creation) elif self.args.command == "show": print(stack.body) elif self.args.command == "protect": # Enable termination protection result = stack.enable_termination_protection() if self.stack_policy_body is not None: stack.set_stack_policy(self.stack_policy_body) else: print("No stack policy to set") elif self.args.command == "show-cfn-policy": try: print( json.dumps( stack.cfn_policy_document(). as_dict, # type: ignore indent=2, )) except AttributeError as attr_e: print( f"command supported only with troposphere stacks: {attr_e}" ) elif self.args.command == "delete": stack.delete(wait=self.args.wait_stack_creation) return 0 except botocore.exceptions.ClientError as e: logging.error(str(e)) return 1