textfile: Allow specifiying multiple directory globs. (#3135)
Some checks are pending
golangci-lint / lint (push) Waiting to run

We already support reading from multiple directories though only using globs. Now we can specify them outright.

Example use case is exporting both static info on a RO FS generated during image building and traditional uses of textfiles (e.g. for R/W service metrics files) without scripting a file copy.

* keep flag name for compatibility
* clarify flag help text
* add test case (replicating the glob one)

Signed-off-by: eduarrrd <eduarrrd@users.noreply.github.com>
This commit is contained in:
eduarrrd 2024-09-30 09:40:03 +02:00 committed by GitHub
parent b5ce6bcc4d
commit 11f93d3da1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 54 additions and 43 deletions

View file

@ -32,8 +32,8 @@ import (
)
var (
textFileDirectory = kingpin.Flag("collector.textfile.directory", "Directory to read text files with metrics from.").Default("").String()
mtimeDesc = prometheus.NewDesc(
textFileDirectories = kingpin.Flag("collector.textfile.directory", "Directory to read text files with metrics from, supports glob matching. (repeatable)").Default("").Strings()
mtimeDesc = prometheus.NewDesc(
"node_textfile_mtime_seconds",
"Unixtime mtime of textfiles successfully read.",
[]string{"file"},
@ -42,7 +42,7 @@ var (
)
type textFileCollector struct {
path string
paths []string
// Only set for testing to get predictable output.
mtime *float64
logger *slog.Logger
@ -56,7 +56,7 @@ func init() {
// in the given textfile directory.
func NewTextFileCollector(logger *slog.Logger) (Collector, error) {
c := &textFileCollector{
path: *textFileDirectory,
paths: *textFileDirectories,
logger: logger,
}
return c, nil
@ -194,11 +194,15 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error {
metricsNamesToFiles := map[string][]string{}
metricsNamesToHelpTexts := map[string][2]string{}
paths, err := filepath.Glob(c.path)
if err != nil || len(paths) == 0 {
// not glob or not accessible path either way assume single
// directory and let os.ReadDir handle it
paths = []string{c.path}
paths := []string{}
for _, glob := range c.paths {
ps, err := filepath.Glob(glob)
if err != nil || len(ps) == 0 {
// not glob or not accessible path either way assume single
// directory and let os.ReadDir handle it
ps = []string{glob}
}
paths = append(paths, ps...)
}
mtimes := make(map[string]time.Time)

View file

@ -52,75 +52,82 @@ func (a collectorAdapter) Collect(ch chan<- prometheus.Metric) {
func TestTextfileCollector(t *testing.T) {
tests := []struct {
path string
out string
paths []string
out string
}{
{
path: "fixtures/textfile/no_metric_files",
out: "fixtures/textfile/no_metric_files.out",
paths: []string{"fixtures/textfile/no_metric_files"},
out: "fixtures/textfile/no_metric_files.out",
},
{
path: "fixtures/textfile/two_metric_files",
out: "fixtures/textfile/two_metric_files.out",
paths: []string{"fixtures/textfile/two_metric_files"},
out: "fixtures/textfile/two_metric_files.out",
},
{
path: "fixtures/textfile/nonexistent_path",
out: "fixtures/textfile/nonexistent_path.out",
paths: []string{"fixtures/textfile/nonexistent_path"},
out: "fixtures/textfile/nonexistent_path.out",
},
{
path: "fixtures/textfile/client_side_timestamp",
out: "fixtures/textfile/client_side_timestamp.out",
paths: []string{"fixtures/textfile/client_side_timestamp"},
out: "fixtures/textfile/client_side_timestamp.out",
},
{
path: "fixtures/textfile/different_metric_types",
out: "fixtures/textfile/different_metric_types.out",
paths: []string{"fixtures/textfile/different_metric_types"},
out: "fixtures/textfile/different_metric_types.out",
},
{
path: "fixtures/textfile/inconsistent_metrics",
out: "fixtures/textfile/inconsistent_metrics.out",
paths: []string{"fixtures/textfile/inconsistent_metrics"},
out: "fixtures/textfile/inconsistent_metrics.out",
},
{
path: "fixtures/textfile/histogram",
out: "fixtures/textfile/histogram.out",
paths: []string{"fixtures/textfile/histogram"},
out: "fixtures/textfile/histogram.out",
},
{
path: "fixtures/textfile/histogram_extra_dimension",
out: "fixtures/textfile/histogram_extra_dimension.out",
paths: []string{"fixtures/textfile/histogram_extra_dimension"},
out: "fixtures/textfile/histogram_extra_dimension.out",
},
{
path: "fixtures/textfile/summary",
out: "fixtures/textfile/summary.out",
paths: []string{"fixtures/textfile/summary"},
out: "fixtures/textfile/summary.out",
},
{
path: "fixtures/textfile/summary_extra_dimension",
out: "fixtures/textfile/summary_extra_dimension.out",
paths: []string{"fixtures/textfile/summary_extra_dimension"},
out: "fixtures/textfile/summary_extra_dimension.out",
},
{
path: "fixtures/textfile/*_extra_dimension",
out: "fixtures/textfile/glob_extra_dimension.out",
paths: []string{
"fixtures/textfile/histogram_extra_dimension",
"fixtures/textfile/summary_extra_dimension",
},
out: "fixtures/textfile/glob_extra_dimension.out",
},
{
path: "fixtures/textfile/metrics_merge_empty_help",
out: "fixtures/textfile/metrics_merge_empty_help.out",
paths: []string{"fixtures/textfile/*_extra_dimension"},
out: "fixtures/textfile/glob_extra_dimension.out",
},
{
path: "fixtures/textfile/metrics_merge_no_help",
out: "fixtures/textfile/metrics_merge_no_help.out",
paths: []string{"fixtures/textfile/metrics_merge_empty_help"},
out: "fixtures/textfile/metrics_merge_empty_help.out",
},
{
path: "fixtures/textfile/metrics_merge_same_help",
out: "fixtures/textfile/metrics_merge_same_help.out",
paths: []string{"fixtures/textfile/metrics_merge_no_help"},
out: "fixtures/textfile/metrics_merge_no_help.out",
},
{
path: "fixtures/textfile/metrics_merge_different_help",
out: "fixtures/textfile/metrics_merge_different_help.out",
paths: []string{"fixtures/textfile/metrics_merge_same_help"},
out: "fixtures/textfile/metrics_merge_same_help.out",
},
{
paths: []string{"fixtures/textfile/metrics_merge_different_help"},
out: "fixtures/textfile/metrics_merge_different_help.out",
},
}
for i, test := range tests {
mtime := 1.0
c := &textFileCollector{
path: test.path,
paths: test.paths,
mtime: &mtime,
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
}
@ -146,7 +153,7 @@ func TestTextfileCollector(t *testing.T) {
}
if string(want) != got {
t.Fatalf("%d.%q want:\n\n%s\n\ngot:\n\n%s", i, test.path, string(want), got)
t.Fatalf("%d.%q want:\n\n%s\n\ngot:\n\n%s", i, test.paths, string(want), got)
}
}
}