Skip to content

Commit 88d8d1e

Browse files
committed
Merge branch 'charmed-snapcraft-io' into staging
2 parents 7051e29 + 0595613 commit 88d8d1e

11 files changed

Lines changed: 247 additions & 0 deletions

File tree

.github/workflows/deploy.yaml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Pack and Deploy
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- staging
8+
workflow_dispatch:
9+
inputs:
10+
environment:
11+
description: 'Environment (Production or Staging)'
12+
required: true
13+
type: choice
14+
options:
15+
- Production
16+
- Staging
17+
jobs:
18+
setup:
19+
runs-on: ubuntu-latest
20+
environment: ${{ github.event.inputs.environment != '' && github.event.inputs.environment || (github.ref == 'refs/heads/main' && 'Production' || 'Staging') }}
21+
outputs:
22+
charm_name: ${{ steps.setup-vars.outputs.charm_name }}
23+
channel: ${{ steps.setup-vars.outputs.channel }}
24+
juju_controller_name: ${{ steps.setup-vars.outputs.juju_controller_name }}
25+
juju_model_name: ${{ steps.setup-vars.outputs.juju_model_name }}
26+
environment: ${{ steps.setup-vars.outputs.environment }}
27+
steps:
28+
- name: setup vars
29+
id: setup-vars
30+
run: |
31+
echo "charm_name=${{ vars.CHARM_NAME }}" >> $GITHUB_OUTPUT
32+
echo "channel=${{ vars.CHANNEL }}" >> $GITHUB_OUTPUT
33+
echo "juju_controller_name=${{ vars.JUJU_CONTROLLER_NAME }}" >> $GITHUB_OUTPUT
34+
echo "juju_model_name=${{ vars.JUJU_MODEL_NAME }}" >> $GITHUB_OUTPUT
35+
echo "environment=${{ github.event.inputs.environment != '' && github.event.inputs.environment || (github.ref == 'refs/heads/main' && 'Production' || 'Staging') }}" >> $GITHUB_OUTPUT
36+
37+
deploy:
38+
needs: setup
39+
name: Deploy
40+
uses: canonical/webteam-devops/.github/workflows/deploy.yaml@main
41+
with:
42+
environment: ${{ needs.setup.outputs.environment }}
43+
charm_name: ${{ needs.setup.outputs.charm_name }}
44+
channel: ${{ needs.setup.outputs.channel }}
45+
juju_controller_name: ${{ needs.setup.outputs.juju_controller_name }}
46+
juju_model_name: ${{ needs.setup.outputs.juju_model_name }}
47+
secrets:
48+
VAULT_APPROLE_ROLE_ID: ${{ secrets.VAULT_APPROLE_ROLE_ID }}
49+
VAULT_APPROLE_SECRET_ID: ${{ secrets.VAULT_APPROLE_SECRET_ID }}
50+
CHARMHUB_TOKEN: ${{ secrets.CHARMHUB_TOKEN }}

.github/workflows/pr.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ jobs:
4040
dotrun &
4141
curl --head --fail --retry-delay 3 --retry 30 --retry-connrefused http://localhost:8004
4242
43+
pack-rock:
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@v4
47+
48+
- name: Setup LXD
49+
uses: canonical/setup-lxd@main
50+
51+
- name: Setup rockcraft
52+
run: sudo snap install rockcraft --classic
53+
54+
- name: Pack rock
55+
run: rockcraft pack
56+
4357
lint-python:
4458
runs-on: ubuntu-latest
4559

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,7 @@ coverage/
6969
.webcache/
7070
.webcache_blog/
7171
.coverage
72+
73+
# Charming artifacts
74+
*.charm
75+
*.rock

