github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/command/plugins.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"runtime"
     9  
    10  	plugin "github.com/hashicorp/go-plugin"
    11  	"github.com/kardianos/osext"
    12  
    13  	fileprovisioner "github.com/hashicorp/terraform/internal/builtin/provisioners/file"
    14  	localexec "github.com/hashicorp/terraform/internal/builtin/provisioners/local-exec"
    15  	remoteexec "github.com/hashicorp/terraform/internal/builtin/provisioners/remote-exec"
    16  	"github.com/hashicorp/terraform/internal/logging"
    17  	tfplugin "github.com/hashicorp/terraform/internal/plugin"
    18  	"github.com/hashicorp/terraform/internal/plugin/discovery"
    19  	"github.com/hashicorp/terraform/internal/provisioners"
    20  )
    21  
    22  // NOTE WELL: The logic in this file is primarily about plugin types OTHER THAN
    23  // providers, which use an older set of approaches implemented here.
    24  //
    25  // The provider-related functions live primarily in meta_providers.go, and
    26  // lean on some different underlying mechanisms in order to support automatic
    27  // installation and a hierarchical addressing namespace, neither of which
    28  // are supported for other plugin types.
    29  
    30  // store the user-supplied path for plugin discovery
    31  func (m *Meta) storePluginPath(pluginPath []string) error {
    32  	if len(pluginPath) == 0 {
    33  		return nil
    34  	}
    35  
    36  	m.fixupMissingWorkingDir()
    37  
    38  	// remove the plugin dir record if the path was set to an empty string
    39  	if len(pluginPath) == 1 && (pluginPath[0] == "") {
    40  		return m.WorkingDir.SetForcedPluginDirs(nil)
    41  	}
    42  
    43  	return m.WorkingDir.SetForcedPluginDirs(pluginPath)
    44  }
    45  
    46  // Load the user-defined plugin search path into Meta.pluginPath if the file
    47  // exists.
    48  func (m *Meta) loadPluginPath() ([]string, error) {
    49  	m.fixupMissingWorkingDir()
    50  	return m.WorkingDir.ForcedPluginDirs()
    51  }
    52  
    53  // the default location for automatically installed plugins
    54  func (m *Meta) pluginDir() string {
    55  	return filepath.Join(m.DataDir(), "plugins", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
    56  }
    57  
    58  // pluginDirs return a list of directories to search for plugins.
    59  //
    60  // Earlier entries in this slice get priority over later when multiple copies
    61  // of the same plugin version are found, but newer versions always override
    62  // older versions where both satisfy the provider version constraints.
    63  func (m *Meta) pluginDirs(includeAutoInstalled bool) []string {
    64  	// user defined paths take precedence
    65  	if len(m.pluginPath) > 0 {
    66  		return m.pluginPath
    67  	}
    68  
    69  	// When searching the following directories, earlier entries get precedence
    70  	// if the same plugin version is found twice, but newer versions will
    71  	// always get preference below regardless of where they are coming from.
    72  	// TODO: Add auto-install dir, default vendor dir and optional override
    73  	// vendor dir(s).
    74  	dirs := []string{"."}
    75  
    76  	// Look in the same directory as the Terraform executable.
    77  	// If found, this replaces what we found in the config path.
    78  	exePath, err := osext.Executable()
    79  	if err != nil {
    80  		log.Printf("[ERROR] Error discovering exe directory: %s", err)
    81  	} else {
    82  		dirs = append(dirs, filepath.Dir(exePath))
    83  	}
    84  
    85  	// add the user vendor directory
    86  	dirs = append(dirs, DefaultPluginVendorDir)
    87  
    88  	if includeAutoInstalled {
    89  		dirs = append(dirs, m.pluginDir())
    90  	}
    91  	dirs = append(dirs, m.GlobalPluginDirs...)
    92  
    93  	return dirs
    94  }
    95  
    96  func (m *Meta) provisionerFactories() map[string]provisioners.Factory {
    97  	dirs := m.pluginDirs(true)
    98  	plugins := discovery.FindPlugins("provisioner", dirs)
    99  	plugins, _ = plugins.ValidateVersions()
   100  
   101  	// For now our goal is to just find the latest version of each plugin
   102  	// we have on the system. All provisioners should be at version 0.0.0
   103  	// currently, so there should actually only be one instance of each plugin
   104  	// name here, even though the discovery interface forces us to pretend
   105  	// that might not be true.
   106  
   107  	factories := make(map[string]provisioners.Factory)
   108  
   109  	// Wire up the internal provisioners first. These might be overridden
   110  	// by discovered provisioners below.
   111  	for name, factory := range internalProvisionerFactories() {
   112  		factories[name] = factory
   113  	}
   114  
   115  	byName := plugins.ByName()
   116  	for name, metas := range byName {
   117  		// Since we validated versions above and we partitioned the sets
   118  		// by name, we're guaranteed that the metas in our set all have
   119  		// valid versions and that there's at least one meta.
   120  		newest := metas.Newest()
   121  
   122  		factories[name] = provisionerFactory(newest)
   123  	}
   124  
   125  	return factories
   126  }
   127  
   128  func provisionerFactory(meta discovery.PluginMeta) provisioners.Factory {
   129  	return func() (provisioners.Interface, error) {
   130  		cfg := &plugin.ClientConfig{
   131  			Cmd:              exec.Command(meta.Path),
   132  			HandshakeConfig:  tfplugin.Handshake,
   133  			VersionedPlugins: tfplugin.VersionedPlugins,
   134  			Managed:          true,
   135  			Logger:           logging.NewLogger("provisioner"),
   136  			AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
   137  			AutoMTLS:         enableProviderAutoMTLS,
   138  			SyncStdout:       logging.PluginOutputMonitor(fmt.Sprintf("%s:stdout", meta.Name)),
   139  			SyncStderr:       logging.PluginOutputMonitor(fmt.Sprintf("%s:stderr", meta.Name)),
   140  		}
   141  		client := plugin.NewClient(cfg)
   142  		return newProvisionerClient(client)
   143  	}
   144  }
   145  
   146  func internalProvisionerFactories() map[string]provisioners.Factory {
   147  	return map[string]provisioners.Factory{
   148  		"file":        provisioners.FactoryFixed(fileprovisioner.New()),
   149  		"local-exec":  provisioners.FactoryFixed(localexec.New()),
   150  		"remote-exec": provisioners.FactoryFixed(remoteexec.New()),
   151  	}
   152  }
   153  
   154  func newProvisionerClient(client *plugin.Client) (provisioners.Interface, error) {
   155  	// Request the RPC client so we can get the provisioner
   156  	// so we can build the actual RPC-implemented provisioner.
   157  	rpcClient, err := client.Client()
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	raw, err := rpcClient.Dispense(tfplugin.ProvisionerPluginName)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	// store the client so that the plugin can kill the child process
   168  	p := raw.(*tfplugin.GRPCProvisioner)
   169  	p.PluginClient = client
   170  	return p, nil
   171  }