mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-01 13:47:51 +00:00
Add HTTP admin server to iscsi-target binary (POST /assign, GET /status, POST /replica, POST /rebuild) and 7 HA integration tests validating failover, split-brain prevention, epoch fencing, and demote-under-IO. New files: - admin.go: HTTP admin endpoint with input validation - ha_target.go: HATarget helper wrapping Target + admin HTTP calls - ha_test.go: 7 HA tests (all PASS on WSL2, 67.7s total) Bug fixes: - BUG-CP4B4-1: CmdSN init (expCmdSN=0 not 1, first SCSI cmd was dropped) - BUG-CP4B4-2: RoleNone->RoleReplica missing SetEpoch (WAL rejected) - BUG-CP4B4-3: replica applyEntry didn't update vol.nextLSN (status=0) - BUG-CP4B4-4: PID discovery killed primary instead of replica (shared binPath; fixed by grepping volFile) - BUG-CP4B4-5: artifact collector overwrote primary log with replica log (added CollectLabeled method) Also: 3s write deadline on WAL shipper data connection to avoid 120s TCP retransmission timeout when replica is dead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
//go:build integration
|
|
|
|
package test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// LogCollector is implemented by any target that can provide a log file.
|
|
type LogCollector interface {
|
|
CollectLog() (string, error)
|
|
}
|
|
|
|
// ArtifactCollector gathers diagnostic info on test failure.
|
|
type ArtifactCollector struct {
|
|
dir string // base artifacts directory
|
|
node *Node // initiator node for dmesg/lsblk
|
|
}
|
|
|
|
// NewArtifactCollector creates a collector rooted at the given directory.
|
|
func NewArtifactCollector(dir string, node *Node) *ArtifactCollector {
|
|
return &ArtifactCollector{
|
|
dir: dir,
|
|
node: node,
|
|
}
|
|
}
|
|
|
|
// Collect gathers diagnostics for a failed test. Call from t.Cleanup().
|
|
// Pass any LogCollector (Target or WeedTarget) for the correct log file.
|
|
func (a *ArtifactCollector) Collect(t *testing.T, tgt LogCollector) {
|
|
a.CollectLabeled(t, tgt, "target")
|
|
}
|
|
|
|
// CollectLabeled is like Collect but uses the given label for the log filename
|
|
// (e.g. "primary" -> "primary.log"). Use this when collecting logs from
|
|
// multiple targets in the same test to avoid overwriting.
|
|
func (a *ArtifactCollector) CollectLabeled(t *testing.T, tgt LogCollector, label string) {
|
|
if !t.Failed() {
|
|
return
|
|
}
|
|
|
|
ts := time.Now().Format("20060102-150405")
|
|
testDir := filepath.Join(a.dir, ts, t.Name())
|
|
if err := os.MkdirAll(testDir, 0755); err != nil {
|
|
t.Logf("artifacts: mkdir failed: %v", err)
|
|
return
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
// Target log (from the test's own target instance)
|
|
if tgt != nil {
|
|
if log, err := tgt.CollectLog(); err == nil && log != "" {
|
|
writeArtifact(t, filepath.Join(testDir, label+".log"), log)
|
|
}
|
|
}
|
|
|
|
// iSCSI session state
|
|
if stdout, _, _, err := a.node.RunRoot(ctx, "iscsiadm -m session 2>&1"); err == nil {
|
|
writeArtifact(t, filepath.Join(testDir, "iscsi-session.txt"), stdout)
|
|
}
|
|
|
|
// Kernel messages (iSCSI errors)
|
|
if stdout, _, _, err := a.node.RunRoot(ctx, "dmesg | tail -200"); err == nil {
|
|
writeArtifact(t, filepath.Join(testDir, "dmesg.txt"), stdout)
|
|
}
|
|
|
|
// Block devices
|
|
if stdout, _, _, err := a.node.Run(ctx, "lsblk 2>&1"); err == nil {
|
|
writeArtifact(t, filepath.Join(testDir, "lsblk.txt"), stdout)
|
|
}
|
|
|
|
t.Logf("artifacts saved to %s", testDir)
|
|
}
|
|
|
|
func writeArtifact(t *testing.T, path, content string) {
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Logf("artifacts: write %s: %v", path, err)
|
|
} else {
|
|
t.Logf("artifacts: wrote %s (%d bytes)", filepath.Base(path), len(content))
|
|
}
|
|
}
|
|
|
|
// CollectPerf saves performance results to a timestamped JSON file.
|
|
func (a *ArtifactCollector) CollectPerf(t *testing.T, name string, data string) {
|
|
ts := time.Now().Format("20060102-150405")
|
|
path := filepath.Join(a.dir, fmt.Sprintf("perf-%s-%s.json", name, ts))
|
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
t.Logf("artifacts: mkdir failed: %v", err)
|
|
return
|
|
}
|
|
writeArtifact(t, path, data)
|
|
}
|