cmd: add CLI
the only command added is 'watch', which serves the only currently expected functionality of the binary for this lib.
This commit is contained in:
parent
16ab40f44d
commit
296c7b8d5e
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"git.st8l.com/luxolus/kdnotify/buildinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rootHelp = "A program for responding to Keepalived's VRRP notify events"
|
||||||
|
rootHelpLong = rootHelp + `
|
||||||
|
|
||||||
|
This is meant to be used with the 'vrrp_notify <PIPE>' option, see
|
||||||
|
man:keepalived.conf(5) for more.`
|
||||||
|
)
|
||||||
|
|
||||||
|
func Execute() error {
|
||||||
|
return rootCmd().Execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
func rootCmd() *cobra.Command {
|
||||||
|
root := &cobra.Command{
|
||||||
|
Use: "kdnotify <subcommand ...>",
|
||||||
|
Short: rootHelp,
|
||||||
|
Long: rootHelpLong,
|
||||||
|
Version: fullVersion(),
|
||||||
|
}
|
||||||
|
|
||||||
|
root.AddCommand(watchCmd())
|
||||||
|
|
||||||
|
root.PersistentFlags().String("log-level", "INFO", "Set program log level. ERROR|WARN|INFO|DEBUG")
|
||||||
|
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullVersion() string {
|
||||||
|
v := build.Version
|
||||||
|
hash := build.ShortHash()
|
||||||
|
features := build.FeaturesList()
|
||||||
|
|
||||||
|
vstr := fmt.Sprintf("%s sha=%s", v, hash)
|
||||||
|
if len(features) > 0 {
|
||||||
|
vstr = vstr + "features=" + strings.Join(features, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return vstr
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"git.st8l.com/luxolus/kdnotify/config"
|
||||||
|
"git.st8l.com/luxolus/kdnotify/handler"
|
||||||
|
"git.st8l.com/luxolus/kdnotify/watcher"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
watchHelp = "Start a watch on the given PIPE, using the provided CONFIG"
|
||||||
|
watchHelpLong = watchHelp + `
|
||||||
|
|
||||||
|
This command creates an IPC FIFO channel at the provided PIPE path.
|
||||||
|
|
||||||
|
It is expected that you hand this named PIPE into Keepalived's
|
||||||
|
vrrp_notify conf option, and arrange for this program to run before
|
||||||
|
Keepalived.
|
||||||
|
|
||||||
|
The configuration file can be used to match events Keepalived produces.
|
||||||
|
There are four fields: TYPE, STATE, INSTANCE and PRIORITY, with the last
|
||||||
|
being optional. You may provide any combination of the above and an action
|
||||||
|
to run when a match occurs.
|
||||||
|
|
||||||
|
A sample configuration:
|
||||||
|
|
||||||
|
---
|
||||||
|
watch:
|
||||||
|
context: # Arbitrary structure you can reference via the 'Cxt' key
|
||||||
|
instance1:
|
||||||
|
ip: 192.168.0.1
|
||||||
|
dev: eth0
|
||||||
|
rules: # List of rules to match on
|
||||||
|
- state: [BACKUP, STOP] # Possible actions: MASTER, BACKUP, STOP
|
||||||
|
instance: instance1 # Refers to the name of a vrrp_instance or vrrp_group in Keepalived
|
||||||
|
exec: /bin/ip addr delete {{.Cxt.instance1.ip}} dev {{.Cxt.instance1.dev}}
|
||||||
|
- state: [MASTER]
|
||||||
|
exec: /bin/logger {{.Event.Instance}} is now MASTER with priority: {{.Event.Priority}}
|
||||||
|
- type: [INSTANCE] # Possible types: INSTANCE, GROUP
|
||||||
|
state: [STOP]
|
||||||
|
exec: /bin/logger {{- vip := index .Cxt .Event.Instance -}}{{vip.ip}}%{{vip.dev}} shutdown`
|
||||||
|
)
|
||||||
|
|
||||||
|
type watchCommand struct {
|
||||||
|
log *zap.SugaredLogger
|
||||||
|
pipe string
|
||||||
|
conffile string
|
||||||
|
config *config.WatchConfig
|
||||||
|
cxt *config.LibCxt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *watchCommand) PreRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
ll, _ := cmd.Flags().GetString("log-level")
|
||||||
|
cfg, _ := cmd.Flags().GetString("config")
|
||||||
|
|
||||||
|
cxt := config.NewLibCxt(ll)
|
||||||
|
log := cxt.Logger.Named("cli.watch").Sugar()
|
||||||
|
|
||||||
|
log.Debugw("reading config file", "config", cfg)
|
||||||
|
conf, err := os.ReadFile(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("loading watcher rules and context")
|
||||||
|
config, err := config.WatchConfigFromYAML(conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.cxt = cxt
|
||||||
|
c.log = log
|
||||||
|
c.conffile = cfg
|
||||||
|
c.config = &config
|
||||||
|
c.pipe = args[0]
|
||||||
|
|
||||||
|
log.Debugw("config loaded",
|
||||||
|
"fifo", c.pipe,
|
||||||
|
"config", c.conffile,
|
||||||
|
"rules", len(c.config.Rules),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *watchCommand) RunE(cmd *cobra.Command, _ []string) error {
|
||||||
|
h := handler.NewVrrp(c.cxt, c.config)
|
||||||
|
w, err := watcher.NewWatcherFifo(c.cxt, c.pipe)
|
||||||
|
if err != nil {
|
||||||
|
c.log.Errorf("unable to initialize watcher", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.log.Infow("starting watch",
|
||||||
|
"fifo", c.pipe,
|
||||||
|
"config", c.conffile,
|
||||||
|
"rules", len(c.config.Rules),
|
||||||
|
)
|
||||||
|
return w.Watch(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchCmd() *cobra.Command {
|
||||||
|
cmd := &watchCommand{}
|
||||||
|
|
||||||
|
watch := &cobra.Command{
|
||||||
|
Use: "watch [-f CONFIG] <PIPE>",
|
||||||
|
Short: watchHelp,
|
||||||
|
Long: watchHelpLong,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
PreRunE: cmd.PreRunE,
|
||||||
|
RunE: cmd.RunE,
|
||||||
|
}
|
||||||
|
|
||||||
|
watch.Flags().StringP("config", "f", "/etc/kdnotify/config.yaml", "Configuration file, see --help for more")
|
||||||
|
|
||||||
|
return watch
|
||||||
|
}
|
Loading…
Reference in New Issue