Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
cb380f8
switch to uv-build
dwilding May 26, 2026
e63f2ca
replace Dockerfile by rockcraft.yaml
dwilding May 26, 2026
919bf5a
update integration test
dwilding May 26, 2026
973314b
install LXD in CI
dwilding May 26, 2026
3320cff
prepare LXD (no need to install)
dwilding May 26, 2026
f38ea8b
use craft-actions instead of manually configuring the runner
dwilding May 26, 2026
d0d3840
restructure integration test
dwilding May 27, 2026
1c08c7d
revert to single test file
dwilding May 27, 2026
c8266df
add spread config
dwilding May 27, 2026
180078b
reduce changes
dwilding May 27, 2026
b09c8ec
make is not installed in the spread runner
dwilding May 27, 2026
38ea980
rewrite README
dwilding Jun 1, 2026
3031db7
add testing details to README
dwilding Jun 1, 2026
38d321d
update dependabot config
dwilding Jun 1, 2026
0d6ff25
bump uvicorn
dwilding Jun 1, 2026
6bff3d1
bump fastapi and python-multipart
dwilding Jun 1, 2026
3540de0
try build workflow on PRs
dwilding Jun 15, 2026
60e30cc
remove ppc64el
dwilding Jun 15, 2026
bbb978a
fix branch name
dwilding Jun 15, 2026
02b8ff2
add dispatch to workflow
dwilding Jun 15, 2026
3a287f4
add workflows from template
dwilding Jun 22, 2026
887b13e
remove old build workflow
dwilding Jun 22, 2026
52db383
main --> master
dwilding Jun 22, 2026
06f1b1c
fix directory in CI
dwilding Jun 22, 2026
fd11b1a
simplify rock CI
dwilding Jun 22, 2026
b65ea1e
override OpenSSL from base
dwilding Jun 22, 2026
9da1803
check then format in Makefile
dwilding Jun 22, 2026
25c0967
overlay instead of stage
dwilding Jun 22, 2026
6f62047
ignore vulnerability instead
dwilding Jun 22, 2026
af7abe6
fix build workflow linting
dwilding Jun 22, 2026
9296f07
switch build workflow back to tag push
dwilding Jun 22, 2026
a3fe3ef
improve JSON building in workflow
dwilding Jun 22, 2026
bd01d5c
switch to bare base
dwilding Jun 23, 2026
76e55bf
fix symlink handling
dwilding Jun 23, 2026
2a7b6f0
remove ignored CVE
dwilding Jun 23, 2026
8cc1708
add dry run build workflow
dwilding Jun 23, 2026
44c727c
adjust parts after feedback
dwilding Jun 28, 2026
6398906
adjust service and check names
dwilding Jun 28, 2026
c932cd5
remove dry-run workflow
dwilding Jun 29, 2026
e1803f7
bump release version
dwilding Jun 29, 2026
0c436ef
remove redundant permissions block
dwilding Jul 1, 2026
a4adb0c
rename 'build' workflow to 'publish'
dwilding Jul 1, 2026
cbc429a
remove flit config
dwilding Jul 1, 2026
0373f86
remove ghcr conditional in publish workflow
dwilding Jul 1, 2026
bc1e086
switch to tag for rocks-security-manifest
dwilding Jul 1, 2026
5ffc420
add note about Docker needing sudo
dwilding Jul 1, 2026
768ee22
Merge remote-tracking branch 'upstream/master' into convert-to-rock
dwilding Jul 1, 2026
b4d807a
remove Ubuntu base from tag
dwilding Jul 1, 2026
abd1ec8
remove image from Docker after testing
dwilding Jul 1, 2026
89c2193
add comment about runner selection
dwilding Jul 1, 2026
e637312
bump deps and uv action
dwilding Jul 1, 2026
9d0dc83
always use GitHub runners
dwilding Jul 2, 2026
c51789c
add comment about runners
dwilding Jul 2, 2026
8cf1eef
for Spread setup, list images instead of running an image
dwilding Jul 7, 2026
6b8cf13
Merge remote-tracking branch 'upstream/master' into convert-to-rock
dwilding Jul 7, 2026
e231991
Merge remote-tracking branch 'upstream/master' into convert-to-rock
dwilding Jul 7, 2026
67db955
bump fastapi to 0.138.1
dwilding Jul 7, 2026
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
10 changes: 1 addition & 9 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,7 @@ updates:
- "dependencies"
cooldown:
default-days: 7
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "monthly"
labels:
- "dependencies"
cooldown:
default-days: 7
- package-ecosystem: "docker"
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"
Expand Down
43 changes: 0 additions & 43 deletions .github/workflows/build.yaml

This file was deleted.

3 changes: 3 additions & 0 deletions .github/workflows/integration-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ jobs:
with:
persist-credentials: false

