-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🌱 Rename util packages and add missing unit tests
Signed-off-by: Per Goncalves da Silva <[email protected]>
- Loading branch information
Per Goncalves da Silva
committed
Jan 31, 2025
1 parent
c3a4406
commit bd1ef6c
Showing
10 changed files
with
258 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package fsutil_test | ||
|
||
import ( | ||
"github.com/stretchr/testify/require" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestEnsureEmptyDirectory(t *testing.T) { | ||
tempDir := t.TempDir() | ||
dirPath := filepath.Join(tempDir, "testdir") | ||
|
||
// Ensure directory is created if not exists | ||
require.NoError(t, EnsureEmptyDirectory(dirPath, 0755)) | ||
Check failure on line 15 in internal/fsutil/helpers_test.go
|
||
|
||
stat, err := os.Stat(dirPath) | ||
require.NoError(t, err) | ||
require.True(t, stat.IsDir()) | ||
|
||
// Create files inside directory | ||
file := filepath.Join(dirPath, "file1") | ||
require.NoError(t, os.WriteFile(file, []byte("test"), 0644)) | ||
|
||
subDir := filepath.Join(dirPath, "subdir") | ||
require.NoError(t, os.Mkdir(subDir, 0755)) | ||
|
||
// Ensure directory is emptied but not deleted | ||
require.NoError(t, EnsureEmptyDirectory(dirPath, 0755)) | ||
Check failure on line 29 in internal/fsutil/helpers_test.go
|
||
|
||
entries, err := os.ReadDir(dirPath) | ||
require.NoError(t, err) | ||
require.Empty(t, entries) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package source | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
) | ||
|
||
const ( | ||
OwnerWritableFileMode os.FileMode = 0700 | ||
OwnerWritableDirMode os.FileMode = 0700 | ||
OwnerReadOnlyFileMode os.FileMode = 0400 | ||
OwnerReadOnlyDirMode os.FileMode = 0500 | ||
) | ||
|
||
// SetReadOnlyRecursive recursively sets files and directories under the path given by `root` as read-only | ||
func SetReadOnlyRecursive(root string) error { | ||
return setModeRecursive(root, OwnerReadOnlyFileMode, OwnerReadOnlyDirMode) | ||
} | ||
|
||
// SetWritableRecursive recursively sets files and directories under the path given by `root` as writable | ||
func SetWritableRecursive(root string) error { | ||
return setModeRecursive(root, OwnerWritableFileMode, OwnerWritableDirMode) | ||
} | ||
|
||
// DeleteReadOnlyRecursive deletes read-only directory with path given by `root` | ||
func DeleteReadOnlyRecursive(root string) error { | ||
if err := SetWritableRecursive(root); err != nil { | ||
return fmt.Errorf("error making directory writable for deletion: %w", err) | ||
} | ||
return os.RemoveAll(root) | ||
} | ||
|
||
// IsImageUnpacked checks whether an image has been unpacked in `unpackPath`. | ||
// If true, time of unpack will also be returned. If false unpack time is gibberish (zero/epoch time). | ||
// If `unpackPath` is a file, it will be deleted and false will be returned without an error. | ||
func IsImageUnpacked(unpackPath string) (bool, time.Time, error) { | ||
unpackStat, err := os.Stat(unpackPath) | ||
if errors.Is(err, os.ErrNotExist) { | ||
return false, time.Time{}, nil | ||
} | ||
if err != nil { | ||
return false, time.Time{}, err | ||
} | ||
if !unpackStat.IsDir() { | ||
return false, time.Time{}, os.Remove(unpackPath) | ||
} | ||
return true, unpackStat.ModTime(), nil | ||
} | ||
|
||
func setModeRecursive(path string, fileMode os.FileMode, dirMode os.FileMode) error { | ||
return filepath.WalkDir(path, func(path string, d os.DirEntry, err error) error { | ||
if os.IsNotExist(err) { | ||
return nil | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
fi, err := d.Info() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch typ := fi.Mode().Type(); typ { | ||
case os.ModeSymlink: | ||
// do not follow symlinks | ||
// 1. if they resolve to other locations in the root, we'll find them anyway | ||
// 2. if they resolve to other locations outside the root, we don't want to change their permissions | ||
return nil | ||
case os.ModeDir: | ||
return os.Chmod(path, dirMode) | ||
case 0: // regular file | ||
return os.Chmod(path, fileMode) | ||
default: | ||
return fmt.Errorf("refusing to change ownership of file %q with type %v", path, typ.String()) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package source_test | ||
|
||
import ( | ||
"errors" | ||
"github.com/operator-framework/operator-controller/internal/rukpak/source" | ||
"github.com/stretchr/testify/require" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestSetReadOnlyRecursive(t *testing.T) { | ||
// Create a nested directory structure that contains a file and sym. link | ||
tempDir := t.TempDir() | ||
nestedDir := filepath.Join(tempDir, "nested") | ||
filePath := filepath.Join(nestedDir, "testfile") | ||
symlinkPath := filepath.Join(nestedDir, "symlink") | ||
|
||
require.NoError(t, os.Mkdir(nestedDir, source.OwnerWritableDirMode)) | ||
require.NoError(t, os.WriteFile(filePath, []byte("test"), source.OwnerWritableFileMode)) | ||
require.NoError(t, os.Symlink(filePath, symlinkPath)) | ||
|
||
// Set directory structure as read-only | ||
require.NoError(t, source.SetReadOnlyRecursive(tempDir)) | ||
|
||
// Check file perm | ||
stat, err := os.Stat(filePath) | ||
require.NoError(t, err) | ||
require.Equal(t, source.OwnerReadOnlyFileMode, stat.Mode().Perm()) | ||
|
||
// Check directory perm | ||
nestedStat, err := os.Stat(nestedDir) | ||
require.NoError(t, err) | ||
require.Equal(t, source.OwnerReadOnlyDirMode, nestedStat.Mode().Perm()) | ||
|
||
// Check sym. link perm - should not be affected | ||
symlinkStat, err := os.Lstat(symlinkPath) | ||
require.NoError(t, err) | ||
require.True(t, symlinkStat.Mode()&os.ModeSymlink != 0) | ||
|
||
// Make directory writable to enable test clean-up | ||
require.NoError(t, source.SetWritableRecursive(tempDir)) | ||
} | ||
|
||
func TestSetWritableRecursive(t *testing.T) { | ||
// Create a nested directory structure that contains a file and sym. link | ||
tempDir := t.TempDir() | ||
nestedDir := filepath.Join(tempDir, "nested") | ||
filePath := filepath.Join(nestedDir, "testfile") | ||
symlinkPath := filepath.Join(nestedDir, "symlink") | ||
|
||
// nested dir mode is writable while we stamp out the other filesystem elements | ||
require.NoError(t, os.Mkdir(nestedDir, source.OwnerWritableDirMode)) | ||
require.NoError(t, os.WriteFile(filePath, []byte("test"), source.OwnerReadOnlyFileMode)) | ||
require.NoError(t, os.Symlink(filePath, symlinkPath)) | ||
|
||
// Now, set nested dir as read-only | ||
require.NoError(t, os.Chmod(nestedDir, source.OwnerReadOnlyDirMode)) | ||
|
||
// Set directory structure as writable | ||
require.NoError(t, source.SetWritableRecursive(tempDir)) | ||
|
||
// Check file perm | ||
stat, err := os.Stat(filePath) | ||
require.NoError(t, err) | ||
require.Equal(t, source.OwnerWritableFileMode, stat.Mode().Perm()) | ||
|
||
// Check directory perm | ||
nestedStat, err := os.Stat(nestedDir) | ||
require.NoError(t, err) | ||
require.Equal(t, source.OwnerWritableDirMode, nestedStat.Mode().Perm()) | ||
|
||
// Check sym link perm - should not be affected | ||
symlinkStat, err := os.Lstat(symlinkPath) | ||
require.NoError(t, err) | ||
require.True(t, symlinkStat.Mode()&os.ModeSymlink != 0) | ||
} | ||
|
||
func TestDeleteReadOnlyRecursive(t *testing.T) { | ||
// Create a nested directory structure | ||
tempDir := t.TempDir() | ||
nestedDir := filepath.Join(tempDir, "nested") | ||
filePath := filepath.Join(nestedDir, "testfile") | ||
|
||
require.NoError(t, os.Mkdir(nestedDir, source.OwnerWritableDirMode)) | ||
require.NoError(t, os.WriteFile(filePath, []byte("test"), source.OwnerReadOnlyFileMode)) | ||
require.NoError(t, os.Chmod(nestedDir, source.OwnerReadOnlyDirMode)) | ||
|
||
// Set directory structure as read-only | ||
require.NoError(t, source.DeleteReadOnlyRecursive(tempDir)) | ||
|
||
// Ensure directory is gone | ||
_, err := os.Stat(tempDir) | ||
require.True(t, errors.Is(err, os.ErrNotExist)) | ||
} | ||
|
||
func TestIsImageUnpacked(t *testing.T) { | ||
tempDir := t.TempDir() | ||
unpackPath := filepath.Join(tempDir, "myimage") | ||
|
||
// Test case: unpack path does not exist | ||
unpacked, modTime, err := source.IsImageUnpacked(unpackPath) | ||
require.NoError(t, err) | ||
require.False(t, unpacked) | ||
require.True(t, modTime.IsZero()) | ||
|
||
// Test case: unpack path points to file | ||
require.NoError(t, os.WriteFile(unpackPath, []byte("test"), source.OwnerWritableFileMode)) | ||
|
||
unpacked, modTime, err = source.IsImageUnpacked(filepath.Join(tempDir, "myimage")) | ||
require.NoError(t, err) | ||
require.False(t, unpacked) | ||
require.True(t, modTime.IsZero()) | ||
|
||
// Expect file to be deleted | ||
_, err = os.Stat(unpackPath) | ||
require.True(t, errors.Is(err, os.ErrNotExist)) | ||
|
||
// Test case: unpack path points to directory (happy path) | ||
require.NoError(t, os.Mkdir(unpackPath, source.OwnerWritableDirMode)) | ||
|
||
unpacked, modTime, err = source.IsImageUnpacked(unpackPath) | ||
require.NoError(t, err) | ||
require.True(t, unpacked) | ||
require.False(t, modTime.IsZero()) | ||
|
||
// Expect unpack time to match directory mod time | ||
stat, err := os.Stat(unpackPath) | ||
require.NoError(t, err) | ||
require.Equal(t, stat.ModTime(), modTime) | ||
} |
Oops, something went wrong.