Add initial support to multiple pihole servers
This commit is contained in:
parent
53aae16d90
commit
fdbd1a678f
4 changed files with 112 additions and 27 deletions
|
@ -20,24 +20,32 @@ type Config struct {
|
|||
PIHolePort uint16 `config:"pihole_port"`
|
||||
PIHolePassword string `config:"pihole_password"`
|
||||
PIHoleApiToken string `config:"pihole_api_token"`
|
||||
Port string `config:"port"`
|
||||
}
|
||||
|
||||
type EnvConfig struct {
|
||||
PIHoleProtocol []string `config:"pihole_protocol"`
|
||||
PIHoleHostname []string `config:"pihole_hostname"`
|
||||
PIHolePort []uint16 `config:"pihole_port"`
|
||||
PIHolePassword []string `config:"pihole_password"`
|
||||
PIHoleApiToken []string `config:"pihole_api_token"`
|
||||
Port uint16 `config:"port"`
|
||||
Interval time.Duration `config:"interval"`
|
||||
}
|
||||
|
||||
func getDefaultConfig() *Config {
|
||||
return &Config{
|
||||
PIHoleProtocol: "http",
|
||||
PIHoleHostname: "127.0.0.1",
|
||||
PIHolePort: 80,
|
||||
PIHolePassword: "",
|
||||
PIHoleApiToken: "",
|
||||
Port: "9617",
|
||||
func getDefaultEnvConfig() *EnvConfig {
|
||||
return &EnvConfig{
|
||||
PIHoleProtocol: []string{"http"},
|
||||
PIHoleHostname: []string{"127.0.0.1"},
|
||||
PIHolePort: []uint16{80},
|
||||
PIHolePassword: []string{},
|
||||
PIHoleApiToken: []string{},
|
||||
Port: 9617,
|
||||
Interval: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// Load method loads the configuration by using both flag or environment variables.
|
||||
func Load() *Config {
|
||||
func Load() (*EnvConfig, []Config) {
|
||||
loaders := []backend.Backend{
|
||||
env.NewBackend(),
|
||||
flags.NewBackend(),
|
||||
|
@ -45,7 +53,7 @@ func Load() *Config {
|
|||
|
||||
loader := confita.NewLoader(loaders...)
|
||||
|
||||
cfg := getDefaultConfig()
|
||||
cfg := getDefaultEnvConfig()
|
||||
err := loader.Load(context.Background(), cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -53,7 +61,7 @@ func Load() *Config {
|
|||
|
||||
cfg.show()
|
||||
|
||||
return cfg
|
||||
return cfg, cfg.Split()
|
||||
}
|
||||
|
||||
//Validate check if the config is valid
|
||||
|
@ -64,6 +72,33 @@ func (c Config) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c EnvConfig) Split() []Config {
|
||||
result := make([]Config, 0, len(c.PIHoleHostname))
|
||||
|
||||
for i, hostname := range c.PIHoleHostname {
|
||||
config := Config{
|
||||
PIHoleHostname: hostname,
|
||||
PIHoleProtocol: c.PIHoleProtocol[i],
|
||||
PIHolePort: c.PIHolePort[i],
|
||||
}
|
||||
|
||||
if c.PIHoleApiToken != nil && len(c.PIHoleApiToken) > 0 {
|
||||
if c.PIHoleApiToken[i] != "" {
|
||||
config.PIHoleApiToken = c.PIHoleApiToken[i]
|
||||
}
|
||||
}
|
||||
|
||||
if c.PIHolePassword != nil && len(c.PIHolePassword) > 0 {
|
||||
if c.PIHolePassword[i] != "" {
|
||||
config.PIHolePassword = c.PIHolePassword[i]
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, config)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c Config) hostnameURL() string {
|
||||
s := fmt.Sprintf("%s://%s", c.PIHoleProtocol, c.PIHoleHostname)
|
||||
if c.PIHolePort != 0 {
|
||||
|
@ -82,7 +117,7 @@ func (c Config) PIHoleLoginURL() string {
|
|||
return c.hostnameURL() + "/admin/index.php?login"
|
||||
}
|
||||
|
||||
func (c Config) show() {
|
||||
func (c EnvConfig) show() {
|
||||
val := reflect.ValueOf(&c).Elem()
|
||||
log.Println("------------------------------------")
|
||||
log.Println("- PI-Hole exporter configuration -")
|
||||
|
@ -95,14 +130,14 @@ func (c Config) show() {
|
|||
if typeField.Name != "PIHolePassword" && typeField.Name != "PIHoleApiToken" {
|
||||
log.Println(fmt.Sprintf("%s : %v", typeField.Name, valueField.Interface()))
|
||||
} else {
|
||||
showAuthenticationMethod(typeField.Name, valueField.String())
|
||||
showAuthenticationMethod(typeField.Name, valueField.Len())
|
||||
}
|
||||
}
|
||||
log.Println("------------------------------------")
|
||||
}
|
||||
|
||||
func showAuthenticationMethod(name, value string) {
|
||||
if len(value) > 0 {
|
||||
func showAuthenticationMethod(name string, length int) {
|
||||
if length > 0 {
|
||||
log.Println(fmt.Sprintf("Pi-Hole Authentication Method : %s", name))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,9 @@ func NewClient(config *config.Config) *Client {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
fmt.Printf("Creating client with config %s\n", config)
|
||||
|
||||
client := &Client{
|
||||
config: config,
|
||||
httpClient: http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
|
@ -40,6 +42,14 @@ func NewClient(config *config.Config) *Client {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Printf("Client created with config %s\n", client)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *Client) String() string {
|
||||
return c.config.PIHoleHostname
|
||||
}
|
||||
|
||||
// Metrics scrapes pihole and sets them
|
||||
|
@ -58,6 +68,23 @@ func (c *Client) Metrics() http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Client) CollectMetrics(writer http.ResponseWriter, request *http.Request) {
|
||||
stats, err := c.getStatistics()
|
||||
if err != nil {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = writer.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
c.setMetrics(stats)
|
||||
|
||||
log.Printf("New tick of statistics: %s", stats.ToString())
|
||||
promhttp.Handler().ServeHTTP(writer, request)
|
||||
}
|
||||
|
||||
func (c *Client) GetHostname() string {
|
||||
return c.config.PIHoleHostname
|
||||
}
|
||||
|
||||
func (c *Client) setMetrics(stats *Stats) {
|
||||
metrics.DomainsBlocked.WithLabelValues(c.config.PIHoleHostname).Set(float64(stats.DomainsBeingBlocked))
|
||||
metrics.DNSQueriesToday.WithLabelValues(c.config.PIHoleHostname).Set(float64(stats.DNSQueriesToday))
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/eko/pihole-exporter/internal/pihole"
|
||||
|
@ -16,15 +18,29 @@ type Server struct {
|
|||
|
||||
// NewServer method initializes a new HTTP server instance and associates
|
||||
// the different routes that will be used by Prometheus (metrics) or for monitoring (readiness, liveness).
|
||||
func NewServer(port string, client *pihole.Client) *Server {
|
||||
func NewServer(port uint16, clients []*pihole.Client) *Server {
|
||||
mux := http.NewServeMux()
|
||||
httpServer := &http.Server{Addr: ":" + port, Handler: mux}
|
||||
httpServer := &http.Server{Addr: ":" + strconv.Itoa(int(port)), Handler: mux}
|
||||
|
||||
s := &Server{
|
||||
httpServer: httpServer,
|
||||
}
|
||||
|
||||
mux.Handle("/metrics", client.Metrics())
|
||||
/*fmt.Printf("Server received clients -> %s\n", clients)
|
||||
for i, client := range clients {
|
||||
fmt.Printf("Server received clients -> idx: %d, Hostname: %s\n", i, &client)
|
||||
}*/
|
||||
|
||||
mux.HandleFunc("/metrics",
|
||||
func(writer http.ResponseWriter, request *http.Request) {
|
||||
for i, client := range clients {
|
||||
fmt.Printf("Idx: %d, Hostname: %s\n", i, client)
|
||||
client.CollectMetrics(writer, request)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
//mux.Handle("/metrics", client.Metrics())
|
||||
mux.Handle("/readiness", s.readinessHandler())
|
||||
mux.Handle("/liveness", s.livenessHandler())
|
||||
|
||||
|
|
11
main.go
11
main.go
|
@ -11,12 +11,19 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
conf := config.Load()
|
||||
envConf, clientConfigs := config.Load()
|
||||
|
||||
metrics.Init()
|
||||
|
||||
serverDead := make(chan struct{})
|
||||
s := server.NewServer(conf.Port, pihole.NewClient(conf))
|
||||
clients := make([]*pihole.Client, 0, len(clientConfigs))
|
||||
for i, _ := range clientConfigs {
|
||||
client := pihole.NewClient(&clientConfigs[i])
|
||||
clients = append(clients, client)
|
||||
fmt.Printf("Append client %s\n", clients)
|
||||
}
|
||||
|
||||
s := server.NewServer(envConf.Port, clients)
|
||||
go func() {
|
||||
s.ListenAndServe()
|
||||
close(serverDead)
|
||||
|
|
Loading…
Reference in a new issue