mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-26 09:54:47 +00:00
Stage 0 (bootstrap closure): PASS on m01/M02
- create RF=2 sync_all → 10s shipper wait → 4k fsync → publish_healthy
- Proves: BarrierAccepted observation, ShipperConnected, DurableLSN > 0
Stage 1 (sustained workload): 32/33 actions PASS
- bootstrap → fio 10s randwrite → dd_write 1M×2 fsync → data checksum
- Remaining: auto-failover promotion (separate issue)
Key fixes:
- BarrierAccepted callback: SyncCache success → core DurableLSN update
- BarrierRejected callback: barrier failures surface to core with reason
- Shipper state callback for new volumes (not just startup volumes)
- CatchUpTo ctrl conn reset: prevents stale control channel after recovery
- CP13-6 max-bytes budget suspended: uses replicaFlushedLSN which can't
advance without barrier; kills healthy shippers during async writes.
Will be replaced by v2 negotiated sync/recovery protocol.
- Barrier diagnostic logging: start/fail/success with reason and LSN
- Scenario restructured: Stage 0 (bootstrap-closure) + Stage 1 (failover)
- dd_write: sync_mode param + real stderr capture
- sw-test-runner suite command: deploy once, run N scenarios
- WAL size plumbing: proto + API + handler (forward-compatible)
Known: 6 blockvol/server test failures from Barrier() path change
(bounded catch-up in Barrier). Need test updates to match new semantics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
package testrunner
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// SuiteConfig is the top-level YAML structure for a test suite.
|
|
// A suite deploys once, runs N scenarios, and collects evidence.
|
|
//
|
|
// Usage: sw-test-runner suite <suite.yaml>
|
|
//
|
|
// Example:
|
|
//
|
|
// name: phase20-t6-stage1
|
|
// topology:
|
|
// nodes:
|
|
// m01: {host: 192.168.1.181, user: testdev, key: /opt/work/testdev_key}
|
|
// m02: {host: 192.168.1.184, user: testdev, key: /opt/work/testdev_key}
|
|
//
|
|
// deploy:
|
|
// build:
|
|
// goos: linux
|
|
// goarch: amd64
|
|
// targets: [weed, sw-test-runner]
|
|
// kill_ports: [9433, 18480, 3295]
|
|
// clean_dirs: ["/tmp/sw-fo-*"]
|
|
// binaries:
|
|
// - local: weed-linux
|
|
// remote: /tmp/sw-test-runner/weed
|
|
// - local: sw-test-runner-linux
|
|
// remote: /opt/work/sw-test-runner
|
|
//
|
|
// scenarios:
|
|
// - path: scenarios/internal/recovery-baseline-failover.yaml
|
|
// id: P20-T6-H1A
|
|
// - path: scenarios/internal/suite-ha-failover.yaml
|
|
// id: P20-T6-H1B
|
|
//
|
|
// evidence:
|
|
// glog_patterns: ["/tmp/weed.*.INFO.*"]
|
|
// debug_endpoints:
|
|
// - "http://10.0.0.1:18480/debug/block/shipper"
|
|
// - "http://10.0.0.3:18480/debug/block/shipper"
|
|
// save_to: results/phase20-t6
|
|
type SuiteConfig struct {
|
|
Name string `yaml:"name"`
|
|
Topology Topology `yaml:"topology"`
|
|
Deploy SuiteDeploy `yaml:"deploy"`
|
|
Scenarios []SuiteScenario `yaml:"scenarios"`
|
|
Evidence SuiteEvidence `yaml:"evidence"`
|
|
Env map[string]string `yaml:"env"`
|
|
}
|
|
|
|
// SuiteDeploy configures the one-time deploy stage.
|
|
type SuiteDeploy struct {
|
|
Build *SuiteBuild `yaml:"build,omitempty"`
|
|
KillPorts []int `yaml:"kill_ports"`
|
|
CleanDirs []string `yaml:"clean_dirs"`
|
|
Binaries []SuiteBinary `yaml:"binaries"`
|
|
Nodes []string `yaml:"nodes"` // which nodes to deploy to (default: all)
|
|
}
|
|
|
|
// SuiteBuild configures cross-compilation.
|
|
type SuiteBuild struct {
|
|
GOOS string `yaml:"goos"`
|
|
GOARCH string `yaml:"goarch"`
|
|
Targets []string `yaml:"targets"` // ["weed", "sw-test-runner"]
|
|
RepoDir string `yaml:"repo_dir,omitempty"`
|
|
}
|
|
|
|
// SuiteBinary maps a local built binary to a remote path.
|
|
type SuiteBinary struct {
|
|
Local string `yaml:"local"`
|
|
Remote string `yaml:"remote"`
|
|
}
|
|
|
|
// SuiteScenario references a scenario YAML file to run.
|
|
type SuiteScenario struct {
|
|
Path string `yaml:"path"`
|
|
ID string `yaml:"id"`
|
|
}
|
|
|
|
// SuiteEvidence configures post-run evidence collection.
|
|
type SuiteEvidence struct {
|
|
GlogPatterns []string `yaml:"glog_patterns"`
|
|
DebugEndpoints []string `yaml:"debug_endpoints"`
|
|
SaveTo string `yaml:"save_to"`
|
|
RunNode string `yaml:"run_node"` // node where scenarios run (for result pull)
|
|
}
|
|
|
|
// ParseSuiteFile reads and parses a suite YAML file.
|
|
func ParseSuiteFile(path string) (*SuiteConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read suite %s: %w", path, err)
|
|
}
|
|
return ParseSuite(data)
|
|
}
|
|
|
|
// ParseSuite parses YAML bytes into a SuiteConfig.
|
|
func ParseSuite(data []byte) (*SuiteConfig, error) {
|
|
var suite SuiteConfig
|
|
if err := yaml.Unmarshal(data, &suite); err != nil {
|
|
return nil, fmt.Errorf("parse suite: %w", err)
|
|
}
|
|
if suite.Name == "" {
|
|
return nil, fmt.Errorf("suite name is required")
|
|
}
|
|
if len(suite.Scenarios) == 0 {
|
|
return nil, fmt.Errorf("suite must have at least one scenario")
|
|
}
|
|
return &suite, nil
|
|
}
|
|
|
|
// ScenarioResult with suite context.
|
|
type SuiteScenarioResult struct {
|
|
ID string `json:"id"`
|
|
Path string `json:"path"`
|
|
Result *ScenarioResult `json:"result"`
|
|
}
|
|
|
|
// SuiteResult is the output of a suite run.
|
|
type SuiteResult struct {
|
|
Name string `json:"name"`
|
|
Status string `json:"status"` // PASS or FAIL
|
|
Scenarios []SuiteScenarioResult `json:"scenarios"`
|
|
Deploy *PhaseResult `json:"deploy,omitempty"`
|
|
Evidence *PhaseResult `json:"evidence,omitempty"`
|
|
}
|