fix(reliability): encode str stdin in device_run; smoke drill path fixes + regression tests
This commit is contained in:
@@ -107,6 +107,8 @@ SELF_PAYLOAD_KEY = os.environ.get('PAGER_SELF_PAYLOAD_KEY', 'user~remote_access~
|
|||||||
|
|
||||||
def device_run(args, timeout=20, input_data=None):
|
def device_run(args, timeout=20, input_data=None):
|
||||||
try:
|
try:
|
||||||
|
if isinstance(input_data, str):
|
||||||
|
input_data = input_data.encode('utf-8')
|
||||||
p = subprocess.run(args, input=input_data, capture_output=True, timeout=timeout)
|
p = subprocess.run(args, input=input_data, capture_output=True, timeout=timeout)
|
||||||
return p.returncode, p.stdout.decode('utf-8', 'replace'), p.stderr.decode('utf-8', 'replace')
|
return p.returncode, p.stdout.decode('utf-8', 'replace'), p.stderr.decode('utf-8', 'replace')
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
|||||||
+22
-7
@@ -39,6 +39,9 @@ set -u
|
|||||||
|
|
||||||
BASE=/mmc/mk8
|
BASE=/mmc/mk8
|
||||||
REL="$BASE/releases/current"
|
REL="$BASE/releases/current"
|
||||||
|
# Release layout: <release>/user/<category>/<key>/... — resolve the payload dir.
|
||||||
|
REL_PAY="$(find "$REL/user" -maxdepth 3 -name server.py 2>/dev/null | head -n 1)"
|
||||||
|
REL_PAY="${REL_PAY%/server.py}"
|
||||||
LEGACY=/root/payloads/user/remote_access/pager-webui
|
LEGACY=/root/payloads/user/remote_access/pager-webui
|
||||||
URL=http://127.0.0.1:8080
|
URL=http://127.0.0.1:8080
|
||||||
GUARD_INIT=/etc/init.d/mk8-guard
|
GUARD_INIT=/etc/init.d/mk8-guard
|
||||||
@@ -221,9 +224,9 @@ check_monitors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_versions() {
|
check_versions() {
|
||||||
sv="$(grep '^SERVER_VERSION' "$REL/server.py" 2>/dev/null | head -n 1 \
|
sv="$(grep '^SERVER_VERSION' "$REL_PAY/server.py" 2>/dev/null | head -n 1 \
|
||||||
| sed -e 's/^SERVER_VERSION = //' -e "s/'//g" | tr -d '\r')"
|
| sed -e 's/^SERVER_VERSION = //' -e "s/'//g" | tr -d '\r')"
|
||||||
pv="$(grep '^#[ ]*[Vv]ersion:' "$REL/payload.sh" 2>/dev/null | head -n 1 \
|
pv="$(grep '^#[ ]*[Vv]ersion:' "$REL_PAY/payload.sh" 2>/dev/null | head -n 1 \
|
||||||
| sed 's/^#[ ]*[Vv]ersion:[ ]*//' | tr -d '\r')"
|
| sed 's/^#[ ]*[Vv]ersion:[ ]*//' | tr -d '\r')"
|
||||||
if [ -n "$sv" ] && [ "$sv" = "$pv" ]; then
|
if [ -n "$sv" ] && [ "$sv" = "$pv" ]; then
|
||||||
pass "versions: release server.py and payload.sh agree ($sv)"
|
pass "versions: release server.py and payload.sh agree ($sv)"
|
||||||
@@ -241,7 +244,7 @@ check_authed_api() {
|
|||||||
fail 'authed API: python3 not found for response check'
|
fail 'authed API: python3 not found for response check'
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
body='{"password":"'"$PASS"'"}'
|
body='{"username":"root","password":"'"$PASS"'"}'
|
||||||
code="$(curl -fsS -m 10 -o /dev/null -w '%{http_code}' \
|
code="$(curl -fsS -m 10 -o /dev/null -w '%{http_code}' \
|
||||||
-c "$JAR" -H 'Content-Type: application/json' \
|
-c "$JAR" -H 'Content-Type: application/json' \
|
||||||
-d "$body" "$URL/api/login" 2>/dev/null)"
|
-d "$body" "$URL/api/login" 2>/dev/null)"
|
||||||
@@ -272,7 +275,7 @@ drill_bad_values() {
|
|||||||
|
|
||||||
uci set pineapd.wlan1mon.bands='2,5'
|
uci set pineapd.wlan1mon.bands='2,5'
|
||||||
uci commit pineapd
|
uci commit pineapd
|
||||||
"$PY" "$REL/server.py" --reconcile >/dev/null 2>&1
|
"$PY" "$REL_PAY/server.py" --reconcile >/dev/null 2>&1
|
||||||
got="$(uci_get pineapd.wlan1mon.bands)"
|
got="$(uci_get pineapd.wlan1mon.bands)"
|
||||||
if [ "$got" = "5" ]; then
|
if [ "$got" = "5" ]; then
|
||||||
pass 'drill: reconcile repaired bands 2,5 -> 5'
|
pass 'drill: reconcile repaired bands 2,5 -> 5'
|
||||||
@@ -289,7 +292,7 @@ drill_bad_values() {
|
|||||||
done
|
done
|
||||||
uci set "pineapd.@ssidpool[0].ssid=${list# }"
|
uci set "pineapd.@ssidpool[0].ssid=${list# }"
|
||||||
uci commit pineapd
|
uci commit pineapd
|
||||||
"$PY" "$REL/server.py" --reconcile >/dev/null 2>&1
|
"$PY" "$REL_PAY/server.py" --reconcile >/dev/null 2>&1
|
||||||
got="$(uci_get 'pineapd.@ssidpool[0].ssid')"
|
got="$(uci_get 'pineapd.@ssidpool[0].ssid')"
|
||||||
if [ -z "$got" ]; then
|
if [ -z "$got" ]; then
|
||||||
pass 'drill: reconcile cleared oversized SSID pool'
|
pass 'drill: reconcile cleared oversized SSID pool'
|
||||||
@@ -303,7 +306,7 @@ drill_bad_values() {
|
|||||||
uci -q delete 'pineapd.@ssidpool[0].ssid'
|
uci -q delete 'pineapd.@ssidpool[0].ssid'
|
||||||
fi
|
fi
|
||||||
uci commit pineapd
|
uci commit pineapd
|
||||||
"$PY" "$REL/server.py" --reconcile >/dev/null 2>&1
|
"$PY" "$REL_PAY/server.py" --reconcile >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
drill_role() {
|
drill_role() {
|
||||||
@@ -368,12 +371,24 @@ PYEOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
drill_watchdog() {
|
drill_watchdog() {
|
||||||
WDOG="$REL/mk8-watchdog.sh"
|
WDOG="$REL_PAY/mk8-watchdog.sh"
|
||||||
[ -f "$WDOG" ] || WDOG="$LEGACY/mk8-watchdog.sh"
|
[ -f "$WDOG" ] || WDOG="$LEGACY/mk8-watchdog.sh"
|
||||||
if [ ! -f "$WDOG" ]; then
|
if [ ! -f "$WDOG" ]; then
|
||||||
fail 'drill: mk8-watchdog.sh not found in release or legacy dir'
|
fail 'drill: mk8-watchdog.sh not found in release or legacy dir'
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
# Ensure a profile exists: snapshot the live config as the drill target.
|
||||||
|
if [ ! -d "$BASE/profiles" ] || [ -z "$(ls "$BASE/profiles" 2>/dev/null)" ]; then
|
||||||
|
"$PY" - "$REL_PAY" <<'PYEOF' >/dev/null 2>&1 || {
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, sys.argv[1])
|
||||||
|
import mk8_profiles
|
||||||
|
mk8_profiles.snapshot('smoke-drill')
|
||||||
|
PYEOF
|
||||||
|
fail 'drill: could not snapshot a profile for watchdog drill'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fi
|
||||||
prof=lastknown-good
|
prof=lastknown-good
|
||||||
[ -d "$BASE/profiles/$prof" ] \
|
[ -d "$BASE/profiles/$prof" ] \
|
||||||
|| prof="$(ls "$BASE/profiles" 2>/dev/null | head -n 1)"
|
|| prof="$(ls "$BASE/profiles" 2>/dev/null | head -n 1)"
|
||||||
|
|||||||
@@ -69,3 +69,15 @@ class DeviceRunTest(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRunInputTest(unittest.TestCase):
|
||||||
|
def test_str_input_data_is_encoded(self):
|
||||||
|
rc, out, err = server.device_run(['cat'], input_data='uci import text')
|
||||||
|
self.assertEqual(rc, 0)
|
||||||
|
self.assertEqual(out, 'uci import text')
|
||||||
|
|
||||||
|
def test_bytes_input_data_passes_through(self):
|
||||||
|
rc, out, err = server.device_run(['cat'], input_data=b'raw')
|
||||||
|
self.assertEqual(rc, 0)
|
||||||
|
self.assertEqual(out, 'raw')
|
||||||
|
|||||||
Reference in New Issue
Block a user