def _dict_from_template(file_path: Path) -> dict: relative_path = str(file_path.relative_to(PROJECT_ROOT)) config_dict = ( BaseConfig() .from_dict( {"project": {"template": relative_path}, "tests": {"default": {}}} ) .to_dict() ) if not file_path.is_file(): raise TaskCatException(f"invalid template path {file_path}") try: template = Template( str(file_path), template_cache=tcat_template_cache ).template except Exception as e: LOG.warning(f"failed to load template from {file_path}") LOG.debug(str(e), exc_info=True) raise e if not template.get("Metadata"): return config_dict if not template["Metadata"].get("taskcat"): return config_dict template_config_dict = template["Metadata"]["taskcat"] if not template_config_dict.get("project"): template_config_dict["project"] = {} template_config_dict["project"]["template"] = relative_path if not template_config_dict.get("tests"): template_config_dict["tests"] = {"default": {}} return template_config_dict
def create( cls, region: TestRegion, stack_name: str, template: Template, tags: List[Tag] = None, disable_rollback: bool = True, test_name: str = "", uuid: UUID = None, ) -> "Stack": parameters = cls._cfn_format_parameters(region.parameters) uuid = uuid if uuid else uuid4() cfn_client = region.client("cloudformation") tags = [t.dump() for t in tags] if tags else [] bucket_name: str = region.s3_bucket.name template.url = s3_url_maker(bucket_name, template.s3_key, region.client("s3")) stack_id = cfn_client.create_stack( StackName=stack_name, TemplateURL=template.url, Parameters=parameters, DisableRollback=disable_rollback, Tags=tags, Capabilities=Capabilities.ALL, )["StackId"] stack = cls(region, stack_id, template, test_name, uuid) # fetch property values from cfn stack.refresh() return stack
def create( cls, region: AWSRegionObject, stack_name: str, template: Template, parameters: List[Parameter] = None, tags: List[Tag] = None, disable_rollback: bool = True, test_name: str = "", uuid: UUID = None, ) -> "Stack": uuid = uuid if uuid else uuid4() cfn_client = region.client("cloudformation") parameters = [p.dump() for p in parameters] if parameters else [] tags = [t.dump() for t in tags] if tags else [] if isinstance(region.s3bucket, (S3Bucket, mock.Mock)): bucket_name: str = region.s3bucket.name else: raise TypeError("region object has unset bucket object") template.url = s3_url_maker(bucket_name, template.s3_key, region.client("s3")) stack_id = cfn_client.create_stack( StackName=stack_name, TemplateURL=template.url, Parameters=parameters, DisableRollback=disable_rollback, Tags=tags, Capabilities=Capabilities.ALL, )["StackId"] stack = cls(region, stack_id, template, test_name, uuid) # fetch property values from cfn stack.refresh() return stack
def _get_templates(self): for _, test in self.tests.items(): test.template = Template( template_path=test.template_file, project_root=self.project_root, s3_key_prefix=f"{self.name}/", client_factory_instance=test.client_factory, )
def get_templates(self, project_root: Path): templates = {} for test_name, test in self.config.tests.items(): templates[test_name] = Template( template_path=project_root / test.template, project_root=project_root, s3_key_prefix=f"{self.config.project.name}/", ) return templates
def _import_child( # pylint: disable=too-many-locals cls, stack_properties: dict, parent_stack: "Stack" ) -> Optional["Stack"]: url = "" for event in parent_stack.events(): if event.physical_id == stack_properties["StackId"] and event.properties: url = event.properties["TemplateURL"] if url.startswith(parent_stack.template.url_prefix()): # Template is part of the project, discovering path relative_path = url.replace(parent_stack.template.url_prefix(), "").lstrip( "/" ) absolute_path = parent_stack.template.project_root / relative_path else: try: # Assuming template is remote to project and downloading it cfn_client = parent_stack.client tempate_body = cfn_client.get_template( StackName=stack_properties["StackId"] )["TemplateBody"] path = parent_stack.template.project_root / Stack.REMOTE_TEMPLATE_PATH os.makedirs(path, exist_ok=True) fname = ( "".join( random.choice(string.ascii_lowercase) # nosec for _ in range(16) ) + ".template" ) absolute_path = path / fname template_str = ordered_dump(tempate_body, dumper=yaml.SafeDumper) if not absolute_path.exists(): with open(absolute_path, "w") as fh: fh.write(template_str) except Exception as e: # pylint: disable=broad-except LOG.warning( f"Failed to attach child stack " f'{stack_properties["StackId"]} {str(e)}' ) LOG.debug("traceback", exc_info=True) return None template = Template( template_path=str(absolute_path), project_root=parent_stack.template.project_root, url=url, ) stack = cls( parent_stack.region, stack_properties["StackId"], template, parent_stack.name, parent_stack.uuid, ) stack.set_stack_properties(stack_properties) return stack
def _import_child( cls, stack_properties: dict, parent_stack: "Stack" ) -> Optional["Stack"]: url = "" for event in parent_stack.events(): if event.physical_id == stack_properties["StackId"] and event.properties: url = event.properties["TemplateURL"] if url.startswith(parent_stack.template.url_prefix()): # Template is part of the project, discovering path relative_path = url.replace(parent_stack.template.url_prefix(), "").lstrip( "/" ) absolute_path = parent_stack.template.project_root / relative_path else: # Assuming template is remote to project and downloading it cfn_client = parent_stack.client tempate_body = cfn_client.get_template( StackName=stack_properties["StackId"] )["TemplateBody"] path = parent_stack.template.project_root / Stack.REMOTE_TEMPLATE_PATH os.makedirs(path, exist_ok=True) fname = ( "".join( random.choice(string.ascii_lowercase) for _ in range(16) # nosec ) + ".template" ) absolute_path = path / fname if not absolute_path.exists(): with open(absolute_path, "w") as fh: fh.write(tempate_body) template = Template( template_path=str(absolute_path), project_root=parent_stack.template.project_root, url=url, client_factory_instance=parent_stack.client, ) stack = cls( parent_stack.region, stack_properties["StackId"], template, parent_stack.name, parent_stack.uuid, ) stack.set_stack_properties(stack_properties) return stack
def _process_template_config(self): if not self.template_path: return template = Template(str(self.template_path)).template try: template_config = template["Metadata"]["taskcat"] except KeyError: name = self.template_path.name.split(".")[0] template_config = { "project": { "name": name }, "tests": { name: {}, "parameters": {} }, } self._add_template_path(template_config) validate(template_config, "project_config") self._set_all(template_config)
def create( cls, region: TestRegion, stack_name: str, template: Template, tags: List[Tag] = None, disable_rollback: bool = True, test_name: str = "", uuid: UUID = None, ) -> "Stack": parameters = cls._cfn_format_parameters(region.parameters) uuid = uuid if uuid else uuid4() cfn_client = region.client("cloudformation") tags = [t.dump() for t in tags] if tags else [] template = Template( template_path=template.template_path, project_root=template.project_root, s3_key_prefix=template.s3_key_prefix, url=s3_url_maker( region.s3_bucket.name, template.s3_key, region.client("s3"), region.s3_bucket.auto_generated, ), template_cache=tcat_template_cache, ) create_options = { "StackName": stack_name, "TemplateURL": template.url, "Parameters": parameters, "DisableRollback": disable_rollback, "Tags": tags, "Capabilities": Capabilities.ALL, } if region.role_arn: create_options["RoleARN"] = region.role_arn stack_id = cfn_client.create_stack(**create_options)["StackId"] stack = cls(region, stack_id, template, test_name, uuid) # fetch property values from cfn stack.refresh() return stack
def _import_child( # pylint: disable=too-many-locals cls, stack_properties: dict, parent_stack: "Stack" ) -> Optional["Stack"]: try: url = "" for event in parent_stack.events(): if ( event.physical_id == stack_properties["StackId"] and event.properties ): url = event.properties["TemplateURL"] if url.startswith(parent_stack.template.url_prefix()): # Template is part of the project, discovering path relative_path = url.replace( parent_stack.template.url_prefix(), "" ).lstrip("/") absolute_path = parent_stack.template.project_root / relative_path if not absolute_path.is_file(): # try with the base folder stripped off relative_path2 = Path(relative_path) relative_path2 = relative_path2.relative_to( *relative_path2.parts[:1] ) absolute_path = parent_stack.template.project_root / relative_path2 if not absolute_path.is_file(): LOG.warning( f"Failed to find template for child stack " f"{stack_properties['StackId']}. tried " f"{parent_stack.template.project_root / relative_path}" f" and {absolute_path}" ) return None else: # Assuming template is remote to project and downloading it cfn_client = parent_stack.client tempate_body = cfn_client.get_template( StackName=stack_properties["StackId"] )["TemplateBody"] path = parent_stack.template.project_root / Stack.REMOTE_TEMPLATE_PATH os.makedirs(path, exist_ok=True) fname = ( "".join( random.choice(string.ascii_lowercase) # nosec for _ in range(16) ) + ".template" ) absolute_path = path / fname if not isinstance(tempate_body, str): tempate_body = ordered_dump(tempate_body, dumper=yaml.SafeDumper) if not absolute_path.exists(): with open(absolute_path, "w", encoding="utf-8") as fh: fh.write(tempate_body) template = Template( template_path=str(absolute_path), project_root=parent_stack.template.project_root, url=url, template_cache=tcat_template_cache, ) stack = cls( parent_stack.region, stack_properties["StackId"], template, parent_stack.name, parent_stack.uuid, ) stack.set_stack_properties(stack_properties) except Exception as e: # pylint: disable=broad-except LOG.warning(f"Failed to import child stack: {str(e)}") LOG.debug("traceback:", exc_info=True) return None return stack
rule_id_to_func = {'E3012': fix_E3012} def find_rule_matches(tc_template): cfnlint.core.configure_logging(None) rules = cfnlint.core.get_rules([], [], [], [], False, []) regions = ['us-east-1'] matches = cfnlint.core.run_checks(tc_template.template_path, tc_template.template, rules, regions) return matches if __name__ == '__main__': parser = argparse.ArgumentParser( description='Fix type-errors from cfnlint') parser.add_argument('--file', type=str) args = parser.parse_args() tc_template = Template(template_path=args.file) x = find_rule_matches(tc_template) modified_lines = set() for match in x: try: result = rule_id_to_func[match.rule.id](tc_template, match) if result: modified_lines.add(result) except KeyError: continue write_template_modifications(modified_lines, tc_template)