Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Fixed a bug on cfn_delete
Browse files Browse the repository at this point in the history
bug: when deleting an active alias record, it deleted the tagged dns alias record as well
  • Loading branch information
yufangzhang committed Aug 8, 2016
1 parent f34f0f4 commit 1df9b52
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 66 deletions.
108 changes: 55 additions & 53 deletions bootstrap_cfn/fab_tasks.py
Expand Up @@ -15,10 +15,10 @@
from bootstrap_cfn.cloudformation import Cloudformation
from bootstrap_cfn.config import ConfigParser, ProjectConfig
from bootstrap_cfn.elb import ELB
from bootstrap_cfn.errors import ActiveTagExistConflictError, BootstrapCfnError,\
CfnConfigError, CloudResourceNotFoundError, DNSRecordNotFoundError,\
PublicELBNotFoundError, StackRecordNotFoundError, TagRecordExistConflictError,\
TagRecordNotFoundError, UpdateDNSRecordError, ZoneIDNotFoundError
from bootstrap_cfn.errors import (ActiveTagExistConflictError, BootstrapCfnError,
CfnConfigError, CloudResourceNotFoundError, DNSRecordNotFoundError,
PublicELBNotFoundError, StackRecordNotFoundError, TagRecordExistConflictError,
TagRecordNotFoundError, UpdateDNSRecordError, ZoneIDNotFoundError)
from bootstrap_cfn.iam import IAM
from bootstrap_cfn.r53 import R53
from bootstrap_cfn.utils import tail
Expand Down Expand Up @@ -349,9 +349,9 @@ def get_stack_name(new=False):
# For back-compatibility
set_stack_name()

if hasattr(env, 'tag'):
try:
stack_tag = get_env_tag()
else:
except AttributeError:
stack_tag = 'active'
env.tag = stack_tag
if not hasattr(env, 'stack_name'):
Expand All @@ -360,22 +360,20 @@ def get_stack_name(new=False):
env.stack_name = 'temp'
zone_name = get_zone_name()
zone_id = get_zone_id()
if not zone_name:
raise CfnConfigError("No master_zone in yaml, unable to create/find DNS records for stack name")
logger.info("fab_tasks::get_stack_name: Found master zone '{}' in config...".format(zone_name))
logger.info("fab_tasks::get_stack_name: Found master zone '%s' in config...", zone_name)
# get record name in the format of: stack.[stack_tag].[app]-[env]
record_name = get_tag_record_name(stack_tag)
dns_name = "{}.{}".format(record_name, zone_name)
r53_conn = get_connection(R53)
try:
# get stack id
stack_suffix = r53_conn.get_record(zone_name, zone_id, record_name, 'TXT').replace('"', "")
logger.info("fab_tasks::get_stack_name: Found stack suffix '{}' "
"for dns record '{}'... ".format(stack_suffix, dns_name))
logger.info("fab_tasks::get_stack_name: Found stack suffix '%s' "
"for dns record '%s'... ", stack_suffix, dns_name)
legacy_name = get_legacy_name()
env.stack_name = "{0}-{1}".format(legacy_name, stack_suffix)
logger.info("fab_tasks::get_stack_name: Found stack name '{}'...".format(env.stack_name))
except:
logger.info("fab_tasks::get_stack_name: Found stack name '%s'...", env.stack_name)
except Exception:
raise DNSRecordNotFoundError(dns_name)

return env.stack_name
Expand All @@ -401,46 +399,48 @@ def set_stack_name():
zone_name = get_zone_name()
zone_id = get_zone_id()
stack_suffix = uuid.uuid4().__str__()[-8:]
if hasattr(env, 'tag'):
try:
if env.tag == 'active':
raise ActiveTagExistConflictError(stack_suffix)
elif r53_conn.hastag(zone_name, zone_id, get_tag_record_name(env.tag)):
raise TagRecordExistConflictError(env.tag)
else:
stack_tag = get_env_tag()
else:
except AttributeError:
stack_tag = stack_suffix
env.tag = stack_tag
record = "{}.{}".format(get_tag_record_name(stack_tag), zone_name)
logger.info("fab_tasks::set_stack_name: "
"Creating stack suffix {} "
"for record '{}' "
"in zone id '{}'...".format(stack_suffix, record, zone_id))
"Creating stack suffix '%s' "
"for record '%s' "
"in zone id '%s'...", stack_suffix, record, zone_id)
# Let DNS update DNSServerError propogate
try:
r53_conn.update_dns_record(zone_id, record, 'TXT', '"{0}"'.format(stack_suffix))
env.stack_name = "{0}-{1}".format(get_legacy_name(), stack_suffix)
except:
except Exception:
raise UpdateDNSRecordError
return env.stack_name


