opentofu/internal/command/workdir/plugin_dirs.go
Martin Atkins 65e0c448a0 workdir: Start of a new package for working directory state management
Thus far our various interactions with the bits of state we keep
associated with a working directory have all been implemented directly
inside the "command" package -- often in the huge command.Meta type -- and
not managed collectively via a single component.

There's too many little codepaths reading and writing from the working
directory and data directory to refactor it all in one step, but this is
an attempt at a first step towards a future where everything that reads
and writes from the current working directory would do so via an object
that encapsulates the implementation details and offers a high-level API
to read and write all of these session-persistent settings.

The design here continues our gradual path towards using a dependency
injection style where "package main" is solely responsible for directly
interacting with the OS command line, the OS environment, the OS working
directory, the stdio streams, and the CLI configuration, and then
communicating the resulting information to the rest of Terraform by wiring
together objects. It seems likely that eventually we'll have enough wiring
code in package main to justify a more explicit organization of that code,
but for this commit the new "workdir.Dir" object is just wired directly in
place of its predecessors, without any significant change of code
organization at that top layer.

This first commit focuses on the main files and directories we use to
find provider plugins, because a subsequent commit will lightly reorganize
the separation of concerns for plugin launching with a similar goal of
collecting all of the relevant logic together into one spot.
2021-09-10 14:56:49 -07:00

84 lines
2.5 KiB
Go

package workdir
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
)
const PluginPathFilename = "plugin_path"
// ProviderLocalCacheDir returns the directory we'll use as the
// working-directory-specific local cache of providers.
//
// The provider installer's job is to make sure that all providers needed for
// a particular working directory are available in this cache directory. No
// other component may write here, and in particular a Dir object itself
// never reads or writes into this directory, instead just delegating all of
// that responsibility to other components.
//
// Typically, the caller will ultimately pass the result of this method either
// directly or indirectly into providercache.NewDir, to get an object
// responsible for managing the contents.
func (d *Dir) ProviderLocalCacheDir() string {
return filepath.Join(d.dataDir, "providers")
}
// ForcedPluginDirs returns a list of directories to use to find plugins,
// instead of the default locations.
//
// Returns an zero-length list and no error in the normal case where there
// are no overridden search directories. If ForcedPluginDirs returns a
// non-empty list with no errors then the result totally replaces the default
// search directories.
func (d *Dir) ForcedPluginDirs() ([]string, error) {
raw, err := ioutil.ReadFile(filepath.Join(d.dataDir, PluginPathFilename))
if os.IsNotExist(err) {
return nil, nil
}
if err != nil {
return nil, err
}
var pluginPath []string
if err := json.Unmarshal(raw, &pluginPath); err != nil {
return nil, err
}
return pluginPath, nil
}
// SetForcedPluginDirs records an overridden list of directories to search
// to find plugins, instead of the default locations. See ForcePluginDirs
// for more information.
//
// Pass a zero-length list to deactivate forced plugin directories altogether,
// thus allowing the working directory to return to using the default
// search directories.
func (d *Dir) SetForcedPluginDirs(dirs []string) error {
filePath := filepath.Join(d.dataDir, PluginPathFilename)
switch {
case len(dirs) == 0:
err := os.Remove(filePath)
if !os.IsNotExist(err) {
return err
}
return nil
default:
// We'll ignore errors from this one, because if we fail to create
// the directory then we'll fail to create the file below too,
// and that subsequent error will more directly reflect what we
// are trying to do here.
d.ensureDataDir()
raw, err := json.MarshalIndent(dirs, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(filePath, raw, 0644)
}
}