Пример #1
0
def get_ts():
    domains = get_domains().values()
    if len(domains) != 1:
        raise RuntimeError("Unexpected number of domains in drift config: %s" %
                           len(domains))
    ts = domains[0]["table_store"]
    return ts
Пример #2
0
def list_command(args):
    # Enumerate subfolders at drift/config and see what's there
    domains = get_domains(user_dir=args.user_dir)
    if not domains:
        print "No Drift configuration found at", config_dir(
            '', user_dir=args.user_dir)
    else:
        for d in domains.values():
            print _format_domain_info(d)
Пример #3
0
def create_command(args):

    domain_info = get_domains(user_dir=args.user_dir).get(args.domain)
    if domain_info:
        print "The domain name '{}' is taken:".format(args.domain)
        print _format_domain_info(domain_info)
        sys.exit(1)

    # Force s3 naming convention. The root folder name and domain name must match.
    if args.source.startswith('s3://'):
        # Strip trailing slashes
        if args.source.endswith('/'):
            args.source = args.source[:-1]

        s3_backend = create_backend(args.source)
        target_folder = s3_backend.folder_name.rsplit('/')[-1]
        if target_folder != args.domain:
            print "Error: For S3 source, the target folder name and domain name must match."
            print "Target folder is '{}' but domain name is '{}'".format(
                target_folder, args.domain)
            print "Suggestion: {}".format(
                args.source.replace(target_folder, args.domain))
            sys.exit(1)
    elif args.source.startswith('file://'):
        # Expand user vars
        args.source = args.source.replace('~', os.path.expanduser('~'))

    # Get empty table store for Drift.
    ts = get_drift_table_store()
    ts.get_table('domain').add({
        'domain_name': args.domain,
        'origin': args.source,
        'display_name': args.display_name or ''
    })

    # Save it locally
    domain_folder = config_dir(args.domain, user_dir=args.user_dir)
    local_store = create_backend('file://' + domain_folder)
    local_store.save_table_store(ts)
    print "New config for '{}' saved to {}.".format(args.domain, domain_folder)
    print "Pushing to origin..."
    result = push_to_origin(ts, _first=True)
    if not result['pushed']:
        print "Push failed. Reason:", result['reason']
    print "Done."
Пример #4
0
def diff_command(args):
    # Get local table store and its meta state
    domain_info = get_domains(user_dir=args.user_dir).get(args.domain)
    if domain_info is None:
        click.secho("Configuration not found: {}".format(args.domain),
                    fg='red')
        sys.exit(1)
    local_ts = domain_info['table_store']
    local_m1, local_m2 = local_ts.refresh_metadata()

    # Get origin table store meta info
    origin = local_ts.get_table('domain')['origin']
    origin_backend = create_backend(origin)
    origin_ts = origin_backend.load_table_store()
    origin_meta = origin_ts.meta.get()

    local_diff = ("Local store and scratch", local_m1, local_m2, False)
    origin_diff = ("Local and origin", origin_meta, local_m2, args.details)

    for title, m1, m2, details in local_diff, origin_diff:
        diff = diff_meta(m1, m2)
        if diff['identical']:
            print title, "is clean."
        else:
            print title, "are different:"
            print "\tFirst checksum: ", diff['checksum']['first'][:7]
            print "\tSecond checksum:", diff['checksum']['second'][:7]
            if diff['modified_diff']:
                print "\tTime since pull: ", str(
                    diff['modified_diff']).split('.')[0]

            print "\tNew tables:", diff['new_tables']
            print "\tDeleted tables:", diff['deleted_tables']
            print "\tModified tables:", diff['modified_tables']

            if details:
                # Diff origin
                origin_ts = get_store_from_url(origin)
                for table_name in diff['modified_tables']:
                    t1 = local_ts.get_table(table_name)
                    t2 = origin_ts.get_table(table_name)
                    tablediff = diff_tables(t1, t2)
                    print "\nTable diff for", table_name, "\n(first=local, second=origin):"
                    print json.dumps(tablediff, indent=4, sort_keys=True)