def get_zone_name():
zone_name = get_basic_config().get('master_zone', None)
if not zone_name:
try:
zone_name = get_basic_config()['master_zone']
except KeyError:
raise CfnConfigError("No master_zone in yaml, unable to create/find DNS records for stack name")
logger.info("fab_tasks::get_zone_id: Found master zone '{}' in config...".format(zone_name))
logger.info("fab_tasks::get_zone_id: Found master zone '%s' in config...", zone_name)
return zone_name


def get_zone_id():
zone_name = get_zone_name()
r53_conn = get_connection(R53)
zone_id = r53_conn.get_hosted_zone_id(zone_name)
if not zone_id:
try:
zone_id = r53_conn.get_hosted_zone_id(zone_name)
except Exception:
raise ZoneIDNotFoundError(zone_name)
logger.info("fab_tasks::get_zone_id: Found zone id '{}' "
"for zone name '{}'...".format(zone_id, zone_name))
logger.info("fab_tasks::get_zone_id: Found zone id '%s' "
"for zone name '%s'...", zone_id, zone_name)
return zone_id


Expand Down Expand Up @@ -540,7 +540,7 @@ def cfn_delete(force=False, pre_delete_callbacks=None):
if not isactive():
# delete inactive stack
stack_tag = get_env_tag()
logger.info("Deleting {} inactive stack {}...".format(stack_tag, stack_name))
logger.info("Deleting '%s' inactive stack '%s'...".format(stack_tag, stack_name))
print green("\nSTACK {0} DELETING...\n").format(stack_name)

# delete Alias and TXT records
Expand All @@ -553,11 +553,15 @@ def cfn_delete(force=False, pre_delete_callbacks=None):
print 'Running in non blocking mode. Exiting.'
sys.exit(0)
tail(cfn, stack_name)

if cfn.stack_missing(stack_name):
print green("Stack successfully deleted")
else:
print red("Stack deletion was unsuccessful")
return False
if 'ssl' in cfn_config.data:
iam = get_connection(IAM)
iam.delete_ssl_certificate(cfn_config.ssl(), stack_name)
else:
# delete active dns records

Expand All @@ -566,9 +570,6 @@ def cfn_delete(force=False, pre_delete_callbacks=None):
txt_tag_record = get_tag_record_name(stack_tag)
r53_conn.delete_record(zone_name, zone_id, elb, stack_id, stack_tag, txt_tag_record)

if 'ssl' in cfn_config.data:
iam = get_connection(IAM)
iam.delete_ssl_certificate(cfn_config.ssl(), stack_name)
return True


Expand All @@ -577,7 +578,11 @@ def get_env_tag():


def isactive():
return hasattr(env, "tag") and env.tag != 'active'
try:
if env.tag == 'active':
return True
except AttributeError:
return False


@task
Expand Down Expand Up @@ -606,7 +611,7 @@ def cfn_create(test=False):
# Inject security groups in stack template and create stacks.
try:
stack = cfn.create(stack_name, cfn_config.process(), tags=get_cloudformation_tags())
except:
except Exception:
# cleanup ssl certificates if any
if 'ssl' in cfn_config.data:
print red("Deleting SSL certificates from stack")
Expand Down Expand Up @@ -781,8 +786,9 @@ def set_active_stack(stack_tag, force=False):
zone_id = get_zone_id()

tag_record = get_tag_record_name(stack_tag)
tag_stack_id = r53_conn.get_record(zone_name, zone_id, tag_record, 'TXT')
if not tag_stack_id:
try:
tag_stack_id = r53_conn.get_record(zone_name, zone_id, tag_record, 'TXT')
except Exception:
raise TagRecordNotFoundError(tag_record)

if get_active_stack() and not force:
Expand All @@ -795,32 +801,28 @@ def set_active_stack(stack_tag, force=False):
r53_conn.update_dns_record(zone_id, "{}.{}".format(active_record, get_zone_name()), 'TXT',
'"{}"'.format(tag_stack_id))
logger.info("fab_tasks::set_active_stack: Successfully updated dns alias record")
except:
except Exception:
raise UpdateDNSRecordError

