Frequently Asked Questions

How do I run and debug reactive charm?

You run a reactive charm by running a hook in the hooks/ directory. That hook will start the reactive framework and initiate the “cascade of flags”.

The hook files in the hooks/ directory are created by layer:basic and by charm build. Make sure to include layer:basic in your layer.yaml file if the hook files aren’t present in the hooks/ directory.

You can find more information about debugging reactive charms in the Juju docs.

Note

Changes to flags are reset when a handler crashes. Changes to flags happen immediately, but they are only persisted at the end of a complete and successful run of the reactive framework. All unpersisted changes are discarded when a hook crashes.

Why doesn’t my Charm do anything? Why are there no hooks in the hooks directory?

You probably forgot to include layer-basic in your layer.yaml file. This layer creates the hook files so that the reactive framework starts when a hook runs.

How can I react to configuration changes?

The base layer provides a set of easy flags to react to configuration changes. These flags will be automatically managed when you include layer:basic in your layer.yaml file.

How to remove a flag immediately when a config changes?

You can use triggers for this, see Coupling Flags with Triggers for more info.

Example: clear the flag apt.sources_configured immediately when the install_sources config option changes.

register_trigger(when='config.changed.install_sources',
                 clear_flag='apt.sources_configured')

How to run a handler even if the flag it reacts to has since been cleared?

Take the following case:

@when('service.stopped')
def restart_service():
    restart_my_service()
    clear_flag('service.stopped')

@when_all('service.stopped',
          'endpoint.clients.connected')
def notify_related_units():
    clients = from_flag('endpoint.clients.connected')
    clients.notify_service_stopped()

The notify_related_units handler will never get invoked because the restart_handler will get invoked first and it removes the service.stopped state. If this is not the desired behavior, if you need to notify the clients even when the service has been restarted by another handler, then you can use a trigger to create a new state specifically for the notify_related_units handler:

register_trigger(when='service.stopped',
                 set_flag='clients.need_notification')

@when('service.stopped')
def restart_service():
    restart_my_service()
    clear_flag('service.stopped')

@when_all('clients.need_notification',
          'endpoint.clients.connected')
def notify_related_units():
    clients = from_flag('endpoint.clients.connected')
    clients.notify_service_stopped()
    clear_flag('clients.need_notification')

See Coupling Flags with Triggers for more information.