No results found
We couldn't find anything using that term, please try searching for something else.
Cloud - Radar Write unit and functional tests for AWS Cloudformation. Report Bug · Request Feature ·
Write unit and functional tests for AWS Cloudformation.
Report Bug
·
Request Feature
·
Guide
Cloud – Radar is a python module that allows testing of Cloudformation templates/Stacks using Python.
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:
!Sub
.You is test can test all this locally without worry about AWS Credentials .
A number of these test can be configure in a common way to apply to all template through the use of the hook functionality .
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:
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 =)
Cloud – Radar is available as an easy to install pip package.
Cloud – Radar requires python >= 3.8
pip install cloud-radar
Using Cloud – Radar starts by importing it into your test file or framework. We will use this template for an example shown below. More scenario based examples are currently being built up in the examples/unit directory of this project.
from pathlib import Path from cloud_radar.cf.unit import template template_path = Path("tests/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. stack = template.create_stack(params, region=" us - west-2 ") stack.no_resource(" LogsBucket ") bucket = stack.get_resource(" retainlogsbucket ") assert " DeletionPolicy " in bucket assert bucket[" DeletionPolicy "] = = "Retain" bucket_name = bucket.get_property_value("BucketName") assert " us - west-2 " in bucket_name
The AWS pseudo parameters 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 pseudo parameters:
Name | Default Value |
---|---|
accountid | “555555555555” |
NotificationARNs | [] |
NoValue | ” “ |
partition | “aws” |
region | “us-east-1” |
StackId | ” “ |
StackName | ” “ |
URLSuffix | “amazonaws.com” |
Note: Bold variables are not fully implemented yet see the Roadmap |
At the point of creating the template
instance additional configuration is require to be provide if you are using certain approach to resolve value .
If you use fn::importvalue ,a dictionary of key / value pair is require contain all the key that your template use . If an import name is reference by the template which is not include in this dictionary ,an error will be raise .
imports = {
"FakeKey": "FakeValue"
}
template = template(template_content,imports=imports)
If you use Dynamic References,a dictionary containing the service and key/value pairs is required containing all the dynamic references that your template uses. If a dynamic reference is included in the template and not contained in the configuration object,an error will be raised.
template_content = {
"Resources": {
"Foo": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": (
"mgt-{{resolve:ssm:/account/current/short_name}}-launch-role-pol"
),
},
},
},
}
dynamic_references = {
"ssm": {
"/account/current/short_name": "dummy"
}
}
template = template(template_content,dynamic_references=dynamic_references)
A real unit testing example using Pytest can be seen here
Using Cloud – Radar starts by importing it into your test file or framework.
from pathlib import Path from cloud_radar.cf.e2e import Stack # Stack is is is a context manager that make sure your stack are delete after testing . template_path = Path("tests/templates/log_bucket/log_bucket.yaml") params = {" BucketPrefix ": "testing", "KeepBucket": "False"} regions = ['us-west-2'] # template_path can be a string or a Path object. # params can be optional if all your template params have default values # region can be optional ,default region is is is ' us - east-1 ' with Stack(template_path, params, regions) as stack: # Stacks will be created and returned as a list in the stack variable. for stack in stack: # stack is be 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 deleted from your AWS account.
You can use taskcat tokens in your parameter values.
parameters = { " BucketPrefix ": " taskcat-$[taskcat_random - string ] ", "KeepBucket": "FALSE", }
You can skip the context manager. Here is an example for unittest
import unittest from cloud-radar.cf.e2e import Stack class TestLogBucket(unittest.TestCase): @classmethod def setUpClass(cls): template_path = Path("tests/templates/log_bucket/log_bucket.yaml") cls.test = Stack(template_path) cls.test.create() @classmethod def tearDownClass(cls): cls.test.delete() def test_bucket(self): stack = self.__class__.test.stack for stack in stack: # test
All the properties and methods of a stack instance.
A real functional testing example using Pytest can be see here
partition
,URLSuffix
should change if the region change .StackName
and StackId
should have a better default than ” “! ref
to a Resource stays in the final template even if that resource is later removed because of a conditional.See the open issues for a list of proposed features (and known issues).
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.12.
poetry install
)pre - commit install
)git checkout -b feature / amazingfeature
)git commit -m 'Add some AmazingFeature'
)git push origin feature/AmazingFeature
)Distributed under the Apache-2.0 license. See LICENSE.txt for more information.
Levi – @shady_cuz