textfile: use opened file's mtime as timestamp (#1326)

Previously, the node_textfile_mtime_seconds metric was based on the
Fileinfo.ModTime() of the ioutil.ReadDir() return value. This is based
on lstat() and therefore has unintended consequences for symlinks
(modification time of the symlink instead of the symlink target is
returned). It is also racy as the lstat() is performed before reading
the file.

This commit changes the node_textfile_mtime_seconds metric to be based
on a fresh Stat() call on the open file.  This eliminates the race and
works as expected for symlinks. Fixes #1324.

Signed-off-by: Christian Hoffmann <mail@hoffmann-christian.info>
This commit is contained in:
Christian Hoffmann 2019-04-18 17:47:04 +02:00 committed by Ben Kochie
parent 5b4140e0bd
commit 36e3b2a923
2 changed files with 9 additions and 2 deletions

View File

@ -14,6 +14,7 @@
### Changes
* [BUGFIX]
* [BUGFIX] Fix node_textfile_mtime_seconds to work properly on symlinks #1326
* [CHANGE] Renamed `interface` label to `device` in netclass collector #1224
* [BUGFIX] Add fallback for missing /proc/1/mounts #1172
* [CHANGE] Add TCPSynRetrans to netstat default filter #1143

View File

@ -204,9 +204,9 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error {
error = 1.0
continue
}
defer file.Close()
var parser expfmt.TextParser
parsedFamilies, err := parser.TextToMetricFamilies(file)
file.Close()
if err != nil {
log.Errorf("Error parsing %q: %v", path, err)
error = 1.0
@ -227,7 +227,13 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error {
// Only set this once it has been parsed and validated, so that
// a failure does not appear fresh.
mtimes[f.Name()] = f.ModTime()
stat, err := file.Stat()
if err != nil {
log.Errorf("Error stat'ing %q: %v", path, err)
error = 1.0
continue
}
mtimes[f.Name()] = stat.ModTime()
for _, mf := range parsedFamilies {
convertMetricFamily(mf, ch)