예제 #1
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(_when_all, desired_states))
     handler.add_args(
         filter(None, map(RelationBase.from_state, desired_states)))
     handler.register_states(desired_states)
     return action
예제 #2
0
def _when_decorator(predicate, desired_flags, action, legacy_args=False):
    endpoint_names = _get_endpoint_names(action)
    has_relname_flag = _has_endpoint_name_flag(desired_flags)
    params = signature(action).parameters
    has_params = len(params) > 0
    if has_relname_flag and not endpoint_names:
        # If this is an Endpoint handler but there are no endpoints
        # for this interface & role, then we shouldn't register its
        # handlers.  It probably means we only use a different role.
        return action
    for endpoint_name in endpoint_names or [None]:
        handler = Handler.get(action, endpoint_name)
        flags = _expand_endpoint_name(endpoint_name, desired_flags)
        handler.add_predicate(partial(predicate, flags))
        if _is_endpoint_method(action):
            # Endpoint handler methods expect self to be passed in to conform
            # to instance method convention. But mutliple decorators should
            # take care to not pass in multiple copies of self.
            if not handler.has_args:
                handler.add_args(map(endpoint_from_name, [endpoint_name]))
        elif has_params and legacy_args:
            # Handlers should all move to not taking any params and getting
            # the Endpoint instances from the context, but during the
            # transition, we need to provide for handlers expecting args.
            handler.add_args(filter(None, map(endpoint_from_flag, flags)))
        handler.register_flags(flags)
    return action
예제 #3
0
    def _register(action):
        def arg_gen():
            # use a generator to defer calling of hookenv.relation_type, for tests
            rel = RelationBase.from_name(hookenv.relation_type())
            if rel:
                yield rel

        handler = Handler.get(action)
        handler.add_predicate(partial(_hook, hook_patterns))
        handler.add_args(arg_gen())
        return action
예제 #4
0
    def _register(action):
        def arg_gen():
            # use a generator to defer calling of hookenv.relation_type, for tests
            rel = RelationBase.from_name(hookenv.relation_type())
            if rel:
                yield rel

        handler = Handler.get(action)
        handler.add_predicate(partial(_hook, hook_patterns))
        handler.add_args(arg_gen())
        return action
예제 #5
0
def only_once(action=None):
    """
    Register the decorated function to be run once, and only once.

    This decorator will never cause arguments to be passed to the handler.
    """
    if action is None:
        # allow to be used as @only_once or @only_once()
        return only_once

    action_id = _action_id(action)
    handler = Handler.get(action)
    handler.add_predicate(lambda: not was_invoked(action_id))
    handler.add_post_callback(partial(mark_invoked, action_id))
    return action
예제 #6
0
def only_once(action=None):
    """
    Register the decorated function to be run once, and only once.

    This decorator will never cause arguments to be passed to the handler.
    """
    if action is None:
        # allow to be used as @only_once or @only_once()
        return only_once

    action_id = _action_id(action)
    handler = Handler.get(action)
    handler.add_predicate(lambda: not was_invoked(action_id))
    handler.add_post_callback(partial(mark_invoked, action_id))
    return action
예제 #7
0
def only_once(action=None):
    """
    .. deprecated:: 0.5.0
       Use :func:`when_not` in combination with :func:`set_state` instead. This
       handler is deprecated because it might actually be
       `called multiple times <https://github.com/juju-solutions/charms.reactive/issues/22>`_.

    Register the decorated function to be run once, and only once.

    This decorator will never cause arguments to be passed to the handler.
    """
    if action is None:
        # allow to be used as @only_once or @only_once()
        return only_once

    action_id = _action_id(action)
    handler = Handler.get(action)
    handler.add_predicate(lambda: not was_invoked(action_id))
    handler.add_post_callback(partial(mark_invoked, action_id))
    return action
예제 #8
0
def everyhook(action):
    '''
    Decorator to run a handler each and every hook, during the hook phase.

    Charms should to minimize code that can only run in a specific hook
    to avoid race conditions. For example, consider a simple service
    configuration change. This will queue the config-changed hook on
    each unit, but if there is already a queue of hooks being processed
    by a unit then these hooks will see the changed configuration in the
    hook environment before the config-changed hook has had a chance to
    run. This can be fatal, with hooks crashing attempting to run code
    paths before the config-changed hook has set things up so they can
    be run successfully. Similar races can be described for leadership
    and relations. The simplest way of avoiding this entire class of
    races is to have a single, general hook instead of several specific
    ones tied to particular events.
    '''
    def in_hook_phase():
        dispatch_phase = unitdata.kv().get('reactive.dispatch.phase')
        return dispatch_phase == 'hooks'

    handler = Handler.get(action)
    handler.add_predicate(in_hook_phase)
    return action
