#!/usr/bin/env bash # # Launch one or more Elasticsearch nodes via the Docker image, # to form a cluster suitable for running the REST API tests. # # Export the ELASTICSEARCH_VERSION variable, eg. 'elasticsearch:8.0.0-SNAPSHOT'. # Version 1.0 # - Initial version of the run-elasticsearch.sh script if [[ -z "$ELASTICSEARCH_VERSION" ]]; then echo -e "\033[31;1mERROR:\033[0m Required environment variable [ELASTICSEARCH_VERSION] not set\033[0m" exit 1 fi set -euxo pipefail # realpath on MacOS use different flags than on Linux if [[ "$OSTYPE" == "darwin"* ]]; then SCRIPT_PATH=$(dirname $(realpath $0)) else SCRIPT_PATH=$(dirname $(realpath -s $0)) fi moniker=$(echo "$ELASTICSEARCH_VERSION" | tr -C "[:alnum:]" '-') suffix=rest-test NODE_NAME=${NODE_NAME-${moniker}node1} MASTER_NODE_NAME=${MASTER_NODE_NAME-${NODE_NAME}} CLUSTER_NAME=${CLUSTER_NAME-${moniker}${suffix}} HTTP_PORT=${HTTP_PORT-9200} ELASTIC_PASSWORD=${ELASTIC_PASSWORD-changeme} DETACH=${DETACH-false} CLEANUP=${CLEANUP-false} volume_name=${NODE_NAME}-${suffix}-data network_default=${moniker}${suffix} NETWORK_NAME=${NETWORK_NAME-"$network_default"} set +x # Set vm.max_map_count kernel setting to 262144 if we're in CI if [[ "$BUILDKITE" == "true" ]]; then sudo sysctl -w vm.max_map_count=262144 fi function cleanup_volume { if [[ "$(docker volume ls -q -f name=$1)" ]]; then echo -e "\033[34;1mINFO:\033[0m Removing volume $1\033[0m" (docker volume rm "$1") || true fi } function container_running { if [[ "$(docker ps -q -f name=$1)" ]]; then return 0; else return 1; fi } function cleanup_node { if container_running "$1"; then echo -e "\033[34;1mINFO:\033[0m Removing container $1\033[0m" (docker container rm --force --volumes "$1") || true cleanup_volume "$1-${suffix}-data" fi } function cleanup_network { if [[ "$(docker network ls -q -f name=$1)" ]]; then echo -e "\033[34;1mINFO:\033[0m Removing network $1\033[0m" (docker network rm "$1") || true fi } function cleanup { if [[ "$DETACH" != "true" ]] || [[ "$1" == "1" ]]; then echo -e "\033[34;1mINFO:\033[0m clean the node and volume on startup (1) OR on exit if not detached\033[0m" cleanup_node "$NODE_NAME" fi if [[ "$DETACH" != "true" ]]; then echo -e "\033[34;1mINFO:\033[0m clean the network if not detached (start and exit)\033[0m" cleanup_network "$NETWORK_NAME" fi }; trap "cleanup 0" EXIT if [[ "$CLEANUP" == "true" ]]; then trap - EXIT if [[ -z "$(docker network ls -q -f name=${NETWORK_NAME})" ]]; then echo -e "\033[34;1mINFO:\033[0m $NETWORK_NAME is already deleted\033[0m" exit 0 fi containers=$(docker network inspect -f '{{ range $key, $value := .Containers }}{{ printf "%s\n" .Name}}{{ end }}' ${NETWORK_NAME}) while read -r container; do cleanup_node "$container" done <<< "$containers" cleanup_network "$NETWORK_NAME" echo -e "\033[32;1mSUCCESS:\033[0m Cleaned up and exiting\033[0m" exit 0 fi echo -e "\033[34;1mINFO:\033[0m Making sure previous run leftover infrastructure is removed \033[0m" cleanup 1 echo -e "\033[34;1mINFO:\033[0m Creating network $NETWORK_NAME if it does not exist already \033[0m" docker network inspect "$NETWORK_NAME" > /dev/null 2>&1 || docker network create "$NETWORK_NAME" environment=($(cat <<-END --env node.name=$NODE_NAME --env cluster.name=$CLUSTER_NAME --env cluster.initial_master_nodes=$MASTER_NODE_NAME --env discovery.seed_hosts=$MASTER_NODE_NAME --env cluster.routing.allocation.disk.threshold_enabled=false --env bootstrap.memory_lock=true --env node.attr.testattr=test --env path.repo=/tmp --env repositories.url.allowed_urls=http://snapshot.test* --env ELASTIC_PASSWORD=$ELASTIC_PASSWORD --env xpack.license.self_generated.type=trial --env xpack.security.enabled=false --env xpack.security.http.ssl.enabled=false --env xpack.security.transport.ssl.enabled=false --env xpack.ml.max_machine_memory_percent=90 END )) volumes=($(cat <<-END --volume $volume_name:/usr/share/elasticsearch/data END )) url="http://elastic:$ELASTIC_PASSWORD@$NODE_NAME" # Pull the container, retry on failures up to 5 times with # short delays between each attempt. Fixes most transient network errors. docker_pull_attempts=0 until [ "$docker_pull_attempts" -ge 5 ] do docker pull docker.elastic.co/elasticsearch/$ELASTICSEARCH_VERSION && break docker_pull_attempts=$((docker_pull_attempts+1)) sleep 10 done echo -e "\033[34;1mINFO:\033[0m Starting container $NODE_NAME \033[0m" set -x docker run \ --name "$NODE_NAME" \ --network "$NETWORK_NAME" \ --env ES_JAVA_OPTS=-"Xms2g -Xmx2g" \ "${environment[@]}" \ "${volumes[@]}" \ --publish "$HTTP_PORT":9200 \ --ulimit nofile=65536:65536 \ --ulimit memlock=-1:-1 \ --detach="$DETACH" \ --health-cmd="curl --insecure --fail $url:9200/_cluster/health || exit 1" \ --health-interval=2s \ --health-retries=20 \ --health-timeout=2s \ --rm \ docker.elastic.co/elasticsearch/"$ELASTICSEARCH_VERSION"; set +x if [[ "$DETACH" == "true" ]]; then until ! container_running "$NODE_NAME" || (container_running "$NODE_NAME" && [[ "$(docker inspect -f "{{.State.Health.Status}}" ${NODE_NAME})" != "starting" ]]); do echo "" docker inspect -f "{{range .State.Health.Log}}{{.Output}}{{end}}" ${NODE_NAME} echo -e "\033[34;1mINFO:\033[0m waiting for node $NODE_NAME to be up\033[0m" sleep 2; done; # Always show logs if the container is running, this is very useful both on CI as well as while developing if container_running $NODE_NAME; then docker logs $NODE_NAME fi if ! container_running $NODE_NAME || [[ "$(docker inspect -f "{{.State.Health.Status}}" ${NODE_NAME})" != "healthy" ]]; then cleanup 1 echo echo -e "\033[31;1mERROR:\033[0m Failed to start ${ELASTICSEARCH_VERSION} in detached mode beyond health checks\033[0m" echo -e "\033[31;1mERROR:\033[0m dumped the docker log before shutting the node down\033[0m" exit 1 else echo echo -e "\033[32;1mSUCCESS:\033[0m Detached and healthy: ${NODE_NAME} on docker network: ${NETWORK_NAME}\033[0m" echo -e "\033[32;1mSUCCESS:\033[0m Running on: ${url/$NODE_NAME/localhost}:${HTTP_PORT}\033[0m" exit 0 fi fi