-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat: warn when .terraform.lock.hcl is modified during terraform init #18280
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -222,6 +222,10 @@ func (e *executor) init(ctx, killCtx context.Context, logr logSink) error { | |
| e.mut.Lock() | ||
| defer e.mut.Unlock() | ||
|
|
||
| // Read .terraform.lock.hcl content before running terraform init | ||
| lockFilePath := getTerraformLockFilePath(e.workdir) | ||
| preInitLockFileContent, _ := os.ReadFile(lockFilePath) | ||
|
|
||
| outWriter, doneOut := logWriter(logr, proto.LogLevel_DEBUG) | ||
| errWriter, doneErr := logWriter(logr, proto.LogLevel_ERROR) | ||
| defer func() { | ||
|
|
@@ -242,6 +246,29 @@ func (e *executor) init(ctx, killCtx context.Context, logr logSink) error { | |
| } | ||
|
|
||
| err := e.execWriteOutput(ctx, killCtx, args, e.basicEnv(), outWriter, errBuf) | ||
|
|
||
| // Check if .terraform.lock.hcl was modified after terraform init | ||
| postInitLockFileContent, _ := os.ReadFile(lockFilePath) | ||
| diff := generateFileDiff(preInitLockFileContent, postInitLockFileContent) | ||
| if diff != "" { | ||
| // Log informational message about lock file changes with diff | ||
| infoMsg := "INFO: .terraform.lock.hcl was modified during 'terraform init'. " + | ||
| "This is normal when Terraform downloads providers or updates dependencies. " + | ||
| "See https://developer.hashicorp.com/terraform/language/files/dependency-lock#understanding-lock-file-changes " + | ||
| "for more information about lock file changes." | ||
|
|
||
| // Write info message to debug stream | ||
| if outWriter != nil { | ||
| _, _ = outWriter.Write([]byte(infoMsg + "\n")) | ||
| _, _ = outWriter.Write([]byte("\nLock file changes:\n" + diff + "\n")) | ||
| } | ||
|
|
||
| e.logger.Info(ctx, "terraform lock file modified during init", | ||
| slog.F("lock_file_path", lockFilePath), | ||
| slog.F("diff", diff), | ||
| ) | ||
| } | ||
|
|
||
| var exitErr *exec.ExitError | ||
| if xerrors.As(err, &exitErr) { | ||
| if bytes.Contains(errBuf.b.Bytes(), []byte("text file busy")) { | ||
|
|
@@ -259,6 +286,53 @@ func getStateFilePath(workdir string) string { | |
| return filepath.Join(workdir, "terraform.tfstate") | ||
| } | ||
|
|
||
| func getTerraformLockFilePath(workdir string) string { | ||
| return filepath.Join(workdir, ".terraform.lock.hcl") | ||
| } | ||
|
|
||
| // generateFileDiff generates a simple diff between two file contents. | ||
| // Returns empty string if files are identical. | ||
| func generateFileDiff(beforeContent, afterContent []byte) string { | ||
| if bytes.Equal(beforeContent, afterContent) { | ||
| return "" | ||
| } | ||
|
|
||
| // Simple line-by-line diff | ||
| beforeLines := strings.Split(string(beforeContent), "\n") | ||
| afterLines := strings.Split(string(afterContent), "\n") | ||
|
|
||
| var diff strings.Builder | ||
| diff.WriteString("--- .terraform.lock.hcl (before terraform init)\n") | ||
| diff.WriteString("+++ .terraform.lock.hcl (after terraform init)\n") | ||
|
|
||
| // Simple diff showing added/removed lines | ||
| beforeMap := make(map[string]bool) | ||
| for _, line := range beforeLines { | ||
| beforeMap[line] = true | ||
| } | ||
|
|
||
| afterMap := make(map[string]bool) | ||
| for _, line := range afterLines { | ||
| afterMap[line] = true | ||
| } | ||
|
|
||
| // Show removed lines | ||
| for _, line := range beforeLines { | ||
| if !afterMap[line] && strings.TrimSpace(line) != "" { | ||
| diff.WriteString("- " + line + "\n") | ||
| } | ||
| } | ||
|
|
||
| // Show added lines | ||
| for _, line := range afterLines { | ||
| if !beforeMap[line] && strings.TrimSpace(line) != "" { | ||
| diff.WriteString("+ " + line + "\n") | ||
| } | ||
| } | ||
|
|
||
| return diff.String() | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See https://pkg.go.dev/github.com/google/go-cmp/cmp#Diff
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent suggestion! Using go-cmp would definitely provide much better and more readable diff output. I'll switch to using cmp.Diff instead of the custom diff implementation. |
||
|
|
||
| // revive:disable-next-line:flag-parameter | ||
| func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr logSink, metadata *proto.Metadata) (*proto.PlanComplete, error) { | ||
| ctx, span := e.server.startTrace(ctx, tracing.FuncName()) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that this is better left at WARN. If the lockfile is being modified, this potentially means that Coder is needlessly downloading providers from the internet instead of using cached versions, which means longer workspace build times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point! You're absolutely right - lock file modifications can indicate performance issues with unnecessary provider downloads. I'll change it back to WARN level.