Skip to content

Module taskcat.testing

None

None

View Source
from ._cfn_test import CFNTest  # noqa: F401

from ._lint_test import LintTest  # noqa: F401

from ._unit_test import UnitTest  # noqa: F401

__all__ = ["CFNTest"]

Sub-modules

Classes

CFNTest

class CFNTest(
    config: taskcat._config.Config,
    printer: Union[taskcat._tui.TerminalPrinter, NoneType] = None,
    test_names: str = 'ALL',
    regions: str = 'ALL',
    skip_upload: bool = False,
    lint_disable: bool = False,
    no_delete: bool = False,
    keep_failed: bool = False,
    dont_wait_for_delete: bool = True,
    _extra_tags: list = None
)
View Source
class CFNTest(BaseTest):  # pylint: disable=too-many-instance-attributes

    """

    Tests Cloudformation template by making sure the stack can properly deploy

    in the specified regions.

    """

    def __init__(

        self,

        config: Config,

        printer: Union[TerminalPrinter, None] = None,

        test_names: str = "ALL",

        regions: str = "ALL",

        skip_upload: bool = False,

        lint_disable: bool = False,

        no_delete: bool = False,

        keep_failed: bool = False,

        dont_wait_for_delete: bool = True,

        _extra_tags: list = None,

    ):

        """The constructor creates a test from the given Config object.

        Args:

            config (Config): A pre-configured Taskcat Config instance.

            printer (Union[TerminalPrinter, None], optional): A printer object that will handle Test output. Defaults to TerminalPrinter.

            test_names (str, optional): A comma separated list of tests to run. Defaults to "ALL".

            regions (str, optional): A comma separated list of regions to test in. Defaults to "ALL".

            skip_upload (bool, optional): Use templates in an existing cloudformation bucket. Defaults to False.

            lint_disable (bool, optional): Disable linting with cfn-lint. Defaults to False.

            no_delete (bool, optional): Don't delete stacks after test is complete. Defaults to False.

            keep_failed (bool, optional): Don't delete failed stacks. Defaults to False.

            dont_wait_for_delete (bool, optional): Exits immediately after calling stack_delete. Defaults to True.

        """  # noqa: B950

        super().__init__(config)

        self.test_definition: Stacker

        self.test_names = test_names

        self.regions = regions

        self.skip_upload = skip_upload

        self.lint_disable = lint_disable

        self.no_delete = no_delete

        self.keep_failed = keep_failed

        self.dont_wait_for_delete = dont_wait_for_delete

        self._extra_tags = _extra_tags if _extra_tags else []

        if printer is None:

            self.printer = TerminalPrinter(minimalist=True)

        else:

            self.printer = printer

    def run(self) -> None:

        """Deploys the required Test resources in AWS.

        Raises:

            TaskCatException: If skip_upload is set without specifying s3_bucket in config.

            TaskCatException: If linting fails with errors.

        """

        _trim_regions(self.regions, self.config)

        _trim_tests(self.test_names, self.config)

        boto3_cache = Boto3Cache()

        templates = self.config.get_templates()

        if self.skip_upload and not self.config.config.project.s3_bucket:

            raise TaskCatException(

                "cannot skip_buckets without specifying s3_bucket in config"

            )

        buckets = self.config.get_buckets(boto3_cache)

        if not self.skip_upload:

            # 1. lint

            if not self.lint_disable:

                lint = TaskCatLint(self.config, templates)

                errors = lint.lints[1]

                lint.output_results()

                if errors or not lint.passed:

                    raise TaskCatException("Lint failed with errors")

            # 2. build lambdas

            if self.config.config.project.package_lambda:

                LambdaBuild(self.config, self.config.project_root)

            # 3. s3 sync

            stage_in_s3(

                buckets, self.config.config.project.name, self.config.project_root, []

            )

        regions = self.config.get_regions(boto3_cache)

        parameters = self.config.get_rendered_parameters(buckets, regions, templates)

        tests = self.config.get_tests(templates, regions, buckets, parameters)

        # pre-hooks

        execute_hooks("prehooks", self.config, tests, parameters)

        self.test_definition = Stacker(

            self.config.config.project.name,

            tests,

            shorten_stack_name=self.config.config.project.shorten_stack_name,

            tags=self._extra_tags,

        )

        self.test_definition.create_stacks()

        # post-hooks

        # TODO: pass in outputs, once there is a standard interface for a test_definition

        execute_hooks("posthooks", self.config, tests, parameters)

        self.printer.report_test_progress(stacker=self.test_definition)

        self.passed = True

        self.result = self.test_definition.stacks

    def clean_up(self) -> None:  # noqa: C901

        """Deletes the Test related resources in AWS.

        Raises:

            TaskCatException: If one or more stacks failed to create.

        """

        if not hasattr(self, "test_definition"):

            LOG.warning("No stacks were created... skipping cleanup.")

            return

        status = self.test_definition.status()

        # Delete Stacks

        if self.no_delete:

            LOG.info("Skipping delete due to cli argument")

        elif self.keep_failed:

            if len(status["COMPLETE"]) > 0:

                LOG.info("deleting successful stacks")

                self.test_definition.delete_stacks({"status": "CREATE_COMPLETE"})

        else:

            self.test_definition.delete_stacks()

        if not self.dont_wait_for_delete:

            self.printer.report_test_progress(stacker=self.test_definition)

        # TODO: summarise stack statusses (did they complete/delete ok) and print any

        #  error events

        # Delete Templates and Buckets

        buckets = self.config.get_buckets()

        if not self.no_delete or (

            self.keep_failed is True and len(status["FAILED"]) == 0

        ):

            deleted: ListType[str] = []

            for test in buckets.values():

                for bucket in test.values():

                    if (bucket.name not in deleted) and not bucket.regional_buckets:

                        bucket.delete(delete_objects=True)

                        deleted.append(bucket.name)

        # 9. raise if something failed

        # - grabbing the status again to ensure everything deleted OK.

        status = self.test_definition.status()

        if len(status["FAILED"]) > 0:

            raise TaskCatException(

                f'One or more stacks failed to create: {status["FAILED"]}'

            )

    def report(

        self,

        output_directory: str = "./taskcat_outputs",

    ):

        """Generates a report of the status of Cloudformation stacks.

        Args:

            output_directory (str, optional): The directory to save the report in. Defaults to "./taskcat_outputs".

        """  # noqa: B950

        report_path = Path(output_directory).resolve()

        report_path.mkdir(exist_ok=True)

        cfn_logs = _CfnLogTools()

        cfn_logs.createcfnlogs(self.test_definition, report_path)

        ReportBuilder(

            self.test_definition, report_path / "index.html"

        ).generate_report()

