Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

README: a significant rework #190

Merged
merged 12 commits into from May 18, 2020
179 changes: 89 additions & 90 deletions README.md
@@ -1,6 +1,7 @@
# The Operator Framework

The Operator Framework provides a simple, lightweight, and powerful way of encapsulating operational experience in code.
The Operator Framework provides a simple, lightweight, and powerful way of
writing juju charms, the best way to encapsulate operational experience in code.

The framework will help you to:

Expand All @@ -11,130 +12,128 @@ The framework will help you to:

## Getting Started

The following overall structure for your charm directory is recommended:
Charms written using the operator framework are just Python code. The intention
is for it to feel very natural for somebody used to coding in Python, and
reasonably easy to pick up for somebody who might be a domain expert but not
necessarily a pythonista themselves.

```
.
├── config.yaml
├── metadata.yaml
├── mod/
├── lib/
│ └── ops -> ../mod/operator/ops
├── src/
│ └── charm.py
└── hooks/
├── install -> ../src/charm.py
└── start -> ../src/charm.py # for k8s charms per below
```

The `mod/` directory should contain the operator framework dependency as a git
submodule:
The dependencies of the operator framework are kept as minimal as possible;
currently that's Python 3.5 or greater, and `PyYAML` (both are included by
default in Ubuntu's cloud images from 16.04 on).

```
git submodule add https://github.com/canonical/operator mod/operator
```
If you're new to the world of juju and charms, you should probably dive into our
[tutorial](/TBD).
facundobatista marked this conversation as resolved.
Show resolved Hide resolved

Then symlink from the git submodule for the operator framework into the `lib/`
directory of your charm so it can be imported at run time:
If you know about juju, and have written charms that didn't use the operator
framework (be it with reactive or without), we have an [introduction to the
operator framework](/TBD) just for you.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same than above.


```
ln -s ../mod/operator/ops lib/ops
```
If you've gone through the above already and just want a refresher, or are
really impatient and need to dive in, feel free to carry on down.

Other dependencies included as git submodules can be added in the `mod/`
directory and symlinked into `lib/` as well.
## A Quick Introduction

You can sync subsequent changes from the framework and other submodule
dependencies by running:
Operator framework charms are just Python code. The entry point to your charm is
a particular Python file. It could be anything that makes sense to your project,
but let's assume this is `src/charm.py`. This file must be executable (and it
must have the appropriate shebang line).

```
git submodule update
```
This file must be symlinked from `dispatch` (as of juju 3.8; otherwise from
`hooks/install`, and also from `hooks/start` if it's for k8s).
chipaca marked this conversation as resolved.
Show resolved Hide resolved

Those cloning and checking out the source for your charm for the first time
will need to run:
You also need the usual `metadata.yaml` and `config.yaml` files, and any Python
dependencies (maybe using some kind of virtualenv). In other words, your project
might look like this:

```
git submodule update --init
.
├── config.yaml
├── metadata.yaml
├── env/
│ └── # ... your Python dependencies, including the operator framework itself
├── src/
│ └── charm.py
└── dispatch -> src/charm.py
```

Your `src/charm.py` is the entry point for your charm logic. It should be set
to executable and use Python 3.6 or greater. At a minimum, it needs to define
a subclass of `CharmBase` and pass that into the framework's `main` function:
> 🛈 once `charmcraft build` works the layout will get simpler!

```python
import sys
sys.path.append('lib') # noqa: E402
`src/charm.py` here is the entry point to your charm code. At a minimum, it
needs to define a subclass of `CharmBase` and pass that into the framework's
`main` function:

```python
#!../env/bin/python3
chipaca marked this conversation as resolved.
Show resolved Hide resolved
from ops.charm import CharmBase
from ops.main import main


class MyCharm(CharmBase):
pass


if __name__ == "__main__":
main(MyCharm)
```

This charm does nothing, because the `MyCharm` class passed to the operator
framework's `main` function is empty. Functionality can be added to the charm
by instructing it to observe particular Juju events when the `MyCharm` object
is initialized. For example,

```python
class MyCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
self.framework.observe(self.on.start, self.on_start)

def on_start(self, event):
# Handle the start event here.

if __name__ == "__main__":
main(MyCharm)
```

Every standard event in Juju may be observed that way, and you can also easily
define your own events in your custom types.
That should be enough for you to be able to run

```
$ juju deploy .
```

> The second argument to `observe` can be either the handler as a bound
> method, or the observer itself if the handler is a method of the observer
> that follows the conventional naming pattern. That is, in this case, we
> could have called just `self.framework.observe(self.on.start, self)`.
Happy charming!

The `hooks/` directory must contain a symlink to your `src/charm.py` entry
point so that Juju can call it. You only need to set up the `hooks/install` link
(`hooks/start` for K8s charms, until [lp#1854635](https://bugs.launchpad.net/juju/+bug/1854635)
is resolved), and the framework will create all others at runtime.
## Testing your charms

Once your charm is ready, upload it to the charm store and deploy it as
normal with:
The operator framework provides a testing harness, so that you can test that
your charm does the right thing when presented with different scenarios, without
having to have a full deployment to do so. `pydoc3 ops.testing` has the details
for that, including this example:

```
# Replace ${CHARM} with the name of the charm.
charm push . cs:~${USER}/${CHARM}
# Replace ${VERSION} with the version created by `charm push`.
charm release cs:~${USER}/${CHARM}-${VERSION}
charm grant cs:~${USER}/${CHARM}-${VERSION} everyone
# And now deploy your charm.
juju deploy cs:~${USER}/$CHARM
```python
harness = Harness(MyCharm)
# Do initial setup here
relation_id = harness.add_relation('db', 'postgresql')
# Now instantiate the charm to see events as the model changes
harness.begin()
harness.add_relation_unit(relation_id, 'postgresql/0', remote_unit_data={'key': 'val'})
chipaca marked this conversation as resolved.
Show resolved Hide resolved
# Check that charm has properly handled the relation_joined event for postgresql/0
self.assertEqual(harness.charm. ...)
```

Alternatively, to deploy directly from local disk, run:
## Talk to us

```
juju deploy .
```
If you need help, have ideas, or would just like to chat with us, reach out on
IRC: we're in [#smooth-operator] on freenode (or try the [webchat]).

# Operator Framework development
We also pay attention to juju's [discourse], but currently we don't actively
post there outside of our little corner of the [docs]; most discussion at this
stage is on IRC.

If you want to work in the framework *itself* you will need Python >= 3.5 and
the dependencies declared in requirements-dev.txt installed in your system. Or you
can use a virtualenv:
[webchat]: https://webchat.freenode.net/#smooth-operator
[#smooth-operator]: irc://chat.freenode.net/%23smooth-operator
[discourse]: https://discourse.juju.is/c/charming
[docs]: https://discourse.juju.is/c/docs/operator-framework

```
virtualenv --python=python3 env
source env/bin/activate
pip install -r requirements-dev.txt
```
## Operator Framework development

If you want to work in the framework *itself* you will need Python >= 3.5 and
the dependencies declared in `requirements-dev.txt` installed in your system.
Or you can use a virtualenv:

virtualenv --python=python3 env
source env/bin/activate
pip install -r requirements-dev.txt

Then you can try `./run_tests`, it should all go green.

If you want to build the documentation you'll need the requirements from
`docs/requirements.txt`, or in your virtualenv

pip install -r docs/requirements.txt

and then you can run `./build_docs`.