예제 #1
0
def handle_subject_create(body, **_):
    try:
        subject = AnarchySubject(body)
        subject.handle_create(runtime)
    except AssertionError as e:
        operator_logger.warning('AnarchySubject %s invalid: %s',
                                body['metadata']['name'], e)
예제 #2
0
def main_loop():
    last_runner_check = 0
    while True:
        try:
            action_cache_lock.acquire()
            AnarchyAction.start_actions(runtime)
        except Exception as e:
            operator_logger.exception("Error in start_actions!")
        finally:
            action_cache_lock.release()

        try:
            AnarchySubject.retry_failures(runtime)
        except Exception as e:
            operator_logger.exception("Error in retry_failures!")

        if runner_check_interval < time.time() - last_runner_check:
            try:
                AnarchyRunner.refresh_all_runner_pods(runtime)
                last_runner_check = time.time()
            except:
                operator_logger.exception(
                    'Error checking runner pods in main loop')

        time.sleep(1)
예제 #3
0
def handle_subject_update(body, old, new, **_):
    try:
        subject = AnarchySubject(body)
        if old['spec'] != new['spec']:
            subject.handle_spec_update(runtime)
    except AssertionError as e:
        operator_logger.warning('AnarchySubject %s invalid: %s',
                                body['metadata']['name'], e)
예제 #4
0
def handle_subject_event(event, logger, **_):
    '''
    Anarchy uses on.event instead of on.delete because Anarchy needs custom
    for removing finalizers. The finalizer will be removed immediately if the
    AnarchyGovernor does not have a delete subject event handler. If there is
    a delete subject event handler then it is up to the governor logic to remove
    the finalizer.
    '''
    obj = event.get('object')
    if obj and obj.get('apiVersion') == runtime.api_group_version:
        if event['type'] in ['ADDED', 'MODIFIED', None]:
            subject = AnarchySubject(obj)
            if subject.is_pending_delete:
                subject.handle_delete(runtime)
예제 #5
0
def patch_or_delete_subject(subject_name):
    anarchy_runner, runner_pod = check_runner_auth(
        flask.request.headers.get('Authorization', ''))
    if not anarchy_runner:
        flask.abort(400)
        return

    if subject_name != runner_pod.metadata.labels.get(runtime.subject_label):
        operator_logger.warning(
            'AnarchyRunner %s Pod %s cannot update AnarchySubject %s!',
            anarchy_runner.name, runner_pod.metadata.name, subject_name)
        flask.abort(400)
        return

    anarchy_subject = AnarchySubject.get(subject_name, runtime)
    if not anarchy_subject:
        operator_logger.warning(
            'AnarchyRunner %s Pod %s attempted %s on deleted AnarchySubject %s!',
            anarchy_runner.name, runner_pod.metadata.name,
            flask.request.method, subject_name)
        flask.abort(400)
        return

    if flask.request.method == 'PATCH':
        if not 'patch' in flask.request.json:
            operator_logger.warning('No patch in AnarchySubject %s post',
                                    subject_name)
            flask.abort(400)
            return
        result = anarchy_subject.patch(flask.request.json['patch'], runtime)
    elif flask.request.method == 'DELETE':
        result = anarchy_subject.delete(
            flask.request.json.get('remove_finalizers', False), runtime)

    return flask.jsonify({'success': True, 'result': result})
예제 #6
0
    def register(anarchy_run, runtime):
        runner_value = anarchy_run.metadata['labels'].get(runtime.runner_label, None)
        anarchy_subject_name = anarchy_run.subject_name
        anarchy_subject = AnarchySubject.get(anarchy_subject_name, runtime)

        if not anarchy_subject:
            operator_logger.warn(
                'Unable to find AnarchySubject %s for AnarchyRun %s',
                anarchy_subject_name, anarchy_run.name
            )
            return

        if not runner_value or runner_value == 'successful':
            return
        elif runner_value == 'failed':
            anarchy_subject.anarchy_run_update(anarchy_run, runtime)
            last_attempt = anarchy_run.run_post_datetime
            if last_attempt:
                if anarchy_run.failures > 9:
                    retry_delay = timedelta(hours=1)
                else:
                    retry_delay = timedelta(seconds=5 * 2**anarchy_run.failures)
                next_attempt = last_attempt + retry_delay
            else:
                next_attempt = datetime.utcnow()
            anarchy_subject.retry_after = next_attempt
        elif runner_value == 'lost':
            anarchy_subject.anarchy_run_update(anarchy_run, runtime)
            anarchy_subject.put_in_job_queue(runtime)
        elif runner_value == 'pending':
            anarchy_subject.enqueue_anarchy_run(anarchy_run, runtime)
        else:
            anarchy_subject.anarchy_run_update(anarchy_run, runtime)
