Fix Synology Admin upgrade by repairing missing /opt/media-node-bin mount.
This commit is contained in:
parent
3c12fbc869
commit
08c7e7d574
2 changed files with 150 additions and 31 deletions
|
|
@ -1,5 +1,7 @@
|
|||
#!/bin/bash
|
||||
# Upgrade media-node binary (download latest from Master + restart container).
|
||||
# Op Synology: herstelt ook /opt/media-node-bin + MEDIA_NODE_BIN_DIR als die ontbreken
|
||||
# (anders faalt Admin→Upgrade met docker.sock / media-node.new permission denied).
|
||||
set -euo pipefail
|
||||
|
||||
MASTER_URL="${MASTER_URL:-__MASTER_PUBLIC_URL__}"
|
||||
|
|
@ -11,6 +13,9 @@ usage() {
|
|||
Gebruik:
|
||||
curl -fsSL ${MASTER_URL}/install/upgrade-node.sh | bash -s -- --appdata=/mnt/user/appdata/media-node
|
||||
|
||||
Synology voorbeeld:
|
||||
curl -fsSL ${MASTER_URL}/install/upgrade-node.sh | bash -s -- --appdata=/volume1/docker/media-node
|
||||
|
||||
Opties:
|
||||
--appdata= Appdata-map met bin/media-node (Unraid: /mnt/user/appdata/media-node, Synology: /volume1/docker/media-node)
|
||||
--container= Docker-containernaam (default: media-node)
|
||||
|
|
@ -29,7 +34,7 @@ for arg in "$@"; do
|
|||
done
|
||||
|
||||
MASTER_URL="$(echo -n "$MASTER_URL" | sed 's:/*$::')"
|
||||
APPDATA="$(echo -n "$APPDATA" | tr -d '\r\n')"
|
||||
APPDATA="$(echo -n "$APPDATA" | tr -d '\r\n' | sed 's:/*$::')"
|
||||
|
||||
docker_cmd() {
|
||||
if docker info >/dev/null 2>&1; then
|
||||
|
|
@ -69,11 +74,101 @@ echo
|
|||
echo "Downloaden ($BIN_NAME)..."
|
||||
curl -fsSL "$MASTER_URL/install/$BIN_NAME" -o "$BIN_PATH.new"
|
||||
chmod +x "$BIN_PATH.new"
|
||||
mv "$BIN_PATH.new" "$BIN_PATH"
|
||||
mv -f "$BIN_PATH.new" "$BIN_PATH"
|
||||
|
||||
# Herstel Admin→Upgrade op Synology: directory-mount + env (file-mount alleen is niet genoeg).
|
||||
ensure_bin_mount() {
|
||||
if ! docker_cmd ps -a --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local has_mount has_env
|
||||
has_mount="$(docker_cmd inspect -f '{{range .Mounts}}{{if eq .Destination "/opt/media-node-bin"}}1{{end}}{{end}}' "$CONTAINER" 2>/dev/null || true)"
|
||||
has_env="$(docker_cmd inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$CONTAINER" 2>/dev/null | grep -c '^MEDIA_NODE_BIN_DIR=' || true)"
|
||||
|
||||
if [ "$has_mount" = "1" ] && [ "${has_env:-0}" -gt 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Container mist /opt/media-node-bin en/of MEDIA_NODE_BIN_DIR — opnieuw aanmaken zodat Admin→Upgrade werkt..."
|
||||
|
||||
local user port image
|
||||
user="$(docker_cmd inspect -f '{{.Config.User}}' "$CONTAINER")"
|
||||
[ -n "$user" ] || user="1026:100"
|
||||
port="$(docker_cmd inspect -f '{{(index (index .HostConfig.PortBindings "8080/tcp") 0).HostPort}}' "$CONTAINER" 2>/dev/null || true)"
|
||||
[ -n "$port" ] || port="8080"
|
||||
image="$(docker_cmd inspect -f '{{.Config.Image}}' "$CONTAINER")"
|
||||
[ -n "$image" ] || image="alpine:3.20"
|
||||
|
||||
# Env behouden, MEDIA_NODE_BIN_DIR forceren, enrollment-token weglaten (credentials bestaan al).
|
||||
local -a env_args=()
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in
|
||||
MEDIA_NODE_ENROLLMENT_TOKEN=*) continue ;;
|
||||
MEDIA_NODE_BIN_DIR=*) continue ;;
|
||||
MEDIA_NODE_CONTAINER_NAME=*) continue ;;
|
||||
esac
|
||||
env_args+=(-e "$line")
|
||||
done < <(docker_cmd inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$CONTAINER")
|
||||
env_args+=(-e "MEDIA_NODE_BIN_DIR=/opt/media-node-bin")
|
||||
env_args+=(-e "MEDIA_NODE_CONTAINER_NAME=$CONTAINER")
|
||||
|
||||
# Bestaande mounts behouden, behalve bin-paden die we zelf opnieuw zetten.
|
||||
local -a vol_args=()
|
||||
local src dst mode
|
||||
while IFS=$'\t' read -r src dst mode; do
|
||||
[ -z "$dst" ] && continue
|
||||
case "$dst" in
|
||||
/usr/local/bin/media-node|/opt/media-node-bin) continue ;;
|
||||
esac
|
||||
if [ "$mode" = "ro" ]; then
|
||||
vol_args+=(-v "$src:$dst:ro")
|
||||
else
|
||||
vol_args+=(-v "$src:$dst")
|
||||
fi
|
||||
done < <(docker_cmd inspect -f '{{range .Mounts}}{{.Source}}{{"\t"}}{{.Destination}}{{"\t"}}{{if .RW}}rw{{else}}ro{{end}}{{"\n"}}{{end}}' "$CONTAINER")
|
||||
|
||||
vol_args+=(-v "$BIN_PATH:/usr/local/bin/media-node:rw")
|
||||
vol_args+=(-v "$BIN_DIR:/opt/media-node-bin:rw")
|
||||
|
||||
local -a group_args=()
|
||||
if [ -S /var/run/docker.sock ]; then
|
||||
local docker_gid
|
||||
docker_gid="$(stat -c '%g' /var/run/docker.sock 2>/dev/null || true)"
|
||||
if [ -n "$docker_gid" ] && [ "$docker_gid" != "0" ]; then
|
||||
group_args+=(--group-add "$docker_gid")
|
||||
fi
|
||||
fi
|
||||
|
||||
docker_cmd stop "$CONTAINER" >/dev/null
|
||||
docker_cmd rm "$CONTAINER" >/dev/null
|
||||
|
||||
docker_cmd run -d \
|
||||
--name "$CONTAINER" \
|
||||
--restart unless-stopped \
|
||||
-p "${port}:8080" \
|
||||
--user "$user" \
|
||||
"${group_args[@]}" \
|
||||
"${env_args[@]}" \
|
||||
"${vol_args[@]}" \
|
||||
--entrypoint /usr/local/bin/media-node \
|
||||
"$image" \
|
||||
run
|
||||
|
||||
echo "Container opnieuw aangemaakt met /opt/media-node-bin (Admin→Upgrade zou nu moeten werken)."
|
||||
return 0
|
||||
}
|
||||
|
||||
if docker_cmd ps -a --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||
echo "Container herstarten..."
|
||||
docker_cmd restart "$CONTAINER" >/dev/null
|
||||
ensure_bin_mount || true
|
||||
if docker_cmd ps -a --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||
# Als ensure_bin_mount al opnieuw startte, restart nog eens zodat verse binary zeker geladen is.
|
||||
echo "Container herstarten..."
|
||||
docker_cmd restart "$CONTAINER" >/dev/null
|
||||
fi
|
||||
echo "Klaar. Controleer versie in Admin → Nodes (verwacht ${MEDIA_NODE_VERSION:-1.3.12}+)."
|
||||
else
|
||||
echo "Waarschuwing: container '$CONTAINER' niet gevonden — binary is wel bijgewerkt."
|
||||
|
|
|
|||
|
|
@ -85,7 +85,9 @@ func (c *Client) runUpgrade(downloadURL string) error {
|
|||
|
||||
if _, err := os.Stat("/var/run/docker.sock"); err != nil {
|
||||
return fmt.Errorf(
|
||||
"binary niet te vervangen (%s) en geen docker.sock — eenmalig via SSH: curl -fsSL \"$MASTER/install/upgrade-node.sh\" | bash -s -- --appdata=<appdata>",
|
||||
"binary niet te vervangen (%s). Op Synology: eenmalig via SSH upgraden én bin-mount herstellen:\n"+
|
||||
" curl -fsSL \"$MASTER/install/upgrade-node.sh\" | bash -s -- --appdata=<appdata>\n"+
|
||||
"(script zet /opt/media-node-bin + MEDIA_NODE_BIN_DIR zodat Admin→Upgrade daarna werkt)",
|
||||
strings.Join(errs, "; "),
|
||||
)
|
||||
}
|
||||
|
|
@ -97,7 +99,11 @@ func (c *Client) runUpgrade(downloadURL string) error {
|
|||
|
||||
binHostDir, dataHostDir, err := dockerContainerBindDirs(containerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w — eerdere pogingen: %s", err, strings.Join(errs, "; "))
|
||||
return fmt.Errorf(
|
||||
"%w — eerdere pogingen: %s (Synology: docker.sock is vaak root-only; gebruik upgrade-node.sh via SSH)",
|
||||
err,
|
||||
strings.Join(errs, "; "),
|
||||
)
|
||||
}
|
||||
|
||||
stagingHost := filepath.Join(dataHostDir, "upgrade", "media-node")
|
||||
|
|
@ -113,17 +119,32 @@ func (c *Client) runUpgrade(downloadURL string) error {
|
|||
}
|
||||
|
||||
func upgradeBinaryTargets() []string {
|
||||
seen := map[string]bool{}
|
||||
var out []string
|
||||
if binDir := strings.TrimSpace(os.Getenv("MEDIA_NODE_BIN_DIR")); binDir != "" {
|
||||
out = append(out, filepath.Join(binDir, "media-node"))
|
||||
add := func(p string) {
|
||||
p = filepath.Clean(p)
|
||||
if p == "" || p == "." || seen[p] {
|
||||
return
|
||||
}
|
||||
seen[p] = true
|
||||
out = append(out, p)
|
||||
}
|
||||
// File bind-mount of the running entrypoint — often ETXTBSY; try last.
|
||||
out = append(out, "/usr/local/bin/media-node")
|
||||
|
||||
if binDir := strings.TrimSpace(os.Getenv("MEDIA_NODE_BIN_DIR")); binDir != "" {
|
||||
add(filepath.Join(binDir, "media-node"))
|
||||
}
|
||||
// Install-scripts mounten $APPDATA/bin hier — ook proberen als env op oude containers ontbreekt.
|
||||
if st, err := os.Stat("/opt/media-node-bin"); err == nil && st.IsDir() {
|
||||
add("/opt/media-node-bin/media-node")
|
||||
}
|
||||
// File bind-mount van de entrypoint — .new ernaast faalt vaak (map is root-owned).
|
||||
add("/usr/local/bin/media-node")
|
||||
return out
|
||||
}
|
||||
|
||||
// installBinaryReplace writes dest.new then replaces dest (unlink+rename).
|
||||
// Unlink of a running Linux binary is allowed; open(O_WRONLY) on it is not (ETXTBSY).
|
||||
// .new must live in the same directory as dest (directory bind-mount like /opt/media-node-bin).
|
||||
func installBinaryReplace(src, dest string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
|
||||
return fmt.Errorf("bin-map: %w", err)
|
||||
|
|
@ -132,29 +153,9 @@ func installBinaryReplace(src, dest string) error {
|
|||
newPath := dest + ".new"
|
||||
_ = os.Remove(newPath)
|
||||
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
|
||||
if err != nil {
|
||||
if err := copyFile(src, newPath, 0o755); err != nil {
|
||||
return fmt.Errorf("schrijven %s: %w", newPath, err)
|
||||
}
|
||||
if _, err := io.Copy(out, in); err != nil {
|
||||
out.Close()
|
||||
_ = os.Remove(newPath)
|
||||
return fmt.Errorf("kopiëren: %w", err)
|
||||
}
|
||||
if err := out.Close(); err != nil {
|
||||
_ = os.Remove(newPath)
|
||||
return err
|
||||
}
|
||||
if err := os.Chmod(newPath, 0o755); err != nil {
|
||||
_ = os.Remove(newPath)
|
||||
return err
|
||||
}
|
||||
|
||||
// Drop old name (ok while process still runs), then put new binary in place.
|
||||
if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
|
||||
|
|
@ -172,6 +173,29 @@ func installBinaryReplace(src, dest string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func copyFile(src, dest string, mode os.FileMode) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(out, in); err != nil {
|
||||
out.Close()
|
||||
_ = os.Remove(dest)
|
||||
return err
|
||||
}
|
||||
if err := out.Close(); err != nil {
|
||||
_ = os.Remove(dest)
|
||||
return err
|
||||
}
|
||||
return os.Chmod(dest, mode)
|
||||
}
|
||||
|
||||
func downloadFile(url, dest string) error {
|
||||
log.Printf("UPGRADE: download %s", url)
|
||||
tmp := dest + ".download"
|
||||
|
|
|
|||
Loading…
Reference in a new issue