# get the first public facing elb
elb = get_first_public_elb()
# helloworld.dsd.io
main_record_name = "{}.{}".format(elb, zone_name)
# helloworld-12345.dsd.io
stack_record_name = "{}-{}.{}".format(elb, tag_stack_id, zone_name)
# get the ELB value in stack_record_name's record
record_name = "{}-{}".format(elb, tag_stack_id)
record_object = r53_conn.get_full_record(zone_name, zone_id, record_name, 'A')
record_value = [record_object.alias_hosted_zone_id,
record_object.alias_dns_name,
record_object.alias_evaluate_target_health]
if record_value:
try:
record_object = r53_conn.get_full_record(zone_name, zone_id, record_name, 'A')
record_value = [record_object.alias_hosted_zone_id,
record_object.alias_dns_name,
record_object.alias_evaluate_target_health]
# point [helloworld.dsd.io] to [helloworld-12345.dsd.io]'s ELB
try:
r53_conn.update_dns_record(zone_id, main_record_name, 'A', record_value, is_alias=True)
logger.info("fab_tasks::set_active_stack: Successfully updated dns alias record")
print green("Active stack is switched to {}".format(tag_record))
except:
except Exception:
raise UpdateDNSRecordError
return True
else:
raise StackRecordNotFoundError(stack_record_name)
except Exception:
raise StackRecordNotFoundError(record_name)


@task
Expand All @@ -838,12 +840,12 @@ def get_active_stack():
dns_record_name = '{}-{}'.format(elb, active_stack_id)
dns_record_value = r53_conn.get_record(zone_name, zone_id, dns_record_name, 'A')
main_record_value = r53_conn.get_record(zone_name, zone_id, elb, 'A')
except:
except Exception:
print green("No active stack exists.")
return None
return
if active_stack_id and dns_record_value and dns_record_value == main_record_value:
logger.info("fab_tasks::get_active_stack: "
"Active stack id is: {}".format(active_stack_id))
"Active stack id is: '%s'", active_stack_id)
print green("Active stack id is: {}".format(active_stack_id))
return active_stack_id
else:
Expand All @@ -868,9 +870,9 @@ def get_first_public_elb():
if len(elbs) < 1:
raise PublicELBNotFoundError
elif len(elbs) == 1:
logger.info("fab_tasks::set_active_stack: Found one ELB '{}', "
"using it for public ELB... ".format(elbs[0]))
logger.info("fab_tasks::set_active_stack: Found one ELB '%s', "
"using it for public ELB... ", elbs[0])
else:
logger.info("fab_tasks::set_active_stack: Found multiple ELBs,"
"using the first one '{}' as public ELB".format(elbs[0]))
"using the first one '%s' as public ELB", elbs[0])
return elbs[0]
29 changes: 16 additions & 13 deletions bootstrap_cfn/r53.py
Expand Up @@ -98,24 +98,27 @@ def delete_dns_record(self, zone_id, record_name, record_type, record_value, is_
changes.commit()
return True

def delete_record(self, zone_name, zone_id, elb, stack_id, stack_tag, txt_tag_record):
elb_name = "{}-{}".format(elb, stack_id)
alias_record_object = self.get_full_record(zone_name, zone_id, elb_name, 'A')
def delete_record(self, zone_name, zone_id, elb_name, stack_id, stack_tag, txt_tag_record):
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
if alias_record_object:
alias_record_value = [alias_record_object.alias_hosted_zone_id,
alias_record_object.alias_dns_name,
alias_record_object.alias_evaluate_target_health]
alias_record_name = "{}.{}".format(elb_name, zone_name)
if active_alias_record_object:
if stack_tag == 'active':
# if deleting "active"
# check if this alias record matches active record
active_record_name = "{}-{}".format(elb, stack_id)
active_record_object = self.get_full_record(zone_name, zone_id, active_record_name, 'A')
if active_record_object.to_print() == alias_record_object.to_print():
self.delete_dns_record(zone_id, alias_record_name, 'A', alias_record_value, is_alias=True)
main_alias_record_name = "{}.{}".format(elb_name, zone_name)
main__alias_record_object = self.get_full_record(zone_name, zone_id, elb_name, 'A')
main_alias_record_value = [main__alias_record_object.alias_hosted_zone_id,
main__alias_record_object.alias_dns_name,
main__alias_record_object.alias_evaluate_target_health]
if main__alias_record_object.to_print() == active_alias_record_object.to_print():
self.delete_dns_record(zone_id, main_alias_record_name, 'A', main_alias_record_value, is_alias=True)
else:
self.delete_dns_record(zone_id, alias_record_name, 'A', alias_record_value, is_alias=True)
active_alias_record_value = [active_alias_record_object.alias_hosted_zone_id,
active_alias_record_object.alias_dns_name,
active_alias_record_object.alias_evaluate_target_health]
active_alias_record_name = "{}.{}".format(active_elb_name, zone_name)
self.delete_dns_record(zone_id, active_alias_record_name, 'A', active_alias_record_value, is_alias=True)

# delete TXT record
txt_record_name = "{}.{}".format(txt_tag_record, zone_name)
Expand Down

0 comments on commit 1df9b52

Please sign in to comment.