Introduce new Apache and NGINX mode. Enhance docs.

This commit is contained in:
sn0w 2018-06-04 00:11:12 +02:00
parent 645a59caf7
commit 93e69389a6
6 changed files with 408 additions and 194 deletions

101
.env.dist
View File

@ -1,53 +1,98 @@
### Docker settings ###
#########################
# Script settings #
#########################
# Create a postgresql container?
SCRIPT_DEPLOY_POSTGRES=true
# Specify the server that is used as a reverse-proxy
SCRIPT_USE_PROXY=traefik
# Enable internal SSL support?
SCRIPT_ENABLE_SSL=false
# The port to serve HTTP on when running in nginx/apache-mode
SCRIPT_PORT_HTTP=80
# The port to serve HTTPs on when running in nginx/apache-mode
SCRIPT_PORT_HTTPS=443
# The ip to bind to in nginx/apache-mode
SCRIPT_BIND_IP=0.0.0.0
#########################
# Docker settings #
#########################
# The docker network to bind to. # The docker network to bind to.
# (Useful for reverse-proxies outside of this compose). # In traefik-mode this should be the same network that your
# (Defaults to "pleroma_docker_1") # traefik-container is connected to or a network that is interconnected
DOCKER_NETWORK= # with traefik's network. In manual, apache or nginx mode this value may be empty or set to any other alphanumeric value.
# (Defaults to something like "pleroma_docker_1" if empty)
DOCKER_NETWORK=pleroma
# The directory where all containers store their data. # The directory where all containers store their data.
# Can be a relative path, "~/...", or absolute. # Can be a relative path, "~/...", or absolute.
DOCKER_DATADIR= # Named docker volumes are currently not supported.
DOCKER_DATADIR=./data
### Database settings ### ###########################
POSTGRES_DB= # Database settings #
POSTGRES_USER= ###########################
POSTGRES_PASSWORD=
### Pleroma Settings ### # Leave POSTGRES_IP empty unless you plan to install your own database
# Leave the POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD as-is
# unless you use your own database.
# The env to use POSTGRES_IP=
MIX_ENV= POSTGRES_DB=pleroma
POSTGRES_USER=pleroma
POSTGRES_PASSWORD=pleroma
PLEROMA_DB_POOL_SIZE=10
# The git tag or branch to check out on build ##########################
PLEROMA_VERSION= # Pleroma Settings #
##########################
# The domain/scheme/port where pleroma will be hosted # The environment to use (dev/prod/test)
PLEROMA_URL= MIX_ENV=prod
PLEROMA_SCHEME=
PLEROMA_PORT= # The git tag, revision, or branch to check out on build
PLEROMA_VERSION=develop
# The domain/scheme where pleroma will be hosted
# URL is a bare TLD
# SCHEME is the protocol without "://"
PLEROMA_URL=example.com
PLEROMA_SCHEME=https
# The seed for your secret keys # The seed for your secret keys
# (Enter something as random as possible) # (Enter something as random as possible)
# (On linux you can try something like "dd if=/dev/urandom bs=1 count=128 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev")
PLEROMA_SECRET_KEY_BASE= PLEROMA_SECRET_KEY_BASE=
# The name of your instance # The name of your instance
# (This is displayed in the top-left in pleroma-fe)
PLEROMA_NAME= PLEROMA_NAME=
# Your contact info # Your contact info
PLEROMA_ADMIN_EMAIL= PLEROMA_ADMIN_EMAIL=
# User settings # How many chars a notice may have at max.
PLEROMA_MAX_TOOT_CHARS= PLEROMA_MAX_NOTICE_CHARS=500
PLEROMA_REGISTRATIONS_OPEN=
# Media proxy # Whether your instance accepts new users or not (true/false)
PLEROMA_MEDIA_PROXY_ENABLED= PLEROMA_REGISTRATIONS_OPEN=true
PLEROMA_MEDIA_PROXY_REDIRECT_ON_FAILURE=
PLEROMA_MEDIA_PROXY_URL=
# DB # Enable media proxy?
PLEROMA_DB_POOL_SIZE= PLEROMA_MEDIA_PROXY_ENABLED=false
# The url of your media proxy (if enabled) [with "http(s)://"]
PLEROMA_MEDIA_PROXY_URL=https://cdn.example.com
# Redirect to source on cache fail?
PLEROMA_MEDIA_PROXY_REDIRECT_ON_FAILURE=true
# Whether to enable the chat feature or not # Whether to enable the chat feature or not
PLEROMA_CHAT_ENABLED= PLEROMA_CHAT_ENABLED=true

