#!/bin/bash
# Filename:      /usr/share/ngcp-ngcpcfg/helper/build_config
# Purpose:       builds output configuration file based on tt2 template file
#                using /usr/share/ngcp-ngcpcfg/helper/tt2-wrapper
################################################################################

set -e
set -u

if [ "${#:-}" -ne 1 ] ; then
  echo "Usage: /usr/share/ngcp-ngcpcfg/helper/build_config <input_file>" >&2
  exit 1
fi

# support for testsuite, assume defaults if unset
CONFIG_POOL="${CONFIG_POOL:-/etc}"
FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}"
HELPER="${HELPER:-/usr/share/ngcp-ngcpcfg/helper/}"

if ! [ -r "${FUNCTIONS}"/main ] ; then
  printf "Error: %s/main could not be read. Exiting.\n" "${FUNCTIONS}" >&2
  exit 1
fi

. "${FUNCTIONS}"/main

# main script

input_file="$1"       # like /etc/ngcp-config/templates/etc/mysql/my.cnf.tt2
# calculate output file {{{
  x=${input_file##${NGCPCTL_MAIN}/templates/}         # drop leading /etc/ngcp-config
  y=${x%%.tt2}                                        # drop trailing suffix '.tt2'
  [ -n "${HA_FILE:-}" ]   && y=${y%%.tt2${HA_FILE}}   # drop trailing suffix '.tt2.sp{1,2}'
  [ -n "${PAIR_FILE:-}" ] && y=${y%%.tt2${PAIR_FILE}} # drop trailing suffix '.tt2.pairname'
  [ -n "${HOST_FILE:-}" ] && y=${y%%.tt2${HOST_FILE}} # drop trailing suffix '.tt2.hostname'
  y=${y%%.customtt}                                   # drop trailing suffix '.customtt'
# export variable for usage within {pre,post}build scripts,
# OUTPUT_DIRECTORY is for customization during testing
if [ -n "${OUTPUT_DIRECTORY:-}" ] ; then
  log_debug "Using output directory $OUTPUT_DIRECTORY"
  export output_file="${OUTPUT_DIRECTORY}/${y}"
else
  export output_file="${y}"
fi
# }}}

# ensure we don't try to generate a file where a directory with same name exists already
if [ -d "${output_file}" ] ; then
  log_error "Generating file ${output_file} not possible, it's an existing directory." >&2
  exit 1
fi

# pre-execution script in template store:
if [ -r "${NGCPCTL_MAIN}/templates/${output_file}.prebuild" ] ; then
  log_info "Executing prebuild for ${output_file}"
  bash "${NGCPCTL_MAIN}/templates/${output_file}.prebuild"
elif [ -r "${NGCPCTL_MAIN}/templates/$(dirname "${output_file}")/ngcpcfg.prebuild" ] ; then
  log_info "Executing prebuild for ${output_file}"
  bash "${NGCPCTL_MAIN}/templates/$(dirname "${output_file}")/ngcpcfg.prebuild"
fi

# if output directory does not exist yet, create it
if ! [ -d "$(dirname "${output_file}")" ] ; then
  umask 0022 # directory permissions should be '755'
  mkdir -p "$(dirname "${output_file}")"
fi

# assume safe defaults
umask 0077

# read host specific configuration file only if it exists
[ -r "${HOST_CONFIG:-}" ] && host_conf="$HOST_CONFIG"

# read local config only if it exists
[ -r "${LOCAL_CONFIG:-}" ] && local_conf="$LOCAL_CONFIG"

TT_WRAPPER="${HELPER}/tt2-wrapper"

# generate in a temporary location, then move if successful
tt_tmp_output_file_base="$(basename "${output_file}")"
tt_tmp_output_file="$(mktemp --tmpdir ngcpcfg."${tt_tmp_output_file_base}".XXXXXXXXXX)"

# XXX: Docker breaks sane Unix expectations when moving a file into /etc/hosts,
# as it creates a bind mount on that pathname. We need to use an implementation
# that will fallback to use copy semantics in that case, but will default to
# use rename semantics to avoid races on ETXTBSY on executable files.
# <https://github.com/moby/moby/issues/22281>
move()
{
  local src="$1"
  local dst="$2"

  perl -MFile::Copy=mv \
       -E "mv('${src}', '${dst}') or die 'error: cannot move $src to $dst: \$!\n'"

  rm -f "${src}"
}

log_debug "Output file ${output_file} based on ${input_file}"
log_debug "Executing: $TT_WRAPPER ${input_file} > ${tt_tmp_output_file}"
log_debug "      and: move ${tt_tmp_output_file} ${output_file}"
# We need to use «readlink -f» so that we do not destroy any symlink pointing
# to the real file, which we were previously preserving while using «cat».
if "$TT_WRAPPER" "${input_file}" > "${tt_tmp_output_file}" 2>/dev/null &&
   ! grep -q -E '^file error' "${tt_tmp_output_file}" 2>/dev/null &&
   move "${tt_tmp_output_file}" "$(readlink -f "${output_file}")" ; then
  log_info "Generating ${output_file}: OK"
  RC=0
else
  log_error "Generating ${output_file} based on ${input_file}: FAILED"
  RC=1

  if [[ -r "${tt_tmp_output_file}" ]] && grep -q -E '^file error' "${tt_tmp_output_file}" ; then
    log_error "from generated file:"
    log_error "  $(grep -E '^file error' "${tt_tmp_output_file}")"
  fi

  log_info "NOTE: Check those files for valid syntax and encoding:"
  for f in "${input_file}" ${host_conf:-} ${local_conf:-} "$NGCPCTL_CONFIG" "${NETWORK_CONFIG:-}" ${EXTRA_CONFIG_FILES:-} "$CONSTANTS_CONFIG" ; do
    [ -r "$f" ] && log_info "$f"
  done
  log_info "Running /usr/share/ngcp-ngcpcfg/helper/tt2-wrapper <file>"
  log_info "or inspecting temporary ${tt_tmp_output_file}"
  log_info "should provide more details."
fi

if [ -L "$output_file" ] ; then
  log_warn "File $output_file is a symlink - NOT adjusting permissions"
else
  # set permissions for generated config based on the ones of the template
  chmod --reference="${input_file}" "${output_file}"
  # finally drop all write permissions
  chmod a-w "${output_file}"
fi

# post-execution script in template store:
if [ -r "${NGCPCTL_MAIN}/templates/${output_file}.postbuild" ] ; then
  log_info "Executing postbuild for ${output_file}"
  bash "${NGCPCTL_MAIN}/templates/${output_file}.postbuild"
elif [ -r "${NGCPCTL_MAIN}/templates/$(dirname "${output_file}")/ngcpcfg.postbuild" ] ; then
  log_info "Executing postbuild for ${output_file}"
  bash "${NGCPCTL_MAIN}/templates/$(dirname "${output_file}")/ngcpcfg.postbuild"
fi

exit $RC

## END OF FILE #################################################################