- name: Pack the rock
uses: canonical/craft-actions/rockcraft-pack@210d55fa6cb7a1ab39e3194315d6a03249af3d34
Comment thread
dwilding marked this conversation as resolved.

- name: Run the integration test
run: make integration
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ jobs:
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- name: Lint the code
run: make lint
80 changes: 80 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Build and publish image

on:
push:
tags:
- '*'

permissions: {}

jobs:
prepare:
runs-on: ubuntu-latest
outputs:
build-matrix: ${{ steps.set-matrix.outputs.build-matrix }}
steps:
- name: Set Build Matrix
id: set-matrix
env:
TAG: ${{ github.ref_name }}
run: |
matrix=$(jq --null-input --compact-output \
--arg tag "${TAG}" \
'{
include: [{
name: "api-demo-server",
tag: $tag,
directory: ".",
"artifact-name": "api-demo-server",
"run-tests": true,
lfs: false,
"lfs-include": ""
}]
}')
echo "build-matrix=${matrix}" >> $GITHUB_OUTPUT
fi

build:
needs: [prepare]
strategy:
matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }}
uses: canonical/oci-factory/.github/workflows/Build-Rock.yaml@43f2710f69c29e43fd90f9e600896a7e32de96de
with:
rock-repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
rock-repo-commit: ${{ github.head_ref || github.ref_name }}
rockfile-directory: ${{ matrix.directory }}
oci-archive-name: ${{ matrix.artifact-name }}
# We don't have any special architecture or performance requirements,
# so use GitHub's runners instead of Canonical's self-hosted runners.
arch-map: '{"amd64":["ubuntu-24.04"],"arm64":["ubuntu-24.04-arm"]}'
rockcraft-test: ${{ matrix.run-tests }}
secrets:
source-github-token: ${{ secrets.REPO_CLONER_TOKEN }}

test:
needs: [prepare, build]
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }}
uses: canonical/oci-factory/.github/workflows/Test-Rock.yaml@43f2710f69c29e43fd90f9e600896a7e32de96de
with:
oci-archive-name: ${{ matrix.artifact-name }}

