Scriptlets and Unit-Less Charms

Unit-less-ness

At the Madrid engineering sprint this year, our AI hackathon yielded the first proof-of-concept for what we call a unit-less charm.

Just like a regular charm, a unit-less charm responds to changes and schedules actions in order to converge on its desired state, but it has no provisioned cloud resource or workload – it runs directly on the controller.

But how do we get an application with behaviour, yet without any units?

Scriptlets

The basis for such charms are scriptlets written in Starlark, a lightweight Python-like scripting language designed for embedded use.

Where regular charms rely on an execution environment in their provisioned machine/container – Python, Bash etc, the controller provides the execution environment for unit-less charms. This can include the enforcement of bounds for memory and compute in order to protect the controller from rogue scriptlets, but that detail is not the subject of this post; see the Canonical Starlark repository linked above for more information.

Not That Different

To anchor in the familiar, let’s take the common charm in its Juju context.

  • The Juju agent watches for changes and compares local/remote state to determine what hooks to fire.
  • The charm defines logic to execute for each hook it wants to handle.
  • Juju dispatches hooks with a context, which includes data about the hook that the charm might need to know.
  • The charm upon receiving a hook can operate its workload, query Juju state and take actions via hook tools.

How does a scriptlet-based charm compare?

Strikingly similar, right? Events are synonymous with hooks, complete with associated data and per-event handlers. Scriptlets can’t have side-effects, but they can return intents, which Juju will action on behalf of the scriptlet, much like the committing of a regular hook at completion.

Even the syntax should be familiar to established charmers. This is from the charm that we wrote during the Madrid hackathon, which admittedly doesn’t do a lot, but shows the congruence with regular charm syntax.

def init():
    juju.observe("config_changed", on_config_changed)
    juju.observe("relation_created", on_relation_created)
    juju.observe("relation_joined", on_relation_joined)
    juju.observe("relation_changed", on_relation_changed)
    juju.observe("relation_departed", on_relation_departed)
    juju.observe("relation_broken", on_relation_broken)

def on_config_changed(event):
    config = event.config
    parts = []
    for key in sorted(config.keys()):
        parts.append("%s=%s" % (key, config[key]))
    msg = ", ".join(parts) if parts else "no config"
    juju.set_status("active", message = "config changed :" + msg)

def on_relation_created(event):
    juju.set_status("active", message = "relation created")

def on_relation_joined(event):
    juju.set_status("active", message = "related")
    juju.set_state("controller-uuid", event.controller_uuid)
    juju.set_state("model-name", event.model_name)

def on_relation_changed(event):
    juju.set_status("active", message = "relation changed")

def on_relation_departed(event):
    juju.set_status("waiting", message = "relation departed")

def on_relation_broken(event):
    juju.set_status("waiting", message = "relation broken")

How Might This Be of Use?

Canonical folks whose work intersects with the Juju ecosystem may over the years have come upon terms like stacks and governors. These are exactly the kind of long-desired capabilities that are within our reach now. But what are they?

  • Stacks are the grouping of of charms so that their aggregation can be treated as a single charm. Being able to relate a model to COS rather than each of the individual charms is just one potential scenario for stacks.
  • Governors affect models based on observed changes. This might be scaling changes based on conditions observed by the governor, or even wholesale deployment and relation of new charms to address changing model requirements.

The Future

The Juju team is adding this capability right now. But we need input from charm authors.

Today, the author of a charm has the following touch points with the unit agent:

  • The list of hooks that the charm can be issued and what changes in state they represent.
  • The data supplied with a hook context and the tools available to further query the current state, such as network-get.
  • The side effects that charms have as a result of handling hooks - open-ports, relation-set etc.

Now let your imagination run wild. What would be amazing? Think of the magic things that you would do if you could write a charm that endows the controller/model with your behaviour. What do you need to achieve this magic?

  • What events do you want to be notified about?
  • What data do you need about these events, and what do you want to query about the wider state of the world?
  • What actions do you want the controller to perform for you as a result of handing these events?

Reach out to the Juju team with your ideas and questions.

Acknowledgements

It must be remembered that althought we anticipate exciting features in Juju, the work on scriptlets was the labour of years from the OCTO team, particularly Marco Manino and @kcza.

7 Likes