app.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This file serves as an entry point for the rock image. It is required by the PaaS app charmer.
2+
# The flask application must be defined in this file under the variable name `app`.
3+
# See - https://documentation.ubuntu.com/rockcraft/en/latest/reference/extensions/flask-framework/
4+
import os
5+
import logging
6+
7+
# canonicalwebteam.flask-base requires SECRET_KEY to be set, this must be done before importing the app
8+
os.environ["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]
9+
10+
# disable talisker logger, as it is not used in this application and clutters logs
11+
logging.getLogger("talisker.context").disabled = True
12+
13+
from webapp.app import create_app
14+
15+
app = create_app()

charm/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
venv/
2+
build/
3+
*.charm
4+
.tox/
5+
.coverage
6+
__pycache__/
7+
*.py[cod]
8+
.idea
9+
.vscode/
10+
lib/

charm/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# The Charm for the snapcraft.io website
2+
3+
This charm was created using the [PaaS App Charmer](https://juju.is/docs/sdk/paas-charm)
4+
5+
## Local development
6+
7+
To work on this charm locally, you first need to set up an environment, follow [this section](https://juju.is/docs/sdk/write-your-first-kubernetes-charm-for-a-flask-app#heading--set-things-up) of the tutorial.
8+
9+
Then, you can run the following command to pack and upload the rock:
10+
11+
```bash
12+
rockcraft pack
13+
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false oci-archive:snapcraft-io*.rock docker://localhost:32000/snapcraft-io:1
14+
```
15+
16+
This will pack the application into a [rock](https://documentation.ubuntu.com/rockcraft/en/latest/explanation/rocks/) (OCI image) and upload it to the local registry.
17+
18+
You can deploy the charm locally with:
19+
20+
```bash
21+
cd charm
22+
charmcraft fetch-libs
23+
charmcraft pack
24+
juju deploy ./*.charm --resource flask-app-image=localhost:32000/snapcraft-io:1
25+
```
26+
27+
This will deploy the charm with the rock image you just uploaded attached as a resource.
28+
29+
once `juju status` reports the charm as `active`, you can test the webserver:
30+
31+
```bash
32+
curl {IP_OF_SNAPCRAFT_IO_UNIT}:8000
33+
```
34+
35+
to connect using a browser, the easiest way is to integrate with `nginx-ingress-integrator`:
36+
37+
```bash
38+
juju deploy nginx-ingress-integrator --trust
39+
juju config nginx-ingress-integrator service-hostname=snapcraft.local path-routes=/
40+
juju integrate nginx-ingress-integrator snapcraft-io
41+
```
42+
43+
You can then add `snapcraft.local` to your `/etc/hosts` file with the IP of the multipass vm:
44+
45+
```bash
46+
multipass ls # Get the IP of the VM
47+
echo "{IP_OF_VM} snapcraft.local" | sudo tee -a /etc/hosts
48+
```
49+
50+
> Note: login will not work using this setup, if you'd like to access publisher pages, change the domain to `staging.snapcraft.io`, but make sure to remove the line from `/etc/hosts/` after you're done.
51+
52+
53+
## Design Decisions:
54+
- To keep the codebase clean and charm libraries updated, they are only fetched before packing the charm in the [Github Actions workflow](https://github.com/canonical/snapcraft.io/blob/main/.github/workflows/publish_charm.yaml#L25).
55+
- As all our work is open source, the charm is publicly available on [snapcraft](https://charmhub.io/snapcraft-io), the rock image is also included as a resource. This significantly simplifies deployment.

charm/charmcraft.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: snapcraft-io
2+
3+
type: charm
4+
5+
bases:
6+
- build-on:
7+
- name: ubuntu
8+
channel: "22.04"
9+
run-on:
10+
- name: ubuntu
11+
channel: "22.04"
12+
13+
summary: The charm for the snapcraft.io website
14+
15+
description: The charm for the snapcraft.io website, built with the PaaS app charmer
16+
17+
extensions:
18+
- flask-framework
19+
20+
requires:
21+
tracing:
22+
interface: tracing
23+
optional: true
24+
limit: 1

charm/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ops ~= 2.17
2+
paas-charm>=1.0,<2

charm/src/charm.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python3
2+
3+
"""Flask Charm entrypoint."""
4+
5+
import logging
6+
import typing
7+
8+
import ops
9+
10+
import paas_charm.flask
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
class CharmCharm(paas_charm.flask.Charm):
16+
"""Flask Charm service."""
17+
18+
def __init__(self, *args: typing.Any) -> None:
19+
"""Initialize the instance.
20+
21+
Args:
22+
args: passthrough to CharmBase.
23+
"""
24+
super().__init__(*args)
25+
26+
27+
if __name__ == "__main__":
28+
ops.main(CharmCharm)

rockcraft.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: snapcraft-io
2+
base: bare
3+
build-base: ubuntu@22.04
4+
version: "0.1"
5+
summary: Rocked snapcraft.io
6+
description: |
7+
This is the rockcraft for the snapcraft.io website.
8+
platforms:
9+
amd64:
10+
arm64:
11+
12+
extensions:
13+
- flask-framework
14+
15+
parts:
16+
build-ui:
17+
plugin: nil
18+
source: .
19+
source-type: local
20+
build-snaps:
21+
- node/22/stable
22+
override-build: |
23+
set -eux
24+
# install dependencies
25+
npm install -g yarn
26+
yarn install
27+
# build the UI
28+
yarn run build
29+
mkdir -p "$CRAFT_PART_INSTALL/flask/app"
30+
cp -r static "$CRAFT_PART_INSTALL/flask/app/"
31+
flask-framework/install-app:
32+
after:
33+
- build-ui
34+
prime:
35+
- flask/app/.env
36+
- flask/app/app.py
37+
- flask/app/webapp
38+
- flask/app/templates
39+
# - flask/app/static # it already gets copied in the build-ui step
40+
- flask/app/redirects.yaml
41+
- flask/app/security.txt
42+
- flask/app/robots.txt

0 commit comments

Comments
 (0)