Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
hubboss / boss / aws_utils.py
Size: Mime:
import os
import sys
import subprocess

from ConfigParser import SafeConfigParser
from boto import ec2

from . import utils
from . import hosts

GROUP_TO_STACK = dict(
    aws_prod='us-east-1',
    aws_stage='stage-us-east-1-150610-005505',
)

STACK_TO_REGION = {
    'us-east-1': 'us-east-1',
    'stage-us-east-1': 'us-east-1',
    'stage-us-east-1-150610-005505': 'us-east-1',
}


class AWSCredsProvider(object):
    DESCRIPTION = None

    def __init__(self, stack_name):
        self.stack = stack_name
        self.region = STACK_TO_REGION[stack_name]

    @property
    def access_key(self):
        raise NotImplementedError

    @property
    def secret(self):
        raise NotImplementedError

    @classmethod
    def get_provider(cls, stack_name):
        excs = []
        for kls in (PassCredsProvider, EnvCredsProvider, AWSCLIProvider):
            try:
                prov = kls(stack_name)
                if None not in (prov.access_key, prov.secret):
                    print >>sys.stderr, "Using AWS credentials from {} for stack {}".format(
                        prov.DESCRIPTION, prov.stack
                    )
                    return prov
            except Exception:
                excs.append((sys.exc_info()))

        if len(excs) == 1:
            raise excs[0]
        else:
            raise Exception(
                "No AWS credentials could be found. " +
                "The following errors occurred:\n" +
                ('\n'.join(e[0].message for e in excs) or
                 'No credentials provider found')
            )


class PassCredsProvider(AWSCredsProvider):
    DESCRIPTION = '`pass` password manager'

    PASS_LOCATION = 'dev/teams/hub/apps/hubboss/'
    ACCESS_KEY_NAME = 'aws_access_key'
    SECRET_KEY_NAME = 'aws_secret_access_key'

    def __init__(self, stack_name):
        AWSCredsProvider.__init__(self, stack_name)
        self._secret = None
        self._access_key = None

    @property
    def access_key(self):
        if getattr(self, '_access_key', None) is None:
            self._access_key = self._get_pass_entry(self.ACCESS_KEY_NAME)
        return self._access_key

    @property
    def secret(self):
        if getattr(self, '_secret', None) is None:
            self._secret = self._get_pass_entry(self.SECRET_KEY_NAME)
        return self._secret

    def _get_pass_entry(self, key, attempt_update=True):
        result = self._pass_show(
            self.PASS_LOCATION + key,
        )

        if result is None and attempt_update:
            return self._get_pass_entry(key, False)

        return result

    def _pass_show(self, path):
        try:
            return subprocess.check_output(['pass', 'show', path]).strip()
        except subprocess.CalledProcessError:
            return None


class AWSCLIProvider(AWSCredsProvider):
    DESCRIPTION = 'AWS configuration (~/.aws)'

    def __init__(self, stack_name):
        AWSCredsProvider.__init__(self, stack_name)

        self._config = SafeConfigParser()
        self._creds = SafeConfigParser()

        self._config.read(utils.normpath('~/.aws/config'))
        self._creds.read(utils.normpath('~/.aws/credentials'))

        self.profile = 'default'
        for profile in self._config.sections():
            if self._config.get(profile, 'region') == self.region:
                self.profile = profile
                break

    @property
    def access_key(self):
        return self._creds.get(self.profile, 'aws_access_key_id')

    @property
    def secret(self):
        return self._creds.get(self.profile, 'aws_secret_access_key')


class EnvCredsProvider(AWSCredsProvider):
    DESCRIPTION = 'Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)'

    @property
    def access_key(self):
        return os.environ.get('AWS_ACCESS_KEY_ID', None)

    @property
    def secret(self):
        return os.environ.get('AWS_SECRET_ACCESS_KEY', None)


@utils.memoize
def get_all_instances(stack_name):
    creds = AWSCredsProvider.get_provider(stack_name)

    con = ec2.connect_to_region(
        STACK_TO_REGION[stack_name],
        aws_access_key_id=creds.access_key,
        aws_secret_access_key=creds.secret
    )

    reservations = con.get_all_instances(
        filters={
            "tag:aws:cloudformation:stack-name": stack_name,
            "tag:secondary-role": "hub",
            "instance-state-name": "running",
        }
    )

    return utils.flatten([reservation.instances for reservation in reservations])


def get_aws_hosts(config, group_name):
    stack_name = GROUP_TO_STACK[group_name]

    # We want to return the hosts with consistent short names. So we
    # need to get a consistent ordering of the ec2 nodes
    instances = get_all_instances(stack_name)
    instances.sort(key=lambda inst: inst.private_dns_name)
    short_group_name = _gen_short_name(group_name)

    for i, instance in enumerate(instances):
        url = "https://{}:2376".format(instance.private_ip_address)
        yield hosts.Host(
            name="{}{}".format(short_group_name, i + 1),
            base_url=url,
            tls=config.get_tls(url),
            environment=group_name,
            ip=instance.private_ip_address
        )


def _gen_short_name(group_name):
    return ''.join([sub[0] for sub in group_name.split('_')])