new: doc: [Hedgedoc] add README to manage users with cli tool #38

Closed
bgallet wants to merge 15 commits from bgallet/0k-charms:hedgedoc into master
  1. 138
      README.org
  2. 188
      apache/README.org
  3. 29
      apache/README.rst
  4. 4
      apache/hooks/publish_dir-relation-joined
  5. 4
      apache/hooks/web_proxy-relation-joined
  6. 315
      apache/lib/common
  7. 316
      apache/test/get_domains
  8. 17
      apache/test/vhost
  9. 2
      apache/test/vhost_cert_provider
  10. 14
      apache/test/vhost_files
  11. 20
      hedgedoc/README.org
  12. 1
      letsencrypt/actions/crt
  13. 15
      mariadb/hooks/sql_database-relation-joined
  14. 1
      mariadb/metadata.yml
  15. 1
      nextcloud/actions/occ
  16. 1
      nextcloud/hooks/init
  17. 51
      nextcloud/hooks/mysql_database-relation-joined
  18. 1
      nextcloud/hooks/mysql_database-relation-joined
  19. 54
      nextcloud/hooks/postgres_database-relation-joined
  20. 75
      nextcloud/hooks/sql_database-relation-joined
  21. 31
      nextcloud/hooks/web_proxy-relation-joined
  22. 45
      nextcloud/lib/common
  23. 11
      nextcloud/metadata.yml
  24. 40
      odoo-tecnativa/README.org
  25. 8
      odoo-tecnativa/README.rst
  26. 39
      odoo-tecnativa/hooks/init
  27. 16
      postgres/hooks/sql_database-relation-joined
  28. 1
      postgres/metadata.yml
  29. 3
      rocketchat/README.org

138
README.org

@ -81,61 +81,121 @@ defined, most tools will look in =/srv/charm-store= by default.
** charm type ** charm type
Not all charm are intended to bring up services as having a container
always running and listening.
Not all charm are designed to set up a continuously running, listening
service.
In ~metadata.yml~, the root level ~type~ can be one of:
In a charm's ~metadata.yml~, the root-level key ~type~ can have one of
these values:
- ~service~ (default)
- ~daemon~ (default)
If not specified, this is the default. A charm brings up a service.
It is meant to be *always running*. For instance, ~apache~, ~mysql~,
~postgres~ are services.
By default, a charm is of type ~daemon~. It's probably the most
expected way to run a service: it brings up a process that is
*always running*. Examples include charms like ~apache~, ~mysql~,
~postgres~.
They usually open ports and are listening to provide their service,
or carry background listening of other ressources (like checking
time and sending scheduling command for the ~cron~ services), and or
use files to trigger or report on their activity.
These charms bring up processes that typically open ports to provide
their functionality, perform background tasks like checking the time
and scheduling commands (as the ~cron~ charm), and may use files to
trigger or report on their activities.
It will have an entry in the final ~docker-compose.yml~, and thus, a
container will run and stay in memory and have a ~restart:
unless-stopped~ policy. They use CPU and memory ressources.
In the final ~docker-compose.yml~, a ~daemon~ type charm will
ensure that an entry is created for the service they manage,
resulting in a container that stays in memory. As such they require
a docker image. They will ensure that these entries are managed with
~restart: unless-stopped~ policy.
- ~run-once~
The processes managed by these charms will be setup via
~docker-compose up~ actions at the end, and they will run in the
background.
The entry is meant to describe *a command that run once*,
it will be called by a service and *will exit after execution*.
Once brought up, the processes from these charms will consume CPU and
memory resources indefinitely, until you manually bring them down.
For instance, ~logrotate~, ~rsync-backup~, or ~letsencrypt~ are
of type ~run-once~.
It makes sense to bring them ~up~ or ~down~.
They are meant to be run by service for specific events. They
usually will use relations to ensure they are called at specific
moment by service...
- ~command~
A command does not have an automatic ~restart~ policy as services
have.
This charm type is used to prepare *a process that run and exits
after execution*. These are more what could be expected of a
"command", and are typically invoked by an other service for
specific events.
They use CPU and memory ressources only when run and gives them back
once finished.
Example includes ~logrotate~, ~rsync-backup~, and ~letsencrypt~,
which are charms of type ~run-once~.
These charms are meant to setup commands that are triggered by
services at specific moments or as a result of specific event. It is
through their ~relation~ hooks with other services that they will
ensure to be called when intended to. They are run through the
~docker-compose run~ call.
Like ~daemon~'s typed charm, these charm will ensure that an entry
is correctly added in the final ~docker-compose.yml~ with all the
necessary options so it is ready to be triggered. They require also
a docker image.
But unlike ~daemon~'s typed charms, these charm will ensure that
the entry they managed in the final ~docker-compose.yml~ *DO NOT*
have an automatic restart policy.
They consume CPU and memory resources only when running and release
resources once finished.
- ~stub~ - ~stub~
The entry describes an entity that will *not be run at all*. It is
used to hold information in the ~compose.yml~ and often to *stand
for* a real service managed outside of ~compose.yml~ (on an other
host or on a different managing system, like a local installation or
LXC, virtualbox, ...).
A ~stub~ charm is more of a placeholder that doesn't have anything
to run at all ! They don't need any docker image. These entities are
used to hold information in ~compose.yml~ and can often be used to
represent a real service managed externally (out of =compose=, on
another host or through a different management system, such as a
local installation, LXC, VirtualBox, etc.).
For example, ~smtp-stub~ charm can be used to build an entity that
will stand for an external ~smtp~ service. Through relations, these
stubs offer interfaces similar to actual services in the setting up
stage. For instance, a ~smtp-stub~ acts as a ~smtp-server~ provider,
and can satisfy ~services~ that would require a ~smtp-server~
provider.
They generally implement relation hooks and act as providers.
No entry is created for them in the final ~docker-compose.yml~.
They do not use any CPU or memory resources
** login and password policy
A charm have to manage different set of password. The best would be
that the charm:
- don't require user to choose password (less configuration)
- will promote reasonable security practice.
There are 2 types of password:
- inter-service passwords (ie: database access password), these are
never used by human operator, and will be required to be known by
the charms to set things up. These should be generated randomly
(although they could be set also via configuration if mentionned).
- they can only be changed by specific backend technical manipulation.
For instance, ~stmp-stub~ can be used to stand for an external ~smtp~.
- user service's admin password (ie: admin user of odoo, nextcloud)
- they can be changed through the service interface.
- this service interface is available to the public and the general users.
- charm doesn't need the password to set things up around the service.
It is through their relation that they shine as they can provide
similar interface than actual services would have
provided. ~smtp-stub~ is a ~smtp-server~ provider and other charm
can connect to it.
*** Inter-service passwords
They usually implement relation hooks, and are providers.
- Login should be defaulted to name of the service when possible
- Should be defaulted to random values if not provided in configuration.
- Should not be advertised even in the command line interface.
- Should be reset-able anytime.
No entry will be created in the final ~docker-compose.yml~.
*** Interactive admin user service's password
They use no CPU or memory ressources at all.
- Login should be defaulted to 'admin'
- Should be defaulted to random values, and not be configurable in configuration.
- Should be advertised at the end of ~compose up~ along with URL of services as long
as the default value chosen by compose is still working.
- Should not be advertised once it was changed by user.

188
apache/README.org

