Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: warn when .terraform.lock.hcl is modified during terraform init#18276

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
blink-so wants to merge6 commits intomainfromfix/terraform-lock-file-warning
Closed
Show file tree
Hide file tree
Changes fromall 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
101 changes: 101 additions & 0 deletionsprovisioner/terraform/executor.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,8 @@
"bufio"
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
Expand DownExpand Up@@ -222,6 +224,16 @@
e.mut.Lock()
defere.mut.Unlock()

// Save .terraform.lock.hcl content before running terraform init
lockFilePath:=getTerraformLockFilePath(e.workdir)
preInitLockFile:=lockFilePath+".pre-init"

// Copy the lock file if it exists
iflockFileData,err:=os.ReadFile(lockFilePath);err==nil {
_=os.WriteFile(preInitLockFile,lockFileData,0644)

Check failure on line 233 in provisioner/terraform/executor.go

View workflow job for this annotation

GitHub Actions/ lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
Copy link
Member

Choose a reason for hiding this comment

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

  1. This needs to be formatted speficially as octal, e.g.:
-_ = os.WriteFile(preInitLockFile, lockFileData, 0644)+_ = os.WriteFile(preInitLockFile, lockFileData, 0o644)
  1. I don't think we should be writing to the work directory directly. If we need to write a file, we should write to a temporary directory instead. Do weactually need to persist the lock file content pre-init? Can't we just store in memory instead?

  2. If we write a file to disk, we need to ensure we use minimal permissions (0o600) or thegosec linter will complain.

deferos.Remove(preInitLockFile)// Clean up temporary file
}

outWriter,doneOut:=logWriter(logr,proto.LogLevel_DEBUG)
errWriter,doneErr:=logWriter(logr,proto.LogLevel_ERROR)
deferfunc() {
Expand All@@ -242,6 +254,28 @@
}

err:=e.execWriteOutput(ctx,killCtx,args,e.basicEnv(),outWriter,errBuf)

// Check if .terraform.lock.hcl was modified after terraform init
diff:=generateFileDiff(preInitLockFile,lockFilePath)
ifdiff!="" {
// 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
ifoutWriter!=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),
)
}

varexitErr*exec.ExitError
ifxerrors.As(err,&exitErr) {
ifbytes.Contains(errBuf.b.Bytes(), []byte("text file busy")) {
Expand All@@ -259,6 +293,73 @@
returnfilepath.Join(workdir,"terraform.tfstate")
}

funcgetTerraformLockFilePath(workdirstring)string {
returnfilepath.Join(workdir,".terraform.lock.hcl")
}

// calculateFileChecksum calculates the SHA256 checksum of a file.
// Returns empty string if file doesn't exist or can't be read.
funccalculateFileChecksum(filePathstring)string {
data,err:=os.ReadFile(filePath)
iferr!=nil {
return""
}
hash:=sha256.Sum256(data)
returnhex.EncodeToString(hash[:])
}

// generateFileDiff generates a simple diff between two file contents.
// Returns empty string if files can't be read or are identical.
funcgenerateFileDiff(beforePath,afterPathstring)string {
beforeData,err:=os.ReadFile(beforePath)
iferr!=nil {
return""
}
afterData,err:=os.ReadFile(afterPath)
iferr!=nil {
return""
}

ifbytes.Equal(beforeData,afterData) {
return""
}

// Simple line-by-line diff
beforeLines:=strings.Split(string(beforeData),"\n")
afterLines:=strings.Split(string(afterData),"\n")

vardiff strings.Builder
diff.WriteString("--- .terraform.lock.hcl (before terraform init)\n")

Check failure on line 332 in provisioner/terraform/executor.go

View workflow job for this annotation

GitHub Actions/ lint

unhandled-error: Unhandled error in call to function strings.Builder.WriteString (revive)
diff.WriteString("+++ .terraform.lock.hcl (after terraform init)\n")

Check failure on line 333 in provisioner/terraform/executor.go

View workflow job for this annotation

GitHub Actions/ lint

unhandled-error: Unhandled error in call to function strings.Builder.WriteString (revive)

// Simple diff showing added/removed lines
beforeMap:=make(map[string]bool)
for_,line:=rangebeforeLines {
beforeMap[line]=true
}

afterMap:=make(map[string]bool)
for_,line:=rangeafterLines {
afterMap[line]=true
}

// Show removed lines
for_,line:=rangebeforeLines {
if!afterMap[line]&&strings.TrimSpace(line)!="" {
diff.WriteString("- "+line+"\n")

Check failure on line 349 in provisioner/terraform/executor.go

View workflow job for this annotation

GitHub Actions/ lint

unhandled-error: Unhandled error in call to function strings.Builder.WriteString (revive)
}
}

// Show added lines
for_,line:=rangeafterLines {
if!beforeMap[line]&&strings.TrimSpace(line)!="" {
diff.WriteString("+ "+line+"\n")

Check failure on line 356 in provisioner/terraform/executor.go

View workflow job for this annotation

GitHub Actions/ lint

unhandled-error: Unhandled error in call to function strings.Builder.WriteString (revive)
}
}

returndiff.String()
}

// revive:disable-next-line:flag-parameter
func (e*executor)plan(ctx,killCtx context.Context,env,vars []string,logrlogSink,metadata*proto.Metadata) (*proto.PlanComplete,error) {
ctx,span:=e.server.startTrace(ctx,tracing.FuncName())
Expand Down
81 changes: 81 additions & 0 deletionsprovisioner/terraform/executor_internal_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,8 @@

import (
"encoding/json"
"os"
"path/filepath"
"testing"

tfjson "github.com/hashicorp/terraform-json"
Expand DownExpand Up@@ -173,3 +175,82 @@
})
}
}

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