181
README.md
View File

@ -1,64 +1,179 @@
# Pleroma-Docker # Pleroma-Docker (Unofficial)
[Pleroma](https://pleroma.social/) is a selfhosted social network that uses OStatus/ActivityPub. [Pleroma](https://pleroma.social/) is a selfhosted social network that uses OStatus/ActivityPub.
This repository is my attempt to dockerize it for easier deployment. This repository dockerizes it for easier deployment.
<hr>
```cpp
#include <public_domain.h>
#include <std_disclaimer.h>
/*
* This repository comes with ABSOLUTELY NO WARRANTY
*
* I am not responsible for burning servers, angry users, fedi drama,
* thermonuclear war, or you getting fired because your boss saw your
* NSFW posts. Please do some research if you have any concerns about included
* features or the software used by this script before using it.
* You are choosing to use this setup, and if you point the finger at me for
* messing up your instance, I will laugh at you.
*/
```
<hr>
## Features ## Features
- 100% generic - 100% generic
- Everything is customizable - Everything is customizable
- Everything is configurable through `.env` files
- Zero special host dependencies - Zero special host dependencies
- "It just works" - "It just works"
## Assumptions ## Alternatives
This repo assumes that you're using [Træfik](https://traefik.io/) as your auto-configuring reverse proxy. If this setup is a bit overwhelming there are a lot of other great dockerfiles
or guides from the community. A few are linked below. This list is not exhaustive and not ordered.
## Tutorial - [Angristan/dockerfiles/pleroma](https://github.com/Angristan/dockerfiles/tree/master/pleroma)
- [RX14/iscute.moe](https://github.com/RX14/kurisu.rx14.co.uk/blob/master/services/iscute.moe/pleroma/Dockerfile)
- [rysiek/docker-pleroma](https://git.pleroma.social/rysiek/docker-pleroma)
- Make sure that `m4` and `docker-compose` are installed ## Installing Pleroma
- Check out this repo
- Create your env file (`cp .env.dist .env`) - Clone this repository
- Edit the env values - Copy `.env.dist` to `.env`
- Run `./pleroma build` - Edit `.env` (see "Configuring Pleroma" section below)
- Run `./pleroma run` - Run `./pleroma build` and `./pleroma start`
- ...
- Profit! - Profit!
## Building an image ## Updating Pleroma
Since this setup injects code into pleroma that moves it's configuration into the environment (ref ["The Twelve-Factor App"](https://12factor.net/)), the image is 100% reusable and can be shared/replicated across multiple hosts. To do that just run `./pleroma build` as usual and then tag your image to whatever you want. Just make sure to start the container with `env_file:` or all required `-e` pairs. Just run `./pleroma build` again and `./pleroma start` afterwards.
You don't need to shutdown pleroma while compiling the new release.
Every time you run `./pleroma build` the script will fetch all upstream changes and checkout `PLEROMA_VERSION`.
This means that setting `PLEROMA_VERSION` to a branch enables rolling-release updates while setting
it to a tag or commit-hash pins the version.
## Customizing Pleroma ## Customizing Pleroma
Just add your customizations (and their folder structure) to `custom.d`.<br> Just add your customizations (and their folder structure) to `custom.d`.<br>
They will be copied into the right place when the container starts.<br> They will be copied (*not* mounted) into the right place when the container starts.<br>
You can even replace/patch pleroma's code with this, because the project is recompiled at startup. You can even replace/patch pleroma's code with this, because the project is recompiled at startup.<br>
In general: Prepending `custom.d` to pleroma's customization guides should work all the time.<br> In general: Prepending `custom.d/` to pleroma's customization guides should work all the time.<br>
Check them out in the [official pleroma wiki](https://git.pleroma.social/pleroma/pleroma/wikis/home). Check them out in the [official pleroma wiki](https://git.pleroma.social/pleroma/pleroma/wikis/home).
Here are a few customization examples: For example: A custom thumbnail now goes into `custom.d/priv/static/instance/thumbnail.jpeg` instead of `priv/static/instance/thumbnail.jpeg`.
- I want to have a custom thumbnail ## Configuring Pleroma
- Save it in `custom.d/priv/static/instance/thumbnail.jpeg`
- I want to change the `config.json`. pleroma-docker tries to stay out of your way as much as possible while providing
- Just modify [the template](https://git.pleroma.social/pleroma/pleroma/blob/develop/priv/static/static/config.json) and save it in `custom.d/priv/static/static/config.json` a good experience for both you and your users. It thus supports multiple
"operation modes" and quite some config variables which you can mix and match.
- I want to change the background This guide will explain some of the tricky `.env` file parts as detailed as possible (but you should still read the comments in there).
- Throw an image into `custom.d/priv/static/static` and then edit the config from above
- I want a custom logo #### Storing Data
- See above
- I need blobs. Give me emojis. Currently all data is stored in subfolders of `DOCKER_DATADIR` which will be bind-mounted into the container by docker.
- Save them in `custom.d/priv/static/emoji`. Then create and/or edit `custom.d/config/custom_emoji.txt`.
- I want custom ToS We'll evaluate named volumes as an option in the future but they're currently not supported.
- Throw a HTML document to `custom.d/priv/static/static/terms-of-service.html`
You get the gist.<br> #### Database (`SCRIPT_DEPLOY_POSTGRES`)
Pretty basic stuff.
Values: `true` / `false`
By default pleroma-docker deploys a postgresql container and links it to pleroma's container as a zero-config data store. If you already have a postgres database or want to host postgres on a physically different machine set this value to `false`. Make sure to set the `POSTGRES_*` variables when doing that.
#### Reverse Proxy (`SCRIPT_USE_PROXY`)
Values: `traefik` / `nginx` / `manual`
Pleroma is usually run behind a reverse-proxy.
Pleroma-docker gives you multiple options here.
##### Traefik
In traefik-mode we will generate a pleroma container with traefik labels.
These will be picked up at runtime to dynamically create a reverse-proxy
configuration. This should 'just work' if `watch=true` and `exposedByDefault=false` are set in the `[docker]` section of your `traefik.conf`. SSL will also 'just work' once you add a matching `[[acme.domains]]` entry.
##### NGINX
In nginx-mode we will generate a bare nginx container that is linked to the
pleroma container. The nginx container is absolutely unmodified and expects to
be configured by you. The nginx file in [Pleroma's Repository](https://git.pleroma.social/pleroma/pleroma/blob/develop/installation/pleroma.nginx) is a good starting point.
We will mount your configs like this:
```
custom.d/server.nginx -> /etc/nginx/nginx.conf
custom.d/vhost.nginx -> /etc/nginx/conf.d/pleroma.conf
```
To reach your pleroma container from inside nginx use `proxy_pass http://pleroma:4000;`.
Set `SCRIPT_PORT_HTTP` and `SCRIPT_PORT_HTTPS` to the ports you want to listen on.
Specify the ip to bind to in `SCRIPT_BIND_IP`. These values are required.
The container only listens on `SCRIPT_PORT_HTTPS` if `SCRIPT_ENABLE_SSL` is `true`.
##### Apache / httpd
Just like nginx-mode this starts an unmodified apache server that expects to be
configured by you. Again [Pleroma's Config](https://git.pleroma.social/pleroma/pleroma/blob/develop/installation/pleroma-apache.conf) is a good starting point.
We will mount your configs like this:
```
custom.d/server.httpd -> /usr/local/apache2/conf/httpd.conf
custom.d/vhost.httpd -> /usr/local/apache2/conf/extra/httpd-vhosts.conf
```
To reach your pleroma container from inside apache use `ProxyPass [loc] http://localhost:4000/`.
Again setting `SCRIPT_PORT_HTTP`, `SCRIPT_PORT_HTTPS` and `SCRIPT_BIND_IP` is required.
The container only listens on `SCRIPT_PORT_HTTPS` if `SCRIPT_ENABLE_SSL` is `true`.
##### Manual
In manual mode we do not create any reverse proxy for you.
You'll have to figure something out on your own.
This mode also doesn't bind to any IP or port.
You'll have to forward something to the container's IP.
#### SSL (`SCRIPT_ENABLE_SSL`)
Values: `true` / `false`
If you want to use SSL with your Apache or NGINX containers you'll need a
certificate. Certificates need to be placed into `custom.d` and will be
bind-mounted into the server's container at runtime.
We will mount your certs like this:
```
custom.d/ssl.crt -> /ssl/ssl.crt
custom.d/ssl.key -> /ssl/ssl.key
```
You can reference them in Apache like this:
```apache
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile "/ssl/ssl.crt"
SSLCertificateKeyFile "/ssl/ssl.key"
</VirtualHost>
```
And in NGINX like this:
```nginx
listen 443 ssl;
ssl_certificate /ssl/ssl.crt;
ssl_certificate_key /ssl/ssl.key;
```
In traefik-mode and manual-mode these files and the `SCRIPT_LOCAL_SSL` value are ignored.

View File

@ -1,72 +1,136 @@
divert(-1) changequote(`<', `>')
define(`upcase', `translit($1, `a-z', `A-Z')')
define(`env', `upcase($1): ${upcase($1):?upcase($1)}')
define(`env_fb', `upcase($1): ${upcase($1):-$2}')
define(`env_inline', `${upcase($1):?upcase($1)}')
define(`env_inline_fb', `${upcase($1):-$2}')
divert(1)dnl
version: "3" define(<upcase>, <translit($1, <a-z>, <A-Z>)>)
define(<env>, <upcase($1)=${upcase($1):?upcase($1)}>)
define(<env_fb>, <upcase($1)=${upcase($1):-$2}>)
define(<env_inline>, <${upcase($1):?upcase($1)}>)
define(<env_inline_fb>, <${upcase($1):-$2}>)
networks: {
default: "version": "3",
external:
name: env_inline_fb(`docker_network', `pleroma_docker_1')
services: ifdef(__DOCKER_NETWORK, <
db: "networks": {
image: postgres:10.3-alpine "default": {
restart: unless-stopped "external": {
environment: "name": "DOCKER_NETWORK"
env(`postgres_db') }
env(`postgres_user') }
env(`postgres_password') },
volumes: >)
- env_inline(`docker_datadir')/db:/var/lib/postgresql/data
- ./initdb.sql:/docker-entrypoint-initdb.d/pleroma.sql
server: "services": {
build: ifelse(__SCRIPT_DEPLOY_POSTGRES, true, <
context: . "db": {
dockerfile: ./pleroma.dockerfile "image": "postgres:10.3-alpine",
args: "restart": "unless-stopped",
env(`pleroma_version') "environment": [
restart: unless-stopped "env(<postgres_db>)",
links: "env(<postgres_user>)",
- db "env(<postgres_password>)"
environment: ],
env_fb(`mix_env', `prod') "volumes": [
"env_inline(<docker_datadir>)/db:/var/lib/postgresql/data",
"./initdb.sql:/docker-entrypoint-initdb.d/pleroma.sql"
]
},
>)
env_fb(`postgres_ip', `db') ifdef(<__SCRIPT_USE_PROXY>, <
env(`postgres_db') ifelse(
env(`postgres_user') __SCRIPT_USE_PROXY, traefik, <>,
env(`postgres_password') __SCRIPT_USE_PROXY, manual, <>,
__SCRIPT_USE_PROXY, nginx, <
"proxy": {
"image": "nginx:alpine",
"ports": [
"__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTP:__SCRIPT_PORT_HTTP"ifdef(__SCRIPT_ENABLE_SSL, <,>)
ifdef(__SCRIPT_ENABLE_SSL, <"__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTPS:__SCRIPT_PORT_HTTPS">)
],
"links": [
"server:pleroma"
],
"volumes": [
"./custom.d/server.nginx:/etc/nginx/nginx.conf:ro",
"./custom.d/vhost.nginx:/etc/nginx/conf.d/pleroma.conf:ro"ifdef(__SCRIPT_ENABLE_SSL, <,>)
ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.crt:/ssl/ssl.crt:ro",>)
ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.key:/ssl/ssl.key:ro">)
]
},
>, __SCRIPT_USE_PROXY, apache, <
"proxy": {
"image": "amd64/apache:alpine",
"ports": [
"__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTP:__SCRIPT_PORT_HTTP"ifdef(__SCRIPT_ENABLE_SSL, <,>)
ifdef(__SCRIPT_ENABLE_SSL, <"__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTPS:__SCRIPT_PORT_HTTPS">)
],
"links": [
"server:pleroma"
],
"volumes": [
"./custom.d/server.httpd:/usr/local/apache2/conf/httpd.conf:ro",
"./custom.d/vhost.httpd:/usr/local/apache2/conf/extra/httpd-vhosts.conf:ro"ifdef(__SCRIPT_ENABLE_SSL, <,>)
ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.crt:/ssl/ssl.crt:ro",>)
ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.key:/ssl/ssl.key:ro">)
]
},
>, <
errprint(Invalid option __SCRIPT_USE_PROXY for <SCRIPT_USE_PROXY>)
m4exit(<1>)
>
)
>)
env(`pleroma_url') "server": {
env(`pleroma_scheme') "build": {
env(`pleroma_port') "context": ".",
env(`pleroma_secret_key_base') "args": [
env(`pleroma_name') "env(<pleroma_version>)"
env(`pleroma_admin_email') ]
env(`pleroma_max_toot_chars') },
env(`pleroma_registrations_open') "restart": "unless-stopped",
env(`pleroma_media_proxy_enabled') "links": [
env(`pleroma_media_proxy_redirect_on_failure') ifelse(__SCRIPT_DEPLOY_POSTGRES, true, <"db">)
env(`pleroma_media_proxy_url') ],
env(`pleroma_db_pool_size') "environment": [
env_fb(`pleroma_uploads_path', `/uploads') "env_fb(<mix_env>, <prod>)",
env(`pleroma_chat_enabled')
volumes:
- ./custom.d:/custom.d
- env_inline(`docker_datadir')/uploads:env_inline_fb(`pleroma_uploads_path', `/uploads')
labels:
traefik.enable: "true"
traefik.fe.port: "4000"
traefik.fe.protocol: "http"
traefik.fe.entryPoints: "http,https"
traefik.fe.frontend.rule: "Host:env_inline(`pleroma_url')"
traefik.cache.port: "4000"
traefik.cache.protocol: "http"
traefik.cache.entryPoints: "http,https"
traefik.cache.frontend.rule: "Host:env_inline(`pleroma_media_proxy_url')"
"env_fb(<postgres_ip>, <db>)",
"env(<postgres_db>)",
"env(<postgres_user>)",
"env(<postgres_password>)",
"env(<pleroma_url>)",
"env(<pleroma_scheme>)",
"env(<pleroma_secret_key_base>)",
"env(<pleroma_name>)",
"env(<pleroma_admin_email>)",
"env(<pleroma_max_notice_chars>)",
"env(<pleroma_registrations_open>)",
"env(<pleroma_media_proxy_enabled>)",
"env(<pleroma_media_proxy_redirect_on_failure>)",
"env(<pleroma_media_proxy_url>)",
"env(<pleroma_db_pool_size>)",
"env(<pleroma_chat_enabled>)",
"env_fb(<pleroma_uploads_path>, </uploads>)"
],
"volumes": [
"./custom.d:/custom.d",
"env_inline(<docker_datadir>)/uploads:env_inline_fb(<pleroma_uploads_path>, </uploads>)"
],
"labels": [
ifelse(SCRIPT_USE_PROXY, traefik, <
"traefik.enable=true",
"traefik.fe.port=4000",
"traefik.fe.protocol=http",
"traefik.fe.entryPoints=http,https",
"traefik.fe.frontend.rule=Host:env_inline(<pleroma_url>)",
"traefik.cache.port=4000",
"traefik.cache.protocol=http",
"traefik.cache.entryPoints=http,https",
"traefik.cache.frontend.rule=Host:env_inline(<pleroma_media_proxy_url>)"
>)
]
}
}
}

View File

@ -18,7 +18,7 @@ config :pleroma, Pleroma.Web.Endpoint,
url: [ url: [
host: Docker.env(:url), host: Docker.env(:url),
scheme: Docker.env(:scheme), scheme: Docker.env(:scheme),
port: Docker.env(:port) port: 4000
], ],
secret_key_base: Docker.env(:secret_key_base) secret_key_base: Docker.env(:secret_key_base)
@ -31,7 +31,7 @@ config :pleroma, :chat,
config :pleroma, :instance, config :pleroma, :instance,
name: Docker.env(:name), name: Docker.env(:name),
email: Docker.env(:admin_email), email: Docker.env(:admin_email),
limit: Docker.env(:max_toot_chars), limit: Docker.env(:max_notice_chars),
registrations_open: Docker.env(:registrations_open) registrations_open: Docker.env(:registrations_open)
config :pleroma, :media_proxy, config :pleroma, :media_proxy,

116
pleroma
View File

@ -1,95 +1,85 @@
#!/bin/bash #!/bin/bash
set -e set -e
set -x
function log_generic { # $1: color, $2: prefix, $3: message # flags=""
echo -e "[$(tput setaf $1)$(tput bold)$2$(tput sgr0)] $3"
}
function log_error { # $1: message # print_help() {
log_generic 1 ERR "$1"
}
function log_ok { # $1: message #
log_generic 2 "OK " "$1"
}
function log_info { # $1: message #
log_generic 4 INF "$1"
}
function print_help {
echo " echo "
Pleroma Maintenance Script Pleroma Maintenance Script
Usage: Usage:
$0 [action] [flags] $0 [action]
Actions: Actions:
start Start pleroma and sibling services build Rebuild the pleroma container
stop Stop pleroma and sibling services
purge Remove the pleroma container and all build caches
start / up Start pleroma and sibling services
stop / down Stop pleroma and sibling services
restart Executes #stop and #start respectively. restart Executes #stop and #start respectively.
status / ps Show the current container status
logs Show the current container logs logs Show the current container logs
enter Enter the pleroma container for debugging/maintenance enter Enter the pleroma container for debugging/maintenance
" "
} }
function action__start { render_template() {
log_info "Booting pleroma" m4 $flags docker-compose.m4 | awk 'NF'
docker-compose up --remove-orphans -d
log_ok "Done"
} }
function action__stop { docker_compose() {
log_info "Stopping pleroma" render_template | docker-compose -f - "$@"
docker-compose down
log_ok "Done"
} }
function action__logs { load_env() {
docker-compose logs -f if [[ ! -f .env ]]; then
echo "Please create a .env file first"
echo "(Copy .env.dist to .env for a template)"
exit 1
fi
while read -r line; do
if [[ "$line" == \#* ]] || [[ -z "$line" ]]; then
continue;
fi
export "${line?}"
flags="-D__${line?} $flags"
done < .env
} }
function action__build { action__start() { docker_compose up --remove-orphans -d; }
docker-compose pull action__up() { action__start; }
docker-compose build --build-arg __BUST_CACHE="$(date)" server action__stop() { docker_compose down; }
} action__down() { action__stop; }
action__restart() { action__stop; action__start; }
function action__enter { action__logs() { docker_compose logs -f; }
docker-compose exec server ash action__build() { docker_compose build --build-arg __BUST_CACHE="$(date +%s)" server; }
} action__enter() { docker_compose exec server ash; }
action__status() { docker_compose ps; }
function prepare { action__ps() { action__status; }
log_info "Preparing script" action__debug() { render_template; }
m4 docker-compose.m4 > docker-compose.yml action__lint() { render_template | jq; }
}
function cleanup {
log_info "Cleaning up"
[[ -f docker-compose.yml ]] && rm docker-compose.yml
}
trap "cleanup" INT TERM EXIT
if [[ -z "$1" ]]; then if [[ -z "$1" ]]; then
log_error "No action provided."
print_help print_help
exit 1 exit 1
fi fi
prepare load_env
case "$1" in actions=(build update purge start up stop down restart logs enter status ps debug lint)
build) action__build;; if [[ ${actions[*]} =~ ${1} ]]; then
start) action__start;; "action__${1}"
stop) action__stop;; else
restart) action__stop; action__start; ;;
logs) action__logs;;
enter) action__enter;;
*)
log_error "The action '$1' is invalid."
print_help print_help
exit 1 exit 1
;; fi
esac
shift