Ancestors (in MRO)

  • taskcat.testing.base_test.BaseTest
  • taskcat.testing._abstract_test.Test
  • abc.ABC

Static methods

from_dict

def from_dict(
    input_config: dict,
    project_root: str = './',
    regions: str = 'ALL',
    enable_sig_v2: bool = False
) -> ~T

Creates a Test from a Taskcat configuration in dictionary form.

Parameters:

Name Type Description Default
input_config dict A Taskcat configuration in the form of a dict. None
project_root str The path to the directory with your template and config file. Defaults to "./". "./"
regions str A comma separated list of regions to test in. Defaults to "ALL". "ALL"
enable_sig_v2 bool Enable legacy sigv2 requests for auto-created buckets. Defaults to False. False

Returns:

Type Description
T Returns a Test instance.
View Source
    @classmethod

    def from_dict(

        cls: Type[T],

        input_config: dict,

        project_root: str = "./",

        regions: str = "ALL",

        enable_sig_v2: bool = False,

    ) -> T:

        """Creates a Test from a Taskcat configuration in dictionary form.

        Args:

            input_config (dict): A Taskcat configuration in the form of a dict.

            project_root (str, optional): The path to the directory with your template and config file. Defaults to "./".

            regions (str, optional): A comma separated list of regions to test in. Defaults to "ALL".

            enable_sig_v2 (bool, optional): Enable legacy sigv2 requests for auto-created buckets. Defaults to False.

        Returns:

            T: Returns a Test instance.

        """  # noqa: B950

        project_root_path: Path = Path(project_root).expanduser().resolve()

        # pylint: disable=too-many-arguments

        args = _build_args(enable_sig_v2, regions, GLOBAL_ARGS.profile)

        sources = [

            {"source": "Manual", "config": input_config},

            {"source": "CliArgument", "config": args},

        ]

        config = Config(

            uid=uuid.uuid4(), project_root=project_root_path, sources=sources

        )

        return cls(config)

from_file

def from_file(
    project_root: str = './',
    input_file: str = './.taskcat.yml',
    regions: str = 'ALL',
    enable_sig_v2: bool = False
) -> ~T

Creates a Test from a Taskcat config file.

Parameters:

Name Type Description Default
project_root str The path to the directory with your template and config file. Defaults to "./". "./"
input_file str The name of the Taskcat confile file. Defaults to "./.taskcat.yml". "./.taskcat.yml"
regions str A comma separated list of regions to test in. Defaults to "ALL". "ALL"
enable_sig_v2 bool Enable legacy sigv2 requests for auto-created buckets. Defaults to False. False

Returns:

Type Description
T Returns a Test instance.
View Source
    @classmethod

    def from_file(

        cls: Type[T],

        project_root: str = "./",

        input_file: str = "./.taskcat.yml",

        regions: str = "ALL",

        enable_sig_v2: bool = False,

    ) -> T:

        """Creates a Test from a Taskcat config file.

        Args:

            project_root (str, optional): The path to the directory with your template and config file. Defaults to "./".

            input_file (str, optional): The name of the Taskcat confile file. Defaults to "./.taskcat.yml".

            regions (str, optional): A comma separated list of regions to test in. Defaults to "ALL".

            enable_sig_v2 (bool, optional): Enable legacy sigv2 requests for auto-created buckets. Defaults to False.

        Returns:

            T: Returns a Test instance.

        """  # noqa: B950

        project_root_path: Path = Path(project_root).expanduser().resolve()

        input_file_path: Path = project_root_path / input_file

        # pylint: disable=too-many-arguments

        args = _build_args(enable_sig_v2, regions, GLOBAL_ARGS.profile)

        config = Config.create(

            project_root=project_root_path,

            project_config_path=input_file_path,

            args=args

            # TODO: detect if input file is taskcat config or CloudFormation template

        )

        return cls(config)

