Port installer bundle fallback fix from v5

This commit is contained in:
rcourtman 2026-05-01 17:23:38 +01:00
parent ff1c21c39b
commit 411e8daa4d
4 changed files with 145 additions and 44 deletions

View file

@ -0,0 +1,43 @@
# Known RC Issue Closure For GA Installer Bundle Fallback Record
- Date: `2026-05-01`
- Gate: `known-rc-issue-closure-for-ga`
- Lane: `L1`
- Result: `passed`
## Context
The v5 maintenance audit found that `release/5.1` had removed an eager
universal-bundle fallback from the root installer. The old fallback attempted
to download `pulse-${version}.tar.gz` during install when every cross-arch
agent binary was not already present locally.
The v6 root installer still carried the same fallback for unified-agent
binaries. That made a normal install perform an unnecessary second network
lookup for a non-arch bundle and could produce a confusing warning even after
the actual platform archive had installed successfully.
## Disposition
The v6 installer now matches the maintenance-line behavior:
- bundled unified-agent binaries are still copied from the extracted release
archive when present;
- the installer no longer tries to download a universal cross-architecture
agent bundle during the main install path;
- missing unified-agent binaries are left for the on-demand agent install path
instead of making the server installer depend on a fallback release asset.
This is the canonical root fix for RC3 because the install-time dependency was
owned by the release installer, not by downstream agent setup commands.
## Proof
- `go test ./scripts/installtests -run 'TestInstallAdditionalAgentBinaries|TestResolveInstallScriptDownloadURL|TestInstallSHRequiresPinnedSignatureVerificationForReleaseDownloads' -count=1`
- `bash -n install.sh`
- `git diff --check`
## Outcome
The v5 installer bundle-fallback fix is ported to the current v6 candidate. The
`known-rc-issue-closure-for-ga` gate remains satisfied for this slice.

View file

@ -2476,6 +2476,11 @@
"path": "docs/release-control/v6/internal/HIGH_RISK_RELEASE_VERIFICATION_MATRIX.md",
"kind": "file"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md",
"kind": "file"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/subsystems/deployment-installability.md",
@ -4388,6 +4393,12 @@
"kind": "file",
"evidence_tier": "managed-runtime-exercise"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-installer-bundle-fallback-2026-05-01.md",
"kind": "file",
"evidence_tier": "managed-runtime-exercise"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/records/known-rc-issue-closure-for-ga-late-issue-intake-2026-05-01.md",

View file

