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

Add composite action for starting a JIMM environment #1321

Merged
merged 8 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
3 changes: 1 addition & 2 deletions .air.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ tmp_dir = "tmp"

[build]
args_bin = []
bin = "env $(cat /vault/vault.env | xargs) ./tmp/jimm"
cmd = "go build -gcflags='all=-N -l' -buildvcs=false -o ./tmp/jimm ./cmd/jimmsrv"
delay = 1000
exclude_dir = [".vscode", "assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = "env $(cat /vault/vault.env | xargs) dlv exec --accept-multiclient --log --headless --continue --listen :2345 --api-version 2 ./tmp/jimm"
full_bin = "set -a && . /vault/vault.env && . /jimm/test.env && set +a && dlv exec --accept-multiclient --log --headless --continue --listen :2345 --api-version 2 ./tmp/jimm"
kian99 marked this conversation as resolved.
Show resolved Hide resolved
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0s"
Expand Down
28 changes: 28 additions & 0 deletions .github/actions/test-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# test-server
An action to create a JIMM server with real dependencies for integration test purposes.

This action requires Docker to be installed to start JIMM and its related services.

The action performs the following steps:
- Starts JIMM's docker compose test environment.
- Uses https://github.com/charmed-kubernetes/actions-operator action to start a Juju controller and connects it to JIMM.
- Ensures the local Juju CLI is setup to communicate with JIMM authenticating as a test user.

Use the action by adding the following to a Github workflow:

```yaml
integration-test:
runs-on: ubuntu-latest
name: Integration testing with JIMM
steps:
- name: Setup JIMM environment
uses: canonical/[email protected]
with:
jimm-version: "v3.1.7"
juju-channel: "3/stable"
ghcr-pat: ${{ secrets.GHCR_PAT }}
```

Note that it's recommended to pin the action version to the same version as `jimm-version` to ensure the action works as expected for that specific version of JIMM.

For full details on the inputs see `action.yaml`.
89 changes: 89 additions & 0 deletions .github/actions/test-server/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: JIMM Server Setup
description: "Create a JIMM environment"

ale8k marked this conversation as resolved.
Show resolved Hide resolved
inputs:
jimm-version:
description: >
JIMM version tag to use. This will decide the version of JIMM to start e.g. v3.1.7
A special tag of "dev" can be provided to use the current development version of JIMM.
required: true
juju-channel:
description: 'Juju snap channel to pass to charmed-kubernetes/actions-operator'
required: false
ghcr-pat:
description: >
PAT Token that has package:read access to canonical/JIMM
The PAT token can be left empty when building the development version of JIMM.
required: true

output:
url:
description: 'URL where JIMM can be reached.'
value: "https://jimm.localhost"
client-id:
description: 'Test client ID to login to JIMM with a service account.'
value: "test-client-id"
client-secret:
description: 'Test client Secret to login to JIMM with a service account.'
value: "2M2blFbO4GX4zfggQpivQSxwWX1XGgNf"

runs:
using: "composite"
steps:
- name: Login to GitHub Container Registry
if: ${{ inputs.jimm-version != 'dev' }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ inputs.ghcr-pat }}
- name: Start server based on released version
if: ${{ inputs.jimm-version != 'dev' }}
run: make integration-test-env
shell: bash
env:
JIMM_VERSION: ${{ inputs.jimm-version }}
- name: Start server based on development version
if: ${{ inputs.jimm-version == 'dev' }}
run: make dev-env
shell: bash
- name: Initialise LXD
run: |
sudo lxd waitready && \
sudo lxd init --auto && \
sudo chmod a+wr /var/snap/lxd/common/lxd/unix.socket && \
lxc network set lxdbr0 ipv6.address none && \
sudo usermod -a -G lxd $USER
shell: bash
- name: Setup cloud-init script for bootstraping Juju controllers
run: ./local/jimm/setup-controller.sh
shell: bash
env:
SKIP_BOOTSTRAP: true
CLOUDINIT_FILE: "cloudinit.temp.yaml"
kian99 marked this conversation as resolved.
Show resolved Hide resolved
- name: Setup Juju Controller
uses: charmed-kubernetes/actions-operator@main
with:
provider: "lxd"
channel: "5.19/stable"
juju-channel: ${{ inputs.juju-channel }}
bootstrap-options: "--config cloudinit.temp.yaml --config login-token-refresh-url=https://jimm.localhost/.well-known/jwks.json"
- name: Save LXD controller name
id: lxd-controller
run: echo "name=$CONTROLLER_NAME" >> $GITHUB_OUTPUT
kian99 marked this conversation as resolved.
Show resolved Hide resolved
shell: bash
- name: Install jimmctl and yq
run: sudo snap install jimmctl --channel=3/stable && sudo snap install yq
shell: bash
- name: Authenticate Juju CLI
run: chmod -R 666 ~/.local/share/juju/*.yaml && ./local/jimm/setup-cli-auth.sh
shell: bash
# Below is a hardcoded JWT using the same test-secret used in JIMM's docker compose and allows the CLI to authenticate as the [email protected] user.
env:
JWT: ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKSVV6STFOaUo5LmV5SnBjM01pT2lKUGJteHBibVVnU2xkVUlFSjFhV3hrWlhJaUxDSnBZWFFpT2pFM01qUXlNamcyTmpBc0ltVjRjQ0k2TXprMk5EYzFNelEyTUN3aVlYVmtJam9pYW1sdGJTSXNJbk4xWWlJNkltcHBiVzB0ZEdWemRFQmpZVzV2Ym1sallXd3VZMjl0SW4wLkpTWVhXcGF6T0FnX1VFZ2hkbjlOZkVQdWxhWWlJQVdaX3BuSmRDbnJvWEk=
- name: Add LXD Juju controller to JIMM
run: ./local/jimm/add-controller.sh
shell: bash
env:
JIMM_CONTROLLER_NAME: "jimm"
CONTROLLER_NAME: ${{ steps.lxd-controller.outputs.name }}
47 changes: 47 additions & 0 deletions .github/workflows/integration-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Integration Test
ale8k marked this conversation as resolved.
Show resolved Hide resolved

on:
workflow_dispatch:
kian99 marked this conversation as resolved.
Show resolved Hide resolved
inputs:
jimm-version:
description: >
JIMM version tag to use. This will decide the version of JIMM to start e.g. v3.1.7.
View all available versions at https://github.com/canonical/jimm/pkgs/container/jimm.
required: true
pull_request:

jobs:
startjimm:
name: Test JIMM with Juju controller
runs-on: ubuntu-22.04
steps:
- name: Checkout JIMM repo
uses: actions/checkout@v4
- name: Setup Go
if: ${{ github.event_name == 'pull_request' }}
uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
- name: Go vendor to speed up docker build
Copy link
Contributor

Choose a reason for hiding this comment

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

Why would this speed it up? Wouldn't it be exactly the same as referencing our mod cache?

Copy link
Contributor Author

@kian99 kian99 Aug 22, 2024

Choose a reason for hiding this comment

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

It speeds up the build inside the docker container because we mount the working directory into the air container and with the vendor folder present, Go does not need to download all the dependencies.

It would indeed be the same thing as adding a volume mount to the mod cache, I can do that instead if you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

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

But vendoring downloads them just like a build would? This would only make it faster when you're bring the compose up and down right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Vendoring puts them in the repo in ./vendor and they are already present in the Go module cache because of the setup-go action. So this ends up bringing them into the air docker image which then greatly speeds up the build.

if: ${{ github.event_name == 'pull_request' }}
run: go mod vendor
- name: Start JIMM (fixed version)
if: ${{ github.event_name == 'pull_request' }}
uses: ./.github/actions/test-server
with:
jimm-version: dev
juju-channel: "3/stable"
ghcr-pat: ${{ secrets.GITHUB_TOKEN }}
- name: Start JIMM (user provided version)
if: ${{ github.event_name == 'workflow_dispatch' }}
uses: ./.github/actions/test-server
with:
jimm-version: ${{ inputs.jimm-version }}
juju-channel: "3/stable"
ghcr-pat: ${{ secrets.GITHUB_TOKEN }}
- name: Create a model, deploy an application and run juju status
run: |
juju add-model foo && \
juju deploy haproxy && \
sleep 5 && \
juju status
17 changes: 14 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ dev-env-setup: sys-deps certs
@make version/commit.txt && make version/version.txt

dev-env: dev-env-setup
@docker compose --profile dev up --force-recreate
@docker compose --profile dev up -d --force-recreate --wait

dev-env-cleanup:
@docker compose --profile dev down -v --remove-orphans

integration-test-env: dev-env-setup
@JIMM_VERSION=$(JIMM_VERSION) docker compose --profile test up -d --force-recreate --wait

# Reformat all source files.
format:
gofmt -w -l .
Expand Down Expand Up @@ -116,17 +119,25 @@ define check_dep
fi
endef

# Install packages required to develop JIMM and run tests.
# Install packages required to develop JIMM and/or run tests.
APT_BASED := $(shell command -v apt-get >/dev/null; echo $$?)
sys-deps:
ifeq ($(APT_BASED),0)
# golangci-lint is necessary for linting.
@$(call check_dep,golangci-lint,Missing Golangci-lint - install from https://golangci-lint.run/welcome/install/)
# Go acts as the test runner.
@$(call check_dep,go,Missing Go - install from https://go.dev/doc/install or 'sudo snap install go --classic')
# Git is useful to have.
@$(call check_dep,git,Missing Git - install with 'sudo apt install git')
# GCC is required for the compilation process.
@$(call check_dep,gcc,Missing gcc - install with 'sudo apt install build-essential')
# yq is necessary for some scripts that process controller-info yaml files.
@$(call check_dep,yq,Missing yq - install with 'sudo snap install yq')
@$(call check_dep,gcc,Missing microk8s - install latest none-classic from snapstore')
# Microk8s is required if you want to start a Juju controller on Microk8s.
@$(call check_dep,microk8s,Missing microk8s - install with 'sudo snap install microk8s')
# Docker is required to start the test dependencies in containers.
@$(call check_dep,docker,Missing Docker - install from https://docs.docker.com/engine/install/)
# juju-db is required for tests that use Juju's test fixture, requiring MongoDB.
@$(call check_dep,juju-db.mongo,Missing juju-db - install with 'sudo snap install juju-db --channel=4.4/stable')
else
@echo sys-deps runs only on systems with apt-get
Expand Down
82 changes: 41 additions & 41 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ services:
traefik:
image: "traefik:2.9"
container_name: traefik
profiles: ["dev"]
profiles: ["dev", "test"]
ports:
- "80:80"
- "443:443"
Expand All @@ -19,7 +19,46 @@ services:
interval: 10s
timeout: 5s
retries: 3

# An instance of JIMM used in integration tests, pulled from a tag.
jimm-test:
image: ghcr.io/canonical/jimm:${JIMM_VERSION}
profiles: ["test"]
container_name: jimm-test
ports:
- 17070:80
entrypoint:
- bash
- -c
- >-
apt update && apt install curl -y
&& set -a && . /test.env && . /vault/vault.env && set +a && /usr/local/bin/jimmsrv
volumes:
- ./local/vault/vault.env:/vault/vault.env:rw
- ./test.env:/test.env
healthcheck:
test: [ "CMD", "curl", "http://jimm.localhost:80" ]
interval: 5s
timeout: 5s
retries: 5 # Should fail after approximately (interval*retry) seconds
depends_on:
db:
condition: service_healthy
openfga:
condition: service_healthy
traefik:
condition: service_healthy
insert-hardcoded-auth-model:
condition: service_completed_successfully
keycloak:
condition: service_healthy
labels:
traefik.enable: true
traefik.http.routers.jimm.rule: Host(`jimm.localhost`)
traefik.http.routers.jimm.entrypoints: websecure
traefik.http.routers.jimm.tls: true

# An instance of JIMM used for dev, built from source.
jimm:
image: cosmtrek/air:latest
profiles: ["dev"]
Expand All @@ -36,47 +75,8 @@ services:
ports:
- 17070:80
- 2345:2345
environment:
kian99 marked this conversation as resolved.
Show resolved Hide resolved
JIMM_LOG_LEVEL: "debug"
JIMM_UUID: "3217dbc9-8ea9-4381-9e97-01eab0b3f6bb"
JIMM_DSN: "postgresql://jimm:jimm@db/jimm"
# Not needed for local test (yet).
# BAKERY_AGENT_FILE: ""
JIMM_ADMINS: "[email protected]"
# Note: You can comment out the Vault ENV vars below and instead use INSECURE_SECRET_STORAGE to place secrets in Postgres.
VAULT_ADDR: "http://vault:8200"
VAULT_PATH: "/jimm-kv/"
# Note: By default we should use Vault as that is the primary means of secret storage.
# INSECURE_SECRET_STORAGE: "enabled"
# JIMM_DASHBOARD_LOCATION: ""
JIMM_DNS_NAME: "jimm.localhost"
JIMM_LISTEN_ADDR: "0.0.0.0:80"
JIMM_TEST_PGXDSN: "postgresql://jimm:jimm@db/jimm"
JIMM_JWT_EXPIRY: 30s
JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS: "1"
TEST_LOGGING_CONFIG: ""
BAKERY_PUBLIC_KEY: "izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk="
BAKERY_PRIVATE_KEY: "ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc="
OPENFGA_SCHEME: "http"
OPENFGA_HOST: "openfga"
OPENFGA_PORT: 8080
OPENFGA_STORE: "01GP1254CHWJC1MNGVB0WDG1T0"
OPENFGA_AUTH_MODEL: "01GP1EC038KHGB6JJ2XXXXCXKB"
OPENFGA_TOKEN: "jimm"
JIMM_IS_LEADER: true
JIMM_OAUTH_ISSUER_URL: "http://keycloak.localhost:8082/realms/jimm" # Scheme required
JIMM_OAUTH_CLIENT_ID: "jimm-device"
JIMM_OAUTH_CLIENT_SECRET: "SwjDofnbDzJDm9iyfUhEp67FfUFMY8L4"
JIMM_OAUTH_SCOPES: "openid profile email" # Space separated list of scopes
JIMM_DASHBOARD_FINAL_REDIRECT_URL: "https://jaas.ai" # Example URL
JIMM_ACCESS_TOKEN_EXPIRY_DURATION: 1h
JIMM_SECURE_SESSION_COOKIES: false
JIMM_SESSION_COOKIE_MAX_AGE: 86400
JIMM_SESSION_SECRET_KEY: Xz2RkR9g87M75xfoumhEs5OmGziIX8D88Rk5YW8FSvkBPSgeK9t5AS9IvPDJ3NnB
volumes:
- ./:/jimm/
- ./local/vault/approle.json:/vault/approle.json:rw
- ./local/vault/roleid.txt:/vault/roleid.txt:rw
- ./local/vault/vault.env:/vault/vault.env:rw
healthcheck:
test: [ "CMD", "curl", "http://jimm.localhost:80" ]
Expand Down Expand Up @@ -193,7 +193,7 @@ services:
# Adds the auth model and updates its authorisation model id to be the expected hard-coded id such that our local JIMM can utilise it for queries.
# The auth model json is retrieved from file via volume mount.
insert-hardcoded-auth-model:
profiles: ["dev"]
profiles: ["dev", "test"]
image: governmentpaas/psql
container_name: insert-hardcoded-auth-model
volumes:
Expand Down
25 changes: 18 additions & 7 deletions local/jimm/add-controller.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,27 @@ echo "JIMM controller name is: $JIMM_CONTROLLER_NAME"
echo "Target controller name is: $CONTROLLER_NAME"
echo "Target controller path is: $CONTROLLER_YAML_PATH"
echo
echo "Building jimmctl..."
# Build jimmctl so we may add a controller.
go build ./cmd/jimmctl
echo "Built."
echo
which jimmctl
jimmctlAvailable=$?
if [ $jimmctlAvailable -ne 0 ]; then
echo "Building jimmctl..."
# Build jimmctl so we may add a controller.
go build ./cmd/jimmctl
echo "Built jimmctl."
echo
else
echo "jimmctl available, skipping build"
fi
if which jimmctl | grep -q 'snap'; then
CONTROLLER_YAML_PATH="$HOME/snap/jimmctl/common/$CONTROLLER_YAML_PATH"
echo "jimmctl is installed as a snap"
echo "placing controller info file at $CONTROLLER_YAML_PATH"
fi
echo "Switching juju controller to $JIMM_CONTROLLER_NAME"
juju switch "$JIMM_CONTROLLER_NAME"
echo
echo "Retrieving controller info for $CONTROLLER_NAME"
./jimmctl controller-info --local "$CONTROLLER_NAME" "$CONTROLLER_YAML_PATH" --tls-hostname juju-apiserver
jimmctl controller-info --local "$CONTROLLER_NAME" "$CONTROLLER_YAML_PATH" --tls-hostname juju-apiserver
if [[ -f "$CONTROLLER_YAML_PATH" ]]; then
echo "Controller info retrieved."
else
Expand All @@ -38,7 +49,7 @@ else
fi
echo
echo "Adding controller from path: $CONTROLLER_YAML_PATH"
./jimmctl add-controller "$CONTROLLER_YAML_PATH"
jimmctl add-controller "$CONTROLLER_YAML_PATH"
echo
echo "Updating cloud credentials for: $JIMM_CONTROLLER_NAME, from client credential: $CLIENT_CREDENTIAL_NAME"
juju update-credentials "$CLIENT_CREDENTIAL_NAME" --controller "$JIMM_CONTROLLER_NAME"
10 changes: 10 additions & 0 deletions local/jimm/setup-cli-auth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# This script is used to setup a Juju CLI to be authenticated with JIMM without going through login.
# This is particularly useful in headless environments like CI/CD.

set -eux

# Note that we are working around the fact that yq is a snap and doesn't have permission to hidden folders due to snap confinement.
cat ~/.local/share/juju/accounts.yaml | yq '.controllers += {"jimm":{"type": "oauth2-device", "user": "[email protected]", "access-token": strenv(JWT)}}' | cat > temp-accounts.yaml && mv temp-accounts.yaml ~/.local/share/juju/accounts.yaml
cat ~/.local/share/juju/controllers.yaml | yq '.controllers += {"jimm":{"uuid": "3217dbc9-8ea9-4381-9e97-01eab0b3f6bb", "api-endpoints": ["jimm.localhost:443"]}}' | cat > temp-controllers.yaml && mv temp-controllers.yaml ~/.local/share/juju/controllers.yaml
Loading
Loading