Instance variables

config
passed
result

Methods

clean_up

def clean_up(
    self
) -> None

Deletes the Test related resources in AWS.

Raises:

Type Description
TaskCatException If one or more stacks failed to create.
View Source
    def clean_up(self) -> None:  # noqa: C901

        """Deletes the Test related resources in AWS.

        Raises:

            TaskCatException: If one or more stacks failed to create.

        """

        if not hasattr(self, "test_definition"):

            LOG.warning("No stacks were created... skipping cleanup.")

            return

        status = self.test_definition.status()

        # Delete Stacks

        if self.no_delete:

            LOG.info("Skipping delete due to cli argument")

        elif self.keep_failed:

            if len(status["COMPLETE"]) > 0:

                LOG.info("deleting successful stacks")

                self.test_definition.delete_stacks({"status": "CREATE_COMPLETE"})

        else:

            self.test_definition.delete_stacks()

        if not self.dont_wait_for_delete:

            self.printer.report_test_progress(stacker=self.test_definition)

        # TODO: summarise stack statusses (did they complete/delete ok) and print any

        #  error events

        # Delete Templates and Buckets

        buckets = self.config.get_buckets()

        if not self.no_delete or (

            self.keep_failed is True and len(status["FAILED"]) == 0

        ):

            deleted: ListType[str] = []

            for test in buckets.values():

                for bucket in test.values():

                    if (bucket.name not in deleted) and not bucket.regional_buckets:

                        bucket.delete(delete_objects=True)

                        deleted.append(bucket.name)

        # 9. raise if something failed

        # - grabbing the status again to ensure everything deleted OK.

        status = self.test_definition.status()

        if len(status["FAILED"]) > 0:

            raise TaskCatException(

                f'One or more stacks failed to create: {status["FAILED"]}'

            )

report

def report(
    self,
    output_directory: str = './taskcat_outputs'
)

Generates a report of the status of Cloudformation stacks.

Parameters:

Name Type Description Default
output_directory str The directory to save the report in. Defaults to "./taskcat_outputs". "./taskcat_outputs"
View Source
    def report(

        self,

        output_directory: str = "./taskcat_outputs",

    ):

        """Generates a report of the status of Cloudformation stacks.

        Args:

            output_directory (str, optional): The directory to save the report in. Defaults to "./taskcat_outputs".

        """  # noqa: B950

        report_path = Path(output_directory).resolve()

        report_path.mkdir(exist_ok=True)

        cfn_logs = _CfnLogTools()

        cfn_logs.createcfnlogs(self.test_definition, report_path)

        ReportBuilder(

            self.test_definition, report_path / "index.html"

        ).generate_report()

run

def run(
    self
) -> None

Deploys the required Test resources in AWS.

Raises:

Type Description
TaskCatException If skip_upload is set without specifying s3_bucket in config.
TaskCatException If linting fails with errors.
View Source
    def run(self) -> None:

        """Deploys the required Test resources in AWS.

        Raises:

            TaskCatException: If skip_upload is set without specifying s3_bucket in config.

            TaskCatException: If linting fails with errors.

        """

        _trim_regions(self.regions, self.config)

        _trim_tests(self.test_names, self.config)

        boto3_cache = Boto3Cache()

        templates = self.config.get_templates()

        if self.skip_upload and not self.config.config.project.s3_bucket:

            raise TaskCatException(

                "cannot skip_buckets without specifying s3_bucket in config"

            )

        buckets = self.config.get_buckets(boto3_cache)

        if not self.skip_upload:

            # 1. lint

            if not self.lint_disable:

                lint = TaskCatLint(self.config, templates)

                errors = lint.lints[1]

                lint.output_results()

                if errors or not lint.passed:

                    raise TaskCatException("Lint failed with errors")

            # 2. build lambdas

            if self.config.config.project.package_lambda:

                LambdaBuild(self.config, self.config.project_root)

            # 3. s3 sync

            stage_in_s3(

                buckets, self.config.config.project.name, self.config.project_root, []

            )

        regions = self.config.get_regions(boto3_cache)

        parameters = self.config.get_rendered_parameters(buckets, regions, templates)

        tests = self.config.get_tests(templates, regions, buckets, parameters)

        # pre-hooks

        execute_hooks("prehooks", self.config, tests, parameters)

        self.test_definition = Stacker(

            self.config.config.project.name,

            tests,

            shorten_stack_name=self.config.config.project.shorten_stack_name,

            tags=self._extra_tags,

        )

        self.test_definition.create_stacks()

        # post-hooks

        # TODO: pass in outputs, once there is a standard interface for a test_definition

        execute_hooks("posthooks", self.config, tests, parameters)

        self.printer.report_test_progress(stacker=self.test_definition)

        self.passed = True

        self.result = self.test_definition.stacks