opentofu/internal/command/workdir/plugin_dirs.go

87 lines
2.6 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
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-01 19:01:44 -05:00
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)
}
}