Auto-deploy proxy for standalone temp monitoring
This commit is contained in:
parent
aa7b7b0922
commit
5951a364f7
3 changed files with 182 additions and 8 deletions
|
|
@ -4002,6 +4002,7 @@ TEMPERATURE_ENABLED=false
|
||||||
TEMP_MONITORING_AVAILABLE=true
|
TEMP_MONITORING_AVAILABLE=true
|
||||||
MIN_PROXY_VERSION="%s"
|
MIN_PROXY_VERSION="%s"
|
||||||
PULSE_VERSION_ENDPOINT="%s/api/version"
|
PULSE_VERSION_ENDPOINT="%s/api/version"
|
||||||
|
STANDALONE_PROXY_DEPLOYED=false
|
||||||
|
|
||||||
version_ge() {
|
version_ge() {
|
||||||
if command -v dpkg >/dev/null 2>&1; then
|
if command -v dpkg >/dev/null 2>&1; then
|
||||||
|
|
@ -4060,6 +4061,12 @@ if command -v pct >/dev/null 2>&1; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Determine if this node is standalone (not joined to a cluster)
|
||||||
|
IS_STANDALONE_NODE=false
|
||||||
|
if ! command -v pvecm >/dev/null 2>&1 || ! pvecm status >/dev/null 2>&1; then
|
||||||
|
IS_STANDALONE_NODE=true
|
||||||
|
fi
|
||||||
|
|
||||||
# Track whether temperature monitoring can work (may be disabled by checks above)
|
# Track whether temperature monitoring can work (may be disabled by checks above)
|
||||||
|
|
||||||
# For containerized Pulse, verify version supports proxy (v4.24.0+)
|
# For containerized Pulse, verify version supports proxy (v4.24.0+)
|
||||||
|
|
@ -4327,6 +4334,42 @@ elif [ "$TEMP_MONITORING_AVAILABLE" = true ]; then
|
||||||
if [[ $SSH_REPLY =~ ^[Yy]$ ]]; then
|
if [[ $SSH_REPLY =~ ^[Yy]$ ]]; then
|
||||||
echo "Configuring temperature monitoring..."
|
echo "Configuring temperature monitoring..."
|
||||||
|
|
||||||
|
if [ "$IS_STANDALONE_NODE" = true ]; then
|
||||||
|
echo " • Deploying hardened pulse-sensor-proxy..."
|
||||||
|
PROXY_INSTALLER_URL="$PULSE_URL/api/install/install-sensor-proxy.sh"
|
||||||
|
PROXY_INSTALLER=$(mktemp)
|
||||||
|
if curl -fsSL "$PROXY_INSTALLER_URL" -o "$PROXY_INSTALLER" 2>/dev/null; then
|
||||||
|
chmod +x "$PROXY_INSTALLER"
|
||||||
|
if "$PROXY_INSTALLER" --standalone --http-mode --pulse-server "$PULSE_URL"; then
|
||||||
|
echo " ✓ pulse-sensor-proxy installed and registered with Pulse"
|
||||||
|
STANDALONE_PROXY_DEPLOYED=true
|
||||||
|
TEMPERATURE_ENABLED=true
|
||||||
|
else
|
||||||
|
echo " ⚠️ Proxy installer reported an error; falling back to SSH-based collector"
|
||||||
|
fi
|
||||||
|
rm -f "$PROXY_INSTALLER"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Unable to download proxy installer from $PULSE_URL"
|
||||||
|
echo " Falling back to SSH-based temperature monitoring."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$STANDALONE_PROXY_DEPLOYED" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "✓ Temperature monitoring will use pulse-sensor-proxy on this host."
|
||||||
|
echo " Temperature data will appear in the dashboard within 10 seconds."
|
||||||
|
if [ -f /root/.ssh/authorized_keys ]; then
|
||||||
|
TMP_AUTH_KEYS=$(mktemp)
|
||||||
|
if grep -v '# pulse-' /root/.ssh/authorized_keys > "$TMP_AUTH_KEYS" 2>/dev/null; then
|
||||||
|
chmod --reference=/root/.ssh/authorized_keys "$TMP_AUTH_KEYS" 2>/dev/null || chmod 600 "$TMP_AUTH_KEYS"
|
||||||
|
chown --reference=/root/.ssh/authorized_keys "$TMP_AUTH_KEYS" 2>/dev/null || true
|
||||||
|
mv "$TMP_AUTH_KEYS" /root/.ssh/authorized_keys
|
||||||
|
else
|
||||||
|
rm -f "$TMP_AUTH_KEYS"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
|
||||||
if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
|
if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
|
||||||
# Add keys to root's authorized_keys
|
# Add keys to root's authorized_keys
|
||||||
mkdir -p /root/.ssh
|
mkdir -p /root/.ssh
|
||||||
|
|
@ -4519,6 +4562,7 @@ Host ${NODE}
|
||||||
echo " journalctl -u pulse -n 100 | grep -i ssh"
|
echo " journalctl -u pulse -n 100 | grep -i ssh"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi # End hardened proxy branch
|
||||||
else
|
else
|
||||||
echo "Temperature monitoring skipped."
|
echo "Temperature monitoring skipped."
|
||||||
fi
|
fi
|
||||||
|
|
@ -4693,14 +4737,8 @@ fi
|
||||||
# Standalone Node Configuration (for non-cluster nodes)
|
# Standalone Node Configuration (for non-cluster nodes)
|
||||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
# Check if this node is standalone (not in a cluster)
|
# If standalone and temperature monitoring was enabled via SSH fallback, ensure proxy key is configured
|
||||||
IS_STANDALONE=false
|
if [ "$IS_STANDALONE_NODE" = true ] && [ "$TEMPERATURE_ENABLED" = true ] && [ "$STANDALONE_PROXY_DEPLOYED" != true ]; then
|
||||||
if ! command -v pvecm >/dev/null 2>&1 || ! pvecm status >/dev/null 2>&1; then
|
|
||||||
IS_STANDALONE=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If standalone and temperature monitoring was enabled, try to fetch proxy key
|
|
||||||
if [ "$IS_STANDALONE" = true ] && [ "$TEMPERATURE_ENABLED" = true ]; then
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
echo "Standalone Node Temperature Setup"
|
echo "Standalone Node Temperature Setup"
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ func (r *Router) setupRoutes() {
|
||||||
r.mux.HandleFunc("/api/install/pulse-sensor-proxy", r.handleDownloadPulseSensorProxy)
|
r.mux.HandleFunc("/api/install/pulse-sensor-proxy", r.handleDownloadPulseSensorProxy)
|
||||||
r.mux.HandleFunc("/api/install/install-sensor-proxy.sh", r.handleDownloadInstallerScript)
|
r.mux.HandleFunc("/api/install/install-sensor-proxy.sh", r.handleDownloadInstallerScript)
|
||||||
r.mux.HandleFunc("/api/install/migrate-sensor-proxy-control-plane.sh", r.handleDownloadMigrationScript)
|
r.mux.HandleFunc("/api/install/migrate-sensor-proxy-control-plane.sh", r.handleDownloadMigrationScript)
|
||||||
|
r.mux.HandleFunc("/api/install/migrate-temperature-proxy.sh", r.handleDownloadTemperatureProxyMigrationScript)
|
||||||
r.mux.HandleFunc("/api/install/install-docker.sh", r.handleDownloadDockerInstallerScript)
|
r.mux.HandleFunc("/api/install/install-docker.sh", r.handleDownloadDockerInstallerScript)
|
||||||
r.mux.HandleFunc("/api/config", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.handleConfig)))
|
r.mux.HandleFunc("/api/config", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.handleConfig)))
|
||||||
r.mux.HandleFunc("/api/backups", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.handleBackups)))
|
r.mux.HandleFunc("/api/backups", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.handleBackups)))
|
||||||
|
|
@ -1330,6 +1331,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
"/api/install/install-sensor-proxy.sh", // Temperature proxy installer fallback
|
"/api/install/install-sensor-proxy.sh", // Temperature proxy installer fallback
|
||||||
"/api/install/pulse-sensor-proxy", // Temperature proxy binary fallback
|
"/api/install/pulse-sensor-proxy", // Temperature proxy binary fallback
|
||||||
"/api/install/migrate-sensor-proxy-control-plane.sh", // Proxy migration helper
|
"/api/install/migrate-sensor-proxy-control-plane.sh", // Proxy migration helper
|
||||||
|
"/api/install/migrate-temperature-proxy.sh", // SSH-to-proxy migration helper
|
||||||
"/api/install/install-docker.sh", // Docker turnkey installer
|
"/api/install/install-docker.sh", // Docker turnkey installer
|
||||||
"/api/system/proxy-public-key", // Temperature proxy public key for setup script
|
"/api/system/proxy-public-key", // Temperature proxy public key for setup script
|
||||||
"/api/temperature-proxy/register", // Temperature proxy registration (called by installer)
|
"/api/temperature-proxy/register", // Temperature proxy registration (called by installer)
|
||||||
|
|
@ -3796,6 +3798,31 @@ func (r *Router) handleDownloadMigrationScript(w http.ResponseWriter, req *http.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) handleDownloadTemperatureProxyMigrationScript(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.Method != http.MethodGet {
|
||||||
|
writeErrorResponse(w, http.StatusMethodNotAllowed, "method_not_allowed", "Only GET is allowed", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptPath := "/opt/pulse/scripts/migrate-temperature-proxy.sh"
|
||||||
|
content, err := os.ReadFile(scriptPath)
|
||||||
|
if err != nil {
|
||||||
|
scriptPath = filepath.Join(r.projectRoot, "scripts", "migrate-temperature-proxy.sh")
|
||||||
|
content, err = os.ReadFile(scriptPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Str("path", scriptPath).Msg("Failed to read temperature migration script")
|
||||||
|
writeErrorResponse(w, http.StatusInternalServerError, "read_error", "Failed to read temperature migration script", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/x-shellscript")
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename=migrate-temperature-proxy.sh")
|
||||||
|
if _, err := w.Write(content); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to write temperature migration script to client")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) resolvePublicURL(req *http.Request) string {
|
func (r *Router) resolvePublicURL(req *http.Request) string {
|
||||||
if publicURL := strings.TrimSpace(r.config.PublicURL); publicURL != "" {
|
if publicURL := strings.TrimSpace(r.config.PublicURL); publicURL != "" {
|
||||||
return strings.TrimRight(publicURL, "/")
|
return strings.TrimRight(publicURL, "/")
|
||||||
|
|
|
||||||
109
scripts/migrate-temperature-proxy.sh
Normal file
109
scripts/migrate-temperature-proxy.sh
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# migrate-temperature-proxy.sh
|
||||||
|
# Converts SSH-based temperature collection to pulse-sensor-proxy on standalone hosts.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PULSE_SERVER=""
|
||||||
|
KEEP_SSH_KEYS=false
|
||||||
|
SKIP_RESTART=false
|
||||||
|
|
||||||
|
print_usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: migrate-temperature-proxy.sh --pulse-server https://pulse.example.com:7655 [--keep-ssh-keys] [--skip-restart]
|
||||||
|
|
||||||
|
Installs pulse-sensor-proxy in standalone mode and removes legacy SSH fallback keys.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
log() { echo "[INFO] $*"; }
|
||||||
|
warn() { echo "[WARN] $*" >&2; }
|
||||||
|
err() { echo "[ERROR] $*" >&2; }
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--pulse-server)
|
||||||
|
PULSE_SERVER="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--keep-ssh-keys)
|
||||||
|
KEEP_SSH_KEYS=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--skip-restart)
|
||||||
|
SKIP_RESTART=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
print_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "Unknown option: $1"
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
err "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$PULSE_SERVER" ]]; then
|
||||||
|
err "--pulse-server is required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALLER_URL="${PULSE_SERVER%/}/api/install/install-sensor-proxy.sh"
|
||||||
|
INSTALLER=$(mktemp)
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$INSTALLER"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
log "Downloading pulse-sensor-proxy installer from $INSTALLER_URL"
|
||||||
|
if ! curl -fsSL "$INSTALLER_URL" -o "$INSTALLER"; then
|
||||||
|
err "Failed to download installer script"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod +x "$INSTALLER"
|
||||||
|
|
||||||
|
INSTALL_ARGS=(--standalone --http-mode --pulse-server "$PULSE_SERVER")
|
||||||
|
if [[ "$SKIP_RESTART" == true ]]; then
|
||||||
|
INSTALL_ARGS+=(--skip-restart)
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Running pulse-sensor-proxy installer..."
|
||||||
|
if ! "$INSTALLER" "${INSTALL_ARGS[@]}"; then
|
||||||
|
err "pulse-sensor-proxy installation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remove_legacy_keys() {
|
||||||
|
local auth="/root/.ssh/authorized_keys"
|
||||||
|
if [[ ! -f "$auth" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local tmp
|
||||||
|
tmp=$(mktemp)
|
||||||
|
if grep -v -E '# pulse-sensors|# pulse-proxy-key' "$auth" > "$tmp"; then
|
||||||
|
chmod --reference="$auth" "$tmp" 2>/dev/null || chmod 600 "$tmp"
|
||||||
|
chown --reference="$auth" "$tmp" 2>/dev/null || true
|
||||||
|
mv "$tmp" "$auth"
|
||||||
|
log "Removed legacy SSH fallback keys from $auth"
|
||||||
|
else
|
||||||
|
rm -f "$tmp"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$KEEP_SSH_KEYS" == false ]]; then
|
||||||
|
remove_legacy_keys
|
||||||
|
else
|
||||||
|
warn "Legacy SSH keys retained at user request (--keep-ssh-keys)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Migration complete. This host now uses pulse-sensor-proxy for temperature collection."
|
||||||
|
log "Verify status with: systemctl status pulse-sensor-proxy --no-pager"
|
||||||
Loading…
Reference in a new issue