#!/bin/sh -e
#
# 2015 Steven Armstrong (steven-cdist at armstrong.cc)
# 2015-2020 Nico Schottelius (nico-cdist at schottelius.org)
# 2019 Timothée Floure (timothee.floure at ungleich.ch)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#

os=$(cat "$__global/explorer/os")

###
# Type parameters.

state="$(cat "$__object/parameter/state")"
user="$(cat "$__object/parameter/user")"
group="$(cat "$__object/parameter/group")"
release=$(cat "$__global/explorer/lsb_release")
if [ -f "$__object/parameter/use-distribution-package" ]; then
  use_distribution_package=1
fi

###
# Those are default that might be overriden by os-specific logic.

data_dir="/var/lib/consul"



tls_dir="$conf_dir/tls"

case "$os" in
    alpine)
        conf_dir="/etc/consul"
        conf_file="server.json"
        ;;
    *)
        conf_dir="/etc/consul/conf.d"
        conf_file="config.json"
        ;;
esac

###
# Sane deployment, based on distribution package when available.

distribution_setup () {
  case "$os" in
     debian)
       # consul is only available starting Debian 10 (buster).
       # See https://packages.debian.org/buster/consul
       if [ "$release" -lt 10 ]; then
         echo "Consul is not available for your debian release." >&2
         echo "Please use the 'manual' (i.e. non-package) installation or \
           upgrade the target system." >&2
         exit 1
       fi

       # Override previously defined environment to match debian packaging.
       conf_dir='/etc/consul.d'
       user='consul'
       group='consul'
     ;;
     alpine)
       # consul is only available starting Alpine 3.12 (= edge during the 3.11 cycle).
       # See https://pkgs.alpinelinux.org/packages?name=consul&branch=edge

       # Override previously defined environment to match alpine packaging.
       conf_dir='/etc/consul'
       conf_file='server.json'
       data_dir='/var/consul'
       user='consul'
       group='consul'
     ;;
     *)
        echo "Your operating system ($os) is currently not supported with the \
          --use-distribution-package flag (${__type##*/})." >&2
        echo "Please use non-package installation or contribute an \
          implementation for if you can." >&2
        exit 1
     ;;
  esac

  # Install consul package.
  __package consul --state "$state"

  export config_deployment_requires="__package/consul"
}

###
# LEGACY manual deployment, kept for compatibility reasons.

init_sysvinit()
{
    __file /etc/init.d/consul \
        --owner root --group root --mode 0755 \
        --state "$state" \
        --source "$__type/files/consul.sysv-$1"
    require="__file/etc/init.d/consul" __start_on_boot consul
}

init_systemd()
{
    __file /lib/systemd/system/consul.service \
        --owner root --group root --mode 0644 \
        --state "$state" \
        --source "$__type/files/consul.systemd"
        require="__file/lib/systemd/system/consul.service" __start_on_boot consul
}

init_upstart()
{
    __file /etc/init/consul-prepare.conf \
        --owner root --group root --mode 0644 \
        --state "$state" \
        --source "$__type/files/consul-prepare.upstart"
    require="__file/etc/init/consul-prepare.conf" \
        __file /etc/init/consul.conf \
            --owner root --group root --mode 0644 \
            --state "$state" \
            --source "$__type/files/consul.upstart"
    require="__file/etc/init/consul.conf" __start_on_boot consul
}

manual_setup () {
  case "$os" in
     alpine|scientific|centos|debian|devuan|redhat|ubuntu)
        # whitelist safeguard
        :
     ;;
     *)
        echo "Your operating system ($os) is currently not supported by this \
          type (${__type##*/})." >&2
        echo "Please contribute an implementation for it if you can." >&2
        exit 1
     ;;
  esac

  # FIXME: there has got to be a better way to handle the dependencies in this case
  case "$state" in
     present)
        __group "$group" --system --state "$state"
        require="__group/$group" __user "$user" \
          --system --gid "$group" --home "$data_dir" --state "$state"
     ;;
     *)
        echo "The $state state is not (yet?) supported by this type." >&2
        exit 1
     ;;
  esac

  # Create data directory.
  require="__user/consul" __directory "$data_dir" \
    --owner "$user" --group "$group" --mode 770 --state "$state"

  # Create config directory.
  require="__user/consul" __directory "$conf_dir" \
    --parents --owner root --group "$group" --mode 750 --state "$state"

  # Install init script to start on boot
  case "$os" in
      devuan)
          init_sysvinit debian
          ;;
      centos|redhat)
          os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")"
          major_version="${os_version%%.*}"
          case "$major_version" in
              [456])
                  init_sysvinit redhat
                  ;;
              7)
                  init_systemd
                  ;;
              *)
                  echo "Unsupported CentOS/Redhat version: $os_version" >&2
                  exit 1
                  ;;
          esac
          ;;

      debian)
          os_version=$(cat "$__global/explorer/os_version")
          major_version="${os_version%%.*}"

          case "$major_version" in
              [567])
                  init_sysvinit debian
              ;;
              [89]|10)
                  init_systemd
              ;;
              *)
                  echo "Unsupported Debian version $os_version" >&2
                  exit 1
              ;;
          esac
          ;;

      ubuntu)
          init_upstart
          ;;
  esac

  config_deployment_requires="__user/consul __directory/$conf_dir"
}

###
# Trigger requested installation method.
if [ $use_distribution_package ]; then
  distribution_setup
else
  manual_setup
fi

###
# Install TLS certificates.

if [ -f "$__object/parameter/ca-file-source" ] || \
   [ -f "$__object/parameter/cert-file-source" ] || \
   [ -f "$__object/parameter/key-file-source" ]; then

   requires="$config_deployment_requires" __directory "$tls_dir" \
     --owner root --group "$group" --mode 750 --state "$state"

   # Append to service restart requirements.
   restart_requires="$restart_requires __directory/$conf_dir/tls"
fi

###
# Generate and deploy configuration.

json_configuration=$(
  echo "{"

  # parameters we define ourself
  printf '   "data_dir": "%s"\n' "$data_dir"

  cd "$__object/parameter/"
  for param in *; do
     case "$param" in
        state|user|group|json-config|use-distribution-package) continue ;;
        ca-file-source|cert-file-source|key-file-source)
           source="$(cat "$__object/parameter/$param")"
           destination="$tls_dir/${source##*/}"
           require="__directory/$tls_dir" \
              __file "$destination" \
                 --owner root --group consul --mode 640 \
                 --source "$source" \
                 --state "$state"
           key="$(echo "${param%-*}" | tr '-' '_')"
           printf '   ,"%s": "%s"\n' "$key" "$destination"
        ;;
        disable-remote-exec|disable-update-check|leave-on-terminate\
          |rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing)
           # handle boolean parameters
           key="$(echo "$param" | tr '-' '_')"
           printf '   ,"%s": true\n' "$key"
        ;;
        retry-join)
           # join multiple parameters into json array
           retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")"
           # remove trailing ,
           printf '   ,"retry_join": [%s]\n' "${retry_join%*,}"
        ;;
        retry-join-wan)
           # join multiple parameters into json array over wan
           retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")"
           # remove trailing ,
           printf '   ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}"
        ;;
        bootstrap-expect)
           # integer key=value parameters
           key="$(echo "$param" | tr '-' '_')"
           printf '   ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")"
        ;;
        *)
           # string key=value parameters
           key="$(echo "$param" | tr '-' '_')"
           printf '   ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")"
        ;;
     esac
  done
  if [ -f "$__object/parameter/json-config" ]; then
     json_config="$(cat "$__object/parameter/json-config")"
     if [ "$json_config" = "-" ]; then
        json_config="$__object/stdin"
     fi
     # remove leading and trailing whitespace and commas from first and last line
     # indent each line with 3 spaces for consistency
     json=$(sed -e 's/^[ \t]*/   /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config")
     printf '   ,%s\n' "$json"
  fi
  echo "}"
)
echo "$json_configuration" | require="$config_deployment_requires" \
  __file "$conf_dir/$conf_file" \
      --owner root --group "$group" --mode 640 \
      --state "$state" \
      --source -

# Set configuration deployment as requirement for service restart.
restart_requires="__file/$conf_dir/$conf_file"

###
# Restart consul agent after everything else.
require="$restart_requires" __service consul --action restart
