Manage configration with the Operator Framework

Add configuration

One goal of charms is to allow a user to quickly and easily deploy an application they are not familiar with. The charm should install and configure the application with production ready default settings. However, there are cases when it is necessary to expose a knob for the administrator to adjust a setting of the underlying application. This can be accomplished with charm configs.

config.yaml

The first step to charm configuration is defining the options which the charm supports. This is done in the file config.yaml. In a new charm, this file will have these contents:

options:
  thing:
    default: 🎁
    description: A thing used by the charm.
    type: string

options

The first line of the yaml is the key options. This key is mandatory and represents a dictionary of named configuration options, able to be adjusted by an administrator.

The second line of the yaml is the key thing - an example configuration created during charmcraft init. This key is the name of a configration to be exposed by the charm. This key represents another dictionary, with three fields.

type

Type is a required field, which defines a data-type to be validated by Juju when an administrator attempts to change a configuration. Valid values include: string, int, float, boolean. By defining type in config.yaml, Juju can validate the type without invoking the charm code.

description

Description is a required field. It provides the administrator with a detailed description of the configuration and its possible values. The description will be indexed when the charm is published and displayed on Charmhub along with the charm’s README.

default

Default is an optional field. It contains a default value for the configuration when one has not been specified by the administrator. If the default field is ommitted and the administrator does not provide a value, the Operator Framework will return the Python object None when the associated configuration is referenced.

Python

Configuration values are accessed in your Python code by referencing the model construct of the Operator Framework:

self.model.config["thing"] # returns "🎁"

Example

One of the most common needs to expose a configuration is to define the bind port for an application. This allows the administrator to prevent port conflicts with multiple applications are required to use the same network interface.

config.yaml

options:
  application_port:
    default: 9000
    description: The port HelloApp will listen to for incoming connections
    type: int

These new lines define an integer type configuration option, with a default value of 9000.

Our style guide recommends all multi-word configuration names be concatenated with an underscore.

src/charm.py

In order for your charm code to properly capture a configuration value which has been changed, the charm will need to observe the config-changed lifecycle event.

class HelloCharm(CharmBase):

    def __init__(self, *args):
        super().__init__(*args)
        self.framework.observe(self.on.config_changed, self._on_config_changed)
        
    def _on_config_changed(self, _):
        port = self.model.config["application_port"] # returns 9000
        # do something with 'port'

This code specifies a new observer using the Operator Framework. It also defines a new private function, which will be executed when the observed config_changed event is emitted.

Changing a configuration

An administrator can set a configuration at deploy time:

$ juju deploy ./hello.charm --config application_port=9090

A configuration can also be changed for a running application:

$ juju config hello application_port=9090