60 lines
2.6 KiB
Go
60 lines
2.6 KiB
Go
package localapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/proxmox"
|
|
)
|
|
|
|
// R-1010: a half-removed vzdump snapshot. The error text is the one demo-hp's task printed on 2026-10-14
|
|
// (`audits/power-cut-fixes-2026-10-14/C4/hand-fix.txt`).
|
|
const r1010MissingLV = `lvremove snapshot 'pve/snap_vm-9201-disk-0_vzdump' error: Failed to find logical volume "pve/snap_vm-9201-disk-0_vzdump"`
|
|
|
|
func r1010TaskErr(exit string) error {
|
|
return &proxmox.TaskError{UPID: "UPID:demo-hp:0001:0002:0003:vzdelsnapshot:9201:root@pam:", ExitStatus: exit,
|
|
LogTail: []string{exit}}
|
|
}
|
|
|
|
// COMPANION RED-PROOF (observed 2026-10-19): with DeleteVzdumpSnapshot back to the single plain delete, the first case
|
|
// fails with
|
|
//
|
|
// stalelock_r1010_test.go: missing volume: calls [plain], want [plain force]; err = proxmox: task … failed: exitstatus "lvremove snapshot …"
|
|
//
|
|
// Restored.
|
|
func TestStaleLock_R1010_ForcesOnceOnlyWhenTheVolumeIsGone(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
errs []error
|
|
wantCalls []string
|
|
wantErr string
|
|
}{
|
|
{"missing volume", []error{r1010TaskErr(r1010MissingLV)}, []string{"plain", "force"}, ""},
|
|
{"missing volume, named only in the log tail", []error{&proxmox.TaskError{UPID: "x", ExitStatus: "command 'lvremove' failed: exit code 5",
|
|
LogTail: []string{r1010MissingLV}}}, []string{"plain", "force"}, ""},
|
|
{"the plain delete works", nil, []string{"plain"}, ""},
|
|
{"another error: no force", []error{r1010TaskErr("lvremove snapshot 'pve/snap_vm-9201-disk-0_vzdump' error: Failed to update pool pve/data.")},
|
|
[]string{"plain"}, "Failed to update pool"},
|
|
{"a missing volume of ANOTHER snapshot: no force", []error{r1010TaskErr(`Failed to find logical volume "pve/snap_vm-9201-disk-0_mine"`)},
|
|
[]string{"plain"}, "_mine"},
|
|
{"the forced delete fails too: returned, never a second force", []error{r1010TaskErr(r1010MissingLV), errors.New("still broken")},
|
|
[]string{"plain", "force"}, "still broken"},
|
|
}
|
|
for _, c := range cases {
|
|
api := &fakeStaleLockAPI{delErrs: c.errs}
|
|
ctrl := NewStaleLockController(api, &recordingRunner{}, "felhom", nil)
|
|
err := ctrl.DeleteVzdumpSnapshot(context.Background(), 9201)
|
|
if strings.Join(api.delCalls, " ") != strings.Join(c.wantCalls, " ") {
|
|
t.Errorf("%s: calls %v, want %v; err = %v", c.name, api.delCalls, c.wantCalls, err)
|
|
}
|
|
switch {
|
|
case c.wantErr == "" && err != nil:
|
|
t.Errorf("%s: err = %v, want nil", c.name, err)
|
|
case c.wantErr != "" && (err == nil || !strings.Contains(err.Error(), c.wantErr)):
|
|
t.Errorf("%s: err = %v, want one naming %q", c.name, err, c.wantErr)
|
|
}
|
|
}
|
|
}
|