Run functional tests on cloudformation stacks.
Project description
Cloud-Radar
Write functional tests for multi-region Cloudformation stacks.
Report Bug
·
Request Feature
Table of Contents
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
- 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)
ortemplate.Resources('MyResource')
- Getting a resource for testing should be as easy as
Unit
- Implement all AWS intrinsic functions.
- Only
!Ref
,!Sub
,!Equals
and!If
currently supported.
- Only
- Add full functionality to pseudo variables.
- Variables like
Partition
,URLSuffix
should change if the region changes. - Variables like
StackName
andStackId
should have a better default than ""
- Variables like
- 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.
- It's currently possible that a
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.
- Fork the Project
- Setup environment (
poetry install
) - Setup commit hooks (
pre-commit install
) - Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - 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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for cloud_radar-0.4.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e692313f92c200c73612af74208d877aff5dfef42dfafa967a9c1791c6cce71b |
|
MD5 | 8e338d4c637952816d62efc7ef517273 |
|
BLAKE2b-256 | 00f3d30453b03cc1e0d4437427df763b20c18edf4553ccac91c1a83f5c108694 |