Пример #5
0
def push_command(args):
    domain_info = get_domains(user_dir=args.user_dir).get(args.domain)
    if not domain_info:
        print "Can't push '{}'.".format(args.domain)
        sys.exit(1)

    ts = domain_info['table_store']
    origin = ts.get_table('domain')['origin']
    print "Pushing local config to source", origin
    result = push_to_origin(ts, args.force)
    if not result['pushed']:
        print "Push failed. Reason:", result['reason']
        print "Origin has changed. Use --force to force push."
        if 'time_diff' in result:
            print "Time diff", result['time_diff']
    else:
        print "Config pushed. Reason: ", result['reason']
        local_store = create_backend('file://' + domain_info['path'])
        local_store.save_table_store(ts)
Пример #6
0
def info():
    """List out all Drift configuration DB's that are active on this machine.
    """
    domains = get_domains()
    if not domains:
        click.secho(
            "No Drift configuration found on this machine. Run 'init' or 'create' "
            "command to remedy.")
    else:
        ts, source = get_default_drift_config_and_source()
        got_default = False

        for domain_info in domains.values():
            domain = domain_info['table_store'].get_table('domain')
            is_default = domain['domain_name'] == ts.get_table(
                'domain')['domain_name']
            if is_default:
                click.secho(domain['domain_name'] + " [DEFAULT]:",
                            bold=True,
                            nl=False)
                got_default = True
            else:
                click.secho(domain['domain_name'] + ":", bold=True, nl=False)

            click.secho(" \"{}\"".format(domain['display_name']), fg='green')
            click.secho("\tOrigin: " + domain['origin'])
            click.secho("\tLocal: " + domain_info['path'])
            click.secho("")

        if got_default:
            if 'DRIFT_CONFIG_URL' in os.environ:
                click.secho(
                    "The default config is specified using the 'DRIFT_CONFIG_URL' environment variable."
                )
            else:
                click.secho(
                    "The config above is the default one as it's the only one cached locally in ~/.drift/config."
                )
        else:
            click.secho("Note: There is no default config specified!")
Пример #7
0
def _pull_command(args):
    for domain_name, domain_info in get_domains(
            user_dir=args.user_dir).items():
        if args.domain and args.domain != domain_name:
            continue

        result = pull_from_origin(domain_info['table_store'],
                                  ignore_if_modified=args.ignore_if_modified,
                                  force=args.force)

        if not result['pulled']:
            print "Pull failed for", domain_name, ". Reason:", result['reason']
            if result['reason'] == 'local_is_modified':
                print "Use --ignore-if-modified to overwrite local changes."
            else:
                print "Use --force to force a pull."
        else:
            if result['reason'] == 'pulled_from_origin':
                local_backend = create_backend('file://' + domain_info['path'])
                local_backend.save_table_store(result['table_store'])

            print "Config for {} pulled. Reason: {}".format(
                domain_name, result['reason'])
