#!/usr/bin/env bash
# Install Puppet Agent bootstrap script

# ---------------------------------------------------------------------------
#                                                                 usage
# ---------------------------------------------------------------------------

script=$(basename $0)

USAGE="\
Usage:  $script -h
        $script [--reconf] [server [CA]]
Where:  -h|--help Invokes help text
        --reconf  Specifies reconfiguration even if an existing Puppet install
                  is detected
	--6	  Install Puppet 6 instead of 7
        server    Refers to the Puppet server this host will use
                  (default: puppet.[this host's domain])
        CA        Refers to the Puppet certificate authority this host will use
                  (default: puppet.computecanada.ca)
"

usage()
{
  echo "$USAGE"
  exit 0
}


# ---------------------------------------------------------------------------
#                                                                 functions
# ---------------------------------------------------------------------------

# Display current status
--()
{
  echo "$@"
}

# Fatal error; bail
fatal()
{
  echo "$1" >> /dev/stderr
  exit 1
}

# This is for catching unexpected errors.  Bash's implementation of this is
# problematic, but it's a last-ditch catchall for errors.
trap_err()
{
  rc=$1
  lineno=$2
  func=$3
  cat <<EOF >>/dev/stderr
Unexpected failure of subshell command.  Preceeding lines may offer some clue.
Please consider updating the script with adequate testing.

Exit status: $rc
Line:        $lineno
Function:    $func
EOF
}

# Ubuntu is a little more complicated than RH variants because apt and dpkg
# do not accept URLs as packages
ubuntu_install()
{
  url=$1

  tmpf=$(mktemp) || fatal "Could not create temporary file for package download"
  curl $url -o $tmpf || fatal "Could not download Puppet repo package from '$url' to '$tmpf'"
  dpkg -i $tmpf || fatal "Could not install Puppet repo package"
  rm $tmpf
  apt update
}

# ---------------------------------------------------------------------------
#                                                                 init
# ---------------------------------------------------------------------------

# exit immediately if a pipeline, subshell command or command enclosed in 
# braces fails
set -e

# and should that happen, print out a message with any available information
trap 'trap_err $? $LINENO ${FUNCNAME}' ERR

# interpret parameters
if [ "$1" == "-h" -o "$1" == "--help" ]
then
  usage
elif [ "$1" == "--6" ]
then
 pup6=1
 shift
elif [ "$1" == "--reconf" ]
then
  reconf=1
  shift
fi

# If they don't pass a puppet server, assume it's puppet.DOMAIN
PUPPET_DOMAIN=$(hostname -d) || fatal "Could not determine host's domain."
if [ -n "$1" ]
then
  PUPPET_SERVER=$1
else
  PUPPET_SERVER="puppet.${PUPPET_DOMAIN}"
fi

# Puppet CA server defaults to puppet.computecanada.ca
if [ -n "$2" ]
then
  PUPPET_CA_SERVER=$2
else
  PUPPET_CA_SERVER="puppet.computecanada.ca"
fi

# ---------------------------------------------------------------------------
#                                                          preflight checks
# ---------------------------------------------------------------------------

# get OS information if available
OSINFO=/etc/os-release
if [ -f $OSINFO ]
then
  . $OSINFO
else
  fatal "Could not find $OSINFO; what are you running?"
fi

cat <<EOF
We're going to configure the agent like so:

Puppet server:    $PUPPET_SERVER
Puppet CA server: $PUPPET_CA_SERVER

EOF

# RedHat uses VERSION_ID=8.3, CentOS uses VERSION_ID=8
if [ -n "$VERSION_ID" ]
then
  VERSION_ID_MAJOR=${VERSION_ID%.*}
fi

if (( $pup6 ))
then
 echo "Puppet 6 selected"
 YUM_REPO_URL="https://yum.puppet.com/puppet6-release-el-${VERSION_ID_MAJOR}.noarch.rpm"
 APT_REPO_URL="https://apt.puppet.com/puppet6-release-${VERSION_CODENAME}.deb"
else
 echo "Puppet 7 selected"
 YUM_REPO_URL="https://yum.puppet.com/puppet7-release-el-${VERSION_ID_MAJOR}.noarch.rpm"
 APT_REPO_URL="https://apt.puppet.com/puppet7-release-${VERSION_CODENAME}.deb"

fi
 
# determine what repo we should be using and commands to run
case "$ID" in
  rocky|almalinux|centos|rhel)
    REPO_URL=$YUM_REPO_URL
    CHECK_PUPPET="rpm --quiet -q puppet-agent"
    INSTALL_REPO="yum -y install ${REPO_URL}"
    INSTALL_PUPPET="yum -y install puppet-agent"
    ;;
  ubuntu)
    REPO_URL=$APT_REPO_URL
    #CHECK_PUPPET="dpkg -l puppet >/dev/null 2>&1"
    CHECK_PUPPET="dpkg-query -Wf'\${db:Status-abbrev}' puppet 2>/dev/null | grep -q '^i'"
    INSTALL_REPO="ubuntu_install ${REPO_URL}"
    INSTALL_PUPPET="apt install -y puppet"
    ;;
  *)
    fatal "Unrecognized Linux distribution: no configured Puppet repository"
    ;;
esac

# Check and see if puppet is installed
if eval $CHECK_PUPPET
then
  if (( $reconf ))
  then
    echo "WARNING: Puppet appears to be installed; reconfiguring"
  else
    fatal "WARNING: Puppet appears to be installed and may already be configured"
  fi
fi

# Make sure that they are root
if [ "$EUID" -ne "0" ]
then
  fatal "You must be root to install Puppet."
fi

# ---------------------------------------------------------------------------
#                                                                 main
# ---------------------------------------------------------------------------

if ! (( $reconf ))
then

  -- Installing release package for Puppet repository
  if ! $INSTALL_REPO
  then
    fatal "Unable to install release package from $REPO_URL"
  fi

  -- Installing Puppet Agent
  if ! $INSTALL_PUPPET
  then
    fatal "Unable to install Puppet"
  fi
fi

-- Updating path to include Puppet
export PATH=/opt/puppetlabs/bin:$PATH

-- Configuring Agent for initial Run
if ! puppet config set server ${PUPPET_SERVER} --section agent 
then
  fatal "Could not configure agent (setting server)"
fi

# Only set the proxy if we aren't connecting directly to the MOM
if [ $PUPPET_SERVER != "puppet.computecanada.ca" ]
then
  # Adding HTTP Proxy via ${PUPPET_SERVER}
  puppet config set http_proxy_host ${PUPPET_SERVER} --section agent \
    || fatal "Could not configure agent (http_proxy_host)"
  puppet config set http_proxy_port 3128 --section agent \
    || fatal "Could not configure agent (http_proxy_port)"
fi
puppet config set ca_server ${PUPPET_CA_SERVER} --section agent \
  || fatal "Could not configure agent (CA server)"

-- Puppet agent configured for Compute Canada
cat <<EOF

This host is now configured for its initial run against the Compute Canada
Puppet infrastructure.  Next, to generate a certificate for subsequent runs,
please run:

  sudo puppet agent --test --noop

Request on Slack in #puppet for an admin to sign the certificate, and then
you will be able to run:

  sudo puppet agent --test

To start the first "real" Puppet run.

Please see https://wiki.computecanada.ca/staff/Puppet/Installation for more
information.

EOF