@ -2960,6 +2960,7 @@ copy_unified_agent_binaries_from_dir() {
install_additional_agent_binaries() {
local version="$1"
local source_dir="${2:-}"
local local_unified_installed=0
if [[ -z "$version" ]]; then
return
@ -2968,7 +2969,9 @@ install_additional_agent_binaries() {
local unified_targets=("linux-amd64" "linux-arm64" "linux-armv7" "linux-armv6" "linux-386" "darwin-amd64" "darwin-arm64" "windows-amd64" "windows-arm64" "windows-386")
# Prefer locally available agents from the extracted archive to avoid network reliance
copy_unified_agent_binaries_from_dir "$source_dir" || true
if copy_unified_agent_binaries_from_dir "$source_dir"; then
local_unified_installed=1
fi
local unified_missing_targets=()
for target in "${unified_targets[@]}"; do
@ -2984,55 +2987,18 @@ install_additional_agent_binaries() {
done
if [[ ${#unified_missing_targets[@]} -eq 0 ]]; then
return
fi
local universal_url="https://github.com/$GITHUB_REPO/releases/download/$version/pulse-${version}.tar.gz"
local universal_tar="/tmp/pulse-universal-${version}.tar.gz"
print_info "Downloading universal agent bundle for cross-architecture support..."
if command -v curl >/dev/null 2>&1; then
if ! curl -fsSL --connect-timeout 10 --max-time 300 -o "$universal_tar" "$universal_url"; then
print_warn "Failed to download universal agent bundle"
rm -f "$universal_tar"
return
if [[ $local_unified_installed -eq 1 ]]; then
print_success "Unified agent binaries installed"
fi
elif command -v wget >/dev/null 2>&1; then
if ! wget -q --timeout=300 -O "$universal_tar" "$universal_url"; then
print_warn "Failed to download universal agent bundle"
rm -f "$universal_tar"
return
fi
else
print_warn "Cannot download universal agent bundle (curl or wget not available)"
return
fi
local temp_dir
temp_dir=$(mktemp -d -t pulse-universal-XXXXXX)
if ! tar -xzf "$universal_tar" -C "$temp_dir"; then
print_warn "Failed to extract universal agent bundle"
rm -f "$universal_tar"
rm -rf "$temp_dir"
return
fi
# Install unified agent binaries (preserve symlinks for Windows targets)
local unified_installed=0
if copy_unified_agent_binaries_from_dir "$temp_dir"; then
unified_installed=1
fi
if [[ $unified_installed -eq 1 ]]; then
if [[ $local_unified_installed -eq 1 ]]; then
print_success "Unified agent binaries installed"
fi
if [[ $unified_installed -eq 0 ]]; then
print_warn "No agent binaries found in universal bundle"
fi
rm -f "$universal_tar"
rm -rf "$temp_dir"
print_info "Skipping eager cross-architecture agent bundle download during install"
print_info "Missing unified agent binaries will be fetched on demand when requested"
return 0
}
deploy_agent_scripts() {

View file

@ -1332,6 +1332,87 @@ exit 1
}
}
func TestInstallAdditionalAgentBinariesCopiesLocalExtrasWithoutNetwork(t *testing.T) {
tmpDir := t.TempDir()
sourceDir := filepath.Join(tmpDir, "source")
installDir := filepath.Join(tmpDir, "opt", "pulse")
if err := os.MkdirAll(filepath.Join(sourceDir, "bin"), 0755); err != nil {
t.Fatalf("mkdir source bin: %v", err)
}
if err := os.MkdirAll(filepath.Join(installDir, "bin"), 0755); err != nil {
t.Fatalf("mkdir install bin: %v", err)
}
agentPath := filepath.Join(sourceDir, "bin", "pulse-agent-linux-arm64")
if err := os.WriteFile(agentPath, []byte("unified-agent\n"), 0755); err != nil {
t.Fatalf("write agent: %v", err)
}
script := `
INSTALL_DIR="` + installDir + `"
GITHUB_REPO="rcourtman/Pulse"
curl_calls=0
wget_calls=0
chown() { :; }
print_info() { :; }
print_warn() { :; }
print_success() { :; }
curl() { curl_calls=$((curl_calls + 1)); return 99; }
wget() { wget_calls=$((wget_calls + 1)); return 99; }
` + extractRootInstallShellFunction(t, "copy_unified_agent_binaries_from_dir") + `
` + extractRootInstallShellFunction(t, "install_additional_agent_binaries") + `
install_additional_agent_binaries "v6.0.0-rc.3" "` + sourceDir + `"
printf 'curl=%s wget=%s\n' "$curl_calls" "$wget_calls"
`
out, err := exec.Command("bash", "-c", script).CombinedOutput()
if err != nil {
t.Fatalf("bash: %v\n%s", err, out)
}
if got := strings.TrimSpace(string(out)); got != "curl=0 wget=0" {
t.Fatalf("expected no network fallback calls, got %q", got)
}
if _, err := os.Stat(filepath.Join(installDir, "bin", "pulse-agent-linux-arm64")); err != nil {
t.Fatalf("expected local unified agent binary to be copied: %v", err)
}
}
func TestInstallAdditionalAgentBinariesSkipsNetworkWhenLocalExtrasAreMissing(t *testing.T) {
tmpDir := t.TempDir()
sourceDir := filepath.Join(tmpDir, "source")
installDir := filepath.Join(tmpDir, "opt", "pulse")
if err := os.MkdirAll(filepath.Join(sourceDir, "bin"), 0755); err != nil {
t.Fatalf("mkdir source bin: %v", err)
}
if err := os.MkdirAll(filepath.Join(installDir, "bin"), 0755); err != nil {
t.Fatalf("mkdir install bin: %v", err)
}
script := `
INSTALL_DIR="` + installDir + `"
GITHUB_REPO="rcourtman/Pulse"
curl_calls=0
wget_calls=0
chown() { :; }
print_info() { :; }
print_warn() { :; }
print_success() { :; }
curl() { curl_calls=$((curl_calls + 1)); return 99; }
wget() { wget_calls=$((wget_calls + 1)); return 99; }
` + extractRootInstallShellFunction(t, "copy_unified_agent_binaries_from_dir") + `
` + extractRootInstallShellFunction(t, "install_additional_agent_binaries") + `
install_additional_agent_binaries "v6.0.0-rc.3" "` + sourceDir + `"
printf 'curl=%s wget=%s\n' "$curl_calls" "$wget_calls"
`
out, err := exec.Command("bash", "-c", script).CombinedOutput()
if err != nil {
t.Fatalf("bash: %v\n%s", err, out)
}
if got := strings.TrimSpace(string(out)); got != "curl=0 wget=0" {
t.Fatalf("expected missing local extras to skip network fallback, got %q", got)
}
}
func TestInstallSHRequiresPinnedSignatureVerificationForReleaseDownloads(t *testing.T) {
content, err := os.ReadFile(repoFile("scripts", "install.sh"))
if err != nil {