diff --git a/.github/workflows/spread_tests.yaml b/.github/workflows/spread_tests.yaml new file mode 100644 index 0000000000..e0268bcd83 --- /dev/null +++ b/.github/workflows/spread_tests.yaml @@ -0,0 +1,35 @@ +name: Tests with spread + +on: + push: + branches: + - master + pull_request: + workflow_dispatch: + +jobs: + run-spread-tests: + runs-on: ubuntu-latest + strategy: + matrix: + spread_job: ['github-ci:ubuntu-22.04-amd64:tests/spread/', 'github-ci:ubuntu-22.04-amd64:tests/spread/scenario/'] + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-go@v5 + with: + check-latest: true + go-version: 'stable' + + - name: Install spread + run: | + go install github.com/snapcore/spread/cmd/spread@latest + - name: Run tests + run: | + sudo adduser --gecos "" --disabled-password ubuntu + echo "ubuntu:ubuntu" | sudo chpasswd + spread ${{ matrix.spread_job }} + - name: Tmate debugging session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 15 diff --git a/charm/generate-src-docs.sh b/charm/generate-src-docs.sh deleted file mode 100755 index 23cb00701c..0000000000 --- a/charm/generate-src-docs.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2024 Canonical Ltd. -# See LICENSE file for licensing details. - -rm -rf src-docs -lazydocs --no-watermark --output-path src-docs src/* diff --git a/charm/requirements-doc.txt b/charm/requirements-doc.txt new file mode 100644 index 0000000000..bb452f6bb2 --- /dev/null +++ b/charm/requirements-doc.txt @@ -0,0 +1,2 @@ +myst-parser==4.0.0 +sphinx-markdown-builder==0.6.7 diff --git a/charm/src-docs/Makefile b/charm/src-docs/Makefile new file mode 100644 index 0000000000..d4bb2cbb9e --- /dev/null +++ b/charm/src-docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/charm/src-docs/charm.py.md b/charm/src-docs/charm.py.md deleted file mode 100644 index b4f7f5f24b..0000000000 --- a/charm/src-docs/charm.py.md +++ /dev/null @@ -1,54 +0,0 @@ - - - - -# module `charm.py` -Django Charm entrypoint. - - - ---- - -## class `DjangoCharm` -Django Charm service. - - ---- - -#### property app - -Application that this unit is part of. - ---- - -#### property charm_dir - -Root directory of the charm as it is running. - ---- - -#### property config - -A mapping containing the charm's config and current values. - ---- - -#### property meta - -Metadata of this charm. - ---- - -#### property model - -Shortcut for more simple access the model. - ---- - -#### property unit - -Unit that this execution is responsible for. - - - - diff --git a/charm/src-docs/conf.py b/charm/src-docs/conf.py new file mode 100644 index 0000000000..eef57bd477 --- /dev/null +++ b/charm/src-docs/conf.py @@ -0,0 +1,30 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'NetBox' +copyright = '2024, Canonical' +author = 'Canonical' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "myst_parser", + "sphinx_markdown_builder", +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'alabaster' +html_static_path = ['_static'] diff --git a/charm/src-docs/index.md b/charm/src-docs/index.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/charm/src-docs/make.bat b/charm/src-docs/make.bat new file mode 100644 index 0000000000..32bb24529f --- /dev/null +++ b/charm/src-docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/charm/src-docs/tutorial/code/getting-started/task.yaml b/charm/src-docs/tutorial/code/getting-started/task.yaml new file mode 100644 index 0000000000..45a6d8e2d4 --- /dev/null +++ b/charm/src-docs/tutorial/code/getting-started/task.yaml @@ -0,0 +1,89 @@ +########################################### +# IMPORTANT +# Comments matter! +# The docs use the wrapping comments as +# markers for including said instructions +# as snippets in the docs. +########################################### +summary: Getting started with NetBox Tutorial + +execute: | + . "$CRAFT_TEST_LIB_PATH"/test-helpers.sh + + # configure minio + export AWS_ACCESS_KEY_ID=minio + export AWS_SECRET_ACCESS_KEY=supersuperkey + IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') + export AWS_ENDPOINT_URL="http://${IPADDR}:9000" + export AWS_BUCKET=netbox + export AWS_REGION=us-west-1 + + mkdir -p ${HOME}/minio/data + docker run -d -p 9000:9000 -p 9001:9001 --user $(id -u):$(id -g) --name minio -e "MINIO_ROOT_USER=minioadmin" -e "MINIO_ROOT_PASSWORD=minioadmin" -v ${HOME}/minio/data:/data quay.io/minio/minio server /data --console-address ":9001" + retry -n 5 --wait 2 docker exec minio mc config host add minio http://127.0.0.1:9000 minioadmin minioadmin + docker exec minio mc config host add minio http://127.0.0.1:9000 minioadmin minioadmin + docker exec minio mc admin user svcacct add --access-key "${AWS_ACCESS_KEY_ID}" --secret-key "${AWS_SECRET_ACCESS_KEY}" minio minioadmin + docker exec minio mc mb "minio/${AWS_BUCKET}" + docker exec minio mc ls minio + + # [docs:juju-add-model] + juju add-model netbox-tutorial + # [docs:juju-add-model-end] + + # [docs:juju-deploy-netbox] + juju deploy netbox + # [docs:juju-deploy-netbox-end] + + # [docs:netbox-config-allowed-hosts] + juju config netbox django-allowed-hosts='*' + # [docs:netbox-config-allowed-hosts-end] + + # [docs:juju-deploy-redis] + juju deploy redis-k8s --channel=latest/edge + # [docs:juju-deploy-redis-end] + + # [docs:juju-integrate-redis-netbox] + juju integrate redis-k8s netbox + # [docs:juju-integrate-redis-netbox-end] + + # [docs:juju-netbox-postgresql] + juju deploy postgresql-k8s --channel 14/stable --trust + juju integrate postgresql-k8s netbox + # [docs:juju-netbox-postgresql-end] + + # [docs:juju-netbox-s3] + juju deploy s3-integrator --channel edge + juju config s3-integrator endpoint="${AWS_ENDPOINT_URL}" bucket="${AWS_BUCKET}" path=/ region="${AWS_REGION}" s3-uri-style=path + juju wait-for application s3-integrator --query='name=="s3-integrator" && (status=="active" || status=="blocked")' + juju run s3-integrator/leader sync-s3-credentials access-key="${AWS_ACCESS_KEY_ID}" secret-key="${AWS_SECRET_ACCESS_KEY}" + juju integrate s3-integrator netbox + # [docs:juju-netbox-s3-end] + + # [docs:traefik] + juju deploy traefik-k8s --channel edge --trust + # juju config traefik-k8s external_hostname= + juju config traefik-k8s routing_mode=path + juju integrate traefik-k8s netbox + # [docs:traefik-end] + + juju wait-for application traefik-k8s + juju wait-for application netbox + + # [docs:traefik-show-endpoints] + juju run traefik-k8s/0 show-proxied-endpoints --format=yaml + # [docs:traefik-show-endpoints-end] + NETBOX_URL=$( juju run traefik-k8s/0 show-proxied-endpoints --format=json | jq -r '."traefik-k8s/0".results."proxied-endpoints"' | jq -r '.netbox.url' ) + curl --fail "${NETBOX_URL}" | grep "Home | NetBox" + + + COMMAND=( + # [docs:netbox-create-superuser] + juju run netbox/0 create-superuser username=admin email=admin@example.com + # [docs:netbox-create-superuser-end] + --format=json + ) + echo ${COMMAND[@]} + PASSWORD=$( ${COMMAND[@]} | jq -r '."netbox/0".results.password' ) + + # just test that we can get a token with the admin user. + curl -H "Accept: application/json" -H "Content-Type: application/json" --fail "${NETBOX_URL}/api/users/tokens/provision/" -X POST -d "{\"username\": \"admin\", \"password\": \"${PASSWORD}\"}" diff --git a/charm/src-docs/tutorial/getting-started.md b/charm/src-docs/tutorial/getting-started.md new file mode 100644 index 0000000000..1412c30802 --- /dev/null +++ b/charm/src-docs/tutorial/getting-started.md @@ -0,0 +1,142 @@ +# Getting Started + +## What you’ll do +- Deploy the NetBox charm. +- Integrate with Redis using the redis-k8s charm. +- Integrate with the PostgreSQL K8s charm. +- Integrate with S3 for storage. +- Expose the NetBox charm with Traefik k8s. +- Create a super user. + +Through the process, you'll verify the workload state, and log in to +your NetBox instance. + +## Requirements +- Juju 3 installed. +- Juju controller that can create a model of type kubernetes. +- Read/write access to a S3 compatible server with a bucket created. +- Configuration compatible with the traefik-k8s charms. In the case of MicroK8S this can be achieved with the metallb addon. + +For more information about how to install Juju, see [Get started with Juju](https://juju.is/docs/olm/get-started-with-juju). + + +## Setting up a Tutorial Model + +To manage resources effectively and to separate this tutorial's workload from +your usual work, we recommend creating a new model using the following command. + +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:juju-add-model] +:end-before: [docs:juju-add-model-end] +:dedent: 2 +``` + +## Deploy the NetBox charm + +Deploy the NetBox charm, with all its mandatory requirements (PostgreSQL, Redis and S3). + +### Deploy the charms: + +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:juju-deploy-netbox] +:end-before: [docs:juju-deploy-netbox-end] +:dedent: 2 +``` + +At this point NetBox should be blocked as there is no S3 integration for +storage, Redis or PostgreSQL. + +Set the allowed hosts. In this example every host is allowed. For a production environment +only the used hosts should be allowed. + +### Deploy the charms: + +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:netbox-config-allowed-hosts] +:end-before: [docs:netbox-config-allowed-hosts-end] +:dedent: 2 +``` + +### Redis + +NetBox requires Redis to work. You can deploy Redis with redis-k8s: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:juju-deploy-redis] +:end-before: [docs:juju-deploy-redis-end] +:dedent: 2 +``` + +Integrate redis-k8s with NetBox with: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:juju-integrate-redis-netbox] +:end-before: [docs:juju-integrate-redis-netbox-end] +:dedent: 2 +``` + +### Deploy PostgreSQL + +NetBox requires PostgreSQL to work. Deploy and integrate with: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:juju-netbox-postgresql] +:end-before: [docs:juju-netbox-postgresql-end] +:dedent: 2 +``` + +### Deploy s3-integrator + +NetBox requires an S3 integration for the uploaded files. This is because +the NetBox charm is designed to work in a high availability (HA) configuration. +This allows uploaded images to be placed on an S3 compatible server instead of +the local filesystem. + +You can configure it with: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:juju-netbox-s3] +:end-before: [docs:juju-netbox-s3-end] +:dedent: 2 +``` + +See the [s3-integrator charmhub page](https://charmhub.io/s3-integrator) for more information. + +### Deploy traefik-k8s + +You need to enable MetalLb if using MicroK8s. See the [traefik-k8s charmhub page](https://charmhub.io/traefik-k8s) for more information. + +With the next example, you can configure Traefik using path mode routing: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:traefik] +:end-before: [docs:traefik-end] +:dedent: 2 +``` + +If the host `netbox_hostname` can be resolved to the correct IP (the load balancer IP), +you should be able to browse NetBox in the url http://netbox_hostname/netbox-tutorial-netbox + +You can check the proxied endpoints with the command: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:traefik-show-endpoints] +:end-at: [docs:traefik-show-endpoints-end] +:dedent: 2 +``` + +## Create superuser +To be able to login to NetBox, you can create a super user with the next command: +```{literalinclude} code/getting-started/task.yaml +:language: bash +:start-after: [docs:netbox-create-superuser] +:end-before: [docs:netbox-create-superuser-end] +:dedent: 2 +``` + +Congratulations, With the username created and the password provided in the response, +you have now full access to your own NetBox! + diff --git a/charm/tests/spread/lib/cloud-config.yaml b/charm/tests/spread/lib/cloud-config.yaml new file mode 100644 index 0000000000..29618c66b6 --- /dev/null +++ b/charm/tests/spread/lib/cloud-config.yaml @@ -0,0 +1,10 @@ +#cloud-config + +ssh_pwauth: true + +users: + - default + - name: spread + plain_text_passwd: spread + lock_passwd: false + sudo: ALL=(ALL) NOPASSWD:ALL diff --git a/charm/tests/spread/lib/test-helpers.sh b/charm/tests/spread/lib/test-helpers.sh new file mode 100644 index 0000000000..90f3ca70f8 --- /dev/null +++ b/charm/tests/spread/lib/test-helpers.sh @@ -0,0 +1,86 @@ + +export PATH=/snap/bin:$PROJECT_PATH/charm/tests/spread/lib/tools:$PATH +export CONTROLLER_NAME="craft-test-$PROVIDER" + + +install_lxd() { + snap install lxd --channel "$LXD_CHANNEL" + snap refresh lxd --channel "$LXD_CHANNEL" + lxd waitready + lxd init --auto + chmod a+wr /var/snap/lxd/common/lxd/unix.socket + lxc network set lxdbr0 ipv6.address none + usermod -a -G lxd "$USER" + + # Work-around clash between docker and lxd on jammy + # https://github.com/docker/for-linux/issues/1034 + iptables -F FORWARD + iptables -P FORWARD ACCEPT +} + + +install_microk8s() { + snap install microk8s --channel "$MICROK8S_CHANNEL" + snap refresh microk8s --channel "$MICROK8S_CHANNEL" + microk8s status --wait-ready + + if [ ! -z "$MICROK8S_ADDONS" ]; then + microk8s enable $MICROK8S_ADDONS + fi + + local version=$(snap list microk8s | grep microk8s | awk '{ print $2 }') + + # workarounds for https://bugs.launchpad.net/juju/+bug/1937282 + retry microk8s kubectl -n kube-system rollout status deployment/coredns + retry microk8s kubectl -n kube-system rollout status deployment/hostpath-provisioner + + retry microk8s kubectl auth can-i create pods +} + + +install_charmcraft() { + snap install charmcraft --classic --channel "$CHARMCRAFT_CHANNEL" + snap refresh charmcraft --classic --channel "$CHARMCRAFT_CHANNEL" +} + + +install_juju() { + snap install juju --classic --channel "$JUJU_CHANNEL" + snap refresh juju --classic --channel "$JUJU_CHANNEL" + mkdir -p "$HOME"/.local/share/juju + snap install juju-crashdump --classic +} + + +bootstrap_juju() { + juju bootstrap --verbose "$PROVIDER" "$CONTROLLER_NAME" \ + $JUJU_BOOTSTRAP_OPTIONS $JUJU_EXTRA_BOOTSTRAP_OPTIONS \ + --bootstrap-constraints=$JUJU_BOOTSTRAP_CONSTRAINTS +} + + +restore_charmcraft() { + snap remove --purge charmcraft +} + + +restore_lxd() { + snap stop lxd + snap remove --purge lxd +} + + +restore_microk8s() { + snap stop microk8s + snap remove --purge microk8s +} + + +restore_juju() { + juju controllers --refresh ||: + juju destroy-controller -v --no-prompt --show-log \ + --destroy-storage --destroy-all-models "$CONTROLLER_NAME" + snap stop juju + snap remove --purge juju + snap remove --purge juju-crashdump +} diff --git a/charm/tests/spread/lib/tools/retry b/charm/tests/spread/lib/tools/retry new file mode 100755 index 0000000000..233ad372a6 --- /dev/null +++ b/charm/tests/spread/lib/tools/retry @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import itertools +import os +import subprocess +import sys +import time + + +def envpair(s: str) -> str: + if not "=" in s: + raise argparse.ArgumentTypeError( + "environment variables expected format is 'KEY=VAL' got '{}'".format(s) + ) + return s + + +def _make_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description=""" +Retry executes COMMAND at most N times, waiting for SECONDS between each +attempt. On failure the exit code from the final attempt is returned. +""" + ) + parser.add_argument( + "-n", + "--attempts", + metavar="N", + type=int, + default=10, + help="number of attempts (default %(default)s)", + ) + parser.add_argument( + "--wait", + metavar="SECONDS", + type=float, + default=5, + help="grace period between attempts (default %(default)ss)", + ) + parser.add_argument( + "--env", + type=envpair, + metavar="KEY=VAL", + action="append", + default=[], + help="environment variable to use with format KEY=VALUE (no default)", + ) + parser.add_argument( + "--maxmins", + metavar="MINUTES", + type=float, + default=0, + help="number of minutes after which to give up (no default, if set attempts is ignored)", + ) + parser.add_argument( + "--expect-rc", + metavar="RETCODE", + type=int, + default=0, + help="the expected return code to consider the command execution successful (default 0)", + ) + parser.add_argument( + "--quiet", + dest="verbose", + action="store_false", + default=True, + help="refrain from printing any output", + ) + parser.add_argument("cmd", metavar="COMMAND", nargs="...", help="command to execute") + return parser + + +def get_env(env: list[str]) -> dict[str, str]: + new_env = os.environ.copy() + maxsplit = 1 # no keyword support for str.split() in py2 + for key, val in [s.split("=", maxsplit) for s in env]: + new_env[key] = val + return new_env + + +def run_cmd( + cmd: list[str], + n: int, + wait: float, + maxmins: float, + verbose: bool, + env: list[str], + expect_rc: bool, +) -> int: + if maxmins != 0: + attempts = itertools.count(1) + t0 = time.time() + after = "{} minutes".format(maxmins) + of_attempts_suffix = "" + else: + attempts = range(1, n + 1) + after = "{} attempts".format(n) + of_attempts_suffix = " of {}".format(n) + retcode = 0 + i = 0 + new_env = get_env(env) + for i in attempts: + retcode = subprocess.call(cmd, env=new_env) + if retcode == expect_rc: + return 0 + if verbose: + print( + f"retry: command {' '.join(cmd)} unexpected code {retcode}", + file=sys.stderr, + ) + if maxmins != 0: + elapsed = (time.time() - t0) / 60 + if elapsed > maxmins: + break + if i < n or maxmins != 0: + if verbose: + print( + f"retry: next attempt in {wait} second(s) (attempt {i}{of_attempts_suffix})", + file=sys.stderr, + ) + time.sleep(wait) + + if verbose and i > 1: + print( + f"retry: command {' '.join(cmd)} keeps failing after {after}", + file=sys.stderr, + ) + return retcode + + +def main() -> None: + parser = _make_parser() + ns = parser.parse_args() + # The command cannot be empty but it is difficult to express in argparse itself. + if len(ns.cmd) == 0: + parser.print_usage() + parser.exit(0) + # Return the last exit code as the exit code of this process. + try: + retcode = run_cmd( + ns.cmd, ns.attempts, ns.wait, ns.maxmins, ns.verbose, ns.env, ns.expect_rc + ) + except OSError as exc: + if ns.verbose: + print( + "retry: cannot execute command {}: {}".format(" ".join(ns.cmd), exc), + file=sys.stderr, + ) + raise SystemExit(1) + else: + raise SystemExit(retcode) + + +if __name__ == "__main__": + main() diff --git a/spread.yaml b/spread.yaml new file mode 100644 index 0000000000..f8c1939788 --- /dev/null +++ b/spread.yaml @@ -0,0 +1,136 @@ +project: netbox-tests + +environment: + PROVIDER: microk8s + CHARMCRAFT_CHANNEL: latest/stable + JUJU_CHANNEL: 3/stable + LXD_CHANNEL: latest/stable + MICROK8S_CHANNEL: 1.28-strict/stable + MICROK8S_ADDONS: hostpath-storage + + JUJU_BOOTSTRAP_OPTIONS: --model-default test-mode=true --model-default automatically-retry-hooks=false --model-default + JUJU_EXTRA_BOOTSTRAP_OPTIONS: "" + JUJU_BOOTSTRAP_CONSTRAINTS: "" + + # important to ensure adhoc and linode/qemu behave the same + SUDO_USER: "" + SUDO_UID: "" + + LANG: "C.UTF-8" + LANGUAGE: "en" + + PROJECT_PATH: /home/spread/proj + CRAFT_TEST_LIB_PATH: /home/spread/proj/charm/tests/spread/lib + +backends: + multipass: + type: adhoc + allocate: | + # Mitigate issues found when launching multiple mutipass instances + # concurrently. See https://github.com/canonical/multipass/issues/3336 + sleep 0.$RANDOM + sleep 0.$RANDOM + sleep 0.$RANDOM + + mkdir -p "$HOME/.spread" + export counter_file="$HOME/.spread/multipass-count" + + # Sequential variable for unique instance names + instance_num=$(flock -x $counter_file bash -c ' + [ -s $counter_file ] || echo 0 > $counter_file + num=$(< $counter_file) + echo $num + echo $(( $num + 1 )) > $counter_file') + + multipass_image=$(echo "${SPREAD_SYSTEM}" | sed -e s/ubuntu-// -e s/-64//) + + system=$(echo "${SPREAD_SYSTEM}" | tr . -) + instance_name="spread-${SPREAD_BACKEND}-${instance_num}-${system}" + + multipass launch -vv --cpus 2 --disk 20G --memory 4G --name "${instance_name}" \ + --cloud-init charm/tests/spread/lib/cloud-config.yaml "${multipass_image}" + + # Get the IP from the instance + ip=$(multipass info --format csv "$instance_name" | tail -1 | cut -d\, -f3) + ADDRESS "$ip" + + discard: | + instance_name=$(multipass list --format csv | grep $SPREAD_SYSTEM_ADDRESS | cut -f1 -d\,) + multipass delete --purge "${instance_name}" + + systems: + - ubuntu-22.04: + username: spread + password: spread + workers: 1 + + + github-ci: + type: adhoc + + allocate: | + echo "Allocating ad-hoc $SPREAD_SYSTEM" + if [ -z "${GITHUB_RUN_ID:-}" ]; then + FATAL "this back-end only works inside GitHub CI" + exit 1 + fi + echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/99-spread-users + ADDRESS localhost:22 + discard: | + echo "Discarding ad-hoc $SPREAD_SYSTEM" + systems: + - ubuntu-22.04: + username: ubuntu + password: ubuntu + workers: 1 + + +path: /home/spread/proj +kill-timeout: 1h + +prepare: | + snap refresh --hold + + if systemctl is-enabled unattended-upgrades.service; then + systemctl stop unattended-upgrades.service + systemctl mask unattended-upgrades.service + fi + +restore: | + apt autoremove -y --purge + rm -Rf "$PROJECT_PATH" + mkdir -p "$PROJECT_PATH" + +suites: + charm/src-docs/tutorial/code/: + summary: tests tutorial from the docs + systems: + - ubuntu-22.04 + + prepare: | + set -e + . "$CRAFT_TEST_LIB_PATH"/test-helpers.sh + DEBIAN_FRONTEND=noninteractive apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get install -y python3-pip jq + snap install docker + + pip3 install tox + + install_lxd + install_juju + install_microk8s + + # we need the ip address for the load balancer + IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') + microk8s enable metallb:$IPADDR-$IPADDR + + bootstrap_juju + + restore: | + set -e + . "$CRAFT_TEST_LIB_PATH"/test-helpers.sh + + # restore_juju + # restore_microk8s + # restore_lxd + diff --git a/tox.ini b/tox.ini index 797ab440ba..c29e249d31 100644 --- a/tox.ini +++ b/tox.ini @@ -38,11 +38,10 @@ setenv = PYTHONPATH = {[vars]charm_path}:{[vars]charm_path}/lib:{[vars]src_path} description = Generate documentation for src deps = - lazydocs - -r{[vars]charm_path}/requirements.txt + -r{[vars]charm_path}/requirements-doc.txt commands = ; cannot run lazydocs directly due to needing to run it on src/* which produces an invocation error in tox - sh generate-src-docs.sh + sphinx-build -M markdown ./src-docs ./build [testenv:lint] description = Check code against coding style standards