Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/example-charm-integration-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
matrix:
dir:
- examples/httpbin-demo
- examples/k8s-1-minimal
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -78,7 +79,6 @@ jobs:
fail-fast: false
matrix:
dir:
- examples/k8s-1-minimal
- examples/k8s-2-configurable
- examples/k8s-3-postgresql
- examples/k8s-4-action
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,22 @@ First, repack and refresh your charm:
```text
charmcraft pack
juju refresh \
--path="./demo-api-charm_ubuntu-22.04-amd64.charm" \
demo-api-charm --force-units --resource \
--path="./fastapi-demo_amd64.charm" \
fastapi-demo --force-units --resource \
demo-server-image=ghcr.io/canonical/api_demo_server:1.0.1
```

Next, test that the basic action invocation works:

```text
juju run demo-api-charm/0 get-db-info
juju run fastapi-demo/0 get-db-info
```

It might take a few seconds, but soon you should see an output similar to the one below, showing the database host and port:

```text
Running operation 1 with 1 task
- task 2 on unit-demo-api-charm-0
- task 2 on unit-fastapi-demo-0

Waiting for task 2...
db-host: postgresql-k8s-primary.testing.svc.cluster.local
Expand All @@ -134,14 +134,14 @@ db-port: "5432"
Now, test that the action parameter (`show-password`) works as well by setting it to `True`:

```text
juju run demo-api-charm/0 get-db-info show-password=True
juju run fastapi-demo/0 get-db-info show-password=True
```

The output should now include the username and the password:

```text
Running operation 3 with 1 task
- task 4 on unit-demo-api-charm-0
- task 4 on unit-fastapi-demo-0

Waiting for task 4...
db-host: postgresql-k8s-primary.testing.svc.cluster.local
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,8 @@ First, repack and refresh your charm:
```text
charmcraft pack
juju refresh \
--path="./demo-api-charm_ubuntu-22.04-amd64.charm" \
demo-api-charm --force-units --resource \
--path="./fastapi-demo_amd64.charm" \
fastapi-demo --force-units --resource \
demo-server-image=ghcr.io/canonical/api_demo_server:1.0.1
```

Expand All @@ -348,7 +348,7 @@ juju deploy postgresql-k8s --channel=14/stable --trust
Now, integrate our charm with the newly deployed `postgresql-k8s` charm:

```text
juju integrate postgresql-k8s demo-api-charm
juju integrate postgresql-k8s fastapi-demo
```

