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:
2026-04-11 12:18:17 +02:00
parent ad7d7e20fc
commit d612086a5f
31 changed files with 1471 additions and 282 deletions

View File

@@ -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{}{}
}
}
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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 {

View File

@@ -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,
},

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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 == "" {

View File

@@ -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)