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:
@@ -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
|
||||
@@ -99,9 +96,10 @@ func buildTree() *Node {
|
||||
}
|
||||
// set frontend <name> pool <pool> backend <name> weight <0-100>
|
||||
setWeightValue := &Node{
|
||||
Word: "<weight>",
|
||||
Help: "weight 0-100",
|
||||
Run: runSetFrontendPoolBackendWeight,
|
||||
Word: "<weight>",
|
||||
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}}
|
||||
setFrontendPoolBackendName := &Node{
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
fmt.Fprintf(ql.rl.Stderr(), "\r\n")
|
||||
if len(candidates) == 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)
|
||||
} else {
|
||||
fmt.Fprintf(ql.rl.Stderr(), " %s\r\n", c.Word)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the '?' from the line and return with cursor one step back.
|
||||
// Emit output. Raw terminal mode requires \r\n.
|
||||
fmt.Fprintf(ql.rl.Stderr(), "\r\n")
|
||||
if len(lines) == 0 {
|
||||
fmt.Fprintf(ql.rl.Stderr(), " <no completions>\r\n")
|
||||
} else {
|
||||
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", 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 step cursor back one position.
|
||||
newLine = append(append([]rune{}, line[:pos-1]...), line[pos:]...)
|
||||
return newLine, pos - 1, true
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
153
cmd/maglevc/tree_test.go
Normal 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])
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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{
|
||||
{
|
||||
|
||||
@@ -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 ----------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user