mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 18:19:04 +02:00
Signed-off-by: EisenbergD <dominik.eisenberg@beiersdorf.com> Co-authored-by: EisenbergD <dominik.eisenberg@beiersdorf.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
170 lines
4.7 KiB
Go
170 lines
4.7 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Copyright The Prometheus Authors
|
|
// Licensed 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 log
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// openWithoutDeleteSharing opens path like a log shipper that doesn't allow
|
|
// delete sharing, which blocks renaming the file.
|
|
func openWithoutDeleteSharing(t *testing.T, path string) windows.Handle {
|
|
t.Helper()
|
|
|
|
pathPtr, err := windows.UTF16PtrFromString(path)
|
|
require.NoError(t, err)
|
|
|
|
handle, err := windows.CreateFile(pathPtr, windows.GENERIC_READ, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
|
require.NoError(t, err)
|
|
|
|
return handle
|
|
}
|
|
|
|
func TestRotatingFileBlockedByOtherProcess(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
path := filepath.Join(t.TempDir(), "windows_exporter.log")
|
|
now := time.Date(2026, 10, 15, 14, 3, 5, 0, time.UTC)
|
|
|
|
file, err := newRotatingFile(path, 10, 0, false, func() time.Time { return now })
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { _ = file.Close() })
|
|
|
|
write(t, file, "1234567890")
|
|
|
|
blocker := openWithoutDeleteSharing(t, path)
|
|
|
|
// The rename fails while the file is held: the line is appended instead.
|
|
write(t, file, "while blocked\n")
|
|
require.Equal(t, []string{"windows_exporter.log"}, dirEntries(t, filepath.Dir(path)))
|
|
|
|
require.NoError(t, windows.CloseHandle(blocker))
|
|
|
|
now = now.Add(file.retryInterval)
|
|
write(t, file, "after release\n")
|
|
require.NoError(t, file.Close())
|
|
|
|
require.Equal(t, "1234567890while blocked\n", readFile(t, filepath.Join(filepath.Dir(path), "windows_exporter-2026-10-15T14-04-05.000.log")))
|
|
require.Equal(t, "after release\n", readFile(t, path))
|
|
}
|
|
|
|
func TestOpenLogFileAllowsRenameAndDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "windows_exporter.log")
|
|
|
|
file, err := openLogFile(path)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { _ = file.Close() })
|
|
|
|
_, err = file.WriteString("first\n")
|
|
require.NoError(t, err)
|
|
|
|
// An admin can move the file while the exporter writes to it.
|
|
moved := filepath.Join(dir, "moved.log")
|
|
require.NoError(t, os.Rename(path, moved))
|
|
|
|
_, err = file.WriteString("second\n")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "first\nsecond\n", readFile(t, moved))
|
|
|
|
// And delete it.
|
|
require.NoError(t, os.Remove(moved))
|
|
}
|
|
|
|
func TestOpenLogFileAppends(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
path := filepath.Join(t.TempDir(), "windows_exporter.log")
|
|
require.NoError(t, os.WriteFile(path, []byte("existing\n"), 0o600))
|
|
|
|
file, err := openLogFile(path)
|
|
require.NoError(t, err)
|
|
|
|
_, err = file.WriteString("appended\n")
|
|
require.NoError(t, err)
|
|
require.NoError(t, file.Close())
|
|
|
|
require.Equal(t, "existing\nappended\n", readFile(t, path))
|
|
}
|
|
|
|
func TestRotatingFileRecoversWhenMovedOrDeleted(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for name, remove := range map[string]func(path string) error{
|
|
"moved": func(path string) error { return os.Rename(path, path+".moved") },
|
|
"deleted": os.Remove,
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
path := filepath.Join(t.TempDir(), "windows_exporter.log")
|
|
now := time.Date(2026, 10, 15, 14, 3, 5, 0, time.UTC)
|
|
|
|
file, err := newRotatingFile(path, 1<<20, 0, false, func() time.Time { return now })
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { _ = file.Close() })
|
|
|
|
write(t, file, "before\n")
|
|
|
|
// Share-delete lets an admin move or delete the file while it is open.
|
|
require.NoError(t, remove(path))
|
|
|
|
now = now.Add(file.retryInterval)
|
|
|
|
write(t, file, "after\n")
|
|
require.NoError(t, file.Close())
|
|
|
|
require.Equal(t, "after\n", readFile(t, path))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSameFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "windows_exporter.log")
|
|
|
|
file, err := openLogFile(path)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { _ = file.Close() })
|
|
|
|
same, err := sameFile(file, path)
|
|
require.NoError(t, err)
|
|
require.True(t, same)
|
|
|
|
require.NoError(t, os.Rename(path, filepath.Join(dir, "moved.log")))
|
|
require.NoError(t, os.WriteFile(path, []byte("other"), 0o600))
|
|
|
|
same, err = sameFile(file, path)
|
|
require.NoError(t, err)
|
|
require.False(t, same, "a new file at the path is not the open file")
|
|
}
|