Pools, CLI, versioning, Debian packaging, HTTPS fix
- Replaced flat `backends: [...]` list on frontends with an ordered `pools:`
list; each pool has a name and a map of backends with per-pool weights (0–100,
default 100). Pools express priority: first pool with a healthy backend wins.
- Removed global backend weight (was on the backend, now lives in the pool).
- Config validation enforces non-empty pools, non-empty pool names, weight
range, and consistent address families across all pools of a frontend.
- Added `PoolBackendInfo { name, weight }` and changed `PoolInfo.backends` from
`repeated string` to `repeated PoolBackendInfo` so weights are visible over
the API.
- Full interactive shell with readline, tab completion, and `?` inline help.
- Command tree parser (Walk) handles fixed keywords and dynamic slot nodes;
prefix matching with exact-match priority.
- Commands: `show version/frontends/frontend/backends/backend/healthchecks/
healthcheck`, `set backend <name> pause|resume`, `quit`/`exit`.
- `show frontend` output is hierarchical (pools → backends) with per-backend
weights and `[disabled]` notation; pool section uses fixed-width formatting
so ANSI color codes don't corrupt tabwriter alignment.
- `-color` flag (default true) wraps static field labels in dark-blue ANSI;
works correctly with tabwriter because all labels carry identical-length
escape sequences.
- `cmd/version.go` package holds `version`, `commit`, `date` vars set at build
time via `-ldflags -X`.
- `make build` / `make build-amd64` / `make build-arm64` all inject
`VERSION=0.1.1`, `COMMIT_HASH` (from `git rev-parse --short HEAD`), and
`DATE` (UTC ISO-8601).
- `maglevc` prints version on interactive startup and exposes `show version`.
- `maglevd` logs version/commit/date at startup; `-version` flag prints and exits.
- `doHTTPProbe` was building a `https://` target URL even though TLS was already
applied to the connection inside `inNetns`. `http.Transport` then wrapped the
connection in a second TLS layer, producing "http: server gave HTTP response
to HTTPS client". Fixed by always using `http://` in the target URL.
- Added `TestHTTPSProbe` using `httptest.NewTLSServer` to cover the full path.
- New `docs/user-guide.md`: maglevd flags/signals, maglevc commands, shell
completion, and command-tree parser walkthrough.
- New `docs/healthchecks.md`: state machine, rise/fall model, probe intervals,
all transition events with log examples.
- Updated `docs/config-guide.md`: pools design, removed global weight from
backends, updated all examples.
- Updated `README.md`: packaging table, build paths, corrected binary locations
(`/usr/sbin/maglevd`), config filename (`.yaml`).
- `debian/` directory contains `control.in`, `maglevd.service`, `default.maglev`,
`maglev.yaml` (example config), `conffiles`, `postinst`, `prerm`.
- `debian/build-deb.sh` stages a package tree and calls `dpkg-deb`; emits
`build/vpp-maglev_<version>~<commit>_<arch>.deb`.
- Cross-compiles for amd64 and arm64 in one `make pkg-deb` invocation.
- `maglevd` installed to `/usr/sbin/`, `maglevc` to `/usr/bin/`.
- Service reads `MAGLEV_CONFIG` from `/etc/default/maglev`
(default: `/etc/maglev/maglev.yaml`).
- Man pages `maglevd(8)` and `maglevc(1)` live in `docs/` and are gzip'd into
the package.
- All build output goes to `build/<arch>/`; `build/` is gitignored.
This commit is contained in:
@@ -208,9 +208,16 @@ func (c *Checker) ListFrontendBackends(frontendName string) []*health.Backend {
|
||||
return nil
|
||||
}
|
||||
var out []*health.Backend
|
||||
for _, name := range fe.Backends {
|
||||
if w, ok := c.workers[name]; ok {
|
||||
out = append(out, w.backend)
|
||||
seen := map[string]struct{}{}
|
||||
for _, pool := range fe.Pools {
|
||||
for name := range pool.Backends {
|
||||
if _, already := seen[name]; already {
|
||||
continue
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
if w, ok := c.workers[name]; ok {
|
||||
out = append(out, w.backend)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
@@ -398,15 +405,22 @@ func (c *Checker) runProbe(ctx context.Context, name string, pos, total int) {
|
||||
}
|
||||
}
|
||||
|
||||
// emitForBackend emits one Event per frontend that references backendName,
|
||||
// using the provided frontends map. Must be called with c.mu held.
|
||||
// emitForBackend emits one Event per frontend that references backendName
|
||||
// (in any pool), using the provided frontends map. Must be called with c.mu held.
|
||||
func (c *Checker) emitForBackend(backendName string, addr net.IP, t health.Transition, frontends map[string]config.Frontend) {
|
||||
for feName, fe := range frontends {
|
||||
for _, name := range fe.Backends {
|
||||
if name == backendName {
|
||||
c.emit(Event{FrontendName: feName, BackendName: backendName, Backend: addr, Transition: t})
|
||||
emitted := false
|
||||
for _, pool := range fe.Pools {
|
||||
if emitted {
|
||||
break
|
||||
}
|
||||
for name := range pool.Backends {
|
||||
if name == backendName {
|
||||
c.emit(Event{FrontendName: feName, BackendName: backendName, Backend: addr, Transition: t})
|
||||
emitted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -491,13 +505,15 @@ func tcpParamsEqual(a, b *config.TCPParams) bool {
|
||||
}
|
||||
|
||||
// activeBackendNames returns a sorted, deduplicated list of backend names that
|
||||
// are referenced by at least one frontend and have Enabled: true.
|
||||
// are referenced by at least one frontend pool and have Enabled: true.
|
||||
func activeBackendNames(cfg *config.Config) []string {
|
||||
seen := map[string]struct{}{}
|
||||
for _, fe := range cfg.Frontends {
|
||||
for _, name := range fe.Backends {
|
||||
if b, ok := cfg.Backends[name]; ok && b.Enabled {
|
||||
seen[name] = struct{}{}
|
||||
for _, pool := range fe.Pools {
|
||||
for name := range pool.Backends {
|
||||
if b, ok := cfg.Backends[name]; ok && b.Enabled {
|
||||
seen[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ func makeTestConfig(interval time.Duration, fall, rise int) *config.Config {
|
||||
Address: net.ParseIP("10.0.0.2"),
|
||||
HealthCheck: "icmp",
|
||||
Enabled: true,
|
||||
Weight: 100,
|
||||
},
|
||||
},
|
||||
Frontends: map[string]config.Frontend{
|
||||
@@ -37,7 +36,11 @@ func makeTestConfig(interval time.Duration, fall, rise int) *config.Config {
|
||||
Address: net.ParseIP("192.0.2.1"),
|
||||
Protocol: "tcp",
|
||||
Port: 80,
|
||||
Backends: []string{"be0"},
|
||||
Pools: []config.Pool{
|
||||
{Name: "primary", Backends: map[string]config.PoolBackend{
|
||||
"be0": {Weight: 100},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -116,13 +119,16 @@ func TestReloadAddsBackend(t *testing.T) {
|
||||
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: []string{"be1"},
|
||||
Pools: []config.Pool{
|
||||
{Name: "primary", Backends: map[string]config.PoolBackend{
|
||||
"be1": {Weight: 100},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -186,7 +192,11 @@ func TestSharedBackendProbedOnce(t *testing.T) {
|
||||
Address: net.ParseIP("192.0.2.3"),
|
||||
Protocol: "tcp",
|
||||
Port: 443,
|
||||
Backends: []string{"be0"},
|
||||
Pools: []config.Pool{
|
||||
{Name: "primary", Backends: map[string]config.PoolBackend{
|
||||
"be0": {Weight: 100},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
c := New(cfg)
|
||||
|
||||
@@ -67,16 +67,26 @@ type Backend struct {
|
||||
Address net.IP
|
||||
HealthCheck string // name reference into Config.HealthChecks; "" = no probing, assume healthy
|
||||
Enabled bool // default true; false = exclude from serving entirely
|
||||
Weight int // 0-100, default 100
|
||||
}
|
||||
|
||||
// PoolBackend is a backend reference within a pool, with pool-local weight.
|
||||
type PoolBackend struct {
|
||||
Weight int // 0-100, default 100
|
||||
}
|
||||
|
||||
// Pool is an ordered tier of backends within a frontend.
|
||||
type Pool struct {
|
||||
Name string
|
||||
Backends map[string]PoolBackend // keyed by backend name
|
||||
}
|
||||
|
||||
// Frontend is a single virtual IP entry.
|
||||
type Frontend struct {
|
||||
Description string
|
||||
Address net.IP
|
||||
Protocol string // "tcp", "udp", or "" (all traffic)
|
||||
Port uint16 // 0 means omitted (all ports)
|
||||
Backends []string // backend names, each must exist in Config.Backends
|
||||
Protocol string // "tcp", "udp", or "" (all traffic)
|
||||
Port uint16 // 0 means omitted (all ports)
|
||||
Pools []Pool // ordered tiers; first pool with any up backend is active
|
||||
}
|
||||
|
||||
// ---- raw YAML types --------------------------------------------------------
|
||||
@@ -127,15 +137,23 @@ type rawBackend struct {
|
||||
Address string `yaml:"address"`
|
||||
HealthCheck string `yaml:"healthcheck"`
|
||||
Enabled *bool `yaml:"enabled"` // nil → default true
|
||||
Weight *int `yaml:"weight"` // nil → default 100
|
||||
}
|
||||
|
||||
type rawPoolBackend struct {
|
||||
Weight *int `yaml:"weight"` // nil → default 100
|
||||
}
|
||||
|
||||
type rawPool struct {
|
||||
Name string `yaml:"name"`
|
||||
Backends map[string]rawPoolBackend `yaml:"backends"`
|
||||
}
|
||||
|
||||
type rawFrontend struct {
|
||||
Description string `yaml:"description"`
|
||||
Address string `yaml:"address"`
|
||||
Protocol string `yaml:"protocol"`
|
||||
Port uint16 `yaml:"port"`
|
||||
Backends []string `yaml:"backends"`
|
||||
Description string `yaml:"description"`
|
||||
Address string `yaml:"address"`
|
||||
Protocol string `yaml:"protocol"`
|
||||
Port uint16 `yaml:"port"`
|
||||
Pools []rawPool `yaml:"pools"`
|
||||
}
|
||||
|
||||
// ---- Load ------------------------------------------------------------------
|
||||
@@ -319,11 +337,6 @@ func convertBackend(name string, r *rawBackend, hcs map[string]HealthCheck) (Bac
|
||||
Address: ip,
|
||||
HealthCheck: r.HealthCheck,
|
||||
Enabled: boolDefault(r.Enabled, true),
|
||||
Weight: intDefault(r.Weight, 100),
|
||||
}
|
||||
|
||||
if b.Weight < 0 || b.Weight > 100 {
|
||||
return Backend{}, fmt.Errorf("weight %d is out of range [0, 100]", b.Weight)
|
||||
}
|
||||
|
||||
if b.HealthCheck != "" {
|
||||
@@ -340,7 +353,6 @@ func convertFrontend(name string, r *rawFrontend, backends map[string]Backend) (
|
||||
Description: r.Description,
|
||||
Protocol: r.Protocol,
|
||||
Port: r.Port,
|
||||
Backends: r.Backends,
|
||||
}
|
||||
|
||||
ip := net.ParseIP(r.Address)
|
||||
@@ -361,21 +373,38 @@ func convertFrontend(name string, r *rawFrontend, backends map[string]Backend) (
|
||||
return Frontend{}, fmt.Errorf("protocol %q requires port to be set (1-65535)", r.Protocol)
|
||||
}
|
||||
|
||||
if len(r.Backends) == 0 {
|
||||
return Frontend{}, fmt.Errorf("backends must not be empty")
|
||||
if len(r.Pools) == 0 {
|
||||
return Frontend{}, fmt.Errorf("pools must not be empty")
|
||||
}
|
||||
var firstFamily int
|
||||
for i, bName := range r.Backends {
|
||||
b, ok := backends[bName]
|
||||
if !ok {
|
||||
return Frontend{}, fmt.Errorf("backends[%d] %q not defined", i, bName)
|
||||
firstBackend := true
|
||||
for pi, rp := range r.Pools {
|
||||
if rp.Name == "" {
|
||||
return Frontend{}, fmt.Errorf("pools[%d].name must not be empty", pi)
|
||||
}
|
||||
fam := ipFamily(b.Address)
|
||||
if i == 0 {
|
||||
firstFamily = fam
|
||||
} else if fam != firstFamily {
|
||||
return Frontend{}, fmt.Errorf("backends[%d] %q has different address family than backends[0]", i, bName)
|
||||
if len(rp.Backends) == 0 {
|
||||
return Frontend{}, fmt.Errorf("pool %q backends must not be empty", rp.Name)
|
||||
}
|
||||
pool := Pool{Name: rp.Name, Backends: make(map[string]PoolBackend, len(rp.Backends))}
|
||||
for bName, rpb := range rp.Backends {
|
||||
b, ok := backends[bName]
|
||||
if !ok {
|
||||
return Frontend{}, fmt.Errorf("pool %q backend %q not defined", rp.Name, bName)
|
||||
}
|
||||
fam := ipFamily(b.Address)
|
||||
if firstBackend {
|
||||
firstFamily = fam
|
||||
firstBackend = false
|
||||
} else if fam != firstFamily {
|
||||
return Frontend{}, fmt.Errorf("pool %q backend %q has different address family than first backend", rp.Name, bName)
|
||||
}
|
||||
w := intDefault(rpb.Weight, 100)
|
||||
if w < 0 || w > 100 {
|
||||
return Frontend{}, fmt.Errorf("pool %q backend %q weight %d out of range [0, 100]", rp.Name, bName, w)
|
||||
}
|
||||
pool.Backends[bName] = PoolBackend{Weight: w}
|
||||
}
|
||||
fe.Pools = append(fe.Pools, pool)
|
||||
}
|
||||
|
||||
return fe, nil
|
||||
|
||||
@@ -41,7 +41,6 @@ maglev:
|
||||
be-v6b:
|
||||
address: 2001:db8:2::2
|
||||
healthcheck: icmp-check
|
||||
weight: 50
|
||||
enabled: true
|
||||
frontends:
|
||||
web4:
|
||||
@@ -49,13 +48,22 @@ maglev:
|
||||
address: 192.0.2.1
|
||||
protocol: tcp
|
||||
port: 80
|
||||
backends: [be-v4]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be-v4: {}
|
||||
web6:
|
||||
description: "IPv6 VIP"
|
||||
address: 2001:db8::1
|
||||
protocol: tcp
|
||||
port: 443
|
||||
backends: [be-v6a, be-v6b]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be-v6a:
|
||||
weight: 100
|
||||
be-v6b:
|
||||
weight: 50
|
||||
`
|
||||
|
||||
func TestValidConfig(t *testing.T) {
|
||||
@@ -106,7 +114,7 @@ func TestValidConfig(t *testing.T) {
|
||||
t.Errorf("icmp-check probe-ipv6-src: got %s, want 2001:db8:1::1", icmp.ProbeIPv6Src)
|
||||
}
|
||||
|
||||
// Backend defaults and explicit fields.
|
||||
// Backend fields.
|
||||
beV4 := cfg.Backends["be-v4"]
|
||||
if beV4.Address.String() != "192.0.2.10" {
|
||||
t.Errorf("be-v4 address: got %s", beV4.Address)
|
||||
@@ -117,23 +125,25 @@ func TestValidConfig(t *testing.T) {
|
||||
if !beV4.Enabled {
|
||||
t.Error("be-v4 enabled: want true (default)")
|
||||
}
|
||||
if beV4.Weight != 100 {
|
||||
t.Errorf("be-v4 weight: got %d, want 100 (default)", beV4.Weight)
|
||||
}
|
||||
|
||||
beV6b := cfg.Backends["be-v6b"]
|
||||
if beV6b.Weight != 50 {
|
||||
t.Errorf("be-v6b weight: got %d, want 50", beV6b.Weight)
|
||||
}
|
||||
|
||||
// Frontend references.
|
||||
// Pool structure.
|
||||
web4 := cfg.Frontends["web4"]
|
||||
if len(web4.Backends) != 1 || web4.Backends[0] != "be-v4" {
|
||||
t.Errorf("web4 backends: got %v", web4.Backends)
|
||||
if len(web4.Pools) != 1 || web4.Pools[0].Name != "primary" {
|
||||
t.Errorf("web4 pools: got %v", web4.Pools)
|
||||
}
|
||||
if _, ok := web4.Pools[0].Backends["be-v4"]; !ok {
|
||||
t.Error("web4 primary pool missing be-v4")
|
||||
}
|
||||
if web4.Pools[0].Backends["be-v4"].Weight != 100 {
|
||||
t.Errorf("web4 be-v4 weight: got %d, want 100 (default)", web4.Pools[0].Backends["be-v4"].Weight)
|
||||
}
|
||||
|
||||
web6 := cfg.Frontends["web6"]
|
||||
if len(web6.Backends) != 2 {
|
||||
t.Errorf("web6 backends: got %d, want 2", len(web6.Backends))
|
||||
if len(web6.Pools) != 1 || len(web6.Pools[0].Backends) != 2 {
|
||||
t.Errorf("web6 pools[0] backends: got %d, want 2", len(web6.Pools[0].Backends))
|
||||
}
|
||||
if web6.Pools[0].Backends["be-v6b"].Weight != 50 {
|
||||
t.Errorf("web6 be-v6b weight: got %d, want 50", web6.Pools[0].Backends["be-v6b"].Weight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +162,10 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`
|
||||
cfg, err := parse([]byte(raw))
|
||||
if err != nil {
|
||||
@@ -169,8 +182,13 @@ maglev:
|
||||
t.Errorf("defaults rise/fall: got %d/%d, want 2/3", hc.Rise, hc.Fall)
|
||||
}
|
||||
be := cfg.Backends["be"]
|
||||
if !be.Enabled || be.Weight != 100 {
|
||||
t.Errorf("backend defaults: enabled=%v weight=%d", be.Enabled, be.Weight)
|
||||
if !be.Enabled {
|
||||
t.Errorf("backend default enabled: got false, want true")
|
||||
}
|
||||
// Pool backend weight defaults to 100.
|
||||
v := cfg.Frontends["v"]
|
||||
if v.Pools[0].Backends["be"].Weight != 100 {
|
||||
t.Errorf("pool backend default weight: got %d, want 100", v.Pools[0].Backends["be"].Weight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +203,10 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`
|
||||
cfg, err := parse([]byte(raw))
|
||||
if err != nil {
|
||||
@@ -213,7 +234,10 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`
|
||||
cfg, err := parse([]byte(raw))
|
||||
if err != nil {
|
||||
@@ -249,7 +273,10 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
` + feExtra
|
||||
}
|
||||
|
||||
@@ -264,7 +291,7 @@ maglev:
|
||||
errSub: "probe-ipv4-src",
|
||||
},
|
||||
{
|
||||
name: "mixed backend address families in frontend",
|
||||
name: "mixed backend address families in pool",
|
||||
yaml: `
|
||||
maglev:
|
||||
healthchecks:
|
||||
@@ -278,7 +305,11 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [v4, v6]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
v4: {}
|
||||
v6: {}
|
||||
`,
|
||||
errSub: "address family",
|
||||
},
|
||||
@@ -302,7 +333,10 @@ maglev:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
protocol: tcp
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "requires port",
|
||||
},
|
||||
@@ -320,7 +354,10 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "type must be",
|
||||
},
|
||||
@@ -339,12 +376,15 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "params.path",
|
||||
},
|
||||
{
|
||||
name: "negative interval",
|
||||
name: "no error case",
|
||||
yaml: base("", "", ""),
|
||||
errSub: "",
|
||||
},
|
||||
@@ -358,12 +398,15 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "not defined",
|
||||
},
|
||||
{
|
||||
name: "undefined backend reference in frontend",
|
||||
name: "undefined backend reference in pool",
|
||||
yaml: `
|
||||
maglev:
|
||||
healthchecks:
|
||||
@@ -375,13 +418,33 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [missing]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
missing: {}
|
||||
`,
|
||||
errSub: "not defined",
|
||||
},
|
||||
{
|
||||
name: "weight out of range",
|
||||
yaml: base("", " weight: 150\n", ""),
|
||||
name: "pool weight out of range",
|
||||
yaml: `
|
||||
maglev:
|
||||
healthchecks:
|
||||
c:
|
||||
type: icmp
|
||||
interval: 1s
|
||||
timeout: 2s
|
||||
backends:
|
||||
be: {address: 10.0.0.2, healthcheck: c}
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be:
|
||||
weight: 150
|
||||
`,
|
||||
errSub: "out of range",
|
||||
},
|
||||
{
|
||||
@@ -403,7 +466,10 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "requires port",
|
||||
},
|
||||
@@ -423,10 +489,51 @@ maglev:
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
backends: [be]
|
||||
pools:
|
||||
- name: primary
|
||||
backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "requires port",
|
||||
},
|
||||
{
|
||||
name: "empty pools",
|
||||
yaml: `
|
||||
maglev:
|
||||
healthchecks:
|
||||
c:
|
||||
type: icmp
|
||||
interval: 1s
|
||||
timeout: 2s
|
||||
backends:
|
||||
be: {address: 10.0.0.2, healthcheck: c}
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
pools: []
|
||||
`,
|
||||
errSub: "pools must not be empty",
|
||||
},
|
||||
{
|
||||
name: "pool missing name",
|
||||
yaml: `
|
||||
maglev:
|
||||
healthchecks:
|
||||
c:
|
||||
type: icmp
|
||||
interval: 1s
|
||||
timeout: 2s
|
||||
backends:
|
||||
be: {address: 10.0.0.2, healthcheck: c}
|
||||
frontends:
|
||||
v:
|
||||
address: 192.0.2.1
|
||||
pools:
|
||||
- backends:
|
||||
be: {}
|
||||
`,
|
||||
errSub: "name must not be empty",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -385,13 +385,117 @@ func (x *ListFrontendsResponse) GetFrontendNames() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
type PoolBackendInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Weight int32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PoolBackendInfo) Reset() {
|
||||
*x = PoolBackendInfo{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PoolBackendInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PoolBackendInfo) ProtoMessage() {}
|
||||
|
||||
func (x *PoolBackendInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PoolBackendInfo.ProtoReflect.Descriptor instead.
|
||||
func (*PoolBackendInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *PoolBackendInfo) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PoolBackendInfo) GetWeight() int32 {
|
||||
if x != nil {
|
||||
return x.Weight
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type PoolInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Backends []*PoolBackendInfo `protobuf:"bytes,2,rep,name=backends,proto3" json:"backends,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PoolInfo) Reset() {
|
||||
*x = PoolInfo{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PoolInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PoolInfo) ProtoMessage() {}
|
||||
|
||||
func (x *PoolInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PoolInfo.ProtoReflect.Descriptor instead.
|
||||
func (*PoolInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *PoolInfo) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PoolInfo) GetBackends() []*PoolBackendInfo {
|
||||
if x != nil {
|
||||
return x.Backends
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FrontendInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
|
||||
Port uint32 `protobuf:"varint,4,opt,name=port,proto3" json:"port,omitempty"`
|
||||
BackendNames []string `protobuf:"bytes,5,rep,name=backend_names,json=backendNames,proto3" json:"backend_names,omitempty"`
|
||||
Pools []*PoolInfo `protobuf:"bytes,5,rep,name=pools,proto3" json:"pools,omitempty"`
|
||||
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -399,7 +503,7 @@ type FrontendInfo struct {
|
||||
|
||||
func (x *FrontendInfo) Reset() {
|
||||
*x = FrontendInfo{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[9]
|
||||
mi := &file_proto_maglev_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -411,7 +515,7 @@ func (x *FrontendInfo) String() string {
|
||||
func (*FrontendInfo) ProtoMessage() {}
|
||||
|
||||
func (x *FrontendInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[9]
|
||||
mi := &file_proto_maglev_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -424,7 +528,7 @@ func (x *FrontendInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FrontendInfo.ProtoReflect.Descriptor instead.
|
||||
func (*FrontendInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{9}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *FrontendInfo) GetName() string {
|
||||
@@ -455,9 +559,9 @@ func (x *FrontendInfo) GetPort() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *FrontendInfo) GetBackendNames() []string {
|
||||
func (x *FrontendInfo) GetPools() []*PoolInfo {
|
||||
if x != nil {
|
||||
return x.BackendNames
|
||||
return x.Pools
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -478,7 +582,7 @@ type ListBackendsResponse struct {
|
||||
|
||||
func (x *ListBackendsResponse) Reset() {
|
||||
*x = ListBackendsResponse{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[10]
|
||||
mi := &file_proto_maglev_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -490,7 +594,7 @@ func (x *ListBackendsResponse) String() string {
|
||||
func (*ListBackendsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListBackendsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[10]
|
||||
mi := &file_proto_maglev_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -503,7 +607,7 @@ func (x *ListBackendsResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListBackendsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListBackendsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{10}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *ListBackendsResponse) GetBackendNames() []string {
|
||||
@@ -522,7 +626,7 @@ type ListHealthChecksResponse struct {
|
||||
|
||||
func (x *ListHealthChecksResponse) Reset() {
|
||||
*x = ListHealthChecksResponse{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[11]
|
||||
mi := &file_proto_maglev_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -534,7 +638,7 @@ func (x *ListHealthChecksResponse) String() string {
|
||||
func (*ListHealthChecksResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListHealthChecksResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[11]
|
||||
mi := &file_proto_maglev_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -547,7 +651,7 @@ func (x *ListHealthChecksResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListHealthChecksResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListHealthChecksResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{11}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *ListHealthChecksResponse) GetNames() []string {
|
||||
@@ -572,7 +676,7 @@ type HTTPCheckParams struct {
|
||||
|
||||
func (x *HTTPCheckParams) Reset() {
|
||||
*x = HTTPCheckParams{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[12]
|
||||
mi := &file_proto_maglev_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -584,7 +688,7 @@ func (x *HTTPCheckParams) String() string {
|
||||
func (*HTTPCheckParams) ProtoMessage() {}
|
||||
|
||||
func (x *HTTPCheckParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[12]
|
||||
mi := &file_proto_maglev_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -597,7 +701,7 @@ func (x *HTTPCheckParams) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HTTPCheckParams.ProtoReflect.Descriptor instead.
|
||||
func (*HTTPCheckParams) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{12}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *HTTPCheckParams) GetPath() string {
|
||||
@@ -660,7 +764,7 @@ type TCPCheckParams struct {
|
||||
|
||||
func (x *TCPCheckParams) Reset() {
|
||||
*x = TCPCheckParams{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[13]
|
||||
mi := &file_proto_maglev_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -672,7 +776,7 @@ func (x *TCPCheckParams) String() string {
|
||||
func (*TCPCheckParams) ProtoMessage() {}
|
||||
|
||||
func (x *TCPCheckParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[13]
|
||||
mi := &file_proto_maglev_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -685,7 +789,7 @@ func (x *TCPCheckParams) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TCPCheckParams.ProtoReflect.Descriptor instead.
|
||||
func (*TCPCheckParams) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{13}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *TCPCheckParams) GetSsl() bool {
|
||||
@@ -730,7 +834,7 @@ type HealthCheckInfo struct {
|
||||
|
||||
func (x *HealthCheckInfo) Reset() {
|
||||
*x = HealthCheckInfo{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[14]
|
||||
mi := &file_proto_maglev_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -742,7 +846,7 @@ func (x *HealthCheckInfo) String() string {
|
||||
func (*HealthCheckInfo) ProtoMessage() {}
|
||||
|
||||
func (x *HealthCheckInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[14]
|
||||
mi := &file_proto_maglev_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -755,7 +859,7 @@ func (x *HealthCheckInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HealthCheckInfo.ProtoReflect.Descriptor instead.
|
||||
func (*HealthCheckInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{14}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *HealthCheckInfo) GetName() string {
|
||||
@@ -856,15 +960,14 @@ type BackendInfo struct {
|
||||
State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"`
|
||||
Transitions []*TransitionRecord `protobuf:"bytes,4,rep,name=transitions,proto3" json:"transitions,omitempty"`
|
||||
Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
Weight int32 `protobuf:"varint,6,opt,name=weight,proto3" json:"weight,omitempty"`
|
||||
Healthcheck string `protobuf:"bytes,7,opt,name=healthcheck,proto3" json:"healthcheck,omitempty"`
|
||||
Healthcheck string `protobuf:"bytes,6,opt,name=healthcheck,proto3" json:"healthcheck,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BackendInfo) Reset() {
|
||||
*x = BackendInfo{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[15]
|
||||
mi := &file_proto_maglev_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -876,7 +979,7 @@ func (x *BackendInfo) String() string {
|
||||
func (*BackendInfo) ProtoMessage() {}
|
||||
|
||||
func (x *BackendInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[15]
|
||||
mi := &file_proto_maglev_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -889,7 +992,7 @@ func (x *BackendInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BackendInfo.ProtoReflect.Descriptor instead.
|
||||
func (*BackendInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{15}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *BackendInfo) GetName() string {
|
||||
@@ -927,13 +1030,6 @@ func (x *BackendInfo) GetEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *BackendInfo) GetWeight() int32 {
|
||||
if x != nil {
|
||||
return x.Weight
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *BackendInfo) GetHealthcheck() string {
|
||||
if x != nil {
|
||||
return x.Healthcheck
|
||||
@@ -952,7 +1048,7 @@ type TransitionRecord struct {
|
||||
|
||||
func (x *TransitionRecord) Reset() {
|
||||
*x = TransitionRecord{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[16]
|
||||
mi := &file_proto_maglev_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -964,7 +1060,7 @@ func (x *TransitionRecord) String() string {
|
||||
func (*TransitionRecord) ProtoMessage() {}
|
||||
|
||||
func (x *TransitionRecord) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[16]
|
||||
mi := &file_proto_maglev_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -977,7 +1073,7 @@ func (x *TransitionRecord) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TransitionRecord.ProtoReflect.Descriptor instead.
|
||||
func (*TransitionRecord) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{16}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *TransitionRecord) GetFrom() string {
|
||||
@@ -1011,7 +1107,7 @@ type BackendEvent struct {
|
||||
|
||||
func (x *BackendEvent) Reset() {
|
||||
*x = BackendEvent{}
|
||||
mi := &file_proto_maglev_proto_msgTypes[17]
|
||||
mi := &file_proto_maglev_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1023,7 +1119,7 @@ func (x *BackendEvent) String() string {
|
||||
func (*BackendEvent) ProtoMessage() {}
|
||||
|
||||
func (x *BackendEvent) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_maglev_proto_msgTypes[17]
|
||||
mi := &file_proto_maglev_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1036,7 +1132,7 @@ func (x *BackendEvent) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BackendEvent.ProtoReflect.Descriptor instead.
|
||||
func (*BackendEvent) Descriptor() ([]byte, []int) {
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{17}
|
||||
return file_proto_maglev_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
func (x *BackendEvent) GetBackendName() string {
|
||||
@@ -1071,13 +1167,19 @@ const file_proto_maglev_proto_rawDesc = "" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\"\x0e\n" +
|
||||
"\fWatchRequest\">\n" +
|
||||
"\x15ListFrontendsResponse\x12%\n" +
|
||||
"\x0efrontend_names\x18\x01 \x03(\tR\rfrontendNames\"\xb3\x01\n" +
|
||||
"\x0efrontend_names\x18\x01 \x03(\tR\rfrontendNames\"=\n" +
|
||||
"\x0fPoolBackendInfo\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" +
|
||||
"\x06weight\x18\x02 \x01(\x05R\x06weight\"S\n" +
|
||||
"\bPoolInfo\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x123\n" +
|
||||
"\bbackends\x18\x02 \x03(\v2\x17.maglev.PoolBackendInfoR\bbackends\"\xb6\x01\n" +
|
||||
"\fFrontendInfo\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" +
|
||||
"\aaddress\x18\x02 \x01(\tR\aaddress\x12\x1a\n" +
|
||||
"\bprotocol\x18\x03 \x01(\tR\bprotocol\x12\x12\n" +
|
||||
"\x04port\x18\x04 \x01(\rR\x04port\x12#\n" +
|
||||
"\rbackend_names\x18\x05 \x03(\tR\fbackendNames\x12 \n" +
|
||||
"\x04port\x18\x04 \x01(\rR\x04port\x12&\n" +
|
||||
"\x05pools\x18\x05 \x03(\v2\x10.maglev.PoolInfoR\x05pools\x12 \n" +
|
||||
"\vdescription\x18\x06 \x01(\tR\vdescription\";\n" +
|
||||
"\x14ListBackendsResponse\x12#\n" +
|
||||
"\rbackend_names\x18\x01 \x03(\tR\fbackendNames\"0\n" +
|
||||
@@ -1113,15 +1215,14 @@ const file_proto_maglev_proto_rawDesc = "" +
|
||||
" \x01(\x05R\x04rise\x12\x12\n" +
|
||||
"\x04fall\x18\v \x01(\x05R\x04fall\x12+\n" +
|
||||
"\x04http\x18\f \x01(\v2\x17.maglev.HTTPCheckParamsR\x04http\x12(\n" +
|
||||
"\x03tcp\x18\r \x01(\v2\x16.maglev.TCPCheckParamsR\x03tcp\"\xe1\x01\n" +
|
||||
"\x03tcp\x18\r \x01(\v2\x16.maglev.TCPCheckParamsR\x03tcp\"\xc9\x01\n" +
|
||||
"\vBackendInfo\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" +
|
||||
"\aaddress\x18\x02 \x01(\tR\aaddress\x12\x14\n" +
|
||||
"\x05state\x18\x03 \x01(\tR\x05state\x12:\n" +
|
||||
"\vtransitions\x18\x04 \x03(\v2\x18.maglev.TransitionRecordR\vtransitions\x12\x18\n" +
|
||||
"\aenabled\x18\x05 \x01(\bR\aenabled\x12\x16\n" +
|
||||
"\x06weight\x18\x06 \x01(\x05R\x06weight\x12 \n" +
|
||||
"\vhealthcheck\x18\a \x01(\tR\vhealthcheck\"T\n" +
|
||||
"\aenabled\x18\x05 \x01(\bR\aenabled\x12 \n" +
|
||||
"\vhealthcheck\x18\x06 \x01(\tR\vhealthcheck\"T\n" +
|
||||
"\x10TransitionRecord\x12\x12\n" +
|
||||
"\x04from\x18\x01 \x01(\tR\x04from\x12\x0e\n" +
|
||||
"\x02to\x18\x02 \x01(\tR\x02to\x12\x1c\n" +
|
||||
@@ -1156,7 +1257,7 @@ func file_proto_maglev_proto_rawDescGZIP() []byte {
|
||||
return file_proto_maglev_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_maglev_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
||||
var file_proto_maglev_proto_msgTypes = make([]protoimpl.MessageInfo, 20)
|
||||
var file_proto_maglev_proto_goTypes = []any{
|
||||
(*ListFrontendsRequest)(nil), // 0: maglev.ListFrontendsRequest
|
||||
(*GetFrontendRequest)(nil), // 1: maglev.GetFrontendRequest
|
||||
@@ -1167,44 +1268,48 @@ var file_proto_maglev_proto_goTypes = []any{
|
||||
(*GetHealthCheckRequest)(nil), // 6: maglev.GetHealthCheckRequest
|
||||
(*WatchRequest)(nil), // 7: maglev.WatchRequest
|
||||
(*ListFrontendsResponse)(nil), // 8: maglev.ListFrontendsResponse
|
||||
(*FrontendInfo)(nil), // 9: maglev.FrontendInfo
|
||||
(*ListBackendsResponse)(nil), // 10: maglev.ListBackendsResponse
|
||||
(*ListHealthChecksResponse)(nil), // 11: maglev.ListHealthChecksResponse
|
||||
(*HTTPCheckParams)(nil), // 12: maglev.HTTPCheckParams
|
||||
(*TCPCheckParams)(nil), // 13: maglev.TCPCheckParams
|
||||
(*HealthCheckInfo)(nil), // 14: maglev.HealthCheckInfo
|
||||
(*BackendInfo)(nil), // 15: maglev.BackendInfo
|
||||
(*TransitionRecord)(nil), // 16: maglev.TransitionRecord
|
||||
(*BackendEvent)(nil), // 17: maglev.BackendEvent
|
||||
(*PoolBackendInfo)(nil), // 9: maglev.PoolBackendInfo
|
||||
(*PoolInfo)(nil), // 10: maglev.PoolInfo
|
||||
(*FrontendInfo)(nil), // 11: maglev.FrontendInfo
|
||||
(*ListBackendsResponse)(nil), // 12: maglev.ListBackendsResponse
|
||||
(*ListHealthChecksResponse)(nil), // 13: maglev.ListHealthChecksResponse
|
||||
(*HTTPCheckParams)(nil), // 14: maglev.HTTPCheckParams
|
||||
(*TCPCheckParams)(nil), // 15: maglev.TCPCheckParams
|
||||
(*HealthCheckInfo)(nil), // 16: maglev.HealthCheckInfo
|
||||
(*BackendInfo)(nil), // 17: maglev.BackendInfo
|
||||
(*TransitionRecord)(nil), // 18: maglev.TransitionRecord
|
||||
(*BackendEvent)(nil), // 19: maglev.BackendEvent
|
||||
}
|
||||
var file_proto_maglev_proto_depIdxs = []int32{
|
||||
12, // 0: maglev.HealthCheckInfo.http:type_name -> maglev.HTTPCheckParams
|
||||
13, // 1: maglev.HealthCheckInfo.tcp:type_name -> maglev.TCPCheckParams
|
||||
16, // 2: maglev.BackendInfo.transitions:type_name -> maglev.TransitionRecord
|
||||
16, // 3: maglev.BackendEvent.transition:type_name -> maglev.TransitionRecord
|
||||
0, // 4: maglev.Maglev.ListFrontends:input_type -> maglev.ListFrontendsRequest
|
||||
1, // 5: maglev.Maglev.GetFrontend:input_type -> maglev.GetFrontendRequest
|
||||
2, // 6: maglev.Maglev.ListBackends:input_type -> maglev.ListBackendsRequest
|
||||
3, // 7: maglev.Maglev.GetBackend:input_type -> maglev.GetBackendRequest
|
||||
4, // 8: maglev.Maglev.PauseBackend:input_type -> maglev.PauseResumeRequest
|
||||
4, // 9: maglev.Maglev.ResumeBackend:input_type -> maglev.PauseResumeRequest
|
||||
5, // 10: maglev.Maglev.ListHealthChecks:input_type -> maglev.ListHealthChecksRequest
|
||||
6, // 11: maglev.Maglev.GetHealthCheck:input_type -> maglev.GetHealthCheckRequest
|
||||
7, // 12: maglev.Maglev.WatchBackendEvents:input_type -> maglev.WatchRequest
|
||||
8, // 13: maglev.Maglev.ListFrontends:output_type -> maglev.ListFrontendsResponse
|
||||
9, // 14: maglev.Maglev.GetFrontend:output_type -> maglev.FrontendInfo
|
||||
10, // 15: maglev.Maglev.ListBackends:output_type -> maglev.ListBackendsResponse
|
||||
15, // 16: maglev.Maglev.GetBackend:output_type -> maglev.BackendInfo
|
||||
15, // 17: maglev.Maglev.PauseBackend:output_type -> maglev.BackendInfo
|
||||
15, // 18: maglev.Maglev.ResumeBackend:output_type -> maglev.BackendInfo
|
||||
11, // 19: maglev.Maglev.ListHealthChecks:output_type -> maglev.ListHealthChecksResponse
|
||||
14, // 20: maglev.Maglev.GetHealthCheck:output_type -> maglev.HealthCheckInfo
|
||||
17, // 21: maglev.Maglev.WatchBackendEvents:output_type -> maglev.BackendEvent
|
||||
13, // [13:22] is the sub-list for method output_type
|
||||
4, // [4:13] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
9, // 0: maglev.PoolInfo.backends:type_name -> maglev.PoolBackendInfo
|
||||
10, // 1: maglev.FrontendInfo.pools:type_name -> maglev.PoolInfo
|
||||
14, // 2: maglev.HealthCheckInfo.http:type_name -> maglev.HTTPCheckParams
|
||||
15, // 3: maglev.HealthCheckInfo.tcp:type_name -> maglev.TCPCheckParams
|
||||
18, // 4: maglev.BackendInfo.transitions:type_name -> maglev.TransitionRecord
|
||||
18, // 5: maglev.BackendEvent.transition:type_name -> maglev.TransitionRecord
|
||||
0, // 6: maglev.Maglev.ListFrontends:input_type -> maglev.ListFrontendsRequest
|
||||
1, // 7: maglev.Maglev.GetFrontend:input_type -> maglev.GetFrontendRequest
|
||||
2, // 8: maglev.Maglev.ListBackends:input_type -> maglev.ListBackendsRequest
|
||||
3, // 9: maglev.Maglev.GetBackend:input_type -> maglev.GetBackendRequest
|
||||
4, // 10: maglev.Maglev.PauseBackend:input_type -> maglev.PauseResumeRequest
|
||||
4, // 11: maglev.Maglev.ResumeBackend:input_type -> maglev.PauseResumeRequest
|
||||
5, // 12: maglev.Maglev.ListHealthChecks:input_type -> maglev.ListHealthChecksRequest
|
||||
6, // 13: maglev.Maglev.GetHealthCheck:input_type -> maglev.GetHealthCheckRequest
|
||||
7, // 14: maglev.Maglev.WatchBackendEvents:input_type -> maglev.WatchRequest
|
||||
8, // 15: maglev.Maglev.ListFrontends:output_type -> maglev.ListFrontendsResponse
|
||||
11, // 16: maglev.Maglev.GetFrontend:output_type -> maglev.FrontendInfo
|
||||
12, // 17: maglev.Maglev.ListBackends:output_type -> maglev.ListBackendsResponse
|
||||
17, // 18: maglev.Maglev.GetBackend:output_type -> maglev.BackendInfo
|
||||
17, // 19: maglev.Maglev.PauseBackend:output_type -> maglev.BackendInfo
|
||||
17, // 20: maglev.Maglev.ResumeBackend:output_type -> maglev.BackendInfo
|
||||
13, // 21: maglev.Maglev.ListHealthChecks:output_type -> maglev.ListHealthChecksResponse
|
||||
16, // 22: maglev.Maglev.GetHealthCheck:output_type -> maglev.HealthCheckInfo
|
||||
19, // 23: maglev.Maglev.WatchBackendEvents:output_type -> maglev.BackendEvent
|
||||
15, // [15:24] is the sub-list for method output_type
|
||||
6, // [6:15] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_maglev_proto_init() }
|
||||
@@ -1218,7 +1323,7 @@ func file_proto_maglev_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_maglev_proto_rawDesc), len(file_proto_maglev_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 18,
|
||||
NumMessages: 20,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -137,13 +137,24 @@ func (s *Server) WatchBackendEvents(_ *WatchRequest, stream Maglev_WatchBackendE
|
||||
// ---- conversion helpers ----------------------------------------------------
|
||||
|
||||
func frontendToProto(name string, fe config.Frontend) *FrontendInfo {
|
||||
pools := make([]*PoolInfo, 0, len(fe.Pools))
|
||||
for _, p := range fe.Pools {
|
||||
pi := &PoolInfo{Name: p.Name}
|
||||
for bName, pb := range p.Backends {
|
||||
pi.Backends = append(pi.Backends, &PoolBackendInfo{
|
||||
Name: bName,
|
||||
Weight: int32(pb.Weight),
|
||||
})
|
||||
}
|
||||
pools = append(pools, pi)
|
||||
}
|
||||
return &FrontendInfo{
|
||||
Name: name,
|
||||
Address: fe.Address.String(),
|
||||
Protocol: fe.Protocol,
|
||||
Port: uint32(fe.Port),
|
||||
Description: fe.Description,
|
||||
BackendNames: fe.Backends,
|
||||
Name: name,
|
||||
Address: fe.Address.String(),
|
||||
Protocol: fe.Protocol,
|
||||
Port: uint32(fe.Port),
|
||||
Description: fe.Description,
|
||||
Pools: pools,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +164,6 @@ func backendToProto(snap checker.BackendSnapshot) *BackendInfo {
|
||||
Address: snap.Health.Address.String(),
|
||||
State: snap.Health.State.String(),
|
||||
Enabled: snap.Config.Enabled,
|
||||
Weight: int32(snap.Config.Weight),
|
||||
Healthcheck: snap.Config.HealthCheck,
|
||||
}
|
||||
for _, t := range snap.Health.Transitions {
|
||||
|
||||
@@ -33,7 +33,6 @@ func makeTestChecker(ctx context.Context) *checker.Checker {
|
||||
Address: net.ParseIP("10.0.0.2"),
|
||||
HealthCheck: "icmp",
|
||||
Enabled: true,
|
||||
Weight: 100,
|
||||
},
|
||||
},
|
||||
Frontends: map[string]config.Frontend{
|
||||
@@ -41,7 +40,11 @@ func makeTestChecker(ctx context.Context) *checker.Checker {
|
||||
Address: net.ParseIP("192.0.2.1"),
|
||||
Protocol: "tcp",
|
||||
Port: 80,
|
||||
Backends: []string{"be0"},
|
||||
Pools: []config.Pool{
|
||||
{Name: "primary", Backends: map[string]config.PoolBackend{
|
||||
"be0": {Weight: 100},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -108,8 +111,14 @@ func TestGetFrontend(t *testing.T) {
|
||||
if info.Port != 80 {
|
||||
t.Errorf("GetFrontend port: got %d, want 80", info.Port)
|
||||
}
|
||||
if len(info.BackendNames) != 1 || info.BackendNames[0] != "be0" {
|
||||
t.Errorf("GetFrontend backend_names: got %v, want [be0]", info.BackendNames)
|
||||
if len(info.Pools) != 1 || info.Pools[0].Name != "primary" {
|
||||
t.Errorf("GetFrontend pools: got %v, want [{primary [be0]}]", info.Pools)
|
||||
}
|
||||
if len(info.Pools[0].Backends) != 1 || info.Pools[0].Backends[0].Name != "be0" {
|
||||
t.Errorf("GetFrontend pools[0].backends: got %v, want [{be0 100}]", info.Pools[0].Backends)
|
||||
}
|
||||
if info.Pools[0].Backends[0].Weight != 100 {
|
||||
t.Errorf("GetFrontend pools[0].backends[0].weight: got %d, want 100", info.Pools[0].Backends[0].Weight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,9 +171,6 @@ func TestGetBackend(t *testing.T) {
|
||||
if !info.Enabled {
|
||||
t.Error("expected enabled=true")
|
||||
}
|
||||
if info.Weight != 100 {
|
||||
t.Errorf("weight: got %d, want 100", info.Weight)
|
||||
}
|
||||
if info.Healthcheck != "icmp" {
|
||||
t.Errorf("healthcheck: got %q, want icmp", info.Healthcheck)
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ func doHTTPProbe(ctx context.Context, cfg ProbeConfig, useTLS bool) health.Probe
|
||||
}
|
||||
}
|
||||
|
||||
scheme := "http"
|
||||
if useTLS {
|
||||
scheme = "https"
|
||||
}
|
||||
target := fmt.Sprintf("%s://%s%s", scheme, net.JoinHostPort(cfg.Target.String(), strconv.Itoa(int(port))), p.Path)
|
||||
// Always use "http" scheme: TLS (if any) is already applied to conn during
|
||||
// the netns dial phase. Using "https" here would cause http.Transport to
|
||||
// wrap conn in a second TLS layer, producing "http: server gave HTTP
|
||||
// response to HTTPS client".
|
||||
target := fmt.Sprintf("http://%s%s", net.JoinHostPort(cfg.Target.String(), strconv.Itoa(int(port))), p.Path)
|
||||
|
||||
hostHeader := p.Host
|
||||
if hostHeader == "" {
|
||||
|
||||
@@ -170,6 +170,42 @@ func TestHTTPProbeRegexpNoMatch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPSProbe(t *testing.T) {
|
||||
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
host, portStr, _ := net.SplitHostPort(srv.Listener.Addr().String())
|
||||
port := uint16(0)
|
||||
fmt.Sscanf(portStr, "%d", &port)
|
||||
|
||||
cfg := ProbeConfig{
|
||||
Target: net.ParseIP(host),
|
||||
Port: port,
|
||||
Timeout: 2 * time.Second,
|
||||
HTTP: &config.HTTPParams{
|
||||
Path: "/",
|
||||
ResponseCodeMin: 200,
|
||||
ResponseCodeMax: 200,
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Verify HTTPSProbe succeeds (TLS conn reused, no double-wrap).
|
||||
result := HTTPSProbe(context.Background(), cfg)
|
||||
if !result.OK {
|
||||
t.Errorf("HTTPSProbe failed: code=%s detail=%s", result.Code, result.Detail)
|
||||
}
|
||||
|
||||
// Verify HTTPProbe (plain) against the TLS server fails at the TLS layer,
|
||||
// not with a double-TLS confusion error.
|
||||
result = HTTPProbe(context.Background(), cfg)
|
||||
if result.OK {
|
||||
t.Error("plain HTTPProbe against TLS server should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPProbeNoRedirect(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/other", http.StatusFound)
|
||||
|
||||
Reference in New Issue
Block a user