diff --git a/cmd/pulse-sensor-proxy/ssh.go b/cmd/pulse-sensor-proxy/ssh.go index 2730f7a..11c3c58 100644 --- a/cmd/pulse-sensor-proxy/ssh.go +++ b/cmd/pulse-sensor-proxy/ssh.go @@ -579,23 +579,51 @@ func discoverClusterNodes() ([]string, error) { stdoutStr := out.String() combinedOutput := stderrStr + stdoutStr - // Check if this is a standalone node or LXC container - // - "does not exist" or "not part of a cluster": standalone node - // - "ipcc_send_rec": running in LXC container without corosync access - // - "Unknown error -1": LXC container corosync communication failure - // - "Unable to load access control list": Permission/access issues in containers - // Note: Some Proxmox versions write these messages to stdout, others to stderr - if strings.Contains(combinedOutput, "does not exist") || - strings.Contains(combinedOutput, "not part of a cluster") || - strings.Contains(combinedOutput, "ipcc_send_rec") || - strings.Contains(combinedOutput, "Unknown error -1") || - strings.Contains(combinedOutput, "Unable to load access control list") { + // Broadly detect standalone/container scenarios by checking for known patterns + // This list comes from real user reports and should handle localization/version differences + // + // Common patterns that indicate standalone/container operation: + // - Configuration missing: "does not exist", "not found", "no such file" + // - Cluster state: "not part of a cluster", "no cluster", "standalone" + // - IPC failures: "ipcc_send_rec", "IPC", "communication failed" + // - Permission/access: "Unknown error -1", "Unable to load", "access denied", "permission denied" + // + // Strategy: Be permissive - if pvecm fails with any of these common patterns, + // assume standalone and fall back to localhost. This is safer than false negatives. + standaloneIndicators := []string{ + // Configuration issues + "does not exist", "not found", "no such file", + // Cluster state + "not part of a cluster", "no cluster", "standalone", + // IPC/communication failures (common in LXC) + "ipcc_send_rec", "IPC", "communication failed", "connection refused", + // Permission/access issues + "Unknown error -1", "Unable to load", "access denied", "permission denied", + "access control list", + } + + isStandalone := false + for _, indicator := range standaloneIndicators { + if strings.Contains(strings.ToLower(combinedOutput), strings.ToLower(indicator)) { + isStandalone = true + break + } + } + + if isStandalone { // Log at INFO level since this is expected for standalone/container scenarios - log.Info().Msg("Standalone Proxmox node or LXC container detected - using localhost for temperature collection") + log.Info(). + Str("exit_code", fmt.Sprintf("%v", err)). + Msg("Standalone Proxmox node or LXC container detected - using localhost for temperature collection") return discoverLocalHostAddresses() } - // For other unexpected errors, fail with details - log.Warn().Str("stderr", stderrStr).Str("stdout", stdoutStr).Msg("pvecm status failed with unexpected error") + + // For truly unexpected errors (rare), fail with full context for debugging + log.Warn(). + Err(err). + Str("stderr", stderrStr). + Str("stdout", stdoutStr). + Msg("pvecm status failed with unexpected error - please report this if temperature monitoring doesn't work") return nil, fmt.Errorf("failed to get cluster status: %w (stderr: %s, stdout: %s)", err, stderrStr, stdoutStr) } diff --git a/cmd/pulse-sensor-proxy/ssh_test.go b/cmd/pulse-sensor-proxy/ssh_test.go index 3288e9e..3ec45ea 100644 --- a/cmd/pulse-sensor-proxy/ssh_test.go +++ b/cmd/pulse-sensor-proxy/ssh_test.go @@ -250,18 +250,41 @@ func TestStandaloneNodeErrorPatterns(t *testing.T) { stderr: "", issueNo: "#571", }, + { + name: "no cluster keyword", + stderr: "Error: no cluster configuration\n", + stdout: "", + issueNo: "variation", + }, + { + name: "IPC failure variant", + stderr: "IPC communication error\n", + stdout: "", + issueNo: "variation", + }, + } + + // Use the same detection logic as the actual code + standaloneIndicators := []string{ + "does not exist", "not found", "no such file", + "not part of a cluster", "no cluster", "standalone", + "ipcc_send_rec", "IPC", "communication failed", "connection refused", + "Unknown error -1", "Unable to load", "access denied", "permission denied", + "access control list", } for _, tc := range standalonePatterns { t.Run(tc.name, func(t *testing.T) { combinedOutput := tc.stderr + tc.stdout - // Check each detection pattern we added - isStandalone := strings.Contains(combinedOutput, "does not exist") || - strings.Contains(combinedOutput, "not part of a cluster") || - strings.Contains(combinedOutput, "ipcc_send_rec") || - strings.Contains(combinedOutput, "Unknown error -1") || - strings.Contains(combinedOutput, "Unable to load access control list") + // Check using the permissive detection strategy + isStandalone := false + for _, indicator := range standaloneIndicators { + if strings.Contains(strings.ToLower(combinedOutput), strings.ToLower(indicator)) { + isStandalone = true + break + } + } if !isStandalone { t.Errorf("Failed to detect standalone/LXC pattern from %s:\n stderr: %q\n stdout: %q", diff --git a/install.sh b/install.sh index 91f9138..9576eff 100755 --- a/install.sh +++ b/install.sh @@ -1585,17 +1585,30 @@ fi'; then if bash "$proxy_script" "${proxy_install_args[@]}" 2>&1 | tee /tmp/proxy-install-${CTID}.log; then print_info "Temperature proxy installation script completed" - # Verify proxy is actually working - echo - print_info "Verifying temperature proxy health..." - local proxy_health_ok=true - - # Check 1: Service is running - if ! systemctl is-active --quiet pulse-sensor-proxy 2>/dev/null; then - print_error "✗ Service not running" - proxy_health_ok=false + # Check if health checks should be skipped + if [[ "${PULSE_SKIP_HEALTH_CHECKS:-}" == "1" ]]; then + print_warn "⚠ Health checks skipped (PULSE_SKIP_HEALTH_CHECKS=1)" + print_warn " Temperature monitoring may not work correctly" + print_warn " Verify manually: systemctl status pulse-sensor-proxy" + echo else - print_info "✓ Service running" + # Verify proxy is actually working (with graceful fallbacks) + echo + print_info "Verifying temperature proxy health..." + local proxy_health_ok=true + local health_warnings=() + + # Check 1: Service is running (with timeout) + if command -v systemctl >/dev/null 2>&1; then + if timeout 5 systemctl is-active --quiet pulse-sensor-proxy 2>/dev/null; then + print_info "✓ Service running" + else + print_error "✗ Service not running" + proxy_health_ok=false + fi + else + print_warn "⚠ systemctl not available - skipping service check" + health_warnings+=("systemctl not available") fi # Check 2: Socket exists @@ -1606,27 +1619,60 @@ fi'; then print_info "✓ Socket exists" fi - # Check 3: Socket is accessible from container - if ! pct exec $CTID -- test -S /mnt/pulse-proxy/pulse-sensor-proxy.sock 2>/dev/null; then - print_error "✗ Socket not visible inside container at /mnt/pulse-proxy/pulse-sensor-proxy.sock" - print_error " Bind mount may not be configured correctly" - proxy_health_ok=false + # Check 3: Socket is accessible from container (with timeout and fallback) + if command -v pct >/dev/null 2>&1; then + # Use timeout to prevent hanging if container is stopped/frozen + if timeout 10 pct exec $CTID -- test -S /mnt/pulse-proxy/pulse-sensor-proxy.sock 2>/dev/null; then + print_info "✓ Socket accessible from container" + else + # Don't fail immediately - container might be stopped or not fully booted + print_warn "⚠ Could not verify socket accessibility from container" + print_warn " This may be normal if container is stopped or not fully started" + print_warn " Socket will be checked when container starts" + health_warnings+=("container socket check failed (may be transient)") + fi else - print_info "✓ Socket accessible from container" + print_warn "⚠ pct command not available - skipping container socket check" + health_warnings+=("pct not available") fi + # Only fail if critical checks failed (service or socket on host) if [[ "$proxy_health_ok" != "true" ]]; then echo print_error "Temperature proxy health check failed" print_error "See diagnostics above and logs: /tmp/proxy-install-${CTID}.log" print_error "" - print_error "Check: systemctl status pulse-sensor-proxy" - print_error "Check: journalctl -u pulse-sensor-proxy -n 50" + + # Provide actionable diagnostics if tools are available + if command -v systemctl >/dev/null 2>&1; then + print_error "Check: systemctl status pulse-sensor-proxy" + fi + if command -v journalctl >/dev/null 2>&1; then + print_error "Check: journalctl -u pulse-sensor-proxy -n 50" + else + print_error "Check: cat /var/log/syslog | grep pulse-sensor-proxy" + fi + print_error "" + print_error "To bypass health checks (not recommended): Set PULSE_SKIP_HEALTH_CHECKS=1" echo exit 1 fi - print_success "Temperature proxy is healthy and ready" + # Show warnings summary if any + if [[ ${#health_warnings[@]} -gt 0 ]]; then + echo + print_warn "Health check completed with warnings:" + for warning in "${health_warnings[@]}"; do + print_warn " - $warning" + done + print_info "Proxy installed but some checks could not be verified" + print_info "Temperature monitoring may still work correctly" + echo + fi + + print_success "Temperature proxy is healthy and ready" + fi # End of health checks + # Clean up temporary binary if it was copied [[ -f "$local_proxy_binary" ]] && rm -f "$local_proxy_binary" else