mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-03 04:38:48 +00:00
test(agent): exercise offline FreeBSD preflight through local downloads
Cover real installer and local HTTP handlers with an explicit uname shim. Distinguish bundled artifacts from missing binaries or signature sidecars without claiming native pfSense installation acceptance. Change-source: pulse-maintainer
This commit is contained in:
parent
00f9c3c243
commit
6e2841cc65
1 changed files with 151 additions and 0 deletions
151
internal/api/unified_agent_offline_preflight_test.go
Normal file
151
internal/api/unified_agent_offline_preflight_test.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This is an offline HTTP/preflight contract, NOT native pfSense acceptance.
|
||||
// Only uname is simulated. Bash, curl, the served installer and download
|
||||
// handler are real; the agent bytes and signature sidecars are fixtures and
|
||||
// are never executed or claimed to be cryptographically qualified artifacts.
|
||||
func TestFreeBSDOfflineAgentPreflight(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
binary, sidecars bool
|
||||
wantExit int
|
||||
}{
|
||||
{"bundled", true, true, 0},
|
||||
{"missing_binary", false, false, 11},
|
||||
{"missing_sidecars", true, false, 11},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
binDir := setupTempPulseBin(t)
|
||||
payload := validTestUnifiedAgentBinary("offline-freebsd-fixture")
|
||||
binaryPath := filepath.Join(binDir, "pulse-agent-freebsd-amd64")
|
||||
write := func(path string, data []byte) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(path, data, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if tc.binary {
|
||||
write(binaryPath, payload)
|
||||
}
|
||||
if tc.sidecars {
|
||||
write(binaryPath+".sig", []byte("synthetic-signature"))
|
||||
write(binaryPath+".sshsig", []byte("synthetic-ssh-signature"))
|
||||
}
|
||||
var externalAttempts, healthRequests, downloadRequests atomic.Int32
|
||||
router := &Router{
|
||||
projectRoot: dir, serverVersion: "v6.4.5-rc.1",
|
||||
checksumCache: make(map[string]checksumCacheEntry),
|
||||
installScriptClient: &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
externalAttempts.Add(1)
|
||||
if req.URL.Host != "github.com" || !strings.HasSuffix(req.URL.Path, "/pulse-agent-freebsd-amd64") {
|
||||
t.Errorf("unexpected fallback destination: %s", req.URL)
|
||||
}
|
||||
return nil, errors.New("fixture: server has no external connectivity")
|
||||
})},
|
||||
}
|
||||
script, err := filepath.Abs(filepath.Join("..", "..", "scripts", "install.sh"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/install.sh", func(w http.ResponseWriter, r *http.Request) {
|
||||
handleDownloadInstallScriptCommon(w, r, "dev", filepath.Join(dir, "absent.sh"), script, "install.sh", "text/x-shellscript")
|
||||
})
|
||||
mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { healthRequests.Add(1); w.WriteHeader(http.StatusOK) })
|
||||
mux.HandleFunc("/download/pulse-agent", func(w http.ResponseWriter, r *http.Request) {
|
||||
downloadRequests.Add(1)
|
||||
if r.URL.Query().Get("arch") != "freebsd-amd64" {
|
||||
t.Errorf("wrong arch: %s", r.URL)
|
||||
}
|
||||
router.handleDownloadUnifiedAgent(w, r)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
downloadedScript := filepath.Join(dir, "install.sh")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
fetch := exec.CommandContext(ctx, "curl", "--noproxy", "*", "-fsS", server.URL+"/install.sh", "-o", downloadedScript)
|
||||
if out, err := fetch.CombinedOutput(); err != nil {
|
||||
t.Fatalf("fetch installer: %v\n%s", err, out)
|
||||
}
|
||||
original, err := os.ReadFile(script)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
downloaded, err := os.ReadFile(downloadedScript)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(original, downloaded) {
|
||||
t.Fatal("served installer differs from source")
|
||||
}
|
||||
shimDir := filepath.Join(dir, "shim")
|
||||
if err := os.Mkdir(shimDir, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
write(filepath.Join(shimDir, "uname"), []byte("#!/bin/sh\ncase \"$1\" in -s) echo FreeBSD;; -m) echo amd64;; *) exec /usr/bin/uname \"$@\";; esac\n"))
|
||||
cmd := exec.CommandContext(ctx, "bash", downloadedScript, "--url", server.URL, "--preflight-only", "--output", "json", "--non-interactive")
|
||||
cmd.Env = append(os.Environ(), "PATH="+shimDir+":"+os.Getenv("PATH"), "NO_PROXY=*", "no_proxy=*")
|
||||
out, runErr := cmd.CombinedOutput()
|
||||
exitCode := 0
|
||||
if runErr != nil {
|
||||
var ee *exec.ExitError
|
||||
if !errors.As(runErr, &ee) {
|
||||
t.Fatalf("preflight: %v\n%s", runErr, out)
|
||||
}
|
||||
exitCode = ee.ExitCode()
|
||||
}
|
||||
t.Logf("preflight exit=%d; external attempts=%d\n%s", exitCode, externalAttempts.Load(), out)
|
||||
if exitCode != tc.wantExit {
|
||||
t.Fatalf("exit=%d, want %d", exitCode, tc.wantExit)
|
||||
}
|
||||
if healthRequests.Load() != 1 || downloadRequests.Load() != 1 {
|
||||
t.Fatalf("health/download requests = %d/%d", healthRequests.Load(), downloadRequests.Load())
|
||||
}
|
||||
if tc.wantExit != 0 {
|
||||
if externalAttempts.Load() != 1 || !strings.Contains(string(out), `"code":"agent_download_unavailable"`) {
|
||||
t.Fatal("missing artifact must fail visibly at preflight")
|
||||
}
|
||||
return
|
||||
}
|
||||
if externalAttempts.Load() != 0 || !strings.Contains(string(out), `"code":"agent_download_available"`) {
|
||||
t.Fatal("bundled artifact must preflight without external fetch")
|
||||
}
|
||||
response, err := server.Client().Get(server.URL + "/download/pulse-agent?arch=freebsd-amd64")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
data, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if response.StatusCode != http.StatusOK || !bytes.Equal(data, payload) || response.Header.Get(checksumHeaderName) != fmt.Sprintf("%x", sha256.Sum256(payload)) {
|
||||
t.Fatal("local payload/checksum mismatch")
|
||||
}
|
||||
if externalAttempts.Load() != 0 {
|
||||
t.Fatal("local download attempted external access")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue