Beispiel #1
0
def diff_app(client, app):
    print(shell.bold('Comparing "{}" on-disk definition to the current status in Marathon:'.format(app.id)))
    deployed = client.app_status(app.id)['app']
    changes = diff_json(app.json, deployed)

    if not changes:
        print(shell.bold(shell.green('There were no differences.')))
        return False
    else:
        print(shell.bold(shell.yellow('There were differences.')))
        return True
Beispiel #2
0
def make_account_request(account, password):
    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))

    return request
Beispiel #3
0
def make_account_request(account, password):
    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))

    return request
Beispiel #4
0
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)
Beispiel #5
0
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!')))
Beispiel #6
0
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)
Beispiel #7
0
def run_periodic_functions() -> None:
    global delay_on_error

    # First, import urls so that views are imported, decorators are run, and
    # our periodic functions get registered.
    import ocfweb.urls  # noqa

    was_error = False

    for pf in periodic_functions:
        if pf.seconds_since_last_update() >= pf.period:
            _logger.info(bold(green(f'Updating periodic function: {pf}')))

            try:
                pf.update()
            except Exception as ex:
                was_error = True
                if isinstance(ex, KeyboardInterrupt) or settings.DEBUG:
                    raise

                try:
                    send_problem_report(
                        dedent("""\
                        An exception occurred in an ocfweb periodic function:

                        {traceback}

                        Periodic function:
                          * Key: {pf.function_call_key}
                          * Last Update: {last_update} ({seconds_since_last_update} seconds ago)
                          * Period: {pf.period}
                          * TTL: {pf.ttl}

                        The background process will now pause for {delay} seconds.
                        """).format(
                            traceback=format_exc(),
                            pf=pf,
                            last_update=pf.last_update(),
                            seconds_since_last_update=pf.
                            seconds_since_last_update(),
                            delay=delay_on_error,
                        ), )
                    _logger.error(format_exc())
                except Exception as ex:
                    print(ex)  # just in case it errors again here
                    send_problem_report(
                        dedent("""\
                        An exception occured in ocfweb, but we errored trying to report it:

                        {traceback}
                        """).format(traceback=format_exc()), )
                    raise

        else:
            _logger.debug(bold(
                yellow(f'Not updating periodic function: {pf}')))

    if was_error:
        delay_on_error = min(DELAY_ON_ERROR_MAX, delay_on_error * 2)
        time.sleep(delay_on_error)
    else:
        delay_on_error = max(DELAY_ON_ERROR_MIN, delay_on_error / 2)
Beispiel #8
0
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()
def run_periodic_functions():
    global delay_on_error

    # First, import urls so that views are imported, decorators are run, and
    # our periodic functions get registered.
    import ocfweb.urls  # noqa

    was_error = False

    for pf in periodic_functions:
        if pf.seconds_since_last_update() >= pf.period:
            _logger.info(bold(green('Updating periodic function: {}'.format(pf))))

            try:
                pf.update()
            except Exception as ex:
                was_error = True
                if isinstance(ex, KeyboardInterrupt) or settings.DEBUG:
                    raise

                try:
                    send_problem_report(dedent(
                        """\
                        An exception occurred in an ocfweb periodic function:

                        {traceback}

                        Periodic function:
                          * Key: {pf.function_call_key}
                          * Last Update: {last_update} ({seconds_since_last_update} seconds ago)
                          * Period: {pf.period}
                          * TTL: {pf.ttl}

                        The background process will now pause for {delay} seconds.
                        """
                    ).format(
                        traceback=format_exc(),
                        pf=pf,
                        last_update=pf.last_update(),
                        seconds_since_last_update=pf.seconds_since_last_update(),
                        delay=delay_on_error,
                    ))
                    _logger.error(format_exc())
                except Exception as ex:
                    print(ex)  # just in case it errors again here
                    send_problem_report(dedent(
                        """\
                        An exception occured in ocfweb, but we errored trying to report it:

                        {traceback}
                        """
                    ).format(traceback=format_exc()))
                    raise

        else:
            _logger.debug(bold(yellow('Not updating periodic function: {}'.format(pf))))

    if was_error:
        delay_on_error = min(DELAY_ON_ERROR_MAX, delay_on_error * 2)
        time.sleep(delay_on_error)
    else:
        delay_on_error = max(DELAY_ON_ERROR_MIN, delay_on_error / 2)
Beispiel #10
0
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...')
Beispiel #11
0
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()