Files
seaweedfs/weed/storage/blockvol/testrunner/actions/devops_test.go
T
pingqiuandClaude Opus 4.6 785a7d7efd feat: wire real pinner into flusher retention + real WAL scan executor (Phase 07 P1)
Pinner wired to real retention:
- NewPinner calls vol.SetV2RetentionFloor(p.MinWALRetentionFloor)
- Flusher.RetentionFloorFn() / SetRetentionFloorFn() exposed
- SetV2RetentionFloor chains with existing shipper retention floor
- Holds actually prevent WAL reclaim (not just tracked state)

Executor uses real WAL scan:
- BlockVol.ScanWALEntries(fromLSN, callback) wraps wal.ScanFrom
  with real fd, walOffset, checkpointLSN
- Executor.StreamWALEntries uses ScanWALEntries (not stub)
- Reads real WAL entries, tracks highest LSN scanned

CommittedLSN mapping:
- Explicitly documented as interim V1 model (committed = checkpointed)
- Will diverge when V2 distributed commit separates from local flush

Carry-forward:
- TransferSnapshot/TransferFullBase/TruncateWAL: stubs (need extent I/O)
- Control intent from confirmed failover: deferred

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:01:46 -07:00

177 lines
4.3 KiB
Go

package actions
import (
"sort"
"testing"
tr "github.com/seaweedfs/seaweedfs/weed/storage/blockvol/testrunner"
)
func TestDevOpsActions_Registration(t *testing.T) {
registry := tr.NewRegistry()
RegisterDevOpsActions(registry)
expected := []string{
"build_deploy_weed",
"start_weed_master",
"start_weed_volume",
"stop_weed",
"wait_cluster_ready",
"create_block_volume",
"expand_block_volume",
"lookup_block_volume",
"delete_block_volume",
"wait_block_servers",
"cluster_status",
"wait_block_primary",
"assert_block_field",
"block_status",
"block_promote",
}
for _, name := range expected {
if _, err := registry.Get(name); err != nil {
t.Errorf("action %q not registered: %v", name, err)
}
}
}
func TestDevOpsActions_Tier(t *testing.T) {
registry := tr.NewRegistry()
RegisterDevOpsActions(registry)
byTier := registry.ListByTier()
devopsActions := byTier[tr.TierDevOps]
if len(devopsActions) != 16 {
t.Errorf("devops tier has %d actions, want 16", len(devopsActions))
}
// Verify all are in devops tier.
sort.Strings(devopsActions)
for _, name := range devopsActions {
if tier := registry.ActionTier(name); tier != tr.TierDevOps {
t.Errorf("action %q has tier %q, want devops", name, tier)
}
}
}
func TestDevOpsActions_TierGating(t *testing.T) {
registry := tr.NewRegistry()
RegisterDevOpsActions(registry)
// Without gating, all should be accessible.
if _, err := registry.Get("start_weed_master"); err != nil {
t.Errorf("ungated: %v", err)
}
// Enable only core tier — devops should be blocked.
registry.EnableTiers([]string{tr.TierCore})
if _, err := registry.Get("start_weed_master"); err == nil {
t.Error("expected error when devops tier is disabled")
}
// Enable devops tier — should work again.
registry.EnableTiers([]string{tr.TierDevOps})
if _, err := registry.Get("start_weed_master"); err != nil {
t.Errorf("devops enabled: %v", err)
}
}
func TestAllActions_Registration(t *testing.T) {
registry := tr.NewRegistry()
RegisterCore(registry)
RegisterBlockActions(registry)
RegisterISCSIActions(registry)
RegisterNVMeActions(registry)
RegisterIOActions(registry)
RegisterDevOpsActions(registry)
RegisterSnapshotActions(registry)
RegisterDatabaseActions(registry)
RegisterMetricsActions(registry)
RegisterK8sActions(registry)
byTier := registry.ListByTier()
// Verify tier counts.
if n := len(byTier[tr.TierCore]); n != 17 {
t.Errorf("core: %d, want 17", n)
}
if n := len(byTier[tr.TierBlock]); n != 62 {
t.Errorf("block: %d, want 62", n)
}
if n := len(byTier[tr.TierDevOps]); n != 16 {
t.Errorf("devops: %d, want 16", n)
}
if n := len(byTier[tr.TierChaos]); n != 5 {
t.Errorf("chaos: %d, want 5", n)
}
if n := len(byTier[TierK8s]); n != 14 {
t.Errorf("k8s: %d, want 14", n)
}
// Total should be 114 (112 prev + 2 recovery: measure_recovery, validate_recovery_regression).
total := 0
for _, actions := range byTier {
total += len(actions)
}
if total != 114 {
t.Errorf("total actions: %d, want 114", total)
}
}
func TestK8sActions_Registration(t *testing.T) {
registry := tr.NewRegistry()
RegisterK8sActions(registry)
expected := []string{
"kubectl_apply",
"kubectl_delete",
"kubectl_get_field",
"kubectl_wait_condition",
"kubectl_set_image",
"kubectl_assert_exists",
"kubectl_assert_not_exists",
"kubectl_logs",
"kubectl_rollout_status",
"kubectl_exec",
"kubectl_delete_pod",
"kubectl_pod_ready_count",
"kubectl_label",
"kubectl_get_condition",
}
for _, name := range expected {
if _, err := registry.Get(name); err != nil {
t.Errorf("action %q not registered: %v", name, err)
}
}
byTier := registry.ListByTier()
if n := len(byTier[TierK8s]); n != 14 {
t.Errorf("k8s tier has %d actions, want 14", n)
}
}
func TestK8sActions_TierGating(t *testing.T) {
registry := tr.NewRegistry()
RegisterK8sActions(registry)
// Without gating, all should be accessible.
if _, err := registry.Get("kubectl_apply"); err != nil {
t.Errorf("ungated: %v", err)
}
// Enable only core tier — k8s should be blocked.
registry.EnableTiers([]string{tr.TierCore})
if _, err := registry.Get("kubectl_apply"); err == nil {
t.Error("expected error when k8s tier is disabled")
}
// Enable k8s tier — should work again.
registry.EnableTiers([]string{TierK8s})
if _, err := registry.Get("kubectl_apply"); err != nil {
t.Errorf("k8s enabled: %v", err)
}
}