diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index c50c838c2dd..c230840c03b 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -348,6 +348,7 @@ automatic splitting at root level, if root level element is an array. {pull}3415 - Adding filename details from zip to response for httpjson {issue}33952[33952] {pull}34044[34044] - Add `clean_session` configuration setting for MQTT input. {pull}35806[16204] - Add fingerprint mode for the filestream scanner and new file identity based on it {issue}34419[34419] {pull}35734[35734] +- Add file system metadata to events ingested via filestream {issue}35801[35801] {pull}36065[36065] *Auditbeat* - Migration of system/package module storage from gob encoding to flatbuffer encoding in bolt db. {pull}34817[34817] @@ -442,6 +443,3 @@ automatic splitting at root level, if root level element is an array. {pull}3415 ==== Known Issues - - - diff --git a/filebeat/input/filestream/fswatch_test.go b/filebeat/input/filestream/fswatch_test.go index 4a05eecdb28..f6dabbaa053 100644 --- a/filebeat/input/filestream/fswatch_test.go +++ b/filebeat/input/filestream/fswatch_test.go @@ -65,7 +65,7 @@ scanner: Op: loginp.OpCreate, Descriptor: loginp.FileDescriptor{ Filename: filename, - Info: testFileInfo{path: basename, size: 5}, // 5 bytes written + Info: testFileInfo{name: basename, size: 5}, // 5 bytes written }, } requireEqualEvents(t, expEvent, e) @@ -88,7 +88,7 @@ scanner: Op: loginp.OpWrite, Descriptor: loginp.FileDescriptor{ Filename: filename, - Info: testFileInfo{path: basename, size: 10}, // +5 bytes appended + Info: testFileInfo{name: basename, size: 10}, // +5 bytes appended }, } requireEqualEvents(t, expEvent, e) @@ -110,7 +110,7 @@ scanner: Op: loginp.OpRename, Descriptor: loginp.FileDescriptor{ Filename: newFilename, - Info: testFileInfo{path: newBasename, size: 10}, + Info: testFileInfo{name: newBasename, size: 10}, }, } requireEqualEvents(t, expEvent, e) @@ -130,7 +130,7 @@ scanner: Op: loginp.OpTruncate, Descriptor: loginp.FileDescriptor{ Filename: filename, - Info: testFileInfo{path: basename, size: 2}, + Info: testFileInfo{name: basename, size: 2}, }, } requireEqualEvents(t, expEvent, e) @@ -150,7 +150,7 @@ scanner: Op: loginp.OpTruncate, Descriptor: loginp.FileDescriptor{ Filename: filename, - Info: testFileInfo{path: basename, size: 2}, + Info: testFileInfo{name: basename, size: 2}, }, } requireEqualEvents(t, expEvent, e) @@ -169,7 +169,7 @@ scanner: Op: loginp.OpDelete, Descriptor: loginp.FileDescriptor{ Filename: filename, - Info: testFileInfo{path: basename, size: 2}, + Info: testFileInfo{name: basename, size: 2}, }, } requireEqualEvents(t, expEvent, e) @@ -207,7 +207,7 @@ scanner: Descriptor: loginp.FileDescriptor{ Filename: filename, Fingerprint: "2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a", - Info: testFileInfo{path: basename, size: 1024}, + Info: testFileInfo{name: basename, size: 1024}, }, } requireEqualEvents(t, expEvent, e) @@ -238,7 +238,7 @@ scanner: Op: loginp.OpCreate, Descriptor: loginp.FileDescriptor{ Filename: filename, - Info: testFileInfo{path: basename, size: 1024}, + Info: testFileInfo{name: basename, size: 1024}, }, } requireEqualEvents(t, expEvent, e) @@ -278,7 +278,7 @@ scanner: Descriptor: loginp.FileDescriptor{ Filename: filename, Fingerprint: "2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a", - Info: testFileInfo{path: basename, size: 1024}, + Info: testFileInfo{name: basename, size: 1024}, }, } requireEqualEvents(t, expEvent, e) @@ -372,35 +372,35 @@ scanner: Filename: normalFilename, Info: testFileInfo{ size: sizes[normalFilename], - path: normalBasename, + name: normalBasename, }, }, undersizedFilename: { Filename: undersizedFilename, Info: testFileInfo{ size: sizes[undersizedFilename], - path: undersizedBasename, + name: undersizedBasename, }, }, excludedFilename: { Filename: excludedFilename, Info: testFileInfo{ size: sizes[excludedFilename], - path: excludedBasename, + name: excludedBasename, }, }, excludedIncludedFilename: { Filename: excludedIncludedFilename, Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, travelerSymlinkFilename: { Filename: travelerSymlinkFilename, Info: testFileInfo{ size: sizes[travelerFilename], - path: travelerSymlinkBasename, + name: travelerSymlinkBasename, }, }, }, @@ -421,28 +421,28 @@ scanner: Filename: normalFilename, Info: testFileInfo{ size: sizes[normalFilename], - path: normalBasename, + name: normalBasename, }, }, undersizedFilename: { Filename: undersizedFilename, Info: testFileInfo{ size: sizes[undersizedFilename], - path: undersizedBasename, + name: undersizedBasename, }, }, excludedFilename: { Filename: excludedFilename, Info: testFileInfo{ size: sizes[excludedFilename], - path: excludedBasename, + name: excludedBasename, }, }, excludedIncludedFilename: { Filename: excludedIncludedFilename, Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, }, @@ -464,21 +464,21 @@ scanner: Filename: normalFilename, Info: testFileInfo{ size: sizes[normalFilename], - path: normalBasename, + name: normalBasename, }, }, undersizedFilename: { Filename: undersizedFilename, Info: testFileInfo{ size: sizes[undersizedFilename], - path: undersizedBasename, + name: undersizedBasename, }, }, travelerSymlinkFilename: { Filename: travelerSymlinkFilename, Info: testFileInfo{ size: sizes[travelerFilename], - path: travelerSymlinkBasename, + name: travelerSymlinkBasename, }, }, }, @@ -495,14 +495,14 @@ scanner: Filename: normalFilename, Info: testFileInfo{ size: sizes[normalFilename], - path: normalBasename, + name: normalBasename, }, }, undersizedFilename: { Filename: undersizedFilename, Info: testFileInfo{ size: sizes[undersizedFilename], - path: undersizedBasename, + name: undersizedBasename, }, }, }, @@ -524,7 +524,7 @@ scanner: Filename: excludedIncludedFilename, Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, }, @@ -541,7 +541,7 @@ scanner: Filename: excludedIncludedFilename, Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, }, @@ -558,14 +558,14 @@ scanner: Filename: excludedIncludedFilename, Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, travelerSymlinkFilename: { Filename: travelerSymlinkFilename, Info: testFileInfo{ size: sizes[travelerFilename], - path: travelerSymlinkBasename, + name: travelerSymlinkBasename, }, }, }, @@ -587,7 +587,7 @@ scanner: Fingerprint: "2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a", Info: testFileInfo{ size: sizes[normalFilename], - path: normalBasename, + name: normalBasename, }, }, excludedFilename: { @@ -595,7 +595,7 @@ scanner: Fingerprint: "bd151321c3bbdb44185414a1b56b5649a00206dd4792e7230db8904e43987336", Info: testFileInfo{ size: sizes[excludedFilename], - path: excludedBasename, + name: excludedBasename, }, }, excludedIncludedFilename: { @@ -603,7 +603,7 @@ scanner: Fingerprint: "bfdb99a65297062658c26dfcea816d76065df2a2da2594bfd9b96e9e405da1c2", Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, travelerSymlinkFilename: { @@ -611,7 +611,7 @@ scanner: Fingerprint: "c4058942bffcea08810a072d5966dfa5c06eb79b902bf0011890dd8d22e1a5f8", Info: testFileInfo{ size: sizes[travelerFilename], - path: travelerSymlinkBasename, + name: travelerSymlinkBasename, }, }, }, @@ -633,7 +633,7 @@ scanner: Fingerprint: "ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb", Info: testFileInfo{ size: sizes[normalFilename], - path: normalBasename, + name: normalBasename, }, }, // undersizedFilename got excluded because of the matching fingerprint @@ -642,7 +642,7 @@ scanner: Fingerprint: "9c225a1e6a7df9c869499e923565b93937e88382bb9188145f117195cd41dcd1", Info: testFileInfo{ size: sizes[excludedFilename], - path: excludedBasename, + name: excludedBasename, }, }, excludedIncludedFilename: { @@ -650,7 +650,7 @@ scanner: Fingerprint: "7985b2b9750bdd3c76903db408aff3859204d6334279eaf516ecaeb618a218d5", Info: testFileInfo{ size: sizes[excludedIncludedFilename], - path: excludedIncludedBasename, + name: excludedIncludedBasename, }, }, travelerSymlinkFilename: { @@ -658,7 +658,7 @@ scanner: Fingerprint: "da437600754a8eed6c194b7241b078679551c06c7dc89685a9a71be7829ad7e5", Info: testFileInfo{ size: sizes[travelerFilename], - path: travelerSymlinkBasename, + name: travelerSymlinkBasename, }, }, }, diff --git a/filebeat/input/filestream/identifier.go b/filebeat/input/filestream/identifier.go index 227efad1186..0cfeb031d63 100644 --- a/filebeat/input/filestream/identifier.go +++ b/filebeat/input/filestream/identifier.go @@ -59,7 +59,7 @@ type fileIdentifier interface { // fileSource implements the Source interface // It is required to identify and manage file sources. type fileSource struct { - info loginp.FileDescriptor + desc loginp.FileDescriptor newPath string oldPath string truncated bool @@ -109,7 +109,7 @@ func newINodeDeviceIdentifier(_ *conf.C) (fileIdentifier, error) { func (i *inodeDeviceIdentifier) GetSource(e loginp.FSEvent) fileSource { return fileSource{ - info: e.Descriptor, + desc: e.Descriptor, newPath: e.NewPath, oldPath: e.OldPath, truncated: e.Op == loginp.OpTruncate, @@ -148,7 +148,7 @@ func (p *pathIdentifier) GetSource(e loginp.FSEvent) fileSource { path = e.OldPath } return fileSource{ - info: e.Descriptor, + desc: e.Descriptor, newPath: e.NewPath, oldPath: e.OldPath, truncated: e.Op == loginp.OpTruncate, diff --git a/filebeat/input/filestream/identifier_fingerprint.go b/filebeat/input/filestream/identifier_fingerprint.go index 9451714778a..5fe524ebaf2 100644 --- a/filebeat/input/filestream/identifier_fingerprint.go +++ b/filebeat/input/filestream/identifier_fingerprint.go @@ -35,7 +35,7 @@ func newFingerprintIdentifier(cfg *conf.C) (fileIdentifier, error) { func (i *fingerprintIdentifier) GetSource(e loginp.FSEvent) fileSource { return fileSource{ - info: e.Descriptor, + desc: e.Descriptor, newPath: e.NewPath, oldPath: e.OldPath, truncated: e.Op == loginp.OpTruncate, diff --git a/filebeat/input/filestream/identifier_inode_deviceid.go b/filebeat/input/filestream/identifier_inode_deviceid.go index 6157c85aa87..af6a5610086 100644 --- a/filebeat/input/filestream/identifier_inode_deviceid.go +++ b/filebeat/input/filestream/identifier_inode_deviceid.go @@ -95,7 +95,7 @@ func (i *inodeMarkerIdentifier) markerContents() string { func (i *inodeMarkerIdentifier) GetSource(e loginp.FSEvent) fileSource { osstate := file.GetOSState(e.Descriptor.Info) return fileSource{ - info: e.Descriptor, + desc: e.Descriptor, newPath: e.NewPath, oldPath: e.OldPath, truncated: e.Op == loginp.OpTruncate, diff --git a/filebeat/input/filestream/input.go b/filebeat/input/filestream/input.go index 30fcb916ef5..b935161c126 100644 --- a/filebeat/input/filestream/input.go +++ b/filebeat/input/filestream/input.go @@ -228,7 +228,7 @@ func (inp *filestream) open(log *logp.Logger, canceler input.Canceler, fs fileSo r = readfile.NewStripNewline(r, inp.readerConfig.LineTerminator) - r = readfile.NewFilemeta(r, fs.newPath, offset) + r = readfile.NewFilemeta(r, fs.newPath, fs.desc.Info, fs.desc.Fingerprint, offset) r = inp.parsers.Create(r) diff --git a/filebeat/input/filestream/prospector_test.go b/filebeat/input/filestream/prospector_test.go index db30697ddba..834784c81da 100644 --- a/filebeat/input/filestream/prospector_test.go +++ b/filebeat/input/filestream/prospector_test.go @@ -724,13 +724,13 @@ func TestOnRenameFileIdentity(t *testing.T) { } type testFileInfo struct { - path string + name string size int64 time time.Time sys interface{} } -func (t testFileInfo) Name() string { return t.path } +func (t testFileInfo) Name() string { return t.name } func (t testFileInfo) Size() int64 { return t.size } func (t testFileInfo) Mode() os.FileMode { return 0 } func (t testFileInfo) ModTime() time.Time { return t.time } diff --git a/libbeat/reader/readfile/fs_metafields_other.go b/libbeat/reader/readfile/fs_metafields_other.go new file mode 100644 index 00000000000..425b7435fe8 --- /dev/null +++ b/libbeat/reader/readfile/fs_metafields_other.go @@ -0,0 +1,47 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build !windows + +package readfile + +import ( + "fmt" + "os" + + "github.com/elastic/beats/v7/libbeat/common/file" + "github.com/elastic/elastic-agent-libs/mapstr" +) + +const ( + deviceIDKey = "log.file.device_id" + inodeKey = "log.file.inode" +) + +func setFileSystemMetadata(fi os.FileInfo, fields mapstr.M) error { + osstate := file.GetOSState(fi) + _, err := fields.Put(deviceIDKey, osstate.Device) + if err != nil { + return fmt.Errorf("failed to set %q: %w", deviceIDKey, err) + } + _, err = fields.Put(inodeKey, osstate.Inode) + if err != nil { + return fmt.Errorf("failed to set %q: %w", inodeKey, err) + } + + return nil +} diff --git a/libbeat/reader/readfile/fs_metafields_windows.go b/libbeat/reader/readfile/fs_metafields_windows.go new file mode 100644 index 00000000000..113a74cf829 --- /dev/null +++ b/libbeat/reader/readfile/fs_metafields_windows.go @@ -0,0 +1,50 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package readfile + +import ( + "fmt" + "os" + + "github.com/elastic/beats/v7/libbeat/common/file" + "github.com/elastic/elastic-agent-libs/mapstr" +) + +const ( + idxhiKey = "log.file.idxhi" + idxloKey = "log.file.idxlo" + volKey = "log.file.vol" +) + +func setFileSystemMetadata(fi os.FileInfo, fields mapstr.M) error { + osstate := file.GetOSState(fi) + _, err := fields.Put(idxhiKey, osstate.IdxHi) + if err != nil { + return fmt.Errorf("failed to set %q: %w", idxhiKey, err) + } + _, err = fields.Put(idxloKey, osstate.IdxLo) + if err != nil { + return fmt.Errorf("failed to set %q: %w", idxloKey, err) + } + _, err = fields.Put(volKey, osstate.Vol) + if err != nil { + return fmt.Errorf("failed to set %q: %w", volKey, err) + } + + return nil +} diff --git a/libbeat/reader/readfile/metafields.go b/libbeat/reader/readfile/metafields.go index 5117bc6ec9b..c4c41e980f6 100644 --- a/libbeat/reader/readfile/metafields.go +++ b/libbeat/reader/readfile/metafields.go @@ -18,6 +18,9 @@ package readfile import ( + "fmt" + "os" + "github.com/elastic/beats/v7/libbeat/reader" "github.com/elastic/elastic-agent-libs/mapstr" ) @@ -25,15 +28,17 @@ import ( // Reader produces lines by reading lines from an io.Reader // through a decoder converting the reader it's encoding to utf-8. type FileMetaReader struct { - reader reader.Reader - path string - offset int64 + reader reader.Reader + path string + fi os.FileInfo + fingerprint string + offset int64 } // New creates a new Encode reader from input reader by applying // the given codec. -func NewFilemeta(r reader.Reader, path string, offset int64) reader.Reader { - return &FileMetaReader{r, path, offset} +func NewFilemeta(r reader.Reader, path string, fi os.FileInfo, fingerprint string, offset int64) reader.Reader { + return &FileMetaReader{r, path, fi, fingerprint, offset} } // Next reads the next line from it's initial io.Reader @@ -56,6 +61,17 @@ func (r *FileMetaReader) Next() (reader.Message, error) { }, }) + err = setFileSystemMetadata(r.fi, message.Fields) + if err != nil { + return message, fmt.Errorf("failed to set file system metadata: %w", err) + } + + if r.fingerprint != "" { + _, err = message.Fields.Put("log.file.fingerprint", r.fingerprint) + if err != nil { + return message, fmt.Errorf("failed to set fingerprint: %w", err) + } + } r.offset += int64(message.Bytes) return message, err diff --git a/libbeat/reader/readfile/metafields_other_test.go b/libbeat/reader/readfile/metafields_other_test.go new file mode 100644 index 00000000000..7874d24d4ae --- /dev/null +++ b/libbeat/reader/readfile/metafields_other_test.go @@ -0,0 +1,58 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build !windows + +package readfile + +import ( + "os" + "syscall" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-agent-libs/mapstr" +) + +func createTestFileInfo() os.FileInfo { + return testFileInfo{ + name: "filename", + size: 42, + time: time.Now(), + sys: &syscall.Stat_t{Dev: 17, Ino: 999}, + } +} + +func checkFields(t *testing.T, expected, actual mapstr.M) { + t.Helper() + + dev, err := actual.GetValue(deviceIDKey) + require.NoError(t, err) + require.Equal(t, uint64(17), dev) + err = actual.Delete(deviceIDKey) + require.NoError(t, err) + + inode, err := actual.GetValue(inodeKey) + require.NoError(t, err) + require.Equal(t, uint64(999), inode) + err = actual.Delete(inodeKey) + require.NoError(t, err) + + require.Equal(t, expected, actual) +} diff --git a/libbeat/reader/readfile/metafields_test.go b/libbeat/reader/readfile/metafields_test.go index 0bc4a2d72ec..e759368c268 100644 --- a/libbeat/reader/readfile/metafields_test.go +++ b/libbeat/reader/readfile/metafields_test.go @@ -18,8 +18,11 @@ package readfile import ( + "errors" "io" + "os" "testing" + "time" "github.com/stretchr/testify/require" @@ -27,19 +30,19 @@ import ( "github.com/elastic/elastic-agent-libs/mapstr" ) -func TestMetaFieldsOffset(t *testing.T) { +func TestMetaFields(t *testing.T) { messages := []reader.Message{ - reader.Message{ + { Content: []byte("my line"), Bytes: 7, Fields: mapstr.M{}, }, - reader.Message{ + { Content: []byte("my line again"), Bytes: 13, Fields: mapstr.M{}, }, - reader.Message{ + { Content: []byte(""), Bytes: 10, Fields: mapstr.M{}, @@ -48,10 +51,11 @@ func TestMetaFieldsOffset(t *testing.T) { path := "test/path" offset := int64(0) - in := &FileMetaReader{msgReader(messages), path, offset} + + in := &FileMetaReader{msgReader(messages), path, createTestFileInfo(), "hash", offset} for { msg, err := in.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } @@ -60,15 +64,18 @@ func TestMetaFieldsOffset(t *testing.T) { expectedFields = mapstr.M{ "log": mapstr.M{ "file": mapstr.M{ - "path": path, + "path": path, + "fingerprint": "hash", }, "offset": offset, }, } + checkFields(t, expectedFields, msg.Fields) + } else { + require.Equal(t, expectedFields, msg.Fields) } offset += int64(msg.Bytes) - require.Equal(t, expectedFields, msg.Fields) require.Equal(t, offset, in.offset) } } @@ -96,3 +103,17 @@ func (r *messageReader) Next() (reader.Message, error) { func (r *messageReader) Close() error { return nil } + +type testFileInfo struct { + name string + size int64 + time time.Time + sys interface{} +} + +func (t testFileInfo) Name() string { return t.name } +func (t testFileInfo) Size() int64 { return t.size } +func (t testFileInfo) Mode() os.FileMode { return 0 } +func (t testFileInfo) ModTime() time.Time { return t.time } +func (t testFileInfo) IsDir() bool { return false } +func (t testFileInfo) Sys() interface{} { return t.sys } diff --git a/libbeat/reader/readfile/metafields_windows_test.go b/libbeat/reader/readfile/metafields_windows_test.go new file mode 100644 index 00000000000..37ff5cb4bda --- /dev/null +++ b/libbeat/reader/readfile/metafields_windows_test.go @@ -0,0 +1,72 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package readfile + +import ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-agent-libs/mapstr" +) + +type winTestInfo struct { + testFileInfo + idxhi uint32 + idxlo uint32 + vol uint32 +} + +func createTestFileInfo() os.FileInfo { + return &winTestInfo{ + testFileInfo: testFileInfo{ + name: "filename", + size: 42, + time: time.Now(), + }, + idxhi: 100, + idxlo: 200, + vol: 300, + } +} + +func checkFields(t *testing.T, expected, actual mapstr.M) { + t.Helper() + + idxhi, err := actual.GetValue(idxhiKey) + require.NoError(t, err) + require.Equal(t, uint64(100), idxhi) + err = actual.Delete(idxhiKey) + require.NoError(t, err) + + idxlo, err := actual.GetValue(idxloKey) + require.NoError(t, err) + require.Equal(t, uint64(200), idxlo) + err = actual.Delete(idxloKey) + require.NoError(t, err) + + vol, err := actual.GetValue(volKey) + require.NoError(t, err) + require.Equal(t, uint64(300), vol) + err = actual.Delete(volKey) + require.NoError(t, err) + + require.Equal(t, expected, actual) +}