diff --git a/util/metrics/reporter.go b/util/metrics/reporter.go index 62a6bb26e4..78d9a67649 100644 --- a/util/metrics/reporter.go +++ b/util/metrics/reporter.go @@ -23,6 +23,7 @@ import ( "net/http" "os" "path/filepath" + "regexp" "strings" "time" // logging imports metrics so that we can have metrics about logging, which is more important than the four Debug lines we had here logging about metrics. TODO: find a more clever cycle resolution @@ -177,6 +178,30 @@ func (reporter *MetricReporter) tryDetachNodeExporter() { } } +// parseNodeExporterArgs parses the NodeExporterPath configuration string to extract Node Exporter's arguments. +func parseNodeExporterArgs(nodeExporterPath string, nodeExporterListenAddress string, nodeExporterMetricsPath string) []string { + whitespaceRE := regexp.MustCompile(`\s+`) + listenAddressRE := regexp.MustCompile(`--web.listen-address=(.+)`) + telemetryPathRE := regexp.MustCompile(`--web.telemetry-path=(.+)`) + vargs := whitespaceRE.Split(nodeExporterPath, -1) + temp := vargs[:0] + for _, varg := range vargs { + if listenAddressRE.MatchString(varg) { + nodeExporterListenAddress = listenAddressRE.FindStringSubmatch(varg)[1] + } else if telemetryPathRE.MatchString(varg) { + nodeExporterMetricsPath = telemetryPathRE.FindStringSubmatch(varg)[1] + } else if varg == "" { + continue + } else { + temp = append(temp, varg) + } + } + vargs = append(vargs[:len(temp)], + "--web.listen-address="+nodeExporterListenAddress, + "--web.telemetry-path="+nodeExporterMetricsPath) + return vargs +} + func (reporter *MetricReporter) tryInvokeNodeExporter(ctx context.Context) { var err error if nil == reporter.neSync { @@ -205,10 +230,7 @@ func (reporter *MetricReporter) tryInvokeNodeExporter(ctx context.Context) { os.Stderr} } // prepare the vargs that the new process is going to have. - vargs := []string{ - reporter.serviceConfig.NodeExporterPath, - "--web.listen-address=" + reporter.serviceConfig.NodeExporterListenAddress, - "--web.telemetry-path=" + nodeExporterMetricsPath} + vargs := parseNodeExporterArgs(reporter.serviceConfig.NodeExporterPath, reporter.serviceConfig.NodeExporterListenAddress, nodeExporterMetricsPath) // launch the process proc, err := os.StartProcess(vargs[0], vargs, &neAttributes) if err != nil { diff --git a/util/metrics/reporter_test.go b/util/metrics/reporter_test.go new file mode 100755 index 0000000000..5e5031a8ed --- /dev/null +++ b/util/metrics/reporter_test.go @@ -0,0 +1,52 @@ +// Copyright (C) 2019 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package metrics + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseNodeExporterArgs(t *testing.T) { + passTestcases := map[string][]string{ + "./node_exporter": []string{"./node_exporter", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // simple case + "./node_exporter --collector.systemd": []string{"./node_exporter", "--collector.systemd", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // extended case with one argument + "./node_exporter random --collector.systemd": []string{"./node_exporter", "random", "--collector.systemd", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // extended case multiple arguments + "/usr/bin/local/node_exporter --collector.systemd random": []string{"/usr/bin/local/node_exporter", "--collector.systemd", "random", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // other executable path + " /usr/bin/local/node_exporter --collector.systemd random": []string{"/usr/bin/local/node_exporter", "--collector.systemd", "random", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // space at beginning of option + "./node_exporter --web.telemetry-path=/foobar --web.listen-address=:9090 ": []string{"./node_exporter", "--web.listen-address=:9090", "--web.telemetry-path=/foobar"}, // overriding defaults + "./node_exporter --web.listen-address=:8080 --web.telemetry-path=/barfoo": []string{"./node_exporter", "--web.listen-address=:8080", "--web.telemetry-path=/barfoo"}, // overriding defaults different order and multiple spaces + "./node_exporter --web.listen-address=:9090 --collector.proc --web.telemetry-path=/foobar": []string{"./node_exporter", "--collector.proc", "--web.listen-address=:9090", "--web.telemetry-path=/foobar"}, // argument in between the persistent ones + "./node_exporter --web.listen-address=:9090 --collector.test --collector.systemd ": []string{"./node_exporter", "--collector.test", "--collector.systemd", "--web.listen-address=:9090", "--web.telemetry-path=/metrics"}, // argument after persistent one + } + for test, expected := range passTestcases { + vargs := parseNodeExporterArgs(test, ":9100", "/metrics") + require.Equalf(t, vargs, expected, "Argument parsing did not result in expected value for: %v, got: %v, want: %v.", test, vargs, expected) + } + + failTestcases := map[string][]string{ + "./node_exporter": []string{"./node_exporter", "--web.listen-address=:9090", "--web.telemetry-path=/foobar"}, // default arguments not being passed + "./node_exporter --collector.systemd": []string{"./node_exporter", "--web.listen-address=:9100", "--web.telemetry-path=/metrics", "--collector.systemd"}, // incorrect order of persistent and added options + "./node_exporter random --collector.systemd": []string{"./node_exporter", "--collector.systemd", "random", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // reversed order of persistent options + " /usr/bin/local/node_exporter --collector.systemd random": []string{" /usr/bin/local/node_exporter", "--collector.systemd", "random", "--web.listen-address=:9100", "--web.telemetry-path=/metrics"}, // space at beginning of option preserved + } + for test, notexpected := range failTestcases { + vargs := parseNodeExporterArgs(test, ":9100", "/metrics") + require.NotEqualf(t, vargs, notexpected, "Argument parsing did result in expected value for: %v, got: %v, want: %v.", test, vargs, notexpected) + } +}