Пример #8
0
def cli(ctx, config_urls, verbose, test):
    """Generate settings for Zappa lambdas.
    pass in bobo
    """
    ctx.obj = Globals()
    domains = get_domains()
    if not config_urls:
        config_urls = domains.keys()  # Use all local configs

    # 'config_urls' contains either urls or domain names. Needs some processing.
    table_stores = []
    for config_url in config_urls:
        if config_url in domains:
            # 'config_url' is a domain name. It should point to a locally stored
            # config, which we must check if it has 'origin' defined.
            ts = domains[config_url]['table_store']
            config_url = ts.get_table('domain').get().get('origin')
            if not config_url:
                click.secho("No origin defined for {}".format(ts))
                continue

        parts = urlparse.urlsplit(config_url)
        if parts.scheme != 's3':
            click.secho("Origin must be on S3, but is {}".format(config_url))
        else:
            click.secho("Fetching {}".format(config_url))
            table_stores.append(get_store_from_url(config_url))

    # Template accepts list of tiers which contains the following attributes:
    # S3_ORIGIN_URL: The config origin.
    # TIER_NAME: Tier name.
    # aws_region: The region where this tier is running.
    # s3_bucket_region: The region of the config origin s3 bucket.
    # bucket_name: Name of the bucket that holds the config.
    # subnets: Array of subnet names.
    # security_groups: Array of sg names.
    tiers = {}
    for ts in table_stores:
        domain = ts.get_table('domain').get()
        for tier in ts.get_table('tiers').find():
            tier_name = tier['tier_name']

            if 'organization_name' not in tier:
                click.secho(
                    "Note: Tier {} does not define 'organization_name'. Skipping."
                    .format(tier_name))
                continue

            s3_origin_url = domain['origin']

            if tier_name in tiers:
                click.secho("Error: Duplicate tier names found. Tier '{}' is "
                            "defined in both of the following configs:".format(
                                tier_name),
                            fg='red')
                click.secho("Config A: {}".format(s3_origin_url))
                click.secho("Config B: {}".format(
                    tiers[tier_name]['s3_origin_url']))
                click.secho(
                    "'{}' from config B will be skipped, but please fix!".
                    format(tier_name))
                continue

            if 'aws' not in tier or 'region' not in tier['aws']:
                click.secho(
                    "Note: Tier {} does not define aws.region. Skipping.".
                    format(tier_name))
                continue

            click.secho("Processing {}".format(tier_name), bold=True)

            # Figure out in which aws region this config is located
            aws_region = tier['aws']['region']
            parts = urlparse.urlsplit(s3_origin_url)
            bucket_name = parts.hostname
            s3_bucket_region = boto3.resource(
                "s3").meta.client.get_bucket_location(
                    Bucket=bucket_name)["LocationConstraint"]
            # If 'get_bucket_location' returns None, it really means 'us-east-1'.
            s3_bucket_region = s3_bucket_region or 'us-east-1'

            print "Connecting to AWS region {} to gather subnets and security group.".format(
                aws_region)
            ec2 = boto3.resource('ec2', aws_region)
            filters = [{
                'Name': 'tag:tier',
                'Values': [tier_name]
            }, {
                'Name':
                'tag:Name',
                'Values': [
                    tier_name + '-private-subnet-1',
                    tier_name + '-private-subnet-2'
                ]
            }]
            subnets = list(ec2.subnets.filter(Filters=filters))
            subnets = [subnet.id for subnet in subnets]

            filters = [{
                'Name': 'tag:tier',
                'Values': [tier_name]
            }, {
                'Name': 'tag:Name',
                'Values': [tier_name + '-private-sg']
            }]

            security_groups = list(ec2.security_groups.filter(Filters=filters))
            security_groups = [sg.id for sg in security_groups]

            # Sum it up
            tier_args = {
                's3_origin_url': s3_origin_url,
                'tier_name': tier_name,
                'organization_name': tier['organization_name'],
                'aws_region': aws_region,
                's3_bucket_region': s3_bucket_region,
                'bucket_name': bucket_name,
                'subnets': subnets,
                'security_groups': security_groups,
                '_ts': ts,
            }
            tiers[tier_name] = tier_args

    if test:
        _run_sanity_check(tiers)
        return

    env = Environment(
        loader=PackageLoader('driftconfig', package_path='templates'))
    template = env.get_template('zappa_settings.yml.jinja')
    zappa_settings_text = template.render(tiers=tiers.values())

    from driftconfig.cli import pretty
    print pretty(zappa_settings_text, 'yaml')
    with open('zappa_settings.yml', 'w') as f:
        f.write(zappa_settings_text)

    click.secho(
        "zappa_settings.yml generated. Run 'zappa deploy' or zappa update'"
        " for each of the tiers, or use the -all switch to zap them all.")
    print pretty("Example: zappa update -s zappa_settings.yml -all", 'bash')