Skip to content

Commit

Permalink
Introduce supervisor deploy (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
tschaefer authored Jan 5, 2025
1 parent 30f0f70 commit 37d77ef
Show file tree
Hide file tree
Showing 23 changed files with 892 additions and 72 deletions.
5 changes: 4 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ Style/Documentation:
Metrics/AbcSize:
CountRepeatedAttributes: false

Metrics/BlockLength:
Max: 30

Metrics/MethodLength:
CountAsOne:
- 'array'
- 'hash'
- 'heredoc'
Max: 25
Max: 30

RSpec/ExampleLength:
CountAsOne:
Expand Down
2 changes: 2 additions & 0 deletions .yamllint
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ extends: default
rules:
indentation:
indent-sequences: consistent
line-length:
max: 120

# vim:ft=yaml
162 changes: 159 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,14 @@ actions.

### Configuration

Before using the CLI, configure the Supervisor base URL and API key by
Before using the CLI, configure the Supervisor base URI and API token by
creating a configuration file at `~/.supervisor`:

```yaml
---
base_url: https://supervisor.example.com
api_key: 8db7fde4-6a11-462e-ba27-6897b7c9281b
api:
uri: https://supervisor.example.com
token: 8db7fde4-6a11-462e-ba27-6897b7c9281b
```
### Command Reference
Expand All @@ -96,6 +97,161 @@ supervisor is-healthy

Checks the health of the Supervisor service.

### Deployment Management

The command `deploy` installs and sets up a containerized Supervisor service
on a vanilla Linux machine by provisioning the docker service and
deploying the application proxy [Traefik](https://traefik.io/).

#### Default Traefik docker command

```bash
docker run \
--detach --restart always --name traefik \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /var/lib/traefik:/etc/traefik \
--network supervisor \
--publish 80:80 --publish 443:443 \
traefik:v3.2.1 \
--providers.docker.exposedbydefault="false" \
--entrypoints.web.address=":80" \
--entrypoints.websecure.address=":443" \
--certificatesresolvers.letsencrypt.acme.email="[email protected]" \
--certificatesresolvers.letsencrypt.acme.storage="/etc/traefik/certs.d/acme.json" \
--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint="web"
```

#### Default Supervisor docker command

```bash
docker run \
--detach --restart always --name supervisor \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /var/lib/supervisor:/rails/storage \
--network supervisor \
--label traefik.enable="true" \
--label traefik.http.routers.supervisor.tls="true" \
--label traefik.http.routers.supervisor.tls.certresolver="letsencrypt" \
--label traefik.http.routers.supervisor.rule="Host(\"supervisor.example.com\")" \
--label traefik.http.routers.supervisor.entrypoints="websecure" \
--env SECRET_KEY_BASE="601f72235d8ea11db69e678f9...1a" \
--env SUPERVISOR_API_KEY="8db7fde4-6a11-462e-ba27-6897b7c9281b" \
ghcr.io/tschaefer/supervisor:main
```

#### Default docker network command

```bash
docker network create \
--attachable true \
--ipv6=true \
--driver=bridge \
--opt com.docker.network.container_iface_prefix=supervisor
supervisor
```

Prerequisites are super-user privileges, a valid DNS record for the
Supervisor service and the above mentioned configuration file.

While setup the necessary certificate is requested from
[Let's Encrypt](https://letsencrypt.org/) via HTTP-challenge.


```bash
supervisor deploy --host [email protected]
```

The provisioning of docker can be skipped wit the option `--skip-docker` as
well as the installation of Traefik with the option `--skip-traefik`. For a
more informative output use `--verbose` - beware, sensible information will be
exposed.

The deployment is customizable by configuration in the root under `deploy`.

```yaml
deploy:

# Network settings
network:

# The name of the network to create, defaults to supervisor
name: supervisor
# Additional options to pass to the network create command
options:
ipv6: false
opt: com.docker.network.driver.mtu=1500

# Traefik settings
traefik:

# The Traefik image to use, defaults to traefik:v3.2.1
image: traefik:v3.2.0

# Additional arguments to pass to the Traefik container
args:
configfile: /etc/traefik/traefik.yml

# Additional environment variables to pass to the Traefik container
env:
CF_API_EMAIL: [email protected]
CF_DNS_API_TOKEN: YSsfAH-d1q57j2D7T41ptAfM

# Supervisor settings
supervisor:

# The Supervisor image to use, defaults to ghcr.io/tschaefer/supervisor:main
image: ghcr.io/tschaefer/supervisor:latest

# Additional labels to apply to the Supervisor container
labels:
traefik.http.routers.supervisor.tls.certresolver: cloudflare

# Additional environment variables to pass to the Supervisor container
env: {}
```
Custom `hooks` scripts can be run before and after certain deployment steps.

* `post-docker-setup`
* `pre-traefik-deploy`
* `post-traefik-deploy`
* `pre-supervisor-deploy`
* `post-supervisor-deploy`

**Example**:

```bash
#!/usr/bin/env sh
# pre-traefik-deploy hook script
cat <<EOF> /var/lib/traefik/traefik.yml
---
certificatesresolvers:
cloudflare:
acme:
email: [email protected]
storage: /etc/traefik/certs.d/cloudflare.json
dnschallenge:
provider: cloudflare
EOF
```

The hook filename must be the hook name without any extension. The path to the
hooks directory can be configured in the root under `hooks`.

```yaml
hooks: /path/to/hooks
```

The Supervisor service can be redeployed with the command `redeploy`.

```bash
supervisor redeploy --host machine.example.com
```

Optionally, Traefik can be redeployed with the option `--with-traefik`.

### Stack Management

The `stacks` commands provide a variety of operations for managing stacks.
Expand Down
140 changes: 129 additions & 11 deletions etc/bash/completion
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ __supervisor_stacks_completion() {
done

local cmd
cmd="supervisor ${configuration_file} list --json"
cmd="supervisor ${configuration_file} stacks list --json"

local stacks
stacks=$(eval ${cmd} | jq -r '.[].uuid')
Expand Down Expand Up @@ -165,7 +165,7 @@ _supervisor_stack_control() {
_get_comp_words_by_ref -n : cur prev words

local options='--help --command'
local actions='start stop restart'
local actions='start stop restart redeploy'

case "$prev" in
--command)
Expand Down Expand Up @@ -223,23 +223,91 @@ _supervisor_health() {
fi
}

_supervisor() {
_supervisor_deploy() {
local cur prev
_get_comp_words_by_ref -n : cur prev

local options='--help'
local options_deploy='--host --skip-docker --skip-traefik --verbose'

options="${options} ${options_deploy}"

case "$prev" in
--help)
return
;;
--host)
_known_hosts
return
;;
esac

if [[ "$cur" == -* ]]; then
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
return
fi
}

_supervisor_redeploy() {
local cur prev
_get_comp_words_by_ref -n : cur prev

local options='--help'
local options_deploy='--host --verbose --with-traefik'

options="${options} ${options_deploy}"

case "$prev" in
--help)
return
;;
--host)
_known_hosts
return
;;
esac

if [[ "$cur" == -* ]]; then
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
return
fi
}

_supervisor_dashboard() {
local cur prev
_get_comp_words_by_ref -n : cur prev

local options='--help'
local options_dashboard='--open'

options="${options} ${options_dashboard}"

case "$prev" in
--help)
return
;;
--open)
return
;;
esac

