Fix Synology Admin upgrade without docker.sock (rw binary mount).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
20433dd747
commit
c3755b48a5
8 changed files with 67 additions and 13 deletions
|
|
@ -65,7 +65,7 @@ export default function NodesPage() {
|
||||||
const [editRoots, setEditRoots] = useState<ScanRootRow[]>([]);
|
const [editRoots, setEditRoots] = useState<ScanRootRow[]>([]);
|
||||||
const [editBusy, setEditBusy] = useState(false);
|
const [editBusy, setEditBusy] = useState(false);
|
||||||
const [upgradingId, setUpgradingId] = useState<string | null>(null);
|
const [upgradingId, setUpgradingId] = useState<string | null>(null);
|
||||||
const [targetNodeVersion, setTargetNodeVersion] = useState("1.3.5");
|
const [targetNodeVersion, setTargetNodeVersion] = useState("1.3.6");
|
||||||
|
|
||||||
function loadNodes() {
|
function loadNodes() {
|
||||||
fetch("/api/v1/admin/nodes", { credentials: "include" })
|
fetch("/api/v1/admin/nodes", { credentials: "include" })
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/** Target media-node version served from /install/media-node-linux-* */
|
/** Target media-node version served from /install/media-node-linux-* */
|
||||||
export const MEDIA_NODE_TARGET_VERSION =
|
export const MEDIA_NODE_TARGET_VERSION =
|
||||||
process.env.MEDIA_NODE_VERSION?.trim() || "1.3.5";
|
process.env.MEDIA_NODE_VERSION?.trim() || "1.3.6";
|
||||||
|
|
||||||
/** Minimum node version that handles UPGRADE over WebSocket */
|
/** Minimum node version that handles UPGRADE over WebSocket */
|
||||||
const REMOTE_UPGRADE_MIN_VERSION = "1.3.3";
|
const REMOTE_UPGRADE_MIN_VERSION = "1.3.3";
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ WORKDIR /app
|
||||||
|
|
||||||
FROM golang:1.22-alpine AS node-build
|
FROM golang:1.22-alpine AS node-build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
ARG MEDIA_NODE_VERSION=1.3.5
|
ARG MEDIA_NODE_VERSION=1.3.6
|
||||||
RUN apk add --no-cache git ca-certificates
|
RUN apk add --no-cache git ca-certificates
|
||||||
COPY node/media-node/ ./
|
COPY node/media-node/ ./
|
||||||
RUN go mod tidy \
|
RUN go mod tidy \
|
||||||
|
|
|
||||||
|
|
@ -163,13 +163,16 @@ docker_cmd rm -f media-node >/dev/null 2>&1 || true
|
||||||
VOLUMES=(
|
VOLUMES=(
|
||||||
-v "$APPDATA/config:/etc/media-node"
|
-v "$APPDATA/config:/etc/media-node"
|
||||||
-v "$APPDATA/data:/var/lib/media-node"
|
-v "$APPDATA/data:/var/lib/media-node"
|
||||||
-v "$BIN_PATH:/usr/local/bin/media-node:ro"
|
# :rw zodat Admin→Upgrade de binary kan overschrijven zonder docker.sock
|
||||||
|
# (op DSM is /var/run/docker.sock meestal root-only → permission denied in de container)
|
||||||
|
-v "$BIN_PATH:/usr/local/bin/media-node:rw"
|
||||||
|
-v "$APPDATA/bin:/opt/media-node-bin:rw"
|
||||||
-v "$MEDIA_ROOT:$MEDIA_ROOT:rw"
|
-v "$MEDIA_ROOT:$MEDIA_ROOT:rw"
|
||||||
)
|
)
|
||||||
if [ -d /etc/ssl/certs ]; then
|
if [ -d /etc/ssl/certs ]; then
|
||||||
VOLUMES+=(-v /etc/ssl/certs:/etc/ssl/certs:ro)
|
VOLUMES+=(-v /etc/ssl/certs:/etc/ssl/certs:ro)
|
||||||
fi
|
fi
|
||||||
# Admin → Herstart via docker.sock (DSM: vaak root:root — group-add helpt niet altijd)
|
# Optioneel: docker.sock voor Admin→Herstart (faalt vaak op DSM; restart-policy vangt exit op)
|
||||||
if [ -S /var/run/docker.sock ]; then
|
if [ -S /var/run/docker.sock ]; then
|
||||||
VOLUMES+=(-v /var/run/docker.sock:/var/run/docker.sock)
|
VOLUMES+=(-v /var/run/docker.sock:/var/run/docker.sock)
|
||||||
fi
|
fi
|
||||||
|
|
@ -202,6 +205,7 @@ docker_cmd run -d \
|
||||||
-e "MEDIA_NODE_FULL_SCAN_INTERVAL=off" \
|
-e "MEDIA_NODE_FULL_SCAN_INTERVAL=off" \
|
||||||
-e "MEDIA_NODE_FULL_SCAN_AT=03:30" \
|
-e "MEDIA_NODE_FULL_SCAN_AT=03:30" \
|
||||||
-e "MEDIA_NODE_CONTAINER_NAME=media-node" \
|
-e "MEDIA_NODE_CONTAINER_NAME=media-node" \
|
||||||
|
-e "MEDIA_NODE_BIN_DIR=/opt/media-node-bin" \
|
||||||
-e "TZ=${TZ:-Europe/Amsterdam}" \
|
-e "TZ=${TZ:-Europe/Amsterdam}" \
|
||||||
--user "${PUID}:${PGID}" \
|
--user "${PUID}:${PGID}" \
|
||||||
"${GROUP_ARGS[@]}" \
|
"${GROUP_ARGS[@]}" \
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,8 @@ docker rm -f media-node >/dev/null 2>&1 || true
|
||||||
VOLUMES=(
|
VOLUMES=(
|
||||||
-v "$APPDATA/config:/etc/media-node"
|
-v "$APPDATA/config:/etc/media-node"
|
||||||
-v "$APPDATA/data:/var/lib/media-node"
|
-v "$APPDATA/data:/var/lib/media-node"
|
||||||
-v "$BIN_PATH:/usr/local/bin/media-node:ro"
|
-v "$BIN_PATH:/usr/local/bin/media-node:rw"
|
||||||
|
-v "$APPDATA/bin:/opt/media-node-bin:rw"
|
||||||
-v /etc/ssl/certs:/etc/ssl/certs:ro
|
-v /etc/ssl/certs:/etc/ssl/certs:ro
|
||||||
-v "$MEDIA_ROOT:$MEDIA_ROOT:ro"
|
-v "$MEDIA_ROOT:$MEDIA_ROOT:ro"
|
||||||
)
|
)
|
||||||
|
|
@ -182,6 +183,7 @@ docker run -d \
|
||||||
-e "MEDIA_NODE_FULL_SCAN_INTERVAL=off" \
|
-e "MEDIA_NODE_FULL_SCAN_INTERVAL=off" \
|
||||||
-e "MEDIA_NODE_FULL_SCAN_AT=03:30" \
|
-e "MEDIA_NODE_FULL_SCAN_AT=03:30" \
|
||||||
-e "MEDIA_NODE_CONTAINER_NAME=media-node" \
|
-e "MEDIA_NODE_CONTAINER_NAME=media-node" \
|
||||||
|
-e "MEDIA_NODE_BIN_DIR=/opt/media-node-bin" \
|
||||||
-e "TZ=${TZ:-Europe/Amsterdam}" \
|
-e "TZ=${TZ:-Europe/Amsterdam}" \
|
||||||
--user 99:100 \
|
--user 99:100 \
|
||||||
"${GROUP_ARGS[@]}" \
|
"${GROUP_ARGS[@]}" \
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,20 @@ done
|
||||||
MASTER_URL="$(echo -n "$MASTER_URL" | sed 's:/*$::')"
|
MASTER_URL="$(echo -n "$MASTER_URL" | sed 's:/*$::')"
|
||||||
APPDATA="$(echo -n "$APPDATA" | tr -d '\r\n')"
|
APPDATA="$(echo -n "$APPDATA" | tr -d '\r\n')"
|
||||||
|
|
||||||
if ! command -v docker >/dev/null 2>&1; then
|
docker_cmd() {
|
||||||
|
if docker info >/dev/null 2>&1; then
|
||||||
|
docker "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if command -v sudo >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then
|
||||||
|
sudo docker "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Docker niet gevonden of geen rechten. Draai als root (Synology: sudo -i)."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! command -v docker >/dev/null 2>&1 && ! command -v sudo >/dev/null 2>&1; then
|
||||||
echo "Docker niet gevonden."
|
echo "Docker niet gevonden."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -58,10 +71,10 @@ curl -fsSL "$MASTER_URL/install/$BIN_NAME" -o "$BIN_PATH.new"
|
||||||
chmod +x "$BIN_PATH.new"
|
chmod +x "$BIN_PATH.new"
|
||||||
mv "$BIN_PATH.new" "$BIN_PATH"
|
mv "$BIN_PATH.new" "$BIN_PATH"
|
||||||
|
|
||||||
if docker ps -a --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
if docker_cmd ps -a --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||||
echo "Container herstarten..."
|
echo "Container herstarten..."
|
||||||
docker restart "$CONTAINER" >/dev/null
|
docker_cmd restart "$CONTAINER" >/dev/null
|
||||||
echo "Klaar. Controleer versie in Admin → Nodes (verwacht ${MEDIA_NODE_VERSION:-1.3.5}+)."
|
echo "Klaar. Controleer versie in Admin → Nodes (verwacht ${MEDIA_NODE_VERSION:-1.3.6}+)."
|
||||||
else
|
else
|
||||||
echo "Waarschuwing: container '$CONTAINER' niet gevonden — binary is wel bijgewerkt."
|
echo "Waarschuwing: container '$CONTAINER' niet gevonden — binary is wel bijgewerkt."
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"github.com/sthmedia/media-node/internal/streaming"
|
"github.com/sthmedia/media-node/internal/streaming"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.3.5"
|
var version = "1.3.6"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,21 @@ func (c *Client) runUpgrade(downloadURL string) error {
|
||||||
return fmt.Errorf("chmod staging: %w", err)
|
return fmt.Errorf("chmod staging: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prefer in-place install (no Docker API). Needs binary bind-mount :rw
|
||||||
|
// (install-*.sh) — werkt op Synology waar docker.sock root-only is.
|
||||||
|
targets := []string{"/usr/local/bin/media-node"}
|
||||||
|
if binDir := strings.TrimSpace(os.Getenv("MEDIA_NODE_BIN_DIR")); binDir != "" {
|
||||||
|
targets = append(targets, filepath.Join(binDir, "media-node"))
|
||||||
|
}
|
||||||
|
for _, dest := range targets {
|
||||||
|
if err := installBinaryFile(stagingFile, dest); err == nil {
|
||||||
|
log.Printf("UPGRADE: binary geplaatst op %s", dest)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := os.Stat("/var/run/docker.sock"); err != nil {
|
if _, err := os.Stat("/var/run/docker.sock"); err != nil {
|
||||||
return fmt.Errorf("docker.sock niet beschikbaar — gebruik handmatig upgrade-script")
|
return fmt.Errorf("geen schrijfrechten op binary-mount en geen docker.sock — SSH: curl -fsSL \"$MASTER/install/upgrade-node.sh\" | bash -s -- --appdata=<appdata>")
|
||||||
}
|
}
|
||||||
|
|
||||||
containerName := strings.TrimSpace(os.Getenv("MEDIA_NODE_CONTAINER_NAME"))
|
containerName := strings.TrimSpace(os.Getenv("MEDIA_NODE_CONTAINER_NAME"))
|
||||||
|
|
@ -80,7 +93,7 @@ func (c *Client) runUpgrade(downloadURL string) error {
|
||||||
|
|
||||||
binHostDir, dataHostDir, err := dockerContainerBindDirs(containerName)
|
binHostDir, dataHostDir, err := dockerContainerBindDirs(containerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w — op Synology vaak docker.sock permission denied; upgrade via SSH met upgrade-node.sh", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stagingHost := filepath.Join(dataHostDir, "upgrade", "media-node")
|
stagingHost := filepath.Join(dataHostDir, "upgrade", "media-node")
|
||||||
|
|
@ -95,6 +108,28 @@ func (c *Client) runUpgrade(downloadURL string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installBinaryFile(src, dest string) error {
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
// Direct overwrite: file-bind-mounts hebben vaak geen writable sibling (.new).
|
||||||
|
out, err := os.OpenFile(dest, os.O_WRONLY|os.O_TRUNC, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(out, in); err != nil {
|
||||||
|
out.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := out.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chmod(dest, 0o755)
|
||||||
|
}
|
||||||
|
|
||||||
func downloadFile(url, dest string) error {
|
func downloadFile(url, dest string) error {
|
||||||
log.Printf("UPGRADE: download %s", url)
|
log.Printf("UPGRADE: download %s", url)
|
||||||
tmp := dest + ".download"
|
tmp := dest + ".download"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue