From cf04392b13d1ce7e98a6e92be2fe7ca520553188 Mon Sep 17 00:00:00 2001 From: Vincent Composieux Date: Wed, 22 Dec 2021 08:45:08 +0100 Subject: [PATCH 1/7] Fixed GitHub Actions go versions --- .github/workflows/master.yml | 2 +- .github/workflows/tag.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index eeb07bd..8553756 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: [ '1.13', '1.14', '1.15', '1.16', '1.17' ] + go_version: [ '1.14', '1.15', '1.16', '1.17' ] steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v1 diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index bcea083..8034442 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: [ '1.12', '1.13', '1.14', '1.15' ] + go_version: [ '1.14', '1.15', '1.16', '1.17' ] steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 From 8d5586558c4b3695a64eff488ff13fb747f7b087 Mon Sep 17 00:00:00 2001 From: Galorhallen Date: Sat, 25 Dec 2021 22:15:41 +0100 Subject: [PATCH 2/7] Add channel --- internal/pihole/client.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/pihole/client.go b/internal/pihole/client.go index c46bd53..789dd60 100644 --- a/internal/pihole/client.go +++ b/internal/pihole/client.go @@ -18,9 +18,10 @@ import ( // Client struct is a PI-Hole client to request an instance of a PI-Hole ad blocker. type Client struct { - httpClient http.Client - interval time.Duration - config *config.Config + httpClient http.Client + interval time.Duration + config *config.Config + MetricRetrieved chan bool } // NewClient method initializes a new PI-Hole client. From 7009c705bbf1f9eba0ff1eb61490682a222390c3 Mon Sep 17 00:00:00 2001 From: Galorhallen Date: Sun, 26 Dec 2021 23:36:30 +0100 Subject: [PATCH 3/7] Add async metrics fetch for multiple piholes parent 8d5586558c4b3695a64eff488ff13fb747f7b087 author Galorhallen 1640558190 +0100 committer Galorhallen 1640821760 +0100 Add test for multiple pihole Add async mode for multiple piholes Fixed GitHub Actions go versions Add test for multiple pihole Cleanup --- config/configuration.go | 55 +++++++++---- config/configuration_test.go | 154 +++++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 9 ++ internal/pihole/client.go | 61 +++++++++----- internal/server/server.go | 55 ++++++++----- main.go | 6 +- 7 files changed, 283 insertions(+), 59 deletions(-) create mode 100644 config/configuration_test.go diff --git a/config/configuration.go b/config/configuration.go index 5917057..7c03318 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -2,6 +2,7 @@ package config import ( "context" + "errors" "fmt" "log" "reflect" @@ -46,7 +47,7 @@ func getDefaultEnvConfig() *EnvConfig { } // Load method loads the configuration by using both flag or environment variables. -func Load() (*EnvConfig, []Config) { +func Load() (*EnvConfig, []Config, error) { loaders := []backend.Backend{ env.NewBackend(), flags.NewBackend(), @@ -62,7 +63,11 @@ func Load() (*EnvConfig, []Config) { cfg.show() - return cfg, cfg.Split() + if clientsConfig, err := cfg.Split(); err != nil { + return cfg, nil, err + } else { + return cfg, clientsConfig, nil + } } func (c *Config) String() string { @@ -87,50 +92,64 @@ func (c Config) Validate() error { return nil } -func (c EnvConfig) Split() []Config { - result := make([]Config, 0, len(c.PIHoleHostname)) +func (c EnvConfig) Split() ([]Config, error) { + hostsCount := len(c.PIHoleHostname) + result := make([]Config, 0, hostsCount) for i, hostname := range c.PIHoleHostname { config := Config{ PIHoleHostname: strings.TrimSpace(hostname), - PIHolePort: c.PIHolePort[i], } - if hasData, data := extractConfig(c.PIHoleProtocol, i); hasData { + if len(c.PIHolePort) == 1 { + config.PIHolePort = c.PIHolePort[0] + } else if len(c.PIHolePort) == hostsCount { + config.PIHolePort = c.PIHolePort[i] + } else if len(c.PIHolePort) != 0 { + return nil, errors.New("Wrong number of ports. Port can be empty to use default, one value to use for all hosts, or match the number of hosts") + } + + if hasData, data, isValid := extractStringConfig(c.PIHoleProtocol, i, hostsCount); hasData { config.PIHoleProtocol = data + } else if !isValid { + return nil, errors.New("Wrong number of PIHoleProtocol. PIHoleProtocol can be empty to use default, one value to use for all hosts, or match the number of hosts") } - if hasData, data := extractConfig(c.PIHoleApiToken, i); hasData { + if hasData, data, isValid := extractStringConfig(c.PIHoleApiToken, i, hostsCount); hasData { config.PIHoleApiToken = data + } else if !isValid { + return nil, errors.New(fmt.Sprintf("Wrong number of PIHoleApiToken %d (Hosts: %d). PIHoleApiToken can be empty to use default, one value to use for all hosts, or match the number of hosts", len(c.PIHoleApiToken), hostsCount)) } - if hasData, data := extractConfig(c.PIHoleApiToken, i); hasData { - config.PIHoleApiToken = data - } - - if hasData, data := extractConfig(c.PIHolePassword, i); hasData { + if hasData, data, isValid := extractStringConfig(c.PIHolePassword, i, hostsCount); hasData { config.PIHolePassword = data + } else if !isValid { + return nil, errors.New("Wrong number of PIHolePassword. PIHolePassword can be empty to use default, one value to use for all hosts, or match the number of hosts") } result = append(result, config) } - return result + + return result, nil } -func extractConfig(data []string, idx int) (bool, string) { +func extractStringConfig(data []string, idx int, hostsCount int) (bool, string, bool) { if len(data) == 1 { v := strings.TrimSpace(data[0]) if v != "" { - return true, v + return true, v, true } - } else if len(data) > 1 { + } else if len(data) == hostsCount { v := strings.TrimSpace(data[idx]) if v != "" { - return true, v + return true, v, true } + } else if len(data) != 0 { //Host count missmatch + return false, "", false } - return false, "" + // Empty + return false, "", true } func (c Config) hostnameURL() string { diff --git a/config/configuration_test.go b/config/configuration_test.go new file mode 100644 index 0000000..5f76d34 --- /dev/null +++ b/config/configuration_test.go @@ -0,0 +1,154 @@ +package config + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + PIHOLE_HOSTNAME = "PIHOLE_HOSTNAME" + PIHOLE_PORT = "PIHOLE_PORT" + PIHOLE_API_TOKEN = "PIHOLE_API_TOKEN" + PIHOLE_PROTOCOL = "PIHOLE_PROTOCOL" +) + +type EnvInitiazlier func(*testing.T) + +type TestCase struct { + Name string + Initializer EnvInitiazlier +} + +func TestSplitDefault(t *testing.T) { + assert := assert.New(t) + + env := getDefaultEnvConfig() + + clientConfigs, err := env.Split() + assert.NoError(err) + + clientConfig := clientConfigs[0] + assert.Equal("127.0.0.1", clientConfig.PIHoleHostname) + assert.Equal("http", clientConfig.PIHoleProtocol) + assert.Equal(uint16(80), clientConfig.PIHolePort) + assert.Empty(clientConfig.PIHoleApiToken) + assert.Empty(clientConfig.PIHolePassword) +} + +func TestSplitMultipleHostWithSameConfig(t *testing.T) { + assert := assert.New(t) + + env := getDefaultEnvConfig() + env.PIHoleHostname = []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"} + env.PIHoleApiToken = []string{"api-token"} + env.PIHolePort = []uint16{8080} + + clientConfigs, err := env.Split() + assert.NoError(err) + assert.Len(clientConfigs, 3) + + testCases := []struct { + Host string + Port uint16 + Protocol string + }{ + { + Host: "127.0.0.1", + Port: 8080, + Protocol: "http", + }, + { + Host: "127.0.0.2", + Port: 8080, + Protocol: "http", + }, + { + Host: "127.0.0.3", + Port: 8080, + Protocol: "http", + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("Test %s", tc.Host), func(t *testing.T) { + clientConfig := clientConfigs[i] + + assert.Equal(tc.Host, clientConfig.PIHoleHostname) + assert.Equal(tc.Protocol, clientConfig.PIHoleProtocol) + assert.Equal(tc.Port, clientConfig.PIHolePort) + assert.Equal("api-token", clientConfig.PIHoleApiToken) + assert.Empty(clientConfig.PIHolePassword) + }) + } +} + +func TestSplitMultipleHostWithMultipleConfigs(t *testing.T) { + assert := assert.New(t) + + env := getDefaultEnvConfig() + env.PIHoleHostname = []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"} + env.PIHoleApiToken = []string{"api-token1", "", "api-token3"} + env.PIHolePassword = []string{"", "password2", ""} + env.PIHolePort = []uint16{8081, 8082, 8083} + + clientConfigs, err := env.Split() + assert.NoError(err) + assert.Len(clientConfigs, 3) + + testCases := []struct { + Host string + Port uint16 + Protocol string + ApiToken string + Password string + }{ + { + Host: "127.0.0.1", + Port: 8081, + Protocol: "http", + ApiToken: "api-token1", + Password: "", + }, + { + Host: "127.0.0.2", + Port: 8082, + Protocol: "http", + ApiToken: "", + Password: "password2", + }, + { + Host: "127.0.0.3", + Port: 8083, + Protocol: "http", + ApiToken: "api-token3", + Password: "", + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("Test %s", tc.Host), func(t *testing.T) { + clientConfig := clientConfigs[i] + + assert.Equal(tc.Host, clientConfig.PIHoleHostname) + assert.Equal(tc.Protocol, clientConfig.PIHoleProtocol) + assert.Equal(tc.Port, clientConfig.PIHolePort) + assert.Equal(tc.ApiToken, clientConfig.PIHoleApiToken) + assert.Equal(tc.Password, clientConfig.PIHolePassword) + }) + } +} + +func TestWrongParams(t *testing.T) { + assert := assert.New(t) + + env := getDefaultEnvConfig() + env.PIHoleHostname = []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"} + env.PIHoleApiToken = []string{"api-token1", "api-token2"} + env.PIHolePort = []uint16{808} + + clientConfigs, err := env.Split() + assert.Errorf(err, "Wrong number of PIHoleApiToken. PIHoleApiToken can be empty to use default, one value to use for all hosts, or match the number of hosts") + assert.Nil(clientConfigs) +} diff --git a/go.mod b/go.mod index a4f08dd..0d00d2a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.15 require ( github.com/heetch/confita v0.10.0 github.com/prometheus/client_golang v1.11.0 + github.com/stretchr/testify v1.7.0 github.com/xonvanetta/shutdown v0.0.3 golang.org/x/net v0.0.0-20200625001655-4c5254603344 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ) diff --git a/go.sum b/go.sum index e2f3f59..7164e67 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -195,12 +197,15 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -241,6 +246,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -292,6 +299,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/internal/pihole/client.go b/internal/pihole/client.go index 789dd60..f453a62 100644 --- a/internal/pihole/client.go +++ b/internal/pihole/client.go @@ -16,12 +16,38 @@ import ( "github.com/eko/pihole-exporter/internal/metrics" ) +type ClientStatus byte + +const ( + MetricsCollectionInProgress ClientStatus = iota + MetricsCollectionSuccess + MetricsCollectionError + MetricsCollectionTimeout +) + +func (status ClientStatus) String() string { + return []string{"MetricsCollectionInProgress", "MetricsCollectionSuccess", "MetricsCollectionError", "MetricsCollectionTimeout"}[status] +} + +type ClientChannel struct { + Status ClientStatus + Err error +} + +func (c *ClientChannel) String() string { + if c.Err != nil { + return fmt.Sprintf("ClientChannel", c.Status, c.Err.Error()) + } else { + return fmt.Sprintf("ClientChannel>", c.Status) + } +} + // Client struct is a PI-Hole client to request an instance of a PI-Hole ad blocker. type Client struct { - httpClient http.Client - interval time.Duration - config *config.Config - MetricRetrieved chan bool + httpClient http.Client + interval time.Duration + config *config.Config + Status chan *ClientChannel } // NewClient method initializes a new PI-Hole client. @@ -41,6 +67,7 @@ func NewClient(config *config.Config) *Client { return http.ErrUseLastResponse }, }, + Status: make(chan *ClientChannel, 1), } } @@ -48,32 +75,24 @@ func (c *Client) String() string { return c.config.PIHoleHostname } -/* -// Metrics scrapes pihole and sets them -func (c *Client) Metrics() http.HandlerFunc { - return func(writer http.ResponseWriter, request *http.Request) { - stats, err := c.getStatistics() - if err != nil { - writer.WriteHeader(http.StatusBadRequest) - _, _ = writer.Write([]byte(err.Error())) - return - } +func (c *Client) CollectMetricsAsync(writer http.ResponseWriter, request *http.Request) { + log.Printf("Collecting from %s", c.config.PIHoleHostname) + if stats, err := c.getStatistics(); err == nil { c.setMetrics(stats) - - log.Printf("New tick of statistics: %s", stats.ToString()) - promhttp.Handler().ServeHTTP(writer, request) + c.Status <- &ClientChannel{Status: MetricsCollectionSuccess, Err: nil} + log.Printf("New tick of statistics from %s: %s", c.config.PIHoleHostname, stats) + } else { + c.Status <- &ClientChannel{Status: MetricsCollectionError, Err: err} } -}*/ +} func (c *Client) CollectMetrics(writer http.ResponseWriter, request *http.Request) error { - stats, err := c.getStatistics() if err != nil { return err } c.setMetrics(stats) - - log.Printf("New tick of statistics from %s: %s", c, stats) + log.Printf("New tick of statistics from %s: %s", c.config.PIHoleHostname, stats) return nil } diff --git a/internal/server/server.go b/internal/server/server.go index 55968b5..3c5efd4 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -22,32 +22,28 @@ type Server struct { // the different routes that will be used by Prometheus (metrics) or for monitoring (readiness, liveness). func NewServer(port uint16, clients []*pihole.Client) *Server { mux := http.NewServeMux() - httpServer := &http.Server{Addr: ":" + strconv.Itoa(int(port)), Handler: mux} + httpServer := &http.Server{ + Addr: ":" + strconv.Itoa(int(port)), + Handler: mux, + } s := &Server{ httpServer: httpServer, } - mux.HandleFunc("/metrics", - func(writer http.ResponseWriter, request *http.Request) { - errors := make([]string, 0) + mux.HandleFunc("/metrics", func(writer http.ResponseWriter, request *http.Request) { + log.Printf("request.Header: %v\n", request.Header) + + for _, client := range clients { + go client.CollectMetricsAsync(writer, request) + } - for _, client := range clients { - if err := client.CollectMetrics(writer, request); err != nil { - errors = append(errors, err.Error()) - fmt.Printf("Error %s\n", err) - } - } + for _, client := range clients { + log.Printf("Received %s from %s\n", <-client.Status, client.GetHostname()) + } - if len(errors) == len(clients) { - writer.WriteHeader(http.StatusBadRequest) - body := strings.Join(errors, "\n") - _, _ = writer.Write([]byte(body)) - } - - promhttp.Handler().ServeHTTP(writer, request) - }, - ) + promhttp.Handler().ServeHTTP(writer, request) + }) mux.Handle("/readiness", s.readinessHandler()) mux.Handle("/liveness", s.livenessHandler()) @@ -73,6 +69,27 @@ func (s *Server) Stop() { s.httpServer.Shutdown(ctx) } +func (s *Server) handleMetrics(clients []*pihole.Client) http.HandlerFunc { + return func(writer http.ResponseWriter, request *http.Request) { + errors := make([]string, 0) + + for _, client := range clients { + if err := client.CollectMetrics(writer, request); err != nil { + errors = append(errors, err.Error()) + fmt.Printf("Error %s\n", err) + } + } + + if len(errors) == len(clients) { + writer.WriteHeader(http.StatusBadRequest) + body := strings.Join(errors, "\n") + _, _ = writer.Write([]byte(body)) + } + + promhttp.Handler().ServeHTTP(writer, request) + } +} + func (s *Server) readinessHandler() http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { if s.isReady() { diff --git a/main.go b/main.go index ea8e7a9..8bee1a8 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "github.com/eko/pihole-exporter/config" "github.com/eko/pihole-exporter/internal/metrics" @@ -11,7 +12,10 @@ import ( ) func main() { - envConf, clientConfigs := config.Load() + envConf, clientConfigs, err := config.Load() + if err != nil { + log.Fatal(err.Error()) + } metrics.Init() From 2c062e5bf9137f933d10a90bc391b51d28560809 Mon Sep 17 00:00:00 2001 From: Galorhallen Date: Wed, 5 Jan 2022 20:38:33 +0100 Subject: [PATCH 4/7] Woring on logging --- config/configuration.go | 15 ++++++++------- go.mod | 3 ++- go.sum | 3 +++ internal/metrics/metrics.go | 5 ++--- internal/pihole/client.go | 25 ++++++++++++++++++------- internal/server/server.go | 13 ++++++++----- main.go | 10 +++++++++- 7 files changed, 50 insertions(+), 24 deletions(-) diff --git a/config/configuration.go b/config/configuration.go index 7c03318..b829926 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -4,11 +4,12 @@ import ( "context" "errors" "fmt" - "log" "reflect" "strings" "time" + log "github.com/sirupsen/logrus" + "github.com/heetch/confita" "github.com/heetch/confita/backend" "github.com/heetch/confita/backend/env" @@ -172,25 +173,25 @@ func (c Config) PIHoleLoginURL() string { func (c EnvConfig) show() { val := reflect.ValueOf(&c).Elem() - log.Println("------------------------------------") - log.Println("- PI-Hole exporter configuration -") - log.Println("------------------------------------") + log.Info("------------------------------------") + log.Info("- PI-Hole exporter configuration -") + log.Info("------------------------------------") for i := 0; i < val.NumField(); i++ { valueField := val.Field(i) typeField := val.Type().Field(i) // Do not print password or api token but do print the authentication method if typeField.Name != "PIHolePassword" && typeField.Name != "PIHoleApiToken" { - log.Println(fmt.Sprintf("%s : %v", typeField.Name, valueField.Interface())) + log.Info(fmt.Sprintf("%s : %v", typeField.Name, valueField.Interface())) } else { showAuthenticationMethod(typeField.Name, valueField.Len()) } } - log.Println("------------------------------------") + log.Info("------------------------------------") } func showAuthenticationMethod(name string, length int) { if length > 0 { - log.Println(fmt.Sprintf("Pi-Hole Authentication Method : %s", name)) + log.Info(fmt.Sprintf("Pi-Hole Authentication Method : %s", name)) } } diff --git a/go.mod b/go.mod index 0d00d2a..bfdf246 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,9 @@ go 1.15 require ( github.com/heetch/confita v0.10.0 github.com/prometheus/client_golang v1.11.0 + github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 github.com/xonvanetta/shutdown v0.0.3 golang.org/x/net v0.0.0-20200625001655-4c5254603344 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect ) diff --git a/go.sum b/go.sum index 7164e67..ced6b40 100644 --- a/go.sum +++ b/go.sum @@ -195,6 +195,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= @@ -260,6 +262,7 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 3a50a3a..c824713 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -1,9 +1,8 @@ package metrics import ( - "log" - "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" ) var ( @@ -201,5 +200,5 @@ func Init() { func initMetric(name string, metric *prometheus.GaugeVec) { prometheus.MustRegister(metric) - log.Printf("New Prometheus metric registered: %s", name) + log.Info("New Prometheus metric registered: ", name) } diff --git a/internal/pihole/client.go b/internal/pihole/client.go index f453a62..022be6b 100644 --- a/internal/pihole/client.go +++ b/internal/pihole/client.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" + "net" "net/http" "net/url" "os" @@ -12,6 +12,8 @@ import ( "strings" "time" + log "github.com/sirupsen/logrus" + "github.com/eko/pihole-exporter/config" "github.com/eko/pihole-exporter/internal/metrics" ) @@ -54,11 +56,18 @@ type Client struct { func NewClient(config *config.Config) *Client { err := config.Validate() if err != nil { - log.Print(err) + log.Error(err) os.Exit(1) } - fmt.Printf("Creating client with config %s\n", config) + log.Info("Creating client with config ", config) + + netTransport := &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 5 * time.Second, + }).Dial, + TLSHandshakeTimeout: 5 * time.Second, + } return &Client{ config: config, @@ -66,6 +75,8 @@ func NewClient(config *config.Config) *Client { CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, + Timeout: 10 * time.Second, + Transport: netTransport, }, Status: make(chan *ClientChannel, 1), } @@ -76,11 +87,11 @@ func (c *Client) String() string { } func (c *Client) CollectMetricsAsync(writer http.ResponseWriter, request *http.Request) { - log.Printf("Collecting from %s", c.config.PIHoleHostname) + log.Infof("Collecting from %s", c.config.PIHoleHostname) if stats, err := c.getStatistics(); err == nil { c.setMetrics(stats) c.Status <- &ClientChannel{Status: MetricsCollectionSuccess, Err: nil} - log.Printf("New tick of statistics from %s: %s", c.config.PIHoleHostname, stats) + log.Infof("New tick of statistics from %s: %s", c.config.PIHoleHostname, stats) } else { c.Status <- &ClientChannel{Status: MetricsCollectionError, Err: err} } @@ -92,7 +103,7 @@ func (c *Client) CollectMetrics(writer http.ResponseWriter, request *http.Reques return err } c.setMetrics(stats) - log.Printf("New tick of statistics from %s: %s", c.config.PIHoleHostname, stats) + log.Infof("New tick of statistics from %s: %s", c.config.PIHoleHostname, stats) return nil } @@ -157,7 +168,7 @@ func (c *Client) getPHPSessionID() (sessionID string) { resp, err := c.httpClient.Do(req) if err != nil { - log.Printf("An error has occured during login to PI-Hole: %v", err) + log.Error("An error has occured during login to PI-Hole: %v", err) } for _, cookie := range resp.Cookies() { diff --git a/internal/server/server.go b/internal/server/server.go index 3c5efd4..dadf250 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,7 +2,6 @@ package server import ( "fmt" - "log" "net/http" "strconv" "strings" @@ -10,6 +9,7 @@ import ( "github.com/eko/pihole-exporter/internal/pihole" "github.com/prometheus/client_golang/prometheus/promhttp" + log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -32,14 +32,17 @@ func NewServer(port uint16, clients []*pihole.Client) *Server { } mux.HandleFunc("/metrics", func(writer http.ResponseWriter, request *http.Request) { - log.Printf("request.Header: %v\n", request.Header) - + log.Debug("request.Header: %v\n", request.Header) + for _, client := range clients { go client.CollectMetricsAsync(writer, request) } for _, client := range clients { - log.Printf("Received %s from %s\n", <-client.Status, client.GetHostname()) + status := <-client.Status + if status.Status == pihole.MetricsCollectionError { + log.Error("Received %s from %s\n", <-client.Status, client.GetHostname()) + } } promhttp.Handler().ServeHTTP(writer, request) @@ -76,7 +79,7 @@ func (s *Server) handleMetrics(clients []*pihole.Client) http.HandlerFunc { for _, client := range clients { if err := client.CollectMetrics(writer, request); err != nil { errors = append(errors, err.Error()) - fmt.Printf("Error %s\n", err) + fmt.Errorf("Error %s\n", err) } } diff --git a/main.go b/main.go index 8bee1a8..6e20ef9 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,10 @@ package main import ( "fmt" - "log" + + "time" + + log "github.com/sirupsen/logrus" "github.com/eko/pihole-exporter/config" "github.com/eko/pihole-exporter/internal/metrics" @@ -12,6 +15,11 @@ import ( ) func main() { + log.SetFormatter(&log.TextFormatter{ + DisableColors: false, + FullTimestamp: true, + TimestampFormat: time.RFC3339, + }) envConf, clientConfigs, err := config.Load() if err != nil { log.Fatal(err.Error()) From 3a0a02c40263a5155cfafd77f35b10f27e05b0a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 04:12:38 +0000 Subject: [PATCH 5/7] Bump github.com/prometheus/client_golang from 1.12.0 to 1.12.1 Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.12.0 to 1.12.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.12.0...v1.12.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d1e5fa2..a3c8a75 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/heetch/confita v0.10.0 - github.com/prometheus/client_golang v1.12.0 + github.com/prometheus/client_golang v1.12.1 github.com/stretchr/testify v1.7.0 github.com/xonvanetta/shutdown v0.0.3 golang.org/x/net v0.0.0-20210525063256-abc453219eb5 diff --git a/go.sum b/go.sum index 1d23765..ed0c84c 100644 --- a/go.sum +++ b/go.sum @@ -247,8 +247,8 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= From 6fdcc9bb16b0f4144ab020c1ac1c13f8276f9a8f Mon Sep 17 00:00:00 2001 From: Galorhallen Date: Thu, 10 Feb 2022 11:21:54 +0100 Subject: [PATCH 6/7] Fix modules --- go.mod | 1 + main.go | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 74a3712..0dfa482 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/heetch/confita v0.10.0 github.com/prometheus/client_golang v1.12.1 + github.com/sirupsen/logrus v1.6.0 github.com/stretchr/testify v1.7.0 github.com/xonvanetta/shutdown v0.0.3 golang.org/x/net v0.0.0-20210525063256-abc453219eb5 diff --git a/main.go b/main.go index ad34050..ad37555 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,6 @@ package main import ( "log" - "time" - - log "github.com/sirupsen/logrus" - "github.com/eko/pihole-exporter/config" "github.com/eko/pihole-exporter/internal/metrics" "github.com/eko/pihole-exporter/internal/pihole" From c2437c8246d61093f4e72ddcea922d4af2939bed Mon Sep 17 00:00:00 2001 From: Galorhallen Date: Thu, 10 Feb 2022 11:33:09 +0100 Subject: [PATCH 7/7] Fix wrong log function --- internal/pihole/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pihole/client.go b/internal/pihole/client.go index a471a9d..683961e 100644 --- a/internal/pihole/client.go +++ b/internal/pihole/client.go @@ -159,7 +159,7 @@ func (c *Client) getPHPSessionID() (sessionID string) { resp, err := c.httpClient.Do(req) if err != nil { - log.Error("An error has occured during login to PI-Hole: %v", err) + log.Errorf("An error has occured during login to PI-Hole: %v", err) } for _, cookie := range resp.Cookies() {