> Read more: {external+juju:ref}`Juju | Relation (integration) <relation>`, [`juju integrate`](inv:juju:std:label#command-juju-integrate)
Expand All @@ -359,22 +359,22 @@ Finally, run:
juju status --relations --watch 1s
```

You should see both applications get to the `active` status, and also that the `postgresql-k8s` charm has a relation to the `demo-api-charm` over the `postgresql_client` interface, as below:
You should see both applications get to the `active` status, and also that the `postgresql-k8s` charm has a relation to the `fastapi-demo` over the `postgresql_client` interface, as below:

```text
Model Controller Cloud/Region Version SLA Timestamp
testing concierge-k8s k8s 3.6.12 unsupported 13:50:39+01:00

App Version Status Scale Charm Channel Rev Address Exposed Message
demo-api-charm active 1 demo-api-charm 2 10.152.183.233 no
fastapi-demo active 1 fastapi-demo 2 10.152.183.233 no
postgresql-k8s 14.15 active 1 postgresql-k8s 14/stable 495 10.152.183.195 no

Unit Workload Agent Address Ports Message
demo-api-charm/0* active idle 10.1.157.90
fastapi-demo/0* active idle 10.1.157.90
postgresql-k8s/0* active idle 10.1.157.92 Primary

Integration provider Requirer Interface Type Message
postgresql-k8s:database demo-api-charm:database postgresql_client regular
postgresql-k8s:database fastapi-demo:database postgresql_client regular
postgresql-k8s:database-peers postgresql-k8s:database-peers postgresql_peers peer
postgresql-k8s:restart postgresql-k8s:restart rolling_op peer
postgresql-k8s:upgrade postgresql-k8s:upgrade upgrade peer
Expand Down Expand Up @@ -530,9 +530,9 @@ When it's done, the output should show two passing tests:
tests/integration/test_charm.py::test_deploy
...
INFO jubilant.wait:_juju.py:1164 wait: status changed:
- .apps['demo-api-charm'].units['demo-api-charm/0'].juju_status.current = 'executing'
- .apps['demo-api-charm'].units['demo-api-charm/0'].juju_status.message = 'running start hook'
+ .apps['demo-api-charm'].units['demo-api-charm/0'].juju_status.current = 'idle'
- .apps['fastapi-demo'].units['fastapi-demo/0'].juju_status.current = 'executing'
- .apps['fastapi-demo'].units['fastapi-demo/0'].juju_status.message = 'running start hook'
+ .apps['fastapi-demo'].units['fastapi-demo/0'].juju_status.current = 'idle'
PASSED
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,21 +174,21 @@ First, repack and refresh your charm:
```text
charmcraft pack
juju refresh \
--path="./demo-api-charm_ubuntu-22.04-amd64.charm" \
demo-api-charm --force-units --resource \
--path="./fastapi-demo_amd64.charm" \
fastapi-demo --force-units --resource \
demo-server-image=ghcr.io/canonical/api_demo_server:1.0.1
```

Now, check the available configuration options:

```text
juju config demo-api-charm
juju config fastapi-demo
```

Our newly defined `server-port` option is there. Let's try to configure it to something else, e.g., `5000`:

```text
juju config demo-api-charm server-port=5000
juju config fastapi-demo server-port=5000
```

Now, let's validate that the app is actually running and reachable on the new port by sending the HTTP request below, where `10.1.157.74` is the IP of our pod and `5000` is the new application port:
Expand All @@ -202,7 +202,7 @@ You should see JSON string with the version of the application: `{"version":"1.0
Let's also verify that our invalid port number check works by setting the port to `22` and then running `juju status`:

```text
juju config demo-api-charm server-port=22
juju config fastapi-demo server-port=22
juju status
```

Expand All @@ -212,19 +212,19 @@ As expected, the application is indeed in the `blocked` state:
Model Controller Cloud/Region Version SLA Timestamp
testing concierge-k8s k8s 3.6.12 unsupported 18:19:24+01:00

App Version Status Scale Charm Channel Rev Address Exposed Message
demo-api-charm blocked 1 demo-api-charm 1 10.152.183.215 no Invalid port number, 22 is reserved for SSH
App Version Status Scale Charm Channel Rev Address Exposed Message
fastapi-demo blocked 1 fastapi-demo 1 10.152.183.215 no Invalid port number, 22 is reserved for SSH

Unit Workload Agent Address Ports Message
demo-api-charm/0* blocked idle 10.1.157.74 Invalid port number, 22 is reserved for SSH
Unit Workload Agent Address Ports Message
fastapi-demo/0* blocked idle 10.1.157.74 Invalid port number, 22 is reserved for SSH
```

Congratulations, you now know how to make your charm configurable!

Before continuing, reset the port to `8000` and check that the application is in `active` status:

```text
juju config demo-api-charm server-port=8000
juju config fastapi-demo server-port=8000
juju status
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ First, repack and refresh your charm:
```text
charmcraft pack
juju refresh \
--path="./demo-api-charm_ubuntu-22.04-amd64.charm" \
demo-api-charm --force-units --resource \
--path="./fastapi-demo_amd64.charm" \
fastapi-demo --force-units --resource \
demo-server-image=ghcr.io/canonical/api_demo_server:1.0.1
```

Expand Down Expand Up @@ -284,9 +284,9 @@ Now switch back to the charm model and integrate your charm with the exposed end

```text
juju switch testing
juju integrate demo-api-charm admin/cos-lite.grafana
juju integrate demo-api-charm admin/cos-lite.loki
juju integrate demo-api-charm admin/cos-lite.prometheus
juju integrate fastapi-demo admin/cos-lite.grafana
juju integrate fastapi-demo admin/cos-lite.loki
juju integrate fastapi-demo admin/cos-lite.prometheus
```

<a href="#heading--access-your-applications-from-the-host-machine"><h3 id="heading--access-your-applications-from-the-host-machine">Access your applications from the host machine</h3></a>
Expand Down
65 changes: 43 additions & 22 deletions examples/k8s-1-minimal/charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,35 +1,56 @@
# This file configures Charmcraft.
# See https://documentation.ubuntu.com/charmcraft/stable/reference/files/charmcraft-yaml-file/
type: charm
bases:
- build-on:
- name: ubuntu
channel: "22.04"
run-on:
- name: ubuntu
channel: "22.04"

name: demo-api-charm
title: |
demo-fastapi-k8s
name: fastapi-demo
title: Web Server Demo
summary: A demo charm that operates a small Python FastAPI server.
description: |
This is a demo charm built on top of a small Python FastAPI server.
summary: |
FastAPI Demo charm for Kubernetes
This charm demonstrates how to write a Kubernetes charm with Ops.
# Documentation:
# https://documentation.ubuntu.com/charmcraft/stable/howto/build-guides/select-platforms/
base: [email protected]
platforms:
amd64:
arm64:

parts:
charm:
plugin: uv
source: .
build-snaps:
- astral-uv

# (Optional) Configuration options for the charm
# This config section defines charm config options, and populates the Configure
# tab on Charmhub.
# More information on this section at:
# https://documentation.ubuntu.com/charmcraft/stable/reference/files/charmcraft-yaml-file/#config
# General configuration documentation:
# https://documentation.ubuntu.com/juju/3.6/reference/configuration/#application-configuration
config:
options:
# An example config option to customise the log level of the workload
log-level:
description: |
Configures the log level of gunicorn.
assumes:
- juju >= 3.1
- k8s-api
Comment on lines -18 to -20
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it's worth retaining these. It seems they're not in the Charmcraft template. I'm kind of surprised that assumes: k8s-api isn't in the Kubernetes one. Is that a considered decision on our part, or an oversight?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess containers means it's K8s so maybe it's just redundant?

Acceptable values are: "info", "debug", "warning", "error" and "critical"
default: "info"
type: string

containers:
demo-server:
resource: demo-server-image

resources:
# An OCI image resource for each container listed above.
# You may remove this if your charm will run without a workload sidecar container.
# An OCI image resource for the container listed above.
demo-server-image:
type: oci-image
description: OCI image from GitHub Container Repository
# The upstream-source field is ignored by Juju. It is included here as a reference
# so the integration testing suite knows which image to deploy during testing. This field
# is also used by the 'canonical/charming-actions' GitHub action for automated releasing.
# The upstream-source field is ignored by Charmcraft and Juju, but it can be
# useful to developers in identifying the source of the OCI image. It is also
# used by the 'canonical/charming-actions' GitHub action for automated releases.
# The test_deploy function in tests/integration/test_charm.py reads upstream-source
# to determine which OCI image to use when running the charm's integration tests.
upstream-source: ghcr.io/canonical/api_demo_server:1.0.1
47 changes: 46 additions & 1 deletion examples/k8s-1-minimal/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
# Copyright 2026 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[project]
name = "fastapi-demo"
version = "0.0.1"
requires-python = ">=3.10"

# Dependencies of the charm code
# You should include the dependencies of the code in src/. You should also include the
# dependencies of any charmlibs that the charm uses (copy the dependencies from PYDEPS).
dependencies = [
"ops>=3,<4",
]

[dependency-groups]
# Dependencies of linting and static type checks
lint = [
"ruff",
"codespell",
"pyright",
]
# Dependencies of unit tests
unit = [
"coverage[toml]",
"ops[testing]",
"pytest",
]
# Dependencies of integration tests
integration = [
"jubilant",
"pytest",
"PyYAML",
]

# Testing tools configuration
[tool.coverage.run]
branch = true
Expand All @@ -11,7 +57,6 @@ log_cli_level = "INFO"

# Linting tools configuration
[tool.ruff]
format.quote-style = "single" # Remove this when switching the tutorial to charmcraft init.
line-length = 99
lint.select = ["E", "W", "F", "C", "N", "D", "I001"]
lint.ignore = [
Expand Down
1 change: 0 additions & 1 deletion examples/k8s-1-minimal/requirements.txt

This file was deleted.

Loading
Loading