Exemplo n.º 1
0
def create_deployment_manifest(method):
    """Returns a dict describing the current deployable."""
    service_info = get_service_info()

    commit = get_commit()
    version = get_git_version()

    info = {
        'deployable': service_info['name'],
        'method': method,
        'username': getpass.getuser(),
        'datetime': datetime.utcnow().isoformat(),
        'branch': get_branch(),
        'commit': commit,
        'commit_url': get_repo_url() + "/commit/" + commit,
        'release': version['tag'] if version else 'untagged-branch'
    }

    return info
Exemplo n.º 2
0
def create_deployment_manifest(method, comment=None):
    """Returns a dict describing the current deployable."""

    git_version = get_git_version()
    git_commit = get_commit()

    info = {
        'method': method,
        'deployable': get_app_name(),
        'version': get_app_version(),
        'username': getpass.getuser(),
        'comment': comment,
        'datetime': datetime.utcnow().isoformat(),
        'git_branch': get_branch(),
        'git_commit': git_commit,
        'git_commit_url': get_repo_url() + "/commit/" + git_commit,
        'git_release':
        git_version['tag'] if git_version else 'untagged-branch',
    }

    return info
Exemplo n.º 3
0
def _bake_command(args):
    if args.ubuntu:
        name = UBUNTU_BASE_IMAGE_NAME
    else:
        name = get_app_name()

    name = get_app_name()
    tier_name = get_tier_name()
    conf = get_drift_config(tier_name=tier_name,
                            deployable_name=name,
                            drift_app=load_flask_config())

    domain = conf.domain.get()
    aws_region = domain['aws']['ami_baking_region']
    ec2 = boto3.resource('ec2', region_name=aws_region)

    print "DOMAIN:\n", json.dumps(domain, indent=4)
    if not args.ubuntu:
        print "DEPLOYABLE:", name
    print "AWS REGION:", aws_region

    # Create a list of all regions that are active
    if args.ubuntu:
        # Get all Ubuntu images from the appropriate region and pick the most recent one.
        # The 'Canonical' owner. This organization maintains the Ubuntu AMI's on AWS.
        print "Finding the latest AMI on AWS that matches", UBUNTU_RELEASE
        filters = [
            {
                'Name': 'name',
                'Values': [UBUNTU_RELEASE]
            },
        ]
        amis = list(
            ec2.images.filter(Owners=[AMI_OWNER_CANONICAL], Filters=filters))
        if not amis:
            print "No AMI found matching '{}'. Not sure what to do now.".format(
                UBUNTU_RELEASE)
            sys.exit(1)
        ami = max(amis, key=operator.attrgetter("creation_date"))
    else:
        filters = [
            {
                'Name': 'tag:service-name',
                'Values': [UBUNTU_BASE_IMAGE_NAME]
            },
            {
                'Name': 'tag:domain-name',
                'Values': [domain['domain_name']]
            },
        ]
        amis = list(ec2.images.filter(Owners=['self'], Filters=filters))
        if not amis:
            criteria = {d['Name']: d['Values'][0] for d in filters}
            print "No '{}' AMI found using the search criteria {}.".format(
                UBUNTU_BASE_IMAGE_NAME, criteria)
            print "Bake one using this command: {} ami bake --ubuntu".format(
                sys.argv[0])

            sys.exit(1)
        ami = max(amis, key=operator.attrgetter("creation_date"))

    print "Using source AMI:"
    print "\tID:\t", ami.id
    print "\tName:\t", ami.name
    print "\tDate:\t", ami.creation_date

    if args.ubuntu:
        manifest = None
        packer_vars = {
            'setup_script':
            pkg_resources.resource_filename(__name__, "ubuntu-packer.sh"),
            'ubuntu_release':
            UBUNTU_RELEASE,
        }
    else:
        current_branch = get_branch()
        if not args.tag:
            args.tag = current_branch

        print "Using branch/tag", args.tag

        # Wrap git branch modification in RAII.
        checkout(args.tag)
        try:
            setup_script = ""
            setup_script_custom = ""
            with open(
                    pkg_resources.resource_filename(__name__,
                                                    "driftapp-packer.sh"),
                    'r') as f:
                setup_script = f.read()
            custom_script_name = os.path.join(conf.drift_app['app_root'],
                                              'scripts', 'ami-bake.sh')
            if os.path.exists(custom_script_name):
                print "Using custom bake shell script", custom_script_name
                setup_script_custom = "echo Executing custom bake shell script from {}\n".format(
                    custom_script_name)
                setup_script_custom += open(custom_script_name, 'r').read()
                setup_script_custom += "\necho Custom bake shell script completed\n"
            else:
                print "Note: No custom ami-bake.sh script found for this application."
            # custom setup needs to happen first because we might be installing some requirements for the regular setup
            setup_script = setup_script_custom + setup_script
            tf = tempfile.NamedTemporaryFile(delete=False)
            tf.write(setup_script)
            tf.close()
            setup_script_filename = tf.name
            manifest = create_deployment_manifest('ami', comment=None)
            packer_vars = {
                'version': get_app_version(),
                'setup_script': setup_script_filename,
            }

            if not args.preview:
                cmd = ['python', 'setup.py', 'sdist', '--formats=zip']
                ret = subprocess.call(cmd)
                if ret != 0:
                    print "Failed to execute build command:", cmd
                    sys.exit(ret)

                cmd = ["zip", "-r", "dist/aws.zip", "aws"]
                ret = subprocess.call(cmd)
                if ret != 0:
                    print "Failed to execute build command:", cmd
                    sys.exit(ret)
        finally:
            print "Reverting to ", current_branch
            checkout(current_branch)

    user = boto.iam.connect_to_region(
        aws_region).get_user()  # The current IAM user running this command

    packer_vars.update({
        "service": name,
        "region": aws_region,
        "source_ami": ami.id,
        "user_name": user.user_name,
        "domain_name": domain['domain_name'],
    })

    print "Packer variables:\n", pretty(packer_vars)

    # See if Packer is installed and generate sensible error code if something is off.
    # This will also write the Packer version to the terminal which is useful info.
    try:
        subprocess.call(['packer', 'version'],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)
    except Exception as e:
        print "Error:", e
        print "'packer version' command failed. Please install it if it's missing."
        sys.exit(127)

    cmd = "packer build "
    if args.debug:
        cmd += "-debug "

    cmd += "-only=amazon-ebs "
    for k, v in packer_vars.iteritems():
        cmd += "-var {}=\"{}\" ".format(k, v)

    # Use generic packer script if project doesn't specify one
    pkg_resources.cleanup_resources()
    if args.ubuntu:
        scriptfile = pkg_resources.resource_filename(__name__,
                                                     "ubuntu-packer.json")
        cmd += scriptfile
    elif os.path.exists("config/packer.json"):
        cmd += "config/packer.json"
    else:
        scriptfile = pkg_resources.resource_filename(__name__,
                                                     "driftapp-packer.json")
        cmd += scriptfile

    print "Baking AMI with: {}".format(cmd)
    if args.preview:
        print "Not building or packaging because --preview is on. Exiting now."
        return

    start_time = time.time()
    try:
        # Execute Packer command and parse the output to find the ami id.
        p = subprocess.Popen(shlex.split(cmd),
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        while True:
            line = p.stdout.readline()
            print line,
            if line == '' and p.poll() is not None:
                break

            # The last lines from the packer execution look like this:
            # ==> Builds finished. The artifacts of successful builds are:
            # --> amazon-ebs: AMIs were created:
            #
            # eu-west-1: ami-0ee5eb68
            if 'ami-' in line:
                ami_id = line[line.rfind('ami-'):].strip()
                ami = ec2.Image(ami_id)
                print ""
                print "AMI ID: %s" % ami.id
                print ""
    finally:
        pkg_resources.cleanup_resources()

    if p.returncode != 0:
        print "Failed to execute packer command:", cmd
        sys.exit(p.returncode)

    duration = time.time() - start_time

    if manifest:
        print "Adding manifest tags to AMI:"
        pretty(manifest)
        prefix = "drift:manifest:"
        tags = []
        for k, v in manifest.iteritems():
            tag_name = "{}{}".format(prefix, k)
            tags.append({'Key': tag_name, 'Value': v or ''})
        ami.create_tags(DryRun=False, Tags=tags)

    if not args.skipcopy:
        _copy_image(ami.id)

    print "Done after %.0f seconds" % (duration)
    slackbot.post_message(
        "Successfully baked a new AMI for '{}' in %.0f seconds".format(
            name, duration))
Exemplo n.º 4
0
def _bake_command(args):
    service_info = get_service_info()
    tier_config = get_tier_config()
    iam_conn = boto.iam.connect_to_region(tier_config["region"])

    if args.ubuntu:
        # Get all Ubuntu Trusty 14.04 images from the appropriate region and
        # pick the most recent one.
        # The 'Canonical' owner. This organization maintains the Ubuntu AMI's on AWS.
        print "Finding the latest AMI on AWS that matches", UBUNTU_RELEASE
        ec2 = boto3.resource('ec2', region_name=tier_config["region"])
        filters = [
            {
                'Name': 'name',
                'Values': [UBUNTU_RELEASE]
            },
        ]
        amis = list(
            ec2.images.filter(Owners=[AMI_OWNER_CANONICAL], Filters=filters))
        if not amis:
            print "No AMI found matching '{}'. Not sure what to do now.".format(
                UBUNTU_RELEASE, tier_config["tier"], sys.argv[0])
            sys.exit(1)
        ami = max(amis, key=operator.attrgetter("creation_date"))
    else:
        ec2 = boto3.resource('ec2', region_name=tier_config["region"])
        filters = [
            {
                'Name': 'tag:service-name',
                'Values': [UBUNTU_BASE_IMAGE_NAME]
            },
            {
                'Name': 'tag:tier',
                'Values': [tier_config["tier"]]
            },
        ]
        amis = list(ec2.images.filter(Owners=['self'], Filters=filters))
        if not amis:
            print "No '{}' AMI found for tier {}. Bake one using this command: {} ami bake --ubuntu".format(
                UBUNTU_BASE_IMAGE_NAME, tier_config["tier"], sys.argv[0])
            sys.exit(1)
        ami = max(amis, key=operator.attrgetter("creation_date"))

    print "Using source AMI:"
    print "\tID:\t", ami.id
    print "\tName:\t", ami.name
    print "\tDate:\t", ami.creation_date

    if args.ubuntu:
        version = None
        branch = ''
        sha_commit = ''
        deployment_manifest = create_deployment_manifest(
            'bakeami')  # Todo: Should be elsewhere or different
    else:
        cmd = "python setup.py sdist --formats=zip"
        current_branch = get_branch()

        if not args.tag:
            # See if service is tagged to a specific version for this tier
            for si in tier_config['deployables']:
                if si['name'] == service_info['name']:
                    if 'release' in si:
                        text = "Error: As deployable '{}' for tier '{}' is pegged to a particular " \
                            "release, you must specify a release tag to which to bake from.\n" \
                            "Note that this is merely a safety measure.\n" \
                            "For reference, the current deployable for this tier is pegged at " \
                            "release tag '{}'."
                        print text.format(service_info['name'],
                                          tier_config['tier'], si['release'])
                        sys.exit(1)
                    break

        if not args.tag:
            args.tag = current_branch

        print "Using branch/tag", args.tag

        checkout(args.tag)
        try:
            deployment_manifest = create_deployment_manifest(
                'bakeami')  # Todo: Should be elsewhere or different
            sha_commit = get_commit()
            branch = get_branch()
            version = get_git_version()
            service_info = get_service_info()
            if not args.preview:
                os.system(cmd)
        finally:
            print "Reverting to ", current_branch
            checkout(current_branch)

    if not version:
        version = {'tag': 'untagged-branch'}

    print "git version:", version

    user = iam_conn.get_user()  # The current IAM user running this command

    # Need to generate a pre-signed url to the tiers root config file on S3
    tiers_config = get_tiers_config()
    tiers_config_url = '{}/{}.{}/{}'.format(tiers_config['region'],
                                            tiers_config['bucket'],
                                            tiers_config['domain'],
                                            TIERS_CONFIG_FILENAME)

    var = {
        "service":
        UBUNTU_BASE_IMAGE_NAME if args.ubuntu else service_info["name"],
        "versionNumber": service_info["version"],
        "region": tier_config["region"],
        "source_ami": ami.id,
        "branch": branch,
        "commit": sha_commit,
        "release": version['tag'],
        "user_name": user.user_name,
        "tier": tier_config["tier"],
        "tier_url": tiers_config_url,
    }

    if args.ubuntu:
        var['setup_script'] = pkg_resources.resource_filename(
            __name__, "ubuntu-packer.sh")
    else:
        var['setup_script'] = pkg_resources.resource_filename(
            __name__, "driftapp-packer.sh")

    print "Using var:\n", pretty(var)

    packer_cmd = "packer"
    try:
        result = subprocess.call(packer_cmd,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
    except Exception as e:
        print "Error:", e
        print "%s was not found. Please install using the following method:" % packer_cmd
        print "  brew tap homebrew/binary\n  brew install %s" % packer_cmd
        sys.exit(1)
    else:
        print "Packer process returned", result

    cmd = "%s build " % packer_cmd
    if args.debug:
        cmd += "-debug "

    cmd += "-only=amazon-ebs "
    for k, v in var.iteritems():
        cmd += "-var {}=\"{}\" ".format(k, v)

    # Use generic packer script if project doesn't specify one
    pkg_resources.cleanup_resources()
    if args.ubuntu:
        scriptfile = pkg_resources.resource_filename(__name__,
                                                     "ubuntu-packer.json")
        cmd += scriptfile
    elif os.path.exists("config/packer.json"):
        cmd += "config/packer.json"
    else:
        scriptfile = pkg_resources.resource_filename(__name__,
                                                     "driftapp-packer.json")
        cmd += scriptfile
    print "Baking AMI with: {}".format(cmd)

    # Dump deployment manifest into dist folder temporarily. The packer script
    # will pick it up and bake it into the AMI.
    deployment_manifest_filename = os.path.join("dist",
                                                "deployment-manifest.json")
    deployment_manifest_json = json.dumps(deployment_manifest, indent=4)
    print "Deployment Manifest:\n", deployment_manifest_json

    if args.preview:
        print "Not building or packaging because --preview is on. Exiting now."
        return

    with open(deployment_manifest_filename, "w") as dif:
        dif.write(deployment_manifest_json)

    start_time = time.time()
    try:
        os.system(cmd)
    finally:
        os.remove(deployment_manifest_filename)
        pkg_resources.cleanup_resources()
    duration = time.time() - start_time
    print "Done after %.0f seconds" % (duration)
    slackbot.post_message(
        "Successfully baked a new AMI for '{}' on tier '{}' in %.0f seconds".
        format(service_info["name"], get_tier_name(), duration))
Exemplo n.º 5
0
def run_command(args):
    service_info = get_service_info()
    tier_config = get_tier_config()
    ec2_conn = boto.ec2.connect_to_region(tier_config["region"])
    iam_conn = boto.iam.connect_to_region(tier_config["region"])

    if args.ubuntu:
        # Get all Ubuntu Trusty 14.04 images from the appropriate region and
        # pick the most recent one.
        print "Finding the latest AMI on AWS that matches 'ubuntu-trusty-14.04*'"
        # The 'Canonical' owner. This organization maintains the Ubuntu AMI's on AWS.
        amis = ec2_conn.get_all_images(
            owners=['099720109477'],
            filters={'name': 'ubuntu/images/hvm/ubuntu-trusty-14.04*'},
        )
        ami = max(amis, key=operator.attrgetter("creationDate"))
    else:

        amis = ec2_conn.get_all_images(
            owners=['self'],  # The current organization
            filters={
                'tag:service-name': UBUNTU_BASE_IMAGE_NAME,
                'tag:tier': tier_config["tier"],
            },
        )
        if not amis:
            print "No '{}' AMI found for tier {}. Bake one using this command: {} bakeami --ubuntu".format(
                UBUNTU_BASE_IMAGE_NAME, tier_config["tier"], sys.argv[0])
            sys.exit(1)

        ami = max(amis, key=operator.attrgetter("creationDate"))
        print "{} AMI(s) found.".format(len(amis))

    print "Using source AMI:"
    print "\tID:\t", ami.id
    print "\tName:\t", ami.name
    print "\tDate:\t", ami.creationDate

    if args.ubuntu:
        version = None
        branch = ''
        sha_commit = ''
    else:
        cmd = "python setup.py sdist --formats=zip"
        current_branch = get_branch()
        if not args.tag:
            args.tag = current_branch

        print "Using branch/tag", args.tag
        checkout(args.tag)
        try:
            sha_commit = get_commit()
            branch = get_branch()
            version = get_git_version()
            if not args.preview:
                os.system(cmd)
        finally:
            print "Reverting to ", current_branch
            checkout(current_branch)

    if not version:
        version = {'tag': 'untagged-branch'}

    print "git version:", version

    service_info = get_service_info()
    user = iam_conn.get_user()  # The current IAM user running this command

    # Need to generate a pre-signed url to the tiers root config file on S3
    tiers_config = get_tiers_config()
    tiers_config_url = '{}/{}.{}/{}'.format(tiers_config['region'],
                                            tiers_config['bucket'],
                                            tiers_config['domain'],
                                            TIERS_CONFIG_FILENAME)

    var = {
        "service":
        UBUNTU_BASE_IMAGE_NAME if args.ubuntu else service_info["name"],
        "versionNumber": service_info["version"],
        "region": tier_config["region"],
        "source_ami": str(ami.id),
        "branch": branch,
        "commit": sha_commit,
        "release": version['tag'],
        "user_name": str(user.user_name),
        "tier": tier_config["tier"],
        "tier_url": str(tiers_config_url),
    }

    if args.ubuntu:
        var['setup_script'] = pkg_resources.resource_filename(
            __name__, "ubuntu-packer.sh")
    else:
        var['setup_script'] = pkg_resources.resource_filename(
            __name__, "driftapp-packer.sh")

    print "Using var:\n", json.dumps({k: str(v)
                                      for k, v in var.iteritems()},
                                     indent=4)

    packer_cmd = "packer"
    try:
        result = subprocess.call(packer_cmd,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
    except Exception as e:
        print "Error:", e
        print "%s was not found. Please install using the following method:" % packer_cmd
        print "  brew tap homebrew/binary\n  brew install %s" % packer_cmd
        sys.exit(1)
    else:
        print "Packer process returned", result

    cmd = "%s build " % packer_cmd
    if args.debug:
        cmd += "-debug "

    cmd += "-only=amazon-ebs "
    for k, v in var.iteritems():
        cmd += "-var {}=\"{}\" ".format(k, v)

    # Use generic packer script if project doesn't specify one
    pkg_resources.cleanup_resources()
    if args.ubuntu:
        scriptfile = pkg_resources.resource_filename(__name__,
                                                     "ubuntu-packer.json")
        cmd += scriptfile
    elif os.path.exists("config/packer.json"):
        cmd += "config/packer.json"
    else:
        scriptfile = pkg_resources.resource_filename(__name__,
                                                     "driftapp-packer.json")
        cmd += scriptfile
    print "Baking AMI with: {}".format(cmd)

    if args.preview:
        print "Not building or packaging because --preview is on. Exiting now."
        return

    start_time = time.time()
    # Dump deployment manifest into dist folder temporarily. The packer script
    # will pick it up and bake it into the AMI.
    deployment_manifest_filename = os.path.join("dist",
                                                "deployment-manifest.json")
    deployment_manifest_json = json.dumps(
        create_deployment_manifest('bakeami'), indent=4)
    print "Deployment Manifest:\n", deployment_manifest_json
    with open(deployment_manifest_filename, "w") as dif:
        dif.write(deployment_manifest_json)

    try:
        os.system(cmd)
    finally:
        os.remove(deployment_manifest_filename)
        pkg_resources.cleanup_resources()
    duration = time.time() - start_time
    print "Done after %.0f seconds" % (duration)
    slackbot.post_message(
        "Successfully baked a new AMI for '{}' on tier '{}' in %.0f seconds".
        format(service_info["name"], get_tier_name(), duration))