@ -0,0 +1,188 @@
* Usage
Other services will often require a service managed with this charm to
act as a HTTP/HTTPS front-end. It can provide certificates with HTTPS.
** Domain assignment
Services using relation =web-proxy= or =publish-dir= will be required
to be assigned a domain name for the virtual host that will be
created.
*** Domain sources
This domain name can be set (in order of priority), the first source
giving a name will be taken.
- *Relation's options* (=web-proxy= or =publish-dir=)
Using =domain= option, and optionally the deprecated
=server-aliases= for additional names.
#+begin_src yaml
myservice:
# ...
relations:
web-proxy:
apache:
domain: mydomain.org
#server-aliases:
# - www.mydomain.org
# - pro.mydomain.org
#+end_src
- *Apache service's options*, using a =service-domain-name= mapping:
#+begin_src yaml
myservice:
# ...
apache:
options:
service-domain-map:
# ...
myservice:
- mydomain.org
- www.mydomain.org
- pro.mydomain.org
# ...
#+end_src
- *the service name* itself if is a domain name:
#+begin_src yaml
www.mydomain.org:
# ...
#+end_src
Please note that this is not recommended, and will be deprecated.
*** Domain and alternate domains
Every source (except the one coming out from the domain name), can use
several ways to provide *more than one domain name*.
Please remember:
- At least one domain name needs to be provided
- and the first domain can't use wildcards and will be considered the main domain name.
If other domains are specified, they will be used as aliases, and
wildcard (using ~*~) is supported.
Additionally, bash braces expansion and regex matching are
available. Space separated YAML string or YAML sequences are
supported, also as mix of both.
As examples, notice the following are equivalent and will serve
=myservice= on the exact same set of domain names:
#+begin_src yaml
myservice:
relations:
web-proxy:
domain:
## A yaml list
- myservice.home.org
- mydomain.org
- www.mydomain.org
- pro.mydomain.org
- *.myservice.hop.org
#+end_src
#+begin_src yaml
myservice:
# ... no domain set in relation
apache:
options:
service-domain-map:
## A yaml list as a mapping value
myservice:
- myservice.home.org
- {,www.,pro.}mydomain.org ## bash braces expansion used
- *.myservice.hop.org
#+end_src
#+begin_src yaml
myservice:
# ...
apache:
options:
service-domain-map:
## space separated YAML string and bash braces expansion
myservice: myservice.home.org {,www.,pro.}mydomain.org *.myservice.hop.org
#+end_src
#+begin_src yaml
myservice:
# ...
apache:
options:
service-domain-map:
## Leveraging bash braces expansion and regex replacement
.*: {$0.home,{,www.,pro.}mydomain,*.$0.hop}.org
#+end_src
** Domain mapping
You can automatically assign a domain to services in relation
=web-proxy= or =publish-dir= with services managed by this charm using
the =service-domain-name= option. For instance:
#+begin_src yaml
apache:
options:
service-domain-map:
.*: $0.mydomain.org
#+end_src
Where ~mydomain.org~ stands for the domain where most of your services
will be served. You can override this behavior for some services:
- by adding a matching rule *before* the given rule.
- by specifying a =domain= in the relation's options.
first rule matching will end the mapping:
#+begin_src yaml
apache:
options:
service-domain-map:
foo: www.mydomain.org
bar: beta.myotherdomain.com
#+end_src
Allows to distribute services to domains quite freely.
* SSH Tunnel
On the server side, you can configure your compose file::
#+begin_src yaml
apache:
options:
ssh-tunnel:
domain: ssh.domain.com ## required
#ssl: ... ## required, but automatically setup if you
## provide a ``cert-provider`` to ``apache``.
#+end_src
On the client side you should add this to your ``~/.ssh/config``::
#+begin_src conf-space
Host ssh.domain.com
Port 443
ProxyCommand proxytunnel -q -E -p ssh.domain.com:443 -d ssh.domain.com:22
DynamicForward 1080
ServerAliveInterval 60
#+end_src
If it doesn't work, you can do some checks thanks to this command::
#+begin_example
$ proxytunnel -E -p ssh.domain.com:443 -d ssh.domain.com:22 -v \
-H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n"
#+end_example

29
apache/README.rst

@ -1,29 +0,0 @@
SSH Tunnel
----------
On the server side, you can configure your compose file::
apache:
options:
ssh-tunnel:
domain: ssh.domain.com ## required
#ssh: ... ## required, but automatically setup if you
## provide a ``cert-provider`` to ``apache``.
On the client side you should add this to your ``~/.ssh/config``::
Host ssh.domain.com
Port 443
ProxyCommand proxytunnel -q -E -p ssh.domain.com:443 -d ssh.domain.com:22
DynamicForward 1080
ServerAliveInterval 60
If it doesn't work, you can do some checks thanks to this command::
$ proxytunnel -E -p ssh.domain.com:443 -d ssh.domain.com:22 -v \
-H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n"

4
apache/hooks/publish_dir-relation-joined

@ -6,8 +6,10 @@
set -e set -e
service_def=$(get_compose_service_def "$SERVICE_NAME")
cfg=$(relation-get) cfg=$(relation-get)
apache_publish_dir "$cfg"
apache_publish_dir "$cfg" "$service_def"
APACHE_CORE_RULES=$(relation-get apache-core-rules 2>/dev/null) || true APACHE_CORE_RULES=$(relation-get apache-core-rules 2>/dev/null) || true
if [ "$APACHE_CORE_RULES" ]; then if [ "$APACHE_CORE_RULES" ]; then

4
apache/hooks/web_proxy-relation-joined

@ -5,8 +5,10 @@ set -e
. lib/common . lib/common
service_def=$(get_compose_service_def "$SERVICE_NAME")
cfg=$(relation-get) cfg=$(relation-get)
apache_proxy_dir "$cfg"
apache_proxy_dir "$cfg" "$service_def"
APACHE_CORE_RULES=$(relation-get apache-core-rules 2>/dev/null) || true APACHE_CORE_RULES=$(relation-get apache-core-rules 2>/dev/null) || true
if [ "$APACHE_CORE_RULES" ]; then if [ "$APACHE_CORE_RULES" ]; then

315
apache/lib/common

