Skip to content
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
4 changes: 2 additions & 2 deletions provisioner/echo/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ func readResponses(sess *provisionersdk.Session, trans string, suffix string) ([
for i := 0; ; i++ {
paths := []string{
// Try more specific path first, then fallback to generic.
filepath.Join(sess.WorkDirectory, fmt.Sprintf("%d.%s.%s", i, trans, suffix)),
filepath.Join(sess.WorkDirectory, fmt.Sprintf("%d.%s", i, suffix)),
filepath.Join(sess.Files.WorkDirectory(), fmt.Sprintf("%d.%s.%s", i, trans, suffix)),
filepath.Join(sess.Files.WorkDirectory(), fmt.Sprintf("%d.%s", i, suffix)),
}
for pathIndex, path := range paths {
_, err := os.Stat(path)
Expand Down
30 changes: 11 additions & 19 deletions provisioner/terraform/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
Expand All @@ -22,6 +21,7 @@ import (
"golang.org/x/xerrors"

"cdr.dev/slog"
"github.com/coder/coder/v2/provisionersdk/tfpath"

"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/tracing"
Expand All @@ -38,10 +38,10 @@ type executor struct {
server *server
mut *sync.Mutex
binaryPath string
// cachePath and workdir must not be used by multiple processes at once.
// cachePath and files must not be used by multiple processes at once.
cachePath string
cliConfigPath string
workdir string
files tfpath.Layout
// used to capture execution times at various stages
timings *timingAggregator
}
Expand Down Expand Up @@ -90,7 +90,7 @@ func (e *executor) execWriteOutput(ctx, killCtx context.Context, args, env []str

// #nosec
cmd := exec.CommandContext(killCtx, e.binaryPath, args...)
cmd.Dir = e.workdir
cmd.Dir = e.files.WorkDirectory()
if env == nil {
// We don't want to passthrough host env when unset.
env = []string{}
Expand Down Expand Up @@ -131,7 +131,7 @@ func (e *executor) execParseJSON(ctx, killCtx context.Context, args, env []strin

// #nosec
cmd := exec.CommandContext(killCtx, e.binaryPath, args...)
cmd.Dir = e.workdir
cmd.Dir = e.files.WorkDirectory()
cmd.Env = env
out := &bytes.Buffer{}
stdErr := &bytes.Buffer{}
Expand Down Expand Up @@ -225,7 +225,7 @@ func (e *executor) init(ctx, killCtx context.Context, logr logSink) error {
defer e.mut.Unlock()

// Record lock file checksum before init
lockFilePath := filepath.Join(e.workdir, ".terraform.lock.hcl")
lockFilePath := e.files.TerraformLockFile()
preInitChecksum := checksumFileCRC32(ctx, e.logger, lockFilePath)

outWriter, doneOut := e.provisionLogWriter(logr)
Expand Down Expand Up @@ -289,14 +289,6 @@ func checksumFileCRC32(ctx context.Context, logger slog.Logger, path string) uin
return crc32.ChecksumIEEE(content)
}

func getPlanFilePath(workdir string) string {
return filepath.Join(workdir, "terraform.tfplan")
}

func getStateFilePath(workdir string) string {
return filepath.Join(workdir, "terraform.tfstate")
}

// revive:disable-next-line:flag-parameter
func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr logSink, req *proto.PlanRequest) (*proto.PlanComplete, error) {
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
Expand All @@ -307,7 +299,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l

metadata := req.Metadata

planfilePath := getPlanFilePath(e.workdir)
planfilePath := e.files.PlanFilePath()
args := []string{
"plan",
"-no-color",
Expand Down Expand Up @@ -359,7 +351,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
// a workspace build. This removes some added costs of sending the modules
// payload back to coderd if coderd is just going to ignore it.
if !req.OmitModuleFiles {
moduleFiles, err = GetModulesArchive(os.DirFS(e.workdir))
moduleFiles, err = GetModulesArchive(os.DirFS(e.files.WorkDirectory()))
if err != nil {
// TODO: we probably want to persist this error or make it louder eventually
e.logger.Warn(ctx, "failed to archive terraform modules", slog.Error(err))
Expand Down Expand Up @@ -551,7 +543,7 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
var out strings.Builder
cmd := exec.CommandContext(killCtx, e.binaryPath, args...) // #nosec
cmd.Stdout = &out
cmd.Dir = e.workdir
cmd.Dir = e.files.WorkDirectory()
cmd.Env = e.basicEnv()

e.server.logger.Debug(ctx, "executing terraform command graph",
Expand Down Expand Up @@ -588,7 +580,7 @@ func (e *executor) apply(
"-auto-approve",
"-input=false",
"-json",
getPlanFilePath(e.workdir),
e.files.PlanFilePath(),
}

outWriter, doneOut := e.provisionLogWriter(logr)
Expand All @@ -608,7 +600,7 @@ func (e *executor) apply(
if err != nil {
return nil, err
}
statefilePath := getStateFilePath(e.workdir)
statefilePath := e.files.StateFilePath()
stateContent, err := os.ReadFile(statefilePath)
if err != nil {
return nil, xerrors.Errorf("read statefile %q: %w", statefilePath, err)
Expand Down
10 changes: 3 additions & 7 deletions provisioner/terraform/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"time"

"golang.org/x/xerrors"

"github.com/coder/coder/v2/coderd/util/xio"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/provisionersdk/tfpath"
)

const (
Expand All @@ -39,10 +39,6 @@ type modulesFile struct {
Modules []*module `json:"Modules"`
}

func getModulesFilePath(workdir string) string {
return filepath.Join(workdir, ".terraform", "modules", "modules.json")
}

func parseModulesFile(filePath string) ([]*proto.Module, error) {
modules := &modulesFile{}
data, err := os.ReadFile(filePath)
Expand All @@ -62,8 +58,8 @@ func parseModulesFile(filePath string) ([]*proto.Module, error) {
// getModules returns the modules from the modules file if it exists.
// It returns nil if the file does not exist.
// Modules become available after terraform init.
func getModules(workdir string) ([]*proto.Module, error) {
filePath := getModulesFilePath(workdir)
func getModules(files tfpath.Layout) ([]*proto.Module, error) {
filePath := files.ModulesFilePath()
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, nil
}
Expand Down
4 changes: 2 additions & 2 deletions provisioner/terraform/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-
defer span.End()

// Load the module and print any parse errors.
parser, diags := tfparse.New(sess.WorkDirectory, tfparse.WithLogger(s.logger.Named("tfparse")))
parser, diags := tfparse.New(sess.Files.WorkDirectory(), tfparse.WithLogger(s.logger.Named("tfparse")))
if diags.HasErrors() {
return provisionersdk.ParseErrorf("load module: %s", formatDiagnostics(sess.WorkDirectory, diags))
return provisionersdk.ParseErrorf("load module: %s", formatDiagnostics(sess.Files.WorkDirectory(), diags))
}

workspaceTags, _, err := parser.WorkspaceTags(ctx)
Expand Down
12 changes: 6 additions & 6 deletions provisioner/terraform/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (s *server) Plan(
defer cancel()
defer kill()

e := s.executor(sess.WorkDirectory, database.ProvisionerJobTimingStagePlan)
e := s.executor(sess.Files, database.ProvisionerJobTimingStagePlan)
if err := e.checkMinVersion(ctx); err != nil {
return provisionersdk.PlanErrorf("%s", err.Error())
}
Expand All @@ -92,7 +92,7 @@ func (s *server) Plan(
return &proto.PlanComplete{}
}

statefilePath := getStateFilePath(sess.WorkDirectory)
statefilePath := sess.Files.StateFilePath()
if len(sess.Config.State) > 0 {
err := os.WriteFile(statefilePath, sess.Config.State, 0o600)
if err != nil {
Expand Down Expand Up @@ -141,7 +141,7 @@ func (s *server) Plan(
return provisionersdk.PlanErrorf("initialize terraform: %s", err)
}

modules, err := getModules(sess.WorkDirectory)
modules, err := getModules(sess.Files)
if err != nil {
// We allow getModules to fail, as the result is used only
// for telemetry purposes now.
Expand Down Expand Up @@ -184,7 +184,7 @@ func (s *server) Apply(
defer cancel()
defer kill()

e := s.executor(sess.WorkDirectory, database.ProvisionerJobTimingStageApply)
e := s.executor(sess.Files, database.ProvisionerJobTimingStageApply)
if err := e.checkMinVersion(ctx); err != nil {
return provisionersdk.ApplyErrorf("%s", err.Error())
}
Expand All @@ -201,7 +201,7 @@ func (s *server) Apply(
}

// Earlier in the session, Plan() will have written the state file and the plan file.
statefilePath := getStateFilePath(sess.WorkDirectory)
statefilePath := sess.Files.StateFilePath()
env, err := provisionEnv(sess.Config, request.Metadata, nil, nil, nil)
if err != nil {
return provisionersdk.ApplyErrorf("provision env: %s", err)
Expand Down Expand Up @@ -348,7 +348,7 @@ func logTerraformEnvVars(sink logSink) {
// shipped in v1.0.4. It will return the stacktraces of the provider, which will hopefully allow us
// to figure out why it hasn't exited.
func tryGettingCoderProviderStacktrace(sess *provisionersdk.Session) string {
path := filepath.Clean(filepath.Join(sess.WorkDirectory, "../.coder/pprof"))
path := filepath.Clean(filepath.Join(sess.Files.WorkDirectory(), "../.coder/pprof"))
sess.Logger.Info(sess.Context(), "attempting to get stack traces", slog.F("path", path))
c := http.Client{
Transport: &http.Transport{
Expand Down
5 changes: 3 additions & 2 deletions provisioner/terraform/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"golang.org/x/xerrors"

"cdr.dev/slog"
"github.com/coder/coder/v2/provisionersdk/tfpath"

"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/jobreaper"
Expand Down Expand Up @@ -160,14 +161,14 @@ func (s *server) startTrace(ctx context.Context, name string, opts ...trace.Span
))...)
}

func (s *server) executor(workdir string, stage database.ProvisionerJobTimingStage) *executor {
func (s *server) executor(files tfpath.Layout, stage database.ProvisionerJobTimingStage) *executor {
return &executor{
server: s,
mut: s.execMut,
binaryPath: s.binaryPath,
cachePath: s.cachePath,
cliConfigPath: s.cliConfigPath,
workdir: workdir,
files: files,
logger: s.logger.Named("executor"),
timings: newTimingAggregator(stage),
}
Expand Down
2 changes: 1 addition & 1 deletion provisionerd/provisionerd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ func TestProvisionerd(t *testing.T) {
_ *sdkproto.ParseRequest,
cancelOrComplete <-chan struct{},
) *sdkproto.ParseComplete {
data, err := os.ReadFile(filepath.Join(s.WorkDirectory, "test.txt"))
data, err := os.ReadFile(filepath.Join(s.Files.WorkDirectory(), "test.txt"))
require.NoError(t, err)
require.Equal(t, "content", string(data))
s.ProvisionLog(sdkproto.LogLevel_INFO, "hello")
Expand Down
24 changes: 13 additions & 11 deletions provisionersdk/cleanup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"cdr.dev/slog"
"github.com/coder/coder/v2/provisionersdk"
"github.com/coder/coder/v2/provisionersdk/tfpath"
"github.com/coder/coder/v2/testutil"
)

Expand Down Expand Up @@ -40,11 +41,11 @@ func TestStaleSessions(t *testing.T) {
fs, logger := prepare()

// given
first := provisionersdk.SessionDir(uuid.NewString())
first := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, first, now.Add(-7*24*time.Hour))
second := provisionersdk.SessionDir(uuid.NewString())
second := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, second, now.Add(-8*24*time.Hour))
third := provisionersdk.SessionDir(uuid.NewString())
third := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, third, now.Add(-9*24*time.Hour))

// when
Expand All @@ -65,9 +66,9 @@ func TestStaleSessions(t *testing.T) {
fs, logger := prepare()

// given
first := provisionersdk.SessionDir(uuid.NewString())
first := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, first, now.Add(-7*24*time.Hour))
second := provisionersdk.SessionDir(uuid.NewString())
second := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, second, now.Add(-6*24*time.Hour))

// when
Expand All @@ -77,7 +78,7 @@ func TestStaleSessions(t *testing.T) {
entries, err := afero.ReadDir(fs, workDirectory)
require.NoError(t, err)
require.Len(t, entries, 1, "one session should be present")
require.Equal(t, second, entries[0].Name(), 1)
require.Equal(t, second.WorkDirectory(), filepath.Join(workDirectory, entries[0].Name()), 1)
})

t.Run("no stale sessions", func(t *testing.T) {
Expand All @@ -89,9 +90,9 @@ func TestStaleSessions(t *testing.T) {
fs, logger := prepare()

// given
first := provisionersdk.SessionDir(uuid.NewString())
first := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, first, now.Add(-6*24*time.Hour))
second := provisionersdk.SessionDir(uuid.NewString())
second := tfpath.Session(workDirectory, uuid.NewString())
addSessionFolder(t, fs, second, now.Add(-5*24*time.Hour))

// when
Expand All @@ -104,9 +105,10 @@ func TestStaleSessions(t *testing.T) {
})
}

func addSessionFolder(t *testing.T, fs afero.Fs, sessionName string, modTime time.Time) {
err := fs.MkdirAll(filepath.Join(workDirectory, sessionName), 0o755)
func addSessionFolder(t *testing.T, fs afero.Fs, files tfpath.Layout, modTime time.Time) {
workdir := files.WorkDirectory()
err := fs.MkdirAll(workdir, 0o755)
require.NoError(t, err, "can't create session folder")
require.NoError(t, fs.Chtimes(filepath.Join(workDirectory, sessionName), now, modTime), "can't chtime of session dir")
require.NoError(t, fs.Chtimes(workdir, now, modTime), "can't chtime of session dir")
require.NoError(t, err, "can't set times")
}
Loading
Loading