diff --git a/.github/workflows/db-charm-tests.yaml b/.github/workflows/db-charm-tests.yaml index 24c760282..05b60ce97 100644 --- a/.github/workflows/db-charm-tests.yaml +++ b/.github/workflows/db-charm-tests.yaml @@ -1,4 +1,4 @@ -name: Data Platform Charm Tests +name: Data Charm Tests on: [push, pull_request, workflow_call] diff --git a/.github/workflows/framework-tests.yaml b/.github/workflows/framework-tests.yaml index dee669d04..20a7695be 100644 --- a/.github/workflows/framework-tests.yaml +++ b/.github/workflows/framework-tests.yaml @@ -1,4 +1,4 @@ -name: Operator Framework Tests +name: ops Tests on: [push, pull_request, workflow_call] diff --git a/HACKING.md b/HACKING.md index dff0026d8..16aeb75f2 100644 --- a/HACKING.md +++ b/HACKING.md @@ -86,14 +86,14 @@ should always be accompanied by user-focused documentation that is posted to https://juju.is/docs/sdk. The content for this site is written and hosted on https://discourse.charmhub.io/c/doc. New documentation should get a new topic/post on this discourse forum and then should be linked into the main -docs navigation page(s) as appropriate. The operator framework SDK page +docs navigation page(s) as appropriate. The ops library's SDK page content is pulled from [here](https://discourse.charmhub.io/t/the-charmed-operator-software-development-kit-sdk-docs/4449). Each page on [juju.is](https://juju.is/docs/sdk) has a link at the bottom that takes you to the corresponding discourse page where docs can be commented on and edited (if you have earned those privileges). -The Operator Framework API docs are automatically built and published to +The ops library's API reference is automatically built and published to [here](https://ops.readthedocs.io/en/latest/). Please be complete with docstrings and keep them informative for _users_. @@ -105,9 +105,17 @@ Currently we don't publish separate versions of documentation for separate relea next to the relevant content (e.g. headings, etc.). + +## Dependencies + +The Python dependencies of `ops` are kept as minimal as possible, to avoid +bloat and to minimise conflict with the charm's dependencies. The dependencies +are listed in [requirements.txt](requirements.txt). + + # Publishing a Release -To make a release of the Operator Framework, do the following: +To make a release of the ops library, do the following: 1. Visit the [releases page on github](https://github.com/canonical/operator/releases). 2. Click "Draft a new release" @@ -118,7 +126,7 @@ To make a release of the Operator Framework, do the following: This will trigger an automatic build for the Python package and publish it to PyPI (the API token/secret is already set up in the repository settings). -See [.github/workflows/publish.yml](.github/workflows/publish.yml) for details. (Note that the versions in publish.yml refer to versions of the github actions, not the versions of the Operator Framework.) +See [.github/workflows/publish.yml](.github/workflows/publish.yml) for details. (Note that the versions in publish.yml refer to versions of the github actions, not the versions of the ops library.) You can troubleshoot errors on the [Actions Tab](https://github.com/canonical/operator/actions). diff --git a/README.md b/README.md index 4ac030e47..8fa10ef1d 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,59 @@ -# The Charmed Operator Framework +# The ops library -This Charmed Operator Framework simplifies [operator](https://charmhub.io/about) development -for [model-driven application management](https://juju.is/model-driven-operations). + -Operators emerged from the Kubernetes community; an operator is software that drives lifecycle -management, configuration, integration and daily actions for an application. Operators simplify -software management and operations. They capture reusable app domain knowledge from experts in a -software component that can be shared. +The ops library is a Python framework ([`available on PyPI`](https://pypi.org/project/ops/)) for developing +and testing [Juju](https://juju.is/) charms in a consistent way, using standard Python constructs +to allow for clean, maintainable, and reusable code. -This project extends the operator pattern to enable -[charmed operators](https://juju.is/universal-operators), not just for Kubernetes but also -operators for traditional Linux application management. +A charm is an operator -- business logic encapsulated in a reusable software +package that automates every aspect of an application's life. -Operators use a [Charmed Operator Lifecycle Manager -(Charmed OLM)](https://juju.is/operator-lifecycle-manager) to coordinate their work in a cluster. -The system uses Golang for concurrent event processing under the hood, but enables the operators to -be written in Python. +Charms written with ops support Kubernetes using Juju's "sidecar charm" +pattern, as well as charms that deploy to Linux-based machines and containers. -## Simple, composable operators +Charms should do one thing and do it well. Each charm drives a single +application and can be integrated with other charms to deliver a complex +system. A charm handles creating the application in addition to scaling, +configuration, optimisation, networking, service mesh, observability, and other +day-2 operations specific to the application. -Operators should 'do one thing and do it well'. Each operator drives a single microservice and can -be [composed with other operators](https://juju.is/integration) to deliver a complex application. +The ops library is part of the Charm SDK (the other part being Charmcraft). +Full developer documentation for the Charm SDK is available at +https://juju.is/docs/sdk. -It is better to have small, reusable operators that each drive a single microservice very well. -The operator handles instantiation, scaling, configuration, optimisation, networking, service mesh, -observability, and day-2 operations specific to that microservice. +To learn more about Juju, visit https://juju.is/docs/olm. -Operator composition takes place through declarative integration in the OLM. Operators declare -integration endpoints, and discover lines of integration between those endpoints dynamically at -runtime. -## Pure Python operators +## Pure Python -The framework provides a standard Python library and object model that represents the application -graph, and an event distribution mechanism for distributed system coordination and communication. +The framework provides a standardised Python object model that represents the +application graph, as well as an event-handling mechanism for distributed +system coordination and communication. -The OLM is written in Golang for efficient concurrency in event handling and distribution. -Operators can be written in any language. We recommend this Python framework for ease of design, -development and collaboration. +The latest version of ops requires Python 3.8 or above. -## Better collaboration +Juju itself is written in Go for efficient concurrency even in large +deployments. Charms can be written in any language, however, we recommend using +Python with this framework to make development easier and more standardised. +All new charms at Canonical are written using it. -Operator developers publish Python libraries that make it easy to integrate your operator with -their operator. The framework includes standard tools to distribute these integration libraries and -keep them up to date. -Development collaboration happens at [Charmhub.io](https://charmhub.io/) where operators are -published along with integration libraries. Design and code review discussions are hosted in the -Charmhub [discourse]. We recommend the [Open Operator Manifesto](https://charmhub.io/manifesto) -as a guideline for high quality operator engineering. +## Getting started -## Event serialization and operator services +A package of operator code is called a charmed operator or simply "charm". +You'll use [charmcraft](https://juju.is/docs/sdk/install-charmcraft) to +register your charm name and publish it when you are ready. You can follow one +of our [charming tutorials](https://juju.is/docs/sdk/tutorials) to get started +writing your first charm. -Distributed systems can be hard! So this framework exists to make it much simpler to reason about -operator behaviour, especially in complex deployments. The Charmed OLM provides -[operator services](https://juju.is/operator-services) such as provisioning, event delivery, -leader election and model management. -Coordination between operators is provided by a cluster-wide event distribution system. Events are -serialized to avoid race conditions in any given container or machine. This greatly simplifies the -development of operators for high availability, scale-out and integrated applications. +## Testing your charms -## Model-driven Operator Lifecycle Manager - -A key goal of the project is to improve the user experience for admins working with multiple -different operators. - -We embrace [model-driven operations](https://juju.is/model-driven-operations) in the Charmed -Operator Lifecycle Manager. The model encompasses capacity, storage, networking, the application -graph and administrative access. - -Admins describe the application graph of integrated microservices, and the OLM then drives -instantiation. A change in the model is propagated to all affected operators, reducing the -duplication of effort and repetition normally found in operating a complex topology of services. - -Administrative actions, updates, configuration and integration are all driven through the OLM. - -# Getting started - -A package of operator code is called a charmed operator or “charm. You will use `charmcraft` to -register your operator name, and publish it when you are ready. There are more details on how to -get a complete development environment setup over in the -[documentation](https://juju.is/docs/sdk/dev-setup) - -Charmed Operators written using the Charmed Operator Framework are just Python code. The goal -is to feel natural for somebody used to coding in Python, and reasonably easy to learn for somebody -who is not a pythonista. - -The dependencies of the operator framework are kept as minimal as possible; currently that's Python -3.8 or greater, and the small number of Python libraries referenced in [requirements.txt](requirements.txt). - -For a brief intro on how to get started, check out the -[Hello, World!](https://juju.is/docs/sdk/hello-world) section of the documentation! - -# Testing your charmed operators - -The operator framework provides a testing harness, so you can check your charmed operator does the -right thing in different scenarios, without having to create a full deployment. -`pydoc3 ops.testing` has the details, including this example: +The framework provides a testing harness, so you can ensure that your charm +does the right thing in different scenarios, without having to create +a full deployment. Our [API documentation](https://ops.readthedocs.io/en/latest/#module-ops.testing) +has the details, including this example: ```python harness = Harness(MyCharm) @@ -111,21 +67,22 @@ harness.update_relation_data(relation_id, 'postgresql/0', {'key': 'val'}) self.assertEqual(harness.charm. ...) ``` + ## Talk to us If you need help, have ideas, or would just like to chat with us, reach out on the Charmhub [Mattermost]. -We also pay attention to the Charmhub [Discourse] +We also pay attention to the Charmhub [Discourse]. -You can deep dive into the [API docs] if that's your thing. +And of course you can deep dive into the [API reference]. -[discourse]: https://discourse.charmhub.io -[api docs]: https://ops.rtfd.io/ -[sdk docs]: https://juju.is/docs/sdk -[mattermost]: https://chat.charmhub.io/charmhub/channels/charm-dev +[Discourse]: https://discourse.charmhub.io/ +[API reference]: https://ops.readthedocs.io/ +[Mattermost]: https://chat.charmhub.io/charmhub/channels/charm-dev -## Operator Framework development -See [HACKING.md](HACKING.md) for details on dev environments, testing, etc. +## Development of the framework +See [HACKING.md](HACKING.md) for details on dev environments, testing, and so +on. diff --git a/docs/conf.py b/docs/conf.py index a1822577b..f335d64f5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ def _compute_navigation_tree(context): # -- Project information ----------------------------------------------------- -project = 'The Operator Framework' +project = 'The ops library' copyright = '2019-2023, Canonical Ltd.' author = 'Canonical Ltd' diff --git a/docs/index.rst b/docs/index.rst index f5e3a73c4..cc0accf9b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,31 +1,31 @@ -Welcome to The Operator Framework's documentation! -================================================== +ops library API reference +========================= .. toctree:: :maxdepth: 2 :caption: Contents: -ops package -=========== +ops module +========== .. automodule:: ops ops.pebble module ------------------ +================= .. automodule:: ops.pebble ops.testing module ------------------- +================== .. automodule:: ops.testing -Indices and tables -================== +Indices +======= * :ref:`genindex` * :ref:`modindex` diff --git a/ops/__init__.py b/ops/__init__.py index 6bcb65805..840ecca30 100644 --- a/ops/__init__.py +++ b/ops/__init__.py @@ -12,31 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""The Charmed Operator Framework. +# NOTE: The text below is also at the top of README.md. Keep in sync! -The Charmed Operator Framework allows the development of operators in a simple -and straightforward way, using standard Python structures to allow for clean, -maintainable, and reusable code. +"""The ops library: a Python framework for writing Juju charms. -A Kubernetes operator is a container that drives lifecycle management, -configuration, integration and daily actions for an application. Operators -simplify software management and operations. They capture reusable app domain -knowledge from experts in a software component that can be shared. +The ops library is a Python framework (`available on PyPI`_) for developing +and testing Juju charms in a consistent way, using standard Python constructs +to allow for clean, maintainable, and reusable code. -The Charmed Operator Framework extends the "operator pattern" to enable Charmed -Operators, packaged as and often referred to as "charms". Charms are not just -for Kubernetes but also operators for traditional Linux application management. -Operators use an Operator Lifecycle Manager (OLM), like Juju, to coordinate -their work in a cluster. The system uses Golang for concurrent event processing -under the hood, but enables the operators to be written in Python. +A charm is an operator -- business logic encapsulated in a reusable software +package that automates every aspect of an application's life. -Operators should do one thing and do it well. Each operator drives a single -application or service and can be composed with other operators to deliver a -complex application or service. An operator handles instantiation, scaling, -configuration, optimisation, networking, service mesh, observability, -and day-2 operations specific to that application. +Charms written with ops support Kubernetes using Juju's "sidecar charm" +pattern, as well as charms that deploy to Linux-based machines and containers. -Full developer documentation is available at https://juju.is/docs/sdk. +Charms should do one thing and do it well. Each charm drives a single +application and can be integrated with other charms to deliver a complex +system. A charm handles creating the application in addition to scaling, +configuration, optimisation, networking, service mesh, observability, and other +day-2 operations specific to the application. + +The ops library is part of the Charm SDK (the other part being Charmcraft). +Full developer documentation for the Charm SDK is available at +https://juju.is/docs/sdk. + +To learn more about Juju, visit https://juju.is/docs/olm. + +.. _available on PyPI: https://pypi.org/project/ops/ """ # The "from .X import Y" imports below don't explicitly tell Pyright (or MyPy) diff --git a/ops/charm.py b/ops/charm.py index 11228b0de..65805af1b 100755 --- a/ops/charm.py +++ b/ops/charm.py @@ -117,7 +117,7 @@ def defer(self): raise RuntimeError('cannot defer action events') def restore(self, snapshot: Dict[str, Any]): - """Used by the operator framework to record the action. + """Used by the framework to record the action. Not meant to be called directly by charm code. """ diff --git a/ops/framework.py b/ops/framework.py index e85330509..954e89c94 100755 --- a/ops/framework.py +++ b/ops/framework.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""The Operator Framework infrastructure.""" +"""The ops library's infrastructure.""" import collections import collections.abc @@ -541,7 +541,7 @@ def __str__(self): class Framework(Object): - """Main interface from the Charm to the Operator Framework internals.""" + """Main interface from the Charm to the ops library's infrastructure.""" on = FrameworkEvents() # type: ignore diff --git a/ops/log.py b/ops/log.py index 9432b6f78..f16bfaf38 100644 --- a/ops/log.py +++ b/ops/log.py @@ -35,7 +35,7 @@ def __init__(self, model_backend: "_ModelBackend", level: int = logging.DEBUG): def emit(self, record: logging.LogRecord): """Send the specified logging record to the Juju backend. - This method is not used directly by the Operator Framework code, but by + This method is not used directly by the ops library, but by :class:`logging.Handler` itself as part of the logging machinery. """ self.model_backend.juju_log(record.levelname, self.format(record)) diff --git a/ops/main.py b/ops/main.py index d6e3ccd33..6bbc2d3fe 100755 --- a/ops/main.py +++ b/ops/main.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Main entry point to the Operator Framework. +"""Main entry point to the framework. Note that this module is callable, and calls the :func:`ops.main.main` function. This is so that :code:`import ops` followed by :code:`ops.main(MyCharm)` works @@ -379,7 +379,7 @@ def main(charm_class: Type[ops.charm.CharmBase], model_backend = ops.model._ModelBackend() debug = ('JUJU_DEBUG' in os.environ) setup_root_logging(model_backend, debug=debug) - logger.debug("Operator Framework %s up and running.", ops.__version__) # type:ignore + logger.debug("ops %s up and running.", ops.__version__) # type:ignore dispatcher = _Dispatcher(charm_dir) dispatcher.run_any_legacy_hook() diff --git a/ops/model.py b/ops/model.py index 51d03883c..7d24ec5ca 100644 --- a/ops/model.py +++ b/ops/model.py @@ -363,7 +363,7 @@ def status(self, value: 'StatusBase'): def planned_units(self) -> int: """Get the number of units that Juju has "planned" for this application. - E.g., if an operator runs "juju deploy foo", then "juju add-unit -n 2 foo", the + E.g., if an admin runs "juju deploy foo", then "juju add-unit -n 2 foo", the planned unit count for foo will be 3. The data comes from the Juju agent, based on data it fetches from the @@ -573,7 +573,7 @@ def open_port(self, protocol: typing.Literal['tcp', 'udp', 'icmp'], Calling this registers intent with Juju that the application should be accessed on the given port, but the port isn't actually opened - externally until the operator runs "juju expose". + externally until the admin runs "juju expose". On Kubernetes sidecar charms, the ports opened are not strictly per-unit: Juju will open the union of ports from all units. @@ -1596,7 +1596,7 @@ def __init__(self, message: str = ''): class BlockedStatus(StatusBase): """The unit requires manual intervention. - An operator has to manually intervene to unblock the unit and let it proceed. + An admin has to manually intervene to unblock the unit and let it proceed. """ name = 'blocked' diff --git a/ops/storage.py b/ops/storage.py index bebeb0655..97bce833c 100755 --- a/ops/storage.py +++ b/ops/storage.py @@ -340,7 +340,7 @@ def juju_backend_available() -> bool: class _JujuStorageBackend: - """Implements the interface from the Operator framework to Juju's state-get/set/etc.""" + """Implements the interface from the ops library to Juju's state-get/set/etc.""" def set(self, key: str, value: Any) -> None: """Set a key to a given value. diff --git a/ops/testing.py b/ops/testing.py index 1a93dcb0d..70bb5c4f4 100755 --- a/ops/testing.py +++ b/ops/testing.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Infrastructure to build unit tests for charms using the Operator Framework.""" +"""Infrastructure to build unit tests for charms using the ops library.""" import dataclasses diff --git a/ops/version.py b/ops/version.py index abd83ab31..79cc38106 100644 --- a/ops/version.py +++ b/ops/version.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Helper to define the version of the Operator Framework project. +"""Helper to define the version of the ops library. -This module is NOT to be used when developing charms using the Operator Framework. +This module is NOT to be used when developing charms using ops. """ import subprocess diff --git a/setup.py b/setup.py index 2810a4db3..22b776a35 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Setup script for the Operator Framework.""" +"""Setup script for the ops library.""" from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path diff --git a/test/charms/test_smoke/src/charm.py b/test/charms/test_smoke/src/charm.py index 29c982325..6e4e5346c 100755 --- a/test/charms/test_smoke/src/charm.py +++ b/test/charms/test_smoke/src/charm.py @@ -17,7 +17,7 @@ """Charm the service. Refer to the following post for a quick-start guide that will help you -develop a new k8s charm using the Operator Framework: +develop a new k8s charm using the ops library: https://discourse.charmhub.io/t/4208 """ diff --git a/test/test_main.py b/test/test_main.py index 5c81d827f..2e9d140fe 100755 --- a/test/test_main.py +++ b/test/test_main.py @@ -41,7 +41,7 @@ VERSION_LOGLINE = [ 'juju-log', '--log-level', 'DEBUG', '--', - f'Operator Framework {ops.__version__} up and running.', + f'ops {ops.__version__} up and running.', ] logger = logging.getLogger(__name__)