From 37650135082030830c6420a404933bdc500d26c1 Mon Sep 17 00:00:00 2001 From: Ben Hoyt Date: Thu, 15 Jun 2023 00:32:03 +0100 Subject: [PATCH] Remove jargon from and simplify README (#950) Remove a lot of the "operator" and K8s jargon from the top of our README. Also simplify and update some of the terminology, consistent with our updated Juju Terminology doc. The first part of the doc is copied to ops/__init__.py for consistency. That appears at the top of our API reference at https://ops.readthedocs.io/ Move the note about dependencies to HACKING.md where I think it belongs. * Other tweaks per @jnsgruk's review * Replace "Operator Framework" with "ops library" or just "ops" Also replace "operator" with "admin" in a few places where it means the human operator, as "operator" is ambiguous. Also have separate TOC entries for each module in docs * Operator Framework -> ops in CI YAML too --- .github/workflows/db-charm-tests.yaml | 2 +- .github/workflows/framework-tests.yaml | 2 +- HACKING.md | 16 ++- README.md | 137 +++++++++---------------- docs/conf.py | 2 +- docs/index.rst | 16 +-- ops/__init__.py | 42 ++++---- ops/charm.py | 2 +- ops/framework.py | 4 +- ops/log.py | 2 +- ops/main.py | 4 +- ops/model.py | 6 +- ops/storage.py | 2 +- ops/testing.py | 2 +- ops/version.py | 4 +- setup.py | 2 +- test/charms/test_smoke/src/charm.py | 2 +- test/test_main.py | 2 +- 18 files changed, 108 insertions(+), 141 deletions(-) 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__)