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

Terraform provider backed Juju model setup + associated charmed operators for API and backend #13

Merged
merged 105 commits into from
Jun 1, 2023
Merged
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
eeed378
Initial charming files
omar-selo May 24, 2023
e693549
Some more charming
mz2 May 24, 2023
573b788
Mark charm.py executable
mz2 May 24, 2023
5e4e2c2
Moved the charm resources to backend/charm
mz2 May 24, 2023
d515e37
Adds Docker image publishing to GitHub Packages
mz2 May 24, 2023
ade5fce
Moves publish.yml to the correct location
mz2 May 24, 2023
46efdef
Context from . to ./backend
mz2 May 24, 2023
2d2f3d9
Fix to an indentation error
mz2 May 25, 2023
6681a2c
A meaningless change to trigger rebuilding the image.
mz2 May 25, 2023
bb82a00
_pebble_layer was meant to be a property
mz2 May 25, 2023
3e44f93
README edits
mz2 May 25, 2023
df17bfd
Adds instructions for local charm development steps
mz2 May 25, 2023
305468b
Adds the needed step to restart microk8s
mz2 May 25, 2023
f7bccb5
Some aesthetic tweaks
mz2 May 25, 2023
5a0842f
Adds some trivial config change handling
mz2 May 25, 2023
e41d826
Add /version endpoint
mz2 May 25, 2023
235c59f
Attempt at getting the database URL from a database relation.
mz2 May 25, 2023
fc0f103
Import path tweak
mz2 May 25, 2023
422a768
Some more attempts
mz2 May 25, 2023
0b9131e
Database relation worketh!
mz2 May 26, 2023
4701e92
Working on DB migration
mz2 May 26, 2023
0746499
Adds the beginnings of a charm for the frontend.
mz2 May 26, 2023
7657853
README tweaks
mz2 May 26, 2023
6d4565d
Fix the HTTP verb used for the (not in use) version fetching.
mz2 May 26, 2023
7fc2ff9
Publish the frontend image on ghcr.io
mz2 May 26, 2023
773ee45
Separate the frontend and backend images and name them clearer
mz2 May 26, 2023
aa95e46
Oops, fixes to publish.yml
mz2 May 26, 2023
ebf263f
README corrections mostly
mz2 May 26, 2023
f2469b2
Frontend charm looks to be now happy
mz2 May 26, 2023
fc4a975
Modify the backend API URL at runtime
mz2 May 26, 2023
f72e21d
Frontend and backend charms can now be successfully related, and serv…
mz2 May 27, 2023
3e9d1fe
README updates
mz2 May 27, 2023
c6c0fb3
Switch to traefik as ingress
mz2 May 28, 2023
aa28962
Update the "upstream-source" fields ahead of an experimental charmhub…
mz2 May 29, 2023
a176296
Fix the handling of the port value
mz2 May 29, 2023
ba5078b
Initial state of terraform resources
mz2 May 29, 2023
bd3d3c4
Terraform driven deploy now fully functional
mz2 May 29, 2023
e25f14e
Drop the nginx_route reference
mz2 May 29, 2023
789f424
README updates
mz2 May 29, 2023
19e3d84
README updates and additional config param for the ingress external h…
mz2 May 30, 2023
594c83e
Refactor tests, use db migrations, correct session, rollback automati…
omar-selo May 25, 2023
594c32a
Add basic endpoints to fetch data from DB (#10)
nadzyah May 29, 2023
873effe
Add /version endpoint
mz2 May 25, 2023
cd3100a
Attempt at getting the database URL from a database relation.
mz2 May 25, 2023
99aac71
Pulls from origin/main
mz2 May 30, 2023
ad0c599
Update .github/workflows/publish.yml based on suggestion
mz2 May 30, 2023
b3f8856
Update frontend/charm/src/charm.py based on suggestion
mz2 May 30, 2023
91a1627
Update frontend/charm/src/charm.py based on suggestion
mz2 May 30, 2023
d045e46
Update README.md based on suggestion
mz2 May 30, 2023
7cbbdfa
Adds a production oriented Dockerfile, with setuptools_scm based vers…
mz2 May 30, 2023
ad30c2c
Adds back the "charm" branch temporarily
mz2 May 30, 2023
4d97ca6
Adds a rule to build images at tags
mz2 May 30, 2023
927ff61
Refer to version 0.0.3 of the API and frontend images
mz2 May 30, 2023
ab28317
Drop the redundant .venv from .gitignore
mz2 May 30, 2023
1667f05
Drops the DB migration script which was really a placeholder.
mz2 May 30, 2023
9a4041d
Sorts out the charm libs
mz2 May 30, 2023
13c0bda
Fixes up dynamic versioning + some remaining references to "src" vs "…
mz2 May 30, 2023
eac3892
Drops the "charm" branch again from .github/workflows/publish.yml
mz2 May 30, 2023
a0eb1cc
README tweaks
mz2 May 30, 2023
79af4ca
Makes the API URL handling a bit clearer
mz2 May 30, 2023
068cf7f
Some clarifying README edits
mz2 May 30, 2023
5828e2d
Some further README tweaks
mz2 May 30, 2023
862609e
Added a note about the Dockerfiles and the application versioning
mz2 May 30, 2023
e263776
README tweak for the frontend charm
mz2 May 30, 2023
4429ca1
Assorted README clarifications, linter, test failure fix related tweaks.
mz2 May 31, 2023
180dd65
Update backend/charm/src/charm.py based on review suggestion
mz2 May 31, 2023
b733c75
Update frontend/charm/metadata.yaml based on suggestion
mz2 May 31, 2023
ea8ab4e
Comments, README tweaks in response to suggestions.
mz2 May 31, 2023
95dbbec
Removes some redundant config change handler that was actually not co…
mz2 May 31, 2023
c4d6f38
Removes a redundant thing that was included in the boilerplate.
mz2 May 31, 2023
c02fe38
Fixes some metadata.yaml typos
mz2 May 31, 2023
c063485
Update README.md based on review suggestion
mz2 May 31, 2023
40f6373
Drops the .flake8 config files
mz2 May 31, 2023
b891c49
Merges in origin/main
mz2 May 31, 2023
cf1e8be
Adds a README section about releasing the charms
mz2 May 31, 2023
96ccda2
Drop the v1-access part from a terraform resource name
mz2 May 31, 2023
586983d
Fixed up enough import issues to get the backend running again
mz2 May 31, 2023
571c3af
Update image tag reference.
mz2 May 31, 2023
de1719d
Fix the relation name change induced bork
mz2 May 31, 2023
def0ad7
poetry build instead of pip3 install
mz2 May 31, 2023
dfc5e84
Update the image reference
mz2 May 31, 2023
5fc27fd
Adds back the version endpoint
mz2 May 31, 2023
ebda20a
GitHub action for releasing the charm
mz2 May 31, 2023
a7d03f3
Turn off publishing frontend images for now
mz2 May 31, 2023
174d10b
Drops some redundant files that were moved around.
mz2 May 31, 2023
ec32789
Drops some redundant imports
mz2 May 31, 2023
738a13c
Update backend image tag
mz2 May 31, 2023
b4d8f23
Get package name with __package__ instead of a hard coded name.
mz2 May 31, 2023
2d55b66
Fix an import path issue from an earlier merge.
mz2 May 31, 2023
84a6a14
Revert "Drops some redundant imports"
mz2 May 31, 2023
9efcd1e
Revert "Drops some redundant files that were moved around."
mz2 May 31, 2023
5d98ab3
Reinstates some files in places they were meant to be in
mz2 May 31, 2023
35ffa7d
Fixes one last import issue
mz2 May 31, 2023
3181f92
Drop the automated channel choice for now for charm releasing
mz2 May 31, 2023
6daa559
Drop the charm building for tags, add it for ghcr images
mz2 May 31, 2023
4dfc023
Update image reference ahead of rebuilding the charm
mz2 May 31, 2023
cee9f16
Divide backend and frontend builds
mz2 May 31, 2023
e38c3d9
Drops the charm releasing action for now.
mz2 May 31, 2023
0788f19
Drop the charm release and frontend image builds.
mz2 May 31, 2023
ef36b1e
Handles the VS Code settings files in a hopefully agreeable way.
mz2 Jun 1, 2023
6cc6912
Frontend build works again
mz2 Jun 1, 2023
51887ff
Merges with origin/charm
mz2 Jun 1, 2023
c2a7f33
Fixes the local dev Docker file and updates README accordingly
mz2 Jun 1, 2023
da10b54
Removes some redundant files
mz2 Jun 1, 2023
f1ac076
Removed one more redundant file.
mz2 Jun 1, 2023
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: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
backend/dist
44 changes: 44 additions & 0 deletions .github/workflows/public_backend_image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Create and publish a Docker image
on:
push:
branches: ["main"]
tags: ["v*.*.*"]

env:
REGISTRY: ghcr.io

jobs:
build-and-push-backend-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

env:
IMAGE_NAME: ${{ github.repository }}/api

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push backend Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
file: ./backend/Dockerfile.production
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
43 changes: 43 additions & 0 deletions .github/workflows/publish_frontend_image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Create and publish a Docker image
on:
push:
branches: ["main"]
tags: ["v*.*.*"]

env:
REGISTRY: ghcr.io

jobs:
build-and-push-frontend-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

env:
IMAGE_NAME: ${{ github.repository }}/frontend

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push frontend Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: ./frontend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
28 changes: 28 additions & 0 deletions .github/workflows/release_charms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
on:
# pull_request:
# branches:
# - main
push:
branches: ["a-branch-that-definitely-does-not-exist"]
# tags: ["v*.*.*"]

jobs:
build:
name: Release test-observer-api charm to edge
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
#- name: Select charmhub channel
# uses: canonical/charming-actions/channel@2.2.0
# id: channel
- name: Upload charm to charmhub
uses: canonical/charming-actions/upload-charm@2.2.0
with:
charm-path: "backend/charm"
credentials: "${{ secrets.CHARMHUB_AUTH_API }}"
github-token: "${{ secrets.GITHUB_TOKEN }}"
upload-image: "true"
channel: edge # "${{ steps.channel.outputs.name }}"
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Visual Studio Code configuations
.vscode/
# Visual Studio Code configurations
.vscode
196 changes: 196 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,199 @@
# Test Observer

Observe the status and state of certification tests for various artefacts

## Prerequisites for developing and deploying locally

- `juju` 3.1 or later (`sudo snap install juju --channel=3.1/stable`)
- `microk8s` 1.27 or later (`sudo snap install microk8s --channel=1.27-strict/stable`) + [permission setup steps after install](https://juju.is/docs/sdk/set-up-your-development-environment#heading--install-microk8s)
- `terraform` 1.4.6 or later (`sudo snap install terraform`)
- `lxd` 5.13 or later (`sudo snap install lxc --channel=5.0/stable`) + `lxd init` after install.
- `charmcraft` 2.3.0 or later (`sudo snap install charmcraft --channel=2.x/stable`)
- optional: `jhack` for all kinds of handy Juju and charm SDK development and debugging operations (`sudo snap install jhack`)

## Deploying a copy of the system with terraform / juju in microk8s

Fist configure microk8s with the needed extensions:

```
sudo microk8s enable community # required for installing traefik
sudo microk8s enable dns hostpath-storage metallb traefik # metallb setup involves choosing a free IP range for the load balancer.
```

Then help microk8s work with an authorized (private) OCI image registry at ghcr.io:

1. Get a GitHub personal access token at https://github.com/settings/tokens/new with the `package:read` permission.
2. Configure containerd in microk8s with the auth credentials needed to pull images from non-default, authorisation requiring OCI registries by appending the following to `/var/snap/microk8s/current/args/containerd-template.toml`:

```yaml
[plugins."io.containerd.grpc.v1.cri".registry.configs."ghcr.io".auth]
username = "your-GitHub-username"
password = "your-GitHub-API-token"
```

After this config file tweak, restart containerd and microk8s:

```bash
sudo systemctl restart snap.microk8s.daemon-containerd.service && sudo microk8s.stop && sudo microk8s.start
juju bootstrap microk8s
juju model-config logging-config="<root>=DEBUG"
```

### Deploy the system locally with Terraform

In the `terraform` directory of your working copy, complete the one-time initialisation:

```bash
cd terraform
terraform init
```

After initialization (or after making changes to the terraform configuration) you can deploy the whole system with:

```bash
TF_VAR_environment=development TF_VAR_external_ingress_hostname="mah-domain.com" terraform apply -auto-approve
```

At the time of writing, this will accomplish deploying the following:

- the backend API server
- the frontend served using nginx
- a postgresql database
- traefik as ingress
- backend connected to frontend (the backend's public facing base URI passed to the frontend app)
- backend connected to database
- backend connected to load balancer
- frontend connected to load balancer

Terraform works by applying changes between the current state of the system and what is in the plan (the test-observer.tf configuration file). When `terraform apply` is run the 1st time, there is no state -> it will create the Juju model and all resources inside it. When it is run with a pre-existing model already in place, it will instead set / unset config values that have changed, add / remove relations, add / remove applications, etc. Basically, it makes working with Juju declarative - yay!

The terraform juju provider is documented over here: https://registry.terraform.io/providers/juju/juju/latest/docs

Terraform tracks its state with a .tfstate file which is created as a result of running `terraform apply` -- for production purposes this will be stored in an S3-like bucket remotely, and for local development purposes it sits in the `terraform` directory aftery you have done a `terraform apply`).

You can optionally get SSL certificates automatically managed for the ingress (in case you happen to have a DNS zone with Cloudflare DNS available):

```bash
TF_VAR_environment=development TF_VAR_external_ingress_hostname="mah-domain.com" TF_VAR_cloudflare_acme=true TF_VAR_cloudflare_dns_api_token=... TF_VAR_cloudflare_zone_read_api_token=... TF_VAR_cloudflare_email=... terraform apply -auto-approve
```

After all is up, `juju status --relations` should give you output to the direction of the following (the acme-operator only there if `TF_VAR_cloudflare_acme` was passed in):

```bash
$ juju status --relations
Model Controller Cloud/Region Version SLA Timestamp
test-observer-development microk8s-localhost microk8s/localhost 3.1.2 unsupported 23:23:01+03:00

App Version Status Scale Charm Channel Rev Address Exposed Message
acme-operator active 1 cloudflare-acme-operator beta 3 10.152.183.59 no
ingress 2.9.6 active 1 traefik-k8s stable 110 192.168.0.202 no
pg 14.7 active 1 postgresql-k8s 14/stable 73 10.152.183.106 no Primary
test-observer-api active 1 test-observer-api edge 6 10.152.183.207 no
test-observer-frontend active 1 test-observer-frontend edge 2 10.152.183.111 no

Unit Workload Agent Address Ports Message
acme-operator/0* active idle 10.1.92.188
ingress/0* active idle 10.1.92.182
pg/0* active idle 10.1.92.137 Primary
test-observer-api/0* active idle 10.1.92.143
test-observer-frontend/0* active idle 10.1.92.189

Relation provider Requirer Interface Type Message
acme-operator:certificates ingress:certificates tls-certificates regular
ingress:ingress test-observer-api:ingress ingress regular
ingress:ingress test-observer-frontend:ingress ingress regular
pg:database test-observer-api:database postgresql_client regular
pg:database-peers pg:database-peers postgresql_peers peer
pg:restart pg:restart rolling_op peer
test-observer-api:test-observer-rest-api test-observer-frontend:test-observer-rest-api http regular
```

To test the application with the frontend and API server ports exposed, you need to create some aliases in `/etc/hosts` to the IP address that the ingress got from `metallb` (`juju status` above will find you the ingress IP). Let's assume you have a domain `mah-domain.com` that you want to expose service under, the backend and frontend will be present as subdomains `test-observer-frontend.mah-domain.com` and `test-observer-api.mah-domain.com`, respectively:

```bash
$ cat /etc/hosts
192.168.0.202 test-observer-frontend.mah-domain.com test-observer-api.mah-domain.com
...
```

## Developing the charm

To develop and test updates to the backend and frontend charms, you would typically want to first complete the above steps to deploy a working system. Once you have done that, proceed with the following steps.

### Build and refresh the backend charm

You can make edits to the backend charm and refresh it in the running system on the fly with:

```bash
cd backend/charm
charmcraft pack
juju refresh test-observer-api --path ./test-observer-api_ubuntu-22.04-amd64.charm

# to update the OCI image that runs the backend
juju attach-resource test-observer-api --resource api-image=ghcr.io/canonical/test_observer/backend:[tag or sha]
```

### Build and refresh the frontend charm

Same thing with the frontend:

```bash
cd frontend/charm
charmcraft pack

juju refresh test-observer-frontend ./test-observer-frontend_ubuntu-22.04-amd64.charm

# to update the OCI image that runs the backend
juju attach-resource test-observer-frontend frontend-image=ghcr.io/canonical/test_observer/frontend:[tag or sha]
```

Note that the frontend app is made aware of the backend URL to connect to using the global `window.testObserverAPIBaseURI`, which is set at runtime with some nginx config level trickery based on...

- the `test-observer-api` charm's `hostname` config value.
- the frontend charm's `test-observer-api-scheme` config value.

These in turn can be set using the terraform plan (`terraform/test-observer.tf` and associated variables).

## Releasing the charms

You can use [release-k8s-charm](https://github.com/mz2/release-k8s-charm) to release the charms to charmhub, until we ingroduce a GitHub action driven workflow for releasing them (the `upload-charm` action in [canonical/charming-actions](https://github.com/canonical/charming-actions) will be the longer term solution).

To release the backend charm:

```bash
cd backend/charm
wherever-you-stash-source-code/release-k8s-charm/main.py --charm-metadata ./metadata.yaml --channel edge
```

To release the frontend charm:

```bash
cd frontend/charm
wherever-you-stash-source-code/release-k8s-charm/main.py --charm-metadata ./metadata.yaml --channel edge
```

## VS Code & charm libraries

VS Code fails to find (for autocompletions and code navigation purposes) the charm libraries under `lib` in each of `backend/charm` and `frontend/charm`. There is a .vscode-settings-default.json found under each of these directories which you can copy to the `.gitignore`d path `.vscode/settings.json` to make them fly. Taking the backend charm as an example:

```bash
mkdir -p backend/charm/.vscode
cp backend/charm/.vscode-settings-default.json backend/charm/.vscode/settings.json

mkdir -p frontend/charm/.vscode
cp frontend/charm/.vscode-settings-default.json frontend/charm/.vscode/settings.json
```

Now if you use as your project the directory `backend/charm` and `frontend/charm` respectively (which you'll want to do also for them to keep their own virtual environments), VS Code should be happy.

## Handy documentation pointers about charming

- [Integrations (how to provide and require relations)](https://juju.is/docs/sdk/integration)

### Enable the K8s Dashboard

You need an auth token in case you want to connect to the kubernetes dashboard:

```bash
microk8s kubectl describe secret -n kube-system microk8s-dashboard-token
```
1 change: 0 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
34 changes: 17 additions & 17 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
FROM python:3.10

EXPOSE 30000

WORKDIR /home/app

COPY poetry.lock .

COPY pyproject.toml .

RUN pip install poetry && \
poetry config virtualenvs.create false && \
poetry install --no-interaction --no-ansi --without dev

COPY . .

CMD [ "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "30000", "--reload" ]
FROM python:3.10

EXPOSE 30000

WORKDIR /home/app

COPY poetry.lock .

COPY pyproject.toml .

RUN pip3 install poetry && \
poetry config virtualenvs.create false && \
poetry install --no-interaction --no-ansi --without dev

COPY . .

CMD [ "uvicorn", "test_observer.main:app", "--host", "0.0.0.0", "--port", "30000", "--reload" ]
20 changes: 20 additions & 0 deletions backend/Dockerfile.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM python:3.10

EXPOSE 30000

WORKDIR /home/app

COPY backend/poetry.lock .

COPY backend/pyproject.toml .

RUN --mount=source=.git,target=.git,type=bind pip3 install poetry && \
poetry config virtualenvs.create false && \
poetry install --no-interaction --no-ansi --without dev && \
pip3 install --user poetry-dynamic-versioning[plugin]

COPY ./backend .

RUN --mount=source=.git,target=.git,type=bind poetry build && pip3 install dist/*.whl

CMD [ "uvicorn", "test_observer.main:app", "--host", "0.0.0.0", "--port", "30000" ]
Loading