コード例 #1
0
ファイル: deploy.py プロジェクト: redplanks/services
def diff_json(local, deployed, path=''):
    """Diff two Marathon JSON blobs.

    Returns True if there are any interesting differences anywhere in the tree.
    """
    # Some key/values change all the time and obviously aren't part of our
    # configuration (e.g. how many tasks are currently healthy), so we don't
    # diff them.
    if path in {
            '["deployments"]',
            '["lastTaskFailure"]',
            '["ports"]',
            '["tasks"]',
            '["tasksHealthy"]',
            '["tasksRunning"]',
            '["tasksStaged"]',
            '["tasksUnhealthy"]',
            '["version"]',
            '["versionInfo"]',
    }:
        return False

    if isinstance(local, dict) and isinstance(deployed, dict):
        found_change = False
        for key in sorted(set(local.keys()) | set(deployed.keys())):
            found_change |= diff_json(local.get(key), deployed.get(key), path + '["{}"]'.format(key))
        return found_change
    elif local != deployed:
        if path == '["container"]["docker"]["image"]':
            local_repo, local_version = split_docker(local)
            deployed_repo, deployed_version = split_docker(deployed)

            # Still update if the local version is explicitly specified
            # (not just 'latest') and doesn't match the deployed version.
            # This allows for pinning and updating of a version in git
            if (local_repo == deployed_repo and
                    local_version == 'latest' or
                    local_version == deployed_version):
                return False

        # Some things are not interesting (e.g. sometimes we omit a key, but
        # really it's an empty list). Only complain if one is interesting.
        def interesting(thing):
            return thing not in (None, [], {})

        if interesting(local) or interesting(deployed):
            print('  Difference at {}'.format(path))
            print('    Current value: {}'.format(shell.red(repr(deployed))))
            print('    Desired value: {}'.format(shell.green(repr(local))))
            return True

    return False
コード例 #2
0
ファイル: approve.py プロジェクト: matthew-mcallister/create
def error_report(request, new_request, response):
    print(bold(red('Error: Entered unexpected state.')))
    print(bold('An email has been sent to OCF staff'))

    error_report = dedent("""\
        Error encountered running approve!

        The request we submitted was:
        {request}

        The request we submitted after being flagged (if any) was:
        {new_request}

        The response we received was:
        {response}
        """).format(request=request, new_request=new_request, reponse=response)

    send_problem_report(error_report)
コード例 #3
0
ファイル: approve.py プロジェクト: ocf/create
def get_group_information(group_oid):
    """Returns tuple (group name, group oid, group email)."""
    if group_oid:
        group = group_by_oid(group_oid)

        if not group:
            print(red('No group with OID {}').format(group_oid))
            sys.exit(1)

        if group['accounts']:
            print(yellow(
                'Warning: there is an existing group account with OID {}: {}'.format(
                    group_oid,
                    ', '.join(group['accounts']),
                ),
            ))
            input('Press enter to continue...')

        return (group['name'], group_oid, group['email'])
    else:
        return ('', '', '')
コード例 #4
0
ファイル: approve.py プロジェクト: matthew-mcallister/create
def get_group_information(group_oid):
    """Returns tuple (group name, group oid, group email)."""
    if group_oid:
        group = group_by_oid(group_oid)

        if not group:
            print(red('No group with OID {}').format(group_oid))
            sys.exit(1)

        if group['accounts']:
            print(
                yellow(
                    'Warning: there is an existing group account with OID {}: {}'
                    .format(
                        group_oid,
                        ', '.join(group['accounts']),
                    ), ))
            input('Press enter to continue...')

        return (group['name'], group_oid, group['email'])
    else:
        return ('', '', '')
