From 43cab7adc260adcd54ca03e26664bde9274fd799 Mon Sep 17 00:00:00 2001 From: Hector Date: Tue, 20 Jun 2023 20:16:23 +0000 Subject: [PATCH] refactor: replace cli parser with kong (!88) https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/88 --- README.md | 36 ++++++++------ cfg/cfg.go | 128 +++++++++++++++++------------------------------- cfg/settings.go | 10 ++++ go.mod | 4 +- go.sum | 20 ++------ 5 files changed, 81 insertions(+), 117 deletions(-) create mode 100644 cfg/settings.go diff --git a/README.md b/README.md index 5d3b940..33fbd9c 100644 --- a/README.md +++ b/README.md @@ -114,23 +114,29 @@ There are no configuration files. **CLI flags** ``` -usage: exporter [] +🚀 Collect prometheus metrics from a running Fail2Ban instance Flags: - -h, --help Show context-sensitive help (also try --help-long and --help-man). - -v, --version show version info and exit - --collector.f2b.socket="/var/run/fail2ban/fail2ban.sock" - path to the fail2ban server socket - --collector.textfile.directory="" - directory to read text files with metrics from - --web.listen-address=":9191" - address to use for the metrics server - --web.basic-auth.username="" - username to use to protect endpoints with basic auth - --web.basic-auth.password="" - password to use to protect endpoints with basic auth - --collector.f2b.exit-on-socket-connection-error - when set to true the exporter will immediately exit on a fail2ban socket connection error + -h, --help Show context-sensitive help. + -v, --version Show version info and exit + --web.listen-address=":9191" Address to use for the metrics server + ($F2B_WEB_LISTEN_ADDRESS) + --collector.f2b.socket="/var/run/fail2ban/fail2ban.sock" + Path to the fail2ban server socket + ($F2B_COLLECTOR_SOCKET) + --collector.f2b.exit-on-socket-connection-error + When set to true the exporter will immediately + exit on a fail2ban socket connection error + ($F2B_EXIT_ON_SOCKET_CONN_ERROR) + --collector.textfile.directory=STRING + Directory to read text files with metrics from + ($F2B_COLLECTOR_TEXT_PATH) + --web.basic-auth.username=STRING + Username to use to protect endpoints with basic auth + ($F2B_WEB_BASICAUTH_USER) + --web.basic-auth.password=STRING + Password to use to protect endpoints with basic auth + ($F2B_WEB_BASICAUTH_PASS) ``` **Environment variables** diff --git a/cfg/cfg.go b/cfg/cfg.go index f1b3b35..cee729e 100644 --- a/cfg/cfg.go +++ b/cfg/cfg.go @@ -2,108 +2,68 @@ package cfg import ( "fmt" - "github.com/alecthomas/kingpin/v2" "os" + + "github.com/alecthomas/kong" ) -const ( - socketEnvName = "F2B_COLLECTOR_SOCKET" - fileCollectorPathEnvName = "F2B_COLLECTOR_TEXT_PATH" - addressEnvName = "F2B_WEB_LISTEN_ADDRESS" - basicAuthUserEnvName = "F2B_WEB_BASICAUTH_USER" - basicAuthPassEnvName = "F2B_WEB_BASICAUTH_PASS" - exitOnSocketConnErrorEnvName = "F2B_EXIT_ON_SOCKET_CONN_ERROR" -) - -type AppSettings struct { - VersionMode bool - MetricsAddress string - Fail2BanSocketPath string - FileCollectorPath string - BasicAuthProvider *hashedBasicAuth - ExitOnSocketConnError bool -} - -func init() { - kingpin.HelpFlag.Short('h') +var cliStruct struct { + VersionMode bool `name:"version" short:"v" help:"Show version info and exit"` + ServerAddress string `name:"web.listen-address" env:"F2B_WEB_LISTEN_ADDRESS" help:"Address to use for the metrics server" default:"${default_address}"` + F2bSocketPath string `name:"collector.f2b.socket" env:"F2B_COLLECTOR_SOCKET" help:"Path to the fail2ban server socket" default:"${default_socket}"` + ExitOnSocketError bool `name:"collector.f2b.exit-on-socket-connection-error" env:"F2B_EXIT_ON_SOCKET_CONN_ERROR" help:"When set to true the exporter will immediately exit on a fail2ban socket connection error"` + TextFileExporterPath string `name:"collector.textfile.directory" env:"F2B_COLLECTOR_TEXT_PATH" help:"Directory to read text files with metrics from"` + BasicAuthUser string `name:"web.basic-auth.username" env:"F2B_WEB_BASICAUTH_USER" help:"Username to use to protect endpoints with basic auth"` + BasicAuthPass string `name:"web.basic-auth.password" env:"F2B_WEB_BASICAUTH_PASS" help:"Password to use to protect endpoints with basic auth"` } func Parse() *AppSettings { - settings := &AppSettings{} - readParamsFromCli(settings) - settings.validateFlags() + ctx := kong.Parse( + &cliStruct, + kong.Vars{ + "default_socket": "/var/run/fail2ban/fail2ban.sock", + "default_address": ":9191", + }, + kong.Name("fail2ban_exporter"), + kong.Description("🚀 Export prometheus metrics from a running Fail2Ban instance"), + kong.UsageOnError(), + ) + + validateFlags(ctx) + settings := &AppSettings{ + VersionMode: cliStruct.VersionMode, + MetricsAddress: cliStruct.ServerAddress, + Fail2BanSocketPath: cliStruct.F2bSocketPath, + FileCollectorPath: cliStruct.TextFileExporterPath, + ExitOnSocketConnError: cliStruct.ExitOnSocketError, + BasicAuthProvider: newHashedBasicAuth(cliStruct.BasicAuthUser, cliStruct.BasicAuthPass), + } return settings } -func readParamsFromCli(settings *AppSettings) { - versionMode := kingpin. - Flag("version", "show version info and exit"). - Short('v'). - Default("false"). - Bool() - socketPath := kingpin. - Flag("collector.f2b.socket", "path to the fail2ban server socket"). - Default("/var/run/fail2ban/fail2ban.sock"). - Envar(socketEnvName). - String() - fileCollectorPath := kingpin. - Flag("collector.textfile.directory", "directory to read text files with metrics from"). - Default(""). - Envar(fileCollectorPathEnvName). - String() - address := kingpin. - Flag("web.listen-address", "address to use for the metrics server"). - Default(":9191"). - Envar(addressEnvName). - String() - rawBasicAuthUsername := kingpin. - Flag("web.basic-auth.username", "username to use to protect endpoints with basic auth"). - Default(""). - Envar(basicAuthUserEnvName). - String() - rawBasicAuthPassword := kingpin. - Flag("web.basic-auth.password", "password to use to protect endpoints with basic auth"). - Default(""). - Envar(basicAuthPassEnvName). - String() - rawExitOnSocketConnError := kingpin. - Flag("collector.f2b.exit-on-socket-connection-error", "when set to true the exporter will immediately exit on a fail2ban socket connection error"). - Default("false"). - Envar(exitOnSocketConnErrorEnvName). - Bool() - - kingpin.Parse() - - settings.VersionMode = *versionMode - settings.MetricsAddress = *address - settings.Fail2BanSocketPath = *socketPath - settings.FileCollectorPath = *fileCollectorPath - settings.setBasicAuthValues(*rawBasicAuthUsername, *rawBasicAuthPassword) - settings.ExitOnSocketConnError = *rawExitOnSocketConnError -} - -func (settings *AppSettings) setBasicAuthValues(rawUsername, rawPassword string) { - settings.BasicAuthProvider = newHashedBasicAuth(rawUsername, rawPassword) -} - -func (settings *AppSettings) validateFlags() { +func validateFlags(cliCtx *kong.Context) { var flagsValid = true - if !settings.VersionMode { - if settings.Fail2BanSocketPath == "" { - fmt.Println("error: fail2ban socket path must not be blank") + var messages = []string{} + if !cliStruct.VersionMode { + if cliStruct.F2bSocketPath == "" { + messages = append(messages, "error: fail2ban socket path must not be blank") flagsValid = false } - if settings.MetricsAddress == "" { - fmt.Println("error: invalid server address, must not be blank") + if cliStruct.ServerAddress == "" { + messages = append(messages, "error: invalid server address, must not be blank") flagsValid = false } - if (len(settings.BasicAuthProvider.username) > 0) != (len(settings.BasicAuthProvider.password) > 0) { - fmt.Println("error: to enable basic auth both the username and the password must be provided") + if (len(cliStruct.BasicAuthUser) > 0) != (len(cliStruct.BasicAuthPass) > 0) { + messages = append(messages, "error: to enable basic auth both the username and the password must be provided") flagsValid = false } } if !flagsValid { - kingpin.Usage() + cliCtx.PrintUsage(false) + fmt.Println() + for i := 0; i < len(messages); i++ { + fmt.Println(messages[i]) + } os.Exit(1) } } diff --git a/cfg/settings.go b/cfg/settings.go new file mode 100644 index 0000000..160234e --- /dev/null +++ b/cfg/settings.go @@ -0,0 +1,10 @@ +package cfg + +type AppSettings struct { + VersionMode bool + MetricsAddress string + Fail2BanSocketPath string + FileCollectorPath string + BasicAuthProvider *hashedBasicAuth + ExitOnSocketConnError bool +} diff --git a/go.mod b/go.mod index 4ca102c..08458ad 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,13 @@ module gitlab.com/hectorjsmith/fail2ban-prometheus-exporter go 1.20 require ( - github.com/alecthomas/kingpin/v2 v2.3.2 + github.com/alecthomas/kong v0.7.1 github.com/kisielk/og-rek v1.2.0 github.com/nlpodyssey/gopickle v0.2.0 github.com/prometheus/client_golang v1.16.0 ) require ( - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -18,7 +17,6 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.0 // indirect - github.com/xhit/go-str2duration/v2 v2.1.0 // indirect golang.org/x/sys v0.9.0 // indirect google.golang.org/protobuf v1.30.0 // indirect ) diff --git a/go.sum b/go.sum index b63c166..a2b6d91 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,11 @@ -github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= -github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= +github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= +github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= +github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -14,14 +13,13 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/kisielk/og-rek v1.2.0 h1:CTvDIin+YnetsSQAYbe+QNAxXU3B50C5hseEz8xEoJw= github.com/kisielk/og-rek v1.2.0/go.mod h1:6ihsOSzSAxR/65S3Bn9zNihoEqRquhDQZ2c6I2+MG3c= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/nlpodyssey/gopickle v0.2.0 h1:4naD2DVylYJupQLbCQFdwo6yiXEmPyp+0xf5MVlrBDY= github.com/nlpodyssey/gopickle v0.2.0/go.mod h1:YIUwjJ2O7+vnBsxUN+MHAAI3N+adqEGiw+nDpwW95bY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= @@ -30,11 +28,6 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -43,6 +36,3 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=