Skip to content

Commit

Permalink
Add support for multiple input artifacts to the CloudFormation action
Browse files Browse the repository at this point in the history
  • Loading branch information
john-shaskin committed Sep 21, 2018
1 parent 966d52f commit 34237f4
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 23 deletions.
2 changes: 1 addition & 1 deletion cumulus/chain/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from cumulus.chain import chaincontext # noqa
from cumulus.chain.params import TemplateRequirements # noqa
from cumulus.util.tropo import TemplateQuery # noqa
from cumulus.util.template_query import TemplateQuery # noqa


class Chain:
Expand Down
4 changes: 2 additions & 2 deletions cumulus/steps/dev_tools/approval_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import cumulus.policies
import cumulus.policies.codebuild
import cumulus.types.codebuild.buildaction
import cumulus.util.tropo
import cumulus.util.template_query
from cumulus.chain import step
from cumulus.steps.dev_tools import META_PIPELINE_BUCKET_POLICY_REF

Expand Down Expand Up @@ -67,7 +67,7 @@ def handle(self, chain_context):
template = chain_context.template
stage_to_add = self.stage_name_to_add

stage = cumulus.util.tropo.TemplateQuery.get_pipeline_stage_by_name(
stage = cumulus.util.template_query.TemplateQuery.get_pipeline_stage_by_name(
template=template,
stage_name=stage_to_add,
)
Expand Down
34 changes: 22 additions & 12 deletions cumulus/steps/dev_tools/cloud_formation_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,26 @@ class CloudFormationAction(step.Step):

def __init__(self,
action_name,
input_artifact_name,
input_template_file,
input_configuration_file,
input_artifact_names,
input_template_path,
input_template_configuration,
stage_name_to_add,
stack_name,
action_mode):
"""
:type action_name: basestring Displayed on the console
:type input_artifact_names: [basestring] List of input artifacts
:type input_template_path: basestring Full path to cloudformation template (ex. ArtifactName::templatefolder/template.json)
:type input_configuration: basestring Full path to cloudformation config file (ex. ArtifactName::envfolder/parameters.json)
:type stage_name_to_add: basestring Name of the pipeline stage to add this action to
:type stack_name: basestring name of the stack that this action will build
:type action_mode: cumulus.types.cloudformation.action_mode.ActionMode The actual CloudFormation action to execute
"""
step.Step.__init__(self)
self.action_name = action_name
self.input_artifact_name = input_artifact_name
self.input_template_file = input_template_file
self.input_configuration_file = input_configuration_file
self.input_artifact_names = input_artifact_names
self.input_template_path = input_template_path
self.input_template_configuration = input_template_configuration
self.stage_name_to_add = stage_name_to_add
self.stack_name = stack_name
self.action_mode = action_mode
Expand Down Expand Up @@ -60,25 +66,29 @@ def handle(self, chain_context):
]
)

input_artifacts = []
for artifact_name in self.input_artifact_names:
input_artifacts.append(codepipeline.InputArtifacts(
Name=artifact_name
))

cloud_formation_action = cumulus.types.codebuild.buildaction.CloudFormationAction(
Name=self.action_name,
InputArtifacts=[
codepipeline.InputArtifacts(Name=self.input_artifact_name)
],
InputArtifacts=input_artifacts,
Configuration={
'ActionMode': self.action_mode.value,
'RoleArn': GetAtt(cloud_formation_role, 'Arn'),
'StackName': self.stack_name,
'Capabilities': 'CAPABILITY_NAMED_IAM',
'TemplateConfiguration': self.input_artifact_name + "::" + self.input_configuration_file,
'TemplatePath': self.input_artifact_name + '::' + self.input_template_file
'TemplateConfiguration': self.input_template_configuration,
'TemplatePath': self.input_template_path
},
RunOrder="1"
)

chain_context.template.add_resource(cloud_formation_role)

