Skip to main content

Run functional tests on cloudformation stacks.

Project description

Python Latest Tests Coverage License


Cloud-Radar

Write functional tests for multi-region Cloudformation stacks.
Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Roadmap
  5. Contributing
  6. License
  7. Contact
  8. Acknowledgements

About The Project

Cloud-Radar is a python module that allows testing of Cloudformation Templates using Python.

Unit Testing

You can now unit test the logic contained inside your Cloudformation template. Cloud-Radar takes your template, the desired region and some parameters. We render the template into its final state and pass it back to you.

You can Test:

  • That Conditionals in your template evaluate to the correct behavior.
  • Conditional resources were created or not.
  • That resources have the correct properties.
  • That resources are named as expected because of !Sub.

You can test all this locally without worrying about AWS Credentials.

Functional Testing

This project is a wrapper around Taskcat. Taskcat is a great tool for ensuring your Cloudformation Template can be deployed in multiple AWS Regions. Cloud-Radar enhances Taskcat by making it easier to write more complete functional tests.

Here's How:

  • You can interact with the deployed resources directly with tools you already know like boto3.
  • You can control the lifecycle of the stack. This allows testing if resources were retained after the stacks were deleted.
  • You can dynamically generate taskcat projects, tests and template parameters without hardcoding them in a config file.

This project is new and it's possible not all features or functionality of Taskcat/Cloudformation are supported (see Roadmap). If you find something missing or have a use case that isn't covered then please let me know =)

Built With

Getting Started

Cloud-Radar is available as an easy to install pip package.

Prerequisites

Cloud-Radar requires python >= 3.8

Installation

  1. Install with pip.
    pip install cloud-radar
    

Usage

Unit Testing

Using Cloud-Radar starts by importing it into your test file or framework. We will use this Template as an example.

from cloud_radar.unit_test import Template

template_path = Path(__file__).parent / "../templates/log_bucket/log_bucket.yaml"

# template_path can be a str or a Path object
template = Template.from_yaml(template_path.resolve())

params = {"BucketPrefix": "testing", "KeepBucket": "TRUE"}

# parameters and region are optional arguments.
result = template.render(params, region="us-west-2")

assert "LogsBucket" not in result["Resources"]

bucket = result["Resources"]["RetainLogsBucket"]

assert "DeletionPolicy" in bucket

assert bucket["DeletionPolicy"] == "Retain"

bucket_name = bucket["Properties"]["BucketName"]

assert "us-west-2" in bucket_name

The AWS pseudo variables are all class attributes and can be modified before rendering a template.

# The value of 'AWS::AccountId' in !Sub "My AccountId is ${AWS::AccountId}" can be changed:
Template.AccountId = '8675309'

Note: Region should only be changed to change the default value. To change the region during testing pass the desired region to render(region='us-west-2')

The default values for psedo variables:

Name Default Value
AccountId "555555555555"
NotificationARNs []
NoValue ""
Partition "aws"
Region "us-east-1"
StackId ""
StackName ""
URLSuffix "amazonaws.com"
Note: Bold variables are not fully impletmented yet see the Roadmap

A real unit testing example using Pytest can be seen here

Function Testing Using Cloud-Radar starts by importing it into your test file or framework.
from cloud_radar import Test

# Test is a context manager that makes sure your stacks are deleted after testing.

# test-name is the name of your test from your taskcat project file.
# ./project_dir is the path to the folder that contains your Cloudformation template
# and taskcat config file.
with Test('test-name', './project_dir') as stacks:
    # Stacks will be created and returned as a list in the stacks variable.

    for stack in stacks:
        # stack will be an instance of Taskcat's Stack class.
        # It has all the expected properties like parameters, outputs and resources

        print(f"Testing {stack.name}")

        bucket_name = ""

        for output in stack.outputs:

            if output.key == "LogsBucketName":
                bucket_name = output.value
                break

        assert "logs" in bucket_name

        assert stack.region.name in bucket_name

        print(f"Created bucket: {bucket_name}")

# Once the test is over then all resources will be cleaned up.

You can also supply a Taskcat config as a python dictionary.

config = {
    "project": {
        "name": "taskcat-test-logbucket",
        "regions": ["us-west-1", "us-west-2"],
    },
    "tests": {
        "log-bucket": {
            "template": "./log_bucket.yaml",
            "parameters": {
                "BucketPrefix": "taskcat-$[taskcat_random-string]",
                "KeepBucket": "FALSE",
            },
        }
    },
}

with Test('log-bucket', './project_dir', config_input=config) as stacks:
    for stack in stacks:
      assert 'us-east' not in stack.region.name

You can also skip the context manager. Here is an example for unittest

import unittest

from cloud-radar import TestManager

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.tm = TestManager('test-name','./project_dir')
        cls.tm.create()

    @classmethod
    def tearDownClass(cls):
        cls.tm.delete()

    def test_bucket(self):
        stacks = self.__class__.tm.stacks

        for stack in stacks:
            # Test

Calling help(cloud-radar.Test)

Test(test_name: str, project_dir: str, config_input: dict = None, config_file: str = './.taskcat.yml', regions: str = 'ALL', wait_for_delete: bool = False) -> Iterator[taskcat._cfn.stack.Stacks]
    Create Stacks for a Taskcat test and return the stacks.

    Must pass in a Taskcat configuration as either a dictionary or file.

    Args:
        test_name (str): The name of the test from the Taskcat config file.
        project_dir (str): The directory that contains your Taskcat config and cloudformation files.
        config_input (dict, optional): Taskcat config file in the form of a dictionary. Defaults to None.
        config_file (str, optional): The name of the Taskcat config file. Defaults to "./.taskcat.yml".
        regions (str, optional): Overide the regions defined in the config file. Defaults to "ALL".
        wait_for_delete (bool, optional): Wait until stacks have deleted. Defaults to False.

    Yields:
        Iterator[Stacks]: The stacks created for the tests.

All the properties and methods of a stack instance.

A real functional testing example using Pytest can be seen here

Roadmap

Project

  • Python 3.7 support
  • Add Logging
  • Add Logo
  • Make it easier to interact with stack resources.
    • Getting a resource for testing should be as easy as stack.Resources('MyResource) or template.Resources('MyResource')

Unit

  • Implement all AWS intrinsic functions.
    • Only !Ref, !Sub, !Equals and !If currently supported.
  • Add full functionality to pseudo variables.
    • Variables like Partition, URLSuffix should change if the region changes.
    • Variables like StackName and StackId should have a better default than ""
  • Handle References to resources that shouldn't exist.
    • It's currently possible that a !Ref to a Resource stays in the final template even if that resource is later removed because of a conditional.

Functional

  • Add the ability to update a stack instance to Taskcat.
  • Add logging to Cloud-Radar
  • Add logo

See the open issues for a list of proposed features (and known issues).

Contributing

Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

This project uses poetry to manage dependencies and pre-commit to run formatting, linting and tests. You will need to have both installed to your system as well as python 3.9.

  1. Fork the Project
  2. Setup environment (poetry install)
  3. Setup commit hooks (pre-commit install)
  4. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  5. Commit your Changes (git commit -m 'Add some AmazingFeature')
  6. Push to the Branch (git push origin feature/AmazingFeature)
  7. Open a Pull Request

License

Distributed under the Apache-2.0 License. See LICENSE.txt for more information.

Contact

Levi - @shady_cuz

Acknowledgements

Project details


Release history Release notifications | RSS feed

This version

0.4.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cloud-radar-0.4.0.tar.gz (19.4 kB view hashes)

Uploaded Source

Built Distribution

cloud_radar-0.4.0-py3-none-any.whl (15.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page