def undeploy(stack_name, region):

    # Disable buffering, from http://stackoverflow.com/questions/107705/disable-output-buffering
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    print("\n\n**** Uneploying stack '" + stack_name + "'")

    # Load previous stack information to see if it has been deployed at all
    describe_stack_command = [ 'aws', 'cloudformation', 'describe-stacks', "--region", region, '--stack-name', stack_name ]
    print("Checking for previous stack info: " + str(describe_stack_command))
    p = subprocess.Popen(describe_stack_command,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    output = p.communicate()
    if p.returncode:
        if (not output[1].endswith("does not exist\n")):
            sys.exit("Failed to retrieve stack for " + stack_name + ": " + output[1])
        print("Stack not deployed, doing nothing.")
        return

    # Dump original status, for the record

    stack_info = aws_infra_util.json_load(output[0])
    status = stack_info['Stacks'][0]['StackStatus']
    print("Status: " + status)

    # Delete stack

    stack_command = \
        ['aws', 'cloudformation', 'delete-stack', "--region", region, '--stack-name',
         stack_name
         ]

    print("Delete stack: " + str(stack_command))
    p = subprocess.Popen(stack_command,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    output = p.communicate()
    if p.returncode:
        sys.exit("Delete stack failed: " + output[1])

    print(output[0])

    # Wait for delete to complete

    print("Waiting for delete stack to complete:")
    while (True):
        p = subprocess.Popen(describe_stack_command,
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
        output = p.communicate()
        if p.returncode:
            if (output[1].endswith("does not exist\n")):
                break
            sys.exit("Describe stack failed: " + output[1])


        stack_info = aws_infra_util.json_load(output[0])
        status = stack_info['Stacks'][0]['StackStatus']
        print("Status: " + status)
        if (not status.endswith("_IN_PROGRESS")):
            sys.exit("Delete stack failed: end state " + status)

        time.sleep(5)

    print("Done!")
def deploy(stack_name, yaml_template, region):

    # Disable buffering, from http://stackoverflow.com/questions/107705/disable-output-buffering
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    # Get AMI metadata
    ami_id = os.getenv('paramAmi', '')
    if ami_id != "":
        describe_ami_command = [ "aws", "ec2", "describe-images", "--region", region, "--image-ids", ami_id ]
        print("Checking AMI " + ami_id + " metadata: " + str(describe_ami_command))
        p = subprocess.Popen(describe_ami_command,
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
        output = p.communicate()
        if p.returncode:
            sys.exit("Failed to retrieve ami metadata for " + ami_id)

        ami_meta = aws_infra_util.json_load(output[0])
        print("Result: " + aws_infra_util.json_save(ami_meta))
        os.environ["paramAmiName"] = ami_meta['Images'][0]['Name']
        os.environ["paramAmiCreated"] = ami_meta['Images'][0]['CreationDate']

    print("\n\n**** Deploying stack '" + stack_name + "' with template '" + yaml_template + "' and ami_id '" + ami_id + "'")

    # Load yaml template and import scripts and patch userdata with metadata hash & params

    template_doc = aws_infra_util.yaml_load(open(yaml_template))
    template_doc = aws_infra_util.import_scripts(template_doc, yaml_template)
    aws_infra_util.patch_launchconf_userdata_with_metadata_hash_and_params(template_doc)

    if "Parameters" not in template_doc:
        template_doc['Parameters'] = []
    template_parameters = template_doc['Parameters']
    if (not "paramAmiName" in template_parameters):
        template_parameters['paramAmiName']    = collections.OrderedDict([("Description", "AMI Name"), ("Type", "String"), ("Default", "")])
    if (not "paramAmiCreated" in template_parameters):
        template_parameters['paramAmiCreated'] = collections.OrderedDict([("Description", "AMI Creation Date"), ("Type", "String"), ("Default", "")])

    json_template = aws_infra_util.json_save(template_doc)
    json_small = aws_infra_util.json_save_small(template_doc)

    # save result

    print("** Final template:")
    print(json_template)
    print("")

    tmp = tempfile.NamedTemporaryFile(delete=False)
    tmp.write(json_small)
    tmp.close()

    # Load previous stack information to see if it has been deployed before

    describe_stack_command = [ 'aws', 'cloudformation', 'describe-stacks', "--region", region, '--stack-name', stack_name ]
    print("Checking for previous stack info: " + str(describe_stack_command))
    p = subprocess.Popen(describe_stack_command,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    output = p.communicate()
    if p.returncode:
        if (not output[1].endswith("does not exist\n")):
            sys.exit("Failed to retrieve old stack for " + stack_name + ": " + output[1])
        stack_oper = 'create-stack'
    else:
        stack_oper = 'update-stack'

        # Dump original status, for the record

        stack_info = aws_infra_util.json_load(output[0])
        status = stack_info['Stacks'][0]['StackStatus']
        print("Status: " + status)

    # Create/update stack

    params_doc = []
    for key in template_parameters.keys():
        if (key in os.environ):
            val = os.environ[key]
            print("Parameter " + key + ": using \033[32;1mCUSTOM value " + val + "\033[m")
            params_doc.append({ 'ParameterKey': key, 'ParameterValue': val })
        else:
            val = template_parameters[key]['Default']
            print("Parameter " + key + ": using default value " + str(val))

    stack_command = \
        ['aws', 'cloudformation', stack_oper, "--region", region, '--stack-name',
         stack_name,
         '--template-body',
         'file://' + tmp.name,
         '--capabilities',
         'CAPABILITY_IAM',
         '--parameters',
         aws_infra_util.json_save(params_doc)
         ]

    currentTimeInCloudWatchFormat = datetime.datetime.utcnow().strftime("%FT%H%%253A%M%%253A%SZ")

    print(stack_oper + ": " + str(stack_command))
    p = subprocess.Popen(stack_command,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    output = p.communicate()
    os.remove(tmp.name)
    if p.returncode:
        sys.exit(stack_oper + " failed: " + output[1])

    print(output[0])

    # Wait for create/update to complete

    cloudWatchNotice = "\nCloudWatch url:  https://console.aws.amazon.com/cloudwatch/home#logEvent:group=instanceDeployment;stream=" + stack_name + ";start=" + currentTimeInCloudWatchFormat + "\n"
    print(cloudWatchNotice)

    print("Waiting for " + stack_oper + " to complete:")
    while (True):
        p = subprocess.Popen(describe_stack_command,
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
        output = p.communicate()
        if p.returncode:
            sys.exit("Describe stack failed: " + output[1])

        stack_info = aws_infra_util.json_load(output[0])
        status = stack_info['Stacks'][0]['StackStatus']
        print("Status: " + status)
        if (not status.endswith("_IN_PROGRESS")):
            break

        time.sleep(5)

    print(cloudWatchNotice)

    if ((stack_oper == "create-stack" and status != "CREATE_COMPLETE") or (stack_oper == "update-stack" and status != "UPDATE_COMPLETE")):
        sys.exit(stack_oper + " failed: end state " + status)

    print("Done!")