Does python charms require charmhelpers?

I’m trying to put together a python charm which should not need to install or include the “charmhelpers” python package, but still implement a relation with a few remote units.

However, all documentation I can find on python charms seems to put charm-helpers/charmhelpers as a hard requirement (It can’t be, right?).

Relying on charmhelpers, allow for many different paths to add that requirement as part of installing charms, which makes it difficult to point out a “best practice” path for charming with python. I’ve seen a number of innovative ways to install that package as part of charming:

  1. Charm-tools (with charm create -t basic-python) tries to clone the bzr repo into the charm ./lib directory thereby attaching the entire codebase to the charm. This has some pros and cons which I won’t cover here.
  2. Some would install “charmhelpers” as part of the “install” hook.
  3. reactive has a layer that deals with this.
  4. Modifying the cloudinituserdata, adding in the charmhelpers package/code as part of the model config.
  5. Via some kind of mechanism using “charm-helpers.yaml” described here. Getting Started — Charm Helpers 0.20.10 documentation which seems to be dependent on the charm-tools (?)
  6. … more?

My question is: “Is there a clear path to implement a juju charm with python without charmhelpers”?

With this information, I hope to iron out a new tutorial covering this topic…

In the same way that any other language can implement a charm, it is possible for a python charm to be written without charmhelpers. What that would end up looking like is something along these lines:

#!/usr/bin/python3
import json
import subprocess

def main():
  # Let's call this a relation-changed hook where I need to get some config and set data on a relation
  config_raw = subprocess.check_output(['config-get', '--all', '--format=json'])
  config = JSON.parse(config_raw)
  value = config.get("value") # No idea what kind of config we want, but this should serve nicely ;-)
  key = 'my_awesome_relation_key'
  subprocess.check_call(['relation-set', '{}={}'.format(key, value)])

if __name__ == '__main__':
  main()

By the end of the above block, we’re read some config, parsed the JSON that it got returned as, and then set some data on a relation. It’s a bit brittle, as there are a lot of potential exceptions we’re not handling but hey, it works without charmhelpers!

On the other hand, we could write the same, with charmhelpers, as:

#!/usr/bin/python3
from charmhelpers.core.hookenv import ( config_get, relation_set )

def main():
  # Let's call this a relation-changed hook where I need to get some config and set data on a relation
  rel_data = {
    'my_awesome_relation_key': config().get("value") # 1, below
  }
  relation_set(rel_data) # 2, below

if __name__ == '__main__':
  main()

These helper functions are quite a bit more complex than my basic examples above, see:
1: https://github.com/juju/charm-helpers/blob/1989f1f255baefb34cba7863606915dcd4ef1e38/charmhelpers/core/hookenv.py#L425
2: https://github.com/juju/charm-helpers/blob/1989f1f255baefb34cba7863606915dcd4ef1e38/charmhelpers/core/hookenv.py#L479

3 Likes

@chris.macnaughton this is very valuable info and code to come with it.

I will try to produce a python charm that is without charmhelpers and yet can handle a super simple master-worker pattern.

It will be part of a tutorial and I think it would be very important to get this right if people are to use it later as a reference. E.g. simple enough code to be able to start with, yet accurate enough not be able to build on.

If you have any thoughts on a code base that lives up to this ambition , I would love to look at it. If not, I’ll try post my own take on this here once I’ve cracked it myself so that you can give feedback.

My goal is to help beginners get started with producing charms with relations, building on the hooks framework only. This is in line with a series of tutorials I’ve posted before. This tutorial will be the natural next step.

Have you guys taken a look at the new Python Operator Framework? It’s pretty much exactly this; pure Python approach without the messiness of reactive layers. A high level intro doc is coming, for the moment here is the detail:

3 Likes

@sabdfl Yes, I have been looking at it briefly but I’m also in the process of getting my head around the details on how juju actually works from a hooks only perspective before I dig into ops. Partially because I also want to complete the line of tutorials I’ve written before on hooks-only. It feels important to complete that path to help people like myself getting productive with what they have learned previously.

I’m slowly getting there…

1 Like