From 13e67259bb8af1164e3eb8ad0ebe99d67f79c795 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 11 Mar 2024 14:00:06 +0100 Subject: [PATCH 1/3] Update(logs): support json format --- app/app.go | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/app/app.go b/app/app.go index 486ed73..3e2641e 100644 --- a/app/app.go +++ b/app/app.go @@ -102,6 +102,17 @@ func NewApp(name string, version string) DNSBLApp { return nil }, }, + &cli.StringFlag{ + Name: "log.format", + Value: "text", + Usage: "format, text is logfmt or use json", + Action: func(cCtx *cli.Context, v string) error { + if v != "text" && v != "json" { + return cli.Exit("We currently support only text and json: --log.format", 2) + } + return nil + }, + }, } return DNSBLApp{ @@ -110,29 +121,36 @@ func NewApp(name string, version string) DNSBLApp { } func (a *DNSBLApp) Bootstrap() { - a.App.Action = func(ctx *cli.Context) error { + a.App.Action = func(cCtx *cli.Context) error { // setup logging handler := &slog.HandlerOptions{} var writer io.Writer - if ctx.Bool("log.debug") { + if cCtx.Bool("log.debug") { handler.Level = slog.LevelDebug } - switch ctx.String("log.output") { + switch cCtx.String("log.output") { case "stdout": writer = os.Stdout case "stderr": writer = os.Stderr } - log := slog.New(handler.NewTextHandler(writer)) + var logHandler slog.Handler + if cCtx.String("log.format") == "text" { + logHandler = handler.NewTextHandler(writer) + } else { + logHandler = handler.NewJSONHandler(writer) + } + + log := slog.New(logHandler) c := config.Config{ Logger: log.With("area", "config"), } - cfgRbls, err := c.LoadFile(ctx.String("config.rbls")) + cfgRbls, err := c.LoadFile(cCtx.String("config.rbls")) if err != nil { return err } @@ -142,7 +160,7 @@ func (a *DNSBLApp) Bootstrap() { return fmt.Errorf("unable to load the rbls from the config: %w", err) } - cfgTargets, err := c.LoadFile(ctx.String("config.targets")) + cfgTargets, err := c.LoadFile(cCtx.String("config.targets")) if err != nil { return err } @@ -174,12 +192,12 @@ func (a *DNSBLApp) Bootstrap() { return err } - rblCollector := setup.CreateCollector(rbls, targets, ctx.Bool("config.domain-based"), dnsUtil, log.With("area", "metrics")) + rblCollector := setup.CreateCollector(rbls, targets, cCtx.Bool("config.domain-based"), dnsUtil, log.With("area", "metrics")) registry.MustRegister(rblCollector) registryExporter := setup.CreateRegistry() - if ctx.Bool("web.include-exporter-metrics") { + if cCtx.Bool("web.include-exporter-metrics") { log.Info("Exposing exporter metrics") registryExporter.MustRegister( @@ -193,21 +211,21 @@ func (a *DNSBLApp) Bootstrap() { RegistryExporter: registryExporter, } - http.Handle(ctx.String("web.telemetry-path"), mHandler.Handler()) + http.Handle(cCtx.String("web.telemetry-path"), mHandler.Handler()) pHandler := prober.ProberHandler{ DNS: dnsUtil, Rbls: rbls, - DomainBased: ctx.Bool("config.domain-based"), + DomainBased: cCtx.Bool("config.domain-based"), Logger: log.With("area", "prober"), } http.Handle("/prober", pHandler) log.Info("starting exporter", - slog.String("web.listen-address", ctx.String("web.listen-address")), + slog.String("web.listen-address", cCtx.String("web.listen-address")), slog.String("resolver", resolver), ) - err = http.ListenAndServe(ctx.String("web.listen-address"), nil) + err = http.ListenAndServe(cCtx.String("web.listen-address"), nil) if err != nil { return err } From c8a8dcf31d8d26004a3bd3eb67f7d4d8498bed17 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 11 Mar 2024 15:20:30 +0100 Subject: [PATCH 2/3] Update(resolver): --config.dns-resolver=system - when provided, pick the first from /etc/resolv.conf - this does not support multiple resolvers - the returned value is not health checked Resolves: #165 --- README.md | 9 +++++ app/app.go | 21 +++++++++++- internal/resolvconf/fixtures/no-server.conf | 1 + internal/resolvconf/fixtures/two-servers.conf | 3 ++ internal/resolvconf/resolvconf.go | 33 +++++++++++++++++++ internal/resolvconf/resolvconf_test.go | 31 +++++++++++++++++ 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 internal/resolvconf/fixtures/no-server.conf create mode 100644 internal/resolvconf/fixtures/two-servers.conf create mode 100644 internal/resolvconf/resolvconf.go create mode 100644 internal/resolvconf/resolvconf_test.go diff --git a/README.md b/README.md index 834fe73..94b30e5 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,15 @@ $ sudo unbound -d -vvvv 192.42.118.104 ``` +### Use /etc/resolv.conf + +Use `system` as a value and the exporter will pick the **first** resolver from `/etc/resolv.conf`. + +Adequate permissions need to be set by yourself so the exporter can read the file. + +- `--config.dns-resolver=system` +- `DNSBL_EXP_RESOLVER=system` + ## License / Author This code is Apache 2.0 licensed. diff --git a/app/app.go b/app/app.go index 3e2641e..47faeb0 100644 --- a/app/app.go +++ b/app/app.go @@ -12,6 +12,7 @@ import ( "github.com/Luzilla/dnsbl_exporter/internal/index" "github.com/Luzilla/dnsbl_exporter/internal/metrics" "github.com/Luzilla/dnsbl_exporter/internal/prober" + "github.com/Luzilla/dnsbl_exporter/internal/resolvconf" "github.com/Luzilla/dnsbl_exporter/internal/setup" "github.com/Luzilla/dnsbl_exporter/pkg/dns" "github.com/prometheus/client_golang/prometheus/collectors" @@ -30,6 +31,8 @@ var ( resolver string ) +const resolvConfFile = "/etc/resolv.conf" + // NewApp ... func NewApp(name string, version string) DNSBLApp { appName = name @@ -42,7 +45,7 @@ func NewApp(name string, version string) DNSBLApp { &cli.StringFlag{ Name: "config.dns-resolver", Value: "127.0.0.1:53", - Usage: "IP address[:port] of the resolver to use.", + Usage: "IP address[:port] of the resolver to use, use `system` to use a resolve from " + resolvConfFile, EnvVars: []string{"DNSBL_EXP_RESOLVER"}, Destination: &resolver, }, @@ -173,6 +176,22 @@ func (a *DNSBLApp) Bootstrap() { log.Info("starting exporter without targets — check the /prober endpoint or correct the .ini file") } + // use the system's resolver + if resolver == "system" { + log.Info("fetching resolver from " + resolvConfFile) + servers, err := resolvconf.GetServers(resolvConfFile) + if err != nil { + return err + } + if len(servers) == 0 { + return fmt.Errorf("unable to return a server from %s", resolvConfFile) + } + + // pick the first + resolver = servers[0] + log.Info("using resolver: " + resolver) + } + iHandler := index.IndexHandler{ Name: appName, Version: appVersion, diff --git a/internal/resolvconf/fixtures/no-server.conf b/internal/resolvconf/fixtures/no-server.conf new file mode 100644 index 0000000..7416e1d --- /dev/null +++ b/internal/resolvconf/fixtures/no-server.conf @@ -0,0 +1 @@ +search anything.local diff --git a/internal/resolvconf/fixtures/two-servers.conf b/internal/resolvconf/fixtures/two-servers.conf new file mode 100644 index 0000000..72ab6a6 --- /dev/null +++ b/internal/resolvconf/fixtures/two-servers.conf @@ -0,0 +1,3 @@ +# two servers +nameserver 1.1.1.1 +nameserver 8.8.8.8 diff --git a/internal/resolvconf/resolvconf.go b/internal/resolvconf/resolvconf.go new file mode 100644 index 0000000..b54e2f1 --- /dev/null +++ b/internal/resolvconf/resolvconf.go @@ -0,0 +1,33 @@ +package resolvconf + +import ( + "bufio" + "os" + "strings" +) + +func GetServers(path string) (servers []string, err error) { + file, err := os.Open(path) + if err != nil { + return + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if !strings.HasPrefix(line, "nameserver ") { + continue + } + + server, ok := strings.CutPrefix(line, "nameserver ") + if !ok { + continue + } + + servers = append(servers, server) + } + + err = scanner.Err() + return +} diff --git a/internal/resolvconf/resolvconf_test.go b/internal/resolvconf/resolvconf_test.go new file mode 100644 index 0000000..965b388 --- /dev/null +++ b/internal/resolvconf/resolvconf_test.go @@ -0,0 +1,31 @@ +package resolvconf_test + +import ( + "path/filepath" + "testing" + + "github.com/Luzilla/dnsbl_exporter/internal/resolvconf" + "github.com/stretchr/testify/assert" +) + +func TestGetServers(t *testing.T) { + testCases := []struct { + path string + expected []string + }{ + { + path: "no-server.conf", + expected: []string(nil), + }, + { + path: "two-servers.conf", + expected: []string{"1.1.1.1", "8.8.8.8"}, + }, + } + + for _, tc := range testCases { + servers, err := resolvconf.GetServers(filepath.Join("fixtures", tc.path)) + assert.NoError(t, err) + assert.Equal(t, tc.expected, servers) + } +} From ac61f634767f5fde95d870934a45cb1bfd221462 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 28 Mar 2024 13:15:01 +0100 Subject: [PATCH 3/3] Fix(ci): depends on used the wrong job --- .github/workflows/integration.yml | 101 +++++++++++++++--------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 24a8dc2..2c70e00 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -1,68 +1,67 @@ ---- name: integration on: pull_request: jobs: - e2e: + snapshot: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: false - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v5 - with: - version: latest - args: release --config ./.goreleaser.ci.yml --clean --snapshot - - name: Copy .ini files - run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1 - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: dnsbl_exporter - path: dist/dnsbl_exporter_linux_amd64_v1 + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: false + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v5 + with: + version: latest + args: release --config ./.goreleaser.ci.yml --clean --snapshot + - name: Copy .ini files + run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1 + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: dnsbl_exporter + path: dist/dnsbl_exporter_linux_amd64_v1 integration: runs-on: ubuntu-latest needs: - - snapshot + - snapshot services: unbound: image: klutchell/unbound:latest ports: - - 5053:5053/tcp - - 5053:5053/udp + - 5053:5053/tcp + - 5053:5053/udp steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: false - - uses: goreleaser/goreleaser-action@v5 - with: - version: latest - args: release --config ./.goreleaser.ci.yml --clean --snapshot - - run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1 - - uses: JarvusInnovations/background-action@v1 - with: - run: | - ls -lah && ./dnsbl-exporter --log.debug --config.dns-resolver=localhost:5053 & - wait-on: | - http-get://localhost:9211/ - http-get://localhost:9211/metrics - http-get://localhost:9211/prober?target=github.com - tail: true # true = stderr,stdout - log-output-resume: stderr - wait-for: 1m - log-output: stderr,stdout - working-directory: ./dist/dnsbl_exporter_linux_amd64_v1 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: false + - uses: goreleaser/goreleaser-action@v5 + with: + version: latest + args: release --config ./.goreleaser.ci.yml --clean --snapshot + - run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1 + - uses: JarvusInnovations/background-action@v1 + with: + run: | + ls -lah && ./dnsbl-exporter --log.debug --config.dns-resolver=localhost:5053 & + wait-on: | + http-get://localhost:9211/ + http-get://localhost:9211/metrics + http-get://localhost:9211/prober?target=github.com + tail: true # true = stderr,stdout + log-output-resume: stderr + wait-for: 1m + log-output: stderr,stdout + working-directory: ./dist/dnsbl_exporter_linux_amd64_v1