@ -1,22 +1,163 @@
# -*- mode: shell-script -*- # -*- mode: shell-script -*-
replace_by_rematch_pattern() {
local input="$1" output="" char next_char i
# Loop through each character in the string
for (( i=0; i<${#input}; i++ )); do
char="${input:$i:1}"
# If a dollar sign is found
if [[ "$char" == '$' ]]; then
next_char="${input:$((i+1)):1}"
# Check if next character is a digit
if [[ "$next_char" =~ [0-9] ]]; then
# Replace $N with ${rematch[N]}
output+='${rematch['"$next_char"']}'
((i++)) # Skip next character as it's already processed
continue
fi
fi
get_domain() {
local cfg="$1" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$MASTER_BASE_SERVICE_NAME" "$@")" \
domain
output+="$char"
done
echo "$output"
}
export -f replace_by_rematch_pattern
get_domains() {
local cfg="$1" service_cfg="$2" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$MASTER_BASE_SERVICE_NAME" "$@")" \
domains new_domains maps m regex rematch type value key domain
if [ -e "$cache_file" ]; then if [ -e "$cache_file" ]; then
cat "$cache_file" cat "$cache_file"
return 0 return 0
fi fi
domain=$(e "$cfg" | cfg-get-value domain 2>/dev/null) || true
if [ "$domain" ]; then
echo "$domain" | tee "$cache_file"
elif [[ "$BASE_SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then
echo "$BASE_SERVICE_NAME" | tee "$cache_file"
else
err "You must specify a ${WHITE}domain$NORMAL option. (${FUNCNAME[*]})"
domains=()
for key in domain server-aliases; do
type=$(e "$cfg" | shyaml -q get-type "$key" 2>/dev/null) || true
value=$(e "$cfg" | shyaml -q get-value -y "$key" 2>/dev/null) || true
while true; do
case "$type" in
sequence)
while read-0 domain; do
if [[ "$domain" != "None" ]]; then
if [ "${#domains[@]}" == 0 ] && [ "$key" == "server-aliases" ]; then
err "You can't specify server aliases if you don't have a domain."
return 1
fi
domains+=("$domain")
fi
done < <(e "$value" | shyaml get-values-0)
;;
str)
if ! domain=$(e "$value" | shyaml get-value 2>/dev/null); then
err "Failed to get domain value from config."
return 1
fi
[[ "$domain" == "" ]] && break
if [ "${#domains[@]}" == 0 ] && [ "$key" == "server-aliases" ]; then
err "You can't specify server aliases if you don't have a domain."
return 1
fi
domains+=("$domain")
;;
NoneType|"")
:
;;
\!*)
value=$(e "$value" | cfg-get-value "$key" 2>/dev/null) || true
type=$(e "$value" | shyaml -q get-type) || true
continue
;;
esac
break
done
## check and expand domain
new_domains=()
for domain in "${domains[@]}"; do
if ! [[ "$domain" =~ ^[a-z0-9\{\}*\ \,.-]+$ ]]; then
err "Invalid domain value '$domain' expression in ${WHITE}$key$NORMAL option."
return 1
fi
new_domains+=($(eval echo "${domain//\*/\\*}"))
done
domains=("${new_domains[@]}")
done
## Fill with service-domain-map
if [ "${#domains[@]}" == 0 ] &&
service_domain_map=$(e "$service_cfg" | cfg-get-value 'options.service-domain-map' 2>/dev/null) &&
[ -n "$service_domain_map" ]; then
while read-0 regex map; do
if [[ "$BASE_SERVICE_NAME" =~ ^$regex$ ]]; then
rematch=("${BASH_REMATCH[@]}")
maps=()
type=$(e "$map" | shyaml -q get-type 2>/dev/null) || true
value=$(e "$map" | shyaml -q get-value -y 2>/dev/null) || true
case "$type" in
sequence)
while read-0 m; do
if [[ "$m" != "None" ]] && [ -n "$m" ]; then
maps+=("$m")
fi
done < <(e "$value" | shyaml get-values-0)
;;
str)
if ! m=$(e "$value" | shyaml get-value 2>/dev/null); then
err "Failed to get mapping value from config."
return 1
fi
[ -z "$m" ] && continue
maps+=("$m")
;;
NoneType|"")
:
;;
esac
for map in "${maps[@]}"; do
if ! [[ "$map" =~ ^([a-z0-9*\{\}\ \,.-]|\$[0-9])+$ ]]; then
err "Invalid mapping value '$map' in ${WHITE}service-domain-map$NORMAL option."
return 1
fi
map="${map//\*/\\*}" ## protect star from eval
map=$(replace_by_rematch_pattern "$map")
domains+=($(set -- "${BASH_REMATCH[@]}"; eval echo "${map//\*/\\*}"))
done
break
fi
done < <(e "$service_domain_map" | yq e -0 'to_entries | .[] | [.key, .value] |.[]')
fi
if [ "${#domains[@]}" == 0 ] &&
[[ "$BASE_SERVICE_NAME" =~ ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$ ]]; then
domains+=("$BASE_SERVICE_NAME")
fi
new_domains=()
## remove duplicates
for domain in "${domains[@]}"; do
if ! [[ " ${new_domains[*]} " == *" $domain "* ]]; then
new_domains+=("$domain")
fi
done
domains=("${new_domains[@]}")
if [ "${#domains[@]}" == 0 ]; then
err "No domain name set for your service ${YELLOW}$BASE_SERVICE_NAME${NORMAL}."
echo " You can specify a ${WHITE}domain$NORMAL option in the" \
"relation with ${YELLOW}$TARGET_SERVICE_NAME${NORMAL}." >&2
echo " Or you can specify a service to domain mapping in" \
"${WHITE}service-domain-map${NORMAL} option of ${YELLOW}$TARGET_SERVICE_NAME${NORMAL}." >&2
return 1
fi
## check that first domain should not have a wildcard
if [[ "${domains[0]}" == *"*"* ]]; then
err "First domain name '${domains[0]}' can't contain a wildcard."
return 1 return 1
fi fi
echo "${domains[@]}" | tee "$cache_file"
} }
@ -25,17 +166,17 @@ get_domain() {
## ##
apache_proxy_dir() { apache_proxy_dir() {
local cfg="$1" domain
apache_vhost_create web_proxy "$cfg" || return 1
local cfg="$1" service_cfg="$2"
apache_vhost_create web_proxy "$cfg" "$service_cfg" || return 1
} }
export -f apache_proxy_dir export -f apache_proxy_dir
apache_publish_dir() { apache_publish_dir() {
local cfg="$1" domain
apache_vhost_create publish_dir "$cfg" || return 1
apache_code_dir "$cfg" || return 1
apache_data_dirs "$cfg"
local cfg="$1" service_cfg="$2"
apache_vhost_create publish_dir "$cfg" "$service_cfg" || return 1
apache_code_dir "$cfg" "$service_cfg" || return 1
apache_data_dirs "$cfg" "$service_cfg"
} }
export -f apache_publish_dir export -f apache_publish_dir
@ -51,7 +192,7 @@ apache_ssh_tunnel() {
err "${WHITE}ssl${NORMAL} must be valued in ${WHITE}ssh-tunnel${NORMAL} config." err "${WHITE}ssl${NORMAL} must be valued in ${WHITE}ssh-tunnel${NORMAL} config."
return 1 return 1
fi fi
apache_vhost_create ssh_tunnel "$cfg" ",https," "000-$domain" || return 1
apache_vhost_create ssh_tunnel "$cfg" "" ",https," "000-$domain" || return 1
} }
export -f apache_publish_dir export -f apache_publish_dir
@ -62,7 +203,7 @@ export -f apache_publish_dir
apache_vhost_create() { apache_vhost_create() {
local type="$1" cfg="$2" protocols="$3" dest="$4" custom_rules vhost_statement creds \
local type="$1" cfg="$2" service_cfg="$3" protocols="$4" dest="$5" custom_rules vhost_statement creds \
redirect domain ssl_plugin_fun ssl_cfg_value ssl_cfg_options redirect domain ssl_plugin_fun ssl_cfg_value ssl_cfg_options
export APACHE_CONFIG_LOCATION="$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled" export APACHE_CONFIG_LOCATION="$SERVICE_CONFIGSTORE/etc/apache2/sites-enabled"
@ -73,21 +214,21 @@ apache_vhost_create() {
protocols=$(__vhost_cfg_normalize_protocol "$cfg") || return 1 protocols=$(__vhost_cfg_normalize_protocol "$cfg") || return 1
fi fi
domain=$(get_domain "$cfg") && {
[ "$RELATION_DATA_FILE" ] && relation-set domain "$domain"
domains=($(get_domains "$cfg" "$service_cfg")) && {
[ "$RELATION_DATA_FILE" ] && relation-set domain "${domains[0]}"
} }
echo "Domains: ${domains[*]}" >&2
if is_protocol_enabled https "$protocols"; then if is_protocol_enabled https "$protocols"; then
[ "$RELATION_DATA_FILE" ] && { [ "$RELATION_DATA_FILE" ] && {
relation-set url "https://$domain"
relation-set url "https://${domains[0]}"
} }
if [ -z "$domain" ]; then
if [ "${#domains[@]}" == 0 ]; then
err "You must specify a domain for ssl to work." err "You must specify a domain for ssl to work."
return 1 return 1
fi fi
read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1 read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1
"$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domain" || return 1
"$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "${domains[*]}" || return 1
redirect=$(e "$cfg" | cfg-get-value 'redirect-to-ssl' 2>/dev/null) || true redirect=$(e "$cfg" | cfg-get-value 'redirect-to-ssl' 2>/dev/null) || true
if is_protocol_enabled http "$protocols"; then if is_protocol_enabled http "$protocols"; then
redirect=${redirect:-true} redirect=${redirect:-true}
@ -116,15 +257,15 @@ $(if [ "$custom_rules" ]; then
fi fi
else else
[ "$RELATION_DATA_FILE" ] && { [ "$RELATION_DATA_FILE" ] && {
relation-set url "http://$domain"
relation-set url "http://${domains[0]}"
} }
fi fi
vhost_statement=$(apache_vhost_statement "$type" "$protocols" "$cfg" "$domain") || {
vhost_statement=$(apache_vhost_statement "$type" "$protocols" "$cfg" "${domains[*]}") || {
err "Failed to get vhost statement for type $type on ${protocols:1:-1}" err "Failed to get vhost statement for type $type on ${protocols:1:-1}"
return 1 return 1
} }
dest=${dest:-$domain}
dest=${dest:-${domains[0]}}
if [ -z "$dest" ]; then if [ -z "$dest" ]; then
err "Please set either a domain or set a destination file." err "Please set either a domain or set a destination file."
return 1 return 1
@ -137,7 +278,7 @@ $(if [ "$custom_rules" ]; then
fi fi
if is_protocol_enabled https "$protocols"; then if is_protocol_enabled https "$protocols"; then
"$ssl_plugin_fun"_prepare "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" || return 1
"$ssl_plugin_fun"_prepare "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "${domains[*]}" || return 1
fi fi
} }
@ -159,7 +300,7 @@ _get_ssl_option_value() {
fi fi
if ssl_cfg=$(e "$cfg" | cfg-get-value ssl 2>/dev/null); then if ssl_cfg=$(e "$cfg" | cfg-get-value ssl 2>/dev/null); then
if [[ "$ssl_cfg" =~ ^False|None$ ]]; then
if [[ "$ssl_cfg" =~ ^False|None|false|null$ ]]; then
ssl_cfg="" ssl_cfg=""
fi fi
echo "$ssl_cfg" | tee "$cache_file" echo "$ssl_cfg" | tee "$cache_file"
@ -249,7 +390,7 @@ ssl_get_plugin_fun() {
} }
type="$(echo "$cfg" | shyaml -y get-type 2>/dev/null)" || return 1 type="$(echo "$cfg" | shyaml -y get-type 2>/dev/null)" || return 1
if [[ "$type" == "bool" ]]; then
if [[ "$type" == "bool" ]] || [[ "$type" == "str" && "$cfg" =~ ^false|true$ ]]; then
printf "%s\0" "ssl_fallback" "" "$cfg" | tee "$cache_file" printf "%s\0" "ssl_fallback" "" "$cfg" | tee "$cache_file"
return 0 return 0
fi fi
@ -296,18 +437,18 @@ ssl_get_plugin_fun() {
ssl_fallback_vars() { ssl_fallback_vars() {
local cfg="$1" ssl_cfg="$2" value="$3" domain="$4" cert key ca_cert domain
local cfg="$1" ssl_cfg="$2" value="$3" domains="$4" cert key ca_cert
domains=($domains)
if __vhost_cfg_ssl_cert=$(echo "$ssl_cfg" | shyaml get-value cert 2>/dev/null); then if __vhost_cfg_ssl_cert=$(echo "$ssl_cfg" | shyaml get-value cert 2>/dev/null); then
__vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/${domain}.pem
__vhost_cfg_SSL_CERT_LOCATION=/etc/ssl/certs/${domains[0]}.pem
fi fi
if __vhost_cfg_ssl_key=$(echo "$ssl_cfg" | shyaml get-value key 2>/dev/null); then if __vhost_cfg_ssl_key=$(echo "$ssl_cfg" | shyaml get-value key 2>/dev/null); then
__vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/${domain}.key
__vhost_cfg_SSL_KEY_LOCATION=/etc/ssl/private/${domains[0]}.key
fi fi
if __vhost_cfg_ssl_ca_cert=$(echo "$ssl_cfg" | shyaml get-value ca-cert 2>/dev/null); then if __vhost_cfg_ssl_ca_cert=$(echo "$ssl_cfg" | shyaml get-value ca-cert 2>/dev/null); then
__vhost_cfg_SSL_CA_CERT_LOCATION=/etc/ssl/certs/${domain}-ca.pem
__vhost_cfg_SSL_CA_CERT_LOCATION=/etc/ssl/certs/${domains[0]}-ca.pem
fi fi
} }
@ -338,29 +479,20 @@ $volumes
} }
ssl_plugin_cert-provider_vars() { ssl_plugin_cert-provider_vars() {
local cfg="$1" ssl_cfg="$2" value="$3" domain="$4"
__vhost_cfg_SSL_CERT_LOCATION=/etc/letsencrypt/live/${domain}/cert.pem
__vhost_cfg_SSL_KEY_LOCATION=/etc/letsencrypt/live/${domain}/privkey.pem
__vhost_cfg_SSL_CHAIN=/etc/letsencrypt/live/${domain}/chain.pem
local cfg="$1" ssl_cfg="$2" value="$3" domains="$4"
domains=($domains)
__vhost_cfg_SSL_CERT_LOCATION=/etc/letsencrypt/live/${domains[0]}/cert.pem
__vhost_cfg_SSL_KEY_LOCATION=/etc/letsencrypt/live/${domains[0]}/privkey.pem
__vhost_cfg_SSL_CHAIN=/etc/letsencrypt/live/${domains[0]}/chain.pem
} }
ssl_plugin_cert-provider_prepare() { ssl_plugin_cert-provider_prepare() {
local cfg="$1" ssl_cfg="$2" service="$3" options domain server_aliases
domain=$(get_domain "$cfg") || return 1
local cfg="$1" ssl_cfg="$2" service="$3" domains="$4" options server_aliases
domains=($domains)
options=$(yaml_key_val_str "options" "$ssl_cfg") || return 1 options=$(yaml_key_val_str "options" "$ssl_cfg") || return 1
service_config=$(yaml_key_val_str "$service" "$options") service_config=$(yaml_key_val_str "$service" "$options")
server_aliases=$(e "$cfg" | cfg-get-value server-aliases 2>/dev/null) || true
[ "$server_aliases" == None ] && server_aliases=""
if [ "$server_aliases" ]; then
server_aliases=($(echo "$server_aliases" | shyaml get-values)) || return 1
else
server_aliases=()
fi
compose --debug --add-compose-content "$service_config" crt "$service" \ compose --debug --add-compose-content "$service_config" crt "$service" \
create "$domain" "${server_aliases[@]}" || {
create "${domains[@]}" || {
err "Failed to launch letsencrypt for certificate creation." err "Failed to launch letsencrypt for certificate creation."
return 1 return 1
} }
@ -399,16 +531,16 @@ apache_passwd_file() {
## Produce the full statements depending on relation-get informations ## Produce the full statements depending on relation-get informations
apache_vhost_statement() { apache_vhost_statement() {
local type="$1" protocols="$2" cfg="$3" domain="$4" \
local type="$1" protocols="$2" cfg="$3" domains="$4" \
vhost_statement vhost_statement
if is_protocol_enabled http "$protocols"; then if is_protocol_enabled http "$protocols"; then
__vhost_full_vhost_statement "$type" http "$cfg" "$domain" || return 1
__vhost_full_vhost_statement "$type" http "$cfg" "$domains" || return 1
fi fi
if is_protocol_enabled https "$protocols"; then if is_protocol_enabled https "$protocols"; then
read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1 read-0 ssl_plugin_fun ssl_cfg_value ssl_cfg_options < <(ssl_get_plugin_fun "$cfg") || return 1
"$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domain" || return 1
vhost_statement=$(__vhost_full_vhost_statement "$type" https "$cfg" "$domain") || return 1
"$ssl_plugin_fun"_vars "$cfg" "$ssl_cfg_options" "$ssl_cfg_value" "$domains" || return 1
vhost_statement=$(__vhost_full_vhost_statement "$type" https "$cfg" "$domains") || return 1
cat <<EOF cat <<EOF
<IfModule mod_ssl.c> <IfModule mod_ssl.c>
@ -421,13 +553,13 @@ export -f apache_vhost_statement
apache_code_dir() { apache_code_dir() {
local cfg="$1" www_data_gid local_path
local cfg="$1" service_cfg="$2" www_data_gid local_path
www_data_gid=$(cached_cmd_on_base_image "$TARGET_SERVICE_NAME" 'id -g www-data') || { www_data_gid=$(cached_cmd_on_base_image "$TARGET_SERVICE_NAME" 'id -g www-data') || {
debug "Failed to query for www-data gid in ${DARKYELLOW}$TARGET_SERVICE_NAME${NORMAL} base image." debug "Failed to query for www-data gid in ${DARKYELLOW}$TARGET_SERVICE_NAME${NORMAL} base image."
return 1 return 1
} }
domain=$(get_domain "$cfg") || return 1
local_path="/var/www/${domain}"
domains=($(get_domains "$cfg" "$service_cfg")) || return 1
local_path="/var/www/${domains[0]}"
host_path=$(e "$cfg" | cfg-get-value location 2>/dev/null) || host_path=$(e "$cfg" | cfg-get-value location 2>/dev/null) ||
host_path="$DATASTORE/$BASE_SERVICE_NAME${local_path}" host_path="$DATASTORE/$BASE_SERVICE_NAME${local_path}"
@ -440,6 +572,9 @@ apache_code_dir() {
"$CONFIGSTORE"*) "$CONFIGSTORE"*)
docker_host_path="$HOST_CONFIGSTORE${host_path##$CONFIGSTORE}" docker_host_path="$HOST_CONFIGSTORE${host_path##$CONFIGSTORE}"
;; ;;
*)
docker_host_path="$host_path"
;;
esac esac
mkdir -p "$host_path" || return 1 mkdir -p "$host_path" || return 1
@ -452,13 +587,13 @@ $SERVICE_NAME:
} }
apache_data_dirs() { apache_data_dirs() {
local cfg="$1" data_dirs dst data fdir to_create
local cfg="$1" service_cfg="$2" data_dirs dst data fdir to_create
data_dirs=$(e "$cfg" | cfg-get-value data-dirs 2>/dev/null | shyaml get-values 2>/dev/null) || true data_dirs=$(e "$cfg" | cfg-get-value data-dirs 2>/dev/null | shyaml get-values 2>/dev/null) || true
if [ -z "$data_dirs" ]; then if [ -z "$data_dirs" ]; then
return 0 return 0
fi fi
domain=$(get_domain "$cfg") || return 1
local_path="/var/www/${domain}"
domains=($(get_domains "$cfg" "$service_cfg")) || return 1
local_path="/var/www/${domains[0]}"
dst=$DATASTORE/$BASE_SERVICE_NAME$local_path dst=$DATASTORE/$BASE_SERVICE_NAME$local_path
data=() data=()
while IFS="," read -ra addr; do while IFS="," read -ra addr; do
@ -574,36 +709,25 @@ EOF
__vhost_head_statement() { __vhost_head_statement() {
local cfg="$1" protocol="$2" domain="$3" server_aliases admin_mail prefix
local cfg="$1" protocol="$2" domains="$3" server_aliases admin_mail prefix
domains=($domains)
admin_mail=$(e "$1" | cfg-get-value "admin-mail" 2>/dev/null) || true admin_mail=$(e "$1" | cfg-get-value "admin-mail" 2>/dev/null) || true
if [ -z "$admin_mail" ]; then if [ -z "$admin_mail" ]; then
if [ -z "$domain" ]; then
if [ "${#domains[@]}" == 0 ]; then
admin_mail=webmaster@localhost admin_mail=webmaster@localhost
else else
admin_mail=${admin_mail:-contact@$domain}
admin_mail=${admin_mail:-contact@${domains[0]}}
fi fi
fi fi
server_aliases=$(e "$cfg" | cfg-get-value server-aliases 2>/dev/null) || true
[ "$server_aliases" == None ] && server_aliases=""
if [ "$server_aliases" ]; then
server_aliases=($(e "$server_aliases" | shyaml get-values)) || return 1
if [ -z "$domain" ]; then
err "You can't specify server aliases if you don't have a domain."
return 1
fi
else
server_aliases=()
fi
if [ "$protocol" == "https" ]; then if [ "$protocol" == "https" ]; then
prefix="s-" prefix="s-"
else else
prefix= prefix=
fi fi
if [ "$domain" ]; then
log_prefix="${prefix}${domain}_"
if [ "${#domains[@]}" != 0 ]; then
log_prefix="${prefix}${domains[0]}_"
else else
log_prefix="" log_prefix=""
fi fi
@ -611,8 +735,8 @@ __vhost_head_statement() {
cat <<EOF cat <<EOF
$( $(
echo "ServerAdmin ${admin_mail}" echo "ServerAdmin ${admin_mail}"
[ "$domain" ] && echo "ServerName ${domain}"
for alias in "${server_aliases[@]}"; do
[ "${#domains[@]}" != 0 ] && echo "ServerName ${domains[0]}"
for alias in "${domains[@]:1}"; do
[ "$alias" ] || continue [ "$alias" ] || continue
echo "ServerAlias $alias" echo "ServerAlias $alias"
done done
@ -690,14 +814,15 @@ target-get() {
} }
__vhost_proxy_statement() { __vhost_proxy_statement() {
local protocol="$1" cfg="$2" dest="$3" proxy_pass_options target
local protocol="$1" cfg="$2" domains="$3" proxy_pass_options target
domains=($domains)
target=$(target-get "$cfg") || return 1 target=$(target-get "$cfg") || return 1
proxy_pass_options=($(e "$cfg" | shyaml -y -q get-value apache-proxy-pass-options | yaml_get_values)) proxy_pass_options=($(e "$cfg" | shyaml -y -q get-value apache-proxy-pass-options | yaml_get_values))
if [ "${#proxy_pass_options[@]}" == 0 ]; then if [ "${#proxy_pass_options[@]}" == 0 ]; then
proxy_pass_options=(${proxy_pass_options:-"retry=0"}) proxy_pass_options=(${proxy_pass_options:-"retry=0"})
fi fi
dest=${domains[0]}
dest=${dest:-html}
cat <<EOF cat <<EOF
@ -730,11 +855,12 @@ EOF
} }
__vhost_full_vhost_statement() { __vhost_full_vhost_statement() {
local type="$1" protocol="$2" cfg="$3" domain="$4" head_statement custom_rules content_statement
local type="$1" protocol="$2" cfg="$3" domains="$4" head_statement custom_rules content_statement
head_statement=$(__vhost_head_statement "$cfg" "$protocol" "$domain") || return 1
domains=($domains)
head_statement=$(__vhost_head_statement "$cfg" "$protocol" "${domains[*]}") || return 1
custom_rules=$(__vhost_custom_rules "$cfg") || return 1 custom_rules=$(__vhost_custom_rules "$cfg") || return 1
content_statement=$(__vhost_content_statement "$type" "$protocol" "$cfg" "${domain:-html}") || return 1
content_statement=$(__vhost_content_statement "$type" "$protocol" "$cfg" "${domains[*]}") || return 1
case "$protocol" in case "$protocol" in
https) https)
@ -767,7 +893,10 @@ EOF
} }
__vhost_publish_dir_statement() { __vhost_publish_dir_statement() {
local protocol="$1" cfg="$2" dest="$3" location
local protocol="$1" cfg="$2" domains="$3" location
domains=($domains)
dest=${domains[0]}
dest=${dest:-html}
local_path="/var/www/${dest}" local_path="/var/www/${dest}"
cat <<EOF cat <<EOF
@ -795,7 +924,13 @@ EOF
__vhost_tunnel_ssh_statement() { __vhost_tunnel_ssh_statement() {
local protocol="$1" cfg="$2" dest="$3" custom_rules content_statement
local protocol="$1" cfg="$2" domains="$3" custom_rules content_statement
domains=($domains)
dest=${domains[0]}
if [ "${#domains[@]}" == 0 ]; then
err "You must specify a domain for ssh tunnel to work."
return 1
fi
cat <<EOF cat <<EOF
@ -817,7 +952,7 @@ AllowConnect 22
### Accept redirect only to same domain ### Accept redirect only to same domain
<Proxy $domain>
<Proxy ${domains[0]}>
Order deny,allow Order deny,allow
$(__vhost_creds_statement "$cfg" "$dest" | prefix " ") $(__vhost_creds_statement "$cfg" "$dest" | prefix " ")
</Proxy> </Proxy>

316
apache/test/get_domains

@ -0,0 +1,316 @@
#!/bin/bash
exname=$(basename $0)
compose_core=$(which compose-core) || {
echo "Requires compose-core executable to be in \$PATH." >&2
exit 1
}
fetch-def() {
local path="$1" fname="$2"
( . "$path" 1>&2 || {
echo "Failed to load '$path'." >&2
exit 1
}
declare -f "$fname"
)
}
prefix_cmd="
. /etc/shlib
include common
include parse
. ../lib/common
$(fetch-def "$compose_core" yaml_get_values)
$(fetch-def "$compose_core" yaml_get_interpret)
" || {
echo "Couldn't build prefix cmd" >&2
exit 1
}
# mock
cfg-get-value() {
local key="$1"
shyaml get-value "$key" 2>/dev/null
}
export -f cfg-get-value
yaml_get_interpret() {
shyaml get-value
}
export -f yaml_get_interpret
export state_tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
trap "rm -rf \"$state_tmpdir\"" EXIT
##
## Tests
##
try "
get_domains '
'"
is errlvl 1
is err reg 'Error: .*domain option.*'
is out ''
try "
get_domains '
domain: toto
'"
noerror
is out 'toto
'
try "
get_domains '
domain: toto titi
'"
noerror
is out 'toto titi
'
try "
get_domains '
domain:
- toto
'"
noerror
is out 'toto
'
try "
get_domains '
server-aliases:
'"
is errlvl 1
is err part 'Error: '
is err part 'No domain name set'
try "
get_domains '
domain:
server-aliases:
'"
is errlvl 1
is err part 'Error: '
is err part 'No domain name set'
try "
get_domains '
domain:
server-aliases:
- toto
'"
is errlvl 1
is err part 'Error: '
is err part "You can't specify server aliases if you don't have a domain"
try "
get_domains '
domain: foo
server-aliases:
- bar
'"
noerror
is out 'foo bar
'
try "
get_domains '
domain: foo
server-aliases: bar
'"
noerror
is out 'foo bar
'
try "
get_domains '
domain:
- foo
server-aliases: bar
'"
noerror
is out 'foo bar
'
try "
get_domains '
domain:
- foo{1,2} bar
server-aliases: wiz
'"
noerror
is out 'foo1 foo2 bar wiz
'
try "
get_domains '
domain:
- foo{1,2} bar
server-aliases: foo1
'"
noerror
is out 'foo1 foo2 bar
'
try "
get_domains '
domain:
- foo{1,2} bar
- \"*.zoo\"
server-aliases: foo1
'"
noerror
is out 'foo1 foo2 bar *.zoo
'
try "
get_domains '
domain: foo+ bar
'"
is errlvl 1
is err part 'Error: '
is err part 'Invalid domain value'
try "
get_domains '
domain:
' '
options.service-domain-map:
'" "empty service-domain-map"
is errlvl 1
is err part 'Error: '
is err part 'No domain name set'
is err part 'service-domain-map'
try "
BASE_SERVICE_NAME=foo
get_domains '
domain:
' '
options:
service-domain-map:
wiz: bar
'" "no map matching in service-domain-map"
is errlvl 1
is err part 'Error: '
is err part 'No domain name set'
is err part 'service-domain-map'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
wiz: bar
'" "matching map in service-domain-map"
noerror
is out 'bar
'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
wiz?: bar
wiz: bar2
'" "only first matching map in service-domain-map"
noerror
is out 'bar
'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
\"[w]i?zz?\": bar
'" "map are regex in service-domain-map"
noerror
is out 'bar
'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
(w)i(z): bar\$1\$2
'" "regex capture in service-domain-map"
noerror
is out 'barwz
'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
.*: \$0.shrubbery
'" "regex capture 2 in service-domain-map"
noerror
is out 'wiz.shrubbery
'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
.*: \$x
'" "refuse other variables in service-domain-map"
is errlvl 1
is err part 'Error: '
is err part 'Invalid mapping value'
try "
export BASE_SERVICE_NAME=wiz
get_domains '
domain:
' '
options:
service-domain-map:
.*:
- \$0.example.com
- my-\$0.domain.org
'" "list is possible as value of service-domain-map"
noerror
is out 'wiz.example.com my-wiz.domain.org
'

17
apache/test/vhost

@ -107,20 +107,13 @@ is out '<VirtualHost *:80>
## ##
try " try "
apache_vhost_statement publish_dir ,http, '
server-aliases:
- toto
' www.example.com"
apache_vhost_statement publish_dir ,http, '' 'www.example.com toto'"
noerror noerror
is out reg 'ServerAlias toto' is out reg 'ServerAlias toto'
try " try "
apache_vhost_statement publish_dir ,http, '
server-aliases:
- toto
- titi
' www.example.com"
apache_vhost_statement publish_dir ,http, '' 'www.example.com toto titi'"
noerror noerror
is out reg 'ServerAlias toto' is out reg 'ServerAlias toto'
is out reg 'ServerAlias titi' is out reg 'ServerAlias titi'
@ -295,7 +288,8 @@ is out '<VirtualHost *:80>
</IfModule> </IfModule>
RequestHeader set "X-Forwarded-Proto" "http"
SetEnvIf X-Forwarded-Proto "^$" forwarded_proto_not_set=true
RequestHeader set "X-Forwarded-Proto" "http" env=forwarded_proto_not_set
## Fix IE problem (httpapache proxy dav error 408/409) ## Fix IE problem (httpapache proxy dav error 408/409)
SetEnv proxy-nokeepalive 1 SetEnv proxy-nokeepalive 1
@ -348,7 +342,8 @@ is out '<VirtualHost *:80>
SSLProxyEngine On SSLProxyEngine On
</IfModule> </IfModule>
RequestHeader set "X-Forwarded-Proto" "https"
SetEnvIf X-Forwarded-Proto "^$" forwarded_proto_not_set=true
RequestHeader set "X-Forwarded-Proto" "https" env=forwarded_proto_not_set
## Fix IE problem (httpapache proxy dav error 408/409) ## Fix IE problem (httpapache proxy dav error 408/409)
SetEnv proxy-nokeepalive 1 SetEnv proxy-nokeepalive 1

2
apache/test/vhost_cert_provider

@ -182,7 +182,7 @@ ssl:
foo: | foo: |
a a
b b
'
' ''
" "unknown cert key" " "unknown cert key"
is errlvl 1 is errlvl 1
is err reg 'Error: .*cert-provider.*' is err reg 'Error: .*cert-provider.*'

14
apache/test/vhost_files

@ -305,8 +305,8 @@ creds:
' '
" "
is errlvl 0 is errlvl 0
is err reg 'setfacl -R -m g:<GID>:rx \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com'
is err reg 'cached_cmd_on_base_image apache id -g www-data'
# is err reg 'setfacl -R -m g:<GID>:rx \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com'
# is err reg 'cached_cmd_on_base_image apache id -g www-data'
try " try "
@ -327,8 +327,8 @@ data-dirs:
' '
" "
is errlvl 0 is errlvl 0
is err reg 'setfacl -R -m g:<GID>:rwx \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/a \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/b \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/c'
is err reg 'setfacl -R -d -m g:<GID>:rwx \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/a \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/b \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/c'
# is err reg 'setfacl -R -m g:<GID>:rwx \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/a \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/b \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/c'
# is err reg 'setfacl -R -d -m g:<GID>:rwx \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/a \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/b \$DATASTORE/\$BASE_SERVICE_NAME/var/www/www.example.com/c'
try " try "
@ -350,16 +350,16 @@ data-dirs:
- b - b
- c - c
' '
"
" "with location"
is errlvl 0 is errlvl 0
is err reg 'mkdir -p /opt/apps/newlocation' is err reg 'mkdir -p /opt/apps/newlocation'
is err reg 'setfacl -R -m g:<GID>:rx /opt/apps/newlocation'
# is err reg 'setfacl -R -m g:<GID>:rx /opt/apps/newlocation'
is out part ' is out part '
init-config-add init-config-add
| |
| $SERVICE_NAME: | $SERVICE_NAME:
| volumes: | volumes:
| - /opt/apps/newlocation:/var/www/www.example.com' RTRIM
| - "/opt/apps/newlocation:/var/www/www.example.com"' RTRIM

20
hedgedoc/README.org

@ -0,0 +1,20 @@
# -*- ispell-local-dictionary: "english" -*-
* How to reset password version 1.X :
#+begin_src sh
## 1. Access the server and then enter the container
docker exec -ti hedgedoc sh
## 2. use the script to manage users ./bin/manage_users
Command-line utility to create users for email-signin.
Usage: bin/manage_users [--pass password] (--add | --del) user-email
Options:
--add Add user with the specified user-email
--del Delete user with specified user-email
--reset Reset user password with specified user-email
--pass Use password from cmdline rather than prompting
#+end_src

1
letsencrypt/actions/crt

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
## compose: no-hooks
if [ -z "$SERVICE_DATASTORE" ]; then if [ -z "$SERVICE_DATASTORE" ]; then
echo "This script is meant to be run through 'compose' to work properly." >&2 echo "This script is meant to be run through 'compose' to work properly." >&2

15
mariadb/hooks/sql_database-relation-joined

@ -0,0 +1,15 @@
#!/bin/bash
## When writing relation script, remember:
## - they should be idempotents
## - they can be launched while the dockers is already up
## - they are launched from the host
## - the target of the link is launched first, and get a chance to ``relation-set``
## - both side of the scripts get to use ``relation-get``.
relation-set type mysql || {
err "Could not set relation ${WHITE}type${NORMAL} to 'mysql'."
exit 1
}
. hooks/mysql_database-relation-joined

1
mariadb/metadata.yml

@ -2,6 +2,7 @@ name: MariaDB
maintainer: "Valentin Lab <valentin.lab@kalysto.org>" maintainer: "Valentin Lab <valentin.lab@kalysto.org>"
provides: provides:
mysql-database: mysql-database:
sql-database:
data-resources: data-resources:
- /var/lib/mysql - /var/lib/mysql
- /var/backups/mysql - /var/backups/mysql

1
nextcloud/actions/occ

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
## compose: no-hooks
if [ -z "$SERVICE_DATASTORE" ]; then if [ -z "$SERVICE_DATASTORE" ]; then
echo "This script is meant to be run through 'compose' to work properly." >&2 echo "This script is meant to be run through 'compose' to work properly." >&2

1
nextcloud/hooks/init

@ -65,6 +65,7 @@ $MASTER_BASE_SERVICE_NAME:
NEXTCLOUD_ADMIN_USER: $admin_user NEXTCLOUD_ADMIN_USER: $admin_user
NEXTCLOUD_ADMIN_PASSWORD: $admin_password NEXTCLOUD_ADMIN_PASSWORD: $admin_password
NEXTCLOUD_DATA_DIR: /var/lib/nextcloud/data NEXTCLOUD_DATA_DIR: /var/lib/nextcloud/data
NEXTCLOUD_TRUSTED_DOMAINS: '\*'
" "
## ensuring data directories are accessible by nextcloud ## ensuring data directories are accessible by nextcloud

51
nextcloud/hooks/mysql_database-relation-joined

@ -1,51 +0,0 @@
#!/bin/bash
. lib/common
set -e
PASSWORD="$(relation-get password)"
USER="$(relation-get user)"
DBNAME="$(relation-get dbname)"
## This check adds purely arbitrary limits to what could be a password
## if we need to open that more, just consider the next script where we'll
## need to write in a PHP structure, or in YAML structure.
## Note that here, "[]" chars are not accepted just because it doesn't seem evident
## to test for those in bash.
if ! [[ "$PASSWORD" =~ ^[a-zA-Z0-9~\`\&+=@\#^\*/\\_%\$:\;\!?.,\<\>{}()\"\'|-]*$ ]]; then
err "Invalid password chosen for mysql database."
exit 1
fi
## if config is not existent
if [ -e "$CONFIGFILE" ] && grep "^ 'dbuser' => '" "$CONFIGFILE" >/dev/null; then
## 'occ' can't be used as it will try to connect to mysql before running and
## will fail if user/password is not correct
## We need to get through bash, and sed interpretation, then PHP single quoted strings.
quoted_user="${USER//\\/\\\\\\\\\\}"
quoted_user="${quoted_user//\'/\\\\\'}"
quoted_password="${PASSWORD//\\/\\\\\\\\\\}"
quoted_password="${quoted_password//\'/\\\\\'}"
sed -ri "s/^( 'dbuser' => ')(.*)(',)$/\1${quoted_user}\3/g;\
s/^( 'dbpassword' => ')(.*)(',)$/\1${quoted_password}\3/g;" "$CONFIGFILE"
else
## These variable are not used by current docker image after first install
config-add "\
services:
$MASTER_BASE_SERVICE_NAME:
environment:
MYSQL_HOST: $MASTER_TARGET_SERVICE_NAME
MYSQL_DATABASE: $DBNAME
MYSQL_PASSWORD: $PASSWORD
MYSQL_USER: $USER
"
fi
info "Configured $SERVICE_NAME code for $TARGET_SERVICE_NAME access."

1
nextcloud/hooks/mysql_database-relation-joined

@ -0,0 +1 @@
postgres_database-relation-joined

54
nextcloud/hooks/postgres_database-relation-joined

@ -1,51 +1,11 @@
#!/bin/bash #!/bin/bash
. lib/common
type="${0##*/}"
type="${type%_database-relation-joined}"
set -e
set-relation type "$type" || {
err "Could not set relation ${WHITE}type${NORMAL} to '$type'."
exit 1
}
PASSWORD="$(relation-get password)"
USER="$(relation-get user)"
DBNAME="$(relation-get dbname)"
## This check adds purely arbitrary limits to what could be a password
## if we need to open that more, just consider the next script where we'll
## need to write in a PHP structure, or in YAML structure.
## Note that here, "[]" chars are not accepted just because it doesn't seem evident
## to test for those in bash.
if ! [[ "$PASSWORD" =~ ^[a-zA-Z0-9~\`\&+=@\#^\*/\\_%\$:\;\!?.,\<\>{}()\"\'|-]*$ ]]; then
err "Invalid password chosen for postgres database."
exit 1
fi
## if config is not existent
if [ -e "$CONFIGFILE" ] && grep "^ 'dbuser' => '" "$CONFIGFILE" >/dev/null; then
## 'occ' can't be used as it will try to connect to postgres before running and
## will fail if user/password is not correct
## We need to get through bash, and sed interpretation, then PHP single quoted strings.
quoted_user="${USER//\\/\\\\\\\\\\}"
quoted_user="${quoted_user//\'/\\\\\'}"
quoted_password="${PASSWORD//\\/\\\\\\\\\\}"
quoted_password="${quoted_password//\'/\\\\\'}"
sed -ri "s/^( 'dbuser' => ')(.*)(',)$/\1${quoted_user}\3/g;\
s/^( 'dbpassword' => ')(.*)(',)$/\1${quoted_password}\3/g;" "$CONFIGFILE"
else
## These variable are not used by current docker image after first install
config-add "\
services:
$MASTER_BASE_SERVICE_NAME:
environment:
POSTGRES_HOST: $MASTER_TARGET_SERVICE_NAME
POSTGRES_DB: $DBNAME
POSTGRES_PASSWORD: $PASSWORD
POSTGRES_USER: $USER
"
fi
info "Configured $SERVICE_NAME code for $TARGET_SERVICE_NAME access."
. ./hooks/sql_database-relation-joined

75
nextcloud/hooks/sql_database-relation-joined

@ -0,0 +1,75 @@
#!/bin/bash
. lib/common
set -e
TYPE="$(relation-get type)" || {
err "No ${WHITE}type${NORMAL} set in relation."
exit 1
}
PASSWORD="$(relation-get password)"
USER="$(relation-get user)"
DBNAME="$(relation-get dbname)"
## This check adds purely arbitrary limits to what could be a password
## if we need to open that more, just consider the next script where we'll
## need to write in a PHP structure, or in YAML structure.
## Note that here, "[]" chars are not accepted just because it doesn't seem evident
## to test for those in bash.
if ! [[ "$PASSWORD" =~ ^[a-zA-Z0-9~\`\&+=@\#^\*/\\_%\$:\;\!?.,\<\>{}()\"\'|-]*$ ]]; then
err "Invalid password chosen for $type database."
exit 1
fi
## if config is not existent
if [ -e "$CONFIGFILE" ] && grep "^ 'dbuser' => '" "$CONFIGFILE" >/dev/null; then
## 'occ' can't be used as it will try to connect to db before running and
## will fail if user/password is not correct
## We need to get through bash, and sed interpretation, then PHP single quoted strings.
quoted_user="${USER//\\/\\\\\\\\\\}"
quoted_user="${quoted_user//\'/\\\\\'}"
quoted_password="${PASSWORD//\\/\\\\\\\\\\}"
quoted_password="${quoted_password//\'/\\\\\'}"
case "$TYPE" in
mysql)
nextcloud_type="mysql";;
postgres)
nextcloud_type="pgsql";;
*)
err "Unknown type '$TYPE' for database."
exit 1
;;
esac
sed -ri "s/^( 'dbuser' => ')(.*)(',)$/\1${quoted_user}\3/g;\
s/^( 'dbpassword' => ')(.*)(',)$/\1${quoted_password}\3/g;\
s/^( 'dbtype' => ')(.*)(',)$/\1${nextcloud_type}\3/g;\
s/^( 'dbhost' => ')(.*)(',)$/\1${MASTER_TARGET_SERVICE_NAME}\3/g;\
" "$CONFIGFILE"
else
## These variable are not used by current docker image after first install
if [ "$TYPE" == "mysql" ]; then
database_env_label="DATABASE"
else
database_env_label="DB"
fi
config-add "\
services:
$MASTER_BASE_SERVICE_NAME:
environment:
${TYPE^^}_HOST: $MASTER_TARGET_SERVICE_NAME
${TYPE^^}_${database_env_label}: $DBNAME
${TYPE^^}_PASSWORD: $PASSWORD
${TYPE^^}_USER: $USER
"
fi
info "Configured $SERVICE_NAME code for $TARGET_SERVICE_NAME access."

31
nextcloud/hooks/web_proxy-relation-joined

@ -1,33 +1,20 @@
#!/bin/bash #!/bin/bash
. lib/common
set -e set -e
DOMAIN=$(relation-get domain) || exit 1 DOMAIN=$(relation-get domain) || exit 1
URL="$(relation-get url)" || exit 1 URL="$(relation-get url)" || exit 1
PROTO="${URL%%://*}" PROTO="${URL%%://*}"
if ! trusted_domains="$(
compose -q --no-relations --no-init occ "$MASTER_BASE_SERVICE_NAME" \
config:system:get trusted_domains)"; then
err "Couldn't get 'trusted_domains'. Here's the ouput:"
echo "$trusted_domains" | prefix " | " >&2
echo "If the code of nextcloud is already there (command occ is found), but " >&2
echo "the database is not yet created, this situation will arise." >&2
nextcloud:config:simple:add overwritehost "$DOMAIN" || {
err "Failed to set ${WHITE}overwritehost${NORMAL} to '$DOMAIN'."
exit 1 exit 1
fi
}
occ_opts=(
## necessary as nextcloud do not detect correctly those, and behind
## a proxy, it will generate a lot of URL that are not detected
## by means of ``ReverseProxyPass`` on apache for instance
nextcloud:config:simple:add overwriteprotocol "$PROTO" || {
err "Failed to set ${WHITE}overwriteprotocol${NORMAL} to '$PROTO'."
exit 1
}
config:system:set overwritehost --value="$DOMAIN" \;
config:system:set overwriteprotocol --value="$PROTO"
)
if ! [[ $'\n'"$trusted_domains"$'\n' == *$'\n'"$MASTER_BASE_SERVICE_NAME"$'\n'* ]]; then
trusted_index=$(echo "$trusted_domains" | wc -l)
debug "Adding $MASTER_TARGET_SERVICE_NAME to ${WHITE}trusted_domains${NORMAL}."
occ_opts+=( \; config:system:set trusted_domains "$trusted_index" --value="$MASTER_BASE_SERVICE_NAME")
fi
compose --no-relations --no-init occ "$MASTER_BASE_SERVICE_NAME" "${occ_opts[@]}"

45
nextcloud/lib/common

@ -124,7 +124,50 @@ occ() {
-v "$HOST_CHARM_STORE/${CHARM_REL_PATH#${CHARM_STORE}/}/src/occ.batch:/var/www/html/occ.batch" \ -v "$HOST_CHARM_STORE/${CHARM_REL_PATH#${CHARM_STORE}/}/src/occ.batch:/var/www/html/occ.batch" \
-T --rm -u www-data "$SERVICE_NAME" /var/www/html/occ.batch "$@" | cat -T --rm -u www-data "$SERVICE_NAME" /var/www/html/occ.batch "$@" | cat
return "${PIPESTATUS[0]}"
if [ "${PIPESTATUS[0]}" != 0 ]; then
err "Failure to execute these ${WHITE}occ${NORMAL} commands:"
printf '%s ' "$@" |
sed -r "s/\\;/\n/g" |
sed -r "s/^\s*(.*)\s*$/${WHITE}\1${NORMAL}/g" |
prefix " ${DARKGRAY}>${NORMAL} " >&2
echo "" >&2
echo "" >&2
echo " If the code of nextcloud is already there (command occ is found), but " >&2
echo " the database is not yet created, this situation will arise." >&2
return "${PIPESTATUS[0]}"
fi
} }
nextcloud:config:simple:add() {
local key="$1" value="$2"
create_occ_if_not_exists || return 1
if ![ -e "$CONFIGFILE" ]; then
err "Config file '$CONFIGFILE' does not exist."
return 1
fi
if [ -z "$value" ]; then
err "Value for '$key' is empty. Skipping."
return 1
fi
## check for \ and ' in value and key
if [[ "$value" =~ [\\\'] ]]; then
err "Unsupported value for '$key' contains a backslash or a single quote."
return 1
fi
if [[ "$key" =~ [\\\'] ]]; then
err "Key '$key' contains a backslash or a single quote."
return 1
fi
if grep "^ '$key' => '" "$CONFIGFILE" >/dev/null; then
sed -ri "s/^( '$key' => ')(.*)(',)$/\1${value}\3/g" "$CONFIGFILE"
return 0
fi
## Add '$key' => 'value', to the end of the file, before the closing paren.
sed -ri "s/^(\);)$/ '$key' => '${value}',\n\1/g" "$CONFIGFILE"
}

11
nextcloud/metadata.yml

@ -7,25 +7,18 @@ config-resources:
provides: provides:
nextcloud-app: nextcloud-app:
uses: uses:
postgres-database:
sql-database:
#constraint: required | recommended | optional #constraint: required | recommended | optional
#auto: pair | summon | none ## default: pair #auto: pair | summon | none ## default: pair
constraint: required constraint: required
auto: summon auto: summon
solves: solves:
database: "main storage" database: "main storage"
mysql-database:
web-proxy:
#constraint: required | recommended | optional #constraint: required | recommended | optional
#auto: pair | summon | none ## default: pair #auto: pair | summon | none ## default: pair
constraint: optional constraint: optional
auto: pair auto: pair
solves:
database: "main storage"
web-proxy:
#constraint: required | recommended | optional
#auto: pair | summon | none ## default: pair
constraint: required
auto: summon
solves: solves:
proxy: "Public access" proxy: "Public access"
default-options: default-options:

40
odoo-tecnativa/README.org

@ -0,0 +1,40 @@
Odoo-tecnativa is a odoo image containing all source and add-ons because
we want to certify the whole image.
So this means there are no builds being managed by compose, and no injection
of code.
* Usage
** dbfilter
With image ~16.0~, an advanced version of ~dbfilter~ is installed. Here
a few examples:
#+begin_src yaml
odoo:
# ..
options:
dbfilter:
## DOMAIN_REGEX: DBFILTER
'^www.domain.org$': '^bar$' ## domain `www.domain.org` can only see `bar`.
'^foo\.': 'foo_.*' ## domain starting with `foo.` can see db `foo_`
'^(?P<name>[^.]+)\.': '%{name}s_.*' ## domain starting with `<PREFIX>.` can see db `PREFIX_`
'': 'other_.*' ## all domains can see db 'other_*'
## Don't forget to configure the domains in the web-proxy part !
relations:
web-proxy:
apache:
domain: www.domain.org
aliases:
- foo.otherdomain.com
- bar.wiz.eu
- test.domain.org
#+end_src
If there's only one database seen because of the ~dbfilter~, odoo will
use it by default.

8
odoo-tecnativa/README.rst

@ -1,8 +0,0 @@
Odoo-tecnativa is a odoo image containing all source and add-ons because
we want to certify the whole image.
So this means there are no builds being managed by compose, and no injection
of code.

39
odoo-tecnativa/hooks/init

@ -36,6 +36,39 @@ else
modules="base,${modules}" modules="base,${modules}"
fi fi
cmd_args=()
## dbfilter management
service_def=$(get_compose_service_def "$SERVICE_NAME") || return 1
dbfilter=$(e "$service_def" | yq e -r=false '.options.dbfilter') || true
if [ -n "$dbfilter" ]; then
type=$(e "$dbfilter" | yq e "type")
case "${type:2}" in
str)
dbfilter=$(e "$dbfilter" | yq e -o=json '.')
cmd_args+=("--db-filter=${dbfilter}")
;;
map)
python_expr="["
while read-0 key val; do
[[ "$key" == "null" ]] && key="None"
python_expr+="(${key//\$/\$\$}, ${val//\$/\$\$}),"
done < <(e "$dbfilter" | yq -o=json -0 'to_entries | .[] | [.key, .value] | .[]')
python_expr+="]"
cmd_args+=("--db-filter=${python_expr}")
;;
null)
:
;;
*)
err "Invalid value for ${WHITE}dbfilter${NORMAL}: ${dbfilter}" >&2
echo " It should be a string or a key/value mapping" >&2
exit 1
;;
esac
fi
## config file ## config file
conf=$(options-get conf 2>/dev/null) || true conf=$(options-get conf 2>/dev/null) || true
@ -65,4 +98,10 @@ $SERVICE_NAME:
command: command:
- \"--workers=${workers}\" - \"--workers=${workers}\"
- \"-i ${modules}\" - \"-i ${modules}\"
$(
for arg in "${cmd_args[@]}"; do
echo " - |"
e "$arg" | prefix " "
done
)
" "

16
postgres/hooks/sql_database-relation-joined

@ -0,0 +1,16 @@
#!/bin/bash
## When writing relation script, remember:
## - they should be idempotents
## - they can be launched while the dockers is already up
## - they are launched from the host
## - the target of the link is launched first, and get a chance to ``relation-set``
## - both side of the scripts get to use ``relation-get``.
relation-set type postgres || {
err "Could not set relation ${WHITE}type${NORMAL} to 'postgres'."
exit 1
}
. hooks/postgres_database-relation-joined

1
postgres/metadata.yml

@ -4,6 +4,7 @@ data-resources:
- /var/lib/postgresql/data - /var/lib/postgresql/data
provides: provides:
postgres-database: postgres-database:
sql-database:
uses: uses:
schedule-command: schedule-command:

3
rocketchat/README.org

@ -11,7 +11,8 @@ We are using official image. Latest tags usually.
** Test new version ** Test new version
Rocket.chat has a powerfull and working database update mecanism that Rocket.chat has a powerfull and working database update mecanism that
will take care of migrating database on startup.
will take care of migrating database on startup *as long as you jump only
from one major version to the next*.
*** Get latest available versions *** Get latest available versions

Loading…
Cancel
Save