github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/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 }