Refactor CLI: birdc-style help, collapsed nouns, ReloadConfig, bug fixes

maglevc
- Rewrite '?' handler (birdc-style): show full command paths from current
  position to every leaf, right-aligned help column, dynamic slot values
  displayed as an indented block when cursor is at a slot position.
- Collapse show frontends/frontend, backends/backend, healthchecks/healthcheck
  into single plural-noun nodes with an optional <name> slot. Allows
  'sh ba' (list all) and 'sh ba nginx0' (show one) without ambiguity.
- Add 'config reload' command.
- Fix tabwriter ANSI alignment: continuation lines in transition output
  now carry the same label() byte overhead as the header line.
- Fix broken Walk for 'set frontend' command: setFrontendPoolName and
  setWeightValue were fixed-word nodes that couldn't capture user input;
  mark them as slot nodes with dynNone.
- Add tree_test.go covering expandPaths, cycle detection, prefix matching,
  and the full weight-command walk.

gRPC / proto
- Add ReloadConfig RPC: checks config then applies it to the running
  checker, returning ok/parse_error/semantic_error/reload_error.
- Add logging to CheckConfig (config-check-start/config-check-done at
  INFO level).

maglevd
- SIGHUP handler now calls maglevServer.TriggerReload(), sharing the
  same code path as the gRPC ReloadConfig RPC.

docs
- Collapse show command documentation to use [<name>] optional syntax.
- Remove developer-facing 'Command tree and parser' section.
- Document 'config reload'.
This commit is contained in:
2026-04-11 18:20:43 +02:00
parent 58391f5463
commit 3bd30b69f4
11 changed files with 657 additions and 222 deletions

View File

