What's the "preferred" way to write charms?

Sorry for being innocence, are there any material regarding whether one should use ops.framework or charms.reactive framework to create charms?

I got the impression that it’s in the process of transitioning into ops.framework, but can’t find any material online that states when and how that will happen.

I’m surveying a devops solution where we can move from public cloud into our own data-center without much friction and juju seems to be a fine solution. But ease of writing and maintaining ops.code will be evaluated as well. Appreciate any info on this topic!

TLDR:

  1. What’s the preferred way to write charms today?
  2. Is charms transitioned into operator?
  3. Is ops.framework replacing charms.reactive?

Hi there,

Thanks for dropping by! :slight_smile:

The preferred approach to writing Charms now is to use the Python Operator Framework. The Reactive framework was it’s predecessor, and though Reactive charms will still work, the Python Operator Framework is actively maintained.

Depending on whether you are developing charms to run on VMs/Machines or Kubernetes, there are a number of examples available on the charmhub store.

Let me know if you have any further questions!

Jon

1 Like

Thanks for your reply! That clears some of my doubts.
If operator.framework is succeeding charms.reactive, are they architecture guide comparing the two?
In charms.reactive, there are layer’s/interface’s that are composable in composed into a charm, is operator on top of operator the comparable pattern here?

Hello!

“Operator on top of operator” is not a pattern in this new world.

Now Charms are more pythonic, and the idea is to use the full language set when writing charms. For example, the Charm itself is a “class”. Interfaces can be written also using classes, even triggering events for the charm main class. Etc.

2 Likes

Didn’t notice I used another account to reply… Oops.

Can I interpret this into that sub-classing is THE way to replace layer.yaml. And one will directly subclass something like apt-operator, systemd-operator instead of third-party layers from https://github.com/juju/layer-index.

Also, this seems to contributed into allowing re-usability at ops code level instead of config level. Although I’m not against the idea and even find this more clearer, I think that discoverability about what a charm would do is kinda flattened.

Are there any plans for creating a landing page or curated list of community-driven sub-classes that a charm can consume? It would be a lot harder to find them from pypi IMHO.

Yes. The new charm store (charmhub) is growing an explicit listing of charm libraries, which are meant to be the shareable pieces of code for charms. And ‘charmcraft’ itself is growing ways to help define what libraries you are using, publishing them, sharing them, etc. Things like pypi will still work in the new system (it is just python code and normal patterns for how to assemble python code together still work), but the overhead of creating a pypi project felt too heavyweight for small, very sharable, libs for charms.

I think that today the focus is shifted into how to make kubernetes charms with the sidecar pattern, so some of the people working on progress towards sharable charm libs are focused on the other use case.

For a concrete example of where we are going, you would add
https://github.com/canonical/ops-lib-pgsql/blob/8a37afd1365aa97a5dd8b6c9faad08bb91690165/pgsql/client.py

ops-lib-pgsql into your project, and then

from ops.lib.pgsql import PostgreSQLClient

...
class MyCharm(CharmBase):

  def __init__(...):
    ...
    self.pgclient = PostgreSQLClient(self, 'db')

Which then registers the appropriate relation handlers (eg db_relation_changed) and provides you with higher level event handlers (eg self.pgclient.on.master_available)

1 Like

Thanks for your explanation about the paradigm shift.
It surely felt somewhat different than what I learned about juju.

The demo is really straight forward, I’ll try port some of my legacy ops code and see how’s ergonomics feels.

I would say that selecting frameworks depends alot about your intention and ambitions.

I personally first develop my charms using only core hooks with bash charms.

From that I learn how complex my charm needs to be.

Then I port the charm to ops (operator) if the value is added. After all, it’s not always needed to add a full framework to do small charms with little workload.

Reactive is dead to me. Way to complex. Super difficult. Value added, low.

I have written a few tutorials on how to write hooks only charms. I suggest checking out the “master worker” pattern which has proven successful material to some of my colleagues struggling to learn juju charming… Let me know and I can help you out if you get stuck.

… Oh, and then there is the Kubernetes charms which I have no knowledge about yet. Completely different class of charms.

… and finally, dont forget to join the community chat at https://chat.charmhub.io

Just to clean up some loose ends in this thread.

With the new ops.framework you can import pure-Python libraries published by other charmers, which offer up functions and classes that are useful for integrating your charm with their charm. We call those ‘charm libraries’ and charmhub.io pages for a charm will explicitly list them if the publisher has shared any. There is tooling in charmcraft to fetch and update these charm libraries in your own charm tree, so you always have the latest point version that matches your selected major version of those libraries.

@extraymond you described this as ‘subclassing the charm’ which is not quite right. You import the charm library. You may well subclass a class from the charm library For example, the charm library for MySQL might offer you a class like SingleRequiredDBEndpoint which is ‘an object that reflects the database related to an endpoint, where it is an error to have no database related, and equally an error to have more than one database related’.

The idea is to have the MySQL charm author do all the work for you, so that you just handle the event where ‘the single database I was expecting is now available’ as well as any other events for things like failover moments or situations where theat database gets made readonly for upgrade purposes, etc.

The idea is to stay pure Python, and feel native. Of course, as Eric says, there is value in proof-of-concept bash charms too! But we’re focused on committed product-grade charms, where Python and these library and test mechanisms really improve the experience of your devops.

One other point that may be interesting to you Eric, is that we think that the new approach to Kubernetes charms should make it pretty straightforward to have an identical charm which drives the containerised workload on K8s and also on machines without K8s.

Of course, if your charm wants to take advantage of k8s-specific substrate features, those will not be available on machines, but it might still be really useful for future charmers to be able to offer unified k8s and non-k8s charms.

1 Like

Thanks for your reply, I can see that it’s about composition rather than inheritance now.

Once key aspect that juju draw my attention was the ability to build both k8s and non-k8s charms using the same ops.framework.
Using pure python rather than some vendor lock-in format is appealing too.

1 Like

You’ve got it all right m8! There is alot of challenges to juju, but once you get over the steep learning-curve, you never go back.

Juju is real “Infrastructure as Code” which your point about Python highlight perfectly.

Let me know if you want help learning more juju charm development although I still have to learn the K8 part myself. I’ve covered some ground, but still I feel like I have so much more to learn here…

There will be some docs coming soon about the new implementation details for Kubernetes charms! I think the initial release of the docs will be at the end of next week - and in the mean time there are some example charms noted in this post