Pulse/internal/api/unified_agent_test.go
rcourtman 191ddfdfc8 Never serve the server installer at /install.sh; serve local agent installer (#1470)
The "Install on Linux/Windows" wizard does `curl -fsSL <server>/install.sh |
bash -s -- --url ...` and never verifies the response signature headers (curl|bash
discards them). But for published releases handleDownloadInstallScriptCommon
proxied the top-level GitHub install.sh asset whenever the local agent installer
lacked its .sig/.sshsig sidecars, and since 49412357a that asset is the SERVER
installer, which rejects --url. Every install missing the sidecars served the
wrong script. The companion deploy_agent_scripts fix deploys the sidecars for new
installs, but existing boxes stay broken until they redeploy.

Serve the locally bundled agent installer when its signatures are absent instead
of proxying. An unsigned-but-correct local script beats a signed-but-wrong proxied
one when nothing verifies the headers, and this retroactively fixes already-deployed
boxes the moment they get the new binary. The proxy now runs only when no local
installer is bundled at all, so the endpoint can no longer hand the agent wizard a
server installer in any reachable deployment state. New installs still ship the
sidecars and are served signed.

Revise the install-script signature/fallback contract this changes, across the
three subsystems that pin it (api-contracts item 8, agent-lifecycle item 14,
storage-recovery item 14) plus the deployment-installability note, to state that
the served endpoint serves the agent installer with correctness outranking
signature presence. Add a handler guard asserting a published-release server with
a present-but-unsigned local installer serves it locally and does not proxy.
2026-05-29 14:19:04 +01:00

564 lines
21 KiB
Go

package api
import (
"archive/tar"
"bytes"
"compress/gzip"
"crypto/sha256"
"encoding/hex"
"errors"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func setupUnifiedAgentRouter(t *testing.T) (*Router, string) {
tempDir := t.TempDir()
// Create required directories
err := os.MkdirAll(filepath.Join(tempDir, "scripts"), 0755)
require.NoError(t, err)
err = os.MkdirAll(filepath.Join(tempDir, "bin"), 0755)
require.NoError(t, err)
router := &Router{
projectRoot: tempDir,
checksumCache: make(map[string]checksumCacheEntry),
}
return router, tempDir
}
func validTestUnifiedAgentBinary(suffix string) []byte {
return []byte("ELF test binary " + canonicalUnifiedAgentReportPath + " " + suffix)
}
func staleTestUnifiedAgentBinary(suffix string) []byte {
return []byte("ELF stale binary " + legacyUnifiedAgentReportPath + " " + suffix)
}
func encodedTestSSHSignature(payload string) string {
return encodeSSHSignatureForHeader([]byte(payload))
}
func TestDownloadInstallScript_Local(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy script
scriptContent := "#!/bin/bash\necho 'installing'"
scriptPath := filepath.Join(tempDir, "scripts", "install.sh")
err := os.WriteFile(scriptPath, []byte(scriptContent), 0644)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/install.sh", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedInstallScript(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, scriptContent, w.Body.String())
assert.Equal(t, "text/x-shellscript", w.Header().Get("Content-Type"))
}
// TestDownloadInstallScript_PublishedReleaseUnsignedLocalServesAgentInstaller
// pins the issue #1470 closure: when the local agent installer is present on a
// published-release server but its .sig/.sshsig sidecars are not, the endpoint
// must serve the LOCAL agent installer, never proxy the GitHub install.sh asset
// (which is the SERVER installer). This is the state every pre-fix LXC/systemd
// install is in until it redeploys the sidecars.
func TestDownloadInstallScript_PublishedReleaseUnsignedLocalServesAgentInstaller(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.6"
// A client that fails any outbound call, so a regression that re-introduces
// the GitHub proxy fallback surfaces as a test failure rather than silently
// serving the server installer.
router.installScriptClient = newTestInstallScriptClient(t, http.MethodGet, "", 0, "", errors.New("proxy must not be used"))
scriptContent := "#!/usr/bin/env bash\n# Pulse Unified Agent Installer\necho 'agent'"
scriptPath := filepath.Join(tempDir, "scripts", "install.sh")
require.NoError(t, os.WriteFile(scriptPath, []byte(scriptContent), 0644))
// Note: no .sig / .sshsig sidecars written.
req := httptest.NewRequest(http.MethodGet, "/install.sh", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedInstallScript(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, scriptContent, w.Body.String())
assert.NotEqual(t, "github-fallback", w.Header().Get("X-Served-From"))
}
func TestDownloadInstallScriptPS_Local(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy script
scriptContent := "Write-Host 'installing'"
scriptPath := filepath.Join(tempDir, "scripts", "install.ps1")
err := os.WriteFile(scriptPath, []byte(scriptContent), 0644)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/install.ps1", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedInstallScriptPS(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, scriptContent, w.Body.String())
assert.Equal(t, "text/plain", w.Header().Get("Content-Type"))
}
func TestDownloadUnifiedAgent_Local_Generic(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy binary in project root / bin
binContent := validTestUnifiedAgentBinary("generic")
binPath := filepath.Join(tempDir, "bin", "pulse-agent")
err := os.WriteFile(binPath, binContent, 0755)
require.NoError(t, err)
// Since cachedSHA256 might not be initialized or working without real file usage pattern,
// checking if our manual Router setup handles it.
// cachedSHA256 needs 'checksumCache' map initialized which we did in setupUnifiedAgentRouter.
req := httptest.NewRequest(http.MethodGet, "/api/install/agent", nil)
w := httptest.NewRecorder()
// Handle calls r.cachedSHA256 which reads the file
router.handleDownloadUnifiedAgent(w, req)
// We expect success if cachedSHA256 works
if w.Code == http.StatusInternalServerError {
// If cachedSHA256 fails (maybe because it's not exported or implemented elsewhere
// and depends on something I missed), we will fail here.
// cachedSHA256 is called in unified_agent.go but defined presumably in router.go or router_utils.go (unexported).
// I initialized checksumCache so it should work.
t.Logf("Handler returned 500: %s", w.Body.String())
}
require.Equal(t, http.StatusOK, w.Code, w.Body.String())
assert.Equal(t, string(binContent), w.Body.String())
// Verify Checksum Header
hash := sha256.Sum256(binContent)
expectedChecksum := hex.EncodeToString(hash[:])
assert.Equal(t, expectedChecksum, w.Header().Get("X-Checksum-Sha256"))
}
func TestDownloadUnifiedAgent_Local_SpecificArch(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
// Create dummy binary for linux-amd64
binContent := validTestUnifiedAgentBinary("linux-amd64")
binPath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
err := os.WriteFile(binPath, binContent, 0755)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, string(binContent), w.Body.String())
}
func TestDownloadUnifiedAgent_LocalReleaseBinaryIncludesSignatureHeader(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0"
binContent := validTestUnifiedAgentBinary("linux-amd64")
binPath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
err := os.WriteFile(binPath, binContent, 0755)
require.NoError(t, err)
err = os.WriteFile(binPath+".sig", []byte("signed-local-agent"), 0644)
require.NoError(t, err)
err = os.WriteFile(binPath+".sshsig", []byte("signed-local-agent-ssh"), 0644)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "signed-local-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-local-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_SkipsStaleLocalBinaryAndProxies(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
stalePath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
require.NoError(t, os.WriteFile(stalePath, staleTestUnifiedAgentBinary("linux-amd64"), 0755))
binaryContent := "fresh github binary"
expectedURL := "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
assert.Equal(t, "github-proxy", w.Header().Get("X-Served-From"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_DevModeRejectsStaleLocalBinary(t *testing.T) {
router, tempDir := setupUnifiedAgentRouter(t)
router.serverVersion = "dev"
stalePath := filepath.Join(tempDir, "bin", "pulse-agent-linux-amd64")
require.NoError(t, os.WriteFile(stalePath, staleTestUnifiedAgentBinary("linux-amd64"), 0755))
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
assert.Contains(t, w.Body.String(), "stale or incompatible")
assert.Contains(t, w.Body.String(), legacyUnifiedAgentReportPath)
assert.Contains(t, w.Body.String(), "go build -o bin/pulse-agent-linux-amd64")
}
func TestDownloadUnifiedAgent_ProxyFromGitHub(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
// Ensure NO local files exist (temp dir is empty of binaries)
// Set up a mock HTTP client to simulate GitHub response
binaryContent := "fake binary content for proxy test"
expectedURL := "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
// Should proxy the binary with checksum header instead of redirecting
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
assert.Equal(t, "github-proxy", w.Header().Get("X-Served-From"))
// Verify checksum header is present and correct
hash := sha256.Sum256([]byte(binaryContent))
expectedChecksum := hex.EncodeToString(hash[:])
assert.Equal(t, expectedChecksum, w.Header().Get("X-Checksum-Sha256"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_UsesConfiguredRepo(t *testing.T) {
t.Setenv("PULSE_GITHUB_REPO", "example/pulse-fork")
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
binaryContent := "fake binary content for proxy test"
expectedURL := "https://github.com/example/pulse-fork/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_Windows(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
binaryContent := "MZ fake windows binary"
expectedURL := "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-windows-amd64.exe"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: expectedURL, Status: http.StatusOK, Body: binaryContent},
{Method: http.MethodGet, URL: expectedURL + ".sig", Status: http.StatusOK, Body: "signed-agent"},
{Method: http.MethodGet, URL: expectedURL + ".sshsig", Status: http.StatusOK, Body: "signed-agent-ssh"},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=windows-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, binaryContent, w.Body.String())
assert.NotEmpty(t, w.Header().Get("X-Checksum-Sha256"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_ArchiveFallback_Darwin(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v9.9.9"
binaryContent := []byte("darwin arm64 binary payload")
archivePayload := buildTestTarGz(t, "pulse-agent-darwin-arm64", binaryContent)
binaryURL := "https://github.com/rcourtman/Pulse/releases/download/v9.9.9/pulse-agent-darwin-arm64"
signatureURL := binaryURL + ".sig"
sshSignatureURL := binaryURL + ".sshsig"
archiveURL := "https://github.com/rcourtman/Pulse/releases/download/v9.9.9/pulse-agent-v9.9.9-darwin-arm64.tar.gz"
router.installScriptClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
switch req.URL.String() {
case binaryURL:
return &http.Response{
StatusCode: http.StatusNotFound,
Status: "404 Not Found",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("not found")),
}, nil
case archiveURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(bytes.NewReader(archivePayload)),
}, nil
case signatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent")),
}, nil
case sshSignatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent-ssh")),
}, nil
default:
t.Fatalf("unexpected URL: %s", req.URL.String())
return nil, nil
}
}),
}
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=darwin-arm64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "github-proxy-archive", w.Header().Get("X-Served-From"))
assert.Equal(t, string(binaryContent), w.Body.String())
hash := sha256.Sum256(binaryContent)
expectedChecksum := hex.EncodeToString(hash[:])
assert.Equal(t, expectedChecksum, w.Header().Get("X-Checksum-Sha256"))
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_ArchiveFallback_UsesConfiguredRepo(t *testing.T) {
t.Setenv("PULSE_GITHUB_REPO", "example/pulse-fork")
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v9.9.9"
binaryContent := []byte("darwin arm64 binary payload")
archivePayload := buildTestTarGz(t, "pulse-agent-darwin-arm64", binaryContent)
binaryURL := "https://github.com/example/pulse-fork/releases/download/v9.9.9/pulse-agent-darwin-arm64"
signatureURL := binaryURL + ".sig"
sshSignatureURL := binaryURL + ".sshsig"
archiveURL := "https://github.com/example/pulse-fork/releases/download/v9.9.9/pulse-agent-v9.9.9-darwin-arm64.tar.gz"
router.installScriptClient = &http.Client{
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
switch req.URL.String() {
case binaryURL:
return &http.Response{
StatusCode: http.StatusNotFound,
Status: "404 Not Found",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("not found")),
}, nil
case archiveURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(bytes.NewReader(archivePayload)),
}, nil
case signatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent")),
}, nil
case sshSignatureURL:
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("signed-agent-ssh")),
}, nil
default:
t.Fatalf("unexpected URL: %s", req.URL.String())
return nil, nil
}
}),
}
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=darwin-arm64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "github-proxy-archive", w.Header().Get("X-Served-From"))
assert.Equal(t, string(binaryContent), w.Body.String())
assert.Equal(t, "signed-agent", w.Header().Get(signatureHeaderName))
assert.Equal(t, encodedTestSSHSignature("signed-agent-ssh"), w.Header().Get(sshSignatureHeaderName))
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_NotFound(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
router.installScriptClient = newTestInstallScriptClientSequence(t, []expectedHTTPExchange{
{Method: http.MethodGet, URL: "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64", Status: http.StatusNotFound, Body: ""},
{Method: http.MethodGet, URL: "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-v6.0.0-rc.1-linux-amd64.tar.gz", Status: http.StatusNotFound, Body: ""},
})
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
}
func TestDownloadUnifiedAgent_ProxyFromGitHub_Error(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-rc.1"
// GitHub is unreachable
router.installScriptClient = newTestInstallScriptClient(t, http.MethodGet, "https://github.com/rcourtman/Pulse/releases/download/v6.0.0-rc.1/pulse-agent-linux-amd64", 0, "", errors.New("connection refused"))
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusServiceUnavailable, w.Code)
}
func TestDownloadUnifiedAgent_DevPrereleaseRejectsGitHubFallback(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "v6.0.0-dev"
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=linux-amd64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
assert.Contains(t, w.Body.String(), "dev mode")
assert.Contains(t, w.Body.String(), "go build -o bin/pulse-agent-linux-amd64")
}
func TestDownloadUnifiedAgent_DevModeReportsRequestedLocalBuildCommand(t *testing.T) {
router, _ := setupUnifiedAgentRouter(t)
router.serverVersion = "dev"
req := httptest.NewRequest(http.MethodGet, "/api/install/agent?arch=darwin-arm64", nil)
w := httptest.NewRecorder()
router.handleDownloadUnifiedAgent(w, req)
require.Equal(t, http.StatusNotFound, w.Code)
assert.Contains(t, w.Body.String(), "Agent binary not found for \"darwin-arm64\" in dev mode.")
assert.Contains(t, w.Body.String(), "CGO_ENABLED=0 GOOS=darwin GOARCH=arm64")
assert.Contains(t, w.Body.String(), "go build -o bin/pulse-agent-darwin-arm64 ./cmd/pulse-agent")
assert.NotContains(t, w.Body.String(), "bin/pulse-agent-linux-amd64")
}
func TestUnifiedAgentLocalBuildCommandHandlesArmVariants(t *testing.T) {
assert.Equal(
t,
"CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o bin/pulse-agent-linux-armv7 ./cmd/pulse-agent",
unifiedAgentLocalBuildCommand("linux-armv7"),
)
assert.Equal(
t,
"CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o bin/pulse-agent-linux-armv6 ./cmd/pulse-agent",
unifiedAgentLocalBuildCommand("linux-armv6"),
)
}
func TestNormalizeUnifiedAgentArch(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"amd64", "linux-amd64"},
{"x86_64", "linux-amd64"},
{"linux-amd64", "linux-amd64"},
{"arm64", "linux-arm64"},
{"aarch64", "linux-arm64"},
{"windows-amd64", "windows-amd64"},
{"darwin-arm64", "darwin-arm64"},
{"unknown", ""},
{"", ""},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
assert.Equal(t, tt.expected, normalizeUnifiedAgentArch(tt.input))
})
}
}
func buildTestTarGz(t *testing.T, name string, payload []byte) []byte {
t.Helper()
var buf bytes.Buffer
gzw := gzip.NewWriter(&buf)
tw := tar.NewWriter(gzw)
header := &tar.Header{
Name: name,
Mode: 0o755,
Size: int64(len(payload)),
}
require.NoError(t, tw.WriteHeader(header))
_, err := tw.Write(payload)
require.NoError(t, err)
require.NoError(t, tw.Close())
require.NoError(t, gzw.Close())
return buf.Bytes()
}