From 16edd6a630bed8417bc0e417b6fa55c34a7ffe5f Mon Sep 17 00:00:00 2001 From: yufangzhang Date: Mon, 15 Aug 2016 17:47:59 +0100 Subject: [PATCH] Add get_stack_list add fab task also add a cleanup to cfn_create exception, it may work on blocking mode. --- bootstrap_cfn/config.py | 6 ++++-- bootstrap_cfn/errors.py | 7 ++++--- bootstrap_cfn/fab_tasks.py | 29 ++++++++++++++++++++++++++--- bootstrap_cfn/iam.py | 19 +++++++++++++------ bootstrap_cfn/r53.py | 11 +++++++++++ tests/test_fab_tasks.py | 11 ++++++++++- 6 files changed, 68 insertions(+), 15 deletions(-) diff --git a/bootstrap_cfn/config.py b/bootstrap_cfn/config.py index 82bc841..0364e77 100644 --- a/bootstrap_cfn/config.py +++ b/bootstrap_cfn/config.py @@ -4,6 +4,8 @@ import sys import textwrap +from fabric.colors import green + from troposphere import Base64, FindInMap, GetAZs, GetAtt, Join, Output, Ref, Tags, Template from troposphere.autoscaling import AutoScalingGroup, BlockDeviceMapping, \ EBSBlockDevice, LaunchConfiguration, Tag @@ -554,8 +556,8 @@ def rds(self, template): if 'identifier' in self.data['rds']: # update identifier name self.data['rds']['identifier'] = "{}-{}".format(self.data['rds']['identifier'], self.stack_id) - logging.info("identifier was updated to {}".format(self.data['rds']['identifier'])) - print "identifier was updated to {}".format(self.data['rds']['identifier']) + # logging.info("identifier was updated to {}".format(self.data['rds']['identifier'])) + print green("identifier was updated to {}".format(self.data['rds']['identifier'])) # TEST FOR REQUIRED FIELDS AND EXIT IF MISSING ANY for yaml_key, rds_prop in required_fields.iteritems(): diff --git a/bootstrap_cfn/errors.py b/bootstrap_cfn/errors.py index 755da7f..7bde144 100644 --- a/bootstrap_cfn/errors.py +++ b/bootstrap_cfn/errors.py @@ -81,13 +81,14 @@ def __init__(self, autoscaling_group, expected_instance_count, instances): class TagRecordExistConflictError(BootstrapCfnError): def __init__(self, stack_tag): - msg = ("An {} record already exists. ".format(stack_tag)) + msg = ("An {0} record already exists. Please specify another tag. " + "Or can run 'fab tag:{0} cfn_delete' to delete the stack".format(stack_tag)) super(TagRecordExistConflictError, self).__init__(msg) class ActiveTagExistConflictError(BootstrapCfnError): - def __init__(self, stack_id): - msg = ("An active record already exists in {}. ".format(stack_id)) + def __init__(self): + msg = "'active' tag is reserved. Please specify anther one." super(ActiveTagExistConflictError, self).__init__(msg) diff --git a/bootstrap_cfn/fab_tasks.py b/bootstrap_cfn/fab_tasks.py index d116970..f24ad01 100755 --- a/bootstrap_cfn/fab_tasks.py +++ b/bootstrap_cfn/fab_tasks.py @@ -2,9 +2,11 @@ import logging import os +import re import sys import uuid +import boto.exception import boto3 from fabric.api import env, task @@ -401,14 +403,17 @@ def set_stack_name(): stack_suffix = uuid.uuid4().__str__()[-8:] try: if env.tag == 'active': - raise ActiveTagExistConflictError(stack_suffix) + # print red("'Active' tag is reserved, please change a tag. ") + raise ActiveTagExistConflictError() elif r53_conn.hastag(zone_name, zone_id, get_tag_record_name(env.tag)): + # print red("{} exists, please change a tag. ".format(env.tag)) raise TagRecordExistConflictError(env.tag) else: stack_tag = get_env_tag() except AttributeError: stack_tag = stack_suffix env.tag = stack_tag + print green("Stack tag is set to {0}".format(stack_tag)) record = "{}.{}".format(get_tag_record_name(stack_tag), zone_name) logger.info("fab_tasks::set_stack_name: " "Creating stack suffix '%s' " @@ -559,9 +564,14 @@ def cfn_delete(force=False, pre_delete_callbacks=None): else: print red("Stack deletion was unsuccessful") return False - if 'ssl' in cfn_config.data: + # cleanup ssl if exists + # currently we read ssl from configuration file instead of from AWS + # this can cause some mismatch when local config file has been changed.s + try: iam = get_connection(IAM) iam.delete_ssl_certificate(cfn_config.ssl(), stack_name) + except AttributeError, boto.exception: + print green("ssl did not exist") else: # delete active dns records @@ -617,10 +627,10 @@ def cfn_create(test=False): print red("Deleting SSL certificates from stack") iam.delete_ssl_certificate(cfn_config.ssl(), stack_name) import traceback + cfn_delete(True) abort(red("Failed to create: {error}".format(error=traceback.format_exc()))) print green("\nSTACK {0} CREATING...\n").format(stack_name) - if not env.blocking: print 'Running in non blocking mode. Exiting.' sys.exit(0) @@ -876,3 +886,16 @@ def get_first_public_elb(): logger.info("fab_tasks::set_active_stack: Found multiple ELBs," "using the first one '%s' as public ELB", elbs[0]) return elbs[0] + + +@task +def get_stack_list(): + r53_conn = get_connection(R53) + rrsets = r53_conn.get_all_resource_records(get_zone_id()) + regex = "stack\.\w+\.{}.+".format(env.application) + print regex + for rr in rrsets: + if re.match(regex, rr.name): + stack_id = rr.resource_records[0][1:-1] + stack_name = "{}-{}-{}".format(env.application, env.environment, stack_id) + print green("DNS record name: {} stack name: {}".format(rr.name.ljust(40), stack_name.ljust(40))) diff --git a/bootstrap_cfn/iam.py b/bootstrap_cfn/iam.py index ecbde5c..47fb0a9 100644 --- a/bootstrap_cfn/iam.py +++ b/bootstrap_cfn/iam.py @@ -116,12 +116,19 @@ def get_remote_certificate(self, cert_name, stack_name): return remote_cert_data # Handle any problems connecting to the remote AWS except AWSQueryConnection.ResponseError as error: - logging.info("IAM::get_remote_certificate: " - "Could not find certificate '%s': " - "Error %s - %s" % (cert_id, - error.status, - error.reason)) - return None + logging.info("IAM::get_remote_certificate: " + "Could not find certificate '%s': " + "Error %s - %s" % (cert_id, + error.status, + error.reason)) + return None + except Exception as e: + logging.info("IAM::get_remote_certificate: " + "Could not find certificate '%s': " + "Error %s - %s" % (cert_id, + e.status, + e.reason)) + return def compare_remote_certificate_data(self, cert_name, stack_name, ssl_data): """ diff --git a/bootstrap_cfn/r53.py b/bootstrap_cfn/r53.py index 6250a2d..dd7b408 100644 --- a/bootstrap_cfn/r53.py +++ b/bootstrap_cfn/r53.py @@ -99,6 +99,17 @@ def delete_dns_record(self, zone_id, record_name, record_type, record_value, is_ return True def delete_record(self, zone_name, zone_id, elb_name, stack_id, stack_tag, txt_tag_record): + ''' + Delete "active" or tagged Alias and TXT records if they exist + Args: + elb_name: Alias record name, [elbname]-[stackid].dsd.io or [elbname].dsd.io + stack_id: + stack_tag: + txt_tag_record: "TXT" record name + + Returns: + + ''' active_elb_name = "{}-{}".format(elb_name, stack_id) active_alias_record_object = self.get_full_record(zone_name, zone_id, active_elb_name, 'A') # delete Alias record diff --git a/tests/test_fab_tasks.py b/tests/test_fab_tasks.py index 73f03b6..a8a77f4 100644 --- a/tests/test_fab_tasks.py +++ b/tests/test_fab_tasks.py @@ -22,7 +22,16 @@ def set_up_basic_config(): 'name': 'unittest', 'scheme': 'internet-facing'}], 'rds': {}, - 's3': {}} + 's3': {}, + 'ssl': {'dsd.io': {'cert': ('-----BEGIN CERTIFICATE-----' + 'CHAIN2CHAIN2CHAIN2CHAIN2CHA' + '-----END CERTIFICATE-----'), + 'key': ('-----BEGIN CERTIFICATE-----' + 'CHAIN2CHAIN2CHAIN2CHAIN2CHA' + '-----END CERTIFICATE-----') + } + } + } return yaml.dump(basic_config)