workdir := "/tmp/test"
expected := filepath.Join(workdir, ".terraform.lock.hcl")
got := getTerraformLockFilePath(workdir)
require.Equal(t, expected, got)
}

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

// Test with non-existent file
checksum := calculateFileChecksum("/non/existent/file")
require.Equal(t, "", checksum)

// Test with actual file
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.txt")
testContent := "test content for checksum"

err := os.WriteFile(testFile, []byte(testContent), 0o600)
require.NoError(t, err)

checksum1 := calculateFileChecksum(testFile)
require.NotEmpty(t, checksum1)
require.Len(t, checksum1, 64) // SHA256 hex string length

// Same content should produce same checksum
checksum2 := calculateFileChecksum(testFile)
require.Equal(t, checksum1, checksum2)

// Different content should produce different checksum
err = os.WriteFile(testFile, []byte("different content"), 0o600)
require.NoError(t, err)

checksum3 := calculateFileChecksum(testFile)
require.NotEqual(t, checksum1, checksum3)
require.Len(t, checksum3, 64)
}

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

tmpDir := t.TempDir()
file1 := filepath.Join(tmpDir, "file1.txt")
file2 := filepath.Join(tmpDir, "file2.txt")

// Test with non-existent files
diff := generateFileDiff("/non/existent/file1", "/non/existent/file2")
require.Equal(t, "", diff)

// Test with identical files
content := "line1\nline2\nline3"
err := os.WriteFile(file1, []byte(content), 0644)

Check failure on line 233 in provisioner/terraform/executor_internal_test.go

View workflow job for this annotation

GitHub Actions/ lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
require.NoError(t, err)
err = os.WriteFile(file2, []byte(content), 0644)

Check failure on line 235 in provisioner/terraform/executor_internal_test.go

View workflow job for this annotation

GitHub Actions/ lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
require.NoError(t, err)

diff = generateFileDiff(file1, file2)
require.Equal(t, "", diff)

// Test with different files
content1 := "line1\nline2\nline3"
content2 := "line1\nmodified line2\nline3\nnew line4"
err = os.WriteFile(file1, []byte(content1), 0644)

Check failure on line 244 in provisioner/terraform/executor_internal_test.go

View workflow job for this annotation

GitHub Actions/ lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
require.NoError(t, err)
err = os.WriteFile(file2, []byte(content2), 0644)

Check failure on line 246 in provisioner/terraform/executor_internal_test.go

View workflow job for this annotation

GitHub Actions/ lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
require.NoError(t, err)

diff = generateFileDiff(file1, file2)
require.NotEmpty(t, diff)
require.Contains(t, diff, "--- .terraform.lock.hcl (before terraform init)")
require.Contains(t, diff, "+++ .terraform.lock.hcl (after terraform init)")
require.Contains(t, diff, "- line2")
require.Contains(t, diff, "+ modified line2")
require.Contains(t, diff, "+ new line4")
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp