The future of Charm Helpers

Stuart Bishop stuart.bishop at canonical.com
Tue Nov 24 09:18:31 UTC 2015


On 24 November 2015 at 01:53, Cory Johns <cory.johns at canonical.com> wrote:

> For example, while I haven't worked with leadership so can't really speak to
> that, I haven't felt the need to translate workload statuses into reactive
> states and vice versa, and have had no issues with calling
> hookenv.status_set() directly.  In particular,

I think the only bits of hookenv.py that make sense to be 'reactive
aware' are the leadership settings and, to a lesser extent, workload
status. Here we have hook environment that can be mutated by the
charm, and which other parts of the charm or other layers may need to
react to.

However, now I've thought about this more having my layer react to
side effects your layer made to the hook environment is rather
dubious. I really should be reacting to state your layer publishes,
even if that requires fixing or forking your layer.

So yes, I'm fine with just a low level hookenv.py replacement that
doesn't set magic states. Please consider the examples below off topic
:)

> https://github.com/johnsca/layer-apache-spark/blob/master/reactive/spark.py
> is roughly how I think about managing workload status in a reactive charm.
> (Of course, if that approach is missing some significant benefit to another
> approach, please let me know!)

@when_not('leadership.set.password')
@when_not('workloadstatus.blocked')
def wait_for_leadership():
    charms.hookenv.status_set('waiting', 'Waiting for leader to lead')

This is how I'm toying with handling waiting and blocked workload
states. If state is such that work cannot be completed until a future
hook, set the waiting status. But only if the status is not already
set to blocked, because overriding a blocked status and message would
hide a problem from the operator. It seems very similar to what you
are doing with spark.py, except that with your code your waiting
states would override and hide a blocked state set by a different
layer. As an example, the apt layer I've got will set the workload
state to 'blocked' if requested packages cannot be installed.

But leadership states are certainly more interesting to me than workload status:

@when('leadership.is_leader')
@when_not('leadership.set.password')
def set_cluster_password():
    charms.hookenv.leader_set(password=mkpass())

@when('leadership.changed.password')
def store_creds():
    rewrite(os.path.expanduser('~root/.cqlshrc', '''
               user=foo
               password={}
               '''.format(charms.hookenv.leader_get('password')))

Here the store_creds method gets invoked on all units. The reactive
state can now reach beyond a single unit, with the leader triggering
handlers on its peers. On the leader, the store_creds handler will be
invoked in the same hook that set_cluster_password was invoked (the
first one in this simple example, so storage-* or install). On the
other units, it will be invoked in a hook run soon after the leader
set the setting. Perhaps the install hook if the leader ran first,
perhaps some later hook, most likely the leader-settings-changed hook
or perhaps the leader-elected hook if leadership failover occurred at
the right moment.

-- 
Stuart Bishop <stuart.bishop at canonical.com>



More information about the Juju mailing list