예제 #7
0
def get_run():
    anarchy_runner, runner_pod = check_runner_auth(flask.request.headers.get('Authorization', ''))
    if not anarchy_runner:
        flask.abort(400)
        return

    anarchy_run = anarchy_runner.runner_pods.get(runner_pod, None)
    if anarchy_run:
        operator_logger.warning(
            'AnarchyRunner %s pod %s get run, but already had run %s!',
            anarchy_runner.name, runner_pod, anarchy_run.name
        )
        anarchy_run.handle_lost_runner(runner_pod, runtime)

    anarchy_subject = AnarchySubject.get_pending(anarchy_runner.name, runtime)
    if not anarchy_subject:
        return flask.jsonify(None)

    anarchy_run = anarchy_subject.get_anarchy_run(runtime)
    if not anarchy_run:
        return flask.jsonify(None)

    anarchy_runner.runner_pods[runner_pod] = anarchy_run
    anarchy_run.set_runner(anarchy_runner.name + '.' + runner_pod, runtime)
    resp = anarchy_run.to_dict(runtime)
    resp['subject'] = anarchy_subject.to_dict(runtime)
    resp['governor'] = anarchy_subject.get_governor(runtime).to_dict(runtime)
    return flask.jsonify(resp)
예제 #8
0
def handle_subject_update(body, **_):
    wait_for_init()
    try:
        subject = AnarchySubject.get_from_resource(body)
        subject.handle_update(runtime)
    except AssertionError as e:
        operator_logger.warning('AnarchySubject %s invalid: %s', body['metadata']['name'], e)
예제 #9
0
def run_subject_action_patch(subject_name, action_name):
    """
    Callback from runner to update AnarchyAction associated with AnarchySubject assigned to runner.

    The only function of this method currently is to pass JSON, `{"successful": true}` or
    `{"failed": true}` to mark the action as finished.
    """
    anarchy_runner, runner_pod = check_runner_auth(
        flask.request.headers.get('Authorization', ''))
    if not anarchy_runner:
        flask.abort(400)

    if not runtime.running_all_in_one \
    and subject_name != runner_pod.metadata.labels.get(runtime.subject_label):
        operator_logger.warning(
            'AnarchyRunner %s Pod %s cannot update actions for AnarchySubject %s!',
            anarchy_runner.name, runner_pod.metadata.name, subject_name)
        flask.abort(400)

    anarchy_action = AnarchyAction.get(action_name, runtime)
    if not anarchy_action:
        operator_logger.warning(
            'AnarchyRunner %s Pod %s attempted to update action on deleted AnarchyAction %s!',
            anarchy_runner.name, runner_pod.metadata.name, action_name)
        flask.abort(400)

    anarchy_subject = AnarchySubject.get(subject_name, runtime)
    if not anarchy_subject:
        operator_logger.warning(
            'AnarchyRunner %s Pod %s attempted to update action on deleted AnarchySubject %s!',
            anarchy_runner.name, runner_pod.metadata.name, subject_name)
        flask.abort(400)

    anarchy_governor = anarchy_subject.get_governor(runtime)
    if not anarchy_governor:
        operator_logger.warning(
            'AnarchyRunner %s Pod %s cannot post action to AnarchySubject %s, unable to find AnarchyGovernor %s!',
            anarchy_runner.name, runner_pod, subject_name,
            anarchy_subject.governor_name)
        flask.abort(400)

    finished_state = None
    if flask.request.json.get('successful', False):
        finished_state = 'successful'
    elif flask.request.json.get('failed', False):
        finished_state = 'failed'

    if finished_state != None:
        anarchy_subject.remove_active_action(anarchy_action, runtime)
        anarchy_action.set_finished(finished_state, runtime)

    return flask.jsonify({
        'success': True,
        'result': anarchy_action.to_dict(runtime)
    })
예제 #10
0
def handle_subject_event(event, **_):
    '''
    Anarchy uses on.event instead of on.delete because Anarchy needs custom
    for removing finalizers. The finalizer will be removed immediately if the
    AnarchyGovernor does not have a delete subject event handler. If there is
    a delete subject event handler then it is up to the governor logic to remove
    the finalizer.
    '''
    wait_for_init()
    resource = event['object']
    if event['type'] in ['ADDED', 'MODIFIED', None]:
        if 'deletionTimestamp' in resource['metadata']:
            subject = AnarchySubject.get_from_resource(resource)
            subject.handle_delete(runtime)
