From e74c486840685718e2164bd6ab672a5a35add1d6 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sat, 2 Dec 2023 20:49:26 +0000 Subject: [PATCH 1/6] Don't copy lock file when source and dest are the same --- util/file.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/util/file.go b/util/file.go index a76205744b..44d41c59d7 100644 --- a/util/file.go +++ b/util/file.go @@ -584,10 +584,19 @@ func (err PathIsNotFile) Error() string { // Terraform 0.14 now generates a lock file when you run `terraform init`. // If any such file exists, this function will copy the lock file to the destination folder func CopyLockFile(sourceFolder string, destinationFolder string, logger *logrus.Entry) error { - sourceLockFilePath := JoinPath(sourceFolder, TerraformLockFile) - destinationLockFilePath := JoinPath(destinationFolder, TerraformLockFile) + sourceLockFilePath, sourceErr := filepath.Abs(JoinPath(sourceFolder, TerraformLockFile)) + destinationLockFilePath, destErr := filepath.Abs(JoinPath(destinationFolder, TerraformLockFile)) - if FileExists(sourceLockFilePath) { + if sourceErr != nil { + return errors.WithStackTrace(sourceErr) + } + if destErr != nil { + return errors.WithStackTrace(destErr) + } + + if sourceLockFilePath == destinationLockFilePath { + logger.Debugf("Source and destination lock file paths are the same: %s. Not copying.", sourceLockFilePath) + } else if FileExists(sourceLockFilePath) { logger.Debugf("Copying lock file from %s to %s", sourceLockFilePath, destinationFolder) return CopyFile(sourceLockFilePath, destinationLockFilePath) } From 34bdae642590ce6932c29d39e6f05e32ca0d5ee7 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sun, 3 Dec 2023 03:57:55 +0000 Subject: [PATCH 2/6] Copy lock file only when contents vary --- util/file.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/util/file.go b/util/file.go index 44d41c59d7..7084ba989c 100644 --- a/util/file.go +++ b/util/file.go @@ -585,23 +585,40 @@ func (err PathIsNotFile) Error() string { // If any such file exists, this function will copy the lock file to the destination folder func CopyLockFile(sourceFolder string, destinationFolder string, logger *logrus.Entry) error { sourceLockFilePath, sourceErr := filepath.Abs(JoinPath(sourceFolder, TerraformLockFile)) - destinationLockFilePath, destErr := filepath.Abs(JoinPath(destinationFolder, TerraformLockFile)) - if sourceErr != nil { return errors.WithStackTrace(sourceErr) } + destinationLockFilePath, destErr := filepath.Abs(JoinPath(destinationFolder, TerraformLockFile)) if destErr != nil { return errors.WithStackTrace(destErr) } if sourceLockFilePath == destinationLockFilePath { logger.Debugf("Source and destination lock file paths are the same: %s. Not copying.", sourceLockFilePath) - } else if FileExists(sourceLockFilePath) { - logger.Debugf("Copying lock file from %s to %s", sourceLockFilePath, destinationFolder) - return CopyFile(sourceLockFilePath, destinationLockFilePath) + return nil } - return nil + if !FileExists(sourceLockFilePath) { + logger.Debugf("Source lock file does not exist: %s. Not copying.", sourceLockFilePath) + return nil + } + + sourceContents, sourceReadErr := os.ReadFile(sourceLockFilePath) + if sourceReadErr != nil { + return errors.WithStackTrace(sourceReadErr) + } + destinationContents, destReadErr := os.ReadFile(destinationLockFilePath) + if destReadErr != nil { + return errors.WithStackTrace(destReadErr) + } + + if string(sourceContents) == string(destinationContents) { + logger.Debugf("Source and destination lock file contents are the same. Not copying.") + return nil + } + + logger.Debugf("Copying lock file from %s to %s", sourceLockFilePath, destinationFolder) + return WriteFileWithSamePermissions(sourceLockFilePath, destinationLockFilePath, sourceContents) } // ListTfFiles returns a list of all TF files in the specified directory. From 3e0ef16d4233512cb7250b05b94f69eecd1ddc22 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sat, 6 Jan 2024 04:58:59 +0000 Subject: [PATCH 3/6] Run precommit --- util/file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/file.go b/util/file.go index 7084ba989c..767c9ec07c 100644 --- a/util/file.go +++ b/util/file.go @@ -616,7 +616,7 @@ func CopyLockFile(sourceFolder string, destinationFolder string, logger *logrus. logger.Debugf("Source and destination lock file contents are the same. Not copying.") return nil } - + logger.Debugf("Copying lock file from %s to %s", sourceLockFilePath, destinationFolder) return WriteFileWithSamePermissions(sourceLockFilePath, destinationLockFilePath, sourceContents) } From a8b9a2af48f25e3feccd2769502a68e425d24007 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sat, 17 Feb 2024 00:50:04 +0000 Subject: [PATCH 4/6] Add check for the existence of the destination lock file --- util/file.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/file.go b/util/file.go index 767c9ec07c..f853f2ad0b 100644 --- a/util/file.go +++ b/util/file.go @@ -607,6 +607,12 @@ func CopyLockFile(sourceFolder string, destinationFolder string, logger *logrus. if sourceReadErr != nil { return errors.WithStackTrace(sourceReadErr) } + + if !FileExists(destinationLockFilePath) { + logger.Debugf("Destination lock file does not exist: %s. Copying.", destinationLockFilePath) + return WriteFileWithSamePermissions(sourceLockFilePath, destinationLockFilePath, sourceContents) + } + destinationContents, destReadErr := os.ReadFile(destinationLockFilePath) if destReadErr != nil { return errors.WithStackTrace(destReadErr) From f2edb5a3ba989a895c028b8d8000ecf3fc48c93d Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sat, 17 Feb 2024 00:50:49 +0000 Subject: [PATCH 5/6] Change string comparison to bytes.Equal --- util/file.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/file.go b/util/file.go index f853f2ad0b..782e128d0d 100644 --- a/util/file.go +++ b/util/file.go @@ -1,6 +1,7 @@ package util import ( + "bytes" "encoding/gob" "io" "os" @@ -618,7 +619,7 @@ func CopyLockFile(sourceFolder string, destinationFolder string, logger *logrus. return errors.WithStackTrace(destReadErr) } - if string(sourceContents) == string(destinationContents) { + if bytes.Equal(sourceContents, destinationContents) { logger.Debugf("Source and destination lock file contents are the same. Not copying.") return nil } From 889aca5a247ac63ec51cd1a87ea00690b0f09e40 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sat, 17 Feb 2024 01:06:00 +0000 Subject: [PATCH 6/6] Add unit test --- util/file_test.go | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/util/file_test.go b/util/file_test.go index 2e032c4356..3a5ecda729 100644 --- a/util/file_test.go +++ b/util/file_test.go @@ -2,6 +2,8 @@ package util import ( "errors" + "io" + "io/ioutil" "os" "path" "path/filepath" @@ -11,6 +13,7 @@ import ( "fmt" "github.com/gruntwork-io/terragrunt/test/helpers" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -336,5 +339,116 @@ func TestEmptyDir(t *testing.T) { assert.NoError(t, err) assert.Equal(t, testCase.expectEmpty, emptyValue, "For path %s", testCase.path) } +} + +func TestCopyLockFile(t *testing.T) { + logger := logrus.New() + logger.Out = io.Discard + loggerEntry := logger.WithFields(logrus.Fields{}) + + t.Run("SameSourceAndDestination", func(t *testing.T) { + sourceFolder := "/path/to/folder" + destinationFolder := "/path/to/folder" + + err := CopyLockFile(sourceFolder, destinationFolder, loggerEntry) + assert.NoError(t, err) + }) + + t.Run("SourceLockFileDoesNotExist", func(t *testing.T) { + sourceFolder := "/path/to/folder" + destinationFolder := "/path/to/destination" + + err := CopyLockFile(sourceFolder, destinationFolder, loggerEntry) + assert.NoError(t, err) + }) + + t.Run("DestinationLockFileDoesNotExist", func(t *testing.T) { + sourceFolder := t.TempDir() + destinationFolder := t.TempDir() + + sourceLockFilePath := filepath.Join(sourceFolder, TerraformLockFile) + destinationLockFilePath := filepath.Join(destinationFolder, TerraformLockFile) + + // Create source lock file + err := ioutil.WriteFile(sourceLockFilePath, []byte("lock file contents"), 0644) + require.NoError(t, err) + + err = CopyLockFile(sourceFolder, destinationFolder, loggerEntry) + assert.NoError(t, err) + + // Verify destination lock file exists + _, err = os.Stat(destinationLockFilePath) + assert.NoError(t, err) + + // Verify destination lock file contents + destinationContents, err := ioutil.ReadFile(destinationLockFilePath) + assert.NoError(t, err) + assert.Equal(t, []byte("lock file contents"), destinationContents) + + // Clean up + err = os.Remove(sourceLockFilePath) + assert.NoError(t, err) + err = os.Remove(destinationLockFilePath) + assert.NoError(t, err) + }) + + t.Run("SameContents", func(t *testing.T) { + sourceFolder := t.TempDir() + destinationFolder := t.TempDir() + sourceLockFilePath := filepath.Join(sourceFolder, TerraformLockFile) + destinationLockFilePath := filepath.Join(destinationFolder, TerraformLockFile) + + // Create source lock file + err := ioutil.WriteFile(sourceLockFilePath, []byte("lock file contents"), 0644) + require.NoError(t, err) + + // Create destination lock file with same contents + err = ioutil.WriteFile(destinationLockFilePath, []byte("lock file contents"), 0644) + require.NoError(t, err) + + err = CopyLockFile(sourceFolder, destinationFolder, loggerEntry) + assert.NoError(t, err) + + // Verify destination lock file contents remain the same + destinationContents, err := ioutil.ReadFile(destinationLockFilePath) + assert.NoError(t, err) + assert.Equal(t, []byte("lock file contents"), destinationContents) + + // Clean up + err = os.Remove(sourceLockFilePath) + assert.NoError(t, err) + err = os.Remove(destinationLockFilePath) + assert.NoError(t, err) + }) + + t.Run("DifferentContents", func(t *testing.T) { + sourceFolder := t.TempDir() + destinationFolder := t.TempDir() + + sourceLockFilePath := filepath.Join(sourceFolder, TerraformLockFile) + destinationLockFilePath := filepath.Join(destinationFolder, TerraformLockFile) + + // Create source lock file + err := ioutil.WriteFile(sourceLockFilePath, []byte("lock file contents"), 0644) + require.NoError(t, err) + + // Create destination lock file with different contents + err = ioutil.WriteFile(destinationLockFilePath, []byte("different contents"), 0644) + require.NoError(t, err) + + err = CopyLockFile(sourceFolder, destinationFolder, loggerEntry) + assert.NoError(t, err) + + // Verify destination lock file contents are updated + destinationContents, err := ioutil.ReadFile(destinationLockFilePath) + assert.NoError(t, err) + assert.Equal(t, []byte("lock file contents"), destinationContents) + + // Clean up + err = os.Remove(sourceLockFilePath) + assert.NoError(t, err) + err = os.Remove(destinationLockFilePath) + assert.NoError(t, err) + }) }