github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/workdir/dir.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package workdir 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 ) 11 12 // Dir represents a single Terraform working directory. 13 // 14 // "Working directory" is unfortunately a slight misnomer, because non-default 15 // options can potentially stretch the definition such that multiple working 16 // directories end up appearing to share a data directory, or other similar 17 // anomolies, but we continue to use this terminology both for historical 18 // reasons and because it reflects the common case without any special 19 // overrides. 20 // 21 // The naming convention for methods on this type is that methods whose names 22 // begin with "Override" affect only characteristics of the particular object 23 // they're called on, changing where it looks for data, while methods whose 24 // names begin with "Set" will write settings to disk such that other instances 25 // referring to the same directories will also see them. Given that, the 26 // "Override" methods should be used only during the initialization steps 27 // for a Dir object, typically only inside "package main", so that all 28 // subsequent work elsewhere will access consistent locations on disk. 29 // 30 // We're gradually transitioning to using this type to manage working directory 31 // settings, and so not everything in the working directory "data dir" is 32 // encapsulated here yet, but hopefully we'll gradually migrate all of those 33 // settings here over time. The working directory state not yet managed in here 34 // is typically managed directly in the "command" package, either directly 35 // inside commands or in methods of the giant command.Meta type. 36 type Dir struct { 37 // mainDir is the path to the directory that we present as the 38 // "working directory" in the user model, which is typically the 39 // current working directory when running Terraform CLI, or the 40 // directory explicitly chosen by the user using the -chdir=... 41 // global option. 42 mainDir string 43 44 // originalDir is the path to the working directory that was 45 // selected when creating the Terraform CLI process, regardless of 46 // -chdir=... being set. This is only for very limited purposes 47 // related to backward compatibility; most functionality should 48 // use mainDir instead. 49 originalDir string 50 51 // dataDir is the path to the directory where we will store our 52 // working directory settings and artifacts. This is typically a 53 // directory named ".terraform" within mainDir, but users may 54 // override it. 55 dataDir string 56 } 57 58 // NewDir constructs a new working directory, anchored at the given path. 59 // 60 // In normal use, mainPath should be "." to reflect the current working 61 // directory, with "package main" having switched the process's current 62 // working directory if necessary prior to calling this function. However, 63 // unusual situations in tests may set mainPath to a temporary directory, or 64 // similar. 65 // 66 // WARNING: Although the logic in this package is intended to work regardless 67 // of whether mainPath is actually the current working directory, we're 68 // currently in a transitional state where this package shares responsibility 69 // for the working directory with various command.Meta methods, and those 70 // often assume that the main path of the working directory will always be 71 // ".". If you're writing test code that spans across both areas of 72 // responsibility then you must ensure that the test temporarily changes the 73 // test process's working directory to the directory returned by RootModuleDir 74 // before using the result inside a command.Meta. 75 func NewDir(mainPath string) *Dir { 76 mainPath = filepath.Clean(mainPath) 77 return &Dir{ 78 mainDir: mainPath, 79 originalDir: mainPath, 80 dataDir: filepath.Join(mainPath, ".terraform"), 81 } 82 } 83 84 // OverrideOriginalWorkingDir records a different path as the 85 // "original working directory" for the reciever. 86 // 87 // Use this only to record the original working directory when Terraform is run 88 // with the -chdir=... global option. In that case, the directory given in 89 // -chdir=... is the "main path" to pass in to NewDir, while the original 90 // working directory should be sent to this method. 91 func (d *Dir) OverrideOriginalWorkingDir(originalPath string) { 92 d.originalDir = filepath.Clean(originalPath) 93 } 94 95 // OverrideDataDir chooses a specific alternative directory to read and write 96 // the persistent working directory settings. 97 // 98 // "package main" can call this if it detects that the user has overridden 99 // the default location by setting the relevant environment variable. Don't 100 // call this when that environment variable isn't set, in order to preserve 101 // the default setting of a dot-prefixed directory directly inside the main 102 // working directory. 103 func (d *Dir) OverrideDataDir(dataDir string) { 104 d.dataDir = filepath.Clean(dataDir) 105 } 106 107 // RootModuleDir returns the directory where we expect to find the root module 108 // configuration for this working directory. 109 func (d *Dir) RootModuleDir() string { 110 // The root module configuration is just directly inside the main directory. 111 return d.mainDir 112 } 113 114 // OriginalWorkingDir returns the true, operating-system-originated working 115 // directory that the current Terraform process was launched from. 116 // 117 // This is usually the same as the main working directory, but differs in the 118 // special case where the user ran Terraform with the global -chdir=... 119 // option. This is here only for a few backward compatibility affordances 120 // from before we had the -chdir=... option, so should typically not be used 121 // for anything new. 122 func (d *Dir) OriginalWorkingDir() string { 123 return d.originalDir 124 } 125 126 // DataDir returns the base path where the reciever keeps all of the settings 127 // and artifacts that must persist between consecutive commands in a single 128 // session. 129 // 130 // This is exported only to allow the legacy behaviors in command.Meta to 131 // continue accessing this directory directly. Over time we should replace 132 // all of those direct accesses with methods on this type, and then remove 133 // this method. Avoid using this method for new use-cases. 134 func (d *Dir) DataDir() string { 135 return d.dataDir 136 } 137 138 // ensureDataDir creates the data directory and all of the necessary parent 139 // directories that lead to it, if they don't already exist. 140 // 141 // For directories that already exist ensureDataDir will preserve their 142 // permissions, while it'll create any new directories to be owned by the user 143 // running Terraform, readable and writable by that user, and readable by 144 // all other users, or some approximation of that on non-Unix platforms which 145 // have a different permissions model. 146 func (d *Dir) ensureDataDir() error { 147 err := os.MkdirAll(d.dataDir, 0755) 148 if err != nil { 149 return fmt.Errorf("failed to prepare working directory: %w", err) 150 } 151 return nil 152 }