stage = cumulus.util.tropo.TemplateQuery.get_pipeline_stage_by_name(
stage = cumulus.util.template_query.TemplateQuery.get_pipeline_stage_by_name(
template=chain_context.template,
stage_name=self.stage_name_to_add
)
Expand Down
4 changes: 2 additions & 2 deletions cumulus/steps/dev_tools/code_build_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import cumulus.policies
import cumulus.policies.codebuild
import cumulus.types.codebuild.buildaction
import cumulus.util.tropo
import cumulus.util.template_query

from troposphere import iam,\
codebuild, codepipeline, Ref, ec2
Expand Down Expand Up @@ -107,7 +107,7 @@ def handle(self, chain_context):
chain_context.template.add_resource(project)

template = chain_context.template
stage = cumulus.util.tropo.TemplateQuery.get_pipeline_stage_by_name(
stage = cumulus.util.template_query.TemplateQuery.get_pipeline_stage_by_name(
template=template,
stage_name=self.stage_name_to_add,
)
Expand Down
2 changes: 1 addition & 1 deletion cumulus/steps/dev_tools/pipeline_source_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from cumulus.chain import step
from cumulus.steps.dev_tools import META_PIPELINE_BUCKET_POLICY_REF
from cumulus.types.codebuild.buildaction import SourceS3Action
from cumulus.util.tropo import TemplateQuery
from cumulus.util.template_query import TemplateQuery


class PipelineSourceAction(step.Step):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from cumulus.chain import step
from cumulus.steps.dev_tools import META_PIPELINE_BUCKET_POLICY_REF
from cumulus.types.codebuild.buildaction import SourceCodeCommitAction
from cumulus.util.tropo import TemplateQuery
from cumulus.util.template_query import TemplateQuery


class SourceActionCodeCommit(step.Step):
Expand Down
2 changes: 1 addition & 1 deletion cumulus/steps/dev_tools/pipeline_stage.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from troposphere import codepipeline

from cumulus.chain import step
from cumulus.util.tropo import TemplateQuery
from cumulus.util.template_query import TemplateQuery


class PipelineStage(step.Step):
Expand Down
File renamed without changes.
115 changes: 115 additions & 0 deletions tests/unit/steps/dev_tools/test_cloud_formation_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
try:
# python 3
from unittest.mock import patch # noqa
from unittest.mock import MagicMock
except: # noqa
# python 2
from mock import patch, MagicMock # noqa

import unittest

import troposphere
from troposphere import codepipeline

from cumulus.chain import chaincontext
from cumulus.steps.dev_tools import cloud_formation_action, META_PIPELINE_BUCKET_POLICY_REF
from cumulus.types.cloudformation import action_mode
from cumulus.util.template_query import TemplateQuery


class TestCloudFormationAction(unittest.TestCase):

def setUp(self):
self.context = chaincontext.ChainContext(
template=troposphere.Template(),
instance_name='justtestin'
)
self.context.metadata[META_PIPELINE_BUCKET_POLICY_REF] = "blah"

self.pipeline_name = "ThatPipeline"
self.deploy_stage_name = "DeployIt"
TestCloudFormationAction._add_pipeline_and_stage_to_template(self.context.template, self.pipeline_name, self.deploy_stage_name)

def tearDown(self):
del self.context

@staticmethod
def _add_pipeline_and_stage_to_template(template, pipeline_name, deploy_stage_name):
pipeline = template.add_resource(troposphere.codepipeline.Pipeline(
pipeline_name,
Stages=[]
))

# Not worrying about adding a source stage right now, as the tests are not assumed to actually
# trigger a CloudFormation build of the pipeline

deploy_stage = template.add_resource(troposphere.codepipeline.Stages(
Name=deploy_stage_name,
Actions=[]
))
pipeline.properties['Stages'].append(deploy_stage)

def test_handle_adds_cloud_formation_action_to_stage(self):
action = cloud_formation_action.CloudFormationAction(
action_name="CloudFormation",
input_artifact_names=["InfraInput"],
input_template_path="InfraInput::template.json",
input_template_configuration="InfraInput::myenv.json",
stage_name_to_add=self.deploy_stage_name,
stack_name="my-microservice",
action_mode=action_mode.ActionMode.REPLACE_ON_FAILURE
)

action.handle(self.context)

deploy_stage = TemplateQuery.get_resource_by_type(self.context.template, codepipeline.Stages)[0]
self.assertEqual(len(deploy_stage.Actions), 1)

test_action = deploy_stage.Actions[0]
self.assertEqual(test_action.Name, "CloudFormation")
self.assertEqual(test_action.ActionTypeId.Category, "Deploy")
self.assertEqual(test_action.ActionTypeId.Provider, "CloudFormation")
self.assertEqual(test_action.Configuration['TemplatePath'], "InfraInput::template.json")
self.assertEqual(test_action.Configuration['TemplateConfiguration'], "InfraInput::myenv.json")
self.assertEqual(test_action.Configuration['ActionMode'], action_mode.ActionMode.REPLACE_ON_FAILURE.value)
self.assertEquals(len(test_action.InputArtifacts), 1)
self.assertEquals(test_action.InputArtifacts[0].Name, "InfraInput")

def test_raises_error_if_target_stage_does_not_exist(self):
action = cloud_formation_action.CloudFormationAction(
action_name="CloudFormation",
input_artifact_names=["InfraInput"],
input_template_path="InfraInput::template.json",
input_template_configuration="InfraInput::myenv.json",
stage_name_to_add="ThisStageDoesNotExist",
stack_name="my-microservice",
action_mode=action_mode.ActionMode.REPLACE_ON_FAILURE
)
self.assertRaises(
ValueError,
action.handle,
self.context)

def test_can_add_multiple_input_artifacts(self):
action = cloud_formation_action.CloudFormationAction(
action_name="CloudFormation",
input_artifact_names=["InfraInput", "ParameterInput"],
input_template_path="InfraInput::template.json",
input_template_configuration="ParameterInput::myenv.json",
stage_name_to_add=self.deploy_stage_name,
stack_name="my-microservice",
action_mode=action_mode.ActionMode.REPLACE_ON_FAILURE
)

action.handle(self.context)

deploy_stage = TemplateQuery.get_resource_by_type(self.context.template, codepipeline.Stages)[0]
self.assertEqual(len(deploy_stage.Actions), 1)

test_action = deploy_stage.Actions[0]
self.assertEqual(test_action.Name, "CloudFormation")
self.assertEqual(test_action.Configuration['TemplatePath'], "InfraInput::template.json")
self.assertEqual(test_action.Configuration['TemplateConfiguration'], "ParameterInput::myenv.json")
self.assertEquals(len(test_action.InputArtifacts), 2)
self.assertEquals(test_action.InputArtifacts[0].Name, "InfraInput")
self.assertEquals(test_action.InputArtifacts[1].Name, "ParameterInput")
2 changes: 1 addition & 1 deletion tests/unit/steps/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from cumulus.chain import chaincontext
from cumulus.steps.dev_tools import pipeline, code_build_action
from cumulus.steps.dev_tools.vpc_config import VpcConfig
from cumulus.util.tropo import TemplateQuery
from cumulus.util.template_query import TemplateQuery


class TestPipelineStep(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
import troposphere
import troposphere.s3

from cumulus.util.tropo import TemplateQuery
from cumulus.util.template_query import TemplateQuery


class TestPipelineStep(unittest.TestCase):
class TestTemplateQuery(unittest.TestCase):

def setUp(self):
pass
Expand Down

0 comments on commit 34237f4

Please sign in to comment.