Skip to content

Add Environment Variable options to flags managed in exporter-toolkit #607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 25, 2024
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ usage: nginx-prometheus-exporter [<flags>]

Flags:
-h, --[no-]help Show context-sensitive help (also try --help-long and --help-man).
--[no-]web.systemd-socket Use systemd socket activation listeners instead
of port listeners (Linux only). ($SYSTEMD_SOCKET)
--web.listen-address=:9113 ...
Addresses on which to expose metrics and web interface. Repeatable for multiple addresses.
--web.config.file="" Path to configuration file that can enable TLS or authentication. See: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md
Addresses on which to expose metrics and web interface. Repeatable for multiple addresses. ($LISTEN_ADDRESS)
--web.config.file="" Path to configuration file that can enable TLS or authentication. See: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md ($CONFIG_FILE)
--web.telemetry-path="/metrics"
Path under which to expose metrics. ($TELEMETRY_PATH)
--[no-]nginx.plus Start the exporter for NGINX Plus. By default, the exporter is started for NGINX. ($NGINX_PLUS)
Expand Down
24 changes: 24 additions & 0 deletions exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ func main() {
flag.AddFlags(kingpin.CommandLine, promlogConfig)
kingpin.Version(version.Print(exporterName))
kingpin.HelpFlag.Short('h')

addMissingEnvironmentFlags(kingpin.CommandLine)

kingpin.Parse()
logger := promlog.New(promlogConfig)

Expand Down Expand Up @@ -281,3 +284,24 @@ func cloneRequest(req *http.Request) *http.Request {
}
return r
}

// addMissingEnvironmentFlags sets Envar on any flag which has
// the "web." prefix which doesn't already have an Envar set
func addMissingEnvironmentFlags(ka *kingpin.Application) {
for _, f := range ka.Model().FlagGroupModel.Flags {
if strings.HasPrefix(f.Name, "web.") && f.Envar == "" {
flag := ka.GetFlag(f.Name)
if flag != nil {
flag.Envar(convertFlagToEnvar(strings.TrimPrefix(f.Name, "web.")))
}
}
}
}

func convertFlagToEnvar(f string) string {
env := strings.ToUpper(f)
for _, s := range []string{"-", "."} {
env = strings.ReplaceAll(env, s, "_")
}
return env
}
61 changes: 61 additions & 0 deletions exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"reflect"
"testing"
"time"

"github.com/alecthomas/kingpin/v2"
"github.com/prometheus/exporter-toolkit/web/kingpinflag"
)

func TestParsePositiveDuration(t *testing.T) {
Expand Down Expand Up @@ -103,3 +106,61 @@ func TestParseUnixSocketAddress(t *testing.T) {
})
}
}

func TestAddMissingEnvironmentFlags(t *testing.T) {
expectedMatches := map[string]string{
"non-matching-flag": "",
"web.missing-env": "MISSING_ENV",
"web.has-env": "HAS_ENV_ALREADY",
"web.listen-address": "LISTEN_ADDRESS",
"web.config.file": "CONFIG_FILE",
}
kingpinflag.AddFlags(kingpin.CommandLine, ":9113")
kingpin.Flag("non-matching-flag", "").String()
kingpin.Flag("web.missing-env", "").String()
kingpin.Flag("web.has-env", "").Envar("HAS_ENV_ALREADY").String()
addMissingEnvironmentFlags(kingpin.CommandLine)

// using Envar() on a flag returned from GetFlag()
// adds an additional flag, which is processed correctly
// at runtime but means that we need to check for a match
// instead of checking the envar of each matching flag name
for k, v := range expectedMatches {
matched := false
for _, f := range kingpin.CommandLine.Model().FlagGroupModel.Flags {
if f.Name == k && f.Envar == v {
matched = true
}
}
if !matched {
t.Errorf("missing %s envar for %s", v, k)
}
}
}

func TestConvertFlagToEnvar(t *testing.T) {
cases := []struct {
input string
output string
}{
{
input: "dot.separate",
output: "DOT_SEPARATE",
},
{
input: "underscore_separate",
output: "UNDERSCORE_SEPARATE",
},
{
input: "mixed_separate_options",
output: "MIXED_SEPARATE_OPTIONS",
},
}

for _, c := range cases {
res := convertFlagToEnvar(c.input)
if res != c.output {
t.Errorf("expected %s to resolve to %s but got %s", c.input, c.output, res)
}
}
}