From 8db1d1bcd26db21ce61005414d57f4a6e2a740c3 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 11 Oct 2023 15:40:36 +0200 Subject: [PATCH] Config:Add getter to make the config globally accessible - Move it to new package to avoid "import cycle" - Dont pass it as param to functions --- cmd/icinga-notifications-daemon/main.go | 7 ++- internal/channel/channel.go | 4 +- internal/daemonConfig/config.go | 77 +++++++++++++++++++++++++ internal/incident/incident.go | 4 +- internal/incident/incidents.go | 3 +- internal/listener/listener.go | 14 ++--- 6 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 internal/daemonConfig/config.go diff --git a/cmd/icinga-notifications-daemon/main.go b/cmd/icinga-notifications-daemon/main.go index d3a56b361..aeecc03b3 100644 --- a/cmd/icinga-notifications-daemon/main.go +++ b/cmd/icinga-notifications-daemon/main.go @@ -7,6 +7,7 @@ import ( "github.com/icinga/icinga-notifications/internal" "github.com/icinga/icinga-notifications/internal/channel" "github.com/icinga/icinga-notifications/internal/config" + "github.com/icinga/icinga-notifications/internal/daemonConfig" "github.com/icinga/icinga-notifications/internal/listener" "github.com/icinga/icinga-notifications/pkg/plugin" "github.com/icinga/icingadb/pkg/icingadb" @@ -44,12 +45,14 @@ func main() { os.Exit(1) } - conf, err := config.FromFile(configPath) + err := daemonConfig.Load(configPath) if err != nil { _, _ = fmt.Fprintln(os.Stderr, "cannot load config:", err) os.Exit(1) } + conf := daemonConfig.Config() + logs, err := logging.NewLogging( "icinga-notifications", conf.Logging.Level, @@ -85,7 +88,7 @@ func main() { go runtimeConfig.PeriodicUpdates(context.TODO(), 1*time.Second) - if err := listener.NewListener(db, conf, runtimeConfig, logs).Run(); err != nil { + if err := listener.NewListener(db, runtimeConfig, logs).Run(); err != nil { panic(err) } } diff --git a/internal/channel/channel.go b/internal/channel/channel.go index a21df048f..d27f1c5e3 100644 --- a/internal/channel/channel.go +++ b/internal/channel/channel.go @@ -4,6 +4,7 @@ import ( "bufio" "errors" "fmt" + "github.com/icinga/icinga-notifications/internal/daemonConfig" "github.com/icinga/icinga-notifications/pkg/rpc" "go.uber.org/zap" "io" @@ -60,8 +61,7 @@ func (p *Plugin) Start(pluginName string) error { p.mu.Lock() defer p.mu.Unlock() - ChannelPluginDir := "/usr/libexec/icinga-notifications/channel" - cmd := exec.Command(filepath.Join(ChannelPluginDir, pluginName)) + cmd := exec.Command(filepath.Join(daemonConfig.Config().ChannelPluginDir, pluginName)) writer, err := cmd.StdinPipe() if err != nil { diff --git a/internal/daemonConfig/config.go b/internal/daemonConfig/config.go new file mode 100644 index 000000000..f3d01aede --- /dev/null +++ b/internal/daemonConfig/config.go @@ -0,0 +1,77 @@ +package daemonConfig + +import ( + "errors" + "github.com/creasty/defaults" + "github.com/goccy/go-yaml" + icingadbConfig "github.com/icinga/icingadb/pkg/config" + "os" +) + +type ConfigFile struct { + Listen string `yaml:"listen" default:"localhost:5680"` + DebugPassword string `yaml:"debug-password"` + ChannelPluginDir string `yaml:"channel-plugin-dir" default:"/usr/libexec/icinga-notifications/channel"` + Icingaweb2URL string `yaml:"icingaweb2-url"` + Database icingadbConfig.Database `yaml:"database"` + Logging icingadbConfig.Logging `yaml:"logging"` +} + +var config *ConfigFile + +// Load loads the daemon config from given path. Call it only once when starting the daemon. +func Load(path string) error { + if config != nil { + return errors.New("config already set") + } + + cfg, err := fromFile(path) + if err != nil { + return err + } + + config = cfg + + return nil +} + +// Config returns the config that was loaded while starting the daemon +func Config() *ConfigFile { + return config +} + +func fromFile(path string) (*ConfigFile, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer func() { _ = f.Close() }() + + var c ConfigFile + + if err := defaults.Set(&c); err != nil { + return nil, err + } + + d := yaml.NewDecoder(f) + if err := d.Decode(&c); err != nil { + return nil, err + } + + if err := c.Validate(); err != nil { + return nil, err + } + + return &c, nil +} + +func (c *ConfigFile) Validate() error { + if err := c.Database.Validate(); err != nil { + return err + } + if err := c.Logging.Validate(); err != nil { + return err + } + + return nil +} diff --git a/internal/incident/incident.go b/internal/incident/incident.go index afae9cf96..e5086aa2e 100644 --- a/internal/incident/incident.go +++ b/internal/incident/incident.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/icinga/icinga-notifications/internal/config" "github.com/icinga/icinga-notifications/internal/contracts" + "github.com/icinga/icinga-notifications/internal/daemonConfig" "github.com/icinga/icinga-notifications/internal/event" "github.com/icinga/icinga-notifications/internal/object" "github.com/icinga/icinga-notifications/internal/recipient" @@ -38,7 +39,6 @@ type Incident struct { db *icingadb.DB logger *zap.SugaredLogger runtimeConfig *config.RuntimeConfig - configFile *config.ConfigFile sync.Mutex } @@ -450,7 +450,7 @@ func (i *Incident) notifyContacts(ctx context.Context, tx *sqlx.Tx, ev *event.Ev continue } - err = ch.Plugin.SendNotification(contact, i, ev, i.configFile.Icingaweb2URL) + err = ch.Plugin.SendNotification(contact, i, ev, daemonConfig.Config().Icingaweb2URL) if err != nil { i.logger.Errorw("Failed to send notification via channel plugin", zap.String("type", chType), zap.Error(err)) continue diff --git a/internal/incident/incidents.go b/internal/incident/incidents.go index 64021c929..0d1bbe5dc 100644 --- a/internal/incident/incidents.go +++ b/internal/incident/incidents.go @@ -20,7 +20,7 @@ var ( func GetCurrent( ctx context.Context, db *icingadb.DB, obj *object.Object, logger *logging.Logger, runtimeConfig *config.RuntimeConfig, - configFile *config.ConfigFile, create bool, + create bool, ) (*Incident, bool, error) { currentIncidentsMu.Lock() defer currentIncidentsMu.Unlock() @@ -35,7 +35,6 @@ func GetCurrent( db: db, logger: logger.With(zap.String("object", obj.DisplayName())), runtimeConfig: runtimeConfig, - configFile: configFile, Recipients: map[recipient.Key]*RecipientState{}, EscalationState: map[escalationID]*EscalationState{}, Rules: map[ruleID]struct{}{}, diff --git a/internal/listener/listener.go b/internal/listener/listener.go index 98968a3fe..7d214d3cf 100644 --- a/internal/listener/listener.go +++ b/internal/listener/listener.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/icinga/icinga-notifications/internal" "github.com/icinga/icinga-notifications/internal/config" + "github.com/icinga/icinga-notifications/internal/daemonConfig" "github.com/icinga/icinga-notifications/internal/event" "github.com/icinga/icinga-notifications/internal/incident" "github.com/icinga/icinga-notifications/internal/object" @@ -18,7 +19,6 @@ import ( ) type Listener struct { - configFile *config.ConfigFile db *icingadb.DB logger *logging.Logger runtimeConfig *config.RuntimeConfig @@ -27,9 +27,8 @@ type Listener struct { mux http.ServeMux } -func NewListener(db *icingadb.DB, configFile *config.ConfigFile, runtimeConfig *config.RuntimeConfig, logs *logging.Logging) *Listener { +func NewListener(db *icingadb.DB, runtimeConfig *config.RuntimeConfig, logs *logging.Logging) *Listener { l := &Listener{ - configFile: configFile, db: db, logger: logs.GetChildLogger("listener"), logs: logs, @@ -47,8 +46,9 @@ func (l *Listener) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } func (l *Listener) Run() error { - l.logger.Infof("Starting listener on http://%s", l.configFile.Listen) - return http.ListenAndServe(l.configFile.Listen, l) + listenAddr := daemonConfig.Config().Listen + l.logger.Infof("Starting listener on http://%s", listenAddr) + return http.ListenAndServe(listenAddr, l) } func (l *Listener) ProcessEvent(w http.ResponseWriter, req *http.Request) { @@ -121,7 +121,7 @@ func (l *Listener) ProcessEvent(w http.ResponseWriter, req *http.Request) { } createIncident := ev.Severity != event.SeverityNone && ev.Severity != event.SeverityOK - currentIncident, created, err := incident.GetCurrent(ctx, l.db, obj, l.logs.GetChildLogger("incident"), l.runtimeConfig, l.configFile, createIncident) + currentIncident, created, err := incident.GetCurrent(ctx, l.db, obj, l.logs.GetChildLogger("incident"), l.runtimeConfig, createIncident) if err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintln(w, err) @@ -177,7 +177,7 @@ func (l *Listener) ProcessEvent(w http.ResponseWriter, req *http.Request) { // checkDebugPassword checks if the valid debug password was provided. If there is no password configured or the // supplied password is incorrect, it sends an error code and returns false. True is returned if access is allowed. func (l *Listener) checkDebugPassword(w http.ResponseWriter, r *http.Request) bool { - expectedPassword := l.configFile.DebugPassword + expectedPassword := daemonConfig.Config().DebugPassword if expectedPassword == "" { w.WriteHeader(http.StatusForbidden) _, _ = fmt.Fprintln(w, "config dump disables, no debug-password set in config")