Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0d44757
Define the dependencies more exactly to get the integration tests run…
mz2 Jun 7, 2023
97e8d4c
juju 3.1/stable -> 3.0/stable
mz2 Jun 7, 2023
a055701
Install strictly confined versions of juju and microk8s.
mz2 Jun 7, 2023
672ea62
Use microk8s 1.27-strict/stable
mz2 Jun 7, 2023
6c11eb5
Adds package repo access to the integration test job
mz2 Jun 7, 2023
5c149e2
Add the containerd template modification
mz2 Jun 7, 2023
11cf30b
Adds some missing sudoing
mz2 Jun 7, 2023
6e8375a
From >> to tee -a
mz2 Jun 7, 2023
97f1531
Drop the docker login
mz2 Jun 7, 2023
1478f45
Oops, escaping quotes did not work quite right there.
mz2 Jun 7, 2023
b03e015
Adjust the integration test scenario, expecting rather than raising o…
mz2 Jun 7, 2023
55b9436
Drops the debugging intended branch from the list of branches to trig…
mz2 Jun 7, 2023
cde6b80
Change to maintenance status instead for clarity, since configuration…
mz2 Jun 7, 2023
1ed9d5f
Adds the "integration-test-fix" branch into the rule still
mz2 Jun 7, 2023
0a30cee
Adds a 503 response / maintenance status to the frontend application …
mz2 Jun 7, 2023
bf2859c
Removs the redundant import StoredState
mz2 Jun 7, 2023
2fc065f
Add a `microk8s status --wait-ready`
mz2 Jun 7, 2023
06a258e
Adds missing sudo
mz2 Jun 7, 2023
4af3b2a
Merge branch 'main' into integration-test-fix
nadzyah Jun 20, 2023
0db5736
Merge branch 'main' into integration-test-fix
nadzyah Jun 22, 2023
8417af7
Merge branch 'main' into integration-test-fix
nadzyah Jun 27, 2023
a3a5404
Let's try with the microk8s version + libpyjuju < 3.1
mz2 Jul 2, 2023
e90849c
Merge branch 'integration-test-fix' of github.com:canonical/test_obse…
mz2 Jul 2, 2023
234b1aa
Back to 5.13/stable for lxd
mz2 Jul 2, 2023
fd3ed35
2.9/stable -> 3.0/stable
mz2 Jul 2, 2023
63f4d6f
Trying with the non-strictly confined microk8s
mz2 Jul 2, 2023
acf47ce
Adds "publish-charm" job
mz2 Jul 2, 2023
b03dddb
Adds promotion action
mz2 Jul 2, 2023
7348559
Swap to self-hosted runners
mz2 Oct 26, 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
8 changes: 4 additions & 4 deletions .github/workflows/frontend_charm_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ on:
branches:
- main
push:
branches: ["main"]
branches: ["main", "integration-test-fix"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I suggest that we actually keep this in there because I imagine this will not be the last case where we have to open a branch & PR to fix integration tests issues, and it's awkward having to add a rule each time (or to tag all the time).

tags: ["v*.*.*"]

jobs:
lint-report:
name: Lint and format report
runs-on: ubuntu-22.04
runs-on: [self-hosted, linux, large]
steps:
- uses: actions/checkout@v3
- name: Install tox
Expand All @@ -23,7 +23,7 @@ jobs:

static-analysis:
name: Static analysis
runs-on: ubuntu-22.04
runs-on: [self-hosted, linux]
steps:
- uses: actions/checkout@v3
- name: Install tox
Expand All @@ -36,7 +36,7 @@ jobs:

unit-tests-with-coverage:
name: Unit tests
runs-on: ubuntu-22.04
runs-on: [self-hosted, linux]
steps:
- uses: actions/checkout@v3
- name: Install tox
Expand Down
59 changes: 56 additions & 3 deletions .github/workflows/frontend_charm_integration_tests.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
name: Run charm tests
on:
push:
branches: ["main"]
branches: ["main", "integration-test-fix"]
tags: ["v*.*.*"]

jobs:
integration-test:
permissions:
contents: read
packages: read

name: Integration tests
runs-on: ubuntu-22.04
runs-on: [self-hosted, linux, large]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand All @@ -16,9 +20,32 @@ jobs:
with:
channel: 5.13/stable
- name: Setup operator environment
uses: charmed-kubernetes/actions-operator@main
uses: charmed-kubernetes/actions-operator@main # mz2/actions-operator@4fcb55b99e2a0f69a5910870d3639d203aeb87aa
with:
channel: 1.27/stable # 1.26.4
provider: microk8s
juju-channel: 3.0/stable
# juju-classic-confinement: "false"
# microk8s-classic-confinement: "false"
charmcraft-channel: 2.x/stable
lxd-channel: 5.13/stable
- name: Adjust containerd template to accept ghcr.io images
run: |
sudo mkdir -p /var/snap/microk8s/current/args &&
echo "" | sudo tee -a /var/snap/microk8s/current/args/containerd-template.toml &&
echo '[plugins."io.containerd.grpc.v1.cri".registry.configs."ghcr.io".auth]' | sudo tee -a /var/snap/microk8s/current/args/containerd-template.toml &&
echo ' username = "$GHCR_PACKAGE_API_USER"' | sudo tee -a /var/snap/microk8s/current/args/containerd-template.toml &&
echo ' password = "$GHCR_PACKAGE_API_TOKEN"' | sudo tee -a /var/snap/microk8s/current/args/containerd-template.toml &&
sudo systemctl restart snap.microk8s.daemon-containerd.service &&
sudo microk8s.stop &&
sleep 10 &&
sudo microk8s.start &&
sleep 10 &&
sudo microk8s status --wait-ready &&
sleep 30
env:
GHCR_PACKAGE_API_USER: ${{ secrets.GHCR_PACKAGE_API_USER }}
GHCR_PACKAGE_API_TOKEN: ${{ secrets.GHCR_PACKAGE_API_TOKEN }}
- name: Run integration tests
run: tox -e integration
- name: Archive Tested Charm
Expand All @@ -43,3 +70,29 @@ jobs:
defaults:
run:
working-directory: ./frontend/charm

publish-charm:
name: Publish Charm
needs: integration-test
runs-on: [self-hosted, linux]
if: ${{ github.ref_name == 'main' }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install charmcraft
run: sudo snap install charmcraft --classic
- name: Fetch Tested Charm
uses: actions/download-artifact@v3
with:
name: tested-charm
- name: Move charm in current directory
run: find ./ -name test-observer-frontend_ubuntu-22.04-amd64.charm -exec mv -t ./ {} \;
- name: Select Charmhub channel
uses: canonical/charming-actions/channel@2.2.2
id: channel
- name: Upload charm to Charmhub
uses: canonical/charming-actions/upload-charm@2.2.2
with:
credentials: "${{ secrets.CHARMHUB_AUTH_API }}"
github-token: "${{ secrets.GITHUB_TOKEN }}"
channel: "${{ steps.channel.outputs.name }}"
43 changes: 43 additions & 0 deletions .github/workflows/frontend_charm_promote.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Promote Charm

on:
workflow_dispatch:
inputs:
promotion:
type: choice
description: Channel to promote from
options:
- edge -> beta
- beta -> candidate
- candidate -> stable

jobs:
promote:
name: Promote Charm
runs-on: [self-hosted, linux]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set target channel
env:
PROMOTE_FROM: ${{ github.event.inputs.promotion }}
run: |
if [ "${PROMOTE_FROM}" == "edge -> beta" ]; then
echo "promote-from=edge" >> ${GITHUB_ENV}
echo "promote-to=beta" >> ${GITHUB_ENV}
elif [ "${PROMOTE_FROM}" == "beta -> candidate" ]; then
echo "promote-from=beta" >> ${GITHUB_ENV}
echo "promote-to=candidate" >> ${GITHUB_ENV}
elif [ "${PROMOTE_FROM}" == "candidate -> stable" ]; then
echo "promote-from=candidate" >> ${GITHUB_ENV}
echo "promote-to=stable" >> ${GITHUB_ENV}
fi
- name: Promote Charm
uses: canonical/charming-actions/release-charm@2.2.0
with:
base-channel: 22.04
credentials: ${{ secrets.CHARMHUB_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
destination-channel: latest/${{ env.promote-to }}
origin-channel: latest/${{ env.promote-from }}
charmcraft-channel: latest/stable
2 changes: 1 addition & 1 deletion .github/workflows/public_backend_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ env:

jobs:
build-and-push-backend-image:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux]
permissions:
contents: read
packages: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_frontend_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ env:

jobs:
build-and-push-frontend-image:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux]
permissions:
contents: read
packages: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_charms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
build:
name: Release test-observer-api charm to edge
runs-on: ubuntu-latest
runs-on: [self-hosted, linux]
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ concurrency:
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux, large]
defaults:
run:
working-directory: backend
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ concurrency:
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
runs-on: [self-hosted, linux, large]
defaults:
run:
working-directory: frontend
Expand Down
2 changes: 1 addition & 1 deletion frontend/charm/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ resources:
frontend-image:
type: oci-image
description: OCI image for test-observer-frontend
upstream-source: ghcr.io/canonical/test_observer/frontend:v0.0.10
upstream-source: ghcr.io/canonical/test_observer/frontend:v0.0.10
98 changes: 73 additions & 25 deletions frontend/charm/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
IngressPerAppRequirer,
IngressPerAppRevokedEvent,
)
from ops.framework import StoredState
from ops.model import (
ActiveStatus,
BlockedStatus,
Expand All @@ -30,8 +29,6 @@
class TestObserverFrontendCharm(ops.CharmBase):
"""The frontend charm operates serving the frontend through nginx."""

_stored = StoredState()

def __init__(self, *args):
super().__init__(*args)
self.pebble_service_name = "test-observer-frontend"
Expand All @@ -56,8 +53,6 @@ def __init__(self, *args):
self.framework.observe(self.ingress.on.ready, self._on_ingress_ready)
self.framework.observe(self.ingress.on.revoked, self._on_ingress_revoked)

self._stored.set_default(backend_hostname=None, backend_port=None)

def _on_frontend_pebble_ready(self, event: ops.PebbleReadyEvent):
container = event.workload
container.add_layer("frontend", self._pebble_layer, combine=True)
Expand Down Expand Up @@ -98,20 +93,12 @@ def _config_is_valid(self, config) -> Tuple[bool, str]:
def _on_rest_api_relation_update(self, event):
api_hostname = event.relation.data[event.app].get("hostname")
api_port = event.relation.data[event.app].get("port")
logger.debug(f"API hostname: {api_hostname} (app: {event.app})")

if self.unit.is_leader():
self._stored.backend_hostname = api_hostname
self._stored.backend_port = api_port

logger.debug(f"API hostname: {api_hostname}, port: {api_port} (app: {event.app})")
self._update_layer_and_restart(event)

def _on_rest_api_relation_broken(self, event):
logger.debug("REST API relation broken -> removing backend hostname")

if self.unit.is_leader():
self._stored.backend_hostname = None
self._stored.backend_port = None
logger.debug("REST API relation broken")
self._update_layer_and_restart(event)

def nginx_config(self, base_uri: str) -> str:
"""Return a config where the backend port `base_uri` is adjusted."""
Expand All @@ -136,30 +123,91 @@ def nginx_config(self, base_uri: str) -> str:
}}
"""

def nginx_503_config(self) -> str:
"""Return a config for the situation when the backend is not yet available."""
return """
server {
listen 80 default_server;
server_name _;
return 503;
error_page 503 @maintenance;

location @maintenance {
rewrite ^(.*)$ /503.html break;
root /usr/share/nginx/html;
}
}
"""

def html_503(self) -> str:
"""Return a 503 response page."""
return """
<html>
<head>
<title>503 Service Unavailable</title>
</head>
<body>
<h1>503 Service Unavailable</h1>
<p>Backend not yet configured.</p>
</body>
</html>
"""

def _update_layer_and_restart(self, event):
self.unit.status = MaintenanceStatus(f"Updating {self.pebble_service_name} layer")

api_relation = self.model.relations["test-observer-rest-api"][0]

if api_relation is None:
if self.container.can_connect():
self.container.add_layer(
self.pebble_service_name, self._pebble_layer, combine=True
)
self.container.push(
"/etc/nginx/sites-available/test-observer-frontend",
self.nginx_503_config(),
make_dirs=True,
)
self.container.push(
"/usr/share/nginx/html/503.html",
self.html_503(),
make_dirs=True,
)
self.container.restart(self.pebble_service_name)
self.unit.status = MaintenanceStatus(
"test-observer-rest-api relation not connected."
)
else:
self.unit.status = WaitingStatus(
"Waiting for Pebble for API to set maintenance state"
)

return

hostname = api_relation.data[api_relation.app]["hostname"]
port = api_relation.data[api_relation.app]["port"]

scheme = self.config["test-observer-api-scheme"]
hostname = self._stored.backend_hostname
port = self._stored.backend_port

logger.info(f"Hostname: {hostname} (from relation: {hostname})")
logger.info(f"Port: {port} (from relation: {port})")

if int(port) == 80 or int(port) == 443:
base_uri = f"{scheme}{hostname}"
else:
base_uri = f"{scheme}{hostname}:{port}"

self.container.push(
"/etc/nginx/sites-available/test-observer-frontend",
self.nginx_config(base_uri=base_uri),
make_dirs=True,
)

if self.container.can_connect():
self.container.add_layer(self.pebble_service_name, self._pebble_layer, combine=True)
self.container.push(
"/etc/nginx/sites-available/test-observer-frontend",
self.nginx_config(base_uri=base_uri),
make_dirs=True,
)
self.container.restart(self.pebble_service_name)
self.unit.status = ActiveStatus()
else:
self.unit.status = WaitingStatus("Waiting for Pebble for API")
self.unit.status = WaitingStatus("Waiting for Pebble for API to set available state")

@property
def _pebble_layer(self):
Expand Down
9 changes: 4 additions & 5 deletions frontend/charm/tests/integration/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@


@pytest.mark.abort_on_fail
async def test_build_and_deploy(ops_test: OpsTest):
"""Build the charm-under-test and deploy it together with related charms.
async def test_build_and_deploy_without_backend(ops_test: OpsTest):
"""Build the charm-under-test and deploy it together without related charms.

Assert on the unit status before any relations/configurations take place.
"""
Expand All @@ -29,8 +29,7 @@ async def test_build_and_deploy(ops_test: OpsTest):
ops_test.model.deploy(charm, resources=resources, application_name=APP_NAME),
ops_test.model.wait_for_idle(
apps=[APP_NAME],
status="active",
raise_on_blocked=True,
timeout=1000,
status="maintenance",
timeout=2000,
),
)
2 changes: 1 addition & 1 deletion frontend/charm/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ commands =
description = Run integration tests
deps =
pytest
juju
juju<3.1
pytest-operator
-r {tox_root}/requirements.txt
commands =
Expand Down