I like to think of it in this manner:
A charm is a piece of software that models an application. An application in the juju sense is a server or collection of servers (be they metals, vms, or containers) which provide a singular addressable service which are deployed by juju into a cloud and configured via the charm source code based on the pre-defined hooks. Each charm has configuration variables such as installation source, ports, etc, as appropriate for configuring the software. The deployment of a charm and a set of configurations for that charm is called an “application” (previous versions of juju called this a “service”). If you wanted to run two different mysql clusters, you would deploy the mysql charm twice, perhaps named mysql-development and mysql-production. Each of these charm deployments is an “application” in the juju nomenclature, using the same “charm” - mysql. Maybe you run mysql-development application with 2 “units” and run on port=1234. And mysql-production may be an application with 10 “units” and run on port=2345. “Units” are instantiations of the application (charm code plus configuration values) on a metal, vm, or container. The “unit” is where the charm code is actually executed. Hooks are called on each individual unit of an application as model states change based on operator input - add unit, add relation, remove unit, change a configuration value - or based on reactions to relations to peer or other application units (remote relations, like a mysql client registering a new user/database, or a second unit of mysql peering with the first unit of the same application).
There are five “versions” to be tracked within a juju deployment:
- Controller Model Agent Version - The version of the juju API and database service running in the current cloud environment (whether MAAS, Openstack, K8s, AWS, GCP, Azure, or LXD/localhost). This can be queried with
juju status -m controller | head -2
(i.e. 2.9.10). - Model Agent Version - The version of the model (which contains deployed applications) agent that will run the code on the various deployed units. This can be queried with
juju status -m <model-name> | head -2
, and each machine and unit’s agent version can be seen withjuju status -m <model-name> --format yaml
. - Charm version - The version of the charm code which is deployed to each of the juju application’s deployed units which controls how the software (workload) is installed and configured. This is the charmhub version of the uploaded charm code (charm store version 2 as described above by @pengale). This is available in the second-to-right-hand column of
juju status
’s Applications section, or available fromjuju status --format yaml
as thecharm: cs:some-charm-<version>
section of a given application stanza. - Workload version - The actual software that is running as configured by the charm based on the charm code version and application configuration. Most useful when you have the “source”, “openstack-origin”, “snap_channel” or other similar configuration set to “latest”. In the context of kubernetes operators, this is most useful when the charm developer queries the deployed OCI container image and posts the version of the software within the container via set_workload_version() to avoid having to query kubernetes directly. This is optional metadata provided by the charm developer that may be available in the second column of
juju status
in the Applications section, or in the version key of an application stanza withinjuju status --format yaml
. - Machine version - This is the version of the operating system running on the machine (metal, VM, lxd, container) i.e bionic, focal. These can be seen in the output of
juju machines
I think when we talk about the “workload” we should be thinking “snaps, container images, or package versions of the running software which provides the useful service”. All versions other than the workload version and machine version are related to the juju configuration management engine and should not have a major bearing on the functionality and security of the deployed service itself.
A given charm, such as nova-compute, may support installation of many different versions of that software (like the nova versions related to Mitaka, Queens, Ussuri, etc), hence the helpful hints of workload_version to make the info available in juju status, rather than checking the charm’s configuration or logging into the unit to get the data from the OS/packaging system.
As Pete noted, this workload_version is not something that juju reacts to, rather it is useful metadata able to be provided by a charm developer for operator visibility of the workload status.
In the case of something like nova-compute, the charm release version is something like 21.04 (this is the charm team’s “release” version and info related to the git repository commit for branch stable/21.04 will show up in the repo-info file - not a standard, but a per-charm team “charmhub → git” translation helper provided by their CI/CD process). This is then published to charmhub with an auto-incrementing version along the lines of charm version #123. Then the operator can configure the openstack-origin setting of the nova-compute application to something like cloud:bionic-ussuri and the version of the workload becomes 21.0.x as the charm code upgrades/installs the packages from the associated repository (or snap channel or OCI image). There are certainly overlaps of which versions of charms support specific versions of workload software, and that must be communicated within release notes and README files of the charm itself.