double-z
8/1/2017 - 5:41 AM

dcos_install.sh

#!/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