agent v0.26.0: slice 10 P2 activation — POST /guest/reboot (user-triggered)
Self-scoped guest reboot (pct reboot, detached, 202) so an enrolled-into-running- guest drive's persisted bind activates at next boot. Tests: accepted + cross-guest 403. Pairs with controller v0.49.0 pending-drive detection + "Újraindítás most". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/proxmox"
|
||||
@@ -380,6 +381,7 @@ type fakeGuestAttacher struct {
|
||||
vmid int
|
||||
slot, where string
|
||||
}
|
||||
reboots []int
|
||||
}
|
||||
|
||||
func (f *fakeGuestAttacher) AttachBind(_ context.Context, vmid int, mountKey, where string) error {
|
||||
@@ -393,6 +395,14 @@ func (f *fakeGuestAttacher) AttachBind(_ context.Context, vmid int, mountKey, wh
|
||||
}
|
||||
func (f *fakeGuestAttacher) count() int { f.mu.Lock(); defer f.mu.Unlock(); return len(f.calls) }
|
||||
|
||||
func (f *fakeGuestAttacher) RebootGuest(_ context.Context, vmid int) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.reboots = append(f.reboots, vmid)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeGuestAttacher) rebootCount() int { f.mu.Lock(); defer f.mu.Unlock(); return len(f.reboots) }
|
||||
|
||||
func newAttachServer(t *testing.T, ga GuestAttacher, mounts map[int]map[string]string) http.Handler {
|
||||
t.Helper()
|
||||
srv, err := NewServer(Options{
|
||||
@@ -462,6 +472,36 @@ func TestGuestAttach_NotConfigured(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Guest reboot (activation) returns 202 and triggers RebootGuest for the token's vmid (detached).
|
||||
func TestGuestReboot_Accepted(t *testing.T) {
|
||||
ga := &fakeGuestAttacher{}
|
||||
h := newAttachServer(t, ga, map[int]map[string]string{8200: {}})
|
||||
w := do(t, h, "POST", "/guest/reboot", "A", "")
|
||||
if w.Code != http.StatusAccepted {
|
||||
t.Fatalf("reboot: got %d want 202 (%s)", w.Code, w.Body.String())
|
||||
}
|
||||
// The reboot runs in a goroutine; give it a moment to record the call.
|
||||
for i := 0; i < 100 && ga.rebootCount() == 0; i++ {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if ga.rebootCount() != 1 || ga.reboots[0] != 8200 {
|
||||
t.Fatalf("RebootGuest not invoked for vmid 8200: %+v", ga.reboots)
|
||||
}
|
||||
}
|
||||
|
||||
// A cross-guest reboot (body vmid != token's) is refused 403, no reboot.
|
||||
func TestGuestReboot_CrossGuest403(t *testing.T) {
|
||||
ga := &fakeGuestAttacher{}
|
||||
h := newAttachServer(t, ga, map[int]map[string]string{8200: {}})
|
||||
if w := do(t, h, "POST", "/guest/reboot", "A", `{"vmid":9300}`); w.Code != http.StatusForbidden {
|
||||
t.Fatalf("cross-guest reboot: got %d want 403", w.Code)
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if ga.rebootCount() != 0 {
|
||||
t.Fatal("reboot triggered for a cross-guest request")
|
||||
}
|
||||
}
|
||||
|
||||
// ---- auth / config ----------------------------------------------------------------------
|
||||
|
||||
func TestDisks_CrossGuest403(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user