aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/plugin/discovery/get.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/get.go134
1 files changed, 129 insertions, 5 deletions
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}