Skip to content

Instantly share code, notes, and snippets.

@double-z
Created August 1, 2017 05:41
Show Gist options
  • Save double-z/ff7c60613712f81a7c4bb7f07256daad to your computer and use it in GitHub Desktop.
Save double-z/ff7c60613712f81a7c4bb7f07256daad to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# BASH script to install DC/OS on a node
#
# Usage:
#
# dcos_install.sh <role>...
#
#
# Metadata:
# dcos image commit: af6ddc2f5e95b1c1d9bd9fd3d3ef1891928136b9
# generation date: 2017-07-31 22:46:11.112364
#
# Copyright 2016 Mesosphere, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit -o nounset -o pipefail
declare -i OVERALL_RC=0
declare -i PREFLIGHT_ONLY=0
declare -i DISABLE_PREFLIGHT=0
declare -i SYSTEMCTL_NO_BLOCK=0
declare ROLES=""
declare RED=""
declare BOLD=""
declare NORMAL=""
# Check if this is a terminal, and if colors are supported, set some basic
# colors for outputs
if [ -t 1 ]; then
colors_supported=$(tput colors)
if [[ $colors_supported -ge 8 ]]; then
RED='\e[1;31m'
BOLD='\e[1m'
NORMAL='\e[0m'
fi
fi
# Setup getopt argument parser
ARGS=$(getopt -o dph --long "disable-preflight,preflight-only,help,no-block-dcos-setup" -n "$(basename "$0")" -- "$@")
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
function setup_directories() {
echo -e "Creating directories under /etc/mesosphere"
mkdir -p /etc/mesosphere/roles
mkdir -p /etc/mesosphere/setup-flags
}
function setup_dcos_roles() {
# Set DC/OS roles
for role in $ROLES
do
echo "Creating role file for ${role}"
touch "/etc/mesosphere/roles/$role"
done
}
# Set DC/OS machine configuration
function configure_dcos() {
echo -e 'Configuring DC/OS'
mkdir -p `dirname /etc/mesosphere/setup-flags/repository-url`
cat <<'EOF' > "/etc/mesosphere/setup-flags/repository-url"
http://192.168.65.50
EOF
chmod 0644 /etc/mesosphere/setup-flags/repository-url
mkdir -p `dirname /etc/mesosphere/setup-flags/cluster-packages.json`
cat <<'EOF' > "/etc/mesosphere/setup-flags/cluster-packages.json"
["3dt--caa28740ba55b7f6de50ab574e91a3f087701e7a", "adminrouter--a4c1e2c311b66185e3fc26350ef55086a4434b3c", "avro-cpp--b705160facaef4071e6e1999d4a3f10b6bebddfc", "boost-libs--2015ccb58fb756f61c02ee6aa05cc1e27459a9ec", "bootstrap--d8cdde7d671f2ef25087c2941e28d60049f168dd", "boto--6344d31eef082c7bd13259b17034ea7b5c34aedf", "check-time--be7d0ba757ec87f9965378fee7c76a6ee5ae996d", "cni--e48337da39a8cd379414acfe0da52a9226a10d24", "cosmos--e51eff0287fa62a5c3f119bf49596f616eafad72", "curl--fc3486c43f98e63f9b12675f1356e8fe842f26b0", "dcos-config--setup_81e0aea0bfb0988c93044950f259bc64beec9e17", "dcos-history--f059725ebe14a980c1b7d889055d78c56720d737", "dcos-image--e42f099fbe4d35adb7ae10316e6e88f308a08397", "dcos-image-deps--83584fd868e5b470f7cf754424a9a75b328e9b68", "dcos-integration-test--3acd68afab37740ef5e19c1658f7c7d6e3ae073a", "dcos-log--4d630df863228f38c6333e44670b4c4b20a74832", "dcos-metadata--setup_81e0aea0bfb0988c93044950f259bc64beec9e17", "dcos-metrics--3f9c56b0c3aef016427b723a7d2e4e6afdf9c04e", "dcos-oauth--0079529da183c0f23a06d2b069721b6fa6cc7b52", "dcos-signal--5633dc8da7e864cb34e3d29ed13e6756c7a6df94", "dcos-ui--7693dca6567e31cc107b765ce6612ae48a417528", "dnspython--0f833eb9a8abeba3179b43f3a200a8cd42d3795a", "docker-gc--59a98ed6446a084bf74e4ff4b8e3479f59ea8528", "dvdcli--5374dd4ffb519f1dcefdec89b2247e3404f2e2e3", "erlang--a9ee2530357a3301e53056b36a93420847b339a3", "exhibitor--6be3f543dbed3abbc22218fd703e52609bc49a67", "flask--26d1bcdb2d1c3dcf1d2c03bc0d4f29c86d321b21", "java--cd5e921ce66b0d3303883c06d73a657314044304", "libevent--208be855d2be29c9271a7bd6c04723ff79946e02", "libffi--83ce3bd7eda2ef089e57efd2bc16c144d5a1f094", "libsodium--9ff915db08c6bba7d6738af5084e782b13c84bf8", "logrotate--7f7bc4416d3ad101d0c5218872858483b516be07", "marathon--8f59f94aaa21adbe497e0f49a8d0bb41515968db", "mesos--8672838b73e866c786446d1a4d7afce701f7df62", "mesos-dns--f8c80e9bffa1fe238711f82fa06006de8715cceb", "mesos-modules--6680e631c9689dd33f1115bdae3d31fafe70051f", "metronome--6eafabd93182e8844c72ce19690e93d2b7926931", "navstar--0b141af667446bbe42069fbdba130276f872061c", "ncurses--d889894b71aa1a5b311bafef0e85479025b4dacb", "octarine--dada18ca28c1b7948ed895580cc0a59d5b3c5583", "openssl--b01a32a42e3ccba52b417276e9509a441e1d4a82", "pkgpanda-api--1acf427af3dd51b7bd56176bce3e8a6580489219", "pkgpanda-role--f8a749a4a821476ad2ef7e9dd9d12b6a8c4643a4", "pytest--78aee3e58a049cdab0d266af74f77d658b360b4f", "python--b7a144a49577a223d37d447c568f51330ee95390", "python-azure-mgmt-resource--03c05550f43b0e7a4455c33fe43b0deb755d87f0", "python-cryptography--4184767c68e48801dd394072cb370c610a05029d", "python-dateutil--fdc6ff929f65dd0918cf75a9ad56704683d31781", "python-docopt--beba78faa13e5bf4c52393b4b82d81f3c391aa65", "python-gunicorn--a537f95661fb2689c52fe12510eb0d01cb83af60", "python-isodate--40d378c688e6badfd16676dd8b51b742bfebc8d5", "python-jinja2--7450f5ae5a822f63f7a58c717207be0456df51ed", "python-kazoo--cb7ce13a1068cd82dd84ea0de32b529a760a4bdd", "python-markupsafe--dd46d2a3c58611656a235f96d4adc51b2a7a590e", "python-passlib--802ec3605c0b82428fedba60983b1bafaa036bb8", "python-pyyaml--81dd44cc4a24db7cefa7016c6586a131acf279c3", "python-requests--1b2cadbd3811cc0c2ee235ce927e13ea1d6af41d", "python-retrying--eb7b8bac133f50492b1e1349cbe77c3e38bd02c3", "python-tox--07244f8a939a10353634c952c6d88ec4a3c05736", "rexray--f07795e2c10f9a1a27de9d8e67ab171029db2e1d", "six--f06424b68523c4dfa2a7c3e7475d479f3d361e42", "spartan--daba6f5a190e67874b3aa852601ba039ecbab039", "strace--7d01796d64994451c1b2b82d161a335cbe90569b", "teamcity-messages--e623a4d86eb3a8d199cefcc240dd4c5460cb2962", "toybox--f235594ab8ea9a2864ee72abe86723d76f92e848"]
EOF
chmod 0644 /etc/mesosphere/setup-flags/cluster-packages.json
mkdir -p `dirname /etc/systemd/journald.conf.d/dcos.conf`
cat <<'EOF' > "/etc/systemd/journald.conf.d/dcos.conf"
[Journal]
MaxLevelConsole=warning
RateLimitInterval=1s
RateLimitBurst=20000
EOF
chmod 0644 /etc/systemd/journald.conf.d/dcos.conf
mkdir -p `dirname /etc/rexray/config.yml`
cat <<'EOF' > "/etc/rexray/config.yml"
rexray:
loglevel: info
modules:
default-admin:
host: tcp://127.0.0.1:61003
default-docker:
disabled: true
EOF
chmod 0644 /etc/rexray/config.yml
}
# Install the DC/OS services, start DC/OS
function setup_and_start_services() {
echo -e 'Setting and starting DC/OS'
mkdir -p `dirname /etc/systemd/system/dcos-link-env.service`
cat <<'EOF' > "/etc/systemd/system/dcos-link-env.service"
[Unit]
Before=dcos.target
[Service]
Type=oneshot
StandardOutput=journal+console
StandardError=journal+console
ExecStartPre=/usr/bin/mkdir -p /etc/profile.d
ExecStart=/usr/bin/ln -sf /opt/mesosphere/bin/add_dcos_path.sh /etc/profile.d/dcos.sh
EOF
chmod 0644 /etc/systemd/system/dcos-link-env.service
mkdir -p `dirname /etc/systemd/system/dcos-download.service`
cat <<'EOF' > "/etc/systemd/system/dcos-download.service"
[Unit]
Description=Pkgpanda: Download DC/OS to this host.
After=network-online.target
Wants=network-online.target
ConditionPathExists=!/opt/mesosphere/
[Service]
Type=oneshot
StandardOutput=journal+console
StandardError=journal+console
ExecStartPre=/usr/bin/curl --keepalive-time 2 -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/bootstrap.tar.xz http://192.168.65.50/bootstrap/79a0dfe0944948a33ab75f6e62335f166e117f3d.bootstrap.tar.xz
ExecStartPre=/usr/bin/mkdir -p /opt/mesosphere
ExecStart=/usr/bin/tar -axf /tmp/bootstrap.tar.xz -C /opt/mesosphere
ExecStartPost=-/usr/bin/rm -f /tmp/bootstrap.tar.xz
EOF
chmod 0644 /etc/systemd/system/dcos-download.service
mkdir -p `dirname /etc/systemd/system/dcos-setup.service`
cat <<'EOF' > "/etc/systemd/system/dcos-setup.service"
[Unit]
Description=Pkgpanda: Specialize DC/OS for this host.
Requires=dcos-download.service
After=dcos-download.service
[Service]
Type=oneshot
StandardOutput=journal+console
StandardError=journal+console
EnvironmentFile=/opt/mesosphere/environment
ExecStart=/opt/mesosphere/bin/pkgpanda setup --no-block-systemd
[Install]
WantedBy=multi-user.target
EOF
chmod 0644 /etc/systemd/system/dcos-setup.service
systemctl restart systemd-journald
systemctl restart docker
systemctl start dcos-link-env
systemctl enable dcos-setup
if (( $SYSTEMCTL_NO_BLOCK == 1 )); then
systemctl start dcos-setup --no-block
else
systemctl start dcos-setup
fi
}
set +e
declare -i DISABLE_VERSION_CHECK=0
# check if sort -V works
function check_sort_capability() {
$( command -v sort >/dev/null 2>&1 || exit 1 )
RC1=$?
$( echo '1' | sort -V >/dev/null 2>&1 )
RC2=$?
if [[ "$RC1" -eq "1" || "$RC2" -eq "2" ]]; then
echo -e "${RED}Disabling version checking as sort -V is not available${NORMAL}"
DISABLE_VERSION_CHECK=1
fi
}
function version_gt() {
# sort -V does version-aware sort
HIGHEST_VERSION="$(echo "$@" | tr " " "
" | sort -V | tail -n 1)"
test $HIGHEST_VERSION == "$1"
}
function print_status() {
CODE_TO_TEST=$1
EXTRA_TEXT=${2:-}
if [[ $CODE_TO_TEST == 0 ]]; then
echo -e "${BOLD}PASS $EXTRA_TEXT${NORMAL}"
else
echo -e "${RED}FAIL $EXTRA_TEXT${NORMAL}"
fi
}
function check_command_exists() {
COMMAND=$1
DISPLAY_NAME=${2:-$COMMAND}
echo -e -n "Checking if $DISPLAY_NAME is installed and in PATH: "
$( command -v $COMMAND >/dev/null 2>&1 || exit 1 )
RC=$?
print_status $RC
(( OVERALL_RC += $RC ))
return $RC
}
function check_version() {
COMMAND_NAME=$1
VERSION_ATLEAST=$2
COMMAND_VERSION=$3
DISPLAY_NAME=${4:-$COMMAND}
echo -e -n "Checking $DISPLAY_NAME version requirement (>= $VERSION_ATLEAST): "
version_gt $COMMAND_VERSION $VERSION_ATLEAST
RC=$?
print_status $RC "${NORMAL}($COMMAND_VERSION)"
(( OVERALL_RC += $RC ))
return $RC
}
function check_selinux() {
ENABLED=$(getenforce)
if [[ $ENABLED != 'Enforcing' ]]; then
RC=0
else
RC=1
fi
print_status $RC "Is SELinux disabled?"
(( OVERALL_RC += $RC ))
return $RC
}
function check() {
# Wrapper to invoke both check_commmand and version check in one go
if [[ $# -eq 4 ]]; then
DISPLAY_NAME=$4
elif [[ $# -eq 2 ]]; then
DISPLAY_NAME=$2
else
DISPLAY_NAME=$1
fi
check_command_exists $1 $DISPLAY_NAME
# check_version takes {3,4} arguments
if [[ "$?" -eq 0 && "$#" -ge 3 && $DISABLE_VERSION_CHECK -eq 0 ]]; then
check_version $*
fi
}
function check_service() {
PORT=$1
NAME=$2
echo -e -n "Checking if port $PORT (required by $NAME) is in use: "
RC=0
cat /proc/net/{udp*,tcp*} | cut -d: -f3 | cut -d' ' -f1 | grep -q $(printf "%04x" $PORT) && RC=1
print_status $RC
(( OVERALL_RC += $RC ))
}
function check_preexisting_dcos() {
echo -e -n 'Checking if DC/OS is already installed: '
if [[ ( -d /etc/systemd/system/dcos.target ) || ( -d /etc/systemd/system/dcos.target.wants ) || ( -d /opt/mesosphere ) ]]; then
# this will print: Checking if DC/OS is already installed: FAIL (Currently installed)
print_status 1 "${NORMAL}(Currently installed)"
echo
cat <<EOM
Found an existing DC/OS installation. To reinstall DC/OS on this this machine you must
first uninstall DC/OS then run dcos_install.sh. To uninstall DC/OS, follow the product
documentation provided with DC/OS.
EOM
echo
exit 1
else
print_status 0 "${NORMAL}(Not installed)"
fi
}
function check_docker_device_mapper_loopback() {
echo -e -n 'Checking Docker is configured with a production storage driver: '
storage_driver="$(docker info | grep 'Storage Driver' | cut -d ':' -f 2 | tr -d '[[:space:]]')"
if [ "$storage_driver" != "devicemapper" ]; then
print_status 0 "${NORMAL}(${storage_driver})"
return
fi
data_file="$(docker info | grep 'Data file' | cut -d ':' -f 2 | tr -d '[[:space:]]')"
if [[ "${data_file}" == /dev/loop* ]]; then
print_status 1 "${NORMAL}(${storage_driver}, ${data_file})"
echo
cat <<EOM
Docker is configured to use the devicemapper storage driver with a loopback
device behind it. This is highly recommended against by Docker and the
community at large for production use[0][1]. See the docker documentation on
selecting an alternate storage driver, or use alternate storage than loopback
for the devicemapper driver.
[0] https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/
[1] http://www.projectatomic.io/blog/2015/06/notes-on-fedora-centos-and-docker-storage-drivers/
EOM
echo
exit 1
else
print_status 0 "${NORMAL}(${storage_driver} ${data_file})"
fi
}
function check_all() {
# Disable errexit because we want the preflight checks to run all the way
# through and not bail in the middle, which will happen as it relies on
# error exit codes
set +e
echo -e "${BOLD}Running preflight checks${NORMAL}"
AGENT_ONLY=0
for ROLE in $ROLES; do
if [[ $ROLE = "slave" || $ROLE = "slave_public" ]]; then
AGENT_ONLY=1
break
fi
done
check_preexisting_dcos
check_selinux
check_sort_capability
local docker_version=$(command -v docker >/dev/null 2>&1 && docker version 2>/dev/null | awk '
BEGIN {
version = 0
client_version = 0
server_version = 0
}
{
if($1 == "Server:") {
server = 1
client = 0
} else if($1 == "Client:") {
server = 0
client = 1
} else if ($1 == "Server" && $2 == "version:") {
server_version = $3
} else if ($1 == "Client" && $2 == "version:") {
client_version = $3
}
if(server && $1 == "Version:") {
server_version = $2
} else if(client && $1 == "Version:") {
client_version = $2
}
}
END {
if(client_version == server_version) {
version = client_version
} else {
cv_length = split(client_version, cv, ".")
sv_length = split(server_version, sv, ".")
y = cv_length > sv_length ? cv_length : sv_length
for(i = 1; i <= y; i++) {
if(cv[i] < sv[i]) {
version = client_version
break
} else if(sv[i] < cv[i]) {
version = server_version
break
}
}
}
print version
}
')
# CoreOS stable as of Aug 2015 has 1.6.2
check docker 1.6 "$docker_version"
check curl
check bash
check ping
check tar
check xz
check unzip
check ipset
check systemd-notify
# $ systemctl --version ->
# systemd nnn
# compiler option string
# Pick up just the first line of output and get the version from it
check systemctl 200 $(systemctl --version | head -1 | cut -f2 -d' ') systemd
echo -e -n "Checking if group 'nogroup' exists: "
getent group nogroup > /dev/null
RC=$?
print_status $RC
(( OVERALL_RC += $RC ))
# Run service check on master node only
if [[ $AGENT_ONLY -eq 0 ]]; then
# master node service checks
for service in "53 spartan" "80 adminrouter" "443 adminrouter" "1050 3dt" "2181 zookeeper" "5050 mesos-master" "7070 cosmos" "8080 marathon" "8101 dcos-oauth" "8123 mesos-dns" "8181 exhibitor" "9000 metronome" "9942 metronome" "9990 cosmos" "15055 dcos-history" "33107 navstar" "36771 marathon" "41281 zookeeper" "42819 spartan" "43911 minuteman" "46839 metronome" "61053 mesos-dns" "61420 epmd" "61421 minuteman" "62053 spartan" "62080 navstar"
do
check_service $service
done
else
# agent / public agent node service checks
for service in "53 spartan" "5051 mesos-agent" "34451 navstar" "39851 spartan" "43995 minuteman" "61001 agent-adminrouter" "61420 epmd" "61421 minuteman" "62053 spartan" "62080 navstar"
do
check_service $service
done
fi
# Check we're not in docker on devicemapper loopback as storage driver.
check_docker_device_mapper_loopback
for role in "$ROLES"
do
if [ "$role" != "master" -a "$role" != "slave" -a "$role" != "slave_public" -a "$role" != "minuteman" ]; then
echo -e "${RED}FAIL Invalid role $role. Role must be one of {master,slave,slave_public}${NORMAL}"
(( OVERALL_RC += 1 ))
fi
done
return $OVERALL_RC
}
function dcos_install()
{
# Enable errexit
set -e
setup_directories
setup_dcos_roles
configure_dcos
setup_and_start_services
}
function usage()
{
echo -e "${BOLD}Usage: $0 [--disable-preflight|--preflight-only] <roles>${NORMAL}"
}
function main()
{
eval set -- "$ARGS"
while true ; do
case "$1" in
-d|--disable-preflight) DISABLE_PREFLIGHT=1; shift ;;
-p|--preflight-only) PREFLIGHT_ONLY=1 ; shift ;;
--no-block-dcos-setup) SYSTEMCTL_NO_BLOCK=1; shift ;;
-h|--help) usage; exit 1 ;;
--) shift ; break ;;
*) usage ; exit 1 ;;
esac
done
if [[ $DISABLE_PREFLIGHT -eq 1 && $PREFLIGHT_ONLY -eq 1 ]]; then
echo -e 'Both --disable-preflight and --preflight-only can not be specified'
usage
exit 1
fi
shift $(($OPTIND - 1))
ROLES=$@
if [[ $PREFLIGHT_ONLY -eq 1 ]] ; then
check_all
else
if [[ -z $ROLES ]] ; then
echo -e 'Atleast one role name must be specified'
usage
exit 1
fi
echo -e "${BOLD}Starting DC/OS Install Process${NORMAL}"
if [[ $DISABLE_PREFLIGHT -eq 0 ]] ; then
check_all
RC=$?
if [[ $RC -ne 0 ]]; then
echo 'Preflight checks failed. Exiting installation. Please consult product documentation'
exit $RC
fi
fi
# Run actual install
dcos_install
fi
}
# Run it all
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment