Revision: Rename to 'maglevd'; Refactor config structure
This commit is contained in:
@@ -10,23 +10,32 @@ import (
|
||||
"git.ipng.ch/ipng/vpp-maglev/internal/health"
|
||||
)
|
||||
|
||||
func makeTestConfig(interval time.Duration, fall, rise int) *config.Frontend {
|
||||
return &config.Frontend{
|
||||
HealthCheckNetns: "test",
|
||||
HealthChecker: config.HealthCheckerConfig{TransitionHistory: 5},
|
||||
VIPs: map[string]config.VIP{
|
||||
func makeTestConfig(interval time.Duration, fall, rise int) *config.Config {
|
||||
return &config.Config{
|
||||
HealthChecker: config.HealthCheckerConfig{TransitionHistory: 5},
|
||||
HealthChecks: map[string]config.HealthCheck{
|
||||
"icmp": {
|
||||
Type: "icmp",
|
||||
Interval: interval,
|
||||
Timeout: time.Second,
|
||||
Fall: fall,
|
||||
Rise: rise,
|
||||
},
|
||||
},
|
||||
Backends: map[string]config.Backend{
|
||||
"be0": {
|
||||
Address: net.ParseIP("10.0.0.2"),
|
||||
HealthCheck: "icmp",
|
||||
Enabled: true,
|
||||
Weight: 100,
|
||||
},
|
||||
},
|
||||
Frontends: map[string]config.Frontend{
|
||||
"web": {
|
||||
Address: net.ParseIP("192.0.2.1"),
|
||||
Protocol: "tcp",
|
||||
Port: 80,
|
||||
Backends: []net.IP{net.ParseIP("10.0.0.2")},
|
||||
HealthCheck: config.HealthCheck{
|
||||
Type: "icmp",
|
||||
Interval: interval,
|
||||
Timeout: time.Second,
|
||||
Fall: fall,
|
||||
Rise: rise,
|
||||
},
|
||||
Backends: []string{"be0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -62,20 +71,16 @@ func TestHealthCheckEqual(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStateMachineViaBackend(t *testing.T) {
|
||||
// Directly test Backend state transitions (rise=2, fall=3) without goroutines.
|
||||
b := health.New("web", net.ParseIP("10.0.0.2"), 2, 3)
|
||||
b := health.New("be0", net.ParseIP("10.0.0.2"), 2, 3)
|
||||
pass := health.ProbeResult{OK: true, Layer: health.LayerL7, Code: "L7OK"}
|
||||
fail := health.ProbeResult{OK: false, Layer: health.LayerL4, Code: "L4CON"}
|
||||
|
||||
// Unknown → Down on first fail.
|
||||
if !b.Record(fail, 5) {
|
||||
t.Error("first fail from Unknown should transition to Down")
|
||||
}
|
||||
if b.State != health.StateDown {
|
||||
t.Errorf("expected down, got %s", b.State)
|
||||
}
|
||||
|
||||
// rise=2 passes → Up.
|
||||
if b.Record(pass, 5) {
|
||||
t.Error("should not transition after 1 pass (rise=2)")
|
||||
}
|
||||
@@ -105,21 +110,19 @@ func TestReloadAddsBackend(t *testing.T) {
|
||||
c := New(cfg)
|
||||
|
||||
newCfg := makeTestConfig(10*time.Millisecond, 3, 2)
|
||||
newCfg.VIPs["web2"] = config.VIP{
|
||||
newCfg.Backends["be1"] = config.Backend{
|
||||
Address: net.ParseIP("10.0.0.3"),
|
||||
HealthCheck: "icmp",
|
||||
Enabled: true,
|
||||
Weight: 100,
|
||||
}
|
||||
newCfg.Frontends["web2"] = config.Frontend{
|
||||
Address: net.ParseIP("192.0.2.2"),
|
||||
Protocol: "tcp",
|
||||
Port: 443,
|
||||
Backends: []net.IP{net.ParseIP("10.0.0.3")},
|
||||
HealthCheck: config.HealthCheck{
|
||||
Type: "icmp",
|
||||
Interval: 10 * time.Millisecond,
|
||||
Timeout: time.Second,
|
||||
Fall: 3,
|
||||
Rise: 2,
|
||||
},
|
||||
Backends: []string{"be1"},
|
||||
}
|
||||
|
||||
// Cancelled context: no probe goroutines actually run.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
@@ -128,7 +131,7 @@ func TestReloadAddsBackend(t *testing.T) {
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
_, ok := c.workers[backendKey{VIPName: "web2", Backend: "10.0.0.3"}]
|
||||
_, ok := c.workers["be1"]
|
||||
c.mu.RUnlock()
|
||||
if !ok {
|
||||
t.Error("new backend not added after Reload")
|
||||
@@ -144,22 +147,22 @@ func TestReloadRemovesBackend(t *testing.T) {
|
||||
|
||||
// Seed a worker manually.
|
||||
c.mu.Lock()
|
||||
key := backendKey{VIPName: "web", Backend: "10.0.0.2"}
|
||||
wCtx, wCancel := context.WithCancel(context.Background())
|
||||
c.workers[key] = &worker{
|
||||
backend: health.New("web", net.ParseIP("10.0.0.2"), 2, 3),
|
||||
hc: cfg.VIPs["web"].HealthCheck,
|
||||
vip: cfg.VIPs["web"],
|
||||
c.workers["be0"] = &worker{
|
||||
backend: health.New("be0", net.ParseIP("10.0.0.2"), 2, 3),
|
||||
hc: cfg.HealthChecks["icmp"],
|
||||
entry: cfg.Backends["be0"],
|
||||
cancel: wCancel,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
_ = wCtx
|
||||
|
||||
// New config with "web" VIP removed.
|
||||
newCfg := &config.Frontend{
|
||||
HealthCheckNetns: cfg.HealthCheckNetns,
|
||||
HealthChecker: cfg.HealthChecker,
|
||||
VIPs: map[string]config.VIP{},
|
||||
// Remove all frontends → be0 is no longer active.
|
||||
newCfg := &config.Config{
|
||||
HealthChecker: cfg.HealthChecker,
|
||||
HealthChecks: cfg.HealthChecks,
|
||||
Backends: cfg.Backends,
|
||||
Frontends: map[string]config.Frontend{},
|
||||
}
|
||||
|
||||
if err := c.Reload(ctx, newCfg); err != nil {
|
||||
@@ -167,13 +170,30 @@ func TestReloadRemovesBackend(t *testing.T) {
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
_, ok := c.workers[key]
|
||||
_, ok := c.workers["be0"]
|
||||
c.mu.RUnlock()
|
||||
if ok {
|
||||
t.Error("removed backend still present after Reload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedBackendProbedOnce(t *testing.T) {
|
||||
// be0 is referenced by two frontends — only one worker should exist.
|
||||
cfg := makeTestConfig(10*time.Millisecond, 3, 2)
|
||||
cfg.Frontends["web-tls"] = config.Frontend{
|
||||
Address: net.ParseIP("192.0.2.3"),
|
||||
Protocol: "tcp",
|
||||
Port: 443,
|
||||
Backends: []string{"be0"},
|
||||
}
|
||||
|
||||
c := New(cfg)
|
||||
names := activeBackendNames(c.cfg)
|
||||
if len(names) != 1 || names[0] != "be0" {
|
||||
t.Errorf("expected exactly one active backend, got %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubscribe(t *testing.T) {
|
||||
cfg := makeTestConfig(10*time.Millisecond, 1, 1)
|
||||
c := New(cfg)
|
||||
@@ -186,8 +206,9 @@ func TestSubscribe(t *testing.T) {
|
||||
defer unsub()
|
||||
|
||||
e := Event{
|
||||
VIPName: "web",
|
||||
Backend: net.ParseIP("10.0.0.2"),
|
||||
FrontendName: "web",
|
||||
BackendName: "be0",
|
||||
Backend: net.ParseIP("10.0.0.2"),
|
||||
Transition: health.Transition{
|
||||
From: health.StateUnknown,
|
||||
To: health.StateUp,
|
||||
@@ -199,8 +220,11 @@ func TestSubscribe(t *testing.T) {
|
||||
|
||||
select {
|
||||
case got := <-ch:
|
||||
if got.VIPName != "web" {
|
||||
t.Errorf("event VIPName: got %q, want %q", got.VIPName, "web")
|
||||
if got.FrontendName != "web" {
|
||||
t.Errorf("event FrontendName: got %q, want web", got.FrontendName)
|
||||
}
|
||||
if got.BackendName != "be0" {
|
||||
t.Errorf("event BackendName: got %q, want be0", got.BackendName)
|
||||
}
|
||||
if got.Transition.To != health.StateUp {
|
||||
t.Errorf("event To state: got %s, want up", got.Transition.To)
|
||||
@@ -211,38 +235,36 @@ func TestSubscribe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPauseResume(t *testing.T) {
|
||||
cfg := makeTestConfig(time.Hour, 3, 2) // long interval so probes never fire
|
||||
cfg := makeTestConfig(time.Hour, 3, 2)
|
||||
c := New(cfg)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go c.fanOut(ctx)
|
||||
|
||||
// Seed a worker.
|
||||
c.mu.Lock()
|
||||
key := backendKey{VIPName: "web", Backend: "10.0.0.2"}
|
||||
_, wCancel := context.WithCancel(ctx)
|
||||
c.workers[key] = &worker{
|
||||
backend: health.New("web", net.ParseIP("10.0.0.2"), 2, 3),
|
||||
hc: cfg.VIPs["web"].HealthCheck,
|
||||
vip: cfg.VIPs["web"],
|
||||
c.workers["be0"] = &worker{
|
||||
backend: health.New("be0", net.ParseIP("10.0.0.2"), 2, 3),
|
||||
hc: cfg.HealthChecks["icmp"],
|
||||
entry: cfg.Backends["be0"],
|
||||
cancel: wCancel,
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
b, ok := c.PauseBackend("web", "10.0.0.2")
|
||||
b, ok := c.PauseBackend("be0")
|
||||
if !ok {
|
||||
t.Fatal("PauseBackend: not found")
|
||||
}
|
||||
if b.State != health.StatePaused {
|
||||
t.Errorf("after pause: %s", b.State)
|
||||
if b.Health.State != health.StatePaused {
|
||||
t.Errorf("after pause: %s", b.Health.State)
|
||||
}
|
||||
|
||||
b, ok = c.ResumeBackend("web", "10.0.0.2")
|
||||
b, ok = c.ResumeBackend("be0")
|
||||
if !ok {
|
||||
t.Fatal("ResumeBackend: not found")
|
||||
}
|
||||
if b.State != health.StateUnknown {
|
||||
t.Errorf("after resume: %s", b.State)
|
||||
if b.Health.State != health.StateUnknown {
|
||||
t.Errorf("after resume: %s", b.Health.State)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user