upload-ghcr:
needs: [prepare, test]
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }}
fail-fast: false
permissions:
packages: write
steps:
- name: Upload Rock to GHCR
uses: canonical/oci-factory/.github/actions/upload-rock@43f2710f69c29e43fd90f9e600896a7e32de96de
with:
artifact_name: ${{ matrix.artifact-name }}
tags: ${{ matrix.tag }}
name: ${{ matrix.name }}
registry: ghcr.io/${{ github.repository }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- name: Export locked dependencies (excluding this project)
run: uv export --frozen --no-emit-project --no-hashes --format requirements-txt -o requirements.txt
- name: Run pip-audit
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zizmor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
persist-credentials: false

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0

- name: Run zizmor
run: uvx zizmor@v1.23.1 --format=sarif . > results.sarif
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.rock
.craft-spread-*
.spread-reuse.*
23 changes: 22 additions & 1 deletion .scripts/integration-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,32 @@ set -euo pipefail

cleanup_docker() {
docker compose down --volumes --remove-orphans
docker image rm api-demo-server:integration
}

rock_path="${ROCK_PATH:-}"
if [[ -z "$rock_path" ]]; then
set -- ./*.rock
if [[ "$1" == "./*.rock" ]]; then
echo "No rocks were found in $(pwd)" >&2
exit 1
elif [[ $# -gt 1 ]]; then
echo "Found more than one rock $*" >&2
exit 1
else
rock_path="$1"
fi
fi
if [[ ! -f "$rock_path" ]]; then
echo "$rock_path is not a file" >&2
exit 1
fi

set -x
rockcraft.skopeo --insecure-policy \
copy "oci-archive:${rock_path}" "docker-daemon:api-demo-server:integration"
trap cleanup_docker EXIT
docker compose up --build --detach --wait
docker compose up --detach --wait
curl --silent --fail --request POST http://localhost:8000/createtable
curl --silent --fail --request POST http://localhost:8000/addname/ --data "name=Alice"
response="$(curl --silent --fail http://localhost:8000/names)"
Expand Down
16 changes: 0 additions & 16 deletions Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
MAKEFLAGS += --always-make

format:
uv run ruff format
uv run ruff check --fix
uv run ruff format
Comment thread
dwilding marked this conversation as resolved.

lint:
uv run ruff check
Expand Down
95 changes: 60 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,70 @@
# Description
This is a demo server based on Python FastAPI.
Server is used to show connections to PostgreSQL and Prometheus.
This is a demo Python server written with FastAPI. The server connects to PostgreSQL and exposes Prometheus metrics.

To see API reference start the server and open: http://127.0.0.1:8000/docs
To get prometheus metrics: http://127.0.0.1:8000/metrics
The server is packaged as a "rock" using Canonical's OCI-compliant format for container images.

# Usage
Download and start PostgreSQL container:
```
docker run --name postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
```
# Build and run the container image

Get psql container IP:
```
docker ps # get postgres container ID
docker inspect <postgres container ID> | grep IPAddress
```
You'll need [Rockcraft](https://documentation.ubuntu.com/rockcraft/stable/) and [Docker](https://docs.docker.com/).

Build a docker container via:
```
docker build -t api_demo_server .
```
By default, Docker requires `sudo`. To use Docker as a regular user, run `sudo usermod -aG docker $USER` then log out and log back in again.

Start demo server:
```
docker run --rm -e DEMO_SERVER_DB_HOST=<postgres container IP> -p 8000:8000 api_demo_server
```
1. Build the container image from source:

# Configuration via environment variables
You can configure application by applying following environment variables:
```text
rockcraft pack
```

| Environment Variable | Value | Description |
|------------------------- |------------------- |------------------------------------------------- |
| DEMO_SERVER_LOGFILE | \<path/to/log.log> | Path to the file where logs should be written |
| DEMO_SERVER_DB_HOST | \<Host IP> | IP address of the host where Database is hosted |
| DEMO_SERVER_DB_PORT | \<Host Port> | Port of the host where Database is hosted |
| DEMO_SERVER_DB_USER | \<Username> | Username that has access to `names` Database |
| DEMO_SERVER_DB_PASSWORD | \<Password> | Password to the `DEMO_SERVER_DB_USER` user |
This creates a `.rock` file. The name of the `.rock` file depends on your system architecture.

# Publish to registry
2. Make the container image available to Docker:

```text
rockcraft.skopeo --insecure-policy \
copy "oci-archive:<rock>" "docker-daemon:api-demo-server:integration"
```
Comment thread
dwilding marked this conversation as resolved.

Where `<rock>` is the path to the `.rock` file.

3. Run the container image alongside PostgreSQL:

```text
docker compose up
```

4. In a separate terminal, check that the server is available:

```text
curl http://localhost:8000/version
```

This returns a JSON object that contains the server's version number.

The server has several other API endpoints, including:

- API docs - http://localhost:8000/docs
- Prometheus metrics - http://localhost:8000/metrics

# Run automated tests

If you modify the server, rebuild the container image and run the PostgreSQL integration tests:

```text
rockcraft pack
make integration
```
docker buildx build -t ghcr.io/canonical/api_demo_server:1.0.4 --platform linux/amd64,linux/arm64,linux/ppc64le --push .
```

The integration tests require Docker.

Alternatively, run `rockcraft test`. This builds the container image and runs the integration tests inside a LXD container. It's slower, but you don't need Docker on your system.

# Server environment variables

- `DEMO_SERVER_LOGFILE` - Path to the file where logs should be written
- `DEMO_SERVER_DB_HOST` - IP address of the database host
- `DEMO_SERVER_DB_PORT` - Port of the database host
- `DEMO_SERVER_DB_USER` - Username that has access to the database
- `DEMO_SERVER_DB_PASSWORD` - Password for the `DEMO_SERVER_DB_USER` user

# Deploy the container image as a Juju charm

See [From zero to hero: Write your first Kubernetes charm](https://documentation.ubuntu.com/ops/latest/tutorial/from-zero-to-hero-write-your-first-kubernetes-charm/)
2 changes: 1 addition & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ services:
retries: 10

demo-server:
build: .
image: api-demo-server:integration
environment:
DEMO_SERVER_DB_HOST: postgres
ports:
Expand Down
15 changes: 6 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "api_demo_server"
dynamic = ["version"]
version = "2.0.0"
description = "FastAPI demo server"
authors = [
{ name = "The Charm Tech team at Canonical Ltd." },
Expand All @@ -9,9 +9,9 @@ license = "Apache-2.0"
license-files = ["LICENSE"]
requires-python = ">=3.10"
dependencies = [
"fastapi==0.136.1",
"uvicorn==0.46.0",
"python-multipart==0.0.31",
"fastapi==0.138.1",
"uvicorn==0.49.0",
"python-multipart==0.0.32",
"psycopg2-binary==2.9.12",
"starlette==1.3.1",
Comment thread
tonyandrewmeyer marked this conversation as resolved.
"starlette_exporter==0.23.0"
Expand All @@ -26,12 +26,9 @@ dev = [

[build-system]
requires = [
"flit_core >=3.2,<4",
"uv_build>=0.11.16,<1",
]
build-backend = "flit_core.buildapi"

[tool.flit.module]
name = "api_demo_server"
build-backend = "uv_build"

[tool.ruff]
line-length = 99
Expand Down
Loading
Loading