150 lines
6.5 KiB
Go
150 lines
6.5 KiB
Go
|
package workdir
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
)
|
||
|
|
||
|
// Dir represents a single Terraform working directory.
|
||
|
//
|
||
|
// "Working directory" is unfortunately a slight misnomer, because non-default
|
||
|
// options can potentially stretch the definition such that multiple working
|
||
|
// directories end up appearing to share a data directory, or other similar
|
||
|
// anomolies, but we continue to use this terminology both for historical
|
||
|
// reasons and because it reflects the common case without any special
|
||
|
// overrides.
|
||
|
//
|
||
|
// The naming convention for methods on this type is that methods whose names
|
||
|
// begin with "Override" affect only characteristics of the particular object
|
||
|
// they're called on, changing where it looks for data, while methods whose
|
||
|
// names begin with "Set" will write settings to disk such that other instances
|
||
|
// referring to the same directories will also see them. Given that, the
|
||
|
// "Override" methods should be used only during the initialization steps
|
||
|
// for a Dir object, typically only inside "package main", so that all
|
||
|
// subsequent work elsewhere will access consistent locations on disk.
|
||
|
//
|
||
|
// We're gradually transitioning to using this type to manage working directory
|
||
|
// settings, and so not everything in the working directory "data dir" is
|
||
|
// encapsulated here yet, but hopefully we'll gradually migrate all of those
|
||
|
// settings here over time. The working directory state not yet managed in here
|
||
|
// is typically managed directly in the "command" package, either directly
|
||
|
// inside commands or in methods of the giant command.Meta type.
|
||
|
type Dir struct {
|
||
|
// mainDir is the path to the directory that we present as the
|
||
|
// "working directory" in the user model, which is typically the
|
||
|
// current working directory when running Terraform CLI, or the
|
||
|
// directory explicitly chosen by the user using the -chdir=...
|
||
|
// global option.
|
||
|
mainDir string
|
||
|
|
||
|
// originalDir is the path to the working directory that was
|
||
|
// selected when creating the Terraform CLI process, regardless of
|
||
|
// -chdir=... being set. This is only for very limited purposes
|
||
|
// related to backward compatibility; most functionality should
|
||
|
// use mainDir instead.
|
||
|
originalDir string
|
||
|
|
||
|
// dataDir is the path to the directory where we will store our
|
||
|
// working directory settings and artifacts. This is typically a
|
||
|
// directory named ".terraform" within mainDir, but users may
|
||
|
// override it.
|
||
|
dataDir string
|
||
|
}
|
||
|
|
||
|
// NewDir constructs a new working directory, anchored at the given path.
|
||
|
//
|
||
|
// In normal use, mainPath should be "." to reflect the current working
|
||
|
// directory, with "package main" having switched the process's current
|
||
|
// working directory if necessary prior to calling this function. However,
|
||
|
// unusual situations in tests may set mainPath to a temporary directory, or
|
||
|
// similar.
|
||
|
//
|
||
|
// WARNING: Although the logic in this package is intended to work regardless
|
||
|
// of whether mainPath is actually the current working directory, we're
|
||
|
// currently in a transitional state where this package shares responsibility
|
||
|
// for the working directory with various command.Meta methods, and those
|
||
|
// often assume that the main path of the working directory will always be
|
||
|
// ".". If you're writing test code that spans across both areas of
|
||
|
// responsibility then you must ensure that the test temporarily changes the
|
||
|
// test process's working directory to the directory returned by RootModuleDir
|
||
|
// before using the result inside a command.Meta.
|
||
|
func NewDir(mainPath string) *Dir {
|
||
|
mainPath = filepath.Clean(mainPath)
|
||
|
return &Dir{
|
||
|
mainDir: mainPath,
|
||
|
originalDir: mainPath,
|
||
|
dataDir: filepath.Join(mainPath, ".terraform"),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// OverrideOriginalWorkingDir records a different path as the
|
||
|
// "original working directory" for the reciever.
|
||
|
//
|
||
|
// Use this only to record the original working directory when Terraform is run
|
||
|
// with the -chdir=... global option. In that case, the directory given in
|
||
|
// -chdir=... is the "main path" to pass in to NewDir, while the original
|
||
|
// working directory should be sent to this method.
|
||
|
func (d *Dir) OverrideOriginalWorkingDir(originalPath string) {
|
||
|
d.originalDir = filepath.Clean(originalPath)
|
||
|
}
|
||
|
|
||
|
// OverrideDataDir chooses a specific alternative directory to read and write
|
||
|
// the persistent working directory settings.
|
||
|
//
|
||
|
// "package main" can call this if it detects that the user has overridden
|
||
|
// the default location by setting the relevant environment variable. Don't
|
||
|
// call this when that environment variable isn't set, in order to preserve
|
||
|
// the default setting of a dot-prefixed directory directly inside the main
|
||
|
// working directory.
|
||
|
func (d *Dir) OverrideDataDir(dataDir string) {
|
||
|
d.dataDir = filepath.Clean(dataDir)
|
||
|
}
|
||
|
|
||
|
// RootModuleDir returns the directory where we expect to find the root module
|
||
|
// configuration for this working directory.
|
||
|
func (d *Dir) RootModuleDir() string {
|
||
|
// The root module configuration is just directly inside the main directory.
|
||
|
return d.mainDir
|
||
|
}
|
||
|
|
||
|
// OriginalWorkingDir returns the true, operating-system-originated working
|
||
|
// directory that the current Terraform process was launched from.
|
||
|
//
|
||
|
// This is usually the same as the main working directory, but differs in the
|
||
|
// special case where the user ran Terraform with the global -chdir=...
|
||
|
// option. This is here only for a few backward compatibility affordances
|
||
|
// from before we had the -chdir=... option, so should typically not be used
|
||
|
// for anything new.
|
||
|
func (d *Dir) OriginalWorkingDir() string {
|
||
|
return d.originalDir
|
||
|
}
|
||
|
|
||
|
// DataDir returns the base path where the reciever keeps all of the settings
|
||
|
// and artifacts that must persist between consecutive commands in a single
|
||
|
// session.
|
||
|
//
|
||
|
// This is exported only to allow the legacy behaviors in command.Meta to
|
||
|
// continue accessing this directory directly. Over time we should replace
|
||
|
// all of those direct accesses with methods on this type, and then remove
|
||
|
// this method. Avoid using this method for new use-cases.
|
||
|
func (d *Dir) DataDir() string {
|
||
|
return d.dataDir
|
||
|
}
|
||
|
|
||
|
// ensureDataDir creates the data directory and all of the necessary parent
|
||
|
// directories that lead to it, if they don't already exist.
|
||
|
//
|
||
|
// For directories that already exist ensureDataDir will preserve their
|
||
|
// permissions, while it'll create any new directories to be owned by the user
|
||
|
// running Terraform, readable and writable by that user, and readable by
|
||
|
// all other users, or some approximation of that on non-Unix platforms which
|
||
|
// have a different permissions model.
|
||
|
func (d *Dir) ensureDataDir() error {
|
||
|
err := os.MkdirAll(d.dataDir, 0755)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to prepare working directory: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|