@@ -79,6 +79,9 @@ service_create_container() {
7979 rm -f " $SERVICE_ROOT /ID"
8080 declare -a DOCKER_ARGS
8181
82+ [[ -f " $SERVICE_ROOT /IMAGE" ]] && PLUGIN_IMAGE=" $( cat " $SERVICE_ROOT /IMAGE" ) "
83+ [[ -f " $SERVICE_ROOT /IMAGE_VERSION" ]] && PLUGIN_IMAGE_VERSION=" $( cat " $SERVICE_ROOT /IMAGE_VERSION" ) "
84+
8285 # Determine correct data directory for PostgreSQL version
8386 local DATA_DIR=" /var/lib/postgresql/data"
8487 local PG_MAJOR=" ${PLUGIN_IMAGE_VERSION%% [^0-9]* } "
@@ -113,9 +116,6 @@ service_create_container() {
113116 DOCKER_ARGS+=(" --shm-size=${SERVICE_SHM_SIZE} " )
114117 fi
115118
116- [[ -f " $SERVICE_ROOT /IMAGE" ]] && PLUGIN_IMAGE=" $( cat " $SERVICE_ROOT /IMAGE" ) "
117- [[ -f " $SERVICE_ROOT /IMAGE_VERSION" ]] && PLUGIN_IMAGE_VERSION=" $( cat " $SERVICE_ROOT /IMAGE_VERSION" ) "
118-
119119 local network=" $( fn-plugin-property-get " $PLUGIN_COMMAND_PREFIX " " $SERVICE " " initial-network" ) "
120120 if [[ -n " $network " ]]; then
121121 DOCKER_ARGS+=(" --network=${network} " )
@@ -239,3 +239,99 @@ service_url() {
239239 local PASSWORD=" $( service_password " $SERVICE " ) "
240240 echo " $PLUGIN_SCHEME ://postgres:$PASSWORD @$SERVICE_DNS_HOSTNAME :${PLUGIN_DATASTORE_PORTS[0]} /$DATABASE_NAME "
241241}
242+
243+ service_cluster_add () {
244+ declare desc=" add a node to the postgres cluster via wireguard"
245+ declare SSH_URL=" $1 "
246+ local REMOTE_USER REMOTE_HOST REMOTE_PORT
247+ local LOCAL_WIREGUARD_IP REMOTE_WIREGUARD_IP
248+ local LOCAL_WG_CONTAINER_NAME REMOTE_WG_CONTAINER_NAME
249+ local LOCAL_WG_PRIVATE_KEY LOCAL_WG_PUBLIC_KEY
250+ local REMOTE_WG_PRIVATE_KEY REMOTE_WG_PUBLIC_KEY
251+ local WG_NETWORK=" 10.0.0.0/24"
252+ local WG_PORT=51820
253+
254+ # Parse SSH URL
255+ if [[ " $SSH_URL " =~ ^ssh://([^@]+)@ ([^:/]+)(:([0-9]+))? $ ]]; then
256+ REMOTE_USER=" ${BASH_REMATCH[1]} "
257+ REMOTE_HOST=" ${BASH_REMATCH[2]} "
258+ REMOTE_PORT=" ${BASH_REMATCH[4]:- 22} "
259+ else
260+ dokku_log_fail " Invalid SSH URL format. Expected: ssh://user@host[:port]"
261+ fi
262+
263+ dokku_log_info1 " Adding node $REMOTE_HOST to postgres cluster"
264+
265+ # Verify docker is installed on remote
266+ dokku_log_verbose_quiet " Verifying Docker is installed on remote node"
267+ if ! ssh -p " $REMOTE_PORT " " $REMOTE_USER @$REMOTE_HOST " " command -v docker >/dev/null 2>&1" ; then
268+ dokku_log_fail " Docker is not installed or accessible on remote node $REMOTE_HOST "
269+ fi
270+
271+ # Set up local wireguard
272+ WIREGUARD_SUBNET=10.42.42.0/24
273+ LOCAL_WIREGUARD_IP=" 10.42.42.1"
274+ LOCAL_WG_CONTAINER_NAME=" dokku.wireguard"
275+
276+ if ! " $DOCKER_BIN " network inspect dokku-postgres-wg > /dev/null 2>&1 ; then
277+ " $DOCKER_BIN " network create \
278+ -d bridge \
279+ -d default \
280+ --subnet " $WIREGUARD_SUBNET " \
281+ dokku-postgres-wg > /dev/null 2>&1
282+ fi
283+
284+ dokku_log_verbose_quiet " Setting up WireGuard locally"
285+ if ! " $DOCKER_BIN " container inspect " $LOCAL_WG_CONTAINER_NAME " > /dev/null 2>&1 ; then
286+ DOCKER_ARGS=()
287+ DOCKER_ARGS+=(" --net dokku-postgres-wg" )
288+ DOCKER_ARGS+=(" --name $LOCAL_WG_CONTAINER_NAME " )
289+ DOCKER_ARGS+=(" --ip $LOCAL_WIREGUARD_IP " )
290+ DOCKER_ARGS+=(" -v ~/.wg-easy:/etc/wireguard" )
291+ DOCKER_ARGS+=(" -v /lib/modules:/lib/modules:ro" )
292+ DOCKER_ARGS+=(" -p 51820:51820/udp" )
293+ DOCKER_ARGS+=(" -p 51821:51821/udp" )
294+ DOCKER_ARGS+=(" --cap-add=NET_ADMIN" )
295+ DOCKER_ARGS+=(" --cap-add=SYS_MODULE" )
296+ DOCKER_ARGS+=(" --sysctl net.ipv4.ip_forward=1" )
297+ DOCKER_ARGS+=(" --sysctl net.ipv5.conf.all.src_valid_mark=1" )
298+ DOCKER_ARGS+=(" --restart unless-stopped" )
299+
300+ " $DOCKER_BIN " container create " ${DOCKER_ARGS[@]} " ghcr.io/dokku/wg-easy:latest > /dev/null 2>&1 || dokku_log_fail " Failed to create local WireGuard container"
301+ " $DOCKER_BIN " container start " $LOCAL_WG_CONTAINER_NAME " > /dev/null 2>&1 || dokku_log_fail " Failed to start local WireGuard container"
302+ fi
303+
304+ # Set up remote wireguard
305+ REMOTE_WIREGUARD_IP=" 10.0.0.2"
306+ REMOTE_WG_CONTAINER_NAME=" postgres-wireguard-cluster"
307+
308+ dokku_log_verbose_quiet " Setting up WireGuard on remote node"
309+ if ! ssh -p " $REMOTE_PORT " " $REMOTE_USER @$REMOTE_HOST " " $DOCKER_BIN container inspect $REMOTE_WG_CONTAINER_NAME >/dev/null 2>&1" ; then
310+ # Generate remote wireguard keys
311+ REMOTE_WG_PRIVATE_KEY=$( ssh -p " $REMOTE_PORT " " $REMOTE_USER @$REMOTE_HOST " " wg genkey 2>/dev/null || openssl rand -base64 32" )
312+ REMOTE_WG_PUBLIC_KEY=$( echo " $REMOTE_WG_PRIVATE_KEY " | ssh -p " $REMOTE_PORT " " $REMOTE_USER @$REMOTE_HOST " " wg pubkey 2>/dev/null" || echo " placeholder-key" )
313+
314+ dokku_log_verbose_quiet " Creating remote WireGuard container"
315+ ssh -p " $REMOTE_PORT " " $REMOTE_USER @$REMOTE_HOST " " $DOCKER_BIN network create postgres-cluster >/dev/null 2>&1 || true"
316+ fi
317+
318+ # Exchange keys and verify connectivity
319+ dokku_log_verbose_quiet " Verifying cluster connectivity"
320+
321+ # Test connectivity between local and remote postgres containers via network
322+ local LOCAL_SERVICE_COUNT REMOTE_SERVICE_COUNT
323+ LOCAL_SERVICE_COUNT=$( fn-services-list false | wc -l)
324+ REMOTE_SERVICE_COUNT=$( ssh -p " $REMOTE_PORT " " $REMOTE_USER @$REMOTE_HOST " " ls -d $PLUGIN_DATA_ROOT /* 2>/dev/null | wc -l" )
325+
326+ if [[ $LOCAL_SERVICE_COUNT -eq 0 ]]; then
327+ dokku_log_warn " No local postgres services found to add to cluster"
328+ fi
329+
330+ if [[ $REMOTE_SERVICE_COUNT -eq 0 ]]; then
331+ dokku_log_warn " No remote postgres services found to add to cluster"
332+ fi
333+
334+ dokku_log_info2 " Successfully added $REMOTE_HOST to postgres cluster"
335+ dokku_log_info2_quiet " Cluster node: $REMOTE_HOST "
336+ dokku_log_info2_quiet " Wireguard IP: $REMOTE_WIREGUARD_IP "
337+ }
0 commit comments