Files
2026-07-20 04:54:03 +00:00

155 lines
5.3 KiB
Go

//go:build linux
package scenario
import "fmt"
func validateStressPreparedBinaries(evidence StressPreparedBinaries) error {
if evidence.DashboardBuildCount != 1 || !evidence.DashboardPathReused || evidence.AgentBuildCount != 1 || !evidence.AgentPathReused {
return fmt.Errorf("prepared binaries=%+v: %w", evidence, ErrStressEvidence)
}
return nil
}
func validateStressWarmups(warmups []StressWarmupEvidence, agentCount int) error {
if len(warmups) != agentCount {
return fmt.Errorf("warmups=%d want=%d: %w", len(warmups), agentCount, ErrStressEvidence)
}
seen := make(map[int]struct{}, len(warmups))
for _, warmup := range warmups {
if _, duplicate := seen[warmup.Agent.Int()]; duplicate || !warmup.Exec || !warmup.Filesystem || !warmup.Terminal || !warmup.NAT || !warmup.FM {
return fmt.Errorf("warmup=%+v: %w", warmup, ErrStressEvidence)
}
seen[warmup.Agent.Int()] = struct{}{}
}
return nil
}
func validateStressSessions(sessions []StressSessionEvidence, plan []StressSessionPlan, countPerKind int) error {
if len(sessions) != countPerKind*3 {
return fmt.Errorf("sessions=%d want=%d: %w", len(sessions), countPerKind*3, ErrStressEvidence)
}
if len(plan) != len(sessions) {
return fmt.Errorf("session plan=%d evidence=%d: %w", len(plan), len(sessions), ErrStressEvidence)
}
expected := make(map[StressSessionID]StressSessionPlan, len(plan))
for _, session := range plan {
expected[session.ID] = session
}
counts := make(map[StressSessionKind]int, 3)
seen := make(map[StressSessionID]struct{}, len(sessions))
for _, session := range sessions {
planned, exists := expected[session.ID]
_, duplicate := seen[session.ID]
if !exists || planned.Kind != session.Kind || duplicate || !session.Succeeded {
return fmt.Errorf("session=%+v: %w", session, ErrStressEvidence)
}
seen[session.ID] = struct{}{}
counts[session.Kind]++
}
for _, kind := range []StressSessionKind{StressSessionTerminal, StressSessionNAT, StressSessionFM} {
if counts[kind] != countPerKind {
return fmt.Errorf("session kind=%s count=%d want=%d: %w", kind, counts[kind], countPerKind, ErrStressEvidence)
}
}
return nil
}
func validateStressIteration(plan StressPlan, evidence StressIterationEvidence, iteration int, faultAware bool) (int, error) {
if evidence.Iteration != iteration || len(evidence.Rounds) != len(plan.Rounds) || len(evidence.Resources) != 1+canonicalAgentCount(plan) {
return 0, fmt.Errorf("iteration=%+v: %w", evidence, ErrStressEvidence)
}
failed := 0
for index, round := range evidence.Rounds {
matched, err := matchStressRoundEvidence(plan.Rounds[index], round)
if err != nil {
return 0, err
}
for _, operation := range matched {
if !operation.Succeeded || operation.Error != "" {
failed++
if !faultAware || !isStressFaultOperation(plan.Rounds[index], operation, iteration) {
return 0, fmt.Errorf("unexpected failed operation=%s: %w", operation.ID.String(), ErrStressFault)
}
}
}
}
if err := validateStressResourceIdentities(plan, evidence.Resources); err != nil {
return 0, err
}
for _, windows := range evidence.Resources {
if _, err := EvaluateStressResource(windows); err != nil {
return 0, err
}
}
return failed, nil
}
func validateStressFault(target *StressFaultTarget, failed int, faultAware bool) error {
if !faultAware {
if target != nil || failed != 0 {
return ErrStressFault
}
return nil
}
want := StressWorkerFaultTarget()
if target == nil || *target != want || failed != 1 {
return fmt.Errorf("target=%+v failed=%d want=%+v/1: %w", target, failed, want, ErrStressFault)
}
return nil
}
func isStressFaultOperation(plan StressRoundPlan, evidence StressOperationEvidence, iteration int) bool {
target := StressWorkerFaultTarget()
if iteration != target.Iteration || plan.Round != target.Round {
return false
}
for _, operation := range plan.Operations {
if operation.ID == evidence.ID {
return operation.Agent == target.Agent && operation.Kind == target.Kind
}
}
return false
}
func planAgentCount(plan StressPlan) int {
return canonicalAgentCount(plan)
}
func canonicalAgentCount(plan StressPlan) int {
if len(plan.Rounds) == 0 {
return 0
}
return len(plan.Rounds[0].Operations) / 2
}
func validateStressResourceIdentities(plan StressPlan, resources []StressProcessWindows) error {
wantAgents := planAgentCount(plan)
if len(resources) != wantAgents+1 {
return fmt.Errorf("resources=%d want=%d: %w", len(resources), wantAgents+1, ErrStressEvidence)
}
dashboardCount := 0
seenAgents := make(map[int]struct{}, wantAgents)
for _, resource := range resources {
switch resource.Process.Kind {
case StressProcessDashboard:
dashboardCount++
case StressProcessAgent:
ordinal := resource.Process.Agent.Int()
if ordinal < 1 || ordinal > wantAgents {
return fmt.Errorf("unknown agent ordinal=%d: %w", ordinal, ErrStressEvidence)
}
if _, duplicate := seenAgents[ordinal]; duplicate {
return fmt.Errorf("duplicate agent ordinal=%d: %w", ordinal, ErrStressEvidence)
}
seenAgents[ordinal] = struct{}{}
default:
return fmt.Errorf("unknown process kind=%q: %w", resource.Process.Kind, ErrStressEvidence)
}
}
if dashboardCount != 1 || len(seenAgents) != wantAgents {
return fmt.Errorf("dashboard=%d agents=%d want=1/%d: %w", dashboardCount, len(seenAgents), wantAgents, ErrStressEvidence)
}
return nil
}