예제 #9
0
def everyhook(action):
    """
    Decorator to run a handler each and every hook, during the hook phase.

    Charms should to minimize code that can only run in a specific hook
    to avoid race conditions. For example, consider a simple service
    configuration change. This will queue the config-changed hook on
    each unit, but if there is already a queue of hooks being processed
    by a unit then these hooks will see the changed configuration in the
    hook environment before the config-changed hook has had a chance to
    run. This can be fatal, with hooks crashing attempting to run code
    paths before the config-changed hook has set things up so they can
    be run successfully. Similar races can be described for leadership
    and relations. The simplest way of avoiding this entire class of
    races is to have a single, general hook instead of several specific
    ones tied to particular events.
    """
    def in_hook_phase():
        dispatch_phase = unitdata.kv().get("reactive.dispatch.phase")
        return dispatch_phase == "hooks"

    handler = Handler.get(action)
    handler.add_predicate(in_hook_phase)
    return action
예제 #10
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(_when, desired_states, True))
     handler.register_states(desired_states)
     return action
예제 #11
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(_when, desired_states, False))
     handler.add_args(filter(None, map(RelationBase.from_state, desired_states)))
     handler.register_states(desired_states)
     return action
예제 #12
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(_restricted_hook, 'collect-metrics'))
     return action
예제 #13
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(any_file_changed, filenames, **kwargs))
     return action
예제 #14
0
    def test_handlers(self):
        Handler._HANDLERS = {
            k: h
            for k, h in Handler._HANDLERS.items() if hasattr(h, '_action')
            and h._action.__qualname__.startswith('TestAltRequires.')
        }
        assert Handler._HANDLERS
        preds = [h._predicates[0].args[0][0] for h in Handler.get_handlers()]
        for pred in preds:
            self.assertRegex(pred, r'^endpoint.test-endpoint.')

        self.data_changed.return_value = False
        Endpoint._startup()
        tep = Endpoint.from_name('test-endpoint')

        self.assertCountEqual(tep.invocations, [])
        dispatch()
        self.assertCountEqual(tep.invocations, [
            'joined: test-endpoint',
        ])

        tep.invocations.clear()
        clear_flag('endpoint.test-endpoint.joined')
        clear_flag('endpoint.test-endpoint.changed')
        clear_flag('endpoint.test-endpoint.changed.foo')
        clear_flag('endpoint.test-endpoint2.joined')
        clear_flag('endpoint.test-endpoint2.changed')
        clear_flag('endpoint.test-endpoint2.changed.foo')
        self.data_changed.return_value = True
        Endpoint._startup()
        dispatch()
        self.assertCountEqual(tep.invocations, [
            'joined: test-endpoint',
            'changed: test-endpoint',
            'changed.foo: test-endpoint',
        ])

        tep.invocations.clear()
        clear_flag('endpoint.test-endpoint.joined')
        clear_flag('endpoint.test-endpoint.changed')
        clear_flag('endpoint.test-endpoint.changed.foo')
        clear_flag('endpoint.test-endpoint2.joined')
        clear_flag('endpoint.test-endpoint2.changed')
        clear_flag('endpoint.test-endpoint2.changed.foo')
        self.relations['test-endpoint2'] = [
            {
                'unit/0': {
                    'foo': 'yes'
                },
                'unit/1': {},
            },
            {
                'unit/0': {},
                'unit/1': {
                    'foo': 'no'
                },
            },
        ]
        Endpoint._startup()
        dispatch()
        self.assertCountEqual(tep.invocations, [
            'joined: test-endpoint',
            'joined: test-endpoint2',
            'changed: test-endpoint',
            'changed: test-endpoint2',
            'changed.foo: test-endpoint',
            'changed.foo: test-endpoint2',
        ])
예제 #15
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(_restricted_hook,
                                   'meter-status-changed'))
     return action
예제 #16
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(any_file_changed, filenames, **kwargs))
     return action
예제 #17
0
 def _register(action):
     handler = Handler.get(action)
     handler.add_predicate(partial(_when_not_all, desired_states))
     handler.register_states(desired_states)
     return action