def __init__( self, package: str, aws_profile: str = "default", region="default", _stack_type="package", ): """ :param package: installed package to delete, can be an install name or uuid :param aws_profile: aws profile to use for deletion :param region: region to delete from, default will use aws cli configured default """ LOG.warning("delete is in alpha feature, use with caution") boto3_cache = Boto3Cache() if region == "default": region = boto3_cache.get_default_region(aws_profile) if isinstance(region, str): region = [region] stacks = Stacker.list_stacks([aws_profile], region) jobs = [] for stack in stacks: name = stack.get("taskcat-installer", stack["taskcat-project-name"]) job = { "name": name, "project_name": stack["taskcat-project-name"], "test_name": stack["taskcat-test-name"], "taskcat_id": stack["taskcat-id"].hex, "region": stack["region"], "type": "package" if stack.get("taskcat-installer") else "test", "stack_id": stack["stack-id"], } if _stack_type == job["type"]: if package in [job["name"], job["taskcat_id"], "ALL"]: jobs.append(job) # TODO: concurrency and wait for complete for job in jobs: client = boto3_cache.client("cloudformation", profile=aws_profile, region=job["region"]) Stack.delete(client=client, stack_id=job["stack_id"])
def __init__( # noqa: C901 self, profiles: Union[str, ListType[str]] = "default", regions="ALL", _stack_type="package", ): """ :param profiles: comma separated list of aws profiles to search :param regions: comma separated list of regions to search, default is to check all commercial regions """ LOG.warning("list is in alpha feature, use with caution") if isinstance(profiles, str): profiles = profiles.split(",") if regions == "ALL": region_set: set = set() for profile in profiles: region_set = region_set.union( set( boto3.Session(profile_name=profile). get_available_regions("cloudformation"))) regions = list(region_set) else: regions = regions.split(",") stacks = Stacker.list_stacks(profiles, regions) jobs: dict = {} for stack in stacks: stack_key = stack["taskcat-id"].hex + "-" + stack["region"] if stack_key not in jobs: name = stack.get("taskcat-installer") if _stack_type == "test" and not name: name = stack["taskcat-project-name"] jobs[stack_key] = { "name": name, "id": stack["taskcat-id"].hex, "project_name": stack["taskcat-project-name"], "active_stacks": 1, "region": stack["region"], } elif name and _stack_type == "package": jobs[stack_key] = { "name": name, "id": stack["taskcat-id"].hex, "project_name": stack["taskcat-project-name"], "active_stacks": 1, "region": stack["region"], } else: jobs[stack_key]["active_stacks"] += 1 def longest(things: list): lengths = [len(thing) for thing in things] return sorted(lengths)[-1] if lengths else 0 def spaces(number): ret = "" for _ in range(number): ret += " " return ret def pad(string, length): while len(string) < length: string += " " return string longest_name = longest([v["name"] for _, v in jobs.items()]) longest_project_name = longest( [v["project_name"] for _, v in jobs.items()]) if not jobs: LOG.info("no stacks found") return if _stack_type != "test": header = ( f"NAME{spaces(longest_name)}PROJECT{spaces(longest_project_name)}" f"ID{spaces(34)}REGION") column = "{} {} {} {}" else: header = f"NAME{spaces(longest_name)}ID{spaces(34)}REGION" column = "{} {} {}" LOG.error(header, extra={"nametag": ""}) for job in jobs.values(): args = [ pad(job["name"], longest_name), pad(job["project_name"], longest_project_name), job["id"], job["region"], ] if _stack_type == "test": args = [ pad(job["name"], longest_name), job["id"], job["region"] ] LOG.error(column.format(*args), extra={"nametag": ""})
def __init__( # noqa: C901 self, profiles: Union[str, ListType[str]] = "default", regions="ALL", stack_type="ALL", ): """ :param profiles: comma separated list of aws profiles to search :param regions: comma separated list of regions to search, default is to check \ all commercial regions :param stack_type: type of stacks to check, options are 'test', 'project', or 'ALL'. \ default is 'ALL' """ if isinstance(profiles, str): profiles = profiles.split(",") if regions == "ALL": region_set: set = set() for profile in profiles: region_set = region_set.union( set( boto3.Session(profile_name=profile). get_available_regions("cloudformation"))) regions = list(region_set) else: regions = regions.split(",") stacks = Stacker.list_stacks(profiles, regions) jobs: dict = {} for stack in stacks: stack_key = stack["taskcat-id"].hex + "-" + stack["region"] if stack_key not in jobs: name = stack.get("taskcat-installer") if stack_type == "ALL": if not name: name = stack["taskcat-project-name"] jobs[stack_key] = { "name": name, "id": stack["taskcat-id"].hex, "project_name": stack["taskcat-project-name"], "active_stacks": 1, "region": stack["region"], } elif stack_type == "test" and not name: name = stack["taskcat-project-name"] jobs[stack_key] = { "name": name, "id": stack["taskcat-id"].hex, "project_name": stack["taskcat-project-name"], "active_stacks": 1, "region": stack["region"], } elif name and stack_type == "project": jobs[stack_key] = { "name": name, "id": stack["taskcat-id"].hex, "project_name": stack["taskcat-project-name"], "active_stacks": 1, "region": stack["region"], } else: jobs[stack_key]["active_stacks"] += 1 longest_name = List._longest([v["name"] for _, v in jobs.items()]) longest_project_name = List._longest( [v["project_name"] for _, v in jobs.items()]) if not jobs: LOG.info("no stacks found") return if stack_type != "test": header = ( f"NAME{List._spaces(longest_name)}PROJECT{List._spaces(longest_project_name)}" f"ID{List._spaces(34)}REGION") column = "{} {} {} {}" else: header = f"NAME{List._spaces(longest_name)}ID{List._spaces(34)}REGION" column = "{} {} {}" LOG.error(header, extra={"nametag": ""}) for job in jobs.values(): args = [ List._pad(job["name"], longest_name), List._pad(job["project_name"], longest_project_name), job["id"], job["region"], ] if stack_type == "test": args = [ List._pad(job["name"], longest_name), job["id"], job["region"] ] LOG.error(column.format(*args), extra={"nametag": ""})
def __init__( self, project: str, aws_profile: str = "default", region="ALL", no_verify: bool = False, stack_type: str = "ALL", ): """ :param project: installed project to delete, can be an install name, uuid, or project name :param aws_profile: aws profile to use for deletion :param region: region(s) to delete from, by default, will delete all applicable\ stacks, supply a csv "us-east-1,us-west-1" to override this default :param no_verify: ignore region verification, delete will not error if an invalid\ region is detected :param stack_type: type of stacks to delete, allowable options are ["project","test","ALL"] """ boto3_cache = Boto3Cache() if region == "default": regions = boto3_cache.get_default_region(aws_profile) elif region == "ALL": region_set: set = set() region_set = region_set.union( # pylint: disable=duplicate-code set( boto3.Session(profile_name=aws_profile). get_available_regions("cloudformation"))) regions = list(region_set) elif isinstance(region, str): regions = (self._validate_regions(region) if not no_verify else region.split(",")) stacks = Stacker.list_stacks([aws_profile], regions) jobs = [] for stack in stacks: name = stack.get("taskcat-installer", stack["taskcat-project-name"]) job = { "name": name, "project_name": stack["taskcat-project-name"], "test_name": stack["taskcat-test-name"], "taskcat_id": stack["taskcat-id"].hex, "region": stack["region"], "stack_id": stack["stack-id"], } if stack_type in ["project", "ALL"] and project in [ job["name"], job["taskcat_id"], "ALL", ]: jobs.append(job) if stack_type in ["test", "ALL"] and project in [ job["project_name"], "ALL", ]: jobs.append(job) with ThreadPoolExecutor() as executor: stack_futures = { executor.submit( self._delete_stack, boto3_cache=boto3_cache, job=job, aws_profile=aws_profile, ): [job["name"], job["region"]] for job in jobs } for stack_future in as_completed(stack_futures): name_and_region = stack_futures[stack_future] try: stack_future.result() # pylint: disable=broad-except except Exception: LOG.error( f"{name_and_region[0]} failed in {name_and_region[1]}") else: LOG.info( f"{name_and_region[0]} deleted in {name_and_region[1]}" )