#!/bin/bash
# Readiness of an etcd member = the process LISTENS: its client port (2379)
# and its peer port (2380) accept a TCP connection. Not quorum.
#
# Quorum must never bind a node's boot transition or the s6-rc lock. etcd
# serves client requests only after the cluster has quorum -- "endpoint
# health", "endpoint status" and even a serializable read all fail on a
# member whose peers are down (measured 23.09.2026 in three test containers).
# With such a check s6-rc's boot transition waited for the missing peers and
# held its lock, and every svctl start/stop on the node failed with rc 111
# until quorum came. Measured the same day: etcd 3.6 binds both ports without
# quorum. Quorum and cluster health are measured by monitoring (Zabbix item,
# later the operator), not by readiness.
#
# Ports come from listen-client-urls / listen-peer-urls in the config file
# etcd is started with (--config-file in DAEMON_ARGS, e.g. /etc/etcd/etcd.yml
# as profiles::etcd writes it); a wildcard address is checked on 127.0.0.1.
# /etc/default/etcd extends DAEMON_ARGS ("$DAEMON_ARGS --config-file ...");
# source it before "set -u", or the unset variable aborts the probe silently.
DAEMON_ARGS=
[ -f /etc/default/etcd ] && . /etc/default/etcd 2>/dev/null
set -u
cfg=$(printf '%s\n' "${DAEMON_ARGS:-}" | sed -n 's/.*--config-file[= ]\([^ ]*\).*/\1/p')

# first URL of a config key -> "host port"; defaults if absent
hostport() {
  local key=$1 port=$2 url='' h p
  if [ -n "$cfg" ] && [ -r "$cfg" ]; then
    url=$(sed -n "s/^$key:[[:space:]]*\"\{0,1\}\([^\",]*\).*/\1/p" "$cfg" | head -n 1)
  fi
  url=${url#*://}; url=${url%%/*}
  h=${url%:*}; p=${url##*:}
  [ -n "$url" ] && [ "$h" != "$url" ] || { h=127.0.0.1; p=$port; }
  case "$h" in ''|0.0.0.0|'[::]'|'::'|'*') h=127.0.0.1 ;; esac
  h=${h#[}; h=${h%]}
  echo "$h $p"
}

for spec in "client:$(hostport listen-client-urls 2379)" "peer:$(hostport listen-peer-urls 2380)"; do
  name=${spec%%:*}; set -- ${spec#*:}
  if ! { exec 3<>"/dev/tcp/$1/$2"; } 2>/dev/null; then
    echo "$name port $1:$2 does not accept connections"
    exit 1
  fi
  exec 3<&- 3>&-
done
exit 0
