1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,71 @@
# Filecount Input Plugin
This plugin reports the number and total size of files in specified directories.
⭐ Telegraf v1.8.0
🏷️ system
💻 all
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Count files in a directory
[[inputs.filecount]]
## Directories to gather stats about.
## This accept standard unit glob matching rules, but with the addition of
## ** as a "super asterisk". ie:
## /var/log/** -> recursively find all directories in /var/log and count files in each directories
## /var/log/*/* -> find all directories with a parent dir in /var/log and count files in each directories
## /var/log -> count all files in /var/log and all of its subdirectories
directories = ["/var/cache/apt", "/tmp"]
## Only count files that match the name pattern. Defaults to "*".
name = "*"
## Count files in subdirectories. Defaults to true.
recursive = true
## Only count regular files. Defaults to true.
regular_only = true
## Follow all symlinks while walking the directory tree. Defaults to false.
follow_symlinks = false
## Only count files that are at least this size. If size is
## a negative number, only count files that are smaller than the
## absolute value of size. Acceptable units are B, KiB, MiB, KB, ...
## Without quotes and units, interpreted as size in bytes.
size = "0B"
## Only count files that have not been touched for at least this
## duration. If mtime is negative, only count files that have been
## touched in this duration. Defaults to "0s".
mtime = "0s"
```
## Metrics
- filecount
- tags:
- directory (the directory path)
- fields:
- count (integer)
- size_bytes (integer)
- oldest_file_timestamp (int, unix time nanoseconds)
- newest_file_timestamp (int, unix time nanoseconds)
## Example Output
```text
filecount,directory=/var/cache/apt count=7i,size_bytes=7438336i,oldest_file_timestamp=1507152973123456789i,newest_file_timestamp=1507152973123456789i 1530034445000000000
filecount,directory=/tmp count=17i,size_bytes=28934786i,oldest_file_timestamp=1507152973123456789i,newest_file_timestamp=1507152973123456789i 1530034445000000000
```

View file

@ -0,0 +1,318 @@
//go:generate ../../../tools/readme_config_includer/generator
package filecount
import (
_ "embed"
"errors"
"io/fs"
"os"
"path/filepath"
"time"
"github.com/karrick/godirwalk"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal/globpath"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
type FileCount struct {
Directory string `toml:"directory" deprecated:"1.9.0;1.35.0;use 'directories' instead"`
Directories []string `toml:"directories"`
Name string `toml:"name"`
Recursive bool `toml:"recursive"`
RegularOnly bool `toml:"regular_only"`
FollowSymlinks bool `toml:"follow_symlinks"`
Size config.Size `toml:"size"`
MTime config.Duration `toml:"mtime"`
Log telegraf.Logger `toml:"-"`
fs fileSystem
fileFilters []fileFilterFunc
globPaths []globpath.GlobPath
}
type fileFilterFunc func(os.FileInfo) (bool, error)
func (*FileCount) SampleConfig() string {
return sampleConfig
}
func (fc *FileCount) Gather(acc telegraf.Accumulator) error {
if fc.globPaths == nil {
fc.initGlobPaths(acc)
}
for _, glob := range fc.globPaths {
for _, dir := range fc.onlyDirectories(glob.GetRoots()) {
fc.count(acc, dir, glob)
}
}
return nil
}
func rejectNilFilters(filters []fileFilterFunc) []fileFilterFunc {
filtered := make([]fileFilterFunc, 0, len(filters))
for _, f := range filters {
if f != nil {
filtered = append(filtered, f)
}
}
return filtered
}
func (fc *FileCount) nameFilter() fileFilterFunc {
if fc.Name == "*" {
return nil
}
return func(f os.FileInfo) (bool, error) {
match, err := filepath.Match(fc.Name, f.Name())
if err != nil {
return false, err
}
return match, nil
}
}
func (fc *FileCount) regularOnlyFilter() fileFilterFunc {
if !fc.RegularOnly {
return nil
}
return func(f os.FileInfo) (bool, error) {
return f.Mode().IsRegular(), nil
}
}
func (fc *FileCount) sizeFilter() fileFilterFunc {
if fc.Size == 0 {
return nil
}
return func(f os.FileInfo) (bool, error) {
if !f.Mode().IsRegular() {
return false, nil
}
if fc.Size < 0 {
return f.Size() < -int64(fc.Size), nil
}
return f.Size() >= int64(fc.Size), nil
}
}
func (fc *FileCount) mtimeFilter() fileFilterFunc {
if time.Duration(fc.MTime) == 0 {
return nil
}
return func(f os.FileInfo) (bool, error) {
age := absDuration(time.Duration(fc.MTime))
mtime := time.Now().Add(-age)
if time.Duration(fc.MTime) < 0 {
return f.ModTime().After(mtime), nil
}
return f.ModTime().Before(mtime), nil
}
}
func absDuration(x time.Duration) time.Duration {
if x < 0 {
return -x
}
return x
}
func (fc *FileCount) initFileFilters() {
filters := []fileFilterFunc{
fc.nameFilter(),
fc.regularOnlyFilter(),
fc.sizeFilter(),
fc.mtimeFilter(),
}
fc.fileFilters = rejectNilFilters(filters)
}
func (fc *FileCount) count(acc telegraf.Accumulator, basedir string, glob globpath.GlobPath) {
childCount := make(map[string]int64)
childSize := make(map[string]int64)
oldestFileTimestamp := make(map[string]int64)
newestFileTimestamp := make(map[string]int64)
walkFn := func(path string, _ *godirwalk.Dirent) error {
rel, err := filepath.Rel(basedir, path)
if err == nil && rel == "." {
return nil
}
file, err := fc.resolveLink(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
match, err := fc.filter(file)
if err != nil {
acc.AddError(err)
return nil
}
if match {
parent := filepath.Dir(path)
childCount[parent]++
childSize[parent] += file.Size()
if oldestFileTimestamp[parent] == 0 || oldestFileTimestamp[parent] > file.ModTime().UnixNano() {
oldestFileTimestamp[parent] = file.ModTime().UnixNano()
}
if newestFileTimestamp[parent] == 0 || newestFileTimestamp[parent] < file.ModTime().UnixNano() {
newestFileTimestamp[parent] = file.ModTime().UnixNano()
}
}
if file.IsDir() && !fc.Recursive && !glob.HasSuperMeta {
return filepath.SkipDir
}
return nil
}
postChildrenFn := func(path string, _ *godirwalk.Dirent) error {
if glob.MatchString(path) {
gauge := map[string]interface{}{
"count": childCount[path],
"size_bytes": childSize[path],
}
gauge["oldest_file_timestamp"] = oldestFileTimestamp[path]
gauge["newest_file_timestamp"] = newestFileTimestamp[path]
acc.AddGauge("filecount", gauge,
map[string]string{
"directory": path,
})
}
parent := filepath.Dir(path)
if fc.Recursive {
childCount[parent] += childCount[path]
childSize[parent] += childSize[path]
if oldestFileTimestamp[parent] == 0 || oldestFileTimestamp[parent] > oldestFileTimestamp[path] {
oldestFileTimestamp[parent] = oldestFileTimestamp[path]
}
if newestFileTimestamp[parent] == 0 || newestFileTimestamp[parent] < newestFileTimestamp[path] {
newestFileTimestamp[parent] = newestFileTimestamp[path]
}
}
delete(childCount, path)
delete(childSize, path)
delete(oldestFileTimestamp, path)
delete(newestFileTimestamp, path)
return nil
}
err := godirwalk.Walk(basedir, &godirwalk.Options{
Callback: walkFn,
PostChildrenCallback: postChildrenFn,
Unsorted: true,
FollowSymbolicLinks: fc.FollowSymlinks,
ErrorCallback: func(_ string, err error) godirwalk.ErrorAction {
if errors.Is(err, fs.ErrPermission) {
fc.Log.Debug(err)
return godirwalk.SkipNode
}
return godirwalk.Halt
},
})
if err != nil {
acc.AddError(err)
}
}
func (fc *FileCount) filter(file os.FileInfo) (bool, error) {
if fc.fileFilters == nil {
fc.initFileFilters()
}
for _, fileFilter := range fc.fileFilters {
match, err := fileFilter(file)
if err != nil {
return false, err
}
if !match {
return false, nil
}
}
return true, nil
}
func (fc *FileCount) resolveLink(path string) (os.FileInfo, error) {
if fc.FollowSymlinks {
return fc.fs.stat(path)
}
fi, err := fc.fs.lstat(path)
if err != nil {
return fi, err
}
if fi.Mode()&os.ModeSymlink != 0 {
// if this file is a symlink, skip it
return nil, godirwalk.SkipThis
}
return fi, nil
}
func (fc *FileCount) onlyDirectories(directories []string) []string {
out := make([]string, 0)
for _, path := range directories {
info, err := fc.fs.stat(path)
if err == nil && info.IsDir() {
out = append(out, path)
}
}
return out
}
func (fc *FileCount) getDirs() []string {
dirs := make([]string, 0, len(fc.Directories)+1)
for _, dir := range fc.Directories {
dirs = append(dirs, filepath.Clean(dir))
}
if fc.Directory != "" {
dirs = append(dirs, filepath.Clean(fc.Directory))
}
return dirs
}
func (fc *FileCount) initGlobPaths(acc telegraf.Accumulator) {
dirs := fc.getDirs()
fc.globPaths = make([]globpath.GlobPath, 0, len(dirs))
for _, directory := range dirs {
glob, err := globpath.Compile(directory)
if err != nil {
acc.AddError(err)
} else {
fc.globPaths = append(fc.globPaths, *glob)
}
}
}
func newFileCount() *FileCount {
return &FileCount{
Directory: "",
Name: "*",
Recursive: true,
RegularOnly: true,
FollowSymlinks: false,
Size: config.Size(0),
MTime: config.Duration(0),
fileFilters: nil,
fs: osFS{},
}
}
func init() {
inputs.Add("filecount", func() telegraf.Input {
return newFileCount()
})
}

View file

@ -0,0 +1,249 @@
//go:build !windows
// TODO: Windows - should be enabled for Windows when super asterisk is fixed on Windows
// https://github.com/influxdata/telegraf/issues/6248
package filecount
import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/testutil"
)
func TestNoFilters(t *testing.T) {
fc := getNoFilterFileCount()
matches := []string{"foo", "bar", "baz", "qux",
"subdir/", "subdir/quux", "subdir/quuz",
"subdir/nested2", "subdir/nested2/qux"}
fileCountEquals(t, fc, len(matches), 5096)
}
func TestNoFiltersOnChildDir(t *testing.T) {
fc := getNoFilterFileCount()
fc.Directories = []string{getTestdataDir() + "/*"}
matches := []string{"subdir/quux", "subdir/quuz",
"subdir/nested2/qux", "subdir/nested2"}
tags := map[string]string{"directory": getTestdataDir() + "/subdir"}
acc := testutil.Accumulator{}
require.NoError(t, acc.GatherError(fc.Gather))
require.True(t, acc.HasPoint("filecount", tags, "count", int64(len(matches))))
require.True(t, acc.HasPoint("filecount", tags, "size_bytes", int64(600)))
require.True(t, acc.HasInt64Field("filecount", "oldest_file_timestamp"))
require.True(t, acc.HasInt64Field("filecount", "newest_file_timestamp"))
}
func TestNoRecursiveButSuperMeta(t *testing.T) {
fc := getNoFilterFileCount()
fc.Recursive = false
fc.Directories = []string{getTestdataDir() + "/**"}
matches := []string{"subdir/quux", "subdir/quuz", "subdir/nested2"}
tags := map[string]string{"directory": getTestdataDir() + "/subdir"}
acc := testutil.Accumulator{}
require.NoError(t, acc.GatherError(fc.Gather))
require.True(t, acc.HasPoint("filecount", tags, "count", int64(len(matches))))
require.True(t, acc.HasPoint("filecount", tags, "size_bytes", int64(200)))
require.True(t, acc.HasInt64Field("filecount", "oldest_file_timestamp"))
require.True(t, acc.HasInt64Field("filecount", "newest_file_timestamp"))
}
func TestNameFilter(t *testing.T) {
fc := getNoFilterFileCount()
fc.Name = "ba*"
matches := []string{"bar", "baz"}
fileCountEquals(t, fc, len(matches), 0)
}
func TestNonRecursive(t *testing.T) {
fc := getNoFilterFileCount()
fc.Recursive = false
matches := []string{"foo", "bar", "baz", "qux", "subdir"}
fileCountEquals(t, fc, len(matches), 4496)
}
func TestDoubleAndSimpleStar(t *testing.T) {
fc := getNoFilterFileCount()
fc.Directories = []string{getTestdataDir() + "/**/*"}
matches := []string{"qux"}
tags := map[string]string{"directory": getTestdataDir() + "/subdir/nested2"}
acc := testutil.Accumulator{}
require.NoError(t, acc.GatherError(fc.Gather))
require.True(t, acc.HasPoint("filecount", tags, "count", int64(len(matches))))
require.True(t, acc.HasPoint("filecount", tags, "size_bytes", int64(400)))
}
func TestRegularOnlyFilter(t *testing.T) {
fc := getNoFilterFileCount()
fc.RegularOnly = true
matches := []string{
"foo", "bar", "baz", "qux", "subdir/quux", "subdir/quuz",
"subdir/nested2/qux"}
fileCountEquals(t, fc, len(matches), 800)
}
func TestSizeFilter(t *testing.T) {
fc := getNoFilterFileCount()
fc.Size = config.Size(-100)
matches := []string{"foo", "bar", "baz",
"subdir/quux", "subdir/quuz"}
fileCountEquals(t, fc, len(matches), 0)
fc.Size = config.Size(100)
matches = []string{"qux", "subdir/nested2//qux"}
fileCountEquals(t, fc, len(matches), 800)
}
func TestMTimeFilter(t *testing.T) {
mtime := time.Date(2011, time.December, 14, 18, 25, 5, 0, time.UTC)
fileAge := time.Since(mtime) - (60 * time.Second)
fc := getNoFilterFileCount()
fc.MTime = config.Duration(-fileAge)
matches := []string{"foo", "bar", "qux",
"subdir/", "subdir/quux", "subdir/quuz",
"subdir/nested2", "subdir/nested2/qux"}
fileCountEquals(t, fc, len(matches), 5096)
fc.MTime = config.Duration(fileAge)
matches = []string{"baz"}
fileCountEquals(t, fc, len(matches), 0)
}
// The library dependency karrick/godirwalk completely abstracts out the
// behavior of the FollowSymlinks plugin input option. However, it should at
// least behave identically when enabled on a filesystem with no symlinks.
func TestFollowSymlinks(t *testing.T) {
fc := getNoFilterFileCount()
fc.FollowSymlinks = true
matches := []string{"foo", "bar", "baz", "qux",
"subdir/", "subdir/quux", "subdir/quuz",
"subdir/nested2", "subdir/nested2/qux"}
fileCountEquals(t, fc, len(matches), 5096)
}
// Paths with a trailing slash will not exactly match paths produced during the
// walk as these paths are cleaned before being returned from godirwalk. #6329
func TestDirectoryWithTrailingSlash(t *testing.T) {
plugin := &FileCount{
Directories: []string{getTestdataDir() + string(filepath.Separator)},
Name: "*",
Recursive: true,
fs: getFakeFileSystem(getTestdataDir()),
}
var acc testutil.Accumulator
err := plugin.Gather(&acc)
require.NoError(t, err)
expected := []telegraf.Metric{
testutil.MustMetric(
"filecount",
map[string]string{
"directory": getTestdataDir(),
},
map[string]interface{}{
"count": 9,
"size_bytes": 5096,
"newest_file_timestamp": time.Unix(1450117505, 0).UnixNano(),
"oldest_file_timestamp": time.Unix(1292351105, 0).UnixNano(),
},
time.Unix(0, 0),
telegraf.Gauge,
),
}
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
}
func getNoFilterFileCount() FileCount {
return FileCount{
Log: testutil.Logger{},
Directories: []string{getTestdataDir()},
Name: "*",
Recursive: true,
RegularOnly: false,
Size: config.Size(0),
MTime: config.Duration(0),
fileFilters: nil,
fs: getFakeFileSystem(getTestdataDir()),
}
}
func getTestdataDir() string {
dir, err := os.Getwd()
if err != nil {
// if we cannot even establish the test directory, further progress is meaningless
panic(err)
}
var chunks []string
var testDirectory string
//nolint:staticcheck // Silence linter for now as we plan to reenable tests for Windows later
if runtime.GOOS == "windows" {
chunks = strings.Split(dir, "\\")
testDirectory = strings.Join(chunks[:], "\\") + "\\testdata"
} else {
chunks = strings.Split(dir, "/")
testDirectory = strings.Join(chunks[:], "/") + "/testdata"
}
return testDirectory
}
func getFakeFileSystem(basePath string) fakeFileSystem {
// create our desired "filesystem" object, complete with an internal map allowing our funcs to return meta data as requested
mtime := time.Date(2015, time.December, 14, 18, 25, 5, 0, time.UTC)
olderMtime := time.Date(2010, time.December, 14, 18, 25, 5, 0, time.UTC)
// set file permissions
var fmask uint32 = 0666
var dmask uint32 = 0666
// set directory bit
dmask |= 1 << uint(32-1)
// create a lookup map for getting "files" from the "filesystem"
fileList := map[string]fakeFileInfo{
basePath: {name: "testdata", size: int64(4096), filemode: dmask, modtime: mtime, isdir: true},
basePath + "/foo": {name: "foo", filemode: fmask, modtime: mtime},
basePath + "/bar": {name: "bar", filemode: fmask, modtime: mtime},
basePath + "/baz": {name: "baz", filemode: fmask, modtime: olderMtime},
basePath + "/qux": {name: "qux", size: int64(400), filemode: fmask, modtime: mtime},
basePath + "/subdir": {name: "subdir", size: int64(4096), filemode: dmask, modtime: mtime, isdir: true},
basePath + "/subdir/quux": {name: "quux", filemode: fmask, modtime: mtime},
basePath + "/subdir/quuz": {name: "quuz", filemode: fmask, modtime: mtime},
basePath + "/subdir/nested2": {name: "nested2", size: int64(200), filemode: dmask, modtime: mtime, isdir: true},
basePath + "/subdir/nested2/qux": {name: "qux", filemode: fmask, modtime: mtime, size: int64(400)},
}
return fakeFileSystem{files: fileList}
}
func fileCountEquals(t *testing.T, fc FileCount, expectedCount, expectedSize int) {
tags := map[string]string{"directory": getTestdataDir()}
acc := testutil.Accumulator{}
require.NoError(t, acc.GatherError(fc.Gather))
require.True(t, acc.HasPoint("filecount", tags, "count", int64(expectedCount)))
require.True(t, acc.HasPoint("filecount", tags, "size_bytes", int64(expectedSize)))
}

View file

@ -0,0 +1,33 @@
package filecount
import (
"io"
"os"
)
/*
The code below is lifted from numerous articles and originates from Andrew Gerrand's 10 things you (probably) don't know about Go.
It allows for mocking a filesystem; this allows for consistent testing of this code across platforms (directory sizes reported
differently by different platforms, for example), while preserving the rest of the functionality as-is, without modification.
*/
type fileSystem interface {
open(name string) (file, error)
stat(name string) (os.FileInfo, error)
lstat(name string) (os.FileInfo, error)
}
type file interface {
io.Closer
io.Reader
io.ReaderAt
io.Seeker
Stat() (os.FileInfo, error)
}
// osFS implements fileSystem using the local disk
type osFS struct{}
func (osFS) open(name string) (file, error) { return os.Open(name) }
func (osFS) stat(name string) (os.FileInfo, error) { return os.Stat(name) }
func (osFS) lstat(name string) (os.FileInfo, error) { return os.Lstat(name) }

View file

@ -0,0 +1,57 @@
//go:build !windows
// TODO: These types are not used in Windows tests because they are disabled for Windows.
// They can be moved to filesystem_helpers.go when following bug is fixed:
// https://github.com/influxdata/telegraf/issues/6248
package filecount
import (
"errors"
"os"
"time"
)
/*
The following are for mocking the filesystem - this allows us to mock Stat() files. This means that we can set file attributes, and know that they
will be the same regardless of the platform sitting underneath our tests (directory sizes vary see https://github.com/influxdata/telegraf/issues/6011)
NOTE: still need the on-disk file structure to mirror this because the 3rd party library ("github.com/karrick/godirwalk") uses its own
walk functions, that we cannot mock from here.
*/
type fakeFileSystem struct {
files map[string]fakeFileInfo
}
type fakeFileInfo struct {
name string
size int64
filemode uint32
modtime time.Time
isdir bool
sys interface{}
}
func (f fakeFileInfo) Name() string { return f.name }
func (f fakeFileInfo) Size() int64 { return f.size }
func (f fakeFileInfo) Mode() os.FileMode { return os.FileMode(f.filemode) }
func (f fakeFileInfo) ModTime() time.Time { return f.modtime }
func (f fakeFileInfo) IsDir() bool { return f.isdir }
func (f fakeFileInfo) Sys() interface{} { return f.sys }
func (fakeFileSystem) open(name string) (file, error) {
return nil, &os.PathError{Op: "Open", Path: name, Err: errors.New("not implemented by fake filesystem")}
}
func (f fakeFileSystem) stat(name string) (os.FileInfo, error) {
if fakeInfo, found := f.files[name]; found {
return fakeInfo, nil
}
return nil, &os.PathError{Op: "Stat", Path: name, Err: errors.New("no such file or directory")}
}
func (f fakeFileSystem) lstat(name string) (os.FileInfo, error) {
// not able to test with symlinks currently
return f.stat(name)
}

View file

@ -0,0 +1,93 @@
//go:build !windows
// TODO: Windows - should be enabled for Windows when super asterisk is fixed on Windows
// https://github.com/influxdata/telegraf/issues/6248
package filecount
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestMTime(t *testing.T) {
// this is the time our foo file should have
mtime := time.Date(2015, time.December, 14, 18, 25, 5, 0, time.UTC)
fs := getTestFileSystem()
fileInfo, err := fs.stat("/testdata/foo")
require.NoError(t, err)
require.Equal(t, mtime, fileInfo.ModTime())
}
func TestSize(t *testing.T) {
// this is the time our foo file should have
size := int64(4096)
fs := getTestFileSystem()
fileInfo, err := fs.stat("/testdata")
require.NoError(t, err)
require.Equal(t, size, fileInfo.Size())
}
func TestIsDir(t *testing.T) {
// this is the time our foo file should have
fs := getTestFileSystem()
fileInfo, err := fs.stat("/testdata")
require.NoError(t, err)
require.True(t, fileInfo.IsDir())
}
func TestRealFS(t *testing.T) {
// test that the default (non-test) empty FS causes expected behaviour
var fs fileSystem = osFS{}
// the following file exists on disk - and not in our fake fs
fileInfo, err := fs.stat(getTestdataDir() + "/qux")
require.NoError(t, err)
require.False(t, fileInfo.IsDir())
require.Equal(t, int64(446), fileInfo.Size())
// now swap out real, for fake filesystem
fs = getTestFileSystem()
// now, the same test as above will return an error as the file doesn't exist in our fake fs
expectedError := "Stat " + getTestdataDir() + "/qux: No such file or directory"
_, err = fs.stat(getTestdataDir() + "/qux")
require.Error(t, err, expectedError)
// and verify that what we DO expect to find, we do
fileInfo, err = fs.stat("/testdata/foo")
require.NoError(t, err)
require.NotNil(t, fileInfo)
}
func getTestFileSystem() fakeFileSystem {
/*
create our desired "filesystem" object, complete with an internal map allowing our funcs to return meta data as requested
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes of file
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // returns bool indicating if a Dir or not
Sys() interface{} // underlying data source. always nil (in this case)
}
*/
mtime := time.Date(2015, time.December, 14, 18, 25, 5, 0, time.UTC)
// set file permissions
var fmask uint32 = 0666
var dmask uint32 = 0666
// set directory bit
dmask |= 1 << uint(32-1)
fileList := map[string]fakeFileInfo{
"/testdata": {name: "testdata", size: int64(4096), filemode: dmask, modtime: mtime, isdir: true},
"/testdata/foo": {name: "foo", filemode: fmask, modtime: mtime},
}
return fakeFileSystem{files: fileList}
}

View file

@ -0,0 +1,32 @@
# Count files in a directory
[[inputs.filecount]]
## Directories to gather stats about.
## This accept standard unit glob matching rules, but with the addition of
## ** as a "super asterisk". ie:
## /var/log/** -> recursively find all directories in /var/log and count files in each directories
## /var/log/*/* -> find all directories with a parent dir in /var/log and count files in each directories
## /var/log -> count all files in /var/log and all of its subdirectories
directories = ["/var/cache/apt", "/tmp"]
## Only count files that match the name pattern. Defaults to "*".
name = "*"
## Count files in subdirectories. Defaults to true.
recursive = true
## Only count regular files. Defaults to true.
regular_only = true
## Follow all symlinks while walking the directory tree. Defaults to false.
follow_symlinks = false
## Only count files that are at least this size. If size is
## a negative number, only count files that are smaller than the
## absolute value of size. Acceptable units are B, KiB, MiB, KB, ...
## Without quotes and units, interpreted as size in bytes.
size = "0B"
## Only count files that have not been touched for at least this
## duration. If mtime is negative, only count files that have been
## touched in this duration. Defaults to "0s".
mtime = "0s"

0
plugins/inputs/filecount/testdata/bar vendored Normal file
View file

0
plugins/inputs/filecount/testdata/baz vendored Normal file
View file

0
plugins/inputs/filecount/testdata/foo vendored Normal file
View file

7
plugins/inputs/filecount/testdata/qux vendored Normal file
View file

@ -0,0 +1,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.

View file

@ -0,0 +1,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.

View file

View file