if [[ "$cur" == -* ]]; then
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
return
fi
}


_supervisor_stacks() {
local cur prev words
_get_comp_words_by_ref -n : cur prev words

local actions="is-healthy create delete list show stats update control log"
local actions="create delete list show stats update control log"
local options='--help --man --version'
local options_config='--configuration-file'

options="${options} ${options_config}"

local word
for word in "${words[@]}"; do
case $word in
is-healthy)
_supervisor_health
return
;;
delete)
_supervisor_stack_delete
return
Expand Down Expand Up @@ -275,6 +343,56 @@ _supervisor() {
esac
done

case "$prev" in
--help|--man|--version)
return
;;
esac

if [[ "$cur" == -* ]]; then
mapfile -t COMPREPLY < <(compgen -W "${options}" -- "$cur")
return
fi

mapfile -t COMPREPLY < <(compgen -W "${actions}" -- "$cur")
}

_supervisor() {
local cur prev words
_get_comp_words_by_ref -n : cur prev words

local actions="dashboard redeploy deploy is-healthy stacks"
local options='--help --man --version'
local options_config='--configuration-file'

options="${options} ${options_config}"

local word
for word in "${words[@]}"; do
case $word in
dashboard)
_supervisor_dashboard
return
;;
deploy)
_supervisor_deploy
return
;;
redeploy)
_supervisor_redeploy
return
;;
is-healthy)
_supervisor_health
return
;;
stacks)
_supervisor_stacks
return
;;
esac
done

case "$prev" in
--help|--man|--version)
return
Expand Down
Loading

0 comments on commit 37d77ef

Please sign in to comment.