Skip to content

Commit 19bce8a

Browse files
committed
Restrict auto-updater to client binaries only
Only replace daemon, pilotctl, gateway, and updater during auto-update. Server binaries (registry, beacon, rendezvous, nameserver) are skipped since they are managed separately on infrastructure hosts.
1 parent ff5f32d commit 19bce8a

2 files changed

Lines changed: 103 additions & 1 deletion

File tree

pkg/updater/updater.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,15 @@ func (u *Updater) applyUpdate(release *GitHubRelease) error {
228228
return fmt.Errorf("extract archive: %w", err)
229229
}
230230

231-
// Replace binaries (rm -f first, then copy — same pattern as GCP deploy).
231+
// Only replace client binaries — server binaries (registry, beacon,
232+
// rendezvous, nameserver) are managed separately.
233+
clientBins := map[string]bool{
234+
"daemon": true,
235+
"pilotctl": true,
236+
"gateway": true,
237+
"updater": true,
238+
}
239+
232240
entries, err := os.ReadDir(stagingDir)
233241
if err != nil {
234242
return fmt.Errorf("read staging dir: %w", err)
@@ -238,6 +246,10 @@ func (u *Updater) applyUpdate(release *GitHubRelease) error {
238246
if entry.IsDir() {
239247
continue
240248
}
249+
if !clientBins[entry.Name()] {
250+
slog.Debug("skipping server binary", "name", entry.Name())
251+
continue
252+
}
241253
src := filepath.Join(stagingDir, entry.Name())
242254
dst := filepath.Join(u.config.InstallDir, entry.Name())
243255
if err := replaceBinary(src, dst); err != nil {

pkg/updater/updater_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"net/http/httptest"
1111
"os"
1212
"path/filepath"
13+
"runtime"
1314
"testing"
1415
)
1516

@@ -278,6 +279,95 @@ func TestReplaceBinary(t *testing.T) {
278279
}
279280
}
280281

282+
func TestApplyUpdate_SkipsServerBinaries(t *testing.T) {
283+
tmpDir := t.TempDir()
284+
installDir := filepath.Join(tmpDir, "bin")
285+
os.MkdirAll(installDir, 0755)
286+
287+
// Seed existing binaries (client + server).
288+
os.WriteFile(filepath.Join(installDir, "daemon"), []byte("old-daemon"), 0755)
289+
os.WriteFile(filepath.Join(installDir, "registry"), []byte("old-registry"), 0755)
290+
os.WriteFile(filepath.Join(installDir, "beacon"), []byte("old-beacon"), 0755)
291+
os.WriteFile(filepath.Join(installDir, ".pilot-version"), []byte("v1.0.0\n"), 0644)
292+
293+
// Build an archive with both client and server binaries.
294+
archiveDir := t.TempDir()
295+
archiveName := fmt.Sprintf("pilot-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH)
296+
archivePath := filepath.Join(archiveDir, archiveName)
297+
createTestTarGz(t, archivePath, map[string]string{
298+
"daemon": "new-daemon",
299+
"pilotctl": "new-pilotctl",
300+
"gateway": "new-gateway",
301+
"updater": "new-updater",
302+
"registry": "new-registry",
303+
"beacon": "new-beacon",
304+
"rendezvous": "new-rendezvous",
305+
"nameserver": "new-nameserver",
306+
})
307+
archiveContent, _ := os.ReadFile(archivePath)
308+
archiveHash := sha256.Sum256(archiveContent)
309+
checksumsContent := fmt.Sprintf("%x %s\n", archiveHash, archiveName)
310+
311+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
312+
switch r.URL.Path {
313+
case "/download/" + archiveName:
314+
w.Write(archiveContent)
315+
case "/download/checksums.txt":
316+
w.Write([]byte(checksumsContent))
317+
default:
318+
http.NotFound(w, r)
319+
}
320+
}))
321+
defer srv.Close()
322+
323+
u := &Updater{
324+
config: Config{InstallDir: installDir},
325+
client: srv.Client(),
326+
stopCh: make(chan struct{}),
327+
}
328+
329+
release := &GitHubRelease{
330+
TagName: "v1.1.0",
331+
Assets: []GitHubAsset{
332+
{Name: archiveName, BrowserDownloadURL: srv.URL + "/download/" + archiveName},
333+
{Name: "checksums.txt", BrowserDownloadURL: srv.URL + "/download/checksums.txt"},
334+
},
335+
}
336+
337+
if err := u.applyUpdate(release); err != nil {
338+
t.Fatalf("applyUpdate: %v", err)
339+
}
340+
341+
// Client binaries should be updated.
342+
for _, name := range []string{"daemon", "pilotctl", "gateway", "updater"} {
343+
data, err := os.ReadFile(filepath.Join(installDir, name))
344+
if err != nil {
345+
t.Fatalf("read %s: %v", name, err)
346+
}
347+
if string(data) != "new-"+name {
348+
t.Errorf("%s = %q, want %q", name, string(data), "new-"+name)
349+
}
350+
}
351+
352+
// Server binaries should NOT be updated.
353+
for _, name := range []string{"registry", "beacon"} {
354+
data, err := os.ReadFile(filepath.Join(installDir, name))
355+
if err != nil {
356+
t.Fatalf("read %s: %v", name, err)
357+
}
358+
if string(data) != "old-"+name {
359+
t.Errorf("%s = %q, want %q (should be unchanged)", name, string(data), "old-"+name)
360+
}
361+
}
362+
363+
// Server binaries not previously present should NOT be created.
364+
for _, name := range []string{"rendezvous", "nameserver"} {
365+
if _, err := os.Stat(filepath.Join(installDir, name)); err == nil {
366+
t.Errorf("%s should not have been created", name)
367+
}
368+
}
369+
}
370+
281371
// createTestTarGz creates a tar.gz archive with the given file name→content map.
282372
func createTestTarGz(t *testing.T, path string, files map[string]string) {
283373
t.Helper()

0 commit comments

Comments
 (0)