Esempio n. 1
0
 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
Esempio n. 2
0
 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
Esempio n. 3
0
 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
Esempio n. 4
0
 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,
         )
Esempio n. 5
0
 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
Esempio n. 6
0
 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
Esempio n. 7
0
 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
Esempio n. 8
0
 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)
Esempio n. 9
0
 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
Esempio n. 10
0
 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
Esempio n. 11
0
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)