Juju Ops Framework
Ops is the official Python library for writing Juju charms, enabling developers to build robust and reactive operators for cloud-native applications. It provides high-level abstractions for interacting with Juju, handling lifecycle events, managing application status, and interacting with container workloads via Pebble. The library is actively maintained, with frequent releases addressing bug fixes, performance improvements, and compatibility with the latest Juju versions, typically every few weeks.
Warnings
- breaking The default Juju version used in `ops.testing.Context` for mock environments was updated from Juju 2.x to Juju 3.6.14. Charms or tests that implicitly relied on specific Juju 2.x behaviors during testing without explicitly setting the Juju version might encounter unexpected failures.
- gotcha When `PebbleClient.exec()` fails (e.g., due to timeout), the exception message now only includes the *first item* of the executed command, not the entire command string. This change protects against sensitive data leaking into exception logs.
- gotcha By default, exceptions raised directly from charm code during `ops.testing` state-transition tests are wrapped in an `UncaughtCharmError`. While this helps distinguish charm errors, it can obscure the original exception type, making debugging and asserting specific error types more cumbersome.
- gotcha The `ops.hookcmds` module provides a low-level, direct API for Juju hook commands. This API is powerful but intended primarily for building experimental charm APIs or frameworks rather than for direct use within production charms. Direct usage can lead to less portable or harder-to-maintain charm code that bypasses `ops` abstractions.
Install
-
pip install ops -
pip install ops[testing]
Imports
- CharmBase
from ops.charm import CharmBase
- Framework
from ops.framework import Framework
- StoredState
from ops.framework import StoredState
- main
from ops.main import main
- Container
from ops.pebble import Container
- PebbleClient
from ops.pebble import PebbleClient
- Context
from ops.testing import Context
- ActiveStatus
from ops.model import ActiveStatus
- BlockedStatus
from ops.model import BlockedStatus
Quickstart
import ops
from ops.charm import CharmBase
from ops.framework import StoredState
class MyCharm(CharmBase):
_stored = StoredState()
def __init__(self, framework: ops.Framework):
super().__init__(framework)
self.framework.observe(self.on.install, self._on_install)
self.framework.observe(self.on.config_changed, self._on_config_changed)
self._stored.set_default(initialized=False)
def _on_install(self, event: ops.InstallEvent):
# Example: Set initial status and workload version
self.unit.status = ops.BlockedStatus("Waiting for configuration")
self.unit.set_workload_version("v1.0.0")
self._stored.initialized = True
self.logger.info("Charm installed.")
def _on_config_changed(self, event: ops.ConfigChangedEvent):
# Example: Update status after configuration change
if self._stored.initialized:
self.unit.status = ops.ActiveStatus("Ready")
self.logger.info("Configuration changed and charm is active.")
if __name__ == "__main__":
# The main entry point for a Juju charm
ops.main(MyCharm)