Managing flags in Endpoint classes with @when
etc has always had some edge cases and timing issues which were non-obvious. With the release today of charms.reactive 1.3.0, Endpoint classes can now implement a Endpoint.manage_flags
method which is guaranteed to be called before any decorated handlers as part of the instance initialization, after the framework-provided automatic flags are adjusted, and is a good place to do all flag manipulation for the interface layer.
As an example, the kube-control interface provides could be changed from:
@when_any('endpoint.{endpoint_name}.joined',
'endpoint.{endpoint_name}.changed')
def joined_or_changed(self):
set_flag(self.expand_name('{endpoint_name}.connected'))
hookenv.log('Checking for gpu-enabled workers')
if self._get_gpu():
set_flag(
self.expand_name(
'{endpoint_name}.gpu.available'))
else:
clear_flag(
self.expand_name(
'{endpoint_name}.gpu.available'))
clear_flag(self.expand_name('endpoint.{endpoint_name}.changed'))
@when_not('endpoint.{endpoint_name}.joined')
def departed(self):
"""
Remove all states.
"""
clear_flag(
self.expand_name(
'{endpoint_name}.connected'))
clear_flag(
self.expand_name(
'{endpoint_name}.gpu.available'))
To the more concise:
def manage_flags(self):
toggle_flag(self.expand_name('{endpoint_name}.connected'),
self.is_joined)
toggle_flag(self.expand_name('{endpoint_name}.gpu.available'),
self.is_joined and self._get_gpu())
Using this approach also avoids a race condition where handlers in the charm could could potentially be called before the interface layer had a chance to do its internal flag adjustments. While this is usually not a problem for most charms, and could also be addressed with triggers, I think the manage_flags
approach is more intuitive.
Feedback on this approach is appreciated.