Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add support for configuring labels emitted in metrics #70

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions cmd/node-cert-exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ var BRANCH string
var GOVERSION string

var (
host string
port int
listen string
paths []string
excludePaths []string
includeGlobs []string
excludeGlobs []string
tls bool
tlsCertFile string
tlsKeyFile string
host string
port int
listen string
paths []string
excludePaths []string
includeGlobs []string
excludeGlobs []string
includeLabels []string
tls bool
tlsCertFile string
tlsKeyFile string
)

func init() {
Expand All @@ -46,6 +47,7 @@ func init() {
pflag.StringSliceVar(&excludePaths, "exclude-path", []string{}, "List of paths to exclute from searching for SSL certificates.")
pflag.StringSliceVar(&includeGlobs, "include-glob", []string{}, "List files matching a pattern to include. This flag can be used multiple times.")
pflag.StringSliceVar(&excludeGlobs, "exclude-glob", []string{}, "List files matching a pattern to exclude. This flag can be used multiple times.")
pflag.StringSliceVar(&includeLabels, "include-labels", []string{}, "List of labels to include in emitted metrics. This flag can be used multiple times. Default is all labels.")
pflag.BoolVar(&tls, "tls", false, "Enable TLS for node-cert-exporter. Defaults to false.")
pflag.StringVar(&tlsCertFile, "tls-cert-file", "", "Path to a TLS certificate to use when serving. Required for TLS.")
pflag.StringVar(&tlsKeyFile, "tls-key-file", "", "Path to a TLS private key to use when serving. Required for TLS.")
Expand All @@ -66,7 +68,7 @@ func main() {
return
}

e := exporter.New()
e := exporter.New(includeLabels...)
e.SetRoots(paths)
e.SetExcludeRoots(excludePaths)
e.IncludeGlobs(includeGlobs)
Expand Down
48 changes: 35 additions & 13 deletions pkg/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ func getFirstCertBlock(data []byte) []byte {

// Exporter implements prometheus.Collector interface
type Exporter struct {
mux sync.Mutex
includeGlobs []string
excludeGlobs []string
roots []string
exRoots []string
certExpiry *prometheus.GaugeVec
certFailed *prometheus.GaugeVec
mux sync.Mutex
includeGlobs []string
excludeGlobs []string
roots []string
exRoots []string
includeLabels []string
certExpiry *prometheus.GaugeVec
certFailed *prometheus.GaugeVec
}

// IncludeGlobs sets the list of file globs the exporter uses to match certificate files for scraping
Expand Down Expand Up @@ -87,7 +88,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {

// Describe satisfies prometheus.Collector interface
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- e.certExpiry.WithLabelValues("path", "issuer", "alg", "version", "subject", "dns_names", "email_addresses", "hostname", "nodename").Desc()
ch <- e.certExpiry.WithLabelValues(e.includeLabels...).Desc()
}

// Scrape iterates over the list of file paths (set by SetRoot) and parses any found x509 certificates.
Expand Down Expand Up @@ -159,7 +160,7 @@ func (e *Exporter) Scrape(ch chan<- prometheus.Metric) {
continue
}

labels := prometheus.Labels{
defaultPromLabels := prometheus.Labels{
"path": path,
"issuer": cert.Issuer.String(),
"alg": cert.SignatureAlgorithm.String(),
Expand All @@ -170,24 +171,45 @@ func (e *Exporter) Scrape(ch chan<- prometheus.Metric) {
"hostname": hostname,
"nodename": nodename,
}
var promLabels = make(prometheus.Labels)
for _, l := range e.includeLabels {
if val, ok := defaultPromLabels[l]; ok {
promLabels[l] = val
}
}

since := time.Until(cert.NotAfter)
e.certExpiry.With(labels).Set(since.Seconds())
ch <- e.certExpiry.With(labels)
e.certExpiry.With(promLabels).Set(since.Seconds())
ch <- e.certExpiry.With(promLabels)
}

}

// New creates an instance of Exporter and returns it
func New() *Exporter {
func New(includeLabels ...string) *Exporter {
defaultPromLabels := []string{"path", "issuer", "alg", "version", "subject", "dns_names", "email_addresses", "hostname", "nodename"}
promLabels := []string{}

if len(includeLabels) == 0 {
promLabels = defaultPromLabels
} else {
for _, i := range includeLabels {
for _, f := range defaultPromLabels {
if i == f {
promLabels = append(promLabels, i)
}
}
}
}
return &Exporter{
includeLabels: promLabels,
certExpiry: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "ssl_certificate",
Subsystem: "expiry",
Name: "seconds",
Help: "Number of seconds until certificate expires",
},
[]string{"path", "issuer", "alg", "version", "subject", "dns_names", "email_addresses", "hostname", "nodename"}),
promLabels),
certFailed: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "ssl_certificate",
Subsystem: "expiry",
Expand Down
21 changes: 21 additions & 0 deletions pkg/exporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package exporter
import (
"bytes"
"encoding/pem"
"reflect"
"strings"
"testing"
)
Expand Down Expand Up @@ -93,3 +94,23 @@ func TestGetFirstCertBlock(t *testing.T) {
}
}
}

func TestNewExporterWithIncludeLabels(t *testing.T) {
e := New("alg", "path")
w := []string{"alg", "path"}
if !reflect.DeepEqual(e.includeLabels, w) {
t.Errorf("configuring prometheus labels in New() did not return successfully")
t.Errorf("Have: %v", e.includeLabels)
t.Errorf("Want: %v", w)
}
}

func TestNewExporterWithDefaultLabels(t *testing.T) {
e := New()
w := []string{"path", "issuer", "alg", "version", "subject", "dns_names", "email_addresses", "hostname", "nodename"}
if !reflect.DeepEqual(e.includeLabels, w) {
t.Errorf("default prometheus labels in New() did not return successfully")
t.Errorf("Have: %v", e.includeLabels)
t.Errorf("Want: %v", w)
}
}