예제 #11
0
 def get_subject(self, runtime):
     return AnarchySubject.get(self.subject_name, runtime)
예제 #12
0
def post_subject_action(subject_name):
    anarchy_runner, runner_pod = check_runner_auth(
        flask.request.headers.get('Authorization', ''))
    if not anarchy_runner:
        flask.abort(400)
        return

    if subject_name != runner_pod.metadata.labels.get(runtime.subject_label):
        operator_logger.warning(
            'AnarchyRunner %s Pod %s cannot update AnarchySubject %s!',
            anarchy_runner.name, runner_pod.metadata.name, subject_name)
        flask.abort(400)
        return

    anarchy_subject = AnarchySubject.get(subject_name, runtime)
    if not anarchy_subject:
        operator_logger.warning(
            'AnarchyRunner %s Pod %s attempted to create action on deleted AnarchySubject %s!',
            anarchy_runner.name, runner_pod.metadata.name, subject_name)
        flask.abort(400)
        return

    anarchy_governor = anarchy_subject.get_governor(runtime)
    if not anarchy_governor:
        operator_logger.warning(
            'AnarchyRunner %s Pod %s cannot post action to AnarchySubject %s, unable to find AnarchyGovernor %s!',
            anarchy_runner.name, runner_pod, subject_name,
            anarchy_subject.governor_name)
        flask.abort(400)
        return

    action_name = flask.request.json.get('action', None)
    after_timestamp = flask.request.json.get('after', None)
    cancel_actions = flask.request.json.get('cancel', None)
    if not action_name and not cancel_actions:
        operator_logger.warning(
            'No action or cancel given for scheduling action')
        flask.abort(400)
        return
    if after_timestamp and not re.match(r'\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ',
                                        after_timestamp):
        operator_logger.warning(
            'Invalide datetime format "%s" given for action after value',
            after_timestamp)
        flask.abort(400)
        return

    if action_name not in cancel_actions:
        cancel_actions.append(action_name)

    for action_resource in runtime.custom_objects_api.list_namespaced_custom_object(
            runtime.operator_domain,
            runtime.api_version,
            runtime.operator_namespace,
            'anarchyactions',
            label_selector='{}/subject={}'.format(runtime.operator_domain,
                                                  anarchy_subject.name)).get(
                                                      'items', []):
        if action_resource['spec']['action'] in cancel_actions \
        and 'status' not in action_resource:
            runtime.custom_objects_api.delete_namespaced_custom_object(
                runtime.operator_domain, runtime.api_version,
                runtime.operator_namespace, 'anarchyactions',
                action_resource['metadata']['name'])

    if action_name:
        result = runtime.custom_objects_api.create_namespaced_custom_object(
            runtime.operator_domain, runtime.api_version,
            runtime.operator_namespace, 'anarchyactions', {
                "apiVersion": runtime.api_group_version,
                "kind": "AnarchyAction",
                "metadata": {
                    "generateName":
                    "%s-%s-" % (anarchy_subject.name, action_name),
                    "labels": {
                        runtime.action_label: action_name,
                        runtime.subject_label: anarchy_subject.name,
                        runtime.governor_label: anarchy_governor.name
                    },
                    "ownerReferences": [{
                        "apiVersion": runtime.api_group_version,
                        "controller": True,
                        "kind": "AnarchySubject",
                        "name": anarchy_subject.name,
                        "uid": anarchy_subject.uid
                    }]
                },
                "spec": {
                    "action": action_name,
                    "after": after_timestamp,
                    "callbackToken": uuid.uuid4().hex,
                    "governorRef": {
                        "apiVersion": runtime.api_group_version,
                        "kind": "AnarchyGovernor",
                        "name": anarchy_governor.name,
                        "namespace": runtime.operator_namespace,
                        "uid": anarchy_governor.uid
                    },
                    "subjectRef": {
                        "apiVersion": runtime.api_group_version,
                        "kind": "AnarchySubject",
                        "name": anarchy_subject.name,
                        "namespace": runtime.operator_namespace,
                        "uid": anarchy_subject.uid
                    }
                }
            })
    else:
        result = None

    return flask.jsonify({'success': True, 'result': result})
예제 #13
0
def handle_subject_timer(body, **_):
    subject = AnarchySubject(body)
    subject.set_active_run_to_pending_from_status(runtime)
예제 #14
0
def handle_subject_update(body, **_):
    wait_for_init()
    subject = AnarchySubject.get_from_resource(body)
    subject.handle_update(runtime)