mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 03:20:11 +00:00
Proxy missing host-agent binaries from GitHub releases (#1254)
This commit is contained in:
parent
1de1392c9b
commit
2ed1c3b839
3 changed files with 146 additions and 0 deletions
|
|
@ -7135,6 +7135,14 @@ func (r *Router) handleDownloadHostAgent(w http.ResponseWriter, req *http.Reques
|
|||
return
|
||||
}
|
||||
|
||||
// Fallback: proxy from GitHub releases for strict platform/arch requests.
|
||||
// This mirrors unified-agent behavior and covers installs that were updated
|
||||
// without refreshing the local host-agent binary bundle.
|
||||
if platformParam != "" && archParam != "" {
|
||||
r.proxyHostAgentBinaryFromGitHub(w, req, platformParam, archParam, strings.HasSuffix(req.URL.Path, ".sha256"))
|
||||
return
|
||||
}
|
||||
|
||||
// Build detailed error message with troubleshooting guidance
|
||||
var errorMsg strings.Builder
|
||||
errorMsg.WriteString(fmt.Sprintf("Host agent binary not found for %s/%s\n\n", platformParam, archParam))
|
||||
|
|
|
|||
|
|
@ -117,3 +117,74 @@ func TestHandleDownloadHostAgentAllowsHEAD(t *testing.T) {
|
|||
t.Fatalf("expected empty body for HEAD, got %d bytes", rr.Body.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDownloadHostAgent_ProxyFromGitHub(t *testing.T) {
|
||||
binDir := setupTempPulseBin(t)
|
||||
router := &Router{
|
||||
projectRoot: t.TempDir(),
|
||||
installScriptClient: newTestInstallScriptClient(t, "https://github.com/rcourtman/Pulse/releases/latest/download/pulse-host-agent-freebsd-amd64", http.StatusOK, "freebsd-binary", nil),
|
||||
}
|
||||
|
||||
for _, path := range []string{
|
||||
filepath.Join(binDir, "pulse-host-agent-freebsd-amd64"),
|
||||
"/opt/pulse/pulse-host-agent-freebsd-amd64",
|
||||
filepath.Join("/app", "pulse-host-agent-freebsd-amd64"),
|
||||
} {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
t.Skipf("local host-agent binary exists at %s; skipping proxy fallback test", path)
|
||||
}
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/download/pulse-host-agent?platform=freebsd&arch=amd64", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.handleDownloadHostAgent(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200 OK, got %d body=%s", rr.Code, rr.Body.String())
|
||||
}
|
||||
if got := rr.Body.String(); got != "freebsd-binary" {
|
||||
t.Fatalf("unexpected response body: %q", got)
|
||||
}
|
||||
if got := rr.Header().Get("X-Served-From"); got != "github-proxy" {
|
||||
t.Fatalf("unexpected X-Served-From header: %q", got)
|
||||
}
|
||||
expectedChecksum := fmt.Sprintf("%x", sha256.Sum256([]byte("freebsd-binary")))
|
||||
if got := rr.Header().Get("X-Checksum-Sha256"); got != expectedChecksum {
|
||||
t.Fatalf("unexpected checksum header: got %q want %q", got, expectedChecksum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDownloadHostAgentChecksum_ProxyFromGitHub(t *testing.T) {
|
||||
binDir := setupTempPulseBin(t)
|
||||
router := &Router{
|
||||
projectRoot: t.TempDir(),
|
||||
installScriptClient: newTestInstallScriptClient(t, "https://github.com/rcourtman/Pulse/releases/latest/download/pulse-host-agent-freebsd-amd64", http.StatusOK, "freebsd-binary", nil),
|
||||
}
|
||||
|
||||
for _, path := range []string{
|
||||
filepath.Join(binDir, "pulse-host-agent-freebsd-amd64"),
|
||||
"/opt/pulse/pulse-host-agent-freebsd-amd64",
|
||||
filepath.Join("/app", "pulse-host-agent-freebsd-amd64"),
|
||||
} {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
t.Skipf("local host-agent binary exists at %s; skipping proxy checksum fallback test", path)
|
||||
}
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/download/pulse-host-agent.sha256?platform=freebsd&arch=amd64", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
router.handleDownloadHostAgent(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200 OK, got %d body=%s", rr.Code, rr.Body.String())
|
||||
}
|
||||
expected := fmt.Sprintf("%x", sha256.Sum256([]byte("freebsd-binary")))
|
||||
if got := strings.TrimSpace(rr.Body.String()); got != expected {
|
||||
t.Fatalf("unexpected checksum body: got %q want %q", got, expected)
|
||||
}
|
||||
if got := rr.Header().Get("X-Served-From"); got != "github-proxy" {
|
||||
t.Fatalf("unexpected X-Served-From header: %q", got)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,6 +249,73 @@ func (r *Router) proxyAgentBinaryFromGitHub(w http.ResponseWriter, req *http.Req
|
|||
w.Write(content)
|
||||
}
|
||||
|
||||
// proxyHostAgentBinaryFromGitHub downloads a host-agent binary from GitHub releases and
|
||||
// serves either the binary (with checksum header) or just the checksum body for .sha256 requests.
|
||||
func (r *Router) proxyHostAgentBinaryFromGitHub(w http.ResponseWriter, req *http.Request, platform, arch string, checksumOnly bool) {
|
||||
binaryName := "pulse-host-agent-" + platform + "-" + arch
|
||||
if platform == "windows" {
|
||||
binaryName += ".exe"
|
||||
}
|
||||
githubURL := "https://github.com/rcourtman/Pulse/releases/latest/download/" + binaryName
|
||||
|
||||
log.Info().
|
||||
Str("platform", platform).
|
||||
Str("arch", arch).
|
||||
Str("url", githubURL).
|
||||
Msg("Local host agent binary not found, proxying from GitHub releases")
|
||||
|
||||
client := r.installScriptClient
|
||||
if client == nil {
|
||||
client = &http.Client{
|
||||
Timeout: 5 * time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Get(githubURL)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("url", githubURL).Msg("Failed to fetch host agent binary from GitHub")
|
||||
http.Error(w, "Failed to fetch host agent binary", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Error().Int("status", resp.StatusCode).Str("url", githubURL).Msg("GitHub returned non-200 status for host agent binary")
|
||||
http.Error(w, "Host agent binary not found on GitHub", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
const maxHostAgentBinarySize = 100 * 1024 * 1024
|
||||
limitedReader := io.LimitReader(resp.Body, maxHostAgentBinarySize+1)
|
||||
|
||||
hasher := sha256.New()
|
||||
content, err := io.ReadAll(io.TeeReader(limitedReader, hasher))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to read host agent binary from GitHub")
|
||||
http.Error(w, "Failed to read host agent binary", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if int64(len(content)) > maxHostAgentBinarySize {
|
||||
log.Error().Int64("size", int64(len(content))).Msg("Host agent binary from GitHub exceeds size limit")
|
||||
http.Error(w, "Host agent binary too large", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
if checksumOnly {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Header().Set("X-Served-From", "github-proxy")
|
||||
_, _ = w.Write([]byte(checksum + "\n"))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("X-Checksum-Sha256", checksum)
|
||||
w.Header().Set("X-Served-From", "github-proxy")
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
_, _ = w.Write(content)
|
||||
}
|
||||
|
||||
// proxyInstallScriptFromGitHub fetches an install script from GitHub releases
|
||||
// This is used as a fallback when scripts aren't available locally (e.g., LXC updates)
|
||||
func (r *Router) proxyInstallScriptFromGitHub(w http.ResponseWriter, req *http.Request, scriptName string) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue