diff --git a/CHANGES.md b/CHANGES.md index 018ef07..06bf04d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changelog +## 0.7.0 +- Add support for SNS Notifcations to Cloudformation create and update operations +- Refactored recursive handling of options withing stack groups + ## 0.6.2 - Fixed custom root directory to allow automated template tests diff --git a/cloudbender/__init__.py b/cloudbender/__init__.py index 71c46d8..74b9305 100644 --- a/cloudbender/__init__.py +++ b/cloudbender/__init__.py @@ -2,7 +2,7 @@ import logging __author__ = "Stefan Reimer" __email__ = "stefan@zero-downtimet.net" -__version__ = "0.6.2" +__version__ = "0.7.0" # Set up logging to ``/dev/null`` like a library is supposed to. diff --git a/cloudbender/stack.py b/cloudbender/stack.py index f9add4d..0b19aa6 100644 --- a/cloudbender/stack.py +++ b/cloudbender/stack.py @@ -33,17 +33,23 @@ class StackStatus(object): class Stack(object): - def __init__(self, name, path, rel_path, tags=None, parameters=None, options=None, region='global', profile=None, template=None, ctx={}): - self.id = (profile, region, name) + def __init__(self, name, template, path, rel_path, sg_config={}, ctx={}): self.stackname = name + self.template = template self.path = path self.rel_path = rel_path - self.tags = tags - self.parameters = parameters - self.options = options - self.region = region - self.profile = profile - self.template = template + self.ctx = ctx + + self.tags = sg_config.get('tags', {}) + self.parameters = sg_config.get('parameters', {}) + self.options = sg_config.get('options', {}) + self.region = sg_config.get('region', 'global') + self.profile = sg_config.get('profile', '') + self.onfailure = sg_config.get('onfailure', "DELETE") + self.notfication_sns = sg_config.get('notification_sns', []) + + self.id = (self.profile, self.region, self.stackname) + self.md5 = None self.mode = 'CloudBender' self.provides = template @@ -51,19 +57,17 @@ class Stack(object): self.cfn_parameters = [] self.cfn_data = None self.connection_manager = BotoConnection(self.profile, self.region) - self.ctx = ctx self.status = None self.dependencies = set() self.default_lock = None self.multi_delete = True - self.onfailure = "DELETE" def dump_config(self): logger.debug("".format(self.id, vars(self))) def read_config(self): _config = read_config_file(self.path) - for p in ["region", "stackname", "template", "default_lock", "multi_delete", "provides", "onfailure"]: + for p in ["region", "stackname", "template", "default_lock", "multi_delete", "provides", "onfailure", "notification_sns"]: if p in _config: setattr(self, p, _config[p]) @@ -345,6 +349,7 @@ class Stack(object): 'TemplateBody': self.cfn_template, 'Parameters': self.cfn_parameters, 'OnFailure': self.onfailure, + 'NotificationARNs': self.notfication_sns, 'Tags': [{"Key": str(k), "Value": str(v)} for k, v in self.tags.items()], 'Capabilities': ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND']}, profile=self.profile, region=self.region) @@ -366,6 +371,7 @@ class Stack(object): {'StackName': self.stackname, 'TemplateBody': self.cfn_template, 'Parameters': self.cfn_parameters, + 'NotificationARNs': self.notfication_sns, 'Tags': [{"Key": str(k), "Value": str(v)} for k, v in self.tags.items()], 'Capabilities': ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND']}, profile=self.profile, region=self.region) diff --git a/cloudbender/stackgroup.py b/cloudbender/stackgroup.py index 3ef5e38..721c71b 100644 --- a/cloudbender/stackgroup.py +++ b/cloudbender/stackgroup.py @@ -26,7 +26,7 @@ class StackGroup(object): for sg in self.sgs: sg.dump_config() - logger.debug("".format(self.name, vars(self))) + logger.debug("".format(self.name, self.config)) for s in self.stacks: s.dump_config() @@ -46,14 +46,9 @@ class StackGroup(object): self.name = os.path.split(self.path)[1] # Merge config with parent config - _config = dict_merge(parent_config, _config) + self.config = dict_merge(parent_config, _config) - tags = _config.get('tags', {}) - parameters = _config.get('parameters', {}) - options = _config.get('options', {}) - region = _config.get('region', 'global') - profile = _config.get('profile', '') - stackname_prefix = _config.get('stacknameprefix', '') + stackname_prefix = self.config.get('stacknameprefix', '') logger.debug("StackGroup {} added.".format(self.name)) @@ -65,23 +60,17 @@ class StackGroup(object): if stackname_prefix: stackname = stackname_prefix + stackname - new_stack = Stack( - name=stackname, template=template, path=stack_path, rel_path=str(self.rel_path), - tags=dict(tags), parameters=dict(parameters), options=dict(options), - region=str(region), profile=str(profile), ctx=self.ctx) + new_stack = Stack(name=stackname, template=template, path=stack_path, rel_path=str(self.rel_path), sg_config=self.config, ctx=self.ctx) new_stack.read_config() self.stacks.append(new_stack) # Create StackGroups recursively for sub_group in [f.path for f in os.scandir(self.path) if f.is_dir()]: sg = StackGroup(sub_group, self.ctx) - sg.read_config(_config) + sg.read_config(self.config) self.sgs.append(sg) - # Return raw, merged config to parent - return _config - def get_stacks(self, name=None, recursive=True, match_by='name'): """ Returns [stack] matching stack_name or [all] """ stacks = []