diff --git a/install.sh b/install.sh index ba02bf6..171851b 100755 --- a/install.sh +++ b/install.sh @@ -1,162 +1,28 @@ #!/usr/bin/env bash set -e - if [[ -n "$MSYSTEM" ]]; then echo "Seems like you are using an MSYS2-based system (such as Git Bash) which is not supported. Please use WSL instead."; exit 1 fi -# Thanks to https://unix.stackexchange.com/a/145654/108960 -log_file="sentry_install_log-`date +'%Y-%m-%d_%H-%M-%S'`.txt" -exec &> >(tee -a "$log_file") - -source "$(dirname $0)/install/_lib.sh" - -echo "${_group}Defining variables and helpers ..." -MIN_DOCKER_VERSION='19.03.6' -MIN_COMPOSE_VERSION='1.24.1' -MIN_RAM_HARD=3800 # MB -MIN_RAM_SOFT=7800 # MB -MIN_CPU_HARD=2 -MIN_CPU_SOFT=4 -echo $_endgroup - -echo "${_group}Parsing command line ..." -show_help() { - cat < /dev/null - fi -} -trap_with_arg cleanup ERR INT TERM EXIT -echo "${_endgroup}" - -echo "${_group}Checking minimum requirements ..." -DOCKER_VERSION=$(docker version --format '{{.Server.Version}}') -COMPOSE_VERSION=$($dc --version | sed 's/docker-compose version \(.\{1,\}\),.*/\1/') -RAM_AVAILABLE_IN_DOCKER=$(docker run --rm busybox free -m 2>/dev/null | awk '/Mem/ {print $2}'); -CPU_AVAILABLE_IN_DOCKER=$(docker run --rm busybox nproc --all); - -# Compare dot-separated strings - function below is inspired by https://stackoverflow.com/a/37939589/808368 -function ver () { echo "$@" | awk -F. '{ printf("%d%03d%03d", $1,$2,$3); }'; } - -if [[ "$(ver $DOCKER_VERSION)" -lt "$(ver $MIN_DOCKER_VERSION)" ]]; then - echo "FAIL: Expected minimum Docker version to be $MIN_DOCKER_VERSION but found $DOCKER_VERSION" - exit 1 -fi - -if [[ "$(ver $COMPOSE_VERSION)" -lt "$(ver $MIN_COMPOSE_VERSION)" ]]; then - echo "FAIL: Expected minimum docker-compose version to be $MIN_COMPOSE_VERSION but found $COMPOSE_VERSION" - exit 1 -fi - -if [[ "$CPU_AVAILABLE_IN_DOCKER" -lt "$MIN_CPU_HARD" ]]; then - echo "FAIL: Required minimum CPU cores available to Docker is $MIN_CPU_HARD, found $CPU_AVAILABLE_IN_DOCKER" - exit 1 -elif [[ "$CPU_AVAILABLE_IN_DOCKER" -lt "$MIN_CPU_SOFT" ]]; then - echo "WARN: Recommended minimum CPU cores available to Docker is $MIN_CPU_SOFT, found $CPU_AVAILABLE_IN_DOCKER" -fi - -if [[ "$RAM_AVAILABLE_IN_DOCKER" -lt "$MIN_RAM_HARD" ]]; then - echo "FAIL: Required minimum RAM available to Docker is $MIN_RAM_HARD MB, found $RAM_AVAILABLE_IN_DOCKER MB" - exit 1 -elif [[ "$RAM_AVAILABLE_IN_DOCKER" -lt "$MIN_RAM_SOFT" ]]; then - echo "WARN: Recommended minimum RAM available to Docker is $MIN_RAM_SOFT MB, found $RAM_AVAILABLE_IN_DOCKER MB" -fi - -#SSE4.2 required by Clickhouse (https://clickhouse.yandex/docs/en/operations/requirements/) -# On KVM, cpuinfo could falsely not report SSE 4.2 support, so skip the check. https://github.com/ClickHouse/ClickHouse/issues/20#issuecomment-226849297 -IS_KVM=$(docker run --rm busybox grep -c 'Common KVM processor' /proc/cpuinfo || :) -if [[ "$IS_KVM" -eq 0 ]]; then - SUPPORTS_SSE42=$(docker run --rm busybox grep -c sse4_2 /proc/cpuinfo || :) - if [[ "$SUPPORTS_SSE42" -eq 0 ]]; then - echo "FAIL: The CPU your machine is running on does not support the SSE 4.2 instruction set, which is required for one of the services Sentry uses (Clickhouse). See https://git.io/JvLDt for more info." - exit 1 - fi -fi -echo "${_endgroup}" - -source ./install/create-docker-volumes.sh -source ./install/ensure-files-from-examples.sh -source ./install/generate-secret-key.sh -source ./install/replace-tsdb.sh -source ./install/update-docker-images.sh -source ./install/build-docker-images.sh -source ./install/turn-things-off.sh -source ./install/set-up-zookeeper.sh -source ./install/bootstrap-snuba.sh -source ./install/create-kafka-topics.sh -source ./install/upgrade-postgres.sh -source ./install/set-up-and-migrate-database.sh -source ./install/migrate-file-storage.sh -source ./install/relay-credentials.sh -source ./install/geoip.sh - -if [[ "$MINIMIZE_DOWNTIME" ]]; then - source ./install/restart-carefully.sh -else - echo "" - echo "-----------------------------------------------------------------" - echo "" - echo "You're all done! Run the following command to get Sentry running:" - echo "" - echo " docker-compose up -d" - echo "" - echo "-----------------------------------------------------------------" - echo "" -fi +source "$(dirname $0)/install/_lib.sh" # does a `cd .../install/`, among other things + +source parse-cli.sh +source error-handling.sh +source check-minimum-requirements.sh +source create-docker-volumes.sh +source ensure-files-from-examples.sh +source generate-secret-key.sh +source replace-tsdb.sh +source update-docker-images.sh +source build-docker-images.sh +source turn-things-off.sh +source set-up-zookeeper.sh +source bootstrap-snuba.sh +source create-kafka-topics.sh +source upgrade-postgres.sh +source set-up-and-migrate-database.sh +source migrate-file-storage.sh +source relay-credentials.sh +source geoip.sh +source wrap-up.sh diff --git a/install/_lib.sh b/install/_lib.sh index 9c636fd..0e0ad5b 100644 --- a/install/_lib.sh +++ b/install/_lib.sh @@ -1,46 +1,49 @@ set -euo pipefail test "${DEBUG:-}" && set -x +# Thanks to https://unix.stackexchange.com/a/145654/108960 +log_file="sentry_install_log-`date +'%Y-%m-%d_%H-%M-%S'`.txt" +exec &> >(tee -a "$log_file") + # Work from the onpremise root, no matter which script is called from where. if [[ "$(basename $0)" = "install.sh" ]]; then - cd "$(dirname $0)" + cd "$(dirname $0)/install/" else - cd "$(dirname $0)/.." + cd "$(dirname $0)" # assume we're a *-test.sh script fi -if [[ ! -d 'install' ]]; then echo 'Where are you?'; exit 1; fi -_ENV="$(realpath .env)" +_ENV="$(realpath ../.env)" # Read .env for default values with a tip o' the hat to https://stackoverflow.com/a/59831605/90297 t=$(mktemp) && export -p > "$t" && set -a && . $_ENV && set +a && . "$t" && rm "$t" && unset t if [ "${GITHUB_ACTIONS:-}" = "true" ]; then _group="::group::" _endgroup="::endgroup::" else _group="▶ " _endgroup="" fi dc="docker-compose --no-ansi" dcr="$dc run --rm" # A couple of the config files are referenced from other subscripts, so they # get vars, while multiple subscripts call ensure_file_from_example. function ensure_file_from_example { if [[ -f "$1" ]]; then echo "$1 already exists, skipped creation." else echo "Creating $1..." cp -n $(echo "$1" | sed 's/\.[^.]*$/.example&/') "$1" # sed from https://stackoverflow.com/a/25123013/90297 fi } -SENTRY_CONFIG_PY='sentry/sentry.conf.py' -SENTRY_CONFIG_YML='sentry/config.yml' +SENTRY_CONFIG_PY='../sentry/sentry.conf.py' +SENTRY_CONFIG_YML='../sentry/config.yml' # Increase the default 10 second SIGTERM timeout # to ensure celery queues are properly drained # between upgrades as task signatures may change across # versions STOP_TIMEOUT=60 # seconds diff --git a/install/avoid-git-bash.sh b/install/avoid-git-bash.sh new file mode 100644 index 0000000..e69de29 diff --git a/install/check-minimum-requirements.sh b/install/check-minimum-requirements.sh new file mode 100644 index 0000000..4527a22 --- /dev/null +++ b/install/check-minimum-requirements.sh @@ -0,0 +1,53 @@ +echo "${_group}Checking minimum requirements ..." + +MIN_DOCKER_VERSION='19.03.6' +MIN_COMPOSE_VERSION='1.24.1' +MIN_RAM_HARD=3800 # MB +MIN_RAM_SOFT=7800 # MB +MIN_CPU_HARD=2 +MIN_CPU_SOFT=4 + +DOCKER_VERSION=$(docker version --format '{{.Server.Version}}') +COMPOSE_VERSION=$($dc --version | sed 's/docker-compose version \(.\{1,\}\),.*/\1/') +RAM_AVAILABLE_IN_DOCKER=$(docker run --rm busybox free -m 2>/dev/null | awk '/Mem/ {print $2}'); +CPU_AVAILABLE_IN_DOCKER=$(docker run --rm busybox nproc --all); + +# Compare dot-separated strings - function below is inspired by https://stackoverflow.com/a/37939589/808368 +function ver () { echo "$@" | awk -F. '{ printf("%d%03d%03d", $1,$2,$3); }'; } + +if [[ "$(ver $DOCKER_VERSION)" -lt "$(ver $MIN_DOCKER_VERSION)" ]]; then + echo "FAIL: Expected minimum Docker version to be $MIN_DOCKER_VERSION but found $DOCKER_VERSION" + exit 1 +fi + +if [[ "$(ver $COMPOSE_VERSION)" -lt "$(ver $MIN_COMPOSE_VERSION)" ]]; then + echo "FAIL: Expected minimum docker-compose version to be $MIN_COMPOSE_VERSION but found $COMPOSE_VERSION" + exit 1 +fi + +if [[ "$CPU_AVAILABLE_IN_DOCKER" -lt "$MIN_CPU_HARD" ]]; then + echo "FAIL: Required minimum CPU cores available to Docker is $MIN_CPU_HARD, found $CPU_AVAILABLE_IN_DOCKER" + exit 1 +elif [[ "$CPU_AVAILABLE_IN_DOCKER" -lt "$MIN_CPU_SOFT" ]]; then + echo "WARN: Recommended minimum CPU cores available to Docker is $MIN_CPU_SOFT, found $CPU_AVAILABLE_IN_DOCKER" +fi + +if [[ "$RAM_AVAILABLE_IN_DOCKER" -lt "$MIN_RAM_HARD" ]]; then + echo "FAIL: Required minimum RAM available to Docker is $MIN_RAM_HARD MB, found $RAM_AVAILABLE_IN_DOCKER MB" + exit 1 +elif [[ "$RAM_AVAILABLE_IN_DOCKER" -lt "$MIN_RAM_SOFT" ]]; then + echo "WARN: Recommended minimum RAM available to Docker is $MIN_RAM_SOFT MB, found $RAM_AVAILABLE_IN_DOCKER MB" +fi + +#SSE4.2 required by Clickhouse (https://clickhouse.yandex/docs/en/operations/requirements/) +# On KVM, cpuinfo could falsely not report SSE 4.2 support, so skip the check. https://github.com/ClickHouse/ClickHouse/issues/20#issuecomment-226849297 +IS_KVM=$(docker run --rm busybox grep -c 'Common KVM processor' /proc/cpuinfo || :) +if [[ "$IS_KVM" -eq 0 ]]; then + SUPPORTS_SSE42=$(docker run --rm busybox grep -c sse4_2 /proc/cpuinfo || :) + if [[ "$SUPPORTS_SSE42" -eq 0 ]]; then + echo "FAIL: The CPU your machine is running on does not support the SSE 4.2 instruction set, which is required for one of the services Sentry uses (Clickhouse). See https://git.io/JvLDt for more info." + exit 1 + fi +fi + +echo "${_endgroup}" diff --git a/install/create-docker-volumes-test.sh b/install/create-docker-volumes-test.sh index 86f8b17..7cf8969 100755 --- a/install/create-docker-volumes-test.sh +++ b/install/create-docker-volumes-test.sh @@ -1,20 +1,20 @@ #!/usr/bin/env bash source "$(dirname $0)/_test_setup.sh" expected=7 count() { docker volume ls --quiet | grep '^sentry-.*' | wc -l } # Maybe they exist prior, maybe they don't. Script is idempotent. before=$(count) test $before -eq 0 || test $before -eq $expected -source ./install/create-docker-volumes.sh -source ./install/create-docker-volumes.sh -source ./install/create-docker-volumes.sh +source create-docker-volumes.sh +source create-docker-volumes.sh +source create-docker-volumes.sh test $(count) -eq $expected report_success diff --git a/install/ensure-files-from-examples.sh b/install/ensure-files-from-examples.sh index 0a507d6..17958a0 100644 --- a/install/ensure-files-from-examples.sh +++ b/install/ensure-files-from-examples.sh @@ -1,8 +1,8 @@ echo "${_group}Ensuring files from examples ..." ensure_file_from_example $SENTRY_CONFIG_PY ensure_file_from_example $SENTRY_CONFIG_YML -ensure_file_from_example 'symbolicator/config.yml' -ensure_file_from_example 'sentry/requirements.txt' +ensure_file_from_example '../symbolicator/config.yml' +ensure_file_from_example '../sentry/requirements.txt' echo "${_endgroup}" diff --git a/install/error-handling.sh b/install/error-handling.sh new file mode 100644 index 0000000..d25ee01 --- /dev/null +++ b/install/error-handling.sh @@ -0,0 +1,35 @@ +echo "${_group}Setting up error handling ..." + +# Courtesy of https://stackoverflow.com/a/2183063/90297 +trap_with_arg() { + func="$1" ; shift + for sig ; do + trap "$func $sig "'$LINENO' "$sig" + done +} + +DID_CLEAN_UP=0 +# the cleanup function will be the exit point +cleanup () { + if [[ "$DID_CLEAN_UP" -eq 1 ]]; then + return 0; + fi + DID_CLEAN_UP=1 + + if [[ "$1" != "EXIT" ]]; then + echo "An error occurred, caught SIG$1 on line $2"; + + if [[ -n "$MINIMIZE_DOWNTIME" ]]; then + echo "*NOT* cleaning up, to clean your environment run \"docker-compose stop\"." + else + echo "Cleaning up..." + fi + fi + + if [[ -z "$MINIMIZE_DOWNTIME" ]]; then + $dc stop -t $STOP_TIMEOUT &> /dev/null + fi +} +trap_with_arg cleanup ERR INT TERM EXIT + +echo "${_endgroup}" diff --git a/install/geoip-test.sh b/install/geoip-test.sh index 38650ab..f6c55cc 100755 --- a/install/geoip-test.sh +++ b/install/geoip-test.sh @@ -1,16 +1,16 @@ #!/usr/bin/env bash source "$(dirname $0)/_test_setup.sh" mmdb="geoip/GeoLite2-City.mmdb" # Starts with no mmdb, ends up with empty. test ! -f $mmdb -source ./install/geoip.sh +source geoip.sh diff -rub $mmdb $mmdb.empty # Doesn't clobber existing, though. echo GARBAGE > $mmdb -source ./install/geoip.sh +source geoip.sh test "$(cat $mmdb)" = "GARBAGE" report_success diff --git a/install/parse-cli.sh b/install/parse-cli.sh new file mode 100644 index 0000000..f1b6218 --- /dev/null +++ b/install/parse-cli.sh @@ -0,0 +1,32 @@ +echo "${_group}Parsing command line ..." + +show_help() { + cat < $cfg echo MOAR GARBAGE > $creds -source ./install/relay-credentials.sh +source relay-credentials.sh test "$(cat $cfg)" = "GARBAGE" test "$(cat $creds)" = "MOAR GARBAGE" report_success diff --git a/install/restart-carefully.sh b/install/restart-carefully.sh deleted file mode 100644 index a25da9e..0000000 --- a/install/restart-carefully.sh +++ /dev/null @@ -1,13 +0,0 @@ -echo "${_group}Waiting for Sentry to start ..." - -# Start the whole setup, except nginx and relay. -$dc up -d --remove-orphans $($dc config --services | grep -v -E '^(nginx|relay)$') -$dc exec -T nginx service nginx reload - -docker run --rm --network="${COMPOSE_PROJECT_NAME}_default" alpine ash \ - -c 'while [[ "$(wget -T 1 -q -O- http://web:9000/_health/)" != "ok" ]]; do sleep 0.5; done' - -# Make sure everything is up. This should only touch relay and nginx -$dc up -d - -echo "${_endgroup}" diff --git a/install/wrap-up.sh b/install/wrap-up.sh new file mode 100644 index 0000000..2671a3a --- /dev/null +++ b/install/wrap-up.sh @@ -0,0 +1,25 @@ +if [[ "$MINIMIZE_DOWNTIME" ]]; then + echo "${_group}Waiting for Sentry to start ..." + + # Start the whole setup, except nginx and relay. + $dc up -d --remove-orphans $($dc config --services | grep -v -E '^(nginx|relay)$') + $dc exec -T nginx service nginx reload + + docker run --rm --network="${COMPOSE_PROJECT_NAME}_default" alpine ash \ + -c 'while [[ "$(wget -T 1 -q -O- http://web:9000/_health/)" != "ok" ]]; do sleep 0.5; done' + + # Make sure everything is up. This should only touch relay and nginx + $dc up -d + + echo "${_endgroup}" +else + echo "" + echo "-----------------------------------------------------------------" + echo "" + echo "You're all done! Run the following command to get Sentry running:" + echo "" + echo " docker-compose up -d" + echo "" + echo "-----------------------------------------------------------------" + echo "" +fi