diff options
author | Tobias Schmidt <tobidt@gmail.com> | 2017-03-13 23:55:19 -0300 |
---|---|---|
committer | Tobias Schmidt <tobidt@gmail.com> | 2017-03-14 00:38:02 -0300 |
commit | dace41e3d4c3dfc3a52fa73fcba322082dcce22a (patch) | |
tree | 8ed0e1883e34a17f72df94e2e8cd05581cf009aa /node_exporter_test.go | |
parent | a0a0dbaad0a53ce6d98eeb34cc8519299aba5155 (diff) | |
download | prometheus_node_collector-dace41e3d4c3dfc3a52fa73fcba322082dcce22a.tar.bz2 prometheus_node_collector-dace41e3d4c3dfc3a52fa73fcba322082dcce22a.tar.xz prometheus_node_collector-dace41e3d4c3dfc3a52fa73fcba322082dcce22a.zip |
Continue scrape with duplicated metrics
Problems of a single collector, like duplicated metrics read via the
textfile collector, should not fail the collection and export of other
metrics.
Diffstat (limited to 'node_exporter_test.go')
-rw-r--r-- | node_exporter_test.go | 132 |
1 files changed, 85 insertions, 47 deletions
diff --git a/node_exporter_test.go b/node_exporter_test.go index 4ff0caa..d1830ec 100644 --- a/node_exporter_test.go +++ b/node_exporter_test.go | |||
@@ -2,21 +2,23 @@ package main | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "io/ioutil" | ||
5 | "net/http" | 6 | "net/http" |
6 | "os" | 7 | "os" |
7 | "os/exec" | 8 | "os/exec" |
9 | "path/filepath" | ||
8 | "testing" | 10 | "testing" |
9 | "time" | 11 | "time" |
10 | 12 | ||
11 | "github.com/prometheus/procfs" | 13 | "github.com/prometheus/procfs" |
12 | ) | 14 | ) |
13 | 15 | ||
14 | func TestFileDescriptorLeak(t *testing.T) { | 16 | const ( |
15 | const ( | 17 | binary = "./node_exporter" |
16 | binary = "./node_exporter" | 18 | address = "localhost:19100" |
17 | address = "localhost:9100" | 19 | ) |
18 | ) | ||
19 | 20 | ||
21 | func TestFileDescriptorLeak(t *testing.T) { | ||
20 | if _, err := os.Stat(binary); err != nil { | 22 | if _, err := os.Stat(binary); err != nil { |
21 | t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err) | 23 | t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err) |
22 | } | 24 | } |
@@ -24,75 +26,111 @@ func TestFileDescriptorLeak(t *testing.T) { | |||
24 | t.Skipf("proc filesystem is not available, but currently required to read number of open file descriptors: %s", err) | 26 | t.Skipf("proc filesystem is not available, but currently required to read number of open file descriptors: %s", err) |
25 | } | 27 | } |
26 | 28 | ||
27 | errc := make(chan error) | ||
28 | exporter := exec.Command(binary, "-web.listen-address", address) | 29 | exporter := exec.Command(binary, "-web.listen-address", address) |
29 | go func() { | 30 | test := func(pid int) error { |
30 | if err := exporter.Run(); err != nil { | 31 | if err := queryExporter(address); err != nil { |
31 | errc <- fmt.Errorf("execution of node_exporter failed: %s", err) | 32 | return err |
32 | } else { | ||
33 | errc <- nil | ||
34 | } | ||
35 | }() | ||
36 | |||
37 | select { | ||
38 | case err := <-errc: | ||
39 | t.Fatal(err) | ||
40 | case <-time.After(100 * time.Millisecond): | ||
41 | } | ||
42 | |||
43 | go func(pid int, url string) { | ||
44 | if err := queryExporter(url); err != nil { | ||
45 | errc <- err | ||
46 | return | ||
47 | } | 33 | } |
48 | proc, err := procfs.NewProc(pid) | 34 | proc, err := procfs.NewProc(pid) |
49 | if err != nil { | 35 | if err != nil { |
50 | errc <- err | 36 | return err |
51 | return | ||
52 | } | 37 | } |
53 | fdsBefore, err := proc.FileDescriptors() | 38 | fdsBefore, err := proc.FileDescriptors() |
54 | if err != nil { | 39 | if err != nil { |
55 | errc <- err | 40 | return err |
56 | return | ||
57 | } | 41 | } |
58 | for i := 0; i < 5; i++ { | 42 | for i := 0; i < 5; i++ { |
59 | if err := queryExporter(url); err != nil { | 43 | if err := queryExporter(address); err != nil { |
60 | errc <- err | 44 | return err |
61 | return | ||
62 | } | 45 | } |
63 | } | 46 | } |
64 | fdsAfter, err := proc.FileDescriptors() | 47 | fdsAfter, err := proc.FileDescriptors() |
65 | if err != nil { | 48 | if err != nil { |
66 | errc <- err | 49 | return err |
67 | return | ||
68 | } | 50 | } |
69 | if want, have := len(fdsBefore), len(fdsAfter); want != have { | 51 | if want, have := len(fdsBefore), len(fdsAfter); want != have { |
70 | errc <- fmt.Errorf("want %d open file descriptors after metrics scrape, have %d", want, have) | 52 | return fmt.Errorf("want %d open file descriptors after metrics scrape, have %d", want, have) |
71 | } | 53 | } |
72 | errc <- nil | 54 | return nil |
73 | }(exporter.Process.Pid, fmt.Sprintf("http://%s/metrics", address)) | 55 | } |
74 | 56 | ||
75 | select { | 57 | if err := runCommandAndTests(exporter, test); err != nil { |
76 | case err := <-errc: | 58 | t.Error(err) |
77 | if exporter.Process != nil { | ||
78 | exporter.Process.Kill() | ||
79 | } | ||
80 | if err != nil { | ||
81 | t.Fatal(err) | ||
82 | } | ||
83 | } | 59 | } |
84 | } | 60 | } |
85 | 61 | ||
86 | func queryExporter(url string) error { | 62 | func TestHandlingOfDuplicatedMetrics(t *testing.T) { |
87 | resp, err := http.Get(url) | 63 | if _, err := os.Stat(binary); err != nil { |
64 | t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err) | ||
65 | } | ||
66 | |||
67 | dir, err := ioutil.TempDir("", "node-exporter") | ||
68 | if err != nil { | ||
69 | t.Fatal(err) | ||
70 | } | ||
71 | defer os.RemoveAll(dir) | ||
72 | |||
73 | content := []byte("dummy_metric 1\n") | ||
74 | if err := ioutil.WriteFile(filepath.Join(dir, "a.prom"), content, 0600); err != nil { | ||
75 | t.Fatal(err) | ||
76 | } | ||
77 | if err := ioutil.WriteFile(filepath.Join(dir, "b.prom"), content, 0600); err != nil { | ||
78 | t.Fatal(err) | ||
79 | } | ||
80 | |||
81 | exporter := exec.Command(binary, "-web.listen-address", address, "-collector.textfile.directory", dir) | ||
82 | test := func(_ int) error { | ||
83 | return queryExporter(address) | ||
84 | } | ||
85 | |||
86 | if err := runCommandAndTests(exporter, test); err != nil { | ||
87 | t.Error(err) | ||
88 | } | ||
89 | } | ||
90 | |||
91 | func queryExporter(address string) error { | ||
92 | resp, err := http.Get(fmt.Sprintf("http://%s/metrics", address)) | ||
88 | if err != nil { | 93 | if err != nil { |
89 | return err | 94 | return err |
90 | } | 95 | } |
91 | if err := resp.Body.Close(); err != nil { | 96 | if err := resp.Body.Close(); err != nil { |
92 | return err | 97 | return err |
93 | } | 98 | } |
94 | if want, have := resp.StatusCode, http.StatusOK; want != have { | 99 | if want, have := http.StatusOK, resp.StatusCode; want != have { |
95 | return fmt.Errorf("want /metrics status code %d, have %d", want, have) | 100 | return fmt.Errorf("want /metrics status code %d, have %d", want, have) |
96 | } | 101 | } |
97 | return nil | 102 | return nil |
98 | } | 103 | } |
104 | |||
105 | func runCommandAndTests(cmd *exec.Cmd, fn func(pid int) error) error { | ||
106 | errc := make(chan error) | ||
107 | go func() { | ||
108 | if err := cmd.Run(); err != nil { | ||
109 | errc <- fmt.Errorf("execution of command failed: %s", err) | ||
110 | } else { | ||
111 | errc <- nil | ||
112 | } | ||
113 | }() | ||
114 | |||
115 | // Allow the process to start before running any tests. | ||
116 | select { | ||
117 | case err := <-errc: | ||
118 | return err | ||
119 | case <-time.After(100 * time.Millisecond): | ||
120 | } | ||
121 | |||
122 | go func(pid int) { | ||
123 | errc <- fn(pid) | ||
124 | }(cmd.Process.Pid) | ||
125 | |||
126 | select { | ||
127 | case err := <-errc: | ||
128 | if cmd.Process != nil { | ||
129 | cmd.Process.Kill() | ||
130 | } | ||
131 | if err != nil { | ||
132 | return err | ||
133 | } | ||
134 | } | ||
135 | return nil | ||
136 | } | ||