mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-22 03:00:12 +00:00
test(agentcompat): add exact stress scenario
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
//go:build linux
|
||||
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/contract"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStressEvidence = errors.New("stress evidence is invalid")
|
||||
ErrStressFault = errors.New("stress worker fault evidence is invalid")
|
||||
)
|
||||
|
||||
type StressPreparedBinaries struct {
|
||||
DashboardBuildCount int `json:"dashboard_build_count"`
|
||||
DashboardPathReused bool `json:"dashboard_path_reused"`
|
||||
AgentBuildCount int `json:"agent_build_count"`
|
||||
AgentPathReused bool `json:"agent_path_reused"`
|
||||
}
|
||||
|
||||
type StressQuotaBoundary struct {
|
||||
Allowed int `json:"allowed"`
|
||||
Rejected int `json:"rejected"`
|
||||
AllowedAccepted bool `json:"allowed_accepted"`
|
||||
RejectedDenied bool `json:"rejected_denied"`
|
||||
}
|
||||
|
||||
type StressQuotaEvidence struct {
|
||||
PATSecond StressQuotaBoundary `json:"pat_second"`
|
||||
PATMinute StressQuotaBoundary `json:"pat_minute"`
|
||||
UserStreams StressQuotaBoundary `json:"user_streams"`
|
||||
ServerStreams StressQuotaBoundary `json:"server_streams"`
|
||||
PathLockStripes int `json:"path_lock_stripes"`
|
||||
}
|
||||
|
||||
type StressWarmupEvidence struct {
|
||||
Agent StressAgentOrdinal `json:"agent"`
|
||||
Exec bool `json:"exec"`
|
||||
Filesystem bool `json:"filesystem"`
|
||||
Terminal bool `json:"terminal"`
|
||||
NAT bool `json:"nat"`
|
||||
FM bool `json:"file_manager"`
|
||||
}
|
||||
|
||||
type StressSessionEvidence struct {
|
||||
ID StressSessionID `json:"id"`
|
||||
Kind StressSessionKind `json:"kind"`
|
||||
Succeeded bool `json:"succeeded"`
|
||||
}
|
||||
|
||||
type StressFaultTarget struct {
|
||||
Iteration int `json:"iteration"`
|
||||
Round int `json:"round"`
|
||||
Agent StressAgentOrdinal `json:"agent"`
|
||||
Kind StressOperationKind `json:"kind"`
|
||||
}
|
||||
|
||||
type StressCleanupSummary struct {
|
||||
Passed bool `json:"passed"`
|
||||
ReceiptCount int `json:"receipt_count"`
|
||||
FailedReceiptCount int `json:"failed_receipt_count"`
|
||||
ForcedCleanupCount int `json:"forced_cleanup_count"`
|
||||
ProcessResidue int `json:"process_residue"`
|
||||
ProcessGroupResidue int `json:"process_group_residue"`
|
||||
WorkspaceResidue int `json:"workspace_residue"`
|
||||
}
|
||||
|
||||
type StressIterationEvidence struct {
|
||||
Iteration int `json:"iteration"`
|
||||
Rounds []StressRoundEvidence `json:"rounds"`
|
||||
Resources []StressProcessWindows `json:"resources"`
|
||||
}
|
||||
|
||||
type StressEvidence struct {
|
||||
Version int `json:"version"`
|
||||
Profile contract.ProfileName `json:"profile"`
|
||||
Seed contract.Seed `json:"seed"`
|
||||
PreparedBinaries StressPreparedBinaries `json:"prepared_binaries"`
|
||||
Quotas StressQuotaEvidence `json:"quotas"`
|
||||
Warmups []StressWarmupEvidence `json:"warmups"`
|
||||
Sessions []StressSessionEvidence `json:"sessions"`
|
||||
Plan StressPlan `json:"plan"`
|
||||
Iterations []StressIterationEvidence `json:"iterations"`
|
||||
FaultTarget *StressFaultTarget `json:"fault_target,omitempty"`
|
||||
SoakTrend StressSoakTrendEvidence `json:"soak_trend,omitempty"`
|
||||
Cleanup StressCleanupSummary `json:"cleanup"`
|
||||
}
|
||||
|
||||
func StressWorkerFaultTarget() StressFaultTarget {
|
||||
agent, err := NewStressAgentOrdinal(4)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return StressFaultTarget{Iteration: 1, Round: 2, Agent: agent, Kind: StressOperationExec}
|
||||
}
|
||||
|
||||
func (e StressEvidence) ValidateSuccess(profile contract.Profile) error {
|
||||
return e.validate(profile, false)
|
||||
}
|
||||
|
||||
func (e StressEvidence) ValidateStressWorker(profile contract.Profile) error {
|
||||
return e.validate(profile, true)
|
||||
}
|
||||
|
||||
func (e StressEvidence) validate(profile contract.Profile, faultAware bool) error {
|
||||
if e.Version != 1 || e.Profile != profile.Name() || e.Seed != contract.DefaultSeed {
|
||||
return ErrStressEvidence
|
||||
}
|
||||
canonical, err := GenerateStressPlan(profile, e.Seed)
|
||||
if err != nil || e.Plan.Profile != e.Profile || e.Plan.Seed != e.Seed || !reflect.DeepEqual(e.Plan, canonical) {
|
||||
return fmt.Errorf("plan does not match canonical plan: %w", ErrStressEvidence)
|
||||
}
|
||||
if err := validateStressPreparedBinaries(e.PreparedBinaries); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.Quotas.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateStressWarmups(e.Warmups, profile.AgentCount()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateStressSessions(e.Sessions, canonical.Sessions, profile.ConcurrentSessions()); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(e.Iterations) != profile.Iterations() {
|
||||
return fmt.Errorf("iterations=%d want=%d: %w", len(e.Iterations), profile.Iterations(), ErrStressEvidence)
|
||||
}
|
||||
failed := 0
|
||||
for index, iteration := range e.Iterations {
|
||||
count, err := validateStressIteration(canonical, iteration, index+1, faultAware)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
failed += count
|
||||
}
|
||||
if err := validateStressFault(e.FaultTarget, failed, faultAware); err != nil {
|
||||
return err
|
||||
}
|
||||
if profile.Iterations() == 3 {
|
||||
if err := ValidateStressSoakTrendForProfile(profile, e.SoakTrend); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !e.Cleanup.Passed || e.Cleanup.ReceiptCount != 9 || e.Cleanup.FailedReceiptCount != 0 || e.Cleanup.ForcedCleanupCount != 0 || e.Cleanup.ProcessResidue != 0 || e.Cleanup.ProcessGroupResidue != 0 || e.Cleanup.WorkspaceResidue != 0 {
|
||||
return fmt.Errorf("cleanup=%+v: %w", e.Cleanup, ErrStressEvidence)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q StressQuotaEvidence) Validate() error {
|
||||
wants := []struct {
|
||||
got StressQuotaBoundary
|
||||
allow int
|
||||
reject int
|
||||
}{{q.PATSecond, 10, 11}, {q.PATMinute, 120, 121}, {q.UserStreams, 20, 21}, {q.ServerStreams, 40, 41}}
|
||||
for _, want := range wants {
|
||||
if want.got.Allowed != want.allow || want.got.Rejected != want.reject || !want.got.AllowedAccepted || !want.got.RejectedDenied {
|
||||
return fmt.Errorf("quota=%+v want=%d/%d: %w", want.got, want.allow, want.reject, ErrStressEvidence)
|
||||
}
|
||||
}
|
||||
if q.PathLockStripes != 1024 {
|
||||
return fmt.Errorf("path lock stripes=%d: %w", q.PathLockStripes, ErrStressEvidence)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user