aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/plugin/discovery
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/plugin/discovery')
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/find.go27
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/get.go134
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go48
3 files changed, 202 insertions, 7 deletions
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
index f5bc4c1..f053312 100644
--- a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
@@ -3,6 +3,7 @@ package discovery
3import ( 3import (
4 "io/ioutil" 4 "io/ioutil"
5 "log" 5 "log"
6 "os"
6 "path/filepath" 7 "path/filepath"
7 "strings" 8 "strings"
8) 9)
@@ -59,7 +60,6 @@ func findPluginPaths(kind string, dirs []string) []string {
59 fullName := item.Name() 60 fullName := item.Name()
60 61
61 if !strings.HasPrefix(fullName, prefix) { 62 if !strings.HasPrefix(fullName, prefix) {
62 log.Printf("[DEBUG] skipping %q, not a %s", fullName, kind)
63 continue 63 continue
64 } 64 }
65 65
@@ -71,6 +71,12 @@ func findPluginPaths(kind string, dirs []string) []string {
71 continue 71 continue
72 } 72 }
73 73
74 // Check that the file we found is usable
75 if !pathIsFile(absPath) {
76 log.Printf("[ERROR] ignoring non-file %s", absPath)
77 continue
78 }
79
74 log.Printf("[DEBUG] found %s %q", kind, fullName) 80 log.Printf("[DEBUG] found %s %q", kind, fullName)
75 ret = append(ret, filepath.Clean(absPath)) 81 ret = append(ret, filepath.Clean(absPath))
76 continue 82 continue
@@ -83,7 +89,13 @@ func findPluginPaths(kind string, dirs []string) []string {
83 continue 89 continue
84 } 90 }
85 91
86 log.Printf("[WARNING] found legacy %s %q", kind, fullName) 92 // Check that the file we found is usable
93 if !pathIsFile(absPath) {
94 log.Printf("[ERROR] ignoring non-file %s", absPath)
95 continue
96 }
97
98 log.Printf("[WARN] found legacy %s %q", kind, fullName)
87 99
88 ret = append(ret, filepath.Clean(absPath)) 100 ret = append(ret, filepath.Clean(absPath))
89 } 101 }
@@ -92,6 +104,17 @@ func findPluginPaths(kind string, dirs []string) []string {
92 return ret 104 return ret
93} 105}
94 106
107// Returns true if and only if the given path refers to a file or a symlink
108// to a file.
109func pathIsFile(path string) bool {
110 info, err := os.Stat(path)
111 if err != nil {
112 return false
113 }
114
115 return !info.IsDir()
116}
117
95// ResolvePluginPaths takes a list of paths to plugin executables (as returned 118// ResolvePluginPaths takes a list of paths to plugin executables (as returned
96// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the 119// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the
97// referenced plugins. 120// referenced plugins.
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
index 241b5cb..815640f 100644
--- a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
@@ -3,19 +3,22 @@ package discovery
3import ( 3import (
4 "errors" 4 "errors"
5 "fmt" 5 "fmt"
6 "io"
6 "io/ioutil" 7 "io/ioutil"
7 "log" 8 "log"
8 "net/http" 9 "net/http"
9 "os" 10 "os"
11 "path/filepath"
10 "runtime" 12 "runtime"
11 "strconv" 13 "strconv"
12 "strings" 14 "strings"
13 15
14 "golang.org/x/net/html" 16 "golang.org/x/net/html"
15 17
16 cleanhttp "github.com/hashicorp/go-cleanhttp"
17 getter "github.com/hashicorp/go-getter" 18 getter "github.com/hashicorp/go-getter"
18 multierror "github.com/hashicorp/go-multierror" 19 multierror "github.com/hashicorp/go-multierror"
20 "github.com/hashicorp/terraform/httpclient"
21 "github.com/mitchellh/cli"
19) 22)
20 23
21// Releases are located by parsing the html listing from releases.hashicorp.com. 24// Releases are located by parsing the html listing from releases.hashicorp.com.
@@ -30,7 +33,19 @@ const protocolVersionHeader = "x-terraform-protocol-version"
30 33
31var releaseHost = "https://releases.hashicorp.com" 34var releaseHost = "https://releases.hashicorp.com"
32 35
33var httpClient = cleanhttp.DefaultClient() 36var httpClient *http.Client
37
38func init() {
39 httpClient = httpclient.New()
40
41 httpGetter := &getter.HttpGetter{
42 Client: httpClient,
43 Netrc: true,
44 }
45
46 getter.Getters["http"] = httpGetter
47 getter.Getters["https"] = httpGetter
48}
34 49
35// An Installer maintains a local cache of plugins by downloading plugins 50// An Installer maintains a local cache of plugins by downloading plugins
36// from an online repository. 51// from an online repository.
@@ -47,6 +62,10 @@ type Installer interface {
47type ProviderInstaller struct { 62type ProviderInstaller struct {
48 Dir string 63 Dir string
49 64
65 // Cache is used to access and update a local cache of plugins if non-nil.
66 // Can be nil to disable caching.
67 Cache PluginCache
68
50 PluginProtocolVersion uint 69 PluginProtocolVersion uint
51 70
52 // OS and Arch specify the OS and architecture that should be used when 71 // OS and Arch specify the OS and architecture that should be used when
@@ -58,6 +77,8 @@ type ProviderInstaller struct {
58 77
59 // Skip checksum and signature verification 78 // Skip checksum and signature verification
60 SkipVerify bool 79 SkipVerify bool
80
81 Ui cli.Ui // Ui for output
61} 82}
62 83
63// Get is part of an implementation of type Installer, and attempts to download 84// Get is part of an implementation of type Installer, and attempts to download
@@ -98,6 +119,12 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
98 // sort them newest to oldest 119 // sort them newest to oldest
99 Versions(versions).Sort() 120 Versions(versions).Sort()
100 121
122 // Ensure that our installation directory exists
123 err = os.MkdirAll(i.Dir, os.ModePerm)
124 if err != nil {
125 return PluginMeta{}, fmt.Errorf("failed to create plugin dir %s: %s", i.Dir, err)
126 }
127
101 // take the first matching plugin we find 128 // take the first matching plugin we find
102 for _, v := range versions { 129 for _, v := range versions {
103 url := i.providerURL(provider, v.String()) 130 url := i.providerURL(provider, v.String())
@@ -116,8 +143,9 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
116 143
117 log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v) 144 log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v)
118 if checkPlugin(url, i.PluginProtocolVersion) { 145 if checkPlugin(url, i.PluginProtocolVersion) {
119 log.Printf("[DEBUG] getting provider %q version %q at %s", provider, v, url) 146 i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %q (%s)...", provider, v.String()))
120 err := getter.Get(i.Dir, url) 147 log.Printf("[DEBUG] getting provider %q version %q", provider, v)
148 err := i.install(provider, v, url)
121 if err != nil { 149 if err != nil {
122 return PluginMeta{}, err 150 return PluginMeta{}, err
123 } 151 }
@@ -164,6 +192,98 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
164 return PluginMeta{}, ErrorNoVersionCompatible 192 return PluginMeta{}, ErrorNoVersionCompatible
165} 193}
166 194
195func (i *ProviderInstaller) install(provider string, version Version, url string) error {
196 if i.Cache != nil {
197 log.Printf("[DEBUG] looking for provider %s %s in plugin cache", provider, version)
198 cached := i.Cache.CachedPluginPath("provider", provider, version)
199 if cached == "" {
200 log.Printf("[DEBUG] %s %s not yet in cache, so downloading %s", provider, version, url)
201 err := getter.Get(i.Cache.InstallDir(), url)
202 if err != nil {
203 return err
204 }
205 // should now be in cache
206 cached = i.Cache.CachedPluginPath("provider", provider, version)
207 if cached == "" {
208 // should never happen if the getter is behaving properly
209 // and the plugins are packaged properly.
210 return fmt.Errorf("failed to find downloaded plugin in cache %s", i.Cache.InstallDir())
211 }
212 }
213
214 // Link or copy the cached binary into our install dir so the
215 // normal resolution machinery can find it.
216 filename := filepath.Base(cached)
217 targetPath := filepath.Join(i.Dir, filename)
218
219 log.Printf("[DEBUG] installing %s %s to %s from local cache %s", provider, version, targetPath, cached)
220
221 // Delete if we can. If there's nothing there already then no harm done.
222 // This is important because we can't create a link if there's
223 // already a file of the same name present.
224 // (any other error here we'll catch below when we try to write here)
225 os.Remove(targetPath)
226
227 // We don't attempt linking on Windows because links are not
228 // comprehensively supported by all tools/apps in Windows and
229 // so we choose to be conservative to avoid creating any
230 // weird issues for Windows users.
231 linkErr := errors.New("link not supported for Windows") // placeholder error, never actually returned
232 if runtime.GOOS != "windows" {
233 // Try hard linking first. Hard links are preferable because this
234 // creates a self-contained directory that doesn't depend on the
235 // cache after install.
236 linkErr = os.Link(cached, targetPath)
237
238 // If that failed, try a symlink. This _does_ depend on the cache
239 // after install, so the user must manage the cache more carefully
240 // in this case, but avoids creating redundant copies of the
241 // plugins on disk.
242 if linkErr != nil {
243 linkErr = os.Symlink(cached, targetPath)
244 }
245 }
246
247 // If we still have an error then we'll try a copy as a fallback.
248 // In this case either the OS is Windows or the target filesystem
249 // can't support symlinks.
250 if linkErr != nil {
251 srcFile, err := os.Open(cached)
252 if err != nil {
253 return fmt.Errorf("failed to open cached plugin %s: %s", cached, err)
254 }
255 defer srcFile.Close()
256
257 destFile, err := os.OpenFile(targetPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, os.ModePerm)
258 if err != nil {
259 return fmt.Errorf("failed to create %s: %s", targetPath, err)
260 }
261
262 _, err = io.Copy(destFile, srcFile)
263 if err != nil {
264 destFile.Close()
265 return fmt.Errorf("failed to copy cached plugin from %s to %s: %s", cached, targetPath, err)
266 }
267
268 err = destFile.Close()
269 if err != nil {
270 return fmt.Errorf("error creating %s: %s", targetPath, err)
271 }
272 }
273
274 // One way or another, by the time we get here we should have either
275 // a link or a copy of the cached plugin within i.Dir, as expected.
276 } else {
277 log.Printf("[DEBUG] plugin cache is disabled, so downloading %s %s from %s", provider, version, url)
278 err := getter.Get(i.Dir, url)
279 if err != nil {
280 return err
281 }
282 }
283
284 return nil
285}
286
167func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) { 287func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) {
168 purge := make(PluginMetaSet) 288 purge := make(PluginMetaSet)
169 289
@@ -261,7 +381,7 @@ func checkPlugin(url string, pluginProtocolVersion uint) bool {
261 if proto == "" { 381 if proto == "" {
262 // The header isn't present, but we don't make this error fatal since 382 // The header isn't present, but we don't make this error fatal since
263 // the latest version will probably work. 383 // the latest version will probably work.
264 log.Printf("[WARNING] missing %s from: %s", protocolVersionHeader, url) 384 log.Printf("[WARN] missing %s from: %s", protocolVersionHeader, url)
265 return true 385 return true
266 } 386 }
267 387
@@ -422,3 +542,7 @@ func getFile(url string) ([]byte, error) {
422 } 542 }
423 return data, nil 543 return data, nil
424} 544}
545
546func GetReleaseHost() string {
547 return releaseHost
548}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go
new file mode 100644
index 0000000..1a10042
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go
@@ -0,0 +1,48 @@
1package discovery
2
3// PluginCache is an interface implemented by objects that are able to maintain
4// a cache of plugins.
5type PluginCache interface {
6 // CachedPluginPath returns a path where the requested plugin is already
7 // cached, or an empty string if the requested plugin is not yet cached.
8 CachedPluginPath(kind string, name string, version Version) string
9
10 // InstallDir returns the directory that new plugins should be installed into
11 // in order to populate the cache. This directory should be used as the
12 // first argument to getter.Get when downloading plugins with go-getter.
13 //
14 // After installing into this directory, use CachedPluginPath to obtain the
15 // path where the plugin was installed.
16 InstallDir() string
17}
18
19// NewLocalPluginCache returns a PluginCache that caches plugins in a
20// given local directory.
21func NewLocalPluginCache(dir string) PluginCache {
22 return &pluginCache{
23 Dir: dir,
24 }
25}
26
27type pluginCache struct {
28 Dir string
29}
30
31func (c *pluginCache) CachedPluginPath(kind string, name string, version Version) string {
32 allPlugins := FindPlugins(kind, []string{c.Dir})
33 plugins := allPlugins.WithName(name).WithVersion(version)
34
35 if plugins.Count() == 0 {
36 // nothing cached
37 return ""
38 }
39
40 // There should generally be only one plugin here; if there's more than
41 // one match for some reason then we'll just choose one arbitrarily.
42 plugin := plugins.Newest()
43 return plugin.Path
44}
45
46func (c *pluginCache) InstallDir() string {
47 return c.Dir
48}