debtik-update 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. umask 077
  4. base=/var/lib/debtik/update
  5. mkdir -p "$base"
  6. exec 9>"$base/lock"
  7. flock -n 9 || { echo 'An update is already running' >&2; exit 1; }
  8. exec >>"$base/update.log" 2>&1
  9. state() { printf '%s\n' "$1" >"$base/status"; printf '[%s] %s\n' "$(date -u +%FT%TZ)" "$1"; }
  10. cleanup_veth() {
  11. python3 - <<'PY'
  12. import json,subprocess,pathlib
  13. p=pathlib.Path('/etc/proxmox-router/vpn/aerotun_instances.json')
  14. if p.exists():
  15. for tunnel in json.loads(p.read_text()):
  16. name=tunnel['name']
  17. result=subprocess.run(['ip','-j','-d','link','show','dev',name],capture_output=True,text=True)
  18. if result.returncode:
  19. continue
  20. links=json.loads(result.stdout)
  21. if len(links)!=1:raise RuntimeError('Ambiguous AeroTUN interface')
  22. link=links[0]
  23. if link.get('linkinfo',{}).get('info_kind')=='veth':
  24. if link.get('link')!=name+'_ep':raise RuntimeError('Unexpected veth peer')
  25. subprocess.run(['ip','link','delete',name],check=True)
  26. PY
  27. }
  28. restore() {
  29. state rolling_back
  30. systemctl stop proxmox-router.service || true
  31. cleanup_veth || true
  32. for name in proxmox-router debtik-cli; do
  33. test -f "$base/previous/$name"
  34. cp -p "$base/previous/$name" "/usr/local/bin/$name.rollback"
  35. mv -f "/usr/local/bin/$name.rollback" "/usr/local/bin/$name"
  36. done
  37. if [[ -f "$base/previous_commit" ]]; then cp -p "$base/previous_commit" "$base/installed_commit"; fi
  38. systemctl start proxmox-router.service
  39. systemctl is-active --quiet proxmox-router.service
  40. state rolled_back
  41. }
  42. if [[ ${1:-} == rollback ]]; then restore; exit; fi
  43. [[ $# == 4 && $1 == install ]] || { echo 'Usage: debtik-update install URL BRANCH | rollback' >&2; exit 2; }
  44. repo=$2 branch=$3 pin=$4
  45. git_opts=()
  46. if [[ -n $pin ]]; then
  47. [[ $pin =~ ^sha256//[A-Za-z0-9+/]{43}=$ ]] || exit 2
  48. git_opts=(-c http.sslVerify=false -c "http.pinnedpubkey=$pin")
  49. fi
  50. [[ $repo == http://* || $repo == https://* || $repo == ssh://* ]] || exit 2
  51. [[ $branch =~ ^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$ && $branch != *..* ]] || exit 2
  52. work=$(mktemp -d "$base/work.XXXXXXXX")
  53. trap 'rm -rf "$work"' EXIT
  54. trap 'state failed' ERR
  55. state fetching
  56. GIT_TERMINAL_PROMPT=0 git -c protocol.file.allow=never "${git_opts[@]}" clone --single-branch --branch "$branch" -- "$repo" "$work/source"
  57. cd "$work/source"
  58. commit=$(git rev-parse HEAD)
  59. if [[ -f "$base/installed_commit" ]]; then
  60. installed=$(cat "$base/installed_commit")
  61. if [[ $installed =~ ^[a-f0-9]{40}$ ]] && ! git merge-base --is-ancestor "$installed" "$commit"; then
  62. state failed
  63. echo "Selected branch does not contain installed commit; refusing downgrade or unrelated history"
  64. exit 1
  65. fi
  66. fi
  67. state building
  68. go_cmd=$(command -v go || true)
  69. if [[ -x /opt/debtik/go/bin/go ]]; then go_cmd=/opt/debtik/go/bin/go; fi
  70. [[ -n $go_cmd ]] || { state failed; echo 'Go is not installed'; exit 1; }
  71. CGO_ENABLED=0 "$go_cmd" build -trimpath -ldflags "-X proxmox-router/internal/api.BuildCommit=$commit" -o "$work/proxmox-router" ./cmd/server
  72. CGO_ENABLED=0 "$go_cmd" build -trimpath -o "$work/debtik-cli" ./cmd/debtik-cli
  73. install -d -m 0700 "$base/previous"
  74. for name in proxmox-router debtik-cli; do cp -p "/usr/local/bin/$name" "$base/previous/$name"; done
  75. if [[ -f "$base/installed_commit" ]]; then cp -p "$base/installed_commit" "$base/previous_commit"; fi
  76. printf '%s\n' "$commit" > "$base/candidate_commit"
  77. state installing
  78. rollback_on_error() { trap - ERR; restore || state failed; exit 1; }
  79. trap rollback_on_error ERR
  80. systemctl stop proxmox-router.service
  81. cleanup_veth
  82. for name in proxmox-router debtik-cli; do
  83. install -m 0755 "$work/$name" "/usr/local/bin/$name.next"
  84. mv -f "/usr/local/bin/$name.next" "/usr/local/bin/$name"
  85. done
  86. systemctl start proxmox-router.service
  87. sleep 3
  88. systemctl is-active --quiet proxmox-router.service
  89. printf '%s\n' "$commit" > "$base/installed_commit"
  90. state complete