Skip to content

Commit

Permalink
feat: initial charm (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
amandahla authored Sep 5, 2024
1 parent c4709a3 commit f063db3
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 202 deletions.
13 changes: 3 additions & 10 deletions .github/workflows/integration_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,8 @@ jobs:
uses: canonical/operator-workflows/.github/workflows/integration_test.yaml@main
secrets: inherit
with:
load-test-enabled: false
load-test-run-args: "-e LOAD_TEST_HOST=localhost"
zap-before-command: "curl -H \"Host: indico.local\" http://localhost/bootstrap --data-raw 'csrf_token=00000000-0000-0000-0000-000000000000&first_name=admin&last_name=admin&email=admin%40admin.com&username=admin&password=lunarlobster&confirm_password=lunarlobster&affiliation=Canonical'"
zap-enabled: true
zap-cmd-options: '-T 60 -z "-addoninstall jython" --hook "/zap/wrk/tests/zap/hook.py"'
zap-target: localhost
zap-target-port: 80
zap-rules-file-name: "zap_rules.tsv"
trivy-fs-enabled: true
trivy-image-config: "trivy.yaml"
channel: 1.28-strict/stable
charmcraft-channel: latest/edge
juju-channel: 3.4/stable
self-hosted-runner: true
self-hosted-runner-label: "edge"
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ jobs:
uses: canonical/operator-workflows/.github/workflows/test.yaml@main
secrets: inherit
with:
charmcraft-channel: latest/edge
self-hosted-runner: true
self-hosted-runner-label: "edge"
46 changes: 37 additions & 9 deletions charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
# Copyright 2024 Canonical Ltd.
# See LICENSE file for licensing details.
# This file configures Charmcraft.
# See https://juju.is/docs/sdk/charmcraft-config for guidance.

name: maubot
title: maubot
description: |
A Juju charm deploying and managing maubot on Kubernetes. Maubot is a
plugin-based Matrix bot system written in Python.
summary: An operator deploying and managing maubot.
links:
issues: https://github.com/canonical/maubot-operator/issues
source: https://github.com/canonical/maubot-operator
contact:
- https://launchpad.net/~canonical-is-devops

resources:
maubot-image:
type: oci-image
description: OCI image for maubot

containers:
maubot:
resource: maubot-image

type: charm
bases:
- build-on:
- name: ubuntu
channel: "22.04"
run-on:
- name: ubuntu
channel: "22.04"
base: [email protected]
build-base: [email protected]
platforms:
amd64:

parts:
charm:
build-packages:
- cargo
- libffi-dev
- libssl-dev
- pkg-config
- rustc

assumes:
- juju >= 3.4
19 changes: 19 additions & 0 deletions maubot_rock/rockcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2024 Canonical Ltd.
# See LICENSE file for licensing details.

name: maubot
summary: Maubot rock
description: Maubot OCI image for the Maubot charm
version: "1.0"
license: Apache-2.0

base: [email protected]
build-base: [email protected]
platforms:
amd64:

parts:
hello:
plugin: nil
stage-packages:
- hello
50 changes: 0 additions & 50 deletions metadata.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ops >= 2.2.0
ops==2.15.0
74 changes: 74 additions & 0 deletions src-docs/charm.py.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<!-- markdownlint-disable -->

<a href="../src/charm.py#L0"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

# <kbd>module</kbd> `charm.py`
Maubot charm service.

**Global Variables**
---------------
- **MAUBOT_SERVICE_NAME**
- **MAUBOT_CONTAINER_NAME**


---

## <kbd>class</kbd> `MaubotCharm`
Maubot charm.

<a href="../src/charm.py#L25"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `__init__`

```python
__init__(*args: Any)
```

Construct.



**Args:**

- <b>`args`</b>: Arguments passed to the CharmBase parent constructor.


---

#### <kbd>property</kbd> app

Application that this unit is part of.

---

#### <kbd>property</kbd> charm_dir

Root directory of the charm as it is running.

---

#### <kbd>property</kbd> config

A mapping containing the charm's config and current values.

---

#### <kbd>property</kbd> meta

Metadata of this charm.

---

#### <kbd>property</kbd> model

Shortcut for more simple access the model.

---

#### <kbd>property</kbd> unit

Unit that this execution is responsible for.




87 changes: 21 additions & 66 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,22 @@

# Learn more at: https://juju.is/docs/sdk

"""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:
https://discourse.charmhub.io/t/4208
"""
"""Maubot charm service."""

import logging
import typing

import ops
from ops import pebble

# Log messages can be retrieved using juju debug-log
logger = logging.getLogger(__name__)

VALID_LOG_LEVELS = ["info", "debug", "warning", "error", "critical"]
MAUBOT_SERVICE_NAME = "maubot"
MAUBOT_CONTAINER_NAME = "maubot"


class IsCharmsTemplateCharm(ops.CharmBase):
"""Charm the service."""
class MaubotCharm(ops.CharmBase):
"""Maubot charm."""

def __init__(self, *args: typing.Any):
"""Construct.
Expand All @@ -35,83 +29,44 @@ def __init__(self, *args: typing.Any):
args: Arguments passed to the CharmBase parent constructor.
"""
super().__init__(*args)
self.framework.observe(self.on.httpbin_pebble_ready, self._on_httpbin_pebble_ready)
self.framework.observe(self.on.maubot_pebble_ready, self._on_maubot_pebble_ready)
self.framework.observe(self.on.config_changed, self._on_config_changed)

def _on_httpbin_pebble_ready(self, event: ops.PebbleReadyEvent) -> None:
"""Define and start a workload using the Pebble API.
Change this example to suit your needs. You'll need to specify the right entrypoint and
environment configuration for your specific workload.
Learn more about interacting with Pebble at at https://juju.is/docs/sdk/pebble.
def _on_maubot_pebble_ready(self, event: ops.PebbleReadyEvent) -> None:
"""Handle maubot pebble ready event.
Args:
event: event triggering the handler.
"""
# Get a reference the container attribute on the PebbleReadyEvent
container = event.workload
# Add initial Pebble config layer using the Pebble API
container.add_layer("httpbin", self._pebble_layer, combine=True)
# Make Pebble reevaluate its plan, ensuring any services are started if enabled.
container.add_layer(MAUBOT_CONTAINER_NAME, self._pebble_layer, combine=True)
container.replan()
# Learn more about statuses in the SDK docs:
# https://juju.is/docs/sdk/constructs#heading--statuses
self.unit.status = ops.ActiveStatus()

def _on_config_changed(self, event: ops.ConfigChangedEvent) -> None:
"""Handle changed configuration.
Change this example to suit your needs. If you don't need to handle config, you can remove
this method.
Learn more about config at https://juju.is/docs/sdk/config
Args:
event: event triggering the handler.
"""
# Fetch the new config value
log_level = str(self.model.config["log-level"]).lower()

# Do some validation of the configuration option
if log_level in VALID_LOG_LEVELS:
# The config is good, so update the configuration of the workload
container = self.unit.get_container("httpbin")
# Verify that we can connect to the Pebble API in the workload container
if container.can_connect():
# Push an updated layer with the new config
container.add_layer("httpbin", self._pebble_layer, combine=True)
container.replan()

logger.debug("Log level for gunicorn changed to '%s'", log_level)
self.unit.status = ops.ActiveStatus()
else:
# We were unable to connect to the Pebble API, so we defer this event
event.defer()
self.unit.status = ops.WaitingStatus("waiting for Pebble API")
else:
# In this case, the config option is bad, so block the charm and notify the operator.
self.unit.status = ops.BlockedStatus("invalid log level: '{log_level}'")
def _on_config_changed(self, _: ops.ConfigChangedEvent) -> None:
"""Handle changed configuration."""
self.unit.status = ops.MaintenanceStatus()
container = self.unit.get_container(MAUBOT_CONTAINER_NAME)
container.add_layer(MAUBOT_SERVICE_NAME, self._pebble_layer, combine=True)
container.replan()
self.unit.status = ops.ActiveStatus()

@property
def _pebble_layer(self) -> pebble.LayerDict:
"""Return a dictionary representing a Pebble layer."""
return {
"summary": "httpbin layer",
"summary": "maubot layer",
"description": "pebble config layer for httpbin",
"services": {
"httpbin": {
MAUBOT_SERVICE_NAME: {
"override": "replace",
"summary": "httpbin",
"command": "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent",
"summary": "maubot",
"command": 'bash -c "hello -t; sleep 10"',
"startup": "enabled",
"environment": {
"GUNICORN_CMD_ARGS": f"--log-level {self.model.config['log-level']}"
},
}
},
}


if __name__ == "__main__": # pragma: nocover
ops.main.main(IsCharmsTemplateCharm)
ops.main.main(MaubotCharm)
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ def pytest_addoption(parser):
parser: Pytest parser.
"""
parser.addoption("--charm-file", action="store")
parser.addoption("--maubot-image", action="store")
Loading

0 comments on commit f063db3

Please sign in to comment.