Ensure Windows download finds .exe (related to #684)

This commit is contained in:
rcourtman 2025-11-14 10:59:45 +00:00
parent 1d0d23d237
commit 6e9a62f663
2 changed files with 102 additions and 20 deletions

View file

@ -36,11 +36,11 @@ import (
// Router handles HTTP routing
type Router struct {
mux *http.ServeMux
config *config.Config
monitor *monitoring.Monitor
alertHandlers *AlertHandlers
configHandlers *ConfigHandlers
mux *http.ServeMux
config *config.Config
monitor *monitoring.Monitor
alertHandlers *AlertHandlers
configHandlers *ConfigHandlers
notificationHandlers *NotificationHandlers
notificationQueueHandlers *NotificationQueueHandlers
dockerAgentHandlers *DockerAgentHandlers
@ -48,14 +48,14 @@ type Router struct {
temperatureProxyHandlers *TemperatureProxyHandlers
systemSettingsHandler *SystemSettingsHandler
wsHub *websocket.Hub
reloadFunc func() error
updateManager *updates.Manager
exportLimiter *RateLimiter
persistence *config.ConfigPersistence
oidcMu sync.Mutex
oidcService *OIDCService
wrapped http.Handler
projectRoot string
reloadFunc func() error
updateManager *updates.Manager
exportLimiter *RateLimiter
persistence *config.ConfigPersistence
oidcMu sync.Mutex
oidcService *OIDCService
wrapped http.Handler
projectRoot string
// Cached system settings to avoid loading from disk on every request
settingsMu sync.RWMutex
cachedAllowEmbedding bool
@ -3381,18 +3381,32 @@ func (r *Router) handleDownloadHostAgent(w http.ResponseWriter, req *http.Reques
)
}
checkedPaths := make([]string, 0, len(searchPaths)*2)
shouldCheckWindowsExe := func(path string) bool {
base := strings.ToLower(filepath.Base(path))
return strings.Contains(base, "windows") && !strings.HasSuffix(base, ".exe")
}
for _, candidate := range searchPaths {
if candidate == "" {
continue
}
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
// Check if this is a checksum request
if strings.HasSuffix(req.URL.Path, ".sha256") {
r.serveChecksum(w, req, candidate)
pathsToCheck := []string{candidate}
if shouldCheckWindowsExe(candidate) {
pathsToCheck = append(pathsToCheck, candidate+".exe")
}
for _, path := range pathsToCheck {
checkedPaths = append(checkedPaths, path)
if info, err := os.Stat(path); err == nil && !info.IsDir() {
// Check if this is a checksum request
if strings.HasSuffix(req.URL.Path, ".sha256") {
r.serveChecksum(w, req, path)
return
}
http.ServeFile(w, req, path)
return
}
http.ServeFile(w, req, candidate)
return
}
}
@ -3406,7 +3420,7 @@ func (r *Router) handleDownloadHostAgent(w http.ResponseWriter, req *http.Reques
errorMsg.WriteString(fmt.Sprintf(" GOOS=%s GOARCH=%s go build -o pulse-host-agent-%s-%s ./cmd/pulse-host-agent\n", platformParam, archParam, platformParam, archParam))
errorMsg.WriteString(fmt.Sprintf(" sudo mv pulse-host-agent-%s-%s /opt/pulse/bin/\n\n", platformParam, archParam))
errorMsg.WriteString("Searched locations:\n")
for _, path := range searchPaths {
for _, path := range checkedPaths {
errorMsg.WriteString(fmt.Sprintf(" - %s\n", path))
}

View file

@ -0,0 +1,68 @@
package api
import (
"crypto/sha256"
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
)
func TestHandleDownloadHostAgentServesWindowsExe(t *testing.T) {
t.Cleanup(func() {
_ = os.Remove("/opt/pulse/bin/pulse-host-agent-windows-unit-test.exe")
})
filePath := filepath.Join("/opt/pulse/bin", "pulse-host-agent-windows-unit-test.exe")
if err := os.WriteFile(filePath, []byte("exe-binary"), 0o755); err != nil {
t.Fatalf("failed to write test binary: %v", err)
}
req := httptest.NewRequest(http.MethodGet, "/download/pulse-host-agent?platform=windows&arch=unit-test", nil)
rr := httptest.NewRecorder()
router := &Router{}
router.handleDownloadHostAgent(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("expected 200 OK, got %d", rr.Code)
}
if got := rr.Body.String(); got != "exe-binary" {
t.Fatalf("unexpected response body: %q", got)
}
}
func TestHandleDownloadHostAgentServesChecksumForWindowsExe(t *testing.T) {
const (
arch = "unit-sha"
filename = "pulse-host-agent-windows-" + arch + ".exe"
)
filePath := filepath.Join("/opt/pulse/bin", filename)
t.Cleanup(func() {
_ = os.Remove(filePath)
})
payload := []byte("checksum-data")
if err := os.WriteFile(filePath, payload, 0o755); err != nil {
t.Fatalf("failed to write test binary: %v", err)
}
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/download/pulse-host-agent.sha256?platform=windows&arch=%s", arch), nil)
rr := httptest.NewRecorder()
router := &Router{}
router.handleDownloadHostAgent(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("expected 200 OK, got %d", rr.Code)
}
expected := fmt.Sprintf("%x", sha256.Sum256(payload))
if got := strings.TrimSpace(rr.Body.String()); got != expected {
t.Fatalf("unexpected checksum body: got %q want %q", got, expected)
}
}