mirror of
https://github.com/p1neappleXpress/OpenFlux.git
synced 2026-10-03 13:28:57 +08:00
node-install.sh: any systemd from 219, remove by name, list, uninstall
- Portability: "systemctl --now" (systemd 220) and "show --value" (230) are gone, "ss -H" too (older iproute2); apply creates the channel's state directory and the unit allows it with ReadWritePaths=-, so a channel runs where StateDirectory= is unknown (before 235). The updater (restart, liveness by MainPID, rollback) works on those systems too. - remove CHANNEL removes one channel by name from the server's shell (the apps keep passing a config file); list prints the channels with their state and port; uninstall removes every channel and everything the script installed (updater, unit, cores, folders, node user, firewall ports), stopping channel instances whose config is already gone. - mobile: OfferExitCookies (a sign-in handed to the exit) is removed; the account feature it served is not coming. - CI: .github/workflows/node-install.yml runs TestInstallOnVDS on Ubuntu 24.04/22.04/20.04, Debian 12/11, Rocky 9, Alma 8, Fedora 42, Arch and openSUSE Leap 15.6 in systemd containers (deploy/test/vds-dockerfile.sh); the test now also covers list, remove by name and uninstall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
96a0f95205
commit
768745dd7e
@@ -0,0 +1,87 @@
|
||||
name: Node install
|
||||
|
||||
# deploy/node-install.sh on the distributions a VDS runs: the wizard's whole
|
||||
# path over SSH (provision.TestInstallOnVDS) in a systemd container per
|
||||
# image - plan, apply with a sudo password, the updater (update, rollback
|
||||
# from a crashing core, skip), remove, list and uninstall.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- deploy/**
|
||||
- provision/**
|
||||
- .github/workflows/node-install.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- deploy/**
|
||||
- provision/**
|
||||
- .github/workflows/node-install.yml
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
vds:
|
||||
name: ${{ matrix.image }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image:
|
||||
- ubuntu:24.04
|
||||
- ubuntu:22.04
|
||||
- ubuntu:20.04
|
||||
- debian:12
|
||||
- debian:11
|
||||
- rockylinux/rockylinux:9
|
||||
- almalinux:8
|
||||
- fedora:42
|
||||
- archlinux:latest
|
||||
- opensuse/leap:15.6
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build the node core
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false -tags exitnode \
|
||||
-ldflags="-s -w -buildid=" -o "$RUNNER_TEMP/core" .
|
||||
echo "CORE_SHA=$(sha256sum "$RUNNER_TEMP/core" | cut -d' ' -f1)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Start the VDS
|
||||
run: |
|
||||
sh deploy/test/vds-dockerfile.sh "${{ matrix.image }}" > "$RUNNER_TEMP/Dockerfile"
|
||||
docker build -q -t vds -f "$RUNNER_TEMP/Dockerfile" "$RUNNER_TEMP"
|
||||
docker run -d --name vds --privileged --cgroupns=host \
|
||||
-v /sys/fs/cgroup:/sys/fs/cgroup:rw --tmpfs /run --tmpfs /run/lock vds
|
||||
for i in $(seq 1 60); do
|
||||
state=$(docker exec vds systemctl is-system-running 2>/dev/null || true)
|
||||
case "$state" in running|degraded) break ;; esac
|
||||
sleep 1
|
||||
done
|
||||
echo "systemd: $state"
|
||||
docker exec vds sh -c 'systemctl --version | head -n 1; cat /etc/os-release | grep PRETTY'
|
||||
docker exec vds sh -c 'systemctl is-active sshd 2>/dev/null || systemctl is-active ssh'
|
||||
# The core the test expects already installed (see TestInstallOnVDS).
|
||||
docker exec vds mkdir -p /opt/openflux-node/bin
|
||||
docker cp "$RUNNER_TEMP/core" vds:/opt/openflux-node/bin/openflux-node-v1.0.1
|
||||
docker exec vds sh -c 'chmod 0755 /opt/openflux-node /opt/openflux-node/bin /opt/openflux-node/bin/openflux-node-v1.0.1 && ln -sfn openflux-node-v1.0.1 /opt/openflux-node/bin/openflux'
|
||||
cp "$RUNNER_TEMP/core" core.bin
|
||||
|
||||
- name: TestInstallOnVDS
|
||||
run: |
|
||||
go_version=$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' go.mod)
|
||||
docker run --rm --network container:vds -v "$PWD:/src" -w /src \
|
||||
-e OPENFLUX_TEST_SSH=127.0.0.1:22 \
|
||||
-e OPENFLUX_TEST_ROOT_PASSWORD=rootpass \
|
||||
-e OPENFLUX_TEST_USER=deploy:deploypass \
|
||||
-e OPENFLUX_TEST_CORE_SHA="$CORE_SHA" \
|
||||
-e OPENFLUX_TEST_CORE_FILE=/src/core.bin \
|
||||
-e OPENFLUX_TEST_DOC=https://docs.yandex.ru/edit/d/AbCdEfGhIjKlMnOpQrSt \
|
||||
"golang:$go_version" go test ./provision -run TestInstallOnVDS -count=1 -v -timeout 20m
|
||||
|
||||
- name: VDS logs
|
||||
if: failure()
|
||||
run: |
|
||||
docker exec vds sh -c 'journalctl --no-pager -n 200 -u "openflux-node*" ; systemctl list-units --all "openflux*" --no-pager' || true
|
||||
@@ -60,3 +60,4 @@ watch_stats.sh
|
||||
*.log
|
||||
openflux
|
||||
*.bak
|
||||
core.bin
|
||||
|
||||
+101
-25
@@ -23,6 +23,12 @@
|
||||
# Usage: node-install.sh probe
|
||||
# node-install.sh plan|status (config on stdin)
|
||||
# node-install.sh apply|remove CONFIG_FILE (run as root)
|
||||
# node-install.sh remove CHANNEL (run as root: remove one
|
||||
# channel by name)
|
||||
# node-install.sh list (the channels and their state)
|
||||
# node-install.sh uninstall (run as root: remove every
|
||||
# channel and everything this
|
||||
# script installed)
|
||||
# node-install.sh upgrade (run as root: move every
|
||||
# channel to this core)
|
||||
# node-install.sh set-cookies CONFIG_FILE (run as root: replace a
|
||||
@@ -42,6 +48,9 @@
|
||||
# sudo read the config as further password attempts). Secrets never appear
|
||||
# in arguments, so they stay out of ps and shell history.
|
||||
# Output is one JSON object on stdout.
|
||||
#
|
||||
# Works with any systemd from 219 (CentOS 7) on: no "systemctl --now" or
|
||||
# "show --value", and the unit runs without StateDirectory= too.
|
||||
|
||||
set -u
|
||||
umask 077
|
||||
@@ -98,6 +107,18 @@ json_list() {
|
||||
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# unit_start / unit_stop UNIT: enable and start, stop and disable, in two
|
||||
# calls each: "systemctl --now" needs systemd 220.
|
||||
unit_start() { systemctl enable "$1" >/dev/null 2>&1 && systemctl start "$1" >/dev/null 2>&1; }
|
||||
unit_stop() {
|
||||
systemctl stop "$1" >/dev/null 2>&1
|
||||
systemctl disable "$1" >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
# main_pid UNIT: its main process id, 0 when none ("show --value" needs 230).
|
||||
main_pid() { systemctl show -p MainPID "$1" 2>/dev/null | sed -n 's/^MainPID=//p'; }
|
||||
|
||||
detect_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) echo amd64 ;;
|
||||
@@ -213,7 +234,8 @@ release_repo() {
|
||||
|
||||
port_busy() { # PORT
|
||||
if have ss; then
|
||||
[ -n "$(ss -Hltn "sport = :$1" 2>/dev/null)" ]
|
||||
# No -H: older iproute2 lacks it; the first line is the header.
|
||||
[ -n "$(ss -ltn "sport = :$1" 2>/dev/null | tail -n +2)" ]
|
||||
elif have netstat; then
|
||||
netstat -ltn 2>/dev/null | awk '{print $4}' | grep -q "[:.]$1\$"
|
||||
else
|
||||
@@ -410,7 +432,7 @@ cmd_plan() {
|
||||
CREATED_USER=0; CREATED_UNIT=0; CREATED_BIN=0; CREATED_CONF=0; CREATED_FW=""; STARTED=0
|
||||
|
||||
rollback() {
|
||||
[ "$STARTED" = 1 ] && systemctl disable --now "openflux-node@$CHANNEL" >/dev/null 2>&1
|
||||
[ "$STARTED" = 1 ] && unit_stop "openflux-node@$CHANNEL"
|
||||
case "$CREATED_FW" in
|
||||
ufw) ufw delete allow "$PORT/tcp" >/dev/null 2>&1 ;;
|
||||
firewalld) firewall-cmd --permanent --remove-port="$PORT/tcp" >/dev/null 2>&1 && firewall-cmd --reload >/dev/null 2>&1 ;;
|
||||
@@ -468,6 +490,8 @@ Type=simple
|
||||
User=$NODE_USER
|
||||
Group=$NODE_USER
|
||||
StateDirectory=openflux-node/%i
|
||||
# apply creates the directory too: systemd before 235 ignores StateDirectory=.
|
||||
ReadWritePaths=-$STATE_ROOT/%i
|
||||
WorkingDirectory=$STATE_ROOT/%i
|
||||
ExecStart=$BIN_DIR/openflux --config $CONF_ROOT/%i/node.conf
|
||||
Restart=always
|
||||
@@ -538,11 +562,11 @@ RandomizedDelaySec=30min
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
chmod 0644 "$UPDATE_SERVICE" "$UPDATE_TIMER"
|
||||
systemctl daemon-reload >/dev/null 2>&1 && systemctl enable --now openflux-node-update.timer >/dev/null 2>&1
|
||||
systemctl daemon-reload >/dev/null 2>&1 && unit_start openflux-node-update.timer
|
||||
}
|
||||
|
||||
disable_updater() {
|
||||
systemctl disable --now openflux-node-update.timer >/dev/null 2>&1
|
||||
unit_stop openflux-node-update.timer
|
||||
for f in "$UPDATE_TIMER" "$UPDATE_SERVICE"; do
|
||||
[ -f "$f" ] && grep -qF "$MARKER" "$f" && rm -f "$f"
|
||||
done
|
||||
@@ -622,6 +646,9 @@ cmd_apply() {
|
||||
chmod 0751 "$dir"
|
||||
chmod 0640 "$dir/encryption-key" "$dir/node.conf"
|
||||
chmod 0644 "$dir/port"
|
||||
# The node's state directory (systemd before 235 does not make it).
|
||||
mkdir -p "$STATE_ROOT/$CHANNEL" && chmod 0755 "$STATE_ROOT" && chown "$NODE_USER:$NODE_USER" "$STATE_ROOT/$CHANNEL" \
|
||||
&& chmod 0750 "$STATE_ROOT/$CHANNEL" || apply_fail config "не удалось создать $STATE_ROOT/$CHANNEL"
|
||||
if [ -n "$COOKIES" ]; then
|
||||
write_cookies || apply_fail cookies "не удалось сохранить вход в Яндекс на сервере"
|
||||
fi
|
||||
@@ -644,7 +671,7 @@ cmd_apply() {
|
||||
esac
|
||||
[ -n "$CREATED_FW" ] && printf '%s %s\n' "$CREATED_FW" "$PORT" > "$dir/firewall"
|
||||
|
||||
systemctl enable --now "openflux-node@$CHANNEL" >/dev/null 2>&1 \
|
||||
unit_start "openflux-node@$CHANNEL" \
|
||||
|| apply_fail start "не удалось запустить openflux-node@$CHANNEL"
|
||||
STARTED=1
|
||||
sleep 4
|
||||
@@ -659,32 +686,79 @@ cmd_apply() {
|
||||
printf '{"ok":true,"channel":"%s","port":%s,"core":"%s","autoupdate":%s}\n' "$CHANNEL" "$PORT" "$(core_to_use)" "$autoupdate"
|
||||
}
|
||||
|
||||
cmd_remove() {
|
||||
[ "$(id -u)" = 0 ] || fail remove "нужны права root (sudo)"
|
||||
read_config "$@"
|
||||
check_channel
|
||||
dir="$CONF_ROOT/$CHANNEL"
|
||||
[ -d "$dir" ] || fail remove "канала $CHANNEL нет на сервере"
|
||||
systemctl disable --now "openflux-node@$CHANNEL" >/dev/null 2>&1
|
||||
if [ -f "$dir/firewall" ]; then
|
||||
read -r kind port < "$dir/firewall"
|
||||
# remove_channel CHANNEL: stops the channel, closes its firewall port and
|
||||
# deletes its config and state.
|
||||
remove_channel() {
|
||||
unit_stop "openflux-node@$1"
|
||||
if [ -f "$CONF_ROOT/$1/firewall" ]; then
|
||||
read -r kind port < "$CONF_ROOT/$1/firewall"
|
||||
case "$kind" in
|
||||
ufw) ufw delete allow "$port/tcp" >/dev/null 2>&1 ;;
|
||||
firewalld) firewall-cmd --permanent --remove-port="$port/tcp" >/dev/null 2>&1 && firewall-cmd --reload >/dev/null 2>&1 ;;
|
||||
esac
|
||||
fi
|
||||
rm -rf "${CONF_ROOT:?}/$CHANNEL" "${STATE_ROOT:?}/$CHANNEL"
|
||||
if [ -z "$(list_channels)" ]; then
|
||||
# The last channel is gone: remove everything this script installed.
|
||||
disable_updater
|
||||
rm -f "$UNIT_FILE"
|
||||
systemctl daemon-reload >/dev/null 2>&1
|
||||
rm -rf /opt/openflux-node "$CONF_ROOT" "$STATE_ROOT"
|
||||
userdel "$NODE_USER" >/dev/null 2>&1
|
||||
rm -rf "${CONF_ROOT:?}/$1" "${STATE_ROOT:?}/$1"
|
||||
}
|
||||
|
||||
# remove_everything: what this script installed besides the channels: the
|
||||
# updater, the unit template, the cores, the folders and the node user.
|
||||
# Files that do not carry this script's marker are left alone.
|
||||
remove_everything() {
|
||||
disable_updater
|
||||
[ -f "$UNIT_FILE" ] && grep -qF "$MARKER" "$UNIT_FILE" && rm -f "$UNIT_FILE"
|
||||
systemctl daemon-reload >/dev/null 2>&1
|
||||
systemctl reset-failed 'openflux-node@*' >/dev/null 2>&1
|
||||
rm -rf /opt/openflux-node "$CONF_ROOT" "$STATE_ROOT"
|
||||
id "$NODE_USER" >/dev/null 2>&1 && userdel "$NODE_USER" >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
# remove CONFIG_FILE (the apps: channel= in a temp file) or remove CHANNEL
|
||||
# (by hand on the server).
|
||||
cmd_remove() {
|
||||
[ "$(id -u)" = 0 ] || fail remove "нужны права root (sudo)"
|
||||
if [ $# -gt 0 ] && [ ! -f "$1" ] && valid_channel "$1"; then
|
||||
CHANNEL=$1
|
||||
else
|
||||
read_config "$@"
|
||||
fi
|
||||
check_channel
|
||||
[ -d "$CONF_ROOT/$CHANNEL" ] || fail remove "канала $CHANNEL нет на сервере"
|
||||
remove_channel "$CHANNEL"
|
||||
# The last channel is gone: remove everything this script installed.
|
||||
[ -z "$(list_channels)" ] && remove_everything
|
||||
printf '{"ok":true,"channel":"%s"}\n' "$CHANNEL"
|
||||
}
|
||||
|
||||
# uninstall: every channel, then everything this script installed. Also
|
||||
# stops channel instances whose config is already gone.
|
||||
cmd_uninstall() {
|
||||
[ "$(id -u)" = 0 ] || fail uninstall "нужны права root (sudo)"
|
||||
set --
|
||||
for ch in $(list_channels); do
|
||||
remove_channel "$ch"
|
||||
set -- "$@" "$ch"
|
||||
done
|
||||
for unit in $(systemctl list-units --all --plain --no-legend 'openflux-node@*' 2>/dev/null | awk '{print $1}'); do
|
||||
unit_stop "$unit"
|
||||
done
|
||||
remove_everything
|
||||
printf '{"ok":true,"removed":%s}\n' "$(json_list "$@")"
|
||||
}
|
||||
|
||||
# list: the channels, their state and port; readable without root.
|
||||
cmd_list() {
|
||||
printf '{"ok":true,"core":"%s","autoupdate":%s,"channels":[' "$(installed_core)" "$(autoupdate_on && echo true || echo false)"
|
||||
first=1
|
||||
for ch in $(list_channels); do
|
||||
[ "$first" = 1 ] || printf ','
|
||||
first=0
|
||||
port=$(cat "$CONF_ROOT/$ch/port" 2>/dev/null)
|
||||
printf '{"channel":"%s","state":"%s","port":%s}' "$ch" "$(json_escape "$(systemctl is-active "openflux-node@$ch" 2>/dev/null)")" "${port:-0}"
|
||||
done
|
||||
printf ']}\n'
|
||||
}
|
||||
|
||||
# upgrade: switches every channel to this script's core and restarts the
|
||||
# running ones. Configs, keys and ports stay as they are.
|
||||
cmd_upgrade() {
|
||||
@@ -746,12 +820,12 @@ restart_channels() {
|
||||
channels_stay_up() {
|
||||
sleep 3
|
||||
pids=""
|
||||
for ch in "$@"; do pids="$pids $ch=$(systemctl show -p MainPID --value "openflux-node@$ch" 2>/dev/null)"; done
|
||||
for ch in "$@"; do pids="$pids $ch=$(main_pid "openflux-node@$ch")"; done
|
||||
sleep 20
|
||||
for pair in $pids; do
|
||||
ch=${pair%%=*}; pid=${pair#*=}
|
||||
systemctl is-active --quiet "openflux-node@$ch" || return 1
|
||||
[ -n "$pid" ] && [ "$pid" != 0 ] && [ "$pid" = "$(systemctl show -p MainPID --value "openflux-node@$ch" 2>/dev/null)" ] || return 1
|
||||
[ -n "$pid" ] && [ "$pid" != 0 ] && [ "$pid" = "$(main_pid "openflux-node@$ch")" ] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
@@ -869,10 +943,12 @@ case "${1:-}" in
|
||||
plan) cmd_plan ;;
|
||||
apply) shift; cmd_apply "$@" ;;
|
||||
remove) shift; cmd_remove "$@" ;;
|
||||
uninstall) cmd_uninstall ;;
|
||||
list) cmd_list ;;
|
||||
status) cmd_status ;;
|
||||
upgrade) cmd_upgrade ;;
|
||||
set-cookies) shift; cmd_set_cookies "$@" ;;
|
||||
update) cmd_update ;;
|
||||
autoupdate) shift; cmd_autoupdate "$@" ;;
|
||||
*) fail usage "usage: node-install.sh probe|plan|apply|remove|status|upgrade|set-cookies|update|autoupdate" ;;
|
||||
*) fail usage "usage: node-install.sh probe|plan|apply|remove|list|uninstall|status|upgrade|set-cookies|update|autoupdate" ;;
|
||||
esac
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
# vds-dockerfile.sh BASE_IMAGE: a Dockerfile (on stdout) for a test "VDS"
|
||||
# from a stock distribution image: systemd as PID 1, sshd with root and
|
||||
# password logins, and a sudoer "deploy" whose sudo needs its password -
|
||||
# what provision.TestInstallOnVDS drives node-install.sh on.
|
||||
# Passwords: root "rootpass", deploy "deploypass".
|
||||
set -eu
|
||||
base=$1
|
||||
cat <<DOCKERFILE
|
||||
FROM $base
|
||||
ENV container=docker
|
||||
RUN set -eu; \\
|
||||
if command -v apt-get >/dev/null; then \\
|
||||
export DEBIAN_FRONTEND=noninteractive; apt-get update; \\
|
||||
apt-get install -y --no-install-recommends systemd systemd-sysv dbus openssh-server sudo curl ca-certificates iproute2 procps passwd; \\
|
||||
admin=sudo; \\
|
||||
elif command -v dnf >/dev/null; then \\
|
||||
dnf install -y systemd openssh-server sudo iproute procps-ng passwd shadow-utils findutils which; \\
|
||||
command -v curl >/dev/null || dnf install -y curl; \\
|
||||
admin=wheel; \\
|
||||
elif command -v yum >/dev/null; then \\
|
||||
yum install -y systemd openssh-server sudo curl iproute procps-ng passwd shadow-utils which; \\
|
||||
admin=wheel; \\
|
||||
elif command -v pacman >/dev/null; then \\
|
||||
pacman -Syu --noconfirm --needed systemd openssh sudo curl iproute2 procps-ng shadow; \\
|
||||
admin=wheel; \\
|
||||
elif command -v zypper >/dev/null; then \\
|
||||
zypper --non-interactive install systemd openssh sudo curl iproute2 procps shadow; \\
|
||||
admin=wheel; groupadd -f wheel; \\
|
||||
sed -i -e 's/^Defaults targetpw/# &/' -e 's/^ALL[[:space:]]*ALL=(ALL) ALL/# &/' /etc/sudoers; \\
|
||||
else echo "unknown package manager" >&2; exit 1; fi; \\
|
||||
echo "%\$admin ALL=(ALL) ALL" > /etc/sudoers.d/90-test; chmod 0440 /etc/sudoers.d/90-test; \\
|
||||
echo root:rootpass | chpasswd; \\
|
||||
useradd -m -s /bin/sh deploy; usermod -aG "\$admin" deploy; echo deploy:deploypass | chpasswd; \\
|
||||
ssh-keygen -A; \\
|
||||
sed -i '1i PermitRootLogin yes\\nPasswordAuthentication yes\\nUsePAM yes' /etc/ssh/sshd_config; \\
|
||||
rm -f /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true; \\
|
||||
rm -f /run/nologin /etc/nologin; \\
|
||||
systemctl enable sshd 2>/dev/null || systemctl enable ssh; \\
|
||||
systemctl mask getty.target console-getty.service systemd-firstboot.service 2>/dev/null || true; \\
|
||||
ln -sf "\$(ls /lib/systemd/systemd /usr/lib/systemd/systemd 2>/dev/null | head -n 1)" /usr/local/sbin/test-init
|
||||
STOPSIGNAL SIGRTMIN+3
|
||||
CMD ["/usr/local/sbin/test-init"]
|
||||
DOCKERFILE
|
||||
@@ -1,59 +0,0 @@
|
||||
package mobile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/p1neappleXpress/OpenFlux/transport/manager"
|
||||
)
|
||||
|
||||
// clientSession is the running client Session's manager and its
|
||||
// transports' types by name, so the app can hand the exit a sign-in.
|
||||
var clientSession struct {
|
||||
mu sync.Mutex
|
||||
m *manager.Manager
|
||||
types map[string]string
|
||||
}
|
||||
|
||||
func setClientSession(m *manager.Manager, types map[string]string) {
|
||||
clientSession.mu.Lock()
|
||||
clientSession.m, clientSession.types = m, types
|
||||
clientSession.mu.Unlock()
|
||||
}
|
||||
|
||||
// OfferExitCookies sends a sign-in (a Cookie header, "a=1; b=2") to the
|
||||
// exit for every transport of the client Session whose type is in
|
||||
// transportTypes (comma-separated, e.g. "vyandex,yandex,boards"). The exit
|
||||
// applies and keeps it. Returns how many transports it went to, or an
|
||||
// error when no Session is connected.
|
||||
func OfferExitCookies(transportTypes, cookieHeader string) (int, error) {
|
||||
jar := parseCookieHeader(cookieHeader)
|
||||
if len(jar) == 0 {
|
||||
return 0, fmt.Errorf("нет cookies")
|
||||
}
|
||||
wanted := map[string]bool{}
|
||||
for _, t := range strings.Split(transportTypes, ",") {
|
||||
if t = strings.TrimSpace(t); t != "" {
|
||||
wanted[t] = true
|
||||
}
|
||||
}
|
||||
clientSession.mu.Lock()
|
||||
m, types := clientSession.m, clientSession.types
|
||||
clientSession.mu.Unlock()
|
||||
if m == nil || !m.IsConnected() {
|
||||
return 0, fmt.Errorf("нет подключения к ноде")
|
||||
}
|
||||
sent := 0
|
||||
for name, typ := range types {
|
||||
if !wanted[typ] {
|
||||
continue
|
||||
}
|
||||
if err := m.OfferCookies(name, jar); err != nil {
|
||||
return sent, fmt.Errorf("%s: %w", name, err)
|
||||
}
|
||||
sent++
|
||||
}
|
||||
appendLog(fmt.Sprintf("[ANDROID] Вход передан ноде: %d транспорт(ов)", sent))
|
||||
return sent, nil
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package mobile
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestOfferExitCookiesWithoutSession(t *testing.T) {
|
||||
setClientSession(nil, nil)
|
||||
if _, err := OfferExitCookies("vyandex", "Session_id=s"); err == nil {
|
||||
t.Fatal("want an error without a connected Session")
|
||||
}
|
||||
if _, err := OfferExitCookies("vyandex", ""); err == nil {
|
||||
t.Fatal("want an error without cookies")
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,6 @@ func buildSessionWith(specsJSON, secret string, exit bool, opt sessionOptions) (
|
||||
proxy := &authProxy{demux: demux}
|
||||
setAuthProxy(proxy)
|
||||
attachSessionCaptcha(m, keys, proxy)
|
||||
setClientSession(m, types)
|
||||
appendLog("[ANDROID] Session: шифрование AES-256-GCM, согласование с нодой")
|
||||
return demux, sess, nil
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestInstallOnVDS(t *testing.T) {
|
||||
good = Pinned()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
root := Target{Host: host, Port: port, User: "root", Password: os.Getenv("OPENFLUX_TEST_ROOT_PASSWORD")}
|
||||
@@ -290,6 +290,46 @@ func TestInstallOnVDS(t *testing.T) {
|
||||
if out, _, _ := c.run("test -e /etc/systemd/system/openflux-node-update.timer && echo left", nil); strings.TrimSpace(string(out)) != "" {
|
||||
t.Fatal("updater left after the last channel was removed")
|
||||
}
|
||||
|
||||
// By hand on the server: list, remove one channel by name, uninstall.
|
||||
if err := c.FetchScript(good); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range []string{"first", "second"} {
|
||||
p, err := c.Plan(Channel{ID: name, AutoUpdate: true}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
k, _ := NewKey()
|
||||
if err := c.Apply(Channel{ID: name, Key: k, Port: p.Port, AutoUpdate: true}, userPass); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if out := sudo("stat -c '%a %U' /var/lib/openflux-node/first"); out != "750 openflux-node" {
|
||||
t.Fatalf("state directory: %q", out)
|
||||
}
|
||||
if out := sudo("sh /opt/openflux-node/node-install.sh list"); !strings.Contains(out, `"channel":"first","state":"active"`) ||
|
||||
!strings.Contains(out, `"channel":"second","state":"active"`) || !strings.Contains(out, `"autoupdate":true`) {
|
||||
t.Fatalf("list: %s", out)
|
||||
}
|
||||
if out := sudo("sh /opt/openflux-node/node-install.sh remove first"); !strings.Contains(out, `"ok":true`) {
|
||||
t.Fatalf("remove by name: %s", out)
|
||||
}
|
||||
if out := sudo("test -e /etc/openflux-node/first && echo left; systemctl is-active openflux-node@first; systemctl is-active openflux-node@second"); out != "inactive\nactive" {
|
||||
t.Fatalf("after remove first: %q", out)
|
||||
}
|
||||
if out := sudo("sh /opt/openflux-node/node-install.sh remove nosuch"); !strings.Contains(out, `"ok":false`) {
|
||||
t.Fatalf("remove of a missing channel: %s", out)
|
||||
}
|
||||
if out := sudo("sh /opt/openflux-node/node-install.sh uninstall"); !strings.Contains(out, `"removed":["second"]`) {
|
||||
t.Fatalf("uninstall: %s", out)
|
||||
}
|
||||
left := sudo("for p in /opt/openflux-node /etc/openflux-node /var/lib/openflux-node /etc/systemd/system/openflux-node@.service " +
|
||||
"/etc/systemd/system/openflux-node-update.timer /etc/systemd/system/openflux-node-update.service; do test -e $p && echo $p; done; " +
|
||||
"id openflux-node >/dev/null 2>&1 && echo user; systemctl is-active openflux-node@second; ps -eo args | grep -c '[o]penflux --config'")
|
||||
if left != "inactive\n0" {
|
||||
t.Fatalf("left after uninstall: %q", left)
|
||||
}
|
||||
}
|
||||
|
||||
// fakeReleases is GitHub for the updater: the releases listing, each
|
||||
|
||||
Reference in New Issue
Block a user