コード例 #5
0
ファイル: deploy.py プロジェクト: redplanks/services
def update_app(client, app):
    print(shell.bold(shell.yellow('Starting a new deployment.')))
    deployed = client.app_status(app.id)['app']

    # update the image tag (this doesn't quite match the on-disk tag)
    on_disk_tag = split_docker(app.json['container']['docker']['image'])
    deployed_tag = split_docker(deployed['container']['docker']['image'])

    new_json = copy.deepcopy(app.json)

    if on_disk_tag[0] == deployed_tag[0] and on_disk_tag[1] == 'latest':
        # The repo didn't change, and the local version is not pinned,
        # so keep it as whatever version is currently deployed.
        new_json['container']['docker']['image'] = '{}:{}'.format(*deployed_tag)
    else:
        # The repo or local version changed, so reset to whatever the local
        # one is. By default this is ${repo}:latest.
        new_json['container']['docker']['image'] = '{}:{}'.format(*on_disk_tag)

    client.deploy_app(app.id.lstrip('/'), new_json, report=unbuf_print)

    # make sure there are no longer any differences
    print(shell.bold('Confirming there are now no differences...'))
    new_deployed = client.app_status(app.id)['app']
    if diff_json(app.json, new_deployed):
        print(shell.bold(shell.red(
            'There were still differences in the on-disk and deployed version of "{}", '
            'even after making a new deployment.\n'
            '\n'
            'This most likely means that the app config is missing some default value.\n'
            "This isn't a huge problem, but it *does* mean that every time this repo gets pushed, "
            'we do a no-op push which wastes time :(\n'
            '\n'
            'You should fix this, probably by taking the differences listed above and adding\n'
            "them to the app's config on disk.".format(app.id)
        )))
        raise AssertionError('Deployment failed.')
    else:
        print(shell.bold(shell.green('OK!')))
コード例 #6
0
ファイル: approve.py プロジェクト: ocf/create
def error_report(request, new_request, response):
    print(bold(red('Error: Entered unexpected state.')))
    print(bold('An email has been sent to OCF staff'))

    error_report = dedent(
        """\
        Error encountered running approve!

        The request we submitted was:
        {request}

        The request we submitted after being flagged (if any) was:
        {new_request}

        The response we received was:
        {response}
        """
    ).format(
        request=request,
        new_request=new_request,
        reponse=response
    )

    send_problem_report(error_report)
コード例 #7
0
ファイル: approve.py プロジェクト: matthew-mcallister/create
def main():
    def pause_error_msg():
        input('Press enter to edit group information (or Ctrl-C to exit)...')

    parser = ArgumentParser(description='Create new OCF group accounts.')
    parser.add_argument('oid',
                        type=int,
                        nargs='?',
                        help='CalLink OID for the group.')
    args = parser.parse_args()

    group_name, callink_oid, email = get_group_information(args.oid)

    content = TEMPLATE.format(group_name=group_name,
                              callink_oid=callink_oid,
                              email=email)

    while True:
        content = edit_file(content)
        try:
            account = yaml.safe_load(content)
        except yaml.YAMLError as ex:
            print('Error parsing your YAML:')
            print(ex)
            pause_error_msg()
            continue

        missing_key = False
        for key in ['user_name', 'group_name', 'callink_oid', 'email']:
            if account.get(key) is None:
                print('Missing value for key: ' + key)
                missing_key = True
        if missing_key:
            pause_error_msg()
            continue

        try:
            password = prompt_for_new_password(
                validator=lambda pwd: validate_password(
                    account['user_name'], pwd), )
        except KeyboardInterrupt:
            # we want to allow cancelling during the "enter password" stage
            # without completely exiting approve
            print()
            pause_error_msg()
            continue

        request = make_account_request(account, password)

        if input('Submit request? [yN] ') not in ('y', 'Y'):
            pause_error_msg()
            continue

        tasks, celery, response = create_account(request)
        new_request = None

        if response.status == NewAccountResponse.REJECTED:
            print(
                bold(
                    red('Account requested was rejected for the following reasons:'
                        )))
            for error in response.errors:
                print(red('  - {}'.format(error)))

            pause_error_msg()
            continue
        elif response.status == NewAccountResponse.FLAGGED:
            print(
                bold(
                    yellow(
                        'Account requested was flagged for the following reasons:'
                    )))
            for error in response.errors:
                print(yellow('  - {}'.format(error)))
            print(
                bold(
                    'You can either create the account anyway, or go back and '
                    'modify the request.'))
            choice = input('Create the account anyway? [yN] ')

            if choice in ('y', 'Y'):
                new_request = request._replace(
                    handle_warnings=NewAccountRequest.WARNINGS_CREATE, )
                task = tasks.validate_then_create_account.delay(new_request)
                response = wait_for_task(celery, task)
            else:
                pause_error_msg()
                continue

        if response.status == NewAccountResponse.CREATED:
            print(bold(green('Account created!')))
            print('Your account was created successfully.')
            print('You\'ve been sent an email with more information.')
            return
        else:
            # this shouldn't be possible; we must have entered some weird state
            error_report(request, new_request, response)
            pause_error_msg()
