fix: repair memlimits script and finish #685 apply pass
Env var name mismatch (CLOUDRON_API_TOKEN), dash echo corrupting the 1MB apps JSON (backslash escapes), scheme-prefixed fqdn filter that matched nothing, unset curmbMB under set -u, and the wrong endpoint (POST /configure/memory_limit is the real route). Apply now complete: 15/16 were already at target; photos floor-limited 4096->3584 (Immich manifest floor), task 15687, healthy. Detail: https://projects.knownelement.com/issues/685
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# cloudron-memlimits.sh - right-size app memory limits [#685]
|
# cloudron-memlimits.sh - right-size app memory limits [#685]
|
||||||
# Reads CLOUDRON_URL + CLOUDRON_TOKEN from ~/.creds/cloudron.env
|
# Reads CLOUDRON_URL + CLOUDRON_API_TOKEN from ~/.creds/cloudron.env
|
||||||
# Usage: cloudron-memlimits.sh [canary|apply|plan]
|
# Usage: cloudron-memlimits.sh [canary|apply|plan]
|
||||||
# plan (default) show current vs proposed, change nothing
|
# plan (default) show current vs proposed, change nothing
|
||||||
# canary apply axiosheartstudios.com only, then verify
|
# canary apply axiosheartstudios.com only, then verify
|
||||||
@@ -10,7 +10,7 @@ set -eu
|
|||||||
ENV_FILE="$HOME/.creds/cloudron.env"
|
ENV_FILE="$HOME/.creds/cloudron.env"
|
||||||
[ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE"; exit 1; }
|
[ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE"; exit 1; }
|
||||||
. "$ENV_FILE"
|
. "$ENV_FILE"
|
||||||
: "${CLOUDRON_URL:?}" "${CLOUDRON_TOKEN:?}"
|
: "${CLOUDRON_URL:?}" "${CLOUDRON_API_TOKEN:?}"
|
||||||
|
|
||||||
MODE="${1:-plan}"
|
MODE="${1:-plan}"
|
||||||
|
|
||||||
@@ -20,7 +20,9 @@ axiosheartstudios.com:512
|
|||||||
community.turnsys.com:4096
|
community.turnsys.com:4096
|
||||||
nextcloud.knownelement.com:2048
|
nextcloud.knownelement.com:2048
|
||||||
bookmarks.knownelement.com:2048
|
bookmarks.knownelement.com:2048
|
||||||
photos.knownelement.com:3072
|
# photos floor: Immich package manifest declares 3758096384B (3584MB) as
|
||||||
|
# the minimum user-settable limit; the census's 3072 was unsatisfiable
|
||||||
|
photos.knownelement.com:3584
|
||||||
share.knownelement.com:512
|
share.knownelement.com:512
|
||||||
hfnfc.net:512
|
hfnfc.net:512
|
||||||
notes.knownelement.com:1024
|
notes.knownelement.com:1024
|
||||||
@@ -35,20 +37,37 @@ cmdb.knownelement.com:512
|
|||||||
"
|
"
|
||||||
|
|
||||||
api() {
|
api() {
|
||||||
curl -s -m 30 -H "Authorization: Bearer $CLOUDRON_TOKEN" "$@"
|
curl -s -m 30 -H "Authorization: Bearer $CLOUDRON_API_TOKEN" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
APPS=$(api "$CLOUDRON_URL/api/v1/apps?per_page=200")
|
# fetch to a temp file with one retry: the 1MB apps list occasionally
|
||||||
echo "$APPS" | jq -e '.apps' >/dev/null 2>&1 || { echo "API auth/list failed"; exit 1; }
|
# stalls mid-transfer, and a truncated body would otherwise fail jq later
|
||||||
|
fetch_apps() {
|
||||||
|
local attempt out
|
||||||
|
for attempt in 1 2; do
|
||||||
|
out=$(api -o "$APPS_JSON" -w '%{http_code} %{size_download}' \
|
||||||
|
"$CLOUDRON_URL/api/v1/apps?per_page=200") || true
|
||||||
|
[ "${out%% *}" = "200" ] && [ "${out##* }" -gt 1000 ] && return 0
|
||||||
|
echo "fetch attempt $attempt failed ($out), retrying" >&2
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
APPS_JSON="/tmp/cloudron-apps.$$.json"
|
||||||
|
trap 'rm -f "$APPS_JSON"' EXIT
|
||||||
|
fetch_apps || { echo "API auth/list failed"; exit 1; }
|
||||||
|
jq -e '.apps' "$APPS_JSON" >/dev/null 2>&1 || { echo "API auth/list failed (bad JSON)"; exit 1; }
|
||||||
|
|
||||||
reclaimed=0
|
reclaimed=0
|
||||||
for line in $PROPOSALS; do
|
for line in $PROPOSALS; do
|
||||||
fqdn=${line%%:*}; newmb=${line##*:}
|
fqdn=${line%%:*}; newmb=${line##*:}
|
||||||
match=$(echo "$APPS" | jq -r --arg f "https://$fqdn" \
|
# API returns bare hosts in .fqdn (no scheme); subdomain apps carry
|
||||||
'.apps[] | select(.fqdn==$f or (.domain==$f and (.location=="" or .location=="@"))) | "\(.id) \(.memoryLimit)"' | head -1)
|
# location:null, so matching fqdn alone is sufficient
|
||||||
|
match=$(jq -r --arg f "$fqdn" \
|
||||||
|
'.apps[] | select(.fqdn==$f) | "\(.id) \(.memoryLimit)"' "$APPS_JSON" | head -1)
|
||||||
[ -z "$match" ] && { echo "SKIP $fqdn (not an app)"; continue; }
|
[ -z "$match" ] && { echo "SKIP $fqdn (not an app)"; continue; }
|
||||||
appid=${match%% *}; curmb=$(( ${match##* } / 1048576 ))
|
appid=${match%% *}; curmb=$(( ${match##* } / 1048576 ))
|
||||||
[ "$curmb" -le "$newmb" ] && { echo "SKIP $fqdn (already $curmbMB <= $newmb)"; continue; }
|
[ "$curmb" -le "$newmb" ] && { echo "SKIP $fqdn (already ${curmb}MB <= ${newmb}MB)"; continue; }
|
||||||
|
|
||||||
case "$MODE" in
|
case "$MODE" in
|
||||||
plan)
|
plan)
|
||||||
@@ -57,12 +76,12 @@ for line in $PROPOSALS; do
|
|||||||
canary)
|
canary)
|
||||||
[ "$fqdn" != "axiosheartstudios.com" ] && continue
|
[ "$fqdn" != "axiosheartstudios.com" ] && continue
|
||||||
echo "CANARY $fqdn: ${curmb}MB -> ${newmb}MB"
|
echo "CANARY $fqdn: ${curmb}MB -> ${newmb}MB"
|
||||||
api -X POST "$CLOUDRON_URL/api/v1/apps/$appid" -H 'Content-Type: application/json' \
|
api -X POST "$CLOUDRON_URL/api/v1/apps/$appid/configure/memory_limit" -H 'Content-Type: application/json' \
|
||||||
-d "{\"memoryLimit\": $((newmb * 1048576))}" | jq -r '.status // .message // .'
|
-d "{\"memoryLimit\": $((newmb * 1048576))}" | jq -r '.status // .message // .'
|
||||||
;;
|
;;
|
||||||
apply)
|
apply)
|
||||||
echo "APPLY $fqdn: ${curmb}MB -> ${newmb}MB"
|
echo "APPLY $fqdn: ${curmb}MB -> ${newmb}MB"
|
||||||
api -X POST "$CLOUDRON_URL/api/v1/apps/$appid" -H 'Content-Type: application/json' \
|
api -X POST "$CLOUDRON_URL/api/v1/apps/$appid/configure/memory_limit" -H 'Content-Type: application/json' \
|
||||||
-d "{\"memoryLimit\": $((newmb * 1048576))}" | jq -r '.status // .message // .'
|
-d "{\"memoryLimit\": $((newmb * 1048576))}" | jq -r '.status // .message // .'
|
||||||
sleep 8
|
sleep 8
|
||||||
;;
|
;;
|
||||||
|
|||||||
Reference in New Issue
Block a user