def create_or_update(config, password, run=True): stack = CFStack(config, password) print('Validating template... ', end='', file=sys.stderr) stack.validate() # raises if defect print('successfull.', file=sys.stderr) if run: if stack.active: print("Updating Stack {}...".format(stack.name), end='', file=sys.stderr) stack.update() print(' done.') else: print("Creating Stack {}...".format(stack.name), end='', file=sys.stderr) stack.create() print(' done.', file=sys.stderr) print_stack_events(stack) return stack # IF WE'RE NOT ACTIVELY CREATING OR UPDATING A STACK, WE'RE SHOWING THE CONFIG INSTEAD: print("This is the configuration we'd use if this was for real:\n\n", file=sys.stderr) if not config['JSON_INDENT']: config['JSON_INDENT'] = 4 template = CFTemplate(config) print(template.create_json()) if config['FILES']: print("\n\nHere are all the files you can use:", file=sys.stderr) for name, path in config['FILES'].iteritems(): print('\t"{}":\t"{}"'.format(name, path), file=sys.stderr) return None
class CFStack(object): def __init__(self, config, password=None): """ A thin layer over boto.connect_cloudformation adding templates. config: the usual dict password_getter: an executable that yields the password or None to use AWS IAM. """ self.conf = config self.password = password self.name = config['STACK_NAME'] self.connection = self.__make_cloud_formation_connection() self.template = CFTemplate(self.conf) def get_stack(self): temp = [x for x in self.list() if x.stack_name == self.name and x.stack_status != 'DELETE_COMPLETE'] if len(temp) > 1: raise CFStackError('Confused, too many stacks found: {}'.format(", ".join([x.stack_id for x in temp]))) if len(temp) == 1: return temp[0] return None def __getattr__(self, item): stack = self.get_stack() if hasattr(stack, item): return getattr(stack, item) def delete(self, stack_name_or_id=None): stack = stack_name_or_id or self.name return self.connection.delete_stack(stack) def list(self): return self.connection.list_stacks() def update(self): return self.connection.update_stack(self.name, template_body=self.template.create_json(), capabilities=self.conf['CAPABILITIES'], tags=self.conf['TAGS']) def create(self): return self.connection.create_stack(self.name, template_body=self.template.create_json(), capabilities=self.conf['CAPABILITIES'], tags=self.conf['TAGS']) @property def active(self): stack = self.get_stack() if stack and stack.stack_status.endswith('_COMPLETE'): return stack return False def get_stack_event(self): stack = self.get_stack() if stack: return self.connection.describe_stack_events(stack.stack_id) return None def get_stack_events(self, interval_wait_time=5): events = set() wait = 0 while wait < 1000: # make sure we're not running endlessly try: event_list = self.connection.describe_stack_events(self.name) except boto.exception.BotoServerError: yield 'DELETE_COMPLETE' break event_list.reverse() # we'd like to see the most recent message last. for event in event_list: if event.event_id not in events: yield event events.add(event.event_id) wait += 1 stack = self.get_stack() if stack.stack_status.endswith('_COMPLETE'): yield stack.stack_status break time.sleep(interval_wait_time) def price(self): return self.connection.estimate_template_cost(template_body=self.template.create_json()) def validate(self): return self.connection.validate_template(template_body=self.template.create_json()) def cancel_update(self): return self.connection.cancel_update_stack(self.name) def __make_cloud_formation_connection(self): cf_region = [x for x in boto.cloudformation.regions() if x.name == self.conf['ENDPOINT']] return boto.connect_cloudformation(self.conf['ACCOUNT'], aws_secret_access_key=self.password, region=cf_region[0])
def test_create_json(self): conf = CFTemplate(self.example_config) created = json.loads(conf.create_json()) self.assertEqual(created, self.test_result)
def test_create_yaml(self): self.example_config['TEMPLATE'] = 'prod.yaml' conf = CFTemplate(self.example_config) created = json.loads(conf.create_json()) self.assertEqual(created, self.test_result)
def test_create_yaml(self): self.example_config["TEMPLATE"] = "prod.yaml" conf = CFTemplate(self.example_config) created = json.loads(conf.create_json()) self.assertEqual(created, self.test_result)