mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 03:20:11 +00:00
fix(agent): three backend fixes for FreeBSD, Docker rootless, and duplicate PVE hosts
FreeBSD auto-update (#1254): determineArch() now includes freebsd in its OS switch, producing freebsd-amd64/arm64 instead of falling through to a uname -m fallback that incorrectly returned linux-<arch>. FreeBSD agents were downloading Linux ELF binaries and failing to exec them. Docker rootless socket (#1200): buildRuntimeCandidates() now probes /run/user/<uid>/docker.sock before the system-wide /var/run/docker.sock, enabling auto-detection of Docker rootless installations. Duplicate PVE/PBS hosts (#1245, #1252): handleSecureAutoRegister() now deduplicates by host URL, updating the existing instance's token in-place instead of appending a duplicate entry on each re-run of the setup script. Fixes #1254 Fixes #1200 Fixes #1245 Fixes #1252 (cherry picked from commit 0f1d9e9b9fea6c8b9e65872e8a78e25f93653eef)
This commit is contained in:
parent
97aee77ae7
commit
7522f6599c
5 changed files with 92 additions and 3 deletions
|
|
@ -85,6 +85,15 @@ func TestDetermineArchOverrides(t *testing.T) {
|
|||
unameCommand = origUname
|
||||
})
|
||||
|
||||
// FreeBSD is a first-class OS target for agent auto-updates.
|
||||
// It should not fall back to uname mappings that force a linux-* arch.
|
||||
runtimeGOOS = "freebsd"
|
||||
runtimeGOARCH = "amd64"
|
||||
unameCommand = func() ([]byte, error) { return nil, errors.New("uname should not be called for freebsd") }
|
||||
if got := determineArch(); got != "freebsd-amd64" {
|
||||
t.Fatalf("expected freebsd-amd64, got %q", got)
|
||||
}
|
||||
|
||||
runtimeGOOS = "linux"
|
||||
runtimeGOARCH = "arm"
|
||||
if got := determineArch(); got != "linux-armv7" {
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ func determineArch() string {
|
|||
|
||||
// For known OS/arch combinations, return directly
|
||||
switch os {
|
||||
case "linux", "darwin", "windows":
|
||||
case "linux", "darwin", "windows", "freebsd":
|
||||
return fmt.Sprintf("%s-%s", os, arch)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5753,7 +5753,27 @@ func (h *ConfigHandlers) handleSecureAutoRegister(w http.ResponseWriter, r *http
|
|||
MonitorStorage: true,
|
||||
MonitorBackups: true,
|
||||
}
|
||||
h.getConfig(r.Context()).PVEInstances = append(h.getConfig(r.Context()).PVEInstances, pveNode)
|
||||
// Deduplicate by host to keep secure auto-registration idempotent on reruns.
|
||||
existingIndex := -1
|
||||
for i, node := range h.getConfig(r.Context()).PVEInstances {
|
||||
if node.Host == host {
|
||||
existingIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if existingIndex >= 0 {
|
||||
instance := &h.getConfig(r.Context()).PVEInstances[existingIndex]
|
||||
instance.Host = host
|
||||
instance.User = ""
|
||||
instance.Password = ""
|
||||
instance.TokenName = pveNode.TokenName
|
||||
instance.TokenValue = pveNode.TokenValue
|
||||
instance.Fingerprint = pveNode.Fingerprint
|
||||
instance.VerifySSL = pveNode.VerifySSL
|
||||
log.Info().Str("host", host).Str("type", "pve").Msg("Secure auto-register matched existing node by host; updated token in-place")
|
||||
} else {
|
||||
h.getConfig(r.Context()).PVEInstances = append(h.getConfig(r.Context()).PVEInstances, pveNode)
|
||||
}
|
||||
} else if req.Type == "pbs" {
|
||||
pbsNode := config.PBSInstance{
|
||||
Name: serverName,
|
||||
|
|
@ -5768,7 +5788,27 @@ func (h *ConfigHandlers) handleSecureAutoRegister(w http.ResponseWriter, r *http
|
|||
MonitorVerifyJobs: true,
|
||||
MonitorPruneJobs: true,
|
||||
}
|
||||
h.getConfig(r.Context()).PBSInstances = append(h.getConfig(r.Context()).PBSInstances, pbsNode)
|
||||
// Deduplicate by host to keep secure auto-registration idempotent on reruns.
|
||||
existingIndex := -1
|
||||
for i, node := range h.getConfig(r.Context()).PBSInstances {
|
||||
if node.Host == host {
|
||||
existingIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if existingIndex >= 0 {
|
||||
instance := &h.getConfig(r.Context()).PBSInstances[existingIndex]
|
||||
instance.Host = host
|
||||
instance.User = ""
|
||||
instance.Password = ""
|
||||
instance.TokenName = pbsNode.TokenName
|
||||
instance.TokenValue = pbsNode.TokenValue
|
||||
instance.Fingerprint = pbsNode.Fingerprint
|
||||
instance.VerifySSL = pbsNode.VerifySSL
|
||||
log.Info().Str("host", host).Str("type", "pbs").Msg("Secure auto-register matched existing node by host; updated token in-place")
|
||||
} else {
|
||||
h.getConfig(r.Context()).PBSInstances = append(h.getConfig(r.Context()).PBSInstances, pbsNode)
|
||||
}
|
||||
}
|
||||
|
||||
// Save configuration
|
||||
|
|
|
|||
|
|
@ -501,6 +501,13 @@ func buildRuntimeCandidates(preference RuntimeKind) []runtimeCandidate {
|
|||
}
|
||||
|
||||
if preference == RuntimeDocker || preference == RuntimeAuto {
|
||||
// Prefer rootless docker if present. Rootless installs use the per-user XDG runtime socket.
|
||||
rootlessDocker := fmt.Sprintf("unix:///run/user/%d/docker.sock", os.Getuid())
|
||||
add(runtimeCandidate{
|
||||
host: rootlessDocker,
|
||||
label: "docker rootless socket",
|
||||
})
|
||||
|
||||
add(runtimeCandidate{
|
||||
host: "unix:///var/run/docker.sock",
|
||||
label: "default docker socket",
|
||||
|
|
|
|||
|
|
@ -1001,8 +1001,12 @@ func TestBuildRuntimeCandidatesContent(t *testing.T) {
|
|||
t.Run("auto includes both docker and podman", func(t *testing.T) {
|
||||
candidates := buildRuntimeCandidates(RuntimeAuto)
|
||||
hasDocker := false
|
||||
hasRootlessDocker := false
|
||||
hasPodman := false
|
||||
for _, c := range candidates {
|
||||
if c.label == "docker rootless socket" {
|
||||
hasRootlessDocker = true
|
||||
}
|
||||
if c.label == "default docker socket" {
|
||||
hasDocker = true
|
||||
}
|
||||
|
|
@ -1010,6 +1014,9 @@ func TestBuildRuntimeCandidatesContent(t *testing.T) {
|
|||
hasPodman = true
|
||||
}
|
||||
}
|
||||
if !hasRootlessDocker {
|
||||
t.Error("auto preference should include docker rootless socket")
|
||||
}
|
||||
if !hasDocker {
|
||||
t.Error("auto preference should include docker socket")
|
||||
}
|
||||
|
|
@ -1019,6 +1026,32 @@ func TestBuildRuntimeCandidatesContent(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestBuildRuntimeCandidatesDockerRootlessOrder(t *testing.T) {
|
||||
t.Run("docker tries rootless before system socket", func(t *testing.T) {
|
||||
candidates := buildRuntimeCandidates(RuntimeDocker)
|
||||
rootlessIdx := -1
|
||||
systemIdx := -1
|
||||
for i, c := range candidates {
|
||||
if c.label == "docker rootless socket" && rootlessIdx < 0 {
|
||||
rootlessIdx = i
|
||||
}
|
||||
if c.label == "default docker socket" && systemIdx < 0 {
|
||||
systemIdx = i
|
||||
}
|
||||
}
|
||||
|
||||
if rootlessIdx < 0 {
|
||||
t.Fatal("expected docker rootless socket candidate")
|
||||
}
|
||||
if systemIdx < 0 {
|
||||
t.Fatal("expected default docker socket candidate")
|
||||
}
|
||||
if rootlessIdx > systemIdx {
|
||||
t.Fatalf("expected docker rootless socket to be tried before default docker socket; rootlessIdx=%d systemIdx=%d", rootlessIdx, systemIdx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildRuntimeCandidatesEnv(t *testing.T) {
|
||||
t.Setenv("DOCKER_HOST", "unix:///tmp/docker.sock")
|
||||
t.Setenv("CONTAINER_HOST", "unix:///tmp/container.sock")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue