mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-30 08:16:37 +08:00
146 lines
5.0 KiB
Go
146 lines
5.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestConfigCleanupPreservesSharedDependencies(t *testing.T) {
|
|
a := newCleanupAgent(t)
|
|
a.addRuntime(t, "70_1_0", 1, 1, 1, time.Now())
|
|
a.h.cleanNodeConfigs(1, `{
|
|
"services": [{"name":"70_1_0_tcp", "handler":{"chain":"chains_88"}, "limiter":"13, rule_traffic_limit_70"}],
|
|
"chains": [{"name":"fed_chain_17"}, {"name":"chains_88"}, {"name":"chains_999"}],
|
|
"limiters": [{"name":"13"}, {"name":"rule_traffic_limit_70"}, {"name":"99"}]
|
|
}`)
|
|
a.probe(t)
|
|
if commands := a.commandsOfType("DeleteService"); len(commands) != 0 {
|
|
t.Fatalf("shared service was deleted: %+v", commands)
|
|
}
|
|
assertCleanupDependency(t, a, "DeleteChains", "chain", "chains_999")
|
|
assertCleanupDependency(t, a, "DeleteLimiters", "limiter", "99")
|
|
}
|
|
|
|
func TestConfigCleanupRemovesOrphanedForwardLimiter(t *testing.T) {
|
|
a := newCleanupAgent(t)
|
|
a.h.cleanNodeConfigs(1, `{"limiters":[{"name":"rule_traffic_limit_70"}]}`)
|
|
a.probe(t)
|
|
assertCleanupDependency(t, a, "DeleteLimiters", "limiter", "rule_traffic_limit_70")
|
|
}
|
|
|
|
func TestConfigCleanupProtectsPendingSharedDependencies(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
age time.Duration
|
|
keep bool
|
|
}{
|
|
{name: "pending", age: time.Minute, keep: true},
|
|
{name: "expired-reservation", age: 11 * time.Minute},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
a := newCleanupAgent(t)
|
|
a.addRuntime(t, "", 1, 1, 0, time.Now().Add(-tc.age))
|
|
// Dependencies may arrive before the service and its runtime binding.
|
|
a.h.cleanNodeConfigs(1, `{"chains":[{"name":"chains_88"}],"limiters":[{"name":"13"}]}`)
|
|
a.probe(t)
|
|
if tc.keep {
|
|
for _, commandType := range []string{"DeleteChains", "DeleteLimiters"} {
|
|
if commands := a.commandsOfType(commandType); len(commands) != 0 {
|
|
t.Fatalf("pending shared dependencies deleted: %+v", commands)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
assertCleanupDependency(t, a, "DeleteChains", "chain", "chains_88")
|
|
assertCleanupDependency(t, a, "DeleteLimiters", "limiter", "13")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigCleanupPreservesDependenciesOnLookupFailure(t *testing.T) {
|
|
for _, table := range []string{"peer_share_runtime", "tunnel", "speed_limit", "forward"} {
|
|
t.Run(table, func(t *testing.T) {
|
|
a := newCleanupAgent(t)
|
|
callback := "test:config-cleanup-query-failure"
|
|
injected := false
|
|
if err := a.h.repo.DB().Callback().Query().Before("gorm:query").Register(callback, func(tx *gorm.DB) {
|
|
if tx.Statement.Table == table {
|
|
injected = true
|
|
tx.AddError(errors.New("injected dependency lookup failure"))
|
|
}
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = a.h.repo.DB().Callback().Query().Remove(callback) })
|
|
configs := map[string]string{
|
|
"peer_share_runtime": `{"chains":[{"name":"chains_88"}],"limiters":[{"name":"13"}]}`,
|
|
"tunnel": `{"chains":[{"name":"chains_88"}]}`,
|
|
"speed_limit": `{"limiters":[{"name":"13"}]}`,
|
|
"forward": `{"limiters":[{"name":"rule_traffic_limit_70"}]}`,
|
|
}
|
|
a.h.cleanNodeConfigs(1, configs[table])
|
|
a.probe(t)
|
|
if !injected {
|
|
t.Fatal("expected dependency lookup failure to be injected")
|
|
}
|
|
for _, commandType := range []string{"DeleteChains", "DeleteLimiters"} {
|
|
if commands := a.commandsOfType(commandType); len(commands) != 0 {
|
|
t.Fatalf("lookup failure must not authorize cleanup: %+v", commands)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSharedForwardCleanupDuringRuntimeBinding(t *testing.T) {
|
|
for _, mode := range []string{"single", "batch", "config"} {
|
|
t.Run(mode, func(t *testing.T) {
|
|
a := newCleanupAgent(t)
|
|
a.addRuntime(t, "", 1, 1, 0, time.Now())
|
|
callback := "test:bind-during-cleanup"
|
|
bound := false
|
|
if err := a.h.repo.DB().Callback().Query().After("gorm:query").Register(callback, func(tx *gorm.DB) {
|
|
if tx.Statement.Table != "peer_share_runtime" || bound {
|
|
return
|
|
}
|
|
bound = true
|
|
// Reproduce binding immediately after the first ownership read.
|
|
// Separate name/unbound queries would both miss this runtime.
|
|
if err := a.h.repo.DB().Exec("UPDATE peer_share_runtime SET service_name = ?, applied = 1", "70_1_0").Error; err != nil {
|
|
t.Errorf("bind shared runtime: %v", err)
|
|
}
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = a.h.repo.DB().Callback().Query().Remove(callback) })
|
|
runCleanupPath(a.h, mode, []string{"70_1_0_tcp"})
|
|
a.probe(t)
|
|
if !bound {
|
|
t.Fatal("binding transition did not run")
|
|
}
|
|
if commands := a.commandsOfType("DeleteService"); len(commands) != 0 {
|
|
t.Fatalf("service was deleted during binding: %+v", commands)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func assertCleanupDependency(t *testing.T, a *cleanupAgent, commandType, key, want string) {
|
|
t.Helper()
|
|
commands := a.commandsOfType(commandType)
|
|
if len(commands) != 1 {
|
|
t.Fatalf("expected one %s for %s, got %+v", commandType, want, commands)
|
|
}
|
|
var data map[string]string
|
|
if err := json.Unmarshal(commands[0].Data, &data); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data[key] != want {
|
|
t.Fatalf("unexpected %s target: got %q, want %q", commandType, data[key], want)
|
|
}
|
|
}
|