Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix generation of archives when using exclude_symlink_directories #298

Merged
merged 4 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions .changes/unreleased/BUG FIXES-20240115-101358.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: BUG FIXES
body: 'data-source/archive_file: Prevent error when generating archive from source
containing symbolically linked directories, and `exclude_symlink_directories`
is set to true'
time: 2024-01-15T10:13:58.177253Z
custom:
Issue: "298"
6 changes: 6 additions & 0 deletions .changes/unreleased/BUG FIXES-20240115-101510.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: BUG FIXES
body: 'resource/archive_file: Prevent error when generating archive from source containing
symbolically linked directories, and `exclude_symlink_directories` is set to true'
time: 2024-01-15T10:15:10.869072Z
custom:
Issue: "298"
5 changes: 5 additions & 0 deletions .changes/unreleased/BUG FIXES-20240117-101851.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'resource/archive_file: Return error when generated archive would be empty'
time: 2024-01-17T10:18:51.941981Z
custom:
Issue: "298"
5 changes: 5 additions & 0 deletions .changes/unreleased/BUG FIXES-20240117-101923.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'data-source/archive_file: Return error when generated archive would be empty'
time: 2024-01-17T10:19:23.907477Z
custom:
Issue: "298"
78 changes: 58 additions & 20 deletions internal/provider/data_source_archive_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -960,8 +960,8 @@ func TestAccArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testin
})
}

// TestAccArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an error is generated when
// trying to use a symlink to a directory.
// TestAccArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive
// is generated when trying to archive a directory which only contains a symlink to a directory.
func TestAccArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) {
td := t.TempDir()

Expand All @@ -980,14 +980,14 @@ func TestAccArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *t
exclude_symlink_directories = true
}
`, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: error reading file for`),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`),
},
},
})
}

// TestAccArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an error is generated when
// trying to use a symlink to a directory.
// TestAccArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive
// is generated when trying to archive a directory which only contains a symlink to a directory.
func TestAccArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) {
td := t.TempDir()

Expand All @@ -1011,7 +1011,7 @@ func TestAccArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *t
exclude_symlink_directories = true
}
`, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: error reading file for`),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`),
},
},
})
Expand Down Expand Up @@ -1181,8 +1181,8 @@ func TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkD
})
}

// TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an error is
// generated when trying to a directory which contains a symlink to a directory.
// TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive
// is generated when trying to archive a directory which only contains a symlink to a directory.
func TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) {
td := t.TempDir()

Expand All @@ -1197,18 +1197,17 @@ func TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDir
type = "zip"
source_dir = "%s"
output_path = "%s"
output_file_mode = "0666"
exclude_symlink_directories = true
}
`, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: error reading file for`),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`),
},
},
})
}

// TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an error is
// generated when trying to a directory which contains a symlink to a directory.
// TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive
// is generated when trying to archive a directory which only contains a symlink to a directory.
func TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) {
td := t.TempDir()

Expand All @@ -1228,23 +1227,24 @@ func TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSym
type = "zip"
source_dir = "%s"
output_path = "%s"
output_file_mode = "0666"
exclude_symlink_directories = true
}
`, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: error reading file for`),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`),
},
},
})
}

// TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that an error is
// generated when trying to a directory which contains a symlink to a directory.
// TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that
// symlinked directories are excluded.
func TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) {
td := t.TempDir()

f := filepath.Join(td, "zip_file_acc_test.zip")

var fileSize string

r.ParallelTest(t, r.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),
Steps: []r.TestStep{
Expand All @@ -1258,14 +1258,32 @@ func TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T
exclude_symlink_directories = true
}
`, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: error reading file for`),
Check: r.ComposeTestCheckFunc(
testAccArchiveFileSize(f, &fileSize),
r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize),
r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error {
ensureContents(t, value, map[string][]byte{
"test-dir/test-dir1/file1.txt": []byte("This is file 1"),
"test-dir/test-dir1/file2.txt": []byte("This is file 2"),
"test-dir/test-dir1/file3.txt": []byte("This is file 3"),
"test-dir/test-dir2/file1.txt": []byte("This is file 1"),
"test-dir/test-dir2/file2.txt": []byte("This is file 2"),
"test-dir/test-dir2/file3.txt": []byte("This is file 3"),
"test-dir/test-file.txt": []byte("This is test content"),
"test-dir-with-symlink-file/test-file.txt": []byte("This is test content"),
"test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"),
})
ensureFileMode(t, value, "0666")
return nil
}),
),
},
},
})
}

// TestAccArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories verifies that an error is
// generated when trying to a directory which contains a symlink to a directory.
// TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that
// symlinked directories are excluded.
func TestAccArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) {
td := t.TempDir()

Expand All @@ -1276,6 +1294,8 @@ func TestAccArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T
t.Fatal(err)
}

var fileSize string

r.ParallelTest(t, r.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),
Steps: []r.TestStep{
Expand All @@ -1289,7 +1309,25 @@ func TestAccArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T
exclude_symlink_directories = true
}
`, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)),
ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: error reading file for`),
Check: r.ComposeTestCheckFunc(
testAccArchiveFileSize(f, &fileSize),
r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize),
r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error {
ensureContents(t, value, map[string][]byte{
"test-dir/test-dir1/file1.txt": []byte("This is file 1"),
"test-dir/test-dir1/file2.txt": []byte("This is file 2"),
"test-dir/test-dir1/file3.txt": []byte("This is file 3"),
"test-dir/test-dir2/file1.txt": []byte("This is file 1"),
"test-dir/test-dir2/file2.txt": []byte("This is file 2"),
"test-dir/test-dir2/file3.txt": []byte("This is file 3"),
"test-dir/test-file.txt": []byte("This is test content"),
"test-dir-with-symlink-file/test-file.txt": []byte("This is test content"),
"test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"),
})
ensureFileMode(t, value, "0666")
return nil
}),
),
},
},
})
Expand Down
51 changes: 36 additions & 15 deletions internal/provider/zip_archiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,28 @@ func (a *ZipArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error {
opts.Excludes[i] = filepath.FromSlash(opts.Excludes[i])
}

// Determine whether an empty archive would be generated.
isArchiveEmpty := true

err = filepath.Walk(indirname, a.createWalkFunc("", indirname, opts, &isArchiveEmpty, true))
if err != nil {
return err
}

// Return an error if an empty archive would be generated.
if isArchiveEmpty {
return fmt.Errorf("archive has not been created as it would be empty")
}

if err := a.open(); err != nil {
return err
}
defer a.close()

return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts))
return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts, &isArchiveEmpty, false))
}

func (a *ZipArchiver) createWalkFunc(basePath string, indirname string, opts ArchiveDirOpts) func(path string, info os.FileInfo, err error) error {
func (a *ZipArchiver) createWalkFunc(basePath, indirname string, opts ArchiveDirOpts, isArchiveEmpty *bool, dryRun bool) func(path string, info os.FileInfo, err error) error {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error encountered during file walk: %s", err)
Expand Down Expand Up @@ -146,23 +159,31 @@ func (a *ZipArchiver) createWalkFunc(basePath string, indirname string, opts Arc
}

if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if !opts.ExcludeSymlinkDirectories {
realPath, err := filepath.EvalSymlinks(path)
if err != nil {
return err
}
realPath, err := filepath.EvalSymlinks(path)
if err != nil {
return err
}

realInfo, err := os.Stat(realPath)
if err != nil {
return err
}
realInfo, err := os.Stat(realPath)
if err != nil {
return err
}

if realInfo.IsDir() {
return filepath.Walk(realPath, a.createWalkFunc(archivePath, realPath, opts))
if realInfo.IsDir() {
if !opts.ExcludeSymlinkDirectories {
return filepath.Walk(realPath, a.createWalkFunc(archivePath, realPath, opts, isArchiveEmpty, dryRun))
} else {
return filepath.SkipDir
}

info = realInfo
}

info = realInfo
}

*isArchiveEmpty = false

if dryRun {
return nil
}

fh, err := zip.FileInfoHeader(info)
Expand Down
15 changes: 4 additions & 11 deletions internal/provider/zip_archiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"testing"
"time"
Expand Down Expand Up @@ -218,11 +217,8 @@ func TestZipArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) {
ExcludeSymlinkDirectories: true,
})

regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `)
found := regex.Match([]byte(err.Error()))

if !found {
t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error())
if err != nil {
t.Errorf("expected no error: %s", err)
}
}

Expand Down Expand Up @@ -270,11 +266,8 @@ func TestZipArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) {
ExcludeSymlinkDirectories: true,
})

regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `)
found := regex.Match([]byte(err.Error()))

if !found {
t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error())
if err != nil {
t.Errorf("expected no error: %s", err)
}
}

Expand Down
Loading