Skip to content

Commit

Permalink
feat: globbing support for -f option
Browse files Browse the repository at this point in the history
  • Loading branch information
arcln committed Mar 23, 2023
1 parent 982be0f commit 7fa2298
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 4 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/enix/x509-certificate-exporter/v3
go 1.18

require (
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pborman/getopt/v2 v2.1.0
github.com/pkg/errors v0.9.1
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
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/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
Expand Down
48 changes: 44 additions & 4 deletions internal/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/x509/pkix"
"fmt"
"io/fs"
"net"
"net/http"
"os"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/bmatcuk/doublestar/v4"
"github.com/prometheus/common/promlog"
"github.com/prometheus/exporter-toolkit/web"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -137,10 +139,15 @@ func (exporter *Exporter) parseAllCertificates() ([]*certificateRef, []*certific
}

for _, file := range exporter.Files {
output = append(output, &certificateRef{
path: path.Clean(file),
format: certificateFormatPEM,
})
refs, err := exporter.getAllMatchingCertificates(file)

if err != nil {
raiseError(&certificateError{
err: fmt.Errorf("failed to parse \"%s\": %s", file, err.Error()),
})
}

output = append(output, refs...)
}

for _, file := range exporter.YAMLs {
Expand Down Expand Up @@ -233,6 +240,39 @@ func (exporter *Exporter) parseAllCertificates() ([]*certificateRef, []*certific
return output, outputErrors
}

func (exporter *Exporter) getAllMatchingCertificates(pattern string) ([]*certificateRef, error) {
output := []*certificateRef{}
basepath, match := doublestar.SplitPattern(pattern)

walk := func(filepath string, entry fs.DirEntry) error {
output = append(output, &certificateRef{
path: path.Clean(path.Join(basepath, filepath)),
format: certificateFormatPEM,
})

return nil
}

err := doublestar.GlobWalk(
os.DirFS(basepath),
match,
walk,
doublestar.WithFilesOnly(),
doublestar.WithFailOnIOErrors(),
// doublestar.WithNoFollow(),
// doublestar.WithFailOnPatternNotExist(),
)
if err != nil {
return nil, err
}

if len(output) == 0 {
log.Warnln("no files match pattern \"" + pattern + "\"")
}

return output, nil
}

func (exporter *Exporter) compareCertificates(
leftCert *parsedCertificate,
leftRef *certificateRef,
Expand Down
72 changes: 72 additions & 0 deletions internal/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,78 @@ func TestExposeLabels(t *testing.T) {
removeGeneratedCertificate(certPath)
}

func TestGlobbing(t *testing.T) {
// no matches
testRequest(t, &Exporter{
Files: []string{"does-not-exist/**"},
}, func(metrics []model.MetricFamily) {
foundMetrics := getMetricsForName(metrics, "x509_cert_expired")
assert.Len(t, foundMetrics, 0)
foundNbMetrics := getMetricsForName(metrics, "x509_cert_not_before")
assert.Len(t, foundNbMetrics, 0)
foundNaMetrics := getMetricsForName(metrics, "x509_cert_not_after")
assert.Len(t, foundNaMetrics, 0)
errMetric := getMetricsForName(metrics, "x509_read_errors")
assert.Equal(t, 0., errMetric[0].GetGauge().GetValue())
})

// single star match
testRequest(t, &Exporter{
Files: []string{"../tes*/basic.pem"},
}, func(metrics []model.MetricFamily) {
foundMetrics := getMetricsForName(metrics, "x509_cert_expired")
assert.Len(t, foundMetrics, 1)
foundNbMetrics := getMetricsForName(metrics, "x509_cert_not_before")
assert.Len(t, foundNbMetrics, 1)
foundNaMetrics := getMetricsForName(metrics, "x509_cert_not_after")
assert.Len(t, foundNaMetrics, 1)
errMetric := getMetricsForName(metrics, "x509_read_errors")
assert.Equal(t, 0., errMetric[0].GetGauge().GetValue())
})

// double star match
testRequest(t, &Exporter{
Files: []string{"../test/**/basic.pem"},
}, func(metrics []model.MetricFamily) {
foundMetrics := getMetricsForName(metrics, "x509_cert_expired")
assert.Len(t, foundMetrics, 2)
foundNbMetrics := getMetricsForName(metrics, "x509_cert_not_before")
assert.Len(t, foundNbMetrics, 2)
foundNaMetrics := getMetricsForName(metrics, "x509_cert_not_after")
assert.Len(t, foundNaMetrics, 2)
errMetric := getMetricsForName(metrics, "x509_read_errors")
assert.Equal(t, 0., errMetric[0].GetGauge().GetValue())
})

// combined
testRequest(t, &Exporter{
Files: []string{"../test/**/*.pem"},
}, func(metrics []model.MetricFamily) {
foundMetrics := getMetricsForName(metrics, "x509_cert_expired")
assert.Len(t, foundMetrics, 6)
foundNbMetrics := getMetricsForName(metrics, "x509_cert_not_before")
assert.Len(t, foundNbMetrics, 6)
foundNaMetrics := getMetricsForName(metrics, "x509_cert_not_after")
assert.Len(t, foundNaMetrics, 6)
errMetric := getMetricsForName(metrics, "x509_read_errors")
assert.Equal(t, 4., errMetric[0].GetGauge().GetValue())
})

// invalid pattern
testRequest(t, &Exporter{
Files: []string{"toto["},
}, func(metrics []model.MetricFamily) {
foundMetrics := getMetricsForName(metrics, "x509_cert_expired")
assert.Len(t, foundMetrics, 0)
foundNbMetrics := getMetricsForName(metrics, "x509_cert_not_before")
assert.Len(t, foundNbMetrics, 0)
foundNaMetrics := getMetricsForName(metrics, "x509_cert_not_after")
assert.Len(t, foundNaMetrics, 0)
errMetric := getMetricsForName(metrics, "x509_read_errors")
assert.Equal(t, 1., errMetric[0].GetGauge().GetValue())
})
}

func TestListenError(t *testing.T) {
exporter := &Exporter{ListenAddress: "127.0.0.1:4242"}
err := exporter.Listen()
Expand Down

0 comments on commit 7fa2298

Please sign in to comment.