109 lines
3.4 KiB
Bash
Executable File
109 lines
3.4 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
MODE=${1:-compose}
|
|
COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-who_need_help}
|
|
|
|
case "$MODE" in
|
|
compose)
|
|
TARGET=$(docker compose -p "$COMPOSE_PROJECT_NAME" ps -q web | head -n 1)
|
|
if [ -z "$TARGET" ]; then
|
|
TARGET=$(docker compose -p "$COMPOSE_PROJECT_NAME" ps -q app | head -n 1)
|
|
fi
|
|
if [ -z "$TARGET" ]; then
|
|
echo "No running Compose web or compact app replica was found." >&2
|
|
exit 1
|
|
fi
|
|
RUN="docker exec $TARGET"
|
|
;;
|
|
kind)
|
|
PATH="$ROOT/.tools/bin:$PATH"
|
|
export PATH
|
|
TARGET=$(
|
|
kubectl --context kind-who-need-help --namespace who-need-help get pods \
|
|
-l app.kubernetes.io/component=web \
|
|
--field-selector=status.phase=Running \
|
|
--no-headers \
|
|
-o custom-columns='NAME:.metadata.name,READY:.status.containerStatuses[0].ready,DELETING:.metadata.deletionTimestamp' |
|
|
awk '$2 == "true" && $3 == "<none>" {print $1; exit}'
|
|
)
|
|
if [ -z "$TARGET" ]; then
|
|
echo "No running kind web replica was found." >&2
|
|
exit 1
|
|
fi
|
|
RUN="kubectl --context kind-who-need-help --namespace who-need-help exec $TARGET --"
|
|
;;
|
|
*)
|
|
echo "Usage: $0 [compose|kind]" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# The subscriber runs on the selected web node. The broadcast is executed by a
|
|
# different connected BEAM node. Success proves distributed PubSub fan-out.
|
|
$RUN /app/bin/who_need_help rpc '
|
|
live_peers =
|
|
Enum.filter(Node.list(), fn peer ->
|
|
case :rpc.call(peer, Process, :whereis, [WhoNeedHelp.PubSub]) do
|
|
pid when is_pid(pid) -> true
|
|
_ -> false
|
|
end
|
|
end)
|
|
|
|
case live_peers do
|
|
[] ->
|
|
if Application.fetch_env!(:who_need_help, :app_role) == :combined do
|
|
topic = "local:verify:" <> Integer.to_string(System.unique_integer([:positive]))
|
|
:ok = Phoenix.PubSub.subscribe(WhoNeedHelp.PubSub, topic)
|
|
:ok = Phoenix.PubSub.broadcast(WhoNeedHelp.PubSub, topic, :local_pubsub_probe)
|
|
|
|
receive do
|
|
:local_pubsub_probe ->
|
|
IO.inspect(%{status: :ok, topology: :compact, subscriber: node(), peers: []})
|
|
after
|
|
5_000 -> exit({:local_pubsub_failed, node()})
|
|
end
|
|
else
|
|
exit({:no_live_cluster_peers, Node.list()})
|
|
end
|
|
|
|
peers ->
|
|
topic = "cluster:verify:" <> Integer.to_string(System.unique_integer([:positive]))
|
|
:ok = Phoenix.PubSub.subscribe(WhoNeedHelp.PubSub, topic)
|
|
|
|
result =
|
|
Enum.reduce_while(peers, nil, fn peer, _result ->
|
|
case :rpc.call(peer, Phoenix.PubSub, :broadcast, [
|
|
WhoNeedHelp.PubSub,
|
|
topic,
|
|
{:cross_replica_probe, peer}
|
|
]) do
|
|
:ok ->
|
|
receive do
|
|
{:cross_replica_probe, ^peer} ->
|
|
{:halt,
|
|
%{
|
|
status: :ok,
|
|
subscriber: node(),
|
|
broadcaster: peer,
|
|
peers: Node.list()
|
|
}}
|
|
after
|
|
5_000 -> {:cont, nil}
|
|
end
|
|
|
|
_error ->
|
|
{:cont, nil}
|
|
end
|
|
end)
|
|
|
|
result =
|
|
result ||
|
|
%{status: :timeout, subscriber: node(), attempted_peers: peers, peers: Node.list()}
|
|
|
|
IO.inspect(Map.put(result, :topology, :split))
|
|
if result.status != :ok, do: exit({:cross_replica_pubsub_failed, result})
|
|
end
|
|
'
|