kdnotify/cmd/watch.go

127 lines
3.2 KiB
Go

/*
* 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
}