Skip to content

Copy lock file only when needed #2823

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 1 addition & 10 deletions config/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package config
import (
"github.com/gruntwork-io/terragrunt/options"
"github.com/gruntwork-io/terragrunt/pkg/log"
"github.com/gruntwork-io/terragrunt/tf"
"github.com/gruntwork-io/terragrunt/util"
)

Expand All @@ -12,13 +11,5 @@ import (
// 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(l log.Logger, opts *options.TerragruntOptions, sourceFolder, destinationFolder string) error {
sourceLockFilePath := util.JoinPath(sourceFolder, tf.TerraformLockFile)
destinationLockFilePath := util.JoinPath(destinationFolder, tf.TerraformLockFile)

if util.FileExists(sourceLockFilePath) {
l.Debugf("Copying lock file from %s to %s", sourceLockFilePath, destinationFolder)
return util.CopyFile(sourceLockFilePath, destinationLockFilePath)
}

return nil
return util.CopyLockFile(sourceFolder, destinationFolder, l)
}
Comment on lines 13 to 15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider removing or utilizing the unused opts parameter.

The opts *options.TerragruntOptions parameter is not being passed to the utility function. This could indicate either:

  1. The parameter is no longer needed and should be removed from the function signature
  2. Some functionality from the options should still be utilized

Please verify that all necessary functionality is preserved and consider cleaning up the function signature if the parameter is truly unused.

If the opts parameter is no longer needed, apply this diff to clean up the function signature:

-func CopyLockFile(l log.Logger, opts *options.TerragruntOptions, sourceFolder, destinationFolder string) error {
+func CopyLockFile(l log.Logger, sourceFolder, destinationFolder string) error {
 	return util.CopyLockFile(sourceFolder, destinationFolder, l)
 }

Alternatively, if the options are still needed for some functionality, please ensure they are properly utilized in the delegation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func CopyLockFile(l log.Logger, opts *options.TerragruntOptions, sourceFolder, destinationFolder string) error {
sourceLockFilePath := util.JoinPath(sourceFolder, tf.TerraformLockFile)
destinationLockFilePath := util.JoinPath(destinationFolder, tf.TerraformLockFile)
if util.FileExists(sourceLockFilePath) {
l.Debugf("Copying lock file from %s to %s", sourceLockFilePath, destinationFolder)
return util.CopyFile(sourceLockFilePath, destinationLockFilePath)
}
return nil
return util.CopyLockFile(sourceFolder, destinationFolder, l)
}
func CopyLockFile(l log.Logger, sourceFolder, destinationFolder string) error {
return util.CopyLockFile(sourceFolder, destinationFolder, l)
}
🤖 Prompt for AI Agents
In config/util.go around lines 13 to 15, the function CopyLockFile has an unused
parameter opts of type *options.TerragruntOptions. Review whether this parameter
is necessary for the function's operation; if it is not needed, remove opts from
the function signature and all calls to this function. If opts is required for
some functionality, update the call to util.CopyLockFile to include or utilize
opts appropriately to preserve intended behavior.

50 changes: 50 additions & 0 deletions util/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,56 @@ func GetTempDir() (string, error) {
return tempDir, nil
}

// CopyLockFile ensures that the Terraform lock file is present in the destination
// folder. The file is copied only when it differs from the one in the source
// folder so that Terragrunt mirrors Terraform's behaviour of updating the lock
// file only when necessary.
func CopyLockFile(sourceFolder string, destinationFolder string, logger log.Logger) error {
sourceLockFilePath, sourceErr := filepath.Abs(JoinPath(sourceFolder, TerraformLockFile))
if sourceErr != nil {
return errors.New(sourceErr)
}

destinationLockFilePath, destErr := filepath.Abs(JoinPath(destinationFolder, TerraformLockFile))
if destErr != nil {
return errors.New(destErr)
}

if sourceLockFilePath == destinationLockFilePath {
logger.Debugf("Source and destination lock file paths are the same: %s. Not copying.", sourceLockFilePath)
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.New(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.New(destReadErr)
}

if bytes.Equal(sourceContents, 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)
}

// GetExcludeDirsFromFile returns a list of directories from the given filename, where each directory path starts on a new line.
func GetExcludeDirsFromFile(baseDir, filename string) ([]string, error) {
filename, err := CanonicalPath(filename, baseDir)
Expand Down
122 changes: 122 additions & 0 deletions util/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,3 +647,125 @@ func TestWalkWithSymlinksErrors(t *testing.T) {
return err
}))
}

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

l := logger.CreateLogger()

t.Run("SameSourceAndDestination", func(t *testing.T) {
t.Parallel()

sourceFolder := "/path/to/folder"
destinationFolder := "/path/to/folder"

err := util.CopyLockFile(sourceFolder, destinationFolder, l)
require.NoError(t, err)
})

t.Run("SourceLockFileDoesNotExist", func(t *testing.T) {
t.Parallel()

sourceFolder := "/path/to/folder"
destinationFolder := "/path/to/destination"

err := util.CopyLockFile(sourceFolder, destinationFolder, l)
require.NoError(t, err)
})

t.Run("DestinationLockFileDoesNotExist", func(t *testing.T) {
t.Parallel()

sourceFolder := t.TempDir()
destinationFolder := t.TempDir()

sourceLockFilePath := filepath.Join(sourceFolder, util.TerraformLockFile)
destinationLockFilePath := filepath.Join(destinationFolder, util.TerraformLockFile)

// Create source lock file
err := os.WriteFile(sourceLockFilePath, []byte("lock file contents"), 0644)
require.NoError(t, err)

err = util.CopyLockFile(sourceFolder, destinationFolder, l)
require.NoError(t, err)

// Verify destination lock file exists
_, err = os.Stat(destinationLockFilePath)
require.NoError(t, err)

// Verify destination lock file contents
destinationContents, err := os.ReadFile(destinationLockFilePath)
require.NoError(t, err)
assert.Equal(t, []byte("lock file contents"), destinationContents)

// Clean up
err = os.Remove(sourceLockFilePath)
require.NoError(t, err)
err = os.Remove(destinationLockFilePath)
require.NoError(t, err)
})

t.Run("SameContents", func(t *testing.T) {
t.Parallel()

sourceFolder := t.TempDir()
destinationFolder := t.TempDir()

sourceLockFilePath := filepath.Join(sourceFolder, util.TerraformLockFile)
destinationLockFilePath := filepath.Join(destinationFolder, util.TerraformLockFile)

// Create source lock file
err := os.WriteFile(sourceLockFilePath, []byte("lock file contents"), 0644)
require.NoError(t, err)

// Create destination lock file with same contents
err = os.WriteFile(destinationLockFilePath, []byte("lock file contents"), 0644)
require.NoError(t, err)

err = util.CopyLockFile(sourceFolder, destinationFolder, l)
require.NoError(t, err)

// Verify destination lock file contents remain the same
destinationContents, err := os.ReadFile(destinationLockFilePath)
require.NoError(t, err)
assert.Equal(t, []byte("lock file contents"), destinationContents)

// Clean up
err = os.Remove(sourceLockFilePath)
require.NoError(t, err)
err = os.Remove(destinationLockFilePath)
require.NoError(t, err)
})

t.Run("DifferentContents", func(t *testing.T) {
t.Parallel()

sourceFolder := t.TempDir()
destinationFolder := t.TempDir()

sourceLockFilePath := filepath.Join(sourceFolder, util.TerraformLockFile)
destinationLockFilePath := filepath.Join(destinationFolder, util.TerraformLockFile)

// Create source lock file
err := os.WriteFile(sourceLockFilePath, []byte("lock file contents"), 0644)
require.NoError(t, err)

// Create destination lock file with different contents
err = os.WriteFile(destinationLockFilePath, []byte("different contents"), 0644)
require.NoError(t, err)

err = util.CopyLockFile(sourceFolder, destinationFolder, l)
require.NoError(t, err)

// Verify destination lock file contents are updated
destinationContents, err := os.ReadFile(destinationLockFilePath)
require.NoError(t, err)
assert.Equal(t, []byte("lock file contents"), destinationContents)

// Clean up
err = os.Remove(sourceLockFilePath)
require.NoError(t, err)
err = os.Remove(destinationLockFilePath)
require.NoError(t, err)
})
}