Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions internal/file/file_operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ func (fo *FileOperator) WriteManifestFile(
}

func (fo *FileOperator) MoveFile(ctx context.Context, sourcePath, destPath string) error {
info, statErr := os.Stat(sourcePath)
if statErr != nil {
return fmt.Errorf("failed to stat source file %s: %w", sourcePath, statErr)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("cannot backup non-regular file %s (type: %s) — resolve file manually on the data plane",
sourcePath, info.Mode().Type())
}

inputFile, openErr := os.Open(sourcePath)
if openErr != nil {
return fmt.Errorf("failed to open source file %s: %w", sourcePath, openErr)
Expand Down
17 changes: 17 additions & 0 deletions internal/file/file_operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"path"
"path/filepath"
"sync"
"syscall"
"testing"

"github.com/nginx/agent/v3/pkg/files"
Expand Down Expand Up @@ -179,3 +180,19 @@ func TestFileOperator_MoveFile_destFileDoesNotExist(t *testing.T) {
assert.FileExists(t, tempFile)
assert.NoFileExists(t, newFile)
}

func TestFileOperator_MoveFile_sourceIsNonRegularFile(t *testing.T) {
t.Parallel()

tempDir := t.TempDir()
fifoPath := filepath.Join(tempDir, "hang.conf")

err := syscall.Mkfifo(fifoPath, 0o644)
require.NoError(t, err)

fileOperator := NewFileOperator(&sync.RWMutex{})
err = fileOperator.MoveFile(t.Context(), fifoPath, filepath.Join(tempDir, "hang.conf.bak"))

require.Error(t, err)
assert.Contains(t, err.Error(), "non-regular file")
}
16 changes: 11 additions & 5 deletions internal/file/file_service_operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,16 +308,22 @@ func (fso *FileServiceOperator) UpdateFile(

// renameFile, renames (moves) file from tempDir to new location to update file.
func (fso *FileServiceOperator) RenameFile(
ctx context.Context, source, desination string,
ctx context.Context, source, destination string,
) error {
slog.InfoContext(ctx, fmt.Sprintf("Renaming file %s to %s", source, desination))
slog.InfoContext(ctx, fmt.Sprintf("Renaming file %s to %s", source, destination))

if info, err := os.Stat(destination); err == nil && !info.Mode().IsRegular() {
return fmt.Errorf("cannot write to non-regular destination file %s (type: %s) - "+
"resolve file manually on the data plane",
destination, info.Mode().Type())
}

// Create parent directories for the target file if they don't exist
if err := os.MkdirAll(filepath.Dir(desination), dirPerm); err != nil {
return fmt.Errorf("failed to create directories for %s: %w", desination, err)
if err := os.MkdirAll(filepath.Dir(destination), dirPerm); err != nil {
return fmt.Errorf("failed to create directories for %s: %w", destination, err)
}

moveErr := os.Rename(source, desination)
moveErr := os.Rename(source, destination)
if moveErr != nil {
return fmt.Errorf("failed to rename file: %w", moveErr)
}
Expand Down
38 changes: 38 additions & 0 deletions internal/file/file_service_operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"path/filepath"
"sync"
"sync/atomic"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -195,3 +196,40 @@ func TestFileManagerService_UpdateFile_LargeFile(t *testing.T) {

helpers.RemoveFileWithErrorCheck(t, testFile.Name())
}

func TestFileServiceOperator_RenameFile_success(t *testing.T) {
t.Parallel()

tempDir := t.TempDir()
src := filepath.Join(tempDir, "source.conf")
dest := filepath.Join(tempDir, "dest.conf")

err := os.WriteFile(src, []byte("nginx config"), 0o600)
require.NoError(t, err)

fso := NewFileServiceOperator(types.AgentConfig(), nil, &sync.RWMutex{})
err = fso.RenameFile(t.Context(), src, dest)

require.NoError(t, err)
assert.FileExists(t, dest)
}

func TestFileServiceOperator_RenameFile_destIsNonRegularFile(t *testing.T) {
t.Parallel()

tempDir := t.TempDir()
src := filepath.Join(tempDir, "source.conf")
dest := filepath.Join(tempDir, "dest.conf")

err := os.WriteFile(src, []byte("nginx config"), 0o600)
require.NoError(t, err)

err = syscall.Mkfifo(dest, 0o644)
require.NoError(t, err)

fso := NewFileServiceOperator(types.AgentConfig(), nil, &sync.RWMutex{})
err = fso.RenameFile(t.Context(), src, dest)

require.Error(t, err)
assert.Contains(t, err.Error(), "non-regular")
}
11 changes: 11 additions & 0 deletions internal/watcher/file/file_watcher_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ func (fws *FileWatcherService) handleEvent(ctx context.Context, event fsnotify.E
return
}

if event.Has(fsnotify.Create) || event.Has(fsnotify.Rename) {
if info, err := os.Stat(event.Name); err == nil && !info.Mode().IsRegular() {
slog.ErrorContext(ctx,
"Managed config file changed to un-supported type — "+
"config apply will fail until file is reverted to a supported type manually on the data plane",
"path", event.Name,
"type", info.Mode().Type(),
)
}
}

slog.DebugContext(ctx, "Processing FSNotify event", "event", event)
fws.filesChanged.Store(true)
}
Expand Down
20 changes: 20 additions & 0 deletions internal/watcher/file/file_watcher_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path"
"path/filepath"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -320,3 +321,22 @@ func TestFileWatcherService_checkForUpdates(t *testing.T) {
t.Fatalf("Expected file update event")
}
}

func TestFileWatcherService_handleEvent_nonRegularFile(t *testing.T) {
t.Parallel()

tempDir := t.TempDir()
fifoPath := filepath.Join(tempDir, "hang.conf")

err := syscall.Mkfifo(fifoPath, 0o644)
require.NoError(t, err)

fws := NewFileWatcherService(types.AgentConfig())

fws.handleEvent(t.Context(), fsnotify.Event{
Name: fifoPath,
Op: fsnotify.Create,
})

assert.True(t, fws.filesChanged.Load())
}
Loading