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  }