From f2fd1e25cc99c02eb87fd169d9a282801747eed9 Mon Sep 17 00:00:00 2001 From: Alvar Penning Date: Tue, 25 Nov 2025 13:57:07 +0100 Subject: [PATCH] Environment Variable Configuration Enable configuration via environment variables via the IGL code as also done in Icinga DB. The configuration sections were copied from Icinga DB and slightly adjusted. Fixes #370. --- doc/03-Configuration.md | 52 +++++++++++++++++++++++++++++++++++++-- internal/daemon/config.go | 33 +++++++++++++++++++------ 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/doc/03-Configuration.md b/doc/03-Configuration.md index 09764877..189bec0e 100644 --- a/doc/03-Configuration.md +++ b/doc/03-Configuration.md @@ -4,13 +4,26 @@ The configuration for Icinga Notifications is twofold. The main configuration resides in the database, shared between the Icinga Notifications daemon and Icinga Notifications Web. However, as the Icinga Notifications daemon needs to know how to access this database and some further settings, -it needs its own configuration file as well. +it needs its own configuration as well. -This configuration is stored in `/etc/icinga-notifications/config.yml`. +This configuration may be a YAML file, environment variables, or both. +Environment variables take precedence and override previously defined values from the configuration file. + +The YAML configuration file is stored in `/etc/icinga-notifications/config.yml`. See [config.example.yml](../config.example.yml) for an example configuration. +The following subsections describe the configurations of the various modules. +For the YAML configuration file, each option is written in lowercase, as shown in the tables. +When using environment variables, the variable name is constructed by concatenating `ICINGA_NOTIFICATIONS_`, +the module name in uppercase followed by an underscore, and the option name in uppercase. +The hyphens in the names are to be replaced by underscores. +For example, to set the database host, the `ICINGA_NOTIFICATIONS_DATABASE_HOST` environment variable is used. + ## Top Level Configuration +For YAML configuration, these options are on the top level, not part of a dictionary. +For environment variables, each option is prefixed with `ICINGA_NOTIFICATIONS_`. + ### HTTP API Configuration The HTTP API listener can be used both for submission and for debugging purposes. @@ -38,6 +51,9 @@ It may also be `/usr/lib/icinga-notifications/channels`, depending on the operat Connection configuration for the database where Icinga Notifications stores configuration and historical data. This is also the database used in Icinga Notifications Web to view and work with the data. +For YAML configuration, the options are part of the `database` dictionary. +For environment variables, each option is prefixed with `ICINGA_NOTIFICATIONS_DATABASE_`. + | Option | Description | |----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| | type | **Optional.** Either `mysql` (default) or `pgsql`. | @@ -64,6 +80,9 @@ manual adjustments. Do not change the defaults if you do not have to! +For YAML configuration, the options are part of the `database.options` dictionary. +For environment variables, each option is prefixed with `ICINGA_NOTIFICATIONS_DATABASE_OPTIONS_`. + | Option | Description | |--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| | max_connections | **Optional.** Maximum number of database connections Icinga Notifications is allowed to open in parallel if necessary. Defaults to `16`. | @@ -76,6 +95,9 @@ manual adjustments. Configuration of the logging component used by Icinga Notifications. +For YAML configuration, the options are part of the `logging` dictionary. +For environment variables, each option is prefixed with `ICINGA_NOTIFICATIONS_LOGGING_`. + | Option | Description | |----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | level | **Optional.** Specifies the default logging level. Can be set to `fatal`, `error`, `warn`, `info` or `debug`. Defaults to `info`. | @@ -85,6 +107,32 @@ Configuration of the logging component used by Icinga Notifications. ### Logging Components +The independent components of Icinga Notifications produce log entries. +Each log entry is linked to its component and a log level. + +By default, any log message will be displayed if its log level is at or above the `level` configured above. +However, it is possible to override the log level for each component individually to show more or less information. + +For YAML configuration, the options are part of the `logging.options` dictionary. +For environment variables, `ICINGA_NOTIFICATIONS_LOGGING_OPTIONS` expects a single string of `component:level` pairs joined with `,`. + +The following example would log everything with at least info level, except database and listener entries, where the level is one time raised and one time lowered. + +```yaml +# YAML Configuration File +logging: + level: info + options: + database: error + listener: debug +``` + +``` +# Environment Variables +ICINGA_NOTIFICATIONS_LOGGING_LEVEL=error +ICINGA_NOTIFICATIONS_LOGGING_OPTIONS=database:error,listener:debug +``` + | Component | Description | |-----------------|---------------------------------------------------------------------------| | channel | Notification channels, their configuration and output. | diff --git a/internal/daemon/config.go b/internal/daemon/config.go index f2ab8ab0..4180c87a 100644 --- a/internal/daemon/config.go +++ b/internal/daemon/config.go @@ -17,12 +17,12 @@ const ( ) type ConfigFile struct { - Listen string `yaml:"listen" default:"localhost:5680"` - DebugPassword string `yaml:"debug-password"` - ChannelsDir string `yaml:"channels-dir"` - Icingaweb2URL string `yaml:"icingaweb2-url"` - Database database.Config `yaml:"database"` - Logging logging.Config `yaml:"logging"` + Listen string `yaml:"listen" env:"LISTEN" default:"localhost:5680"` + DebugPassword string `yaml:"debug-password" env:"DEBUG_PASSWORD"` + ChannelsDir string `yaml:"channels-dir" env:"CHANNELS_DIR"` + Icingaweb2URL string `yaml:"icingaweb2-url" env:"ICINGAWEB2_URL"` + Database database.Config `yaml:"database" envPrefix:"DATABASE_"` + Logging logging.Config `yaml:"logging" envPrefix:"LOGGING_"` } // SetDefaults implements the defaults.Setter interface. @@ -59,6 +59,20 @@ type Flags struct { Config string `short:"c" long:"config" description:"path to config file"` } +// GetConfigPath implements [config.Flags]. +func (f Flags) GetConfigPath() string { + if f.Config == "" { + return internal.SysConfDir + "/icinga-notifications/config.yml" + } + + return f.Config +} + +// IsExplicitConfigPath implements [config.Flags]. +func (f Flags) IsExplicitConfigPath() bool { + return f.Config != "" +} + // daemonConfig holds the configuration state as a singleton. // It is initialised by the ParseFlagsAndConfig func and exposed through the Config function. var daemonConfig *ConfigFile @@ -76,7 +90,7 @@ func Config() *ConfigFile { // ParseFlagsAndConfig parses the CLI flags provided to the executable and tries to load the config from the YAML file. // Prints any error during parsing or config loading to os.Stderr and exits. func ParseFlagsAndConfig() { - flags := Flags{Config: internal.SysConfDir + "/icinga-notifications/config.yml"} + var flags Flags if err := config.ParseFlags(&flags); err != nil { if errors.Is(err, config.ErrInvalidArgument) { panic(err) @@ -91,7 +105,10 @@ func ParseFlagsAndConfig() { } daemonConfig = new(ConfigFile) - if err := config.FromYAMLFile(flags.Config, daemonConfig); err != nil { + if err := config.Load(daemonConfig, config.LoadOptions{ + Flags: flags, + EnvOptions: config.EnvOptions{Prefix: "ICINGA_NOTIFICATIONS_"}, + }); err != nil { if errors.Is(err, config.ErrInvalidArgument) { panic(err) }