#!/usr/bin/env bash
#
# Get a list of all redis nodes in a cluster.
#
# Usage: redis-cluster-nodes [-h host] [-p port] [-l]
#   -h <host>    Redis host (default 127.0.0.1)
#   -p <port>    Redis port (default 6379)
#   -l           Only check nodes with the same IP.
#

# Abort if we use an undeclared variable.
set -u

# Print and exit the program.
error() {
	# Echo to stderr instead of stdout.
	cat <<< "$@" 1>&2
	exit 1
}

# Parse the command line flags.
OPTIONS="h:p:l"
LONGOPTIONS="host:,port:,local"

PARSED=$(getopt --options="$OPTIONS" --longoptions="$LONGOPTIONS" --name "$0" -- "$@")
eval set -- "$PARSED"

HOST="127.0.0.1"
PORT="6379"
LOCAL=false

while true; do
	case "$1" in
		-h|--host)
			HOST="$2"
			shift 2
			;;
		-p|--port)
			PORT="$2"
			shift 2
			;;
		-l|--local)
			LOCAL=true
			shift
			;;
		--)
			shift
			break
			;;
	esac
done

# Get the cluster nodes.
OUTPUT=$(timeout 1 redis-cli -h "$HOST" -p "$PORT" CLUSTER NODES 2>&1)
STATUS=$?

if [[ $STATUS == 124 ]]; then
	error "CLUSTER NODES timed out after 1 second"
elif [[ $STATUS != 0 ]] || [[ "$OUTPUT" == ERR* ]]; then
	error "$OUTPUT"
fi

CLUSTER_NODES="$OUTPUT"

# If the local flag is used, filter to just nodes with the same ip.
if [[ $LOCAL = true ]]; then
	MYSELF=$(grep "myself" <<< "$CLUSTER_NODES")
	if [[ -z "$MYSELF" ]]; then
		error "failed to find myself"
	fi

	MYSELF_IP=$(cut -d' ' -f2 <<< "$MYSELF" | cut -d: -f1)
	CLUSTER_NODES=$(grep " ${MYSELF_IP}:" <<< "$CLUSTER_NODES")
fi

# Remove failed or handshake nodes.
CLUSTER_NODES=$(grep -E --invert-match 'fail|handshake|noaddr' <<< "$CLUSTER_NODES")

# Loop over each cluster node entry.
NODES=()
while read -r CLUSTER_NODE; do
	ADDR=$(cut -d' ' -f2 <<< "$CLUSTER_NODE")
	HOST=$(awk -F '[:@]' '{print $1}' <<< "$ADDR")
	PORT=$(awk -F '[:@]' '{print $2}' <<< "$ADDR")
	NODES+=("${HOST}:${PORT}")
done <<< "$CLUSTER_NODES"

# Print out the nodes in sorted order just to make the output look slick.
IFS=$'\n'
SORTED=($(sort <<<"${NODES[*]}"))
echo "${SORTED[*]}"
unset IFS

exit 0