@@ -27,58 +27,55 @@ func buildTree() *Node {
exit := &Node{Word: "exit", Help: "exit the shell", Run: runQuit}
// show version
showVersion := &Node{Word: "version", Help: "show build version", Run: runShowVersion}
showVersion := &Node{Word: "version", Help: "Show build version", Run: runShowVersion}
// show frontends
showFrontends := &Node{Word: "frontends", Help: "list all frontends", Run: runShowFrontends}
// show frontend <name>
// show frontends [<name>] — without name: list all, with name: show details
showFrontendName := &Node{
Word: "<name>",
Help: "frontend name",
Help: "Show details for a single frontend",
Dynamic: dynFrontends,
Run: runShowFrontend,
}
showFrontend := &Node{
Word: "frontend",
Help: "show a single frontend",
showFrontends := &Node{
Word: "frontends",
Help: "List all frontends",
Run: runShowFrontends,
Children: []*Node{showFrontendName},
}
// show backends
showBackends := &Node{Word: "backends", Help: "list all backends", Run: runShowBackends}
// show backend <name>
// show backends [<name>] — without name: list all, with name: show details
showBackendName := &Node{
Word: "<name>",
Help: "backend name",
Help: "Show details for a single backend",
Dynamic: dynBackends,
Run: runShowBackend,
}
showBackend := &Node{
Word: "backend",
Help: "show a single backend",
showBackends := &Node{
Word: "backends",
Help: "List all backends",
Run: runShowBackends,
Children: []*Node{showBackendName},
}
// show healthchecks
showHealthChecks := &Node{Word: "healthchecks", Help: "list all health checks", Run: runShowHealthChecks}
// show healthcheck <name>
// show healthchecks [<name>] — without name: list all, with name: show details
showHealthCheckName := &Node{
Word: "<name>",
Help: "health check name",
Help: "Show details for a single health check",
Dynamic: dynHealthChecks,
Run: runShowHealthCheck,
}
showHealthCheck := &Node{
Word: "healthcheck",
Help: "show a single health check",
showHealthChecks := &Node{
Word: "healthchecks",
Help: "List all health checks",
Run: runShowHealthChecks,
Children: []*Node{showHealthCheckName},
}
show.Children = []*Node{
showVersion,
showFrontends, showFrontend,
showBackends, showBackend,
showHealthChecks, showHealthCheck,
showFrontends,
showBackends,
showHealthChecks,
}
// set backend <name> pause|resume|disabled|enabled
@@ -100,7 +97,8 @@ func buildTree() *Node {
// set frontend <name> pool <pool> backend <name> weight <0-100>
setWeightValue := &Node{
Word: "<weight>",
Help: "weight 0-100",
Help: "Set weight of a backend in a pool (0-100)",
Dynamic: dynNone, // accepts any integer; no tab-completion candidates
Run: runSetFrontendPoolBackendWeight,
}
setFrontendPoolBackendWeight := &Node{Word: "weight", Help: "set backend weight in pool", Children: []*Node{setWeightValue}}
@@ -114,6 +112,7 @@ func buildTree() *Node {
setFrontendPoolName := &Node{
Word: "<pool>",
Help: "pool name",
Dynamic: dynNone, // pool names aren't listed via gRPC; accepts any input
Children: []*Node{setFrontendPoolBackend},
}
setFrontendPool := &Node{Word: "pool", Help: "select a pool", Children: []*Node{setFrontendPoolName}}
@@ -139,7 +138,7 @@ func buildTree() *Node {
var watchEventsOptSlot *Node
watchEventsOptSlot = &Node{
Word: "<opt>",
Help: "watch option",
Help: "Stream events with options",
Dynamic: dynWatchEventOpts,
Run: runWatchEvents,
}
@@ -157,12 +156,13 @@ func buildTree() *Node {
Children: []*Node{watchEvents},
}
// config check
configCheck := &Node{Word: "check", Help: "check configuration file", Run: runConfigCheck}
// config check / reload
configCheck := &Node{Word: "check", Help: "Check configuration file", Run: runConfigCheck}
configReload := &Node{Word: "reload", Help: "Check and reload configuration", Run: runConfigReload}
configNode := &Node{
Word: "config",
Help: "configuration commands",
Children: []*Node{configCheck},
Children: []*Node{configCheck, configReload},
}
root.Children = []*Node{show, set, watch, configNode, quit, exit}
@@ -195,6 +195,10 @@ func dynHealthChecks(ctx context.Context, client grpcapi.MaglevClient) []string
return resp.Names
}
// dynNone marks a slot node that accepts any input but provides no
// tab-completion candidates (e.g. a pool name or numeric weight value).
func dynNone(_ context.Context, _ grpcapi.MaglevClient) []string { return nil }
// ---- run functions ---------------------------------------------------------
func runShowVersion(_ context.Context, _ grpcapi.MaglevClient, _ []string) error {
@@ -315,9 +319,14 @@ func runShowBackend(ctx context.Context, client grpcapi.MaglevClient, args []str
fmt.Fprintf(w, "%s\t%s\n", label("healthcheck"), info.Healthcheck)
for i, t := range info.Transitions {
ts := time.Unix(0, t.AtUnixNs)
lbl := ""
var lbl string
if i == 0 {
lbl = label("transitions")
} else {
// Pad to same visible width as "transitions" and wrap through
// label() so tabwriter sees the same byte count (ANSI overhead
// is identical on every row, keeping columns aligned).
lbl = label(" ")
}
fmt.Fprintf(w, "%s\t%s → %s\t%s\t%s\n",
lbl,
@@ -501,6 +510,26 @@ func runConfigCheck(ctx context.Context, client grpcapi.MaglevClient, _ []string
return fmt.Errorf("semantic error: %s", resp.SemanticError)
}
func runConfigReload(ctx context.Context, client grpcapi.MaglevClient, _ []string) error {
ctx, cancel := context.WithTimeout(ctx, callTimeout)
defer cancel()
resp, err := client.ReloadConfig(ctx, &grpcapi.ReloadConfigRequest{})
if err != nil {
return err
}
if resp.Ok {
fmt.Println("config reloaded")
return nil
}
if resp.ParseError != "" {
return fmt.Errorf("parse error: %s", resp.ParseError)
}
if resp.SemanticError != "" {
return fmt.Errorf("semantic error: %s", resp.SemanticError)
}
return fmt.Errorf("reload error: %s", resp.ReloadError)
}
// formatDuration formats a duration as Xd Xh Xm Xs without milliseconds.
func formatDuration(d time.Duration) string {
if d < 0 {

View File

@@ -67,15 +67,16 @@ func (ql *questionListener) OnChange(line []rune, pos int, key rune) (newLine []
return line, pos, false
}
// line[:pos] includes the '?' just typed — strip it before tokenizing.
// Strip the '?' that was just appended to line[:pos].
before := string(line[:pos])
if len(before) > 0 && before[len(before)-1] == '?' {
before = before[:len(before)-1]
}
tokens := splitTokens(before)
var partial string
// Split into confirmed prefix tokens and the partial token being typed.
var prefix []string
var partial string
if len(before) == 0 || before[len(before)-1] == ' ' {
prefix = tokens
partial = ""
@@ -84,25 +85,65 @@ func (ql *questionListener) OnChange(line []rune, pos int, key rune) (newLine []
partial = tokens[len(tokens)-1]
}
// Walk the confirmed prefix to the current node, then try to advance one
// more step using the partial token (via prefix-match or slot fallback).
// This mirrors birdc: "sh?" expands "sh" to "show" and shows show's subtree.
node, _ := Walk(ql.root, prefix)
displayPrefix := strings.Join(prefix, " ")
if partial != "" {
if next := matchFixedChild(node.Children, partial); next != nil {
// Partial uniquely matched a fixed child — descend into it.
node = next
displayPrefix = strings.Join(tokens, " ")
} else if slot := findSlotChild(node.Children); slot != nil {
// Partial is filling a slot node.
node = slot
displayPrefix = strings.Join(tokens, " ")
}
// If partial matched nothing (ambiguous or dead end), stay at the
// current node and show its subcommands with the confirmed prefix.
}
// Expand all leaf paths reachable from the current node.
lines := expandPaths(node, displayPrefix, make(map[*Node]bool))
// If the cursor is at a position where the next input is a dynamic slot,
// fetch live values now and show them below the syntax lines.
ctx, cancel := context.WithTimeout(context.Background(), completeTimeout)
defer cancel()
var dynValues []string
var dynWord string
if slot := findSlotChild(node.Children); slot != nil && slot.Dynamic != nil {
dynValues = slot.Dynamic(ctx, ql.client)
dynWord = slot.Word
}
candidates := Candidates(ql.root, prefix, partial, ctx, ql.client)
// Right-align the help column at the width of the longest path + 2.
maxLen := 0
for _, l := range lines {
if len(l.path) > maxLen {
maxLen = len(l.path)
}
}
// Emit output. Raw terminal mode requires \r\n.
fmt.Fprintf(ql.rl.Stderr(), "\r\n")
if len(candidates) == 0 {
if len(lines) == 0 {
fmt.Fprintf(ql.rl.Stderr(), " <no completions>\r\n")
} else {
for _, c := range candidates {
if c.Help != "" {
fmt.Fprintf(ql.rl.Stderr(), " %-20s %s\r\n", c.Word, c.Help)
for _, l := range lines {
if l.help != "" {
fmt.Fprintf(ql.rl.Stderr(), "%-*s %s\r\n", maxLen+2, l.path, l.help)
} else {
fmt.Fprintf(ql.rl.Stderr(), " %s\r\n", c.Word)
fmt.Fprintf(ql.rl.Stderr(), "%s\r\n", l.path)
}
}
if len(dynValues) > 0 {
fmt.Fprintf(ql.rl.Stderr(), " %s: %s\r\n", dynWord, strings.Join(dynValues, " "))
}
}
// Remove the '?' from the line and return with cursor one step back.
// Remove the '?' from the line and step cursor back one position.
newLine = append(append([]rune{}, line[:pos-1]...), line[pos:]...)
return newLine, pos - 1, true
}

View File

@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"strings"
"github.com/chzyer/readline"
@@ -75,20 +76,28 @@ func dispatch(ctx context.Context, root *Node, client grpcapi.MaglevClient, toke
return node.Run(ctx, client, args)
}
// showHelp prints available completions for the given token path.
// showHelp prints all reachable commands from the given token path, birdc-style.
func showHelp(root *Node, tokens []string) {
node, _ := Walk(root, tokens)
prefix := strings.Join(tokens, " ")
lines := expandPaths(node, prefix, make(map[*Node]bool))
candidates := filterFixedChildren(node.Children, "")
if len(candidates) == 0 {
maxLen := 0
for _, l := range lines {
if len(l.path) > maxLen {
maxLen = len(l.path)
}
}
if len(lines) == 0 {
fmt.Println(" <no completions>")
return
}
for _, c := range candidates {
if c.Help != "" {
fmt.Printf(" %-20s %s\n", c.Word, c.Help)
for _, l := range lines {
if l.help != "" {
fmt.Printf("%-*s %s\n", maxLen+2, l.path, l.help)
} else {
fmt.Printf(" %s\n", c.Word)
fmt.Printf("%s\n", l.path)
}
}
}

View File

@@ -91,6 +91,36 @@ func findSlotChild(children []*Node) *Node {
return nil
}
// helpLine is a (path, help) pair used when displaying '?' output.
type helpLine struct {
path string
help string
}
// expandPaths returns all (path, help) pairs for every node reachable from
// node that has a Run function. prefix is the display string accumulated so
// far (e.g. "show frontend"). visited prevents infinite loops through
// self-referencing slot nodes like watchEventsOptSlot.
func expandPaths(node *Node, prefix string, visited map[*Node]bool) []helpLine {
if visited[node] {
return nil
}
visited[node] = true
var lines []helpLine
if node.Run != nil {
lines = append(lines, helpLine{path: prefix, help: node.Help})
}
for _, child := range node.Children {
childPrefix := child.Word
if prefix != "" {
childPrefix = prefix + " " + child.Word
}
lines = append(lines, expandPaths(child, childPrefix, visited)...)
}
return lines
}
// Candidates returns the completable children at the current position given
// the already-typed tokens and the partial token being completed.
func Candidates(root *Node, tokens []string, partial string, ctx context.Context, client grpcapi.MaglevClient) []*Node {

153
cmd/maglevc/tree_test.go Normal file
View File

@@ -0,0 +1,153 @@
// Copyright (c) 2026, Pim van Pelt <pim@ipng.ch>
package main
import (
"strings"
"testing"
)
func TestExpandPathsRoot(t *testing.T) {
root := buildTree()
lines := expandPaths(root, "", make(map[*Node]bool))
// Should include well-known leaf paths.
want := []string{
"show version",
"show frontends",
"show frontends <name>",
"show backends",
"show backends <name>",
"show healthchecks",
"show healthchecks <name>",
"set backend <name> pause",
"set backend <name> resume",
"set backend <name> disable",
"set backend <name> enable",
"set frontend <name> pool <pool> backend <backend> weight <weight>",
"watch events",
"watch events <opt>",
"config check",
"config reload",
"quit",
"exit",
}
paths := make(map[string]bool, len(lines))
for _, l := range lines {
paths[l.path] = true
}
for _, w := range want {
if !paths[w] {
t.Errorf("expandPaths(root) missing %q", w)
}
}
}
func TestExpandPathsShow(t *testing.T) {
root := buildTree()
showNode, _ := Walk(root, []string{"show"})
lines := expandPaths(showNode, "show", make(map[*Node]bool))
for _, l := range lines {
if !strings.HasPrefix(l.path, "show ") {
t.Errorf("unexpected path %q: should start with 'show '", l.path)
}
if l.help == "" {
t.Errorf("path %q has empty help", l.path)
}
}
// version, frontends, frontends <name>, backends, backends <name>,
// healthchecks, healthchecks <name> = 7 lines
if len(lines) != 7 {
t.Errorf("expected exactly 7 show subcommands, got %d", len(lines))
}
}
func TestExpandPathsNoCycles(t *testing.T) {
root := buildTree()
// watch events has a self-referencing slot; expandPaths must terminate.
watchEvents, _ := Walk(root, []string{"watch", "events"})
lines := expandPaths(watchEvents, "watch events", make(map[*Node]bool))
// Should produce exactly 2 lines: "watch events" and "watch events <opt>".
if len(lines) != 2 {
t.Errorf("watch events: expected 2 lines, got %d: %v", len(lines), lines)
}
}
func TestExpandPathsSetBackendName(t *testing.T) {
root := buildTree()
// Walk to the name slot so displayPrefix carries the actual arg.
node, _ := Walk(root, []string{"set", "backend", "mybackend"})
lines := expandPaths(node, "set backend mybackend", make(map[*Node]bool))
want := []string{
"set backend mybackend pause",
"set backend mybackend resume",
"set backend mybackend disable",
"set backend mybackend enable",
}
if len(lines) != len(want) {
t.Fatalf("expected %d lines, got %d: %v", len(want), len(lines), lines)
}
for i, w := range want {
if lines[i].path != w {
t.Errorf("line %d: got %q, want %q", i, lines[i].path, w)
}
}
}
func TestPrefixMatchCollapsedNouns(t *testing.T) {
root := buildTree()
// "sh ba" → show backends (list all) via prefix matching.
node, args := Walk(root, []string{"sh", "ba"})
if node.Run == nil {
t.Fatal("'sh ba' did not reach a Run node")
}
if len(args) != 0 {
t.Errorf("'sh ba' should have 0 args, got %v", args)
}
// "sh ba nginx0" → show backends <name> (get specific) via slot.
node, args = Walk(root, []string{"sh", "ba", "nginx0"})
if node.Run == nil {
t.Fatal("'sh ba nginx0' did not reach a Run node")
}
if len(args) != 1 || args[0] != "nginx0" {
t.Errorf("'sh ba nginx0' args: got %v, want [nginx0]", args)
}
// "sh fr" → show frontends (list all).
node, _ = Walk(root, []string{"sh", "fr"})
if node.Run == nil {
t.Fatal("'sh fr' did not reach a Run node")
}
// "sh he icmp" → show healthchecks icmp (get specific).
node, args = Walk(root, []string{"sh", "he", "icmp"})
if node.Run == nil {
t.Fatal("'sh he icmp' did not reach a Run node")
}
if len(args) != 1 || args[0] != "icmp" {
t.Errorf("'sh he icmp' args: got %v, want [icmp]", args)
}
}
func TestExpandPathsWeightSlotWalk(t *testing.T) {
// Verify the weight command is fully walkable (fixes bug: setWeightValue
// and setFrontendPoolName were non-slot nodes that couldn't capture tokens).
root := buildTree()
node, args := Walk(root, []string{"set", "frontend", "web", "pool", "primary", "backend", "be0", "weight", "42"})
if node.Run == nil {
t.Fatal("Walk did not reach a Run node for full weight command")
}
if len(args) != 4 {
t.Errorf("expected 4 args (name, pool, backend, weight), got %d: %v", len(args), args)
}
if args[3] != "42" {
t.Errorf("args[3] (weight): got %q, want 42", args[3])
}
}

View File

@@ -93,7 +93,8 @@ func run() error {
return fmt.Errorf("listen %s: %w", *grpcAddr, err)
}
srv := grpc.NewServer()
grpcapi.RegisterMaglevServer(srv, grpcapi.NewServer(ctx, chkr, logBroadcaster, *configPath))
maglevServer := grpcapi.NewServer(ctx, chkr, logBroadcaster, *configPath)
grpcapi.RegisterMaglevServer(srv, maglevServer)
if *enableReflection {
reflection.Register(srv)
}
@@ -112,21 +113,7 @@ func run() error {
for sig := range sigCh {
switch sig {
case syscall.SIGHUP:
slog.Info("config-reload-start")
newCfg, result := config.Check(*configPath)
if !result.OK() {
if result.ParseError != "" {
slog.Error("config-check-failed", "type", "parse", "err", result.ParseError)
} else {
slog.Error("config-check-failed", "type", "semantic", "err", result.SemanticError)
}
continue
}
if err := chkr.Reload(ctx, newCfg); err != nil {
slog.Error("checker-reload-error", "err", err)
continue
}
slog.Info("config-reload-done", "frontends", len(newCfg.Frontends))
maglevServer.TriggerReload()
case syscall.SIGTERM, syscall.SIGINT:
slog.Info("shutdown", "signal", sig)

View File

@@ -24,7 +24,7 @@ are used for anything not set.
| Signal | Effect |
|---|---|
| `SIGHUP` | Reload the configuration file. The file is checked before applying; if there is a parse or semantic error the reload is aborted and the error is logged (the daemon continues running with its current config). New backends are started, removed backends are stopped, backends whose health-check config is unchanged continue probing without interruption. |
| `SIGHUP` | Reload the configuration file (same code path as `config reload` in `maglevc`). The file is checked before applying; if there is a parse or semantic error the reload is aborted and the error is logged (the daemon continues running with its current config). New backends are started, removed backends are stopped, backends whose health-check config is unchanged continue probing without interruption. |
| `SIGTERM` / `SIGINT` | Graceful shutdown. Active gRPC streams are closed, the server drains, then the process exits. |
### Capabilities
@@ -69,33 +69,33 @@ build version is printed on entry.
```
show version Print build version, commit hash, and build date.
show frontends List all frontend names.
show frontend <name> Show address, protocol, port, description, and pools.
Each pool lists its backends with weights (if != 100)
and marks disabled backends with [disabled].
show frontends [<name>] Without name: list all frontend names.
With name: show address, protocol, port, description,
and pools. Each pool lists its backends with weights
(if != 100) and marks disabled backends with [disabled].
show backends List all backend names.
show backend <name> Show address, current state (with duration in that state),
show backends [<name>] Without name: list all backend names.
With name: show address, current state (with duration),
enabled flag, health check, and recent state transitions
with timestamps and how long ago each occurred.
show healthchecks List all health-check names.
show healthcheck <name> Show full health-check configuration.
show healthchecks [<name>] Without name: list all health-check names.
With name: show full health-check configuration.
set backend <name> pause Suspend health checking for a backend, freezing its state.
set backend <name> resume Resume health checking; backend re-enters unknown state
and is probed immediately.
set frontend <name> pool <pool> backend <name> weight <0-100>
Set the weight of a backend within a pool. Weight 0 keeps
the backend in the pool but assigns it no traffic.
Takes effect immediately without reloading configuration.
set backend <name> disable Stop probing entirely and remove the backend from rotation.
The backend remains visible (state: removed) and can be
re-enabled without reloading configuration.
set backend <name> enable Re-enable a disabled backend. A fresh probe goroutine is
started and the backend re-enters unknown state.
set frontend <name> pool <pool> backend <name> weight <0-100>
Set the weight of a backend within a pool. Weight 0 keeps
the backend in the pool but assigns it no traffic.
Takes effect immediately without reloading configuration.
watch events Stream all events (log, backend transitions, frontend)
[num <n>] Stop after receiving n events.
[log [level <level>]] Include log events. level is debug|info|warn|error
@@ -115,6 +115,10 @@ watch events Stream all events (log, backend transitions, fr
config check Ask maglevd to read and validate its current config file.
Prints "config ok" on success, or the error (parse or
semantic) returned by the daemon.
config reload Check and reload the configuration file. Equivalent to
sending SIGHUP to maglevd. Prints "config reloaded" on
success, or the specific error (parse, semantic, or
reload) that prevented the reload.
quit / exit Leave the interactive shell.
```
@@ -134,32 +138,5 @@ restored.
completions for the current position, with a short description next to each
keyword. The `?` character is not added to the input line.
Commands and keywords support **prefix matching**: typing `sh b` is equivalent
to `show backend` provided the prefix is unambiguous. Exact matches always take
priority over prefix matches, so `show backend` and `show backends` are
unambiguous even though one is a prefix of the other.
### Command tree and parser
Commands form a tree of `Node` values. Each node has a fixed `Word` (a keyword)
or is a *slot node* (marked by a `Dynamic` function that enumerates valid
values at completion time). The parser (`Walk`) descends the tree token by
token:
1. Try to match the current token against the fixed-keyword children of the
current node (exact match first, then unique prefix match).
2. If no fixed child matches, try a slot child — any token is accepted and
stored as an argument.
3. Stop when tokens are exhausted or no match is found.
The leaf node reached by `Walk` must have a `Run` function; otherwise the
available sub-commands at that position are printed as help. Arguments
collected from slot nodes are passed to `Run` as a slice.
Example walk for `set backend nginx0-ams pause`:
```
root → set → backend → <name>(nginx0-ams collected as arg) → pause
```
`pause.Run` is called with `args = ["nginx0-ams"]`.
Commands and keywords support **prefix matching**: typing `sh ba` is equivalent
to `show backends`, and `sh ba nginx0` is equivalent to `show backends nginx0`.

View File

@@ -401,6 +401,110 @@ func (x *CheckConfigResponse) GetSemanticError() string {
return ""
}
type ReloadConfigRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReloadConfigRequest) Reset() {
*x = ReloadConfigRequest{}
mi := &file_proto_maglev_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReloadConfigRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReloadConfigRequest) ProtoMessage() {}
func (x *ReloadConfigRequest) 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 ReloadConfigRequest.ProtoReflect.Descriptor instead.
func (*ReloadConfigRequest) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{9}
}
type ReloadConfigResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"`
ParseError string `protobuf:"bytes,2,opt,name=parse_error,json=parseError,proto3" json:"parse_error,omitempty"` // set when YAML cannot be read or parsed
SemanticError string `protobuf:"bytes,3,opt,name=semantic_error,json=semanticError,proto3" json:"semantic_error,omitempty"` // set when YAML is valid but semantically incorrect
ReloadError string `protobuf:"bytes,4,opt,name=reload_error,json=reloadError,proto3" json:"reload_error,omitempty"` // set when config is valid but the reload itself failed
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReloadConfigResponse) Reset() {
*x = ReloadConfigResponse{}
mi := &file_proto_maglev_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReloadConfigResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReloadConfigResponse) ProtoMessage() {}
func (x *ReloadConfigResponse) 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 ReloadConfigResponse.ProtoReflect.Descriptor instead.
func (*ReloadConfigResponse) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{10}
}
func (x *ReloadConfigResponse) GetOk() bool {
if x != nil {
return x.Ok
}
return false
}
func (x *ReloadConfigResponse) GetParseError() string {
if x != nil {
return x.ParseError
}
return ""
}
func (x *ReloadConfigResponse) GetSemanticError() string {
if x != nil {
return x.SemanticError
}
return ""
}
func (x *ReloadConfigResponse) GetReloadError() string {
if x != nil {
return x.ReloadError
}
return ""
}
type SetWeightRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Frontend string `protobuf:"bytes,1,opt,name=frontend,proto3" json:"frontend,omitempty"`
@@ -413,7 +517,7 @@ type SetWeightRequest struct {
func (x *SetWeightRequest) Reset() {
*x = SetWeightRequest{}
mi := &file_proto_maglev_proto_msgTypes[9]
mi := &file_proto_maglev_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -425,7 +529,7 @@ func (x *SetWeightRequest) String() string {
func (*SetWeightRequest) ProtoMessage() {}
func (x *SetWeightRequest) 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 {
@@ -438,7 +542,7 @@ func (x *SetWeightRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SetWeightRequest.ProtoReflect.Descriptor instead.
func (*SetWeightRequest) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{9}
return file_proto_maglev_proto_rawDescGZIP(), []int{11}
}
func (x *SetWeightRequest) GetFrontend() string {
@@ -483,7 +587,7 @@ type WatchRequest struct {
func (x *WatchRequest) Reset() {
*x = WatchRequest{}
mi := &file_proto_maglev_proto_msgTypes[10]
mi := &file_proto_maglev_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -495,7 +599,7 @@ func (x *WatchRequest) String() string {
func (*WatchRequest) ProtoMessage() {}
func (x *WatchRequest) 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 {
@@ -508,7 +612,7 @@ func (x *WatchRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead.
func (*WatchRequest) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{10}
return file_proto_maglev_proto_rawDescGZIP(), []int{12}
}
func (x *WatchRequest) GetLog() bool {
@@ -548,7 +652,7 @@ type ListFrontendsResponse struct {
func (x *ListFrontendsResponse) Reset() {
*x = ListFrontendsResponse{}
mi := &file_proto_maglev_proto_msgTypes[11]
mi := &file_proto_maglev_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -560,7 +664,7 @@ func (x *ListFrontendsResponse) String() string {
func (*ListFrontendsResponse) ProtoMessage() {}
func (x *ListFrontendsResponse) 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 {
@@ -573,7 +677,7 @@ func (x *ListFrontendsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListFrontendsResponse.ProtoReflect.Descriptor instead.
func (*ListFrontendsResponse) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{11}
return file_proto_maglev_proto_rawDescGZIP(), []int{13}
}
func (x *ListFrontendsResponse) GetFrontendNames() []string {
@@ -593,7 +697,7 @@ type PoolBackendInfo struct {
func (x *PoolBackendInfo) Reset() {
*x = PoolBackendInfo{}
mi := &file_proto_maglev_proto_msgTypes[12]
mi := &file_proto_maglev_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -605,7 +709,7 @@ func (x *PoolBackendInfo) String() string {
func (*PoolBackendInfo) ProtoMessage() {}
func (x *PoolBackendInfo) 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 {
@@ -618,7 +722,7 @@ func (x *PoolBackendInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use PoolBackendInfo.ProtoReflect.Descriptor instead.
func (*PoolBackendInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{12}
return file_proto_maglev_proto_rawDescGZIP(), []int{14}
}
func (x *PoolBackendInfo) GetName() string {
@@ -645,7 +749,7 @@ type PoolInfo struct {
func (x *PoolInfo) Reset() {
*x = PoolInfo{}
mi := &file_proto_maglev_proto_msgTypes[13]
mi := &file_proto_maglev_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -657,7 +761,7 @@ func (x *PoolInfo) String() string {
func (*PoolInfo) ProtoMessage() {}
func (x *PoolInfo) 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 {
@@ -670,7 +774,7 @@ func (x *PoolInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use PoolInfo.ProtoReflect.Descriptor instead.
func (*PoolInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{13}
return file_proto_maglev_proto_rawDescGZIP(), []int{15}
}
func (x *PoolInfo) GetName() string {
@@ -701,7 +805,7 @@ type FrontendInfo struct {
func (x *FrontendInfo) Reset() {
*x = FrontendInfo{}
mi := &file_proto_maglev_proto_msgTypes[14]
mi := &file_proto_maglev_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -713,7 +817,7 @@ func (x *FrontendInfo) String() string {
func (*FrontendInfo) ProtoMessage() {}
func (x *FrontendInfo) 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 {
@@ -726,7 +830,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{14}
return file_proto_maglev_proto_rawDescGZIP(), []int{16}
}
func (x *FrontendInfo) GetName() string {
@@ -780,7 +884,7 @@ type ListBackendsResponse struct {
func (x *ListBackendsResponse) Reset() {
*x = ListBackendsResponse{}
mi := &file_proto_maglev_proto_msgTypes[15]
mi := &file_proto_maglev_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -792,7 +896,7 @@ func (x *ListBackendsResponse) String() string {
func (*ListBackendsResponse) ProtoMessage() {}
func (x *ListBackendsResponse) 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 {
@@ -805,7 +909,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{15}
return file_proto_maglev_proto_rawDescGZIP(), []int{17}
}
func (x *ListBackendsResponse) GetBackendNames() []string {
@@ -824,7 +928,7 @@ type ListHealthChecksResponse struct {
func (x *ListHealthChecksResponse) Reset() {
*x = ListHealthChecksResponse{}
mi := &file_proto_maglev_proto_msgTypes[16]
mi := &file_proto_maglev_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -836,7 +940,7 @@ func (x *ListHealthChecksResponse) String() string {
func (*ListHealthChecksResponse) ProtoMessage() {}
func (x *ListHealthChecksResponse) 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 {
@@ -849,7 +953,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{16}
return file_proto_maglev_proto_rawDescGZIP(), []int{18}
}
func (x *ListHealthChecksResponse) GetNames() []string {
@@ -874,7 +978,7 @@ type HTTPCheckParams struct {
func (x *HTTPCheckParams) Reset() {
*x = HTTPCheckParams{}
mi := &file_proto_maglev_proto_msgTypes[17]
mi := &file_proto_maglev_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -886,7 +990,7 @@ func (x *HTTPCheckParams) String() string {
func (*HTTPCheckParams) ProtoMessage() {}
func (x *HTTPCheckParams) 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 {
@@ -899,7 +1003,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{17}
return file_proto_maglev_proto_rawDescGZIP(), []int{19}
}
func (x *HTTPCheckParams) GetPath() string {
@@ -962,7 +1066,7 @@ type TCPCheckParams struct {
func (x *TCPCheckParams) Reset() {
*x = TCPCheckParams{}
mi := &file_proto_maglev_proto_msgTypes[18]
mi := &file_proto_maglev_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -974,7 +1078,7 @@ func (x *TCPCheckParams) String() string {
func (*TCPCheckParams) ProtoMessage() {}
func (x *TCPCheckParams) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[18]
mi := &file_proto_maglev_proto_msgTypes[20]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -987,7 +1091,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{18}
return file_proto_maglev_proto_rawDescGZIP(), []int{20}
}
func (x *TCPCheckParams) GetSsl() bool {
@@ -1032,7 +1136,7 @@ type HealthCheckInfo struct {
func (x *HealthCheckInfo) Reset() {
*x = HealthCheckInfo{}
mi := &file_proto_maglev_proto_msgTypes[19]
mi := &file_proto_maglev_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1044,7 +1148,7 @@ func (x *HealthCheckInfo) String() string {
func (*HealthCheckInfo) ProtoMessage() {}
func (x *HealthCheckInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[19]
mi := &file_proto_maglev_proto_msgTypes[21]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1057,7 +1161,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{19}
return file_proto_maglev_proto_rawDescGZIP(), []int{21}
}
func (x *HealthCheckInfo) GetName() string {
@@ -1165,7 +1269,7 @@ type BackendInfo struct {
func (x *BackendInfo) Reset() {
*x = BackendInfo{}
mi := &file_proto_maglev_proto_msgTypes[20]
mi := &file_proto_maglev_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1177,7 +1281,7 @@ func (x *BackendInfo) String() string {
func (*BackendInfo) ProtoMessage() {}
func (x *BackendInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[20]
mi := &file_proto_maglev_proto_msgTypes[22]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1190,7 +1294,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{20}
return file_proto_maglev_proto_rawDescGZIP(), []int{22}
}
func (x *BackendInfo) GetName() string {
@@ -1246,7 +1350,7 @@ type TransitionRecord struct {
func (x *TransitionRecord) Reset() {
*x = TransitionRecord{}
mi := &file_proto_maglev_proto_msgTypes[21]
mi := &file_proto_maglev_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1258,7 +1362,7 @@ func (x *TransitionRecord) String() string {
func (*TransitionRecord) ProtoMessage() {}
func (x *TransitionRecord) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[21]
mi := &file_proto_maglev_proto_msgTypes[23]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1271,7 +1375,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{21}
return file_proto_maglev_proto_rawDescGZIP(), []int{23}
}
func (x *TransitionRecord) GetFrom() string {
@@ -1306,7 +1410,7 @@ type LogAttr struct {
func (x *LogAttr) Reset() {
*x = LogAttr{}
mi := &file_proto_maglev_proto_msgTypes[22]
mi := &file_proto_maglev_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1318,7 +1422,7 @@ func (x *LogAttr) String() string {
func (*LogAttr) ProtoMessage() {}
func (x *LogAttr) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[22]
mi := &file_proto_maglev_proto_msgTypes[24]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1331,7 +1435,7 @@ func (x *LogAttr) ProtoReflect() protoreflect.Message {
// Deprecated: Use LogAttr.ProtoReflect.Descriptor instead.
func (*LogAttr) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{22}
return file_proto_maglev_proto_rawDescGZIP(), []int{24}
}
func (x *LogAttr) GetKey() string {
@@ -1361,7 +1465,7 @@ type LogEvent struct {
func (x *LogEvent) Reset() {
*x = LogEvent{}
mi := &file_proto_maglev_proto_msgTypes[23]
mi := &file_proto_maglev_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1373,7 +1477,7 @@ func (x *LogEvent) String() string {
func (*LogEvent) ProtoMessage() {}
func (x *LogEvent) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[23]
mi := &file_proto_maglev_proto_msgTypes[25]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1386,7 +1490,7 @@ func (x *LogEvent) ProtoReflect() protoreflect.Message {
// Deprecated: Use LogEvent.ProtoReflect.Descriptor instead.
func (*LogEvent) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{23}
return file_proto_maglev_proto_rawDescGZIP(), []int{25}
}
func (x *LogEvent) GetAtUnixNs() int64 {
@@ -1428,7 +1532,7 @@ type BackendEvent struct {
func (x *BackendEvent) Reset() {
*x = BackendEvent{}
mi := &file_proto_maglev_proto_msgTypes[24]
mi := &file_proto_maglev_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1440,7 +1544,7 @@ func (x *BackendEvent) String() string {
func (*BackendEvent) ProtoMessage() {}
func (x *BackendEvent) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[24]
mi := &file_proto_maglev_proto_msgTypes[26]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1453,7 +1557,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{24}
return file_proto_maglev_proto_rawDescGZIP(), []int{26}
}
func (x *BackendEvent) GetBackendName() string {
@@ -1479,7 +1583,7 @@ type FrontendEvent struct {
func (x *FrontendEvent) Reset() {
*x = FrontendEvent{}
mi := &file_proto_maglev_proto_msgTypes[25]
mi := &file_proto_maglev_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1491,7 +1595,7 @@ func (x *FrontendEvent) String() string {
func (*FrontendEvent) ProtoMessage() {}
func (x *FrontendEvent) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[25]
mi := &file_proto_maglev_proto_msgTypes[27]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1504,7 +1608,7 @@ func (x *FrontendEvent) ProtoReflect() protoreflect.Message {
// Deprecated: Use FrontendEvent.ProtoReflect.Descriptor instead.
func (*FrontendEvent) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{25}
return file_proto_maglev_proto_rawDescGZIP(), []int{27}
}
// Event is the envelope returned by WatchEvents.
@@ -1522,7 +1626,7 @@ type Event struct {
func (x *Event) Reset() {
*x = Event{}
mi := &file_proto_maglev_proto_msgTypes[26]
mi := &file_proto_maglev_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1534,7 +1638,7 @@ func (x *Event) String() string {
func (*Event) ProtoMessage() {}
func (x *Event) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[26]
mi := &file_proto_maglev_proto_msgTypes[28]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1547,7 +1651,7 @@ func (x *Event) ProtoReflect() protoreflect.Message {
// Deprecated: Use Event.ProtoReflect.Descriptor instead.
func (*Event) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{26}
return file_proto_maglev_proto_rawDescGZIP(), []int{28}
}
func (x *Event) GetEvent() isEvent_Event {
@@ -1627,7 +1731,14 @@ const file_proto_maglev_proto_rawDesc = "" +
"\x02ok\x18\x01 \x01(\bR\x02ok\x12\x1f\n" +
"\vparse_error\x18\x02 \x01(\tR\n" +
"parseError\x12%\n" +
"\x0esemantic_error\x18\x03 \x01(\tR\rsemanticError\"t\n" +
"\x0esemantic_error\x18\x03 \x01(\tR\rsemanticError\"\x15\n" +
"\x13ReloadConfigRequest\"\x91\x01\n" +
"\x14ReloadConfigResponse\x12\x0e\n" +
"\x02ok\x18\x01 \x01(\bR\x02ok\x12\x1f\n" +
"\vparse_error\x18\x02 \x01(\tR\n" +
"parseError\x12%\n" +
"\x0esemantic_error\x18\x03 \x01(\tR\rsemanticError\x12!\n" +
"\freload_error\x18\x04 \x01(\tR\vreloadError\"t\n" +
"\x10SetWeightRequest\x12\x1a\n" +
"\bfrontend\x18\x01 \x01(\tR\bfrontend\x12\x12\n" +
"\x04pool\x18\x02 \x01(\tR\x04pool\x12\x18\n" +
@@ -1723,7 +1834,7 @@ const file_proto_maglev_proto_rawDesc = "" +
"\x03log\x18\x01 \x01(\v2\x10.maglev.LogEventH\x00R\x03log\x120\n" +
"\abackend\x18\x02 \x01(\v2\x14.maglev.BackendEventH\x00R\abackend\x123\n" +
"\bfrontend\x18\x03 \x01(\v2\x15.maglev.FrontendEventH\x00R\bfrontendB\a\n" +
"\x05event2\x87\a\n" +
"\x05event2\xd2\a\n" +
"\x06Maglev\x12L\n" +
"\rListFrontends\x12\x1c.maglev.ListFrontendsRequest\x1a\x1d.maglev.ListFrontendsResponse\x12?\n" +
"\vGetFrontend\x12\x1a.maglev.GetFrontendRequest\x1a\x14.maglev.FrontendInfo\x12I\n" +
@@ -1738,7 +1849,8 @@ const file_proto_maglev_proto_rawDesc = "" +
"\x0eGetHealthCheck\x12\x1d.maglev.GetHealthCheckRequest\x1a\x17.maglev.HealthCheckInfo\x12N\n" +
"\x1cSetFrontendPoolBackendWeight\x12\x18.maglev.SetWeightRequest\x1a\x14.maglev.FrontendInfo\x124\n" +
"\vWatchEvents\x12\x14.maglev.WatchRequest\x1a\r.maglev.Event0\x01\x12F\n" +
"\vCheckConfig\x12\x1a.maglev.CheckConfigRequest\x1a\x1b.maglev.CheckConfigResponseB.Z,git.ipng.ch/ipng/vpp-maglev/internal/grpcapib\x06proto3"
"\vCheckConfig\x12\x1a.maglev.CheckConfigRequest\x1a\x1b.maglev.CheckConfigResponse\x12I\n" +
"\fReloadConfig\x12\x1b.maglev.ReloadConfigRequest\x1a\x1c.maglev.ReloadConfigResponseB.Z,git.ipng.ch/ipng/vpp-maglev/internal/grpcapib\x06proto3"
var (
file_proto_maglev_proto_rawDescOnce sync.Once
@@ -1752,7 +1864,7 @@ func file_proto_maglev_proto_rawDescGZIP() []byte {
return file_proto_maglev_proto_rawDescData
}
var file_proto_maglev_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
var file_proto_maglev_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
var file_proto_maglev_proto_goTypes = []any{
(*ListFrontendsRequest)(nil), // 0: maglev.ListFrontendsRequest
(*GetFrontendRequest)(nil), // 1: maglev.GetFrontendRequest
@@ -1763,36 +1875,38 @@ var file_proto_maglev_proto_goTypes = []any{
(*GetHealthCheckRequest)(nil), // 6: maglev.GetHealthCheckRequest
(*CheckConfigRequest)(nil), // 7: maglev.CheckConfigRequest
(*CheckConfigResponse)(nil), // 8: maglev.CheckConfigResponse
(*SetWeightRequest)(nil), // 9: maglev.SetWeightRequest
(*WatchRequest)(nil), // 10: maglev.WatchRequest
(*ListFrontendsResponse)(nil), // 11: maglev.ListFrontendsResponse
(*PoolBackendInfo)(nil), // 12: maglev.PoolBackendInfo
(*PoolInfo)(nil), // 13: maglev.PoolInfo
(*FrontendInfo)(nil), // 14: maglev.FrontendInfo
(*ListBackendsResponse)(nil), // 15: maglev.ListBackendsResponse
(*ListHealthChecksResponse)(nil), // 16: maglev.ListHealthChecksResponse
(*HTTPCheckParams)(nil), // 17: maglev.HTTPCheckParams
(*TCPCheckParams)(nil), // 18: maglev.TCPCheckParams
(*HealthCheckInfo)(nil), // 19: maglev.HealthCheckInfo
(*BackendInfo)(nil), // 20: maglev.BackendInfo
(*TransitionRecord)(nil), // 21: maglev.TransitionRecord
(*LogAttr)(nil), // 22: maglev.LogAttr
(*LogEvent)(nil), // 23: maglev.LogEvent
(*BackendEvent)(nil), // 24: maglev.BackendEvent
(*FrontendEvent)(nil), // 25: maglev.FrontendEvent
(*Event)(nil), // 26: maglev.Event
(*ReloadConfigRequest)(nil), // 9: maglev.ReloadConfigRequest
(*ReloadConfigResponse)(nil), // 10: maglev.ReloadConfigResponse
(*SetWeightRequest)(nil), // 11: maglev.SetWeightRequest
(*WatchRequest)(nil), // 12: maglev.WatchRequest
(*ListFrontendsResponse)(nil), // 13: maglev.ListFrontendsResponse
(*PoolBackendInfo)(nil), // 14: maglev.PoolBackendInfo
(*PoolInfo)(nil), // 15: maglev.PoolInfo
(*FrontendInfo)(nil), // 16: maglev.FrontendInfo
(*ListBackendsResponse)(nil), // 17: maglev.ListBackendsResponse
(*ListHealthChecksResponse)(nil), // 18: maglev.ListHealthChecksResponse
(*HTTPCheckParams)(nil), // 19: maglev.HTTPCheckParams
(*TCPCheckParams)(nil), // 20: maglev.TCPCheckParams
(*HealthCheckInfo)(nil), // 21: maglev.HealthCheckInfo
(*BackendInfo)(nil), // 22: maglev.BackendInfo
(*TransitionRecord)(nil), // 23: maglev.TransitionRecord
(*LogAttr)(nil), // 24: maglev.LogAttr
(*LogEvent)(nil), // 25: maglev.LogEvent
(*BackendEvent)(nil), // 26: maglev.BackendEvent
(*FrontendEvent)(nil), // 27: maglev.FrontendEvent
(*Event)(nil), // 28: maglev.Event
}
var file_proto_maglev_proto_depIdxs = []int32{
12, // 0: maglev.PoolInfo.backends:type_name -> maglev.PoolBackendInfo
13, // 1: maglev.FrontendInfo.pools:type_name -> maglev.PoolInfo
17, // 2: maglev.HealthCheckInfo.http:type_name -> maglev.HTTPCheckParams
18, // 3: maglev.HealthCheckInfo.tcp:type_name -> maglev.TCPCheckParams
21, // 4: maglev.BackendInfo.transitions:type_name -> maglev.TransitionRecord
22, // 5: maglev.LogEvent.attrs:type_name -> maglev.LogAttr
21, // 6: maglev.BackendEvent.transition:type_name -> maglev.TransitionRecord
23, // 7: maglev.Event.log:type_name -> maglev.LogEvent
24, // 8: maglev.Event.backend:type_name -> maglev.BackendEvent
25, // 9: maglev.Event.frontend:type_name -> maglev.FrontendEvent
14, // 0: maglev.PoolInfo.backends:type_name -> maglev.PoolBackendInfo
15, // 1: maglev.FrontendInfo.pools:type_name -> maglev.PoolInfo
19, // 2: maglev.HealthCheckInfo.http:type_name -> maglev.HTTPCheckParams
20, // 3: maglev.HealthCheckInfo.tcp:type_name -> maglev.TCPCheckParams
23, // 4: maglev.BackendInfo.transitions:type_name -> maglev.TransitionRecord
24, // 5: maglev.LogEvent.attrs:type_name -> maglev.LogAttr
23, // 6: maglev.BackendEvent.transition:type_name -> maglev.TransitionRecord
25, // 7: maglev.Event.log:type_name -> maglev.LogEvent
26, // 8: maglev.Event.backend:type_name -> maglev.BackendEvent
27, // 9: maglev.Event.frontend:type_name -> maglev.FrontendEvent
0, // 10: maglev.Maglev.ListFrontends:input_type -> maglev.ListFrontendsRequest
1, // 11: maglev.Maglev.GetFrontend:input_type -> maglev.GetFrontendRequest
2, // 12: maglev.Maglev.ListBackends:input_type -> maglev.ListBackendsRequest
@@ -1803,24 +1917,26 @@ var file_proto_maglev_proto_depIdxs = []int32{
4, // 17: maglev.Maglev.DisableBackend:input_type -> maglev.BackendRequest
5, // 18: maglev.Maglev.ListHealthChecks:input_type -> maglev.ListHealthChecksRequest
6, // 19: maglev.Maglev.GetHealthCheck:input_type -> maglev.GetHealthCheckRequest
9, // 20: maglev.Maglev.SetFrontendPoolBackendWeight:input_type -> maglev.SetWeightRequest
10, // 21: maglev.Maglev.WatchEvents:input_type -> maglev.WatchRequest
11, // 20: maglev.Maglev.SetFrontendPoolBackendWeight:input_type -> maglev.SetWeightRequest
12, // 21: maglev.Maglev.WatchEvents:input_type -> maglev.WatchRequest
7, // 22: maglev.Maglev.CheckConfig:input_type -> maglev.CheckConfigRequest
11, // 23: maglev.Maglev.ListFrontends:output_type -> maglev.ListFrontendsResponse
14, // 24: maglev.Maglev.GetFrontend:output_type -> maglev.FrontendInfo
15, // 25: maglev.Maglev.ListBackends:output_type -> maglev.ListBackendsResponse
20, // 26: maglev.Maglev.GetBackend:output_type -> maglev.BackendInfo
20, // 27: maglev.Maglev.PauseBackend:output_type -> maglev.BackendInfo
20, // 28: maglev.Maglev.ResumeBackend:output_type -> maglev.BackendInfo
20, // 29: maglev.Maglev.EnableBackend:output_type -> maglev.BackendInfo
20, // 30: maglev.Maglev.DisableBackend:output_type -> maglev.BackendInfo
16, // 31: maglev.Maglev.ListHealthChecks:output_type -> maglev.ListHealthChecksResponse
19, // 32: maglev.Maglev.GetHealthCheck:output_type -> maglev.HealthCheckInfo
14, // 33: maglev.Maglev.SetFrontendPoolBackendWeight:output_type -> maglev.FrontendInfo
26, // 34: maglev.Maglev.WatchEvents:output_type -> maglev.Event
8, // 35: maglev.Maglev.CheckConfig:output_type -> maglev.CheckConfigResponse
23, // [23:36] is the sub-list for method output_type
10, // [10:23] is the sub-list for method input_type
9, // 23: maglev.Maglev.ReloadConfig:input_type -> maglev.ReloadConfigRequest
13, // 24: maglev.Maglev.ListFrontends:output_type -> maglev.ListFrontendsResponse
16, // 25: maglev.Maglev.GetFrontend:output_type -> maglev.FrontendInfo
17, // 26: maglev.Maglev.ListBackends:output_type -> maglev.ListBackendsResponse
22, // 27: maglev.Maglev.GetBackend:output_type -> maglev.BackendInfo
22, // 28: maglev.Maglev.PauseBackend:output_type -> maglev.BackendInfo
22, // 29: maglev.Maglev.ResumeBackend:output_type -> maglev.BackendInfo
22, // 30: maglev.Maglev.EnableBackend:output_type -> maglev.BackendInfo
22, // 31: maglev.Maglev.DisableBackend:output_type -> maglev.BackendInfo
18, // 32: maglev.Maglev.ListHealthChecks:output_type -> maglev.ListHealthChecksResponse
21, // 33: maglev.Maglev.GetHealthCheck:output_type -> maglev.HealthCheckInfo
16, // 34: maglev.Maglev.SetFrontendPoolBackendWeight:output_type -> maglev.FrontendInfo
28, // 35: maglev.Maglev.WatchEvents:output_type -> maglev.Event
8, // 36: maglev.Maglev.CheckConfig:output_type -> maglev.CheckConfigResponse
10, // 37: maglev.Maglev.ReloadConfig:output_type -> maglev.ReloadConfigResponse
24, // [24:38] is the sub-list for method output_type
10, // [10:24] is the sub-list for method input_type
10, // [10:10] is the sub-list for extension type_name
10, // [10:10] is the sub-list for extension extendee
0, // [0:10] is the sub-list for field type_name
@@ -1831,8 +1947,8 @@ func file_proto_maglev_proto_init() {
if File_proto_maglev_proto != nil {
return
}
file_proto_maglev_proto_msgTypes[10].OneofWrappers = []any{}
file_proto_maglev_proto_msgTypes[26].OneofWrappers = []any{
file_proto_maglev_proto_msgTypes[12].OneofWrappers = []any{}
file_proto_maglev_proto_msgTypes[28].OneofWrappers = []any{
(*Event_Log)(nil),
(*Event_Backend)(nil),
(*Event_Frontend)(nil),
@@ -1843,7 +1959,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: 27,
NumMessages: 29,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -32,6 +32,7 @@ const (
Maglev_SetFrontendPoolBackendWeight_FullMethodName = "/maglev.Maglev/SetFrontendPoolBackendWeight"
Maglev_WatchEvents_FullMethodName = "/maglev.Maglev/WatchEvents"
Maglev_CheckConfig_FullMethodName = "/maglev.Maglev/CheckConfig"
Maglev_ReloadConfig_FullMethodName = "/maglev.Maglev/ReloadConfig"
)
// MaglevClient is the client API for Maglev service.
@@ -53,6 +54,7 @@ type MaglevClient interface {
SetFrontendPoolBackendWeight(ctx context.Context, in *SetWeightRequest, opts ...grpc.CallOption) (*FrontendInfo, error)
WatchEvents(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error)
CheckConfig(ctx context.Context, in *CheckConfigRequest, opts ...grpc.CallOption) (*CheckConfigResponse, error)
ReloadConfig(ctx context.Context, in *ReloadConfigRequest, opts ...grpc.CallOption) (*ReloadConfigResponse, error)
}
type maglevClient struct {
@@ -202,6 +204,16 @@ func (c *maglevClient) CheckConfig(ctx context.Context, in *CheckConfigRequest,
return out, nil
}
func (c *maglevClient) ReloadConfig(ctx context.Context, in *ReloadConfigRequest, opts ...grpc.CallOption) (*ReloadConfigResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ReloadConfigResponse)
err := c.cc.Invoke(ctx, Maglev_ReloadConfig_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// MaglevServer is the server API for Maglev service.
// All implementations must embed UnimplementedMaglevServer
// for forward compatibility.
@@ -221,6 +233,7 @@ type MaglevServer interface {
SetFrontendPoolBackendWeight(context.Context, *SetWeightRequest) (*FrontendInfo, error)
WatchEvents(*WatchRequest, grpc.ServerStreamingServer[Event]) error
CheckConfig(context.Context, *CheckConfigRequest) (*CheckConfigResponse, error)
ReloadConfig(context.Context, *ReloadConfigRequest) (*ReloadConfigResponse, error)
mustEmbedUnimplementedMaglevServer()
}
@@ -270,6 +283,9 @@ func (UnimplementedMaglevServer) WatchEvents(*WatchRequest, grpc.ServerStreaming
func (UnimplementedMaglevServer) CheckConfig(context.Context, *CheckConfigRequest) (*CheckConfigResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CheckConfig not implemented")
}
func (UnimplementedMaglevServer) ReloadConfig(context.Context, *ReloadConfigRequest) (*ReloadConfigResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ReloadConfig not implemented")
}
func (UnimplementedMaglevServer) mustEmbedUnimplementedMaglevServer() {}
func (UnimplementedMaglevServer) testEmbeddedByValue() {}
@@ -518,6 +534,24 @@ func _Maglev_CheckConfig_Handler(srv interface{}, ctx context.Context, dec func(
return interceptor(ctx, in, info, handler)
}
func _Maglev_ReloadConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReloadConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaglevServer).ReloadConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maglev_ReloadConfig_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).ReloadConfig(ctx, req.(*ReloadConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
// Maglev_ServiceDesc is the grpc.ServiceDesc for Maglev service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -573,6 +607,10 @@ var Maglev_ServiceDesc = grpc.ServiceDesc{
MethodName: "CheckConfig",
Handler: _Maglev_CheckConfig_Handler,
},
{
MethodName: "ReloadConfig",
Handler: _Maglev_ReloadConfig_Handler,
},
},
Streams: []grpc.StreamDesc{
{

View File

@@ -204,12 +204,57 @@ func (s *Server) WatchEvents(req *WatchRequest, stream Maglev_WatchEventsServer)
// CheckConfig reads and validates the configuration file, returning a
// structured result that distinguishes YAML parse errors from semantic errors.
func (s *Server) CheckConfig(_ context.Context, _ *CheckConfigRequest) (*CheckConfigResponse, error) {
slog.Info("config-check-start", "path", s.configPath)
_, result := config.Check(s.configPath)
return &CheckConfigResponse{
resp := &CheckConfigResponse{
Ok: result.OK(),
ParseError: result.ParseError,
SemanticError: result.SemanticError,
}, nil
}
if result.OK() {
slog.Info("config-check-done", "result", "ok")
} else if result.ParseError != "" {
slog.Info("config-check-done", "result", "failed", "type", "parse", "err", result.ParseError)
} else {
slog.Info("config-check-done", "result", "failed", "type", "semantic", "err", result.SemanticError)
}
return resp, nil
}
// ReloadConfig checks the configuration file and, if valid, applies it to the
// running checker. This is the same code path used by SIGHUP.
func (s *Server) ReloadConfig(_ context.Context, _ *ReloadConfigRequest) (*ReloadConfigResponse, error) {
return s.doReloadConfig(), nil
}
// TriggerReload performs a config check and reload. Intended for use by the
// SIGHUP handler so that signals and gRPC share the same code path.
func (s *Server) TriggerReload() {
s.doReloadConfig()
}
func (s *Server) doReloadConfig() *ReloadConfigResponse {
slog.Info("config-reload-start")
newCfg, result := config.Check(s.configPath)
if !result.OK() {
if result.ParseError != "" {
slog.Error("config-check-failed", "type", "parse", "err", result.ParseError)
} else {
slog.Error("config-check-failed", "type", "semantic", "err", result.SemanticError)
}
return &ReloadConfigResponse{
ParseError: result.ParseError,
SemanticError: result.SemanticError,
}
}
if err := s.checker.Reload(s.ctx, newCfg); err != nil {
slog.Error("checker-reload-error", "err", err)
return &ReloadConfigResponse{
ReloadError: err.Error(),
}
}
slog.Info("config-reload-done", "frontends", len(newCfg.Frontends))
return &ReloadConfigResponse{Ok: true}
}
// ---- conversion helpers ----------------------------------------------------

View File

@@ -19,6 +19,7 @@ service Maglev {
rpc SetFrontendPoolBackendWeight(SetWeightRequest) returns (FrontendInfo);
rpc WatchEvents(WatchRequest) returns (stream Event);
rpc CheckConfig(CheckConfigRequest) returns (CheckConfigResponse);
rpc ReloadConfig(ReloadConfigRequest) returns (ReloadConfigResponse);
}
// ---- requests ---------------------------------------------------------------
@@ -53,6 +54,15 @@ message CheckConfigResponse {
string semantic_error = 3; // set when YAML is valid but semantically incorrect
}
message ReloadConfigRequest {}
message ReloadConfigResponse {
bool ok = 1;
string parse_error = 2; // set when YAML cannot be read or parsed
string semantic_error = 3; // set when YAML is valid but semantically incorrect
string reload_error = 4; // set when config is valid but the reload itself failed
}
message SetWeightRequest {
string frontend = 1;
string pool = 2;