313 lines
8.4 KiB
Plaintext
313 lines
8.4 KiB
Plaintext
|
#!/bin/bash
|
||
|
# SOURCE: GRUNTWORKS
|
||
|
# This script can be used to install Consul and its dependencies. This script has been tested with the following
|
||
|
# operating systems:
|
||
|
#
|
||
|
# 1. Ubuntu 16.04
|
||
|
# 1. Ubuntu 18.04
|
||
|
# 1. Amazon Linux 2
|
||
|
|
||
|
set -e
|
||
|
|
||
|
readonly DEFAULT_INSTALL_PATH="/opt/consul"
|
||
|
readonly DEFAULT_CONSUL_USER="consul"
|
||
|
readonly DOWNLOAD_PACKAGE_PATH="/tmp/consul.zip"
|
||
|
|
||
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
readonly SYSTEM_BIN_DIR="/usr/local/bin"
|
||
|
|
||
|
readonly SCRIPT_NAME="$(basename "$0")"
|
||
|
|
||
|
function print_usage {
|
||
|
echo
|
||
|
echo "Usage: install-consul [OPTIONS]"
|
||
|
echo
|
||
|
echo "This script can be used to install Consul and its dependencies. This script has been tested with Ubuntu 16.04 and Amazon Linux 2."
|
||
|
echo
|
||
|
echo "Options:"
|
||
|
echo
|
||
|
echo -e " --version\t\tThe version of Consul to install. Optional if download-url is provided."
|
||
|
echo -e " --download-url\t\tUrl to exact Consul package to be installed. Optional if version is provided."
|
||
|
echo -e " --path\t\tThe path where Consul should be installed. Optional. Default: $DEFAULT_INSTALL_PATH."
|
||
|
echo -e " --user\t\tThe user who will own the Consul install directories. Optional. Default: $DEFAULT_CONSUL_USER."
|
||
|
echo -e " --ca-file-path\t\tPath to a PEM-encoded certificate authority used to encrypt and verify authenticity of client and server connections. Will be installed under <install-path>/tls/ca."
|
||
|
echo -e " --cert-file-path\t\tPath to a PEM-encoded certificate, which will be provided to clients or servers to verify the agent's authenticity. Will be installed under <install-path>/tls. Must be provided along with --key-file-path."
|
||
|
echo -e " --key-file-path\t\tPath to a PEM-encoded private key, used with the certificate to verify the agent's authenticity. Will be installed under <install-path>/tls. Must be provided along with --cert-file-path"
|
||
|
echo
|
||
|
echo "Example:"
|
||
|
echo
|
||
|
echo " install-consul --version 1.2.2"
|
||
|
}
|
||
|
|
||
|
function log {
|
||
|
local -r level="$1"
|
||
|
local -r message="$2"
|
||
|
local -r timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
||
|
>&2 echo -e "${timestamp} [${level}] [$SCRIPT_NAME] ${message}"
|
||
|
}
|
||
|
|
||
|
function log_info {
|
||
|
local -r message="$1"
|
||
|
log "INFO" "$message"
|
||
|
}
|
||
|
|
||
|
function log_warn {
|
||
|
local -r message="$1"
|
||
|
log "WARN" "$message"
|
||
|
}
|
||
|
|
||
|
function log_error {
|
||
|
local -r message="$1"
|
||
|
log "ERROR" "$message"
|
||
|
}
|
||
|
|
||
|
function assert_not_empty {
|
||
|
local -r arg_name="$1"
|
||
|
local -r arg_value="$2"
|
||
|
|
||
|
if [[ -z "$arg_value" ]]; then
|
||
|
log_error "The value for '$arg_name' cannot be empty"
|
||
|
print_usage
|
||
|
exit 1
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
function assert_either_or {
|
||
|
local -r arg1_name="$1"
|
||
|
local -r arg1_value="$2"
|
||
|
local -r arg2_name="$3"
|
||
|
local -r arg2_value="$4"
|
||
|
|
||
|
if [[ -z "$arg1_value" && -z "$arg2_value" ]]; then
|
||
|
log_error "Either the value for '$arg1_name' or '$arg2_name' must be passed, both cannot be empty"
|
||
|
print_usage
|
||
|
exit 1
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# A retry function that attempts to run a command a number of times and returns the output
|
||
|
function retry {
|
||
|
local -r cmd="$1"
|
||
|
local -r description="$2"
|
||
|
|
||
|
for i in $(seq 1 5); do
|
||
|
log_info "$description"
|
||
|
|
||
|
# The boolean operations with the exit status are there to temporarily circumvent the "set -e" at the
|
||
|
# beginning of this script which exits the script immediatelly for error status while not losing the exit status code
|
||
|
output=$(eval "$cmd") && exit_status=0 || exit_status=$?
|
||
|
log_info "$output"
|
||
|
if [[ $exit_status -eq 0 ]]; then
|
||
|
echo "$output"
|
||
|
return
|
||
|
fi
|
||
|
log_warn "$description failed. Will sleep for 10 seconds and try again."
|
||
|
sleep 10
|
||
|
done;
|
||
|
|
||
|
log_error "$description failed after 5 attempts."
|
||
|
exit $exit_status
|
||
|
}
|
||
|
|
||
|
function has_yum {
|
||
|
[ -n "$(command -v yum)" ]
|
||
|
}
|
||
|
|
||
|
function has_apt_get {
|
||
|
[ -n "$(command -v apt-get)" ]
|
||
|
}
|
||
|
|
||
|
function install_dependencies {
|
||
|
log_info "Installing dependencies"
|
||
|
|
||
|
if $(has_apt_get); then
|
||
|
sudo apt-get update -y
|
||
|
sudo apt-get install -y awscli curl unzip jq
|
||
|
elif $(has_yum); then
|
||
|
sudo yum update -y
|
||
|
sudo yum install -y aws curl unzip jq
|
||
|
else
|
||
|
log_error "Could not find apt-get or yum. Cannot install dependencies on this OS."
|
||
|
exit 1
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
function user_exists {
|
||
|
local -r username="$1"
|
||
|
id "$username" >/dev/null 2>&1
|
||
|
}
|
||
|
|
||
|
function create_consul_user {
|
||
|
local -r username="$1"
|
||
|
|
||
|
if $(user_exists "$username"); then
|
||
|
echo "User $username already exists. Will not create again."
|
||
|
else
|
||
|
log_info "Creating user named $username"
|
||
|
sudo useradd "$username"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
function create_consul_install_paths {
|
||
|
local -r path="$1"
|
||
|
local -r username="$2"
|
||
|
|
||
|
log_info "Creating install dirs for Consul at $path"
|
||
|
sudo mkdir -p "$path"
|
||
|
sudo mkdir -p "$path/bin"
|
||
|
sudo mkdir -p "$path/config"
|
||
|
sudo mkdir -p "$path/data"
|
||
|
sudo mkdir -p "$path/tls/ca"
|
||
|
|
||
|
log_info "Changing ownership of $path to $username"
|
||
|
sudo chown -R "$username:$username" "$path"
|
||
|
}
|
||
|
|
||
|
function fetch_binary {
|
||
|
local -r version="$1"
|
||
|
local download_url="$2"
|
||
|
|
||
|
if [[ -z "$download_url" && -n "$version" ]]; then
|
||
|
download_url="https://releases.hashicorp.com/consul/${version}/consul_${version}_linux_amd64.zip"
|
||
|
fi
|
||
|
|
||
|
retry \
|
||
|
"curl -o '$DOWNLOAD_PACKAGE_PATH' '$download_url' --location --silent --fail --show-error" \
|
||
|
"Downloading Consul to $DOWNLOAD_PACKAGE_PATH"
|
||
|
}
|
||
|
|
||
|
function install_binary {
|
||
|
local -r install_path="$1"
|
||
|
local -r username="$2"
|
||
|
|
||
|
local -r bin_dir="$install_path/bin"
|
||
|
local -r consul_dest_path="$bin_dir/consul"
|
||
|
local -r run_consul_dest_path="$bin_dir/run-consul"
|
||
|
|
||
|
unzip -d /tmp "$DOWNLOAD_PACKAGE_PATH"
|
||
|
|
||
|
log_info "Moving Consul binary to $consul_dest_path"
|
||
|
sudo mv "/tmp/consul" "$consul_dest_path"
|
||
|
sudo chown "$username:$username" "$consul_dest_path"
|
||
|
sudo chmod a+x "$consul_dest_path"
|
||
|
|
||
|
local -r symlink_path="$SYSTEM_BIN_DIR/consul"
|
||
|
if [[ -f "$symlink_path" ]]; then
|
||
|
log_info "Symlink $symlink_path already exists. Will not add again."
|
||
|
else
|
||
|
log_info "Adding symlink to $consul_dest_path in $symlink_path"
|
||
|
sudo ln -s "$consul_dest_path" "$symlink_path"
|
||
|
fi
|
||
|
|
||
|
log_info "Copying Consul run script to $run_consul_dest_path"
|
||
|
sudo cp "$SCRIPT_DIR/run-consul" "$run_consul_dest_path"
|
||
|
sudo chown "$username:$username" "$run_consul_dest_path"
|
||
|
sudo chmod a+x "$run_consul_dest_path"
|
||
|
}
|
||
|
|
||
|
function install_tls_certificates {
|
||
|
local -r path="$1"
|
||
|
local -r user="$2"
|
||
|
local -r ca_file_path="$3"
|
||
|
local -r cert_file_path="$4"
|
||
|
local -r key_file_path="$5"
|
||
|
|
||
|
local -r consul_tls_certs_path="$path/tls"
|
||
|
local -r ca_certs_path="$consul_tls_certs_path/ca"
|
||
|
|
||
|
log_info "Moving TLS certs to $consul_tls_certs_path and $ca_certs_path"
|
||
|
|
||
|
sudo mkdir -p "$ca_certs_path"
|
||
|
sudo mv "$ca_file_path" "$ca_certs_path/"
|
||
|
sudo mv "$cert_file_path" "$consul_tls_certs_path/"
|
||
|
sudo mv "$key_file_path" "$consul_tls_certs_path/"
|
||
|
|
||
|
sudo chown -R "$user:$user" "$consul_tls_certs_path/"
|
||
|
sudo find "$consul_tls_certs_path/" -type f -exec chmod u=r,g=,o= {} \;
|
||
|
}
|
||
|
|
||
|
function install {
|
||
|
local version=""
|
||
|
local download_url=""
|
||
|
local path="$DEFAULT_INSTALL_PATH"
|
||
|
local user="$DEFAULT_CONSUL_USER"
|
||
|
local ca_file_path=""
|
||
|
local cert_file_path=""
|
||
|
local key_file_path=""
|
||
|
|
||
|
while [[ $# > 0 ]]; do
|
||
|
local key="$1"
|
||
|
|
||
|
case "$key" in
|
||
|
--version)
|
||
|
version="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--download-url)
|
||
|
download_url="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--path)
|
||
|
path="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--user)
|
||
|
user="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--ca-file-path)
|
||
|
assert_not_empty "$key" "$2"
|
||
|
ca_file_path="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--cert-file-path)
|
||
|
assert_not_empty "$key" "$2"
|
||
|
cert_file_path="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--key-file-path)
|
||
|
assert_not_empty "$key" "$2"
|
||
|
key_file_path="$2"
|
||
|
shift
|
||
|
;;
|
||
|
--help)
|
||
|
print_usage
|
||
|
exit
|
||
|
;;
|
||
|
*)
|
||
|
log_error "Unrecognized argument: $key"
|
||
|
print_usage
|
||
|
exit 1
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
shift
|
||
|
done
|
||
|
|
||
|
assert_either_or "--version" "$version" "--download-url" "$download_url"
|
||
|
assert_not_empty "--path" "$path"
|
||
|
assert_not_empty "--user" "$user"
|
||
|
|
||
|
log_info "Starting Consul install"
|
||
|
|
||
|
install_dependencies
|
||
|
create_consul_user "$user"
|
||
|
create_consul_install_paths "$path" "$user"
|
||
|
|
||
|
fetch_binary "$version" "$download_url"
|
||
|
install_binary "$path" "$user"
|
||
|
|
||
|
if [[ -n "$ca_file_path" || -n "$cert_file_path" || -n "$key_file_path" ]]; then
|
||
|
install_tls_certificates "$path" "$user" "$ca_file_path" "$cert_file_path" "$key_file_path"
|
||
|
fi
|
||
|
|
||
|
if command -v consul; then
|
||
|
log_info "Consul install complete!";
|
||
|
else
|
||
|
log_info "Could not find consul command. Aborting.";
|
||
|
exit 1;
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
install "$@"
|