Skip to content

fix: use raw syscalls to write binary we execute #11684

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

Merged
merged 1 commit into from
Jan 18, 2024
Merged
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
8 changes: 8 additions & 0 deletions provisioner/terraform/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ func (e *executor) execParseJSON(ctx, killCtx context.Context, args, env []strin
cmd.Stdout = out
cmd.Stderr = stdErr

e.server.logger.Debug(ctx, "executing terraform command with JSON result",
slog.F("binary_path", e.binaryPath),
slog.F("args", args),
)
err := cmd.Start()
if err != nil {
return err
Expand Down Expand Up @@ -348,6 +352,10 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
cmd.Dir = e.workdir
cmd.Env = e.basicEnv()

e.server.logger.Debug(ctx, "executing terraform command graph",
slog.F("binary_path", e.binaryPath),
slog.F("args", "graph"),
)
err := cmd.Start()
if err != nil {
return "", err
Expand Down
13 changes: 12 additions & 1 deletion provisioner/terraform/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"runtime"
"sort"
"strings"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -165,8 +166,18 @@ func TestProvision_Cancel(t *testing.T) {

// Example: exec /path/to/terrafork_fake_cancel.sh 1.2.1 apply "$@"
content := fmt.Sprintf("#!/bin/sh\nexec %q %s %s \"$@\"\n", fakeBin, terraform.TerraformVersion.String(), tt.mode)
err := os.WriteFile(binPath, []byte(content), 0o755) //#nosec

// golang's standard OS library can sometimes leave the file descriptor open even after
// "Closing" the file (which can then lead to a "text file busy" error, so we bypass this
// and use syscall directly).
Comment on lines +170 to +172
Copy link
Member

Choose a reason for hiding this comment

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

Is this a known / documented issue in the stdlib?
Would be nice to remove this workaround if it gets fixed in a later version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TBH, I think what's supposed to happen is that it only leaves the fd open if there is some pending I/O, like on another goroutine, which wouldn't apply here. But, behavior is different on different OSes, so it's hard to be sure, and I'm at a loss for another explanation of "text file busy".

I figured since we don't need any non-blocking IO or polling, it would be better to just simplify the syscalls we make, and see if we still see the flake.

fd, err := syscall.Open(binPath, syscall.O_WRONLY|syscall.O_CREAT, 0o755)
require.NoError(t, err)
n, err := syscall.Write(fd, []byte(content))
require.NoError(t, err)
require.Equal(t, len(content), n)
err = syscall.Close(fd)
require.NoError(t, err)
t.Logf("wrote fake terraform script to %s", binPath)

ctx, api := setupProvisioner(t, &provisionerServeOptions{
binaryPath: binPath,
Expand Down