コード例 #8
0
ファイル: approve.py プロジェクト: gnowxilef/create
def main():
    def_group_name = ''
    def_callink_oid = ''
    def_email = ''

    parser = ArgumentParser(description='Create new OCF group accounts.')
    parser.add_argument('oid', type=int, nargs='?', help='CalLink OID for the group.')
    args = parser.parse_args()

    if args.oid:
        group = group_by_oid(args.oid)
        if not group:
            print(red('No group with OID {}').format(args.oid))
            return
        if group['accounts']:
            print(yellow(
                'Warning: there is an existing group account with OID {}: {}'.format(
                    args.oid,
                    ', '.join(group['accounts']),
                ),
            ))
            input('Press any key to continue...')
        def_group_name = group['name']
        def_callink_oid = args.oid
        def_email = group['email']

    content = TEMPLATE.format(
        group_name=def_group_name,
        callink_oid=def_callink_oid,
        email=def_email
    )

    while True:
        content = edit_file(content)
        try:
            account = yaml.safe_load(content)
        except yaml.YAMLError as ex:
            print('Error parsing your YAML:')
            print(ex)
            input('Press enter to continue...')
            continue

        missing_key = False
        for key in ['user_name', 'group_name', 'callink_oid', 'email']:
            if account.get(key) is None:
                print('Missing value for key: ' + key)
                missing_key = True
        if missing_key:
            input('Press enter to continue...')
            continue

        try:
            password = prompt_for_new_password(
                validator=lambda pwd: validate_password(
                    account['user_name'], pwd),
            )
        except KeyboardInterrupt:
            # we want to allow cancelling during the "enter password" stage
            # without completely exiting approve
            print()
            input('Press enter to start over (or ^C again to cancel)...')
            continue

        request = NewAccountRequest(
            user_name=account['user_name'],
            real_name=account['group_name'],
            is_group=True,
            calnet_uid=None,
            callink_oid=account['callink_oid'],
            email=account['email'],
            encrypted_password=encrypt_password(
                password,
                RSA.importKey(CREATE_PUBLIC_KEY),
            ),
            handle_warnings=NewAccountRequest.WARNINGS_WARN,
        )

        print()
        print(bold('Pending account request:'))
        print(dedent(
            """\
            User Name: {request.user_name}
            Group Name: {request.real_name}
            CalLink OID: {request.callink_oid}
            Email: {request.email}
            """
        ).format(request=request))

        if input('Submit request? [yN] ') != 'y':
            input('Press enter to continue.')
            continue

        conf = ConfigParser()
        conf.read('/etc/ocf-create/ocf-create.conf')

        celery = Celery(
            broker=conf.get('celery', 'broker'),
            backend=conf.get('celery', 'backend'),
        )
        tasks = get_tasks(celery)
        task = tasks.validate_then_create_account.delay(request)

        response = wait_for_task(celery, task)
        new_request = None

        if response.status == NewAccountResponse.REJECTED:
            print(bold(red(
                'Account requested was rejected for the following reasons:'
            )))
            for error in response.errors:
                print(red('  - {}'.format(error)))
            input('Press enter to start over (or ^C to cancel)...')
            continue
        elif response.status == NewAccountResponse.FLAGGED:
            print(bold(yellow(
                'Account requested was flagged for the following reasons:'
            )))
            for error in response.errors:
                print(yellow('  - {}'.format(error)))
            print(bold(
                'You can either create the account anyway, or go back and '
                'modify the request.'
            ))
            choice = input('Create the account anyway? [yN] ')

            if choice in ('y', 'Y'):
                new_request = request._replace(
                    handle_warnings=NewAccountRequest.WARNINGS_CREATE,
                )
                task = tasks.validate_then_create_account.delay(new_request)
                response = wait_for_task(celery, task)
            else:
                input('Starting over, press enter to continue...')
                continue

        if response.status == NewAccountResponse.CREATED:
            print(bold(green('Account created!')))
            print('Your account was created successfully.')
            print('You\'ve been sent an email with more information.')
            return
        else:
            # this shouldn't be possible; we must have entered some weird state
            # TODO: report via ocflib
            print(bold(red('Error: Entered unexpected state.')))
            print(red('The request we submitted was:'))
            print(red(request))
            print(red('The new request we submitted (if any) was:'))
            print(red(new_request))
            print(red('The response we received was:'))
            print(red(response))
            print(bold(red('Not really sure what to do here, sorry.')))
            input('Press enter to start over...')
コード例 #9
0
ファイル: approve.py プロジェクト: ocf/create
def main():
    def pause_error_msg():
        input('Press enter to edit group information (or Ctrl-C to exit)...')

    parser = ArgumentParser(description='Create new OCF group accounts.')
    parser.add_argument('oid', type=int, nargs='?', help='CalLink OID for the group.')
    args = parser.parse_args()

    group_name, callink_oid, email = get_group_information(args.oid)

    content = TEMPLATE.format(
        group_name=group_name,
        callink_oid=callink_oid,
        email=email
    )

    while True:
        content = edit_file(content)
        try:
            account = yaml.safe_load(content)
        except yaml.YAMLError as ex:
            print('Error parsing your YAML:')
            print(ex)
            pause_error_msg()
            continue

        missing_key = False
        for key in ['user_name', 'group_name', 'callink_oid', 'email']:
            if account.get(key) is None:
                print('Missing value for key: ' + key)
                missing_key = True
        if missing_key:
            pause_error_msg()
            continue

        try:
            password = prompt_for_new_password(
                validator=lambda pwd: validate_password(
                    account['user_name'], pwd),
            )
        except KeyboardInterrupt:
            # we want to allow cancelling during the "enter password" stage
            # without completely exiting approve
            print()
            pause_error_msg()
            continue

        request = make_account_request(account, password)

        if input('Submit request? [yN] ') not in ('y', 'Y'):
            pause_error_msg()
            continue

        tasks, celery, response = create_account(request)
        new_request = None

        if response.status == NewAccountResponse.REJECTED:
            print(bold(red(
                'Account requested was rejected for the following reasons:'
            )))
            for error in response.errors:
                print(red('  - {}'.format(error)))

            pause_error_msg()
            continue
        elif response.status == NewAccountResponse.FLAGGED:
            print(bold(yellow(
                'Account requested was flagged for the following reasons:'
            )))
            for error in response.errors:
                print(yellow('  - {}'.format(error)))
            print(bold(
                'You can either create the account anyway, or go back and '
                'modify the request.'
            ))
            choice = input('Create the account anyway? [yN] ')

            if choice in ('y', 'Y'):
                new_request = request._replace(
                    handle_warnings=NewAccountRequest.WARNINGS_CREATE,
                )
                task = tasks.validate_then_create_account.delay(new_request)
                response = wait_for_task(celery, task)
            else:
                pause_error_msg()
                continue

        if response.status == NewAccountResponse.CREATED:
            print(bold(green('Account created!')))
            print('Your account was created successfully.')
            print('You\'ve been sent an email with more information.')
            return
        else:
            # this shouldn't be possible; we must have entered some weird state
            error_report(request, new_request, response)
            pause_error_msg()