aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/plugin/discovery
diff options
context:
space:
mode:
authorRadek Simko <radek.simko@gmail.com>2017-08-10 14:38:14 +0200
committerRadek Simko <radek.simko@gmail.com>2017-08-10 14:38:14 +0200
commitc680a8e1622ed0f18751d9d167c836ee24f5e897 (patch)
tree864f925049d422033dd25a73bafce32b361c8827 /vendor/github.com/hashicorp/terraform/plugin/discovery
parent38f8880ac81bfabc6d7f82e4dc89661f20fc559e (diff)
downloadterraform-provider-statuscake-c680a8e1622ed0f18751d9d167c836ee24f5e897.tar.gz
terraform-provider-statuscake-c680a8e1622ed0f18751d9d167c836ee24f5e897.tar.zst
terraform-provider-statuscake-c680a8e1622ed0f18751d9d167c836ee24f5e897.zip
vendor: github.com/hashicorp/terraform/...@v0.10.0
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/plugin/discovery')
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/error.go30
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/find.go168
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/get.go424
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/meta.go41
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go195
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/requirements.go105
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/signature.go53
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/version.go72
-rw-r--r--vendor/github.com/hashicorp/terraform/plugin/discovery/version_set.go84
9 files changed, 1172 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/error.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/error.go
new file mode 100644
index 0000000..df855a7
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/error.go
@@ -0,0 +1,30 @@
1package discovery
2
3// Error is a type used to describe situations that the caller must handle
4// since they indicate some form of user error.
5//
6// The functions and methods that return these specialized errors indicate so
7// in their documentation. The Error type should not itself be used directly,
8// but rather errors should be compared using the == operator with the
9// error constants in this package.
10//
11// Values of this type are _not_ used when the error being reported is an
12// operational error (server unavailable, etc) or indicative of a bug in
13// this package or its caller.
14type Error string
15
16// ErrorNoSuitableVersion indicates that a suitable version (meeting given
17// constraints) is not available.
18const ErrorNoSuitableVersion = Error("no suitable version is available")
19
20// ErrorNoVersionCompatible indicates that all of the available versions
21// that otherwise met constraints are not compatible with the current
22// version of Terraform.
23const ErrorNoVersionCompatible = Error("no available version is compatible with this version of Terraform")
24
25// ErrorNoSuchProvider indicates that no provider exists with a name given
26const ErrorNoSuchProvider = Error("no provider exists with the given name")
27
28func (err Error) Error() string {
29 return string(err)
30}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
new file mode 100644
index 0000000..f5bc4c1
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
@@ -0,0 +1,168 @@
1package discovery
2
3import (
4 "io/ioutil"
5 "log"
6 "path/filepath"
7 "strings"
8)
9
10// FindPlugins looks in the given directories for files whose filenames
11// suggest that they are plugins of the given kind (e.g. "provider") and
12// returns a PluginMetaSet representing the discovered potential-plugins.
13//
14// Currently this supports two different naming schemes. The current
15// standard naming scheme is a subdirectory called $GOOS-$GOARCH containing
16// files named terraform-$KIND-$NAME-V$VERSION. The legacy naming scheme is
17// files directly in the given directory whose names are like
18// terraform-$KIND-$NAME.
19//
20// Only one plugin will be returned for each unique plugin (name, version)
21// pair, with preference given to files found in earlier directories.
22//
23// This is a convenience wrapper around FindPluginPaths and ResolvePluginsPaths.
24func FindPlugins(kind string, dirs []string) PluginMetaSet {
25 return ResolvePluginPaths(FindPluginPaths(kind, dirs))
26}
27
28// FindPluginPaths looks in the given directories for files whose filenames
29// suggest that they are plugins of the given kind (e.g. "provider").
30//
31// The return value is a list of absolute paths that appear to refer to
32// plugins in the given directories, based only on what can be inferred
33// from the naming scheme. The paths returned are ordered such that files
34// in later dirs appear after files in earlier dirs in the given directory
35// list. Within the same directory plugins are returned in a consistent but
36// undefined order.
37func FindPluginPaths(kind string, dirs []string) []string {
38 // This is just a thin wrapper around findPluginPaths so that we can
39 // use the latter in tests with a fake machineName so we can use our
40 // test fixtures.
41 return findPluginPaths(kind, dirs)
42}
43
44func findPluginPaths(kind string, dirs []string) []string {
45 prefix := "terraform-" + kind + "-"
46
47 ret := make([]string, 0, len(dirs))
48
49 for _, dir := range dirs {
50 items, err := ioutil.ReadDir(dir)
51 if err != nil {
52 // Ignore missing dirs, non-dirs, etc
53 continue
54 }
55
56 log.Printf("[DEBUG] checking for %s in %q", kind, dir)
57
58 for _, item := range items {
59 fullName := item.Name()
60
61 if !strings.HasPrefix(fullName, prefix) {
62 log.Printf("[DEBUG] skipping %q, not a %s", fullName, kind)
63 continue
64 }
65
66 // New-style paths must have a version segment in filename
67 if strings.Contains(strings.ToLower(fullName), "_v") {
68 absPath, err := filepath.Abs(filepath.Join(dir, fullName))
69 if err != nil {
70 log.Printf("[ERROR] plugin filepath error: %s", err)
71 continue
72 }
73
74 log.Printf("[DEBUG] found %s %q", kind, fullName)
75 ret = append(ret, filepath.Clean(absPath))
76 continue
77 }
78
79 // Legacy style with files directly in the base directory
80 absPath, err := filepath.Abs(filepath.Join(dir, fullName))
81 if err != nil {
82 log.Printf("[ERROR] plugin filepath error: %s", err)
83 continue
84 }
85
86 log.Printf("[WARNING] found legacy %s %q", kind, fullName)
87
88 ret = append(ret, filepath.Clean(absPath))
89 }
90 }
91
92 return ret
93}
94
95// ResolvePluginPaths takes a list of paths to plugin executables (as returned
96// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the
97// referenced plugins.
98//
99// If the same combination of plugin name and version appears multiple times,
100// the earlier reference will be preferred. Several different versions of
101// the same plugin name may be returned, in which case the methods of
102// PluginMetaSet can be used to filter down.
103func ResolvePluginPaths(paths []string) PluginMetaSet {
104 s := make(PluginMetaSet)
105
106 type nameVersion struct {
107 Name string
108 Version string
109 }
110 found := make(map[nameVersion]struct{})
111
112 for _, path := range paths {
113 baseName := strings.ToLower(filepath.Base(path))
114 if !strings.HasPrefix(baseName, "terraform-") {
115 // Should never happen with reasonable input
116 continue
117 }
118
119 baseName = baseName[10:]
120 firstDash := strings.Index(baseName, "-")
121 if firstDash == -1 {
122 // Should never happen with reasonable input
123 continue
124 }
125
126 baseName = baseName[firstDash+1:]
127 if baseName == "" {
128 // Should never happen with reasonable input
129 continue
130 }
131
132 // Trim the .exe suffix used on Windows before we start wrangling
133 // the remainder of the path.
134 if strings.HasSuffix(baseName, ".exe") {
135 baseName = baseName[:len(baseName)-4]
136 }
137
138 parts := strings.SplitN(baseName, "_v", 2)
139 name := parts[0]
140 version := VersionZero
141 if len(parts) == 2 {
142 version = parts[1]
143 }
144
145 // Auto-installed plugins contain an extra name portion representing
146 // the expected plugin version, which we must trim off.
147 if underX := strings.Index(version, "_x"); underX != -1 {
148 version = version[:underX]
149 }
150
151 if _, ok := found[nameVersion{name, version}]; ok {
152 // Skip duplicate versions of the same plugin
153 // (We do this during this step because after this we will be
154 // dealing with sets and thus lose our ordering with which to
155 // decide preference.)
156 continue
157 }
158
159 s.Add(PluginMeta{
160 Name: name,
161 Version: VersionStr(version),
162 Path: path,
163 })
164 found[nameVersion{name, version}] = struct{}{}
165 }
166
167 return s
168}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
new file mode 100644
index 0000000..241b5cb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
@@ -0,0 +1,424 @@
1package discovery
2
3import (
4 "errors"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "net/http"
9 "os"
10 "runtime"
11 "strconv"
12 "strings"
13
14 "golang.org/x/net/html"
15
16 cleanhttp "github.com/hashicorp/go-cleanhttp"
17 getter "github.com/hashicorp/go-getter"
18 multierror "github.com/hashicorp/go-multierror"
19)
20
21// Releases are located by parsing the html listing from releases.hashicorp.com.
22//
23// The URL for releases follows the pattern:
24// https://releases.hashicorp.com/terraform-provider-name/<x.y.z>/terraform-provider-name_<x.y.z>_<os>_<arch>.<ext>
25//
26// The plugin protocol version will be saved with the release and returned in
27// the header X-TERRAFORM_PROTOCOL_VERSION.
28
29const protocolVersionHeader = "x-terraform-protocol-version"
30
31var releaseHost = "https://releases.hashicorp.com"
32
33var httpClient = cleanhttp.DefaultClient()
34
35// An Installer maintains a local cache of plugins by downloading plugins
36// from an online repository.
37type Installer interface {
38 Get(name string, req Constraints) (PluginMeta, error)
39 PurgeUnused(used map[string]PluginMeta) (removed PluginMetaSet, err error)
40}
41
42// ProviderInstaller is an Installer implementation that knows how to
43// download Terraform providers from the official HashiCorp releases service
44// into a local directory. The files downloaded are compliant with the
45// naming scheme expected by FindPlugins, so the target directory of a
46// provider installer can be used as one of several plugin discovery sources.
47type ProviderInstaller struct {
48 Dir string
49
50 PluginProtocolVersion uint
51
52 // OS and Arch specify the OS and architecture that should be used when
53 // installing plugins. These use the same labels as the runtime.GOOS and
54 // runtime.GOARCH variables respectively, and indeed the values of these
55 // are used as defaults if either of these is the empty string.
56 OS string
57 Arch string
58
59 // Skip checksum and signature verification
60 SkipVerify bool
61}
62
63// Get is part of an implementation of type Installer, and attempts to download
64// and install a Terraform provider matching the given constraints.
65//
66// This method may return one of a number of sentinel errors from this
67// package to indicate issues that are likely to be resolvable via user action:
68//
69// ErrorNoSuchProvider: no provider with the given name exists in the repository.
70// ErrorNoSuitableVersion: the provider exists but no available version matches constraints.
71// ErrorNoVersionCompatible: a plugin was found within the constraints but it is
72// incompatible with the current Terraform version.
73//
74// These errors should be recognized and handled as special cases by the caller
75// to present a suitable user-oriented error message.
76//
77// All other errors indicate an internal problem that is likely _not_ solvable
78// through user action, or at least not within Terraform's scope. Error messages
79// are produced under the assumption that if presented to the user they will
80// be presented alongside context about what is being installed, and thus the
81// error messages do not redundantly include such information.
82func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, error) {
83 versions, err := i.listProviderVersions(provider)
84 // TODO: return multiple errors
85 if err != nil {
86 return PluginMeta{}, err
87 }
88
89 if len(versions) == 0 {
90 return PluginMeta{}, ErrorNoSuitableVersion
91 }
92
93 versions = allowedVersions(versions, req)
94 if len(versions) == 0 {
95 return PluginMeta{}, ErrorNoSuitableVersion
96 }
97
98 // sort them newest to oldest
99 Versions(versions).Sort()
100
101 // take the first matching plugin we find
102 for _, v := range versions {
103 url := i.providerURL(provider, v.String())
104
105 if !i.SkipVerify {
106 sha256, err := i.getProviderChecksum(provider, v.String())
107 if err != nil {
108 return PluginMeta{}, err
109 }
110
111 // add the checksum parameter for go-getter to verify the download for us.
112 if sha256 != "" {
113 url = url + "?checksum=sha256:" + sha256
114 }
115 }
116
117 log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v)
118 if checkPlugin(url, i.PluginProtocolVersion) {
119 log.Printf("[DEBUG] getting provider %q version %q at %s", provider, v, url)
120 err := getter.Get(i.Dir, url)
121 if err != nil {
122 return PluginMeta{}, err
123 }
124
125 // Find what we just installed
126 // (This is weird, because go-getter doesn't directly return
127 // information about what was extracted, and we just extracted
128 // the archive directly into a shared dir here.)
129 log.Printf("[DEBUG] looking for the %s %s plugin we just installed", provider, v)
130 metas := FindPlugins("provider", []string{i.Dir})
131 log.Printf("[DEBUG] all plugins found %#v", metas)
132 metas, _ = metas.ValidateVersions()
133 metas = metas.WithName(provider).WithVersion(v)
134 log.Printf("[DEBUG] filtered plugins %#v", metas)
135 if metas.Count() == 0 {
136 // This should never happen. Suggests that the release archive
137 // contains an executable file whose name doesn't match the
138 // expected convention.
139 return PluginMeta{}, fmt.Errorf(
140 "failed to find installed plugin version %s; this is a bug in Terraform and should be reported",
141 v,
142 )
143 }
144
145 if metas.Count() > 1 {
146 // This should also never happen, and suggests that a
147 // particular version was re-released with a different
148 // executable filename. We consider releases as immutable, so
149 // this is an error.
150 return PluginMeta{}, fmt.Errorf(
151 "multiple plugins installed for version %s; this is a bug in Terraform and should be reported",
152 v,
153 )
154 }
155
156 // By now we know we have exactly one meta, and so "Newest" will
157 // return that one.
158 return metas.Newest(), nil
159 }
160
161 log.Printf("[INFO] incompatible ProtocolVersion for %s version %s", provider, v)
162 }
163
164 return PluginMeta{}, ErrorNoVersionCompatible
165}
166
167func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) {
168 purge := make(PluginMetaSet)
169
170 present := FindPlugins("provider", []string{i.Dir})
171 for meta := range present {
172 chosen, ok := used[meta.Name]
173 if !ok {
174 purge.Add(meta)
175 }
176 if chosen.Path != meta.Path {
177 purge.Add(meta)
178 }
179 }
180
181 removed := make(PluginMetaSet)
182 var errs error
183 for meta := range purge {
184 path := meta.Path
185 err := os.Remove(path)
186 if err != nil {
187 errs = multierror.Append(errs, fmt.Errorf(
188 "failed to remove unused provider plugin %s: %s",
189 path, err,
190 ))
191 } else {
192 removed.Add(meta)
193 }
194 }
195
196 return removed, errs
197}
198
199// Plugins are referred to by the short name, but all URLs and files will use
200// the full name prefixed with terraform-<plugin_type>-
201func (i *ProviderInstaller) providerName(name string) string {
202 return "terraform-provider-" + name
203}
204
205func (i *ProviderInstaller) providerFileName(name, version string) string {
206 os := i.OS
207 arch := i.Arch
208 if os == "" {
209 os = runtime.GOOS
210 }
211 if arch == "" {
212 arch = runtime.GOARCH
213 }
214 return fmt.Sprintf("%s_%s_%s_%s.zip", i.providerName(name), version, os, arch)
215}
216
217// providerVersionsURL returns the path to the released versions directory for the provider:
218// https://releases.hashicorp.com/terraform-provider-name/
219func (i *ProviderInstaller) providerVersionsURL(name string) string {
220 return releaseHost + "/" + i.providerName(name) + "/"
221}
222
223// providerURL returns the full path to the provider file, using the current OS
224// and ARCH:
225// .../terraform-provider-name_<x.y.z>/terraform-provider-name_<x.y.z>_<os>_<arch>.<ext>
226func (i *ProviderInstaller) providerURL(name, version string) string {
227 return fmt.Sprintf("%s%s/%s", i.providerVersionsURL(name), version, i.providerFileName(name, version))
228}
229
230func (i *ProviderInstaller) providerChecksumURL(name, version string) string {
231 fileName := fmt.Sprintf("%s_%s_SHA256SUMS", i.providerName(name), version)
232 u := fmt.Sprintf("%s%s/%s", i.providerVersionsURL(name), version, fileName)
233 return u
234}
235
236func (i *ProviderInstaller) getProviderChecksum(name, version string) (string, error) {
237 checksums, err := getPluginSHA256SUMs(i.providerChecksumURL(name, version))
238 if err != nil {
239 return "", err
240 }
241
242 return checksumForFile(checksums, i.providerFileName(name, version)), nil
243}
244
245// Return the plugin version by making a HEAD request to the provided url.
246// If the header is not present, we assume the latest version will be
247// compatible, and leave the check for discovery or execution.
248func checkPlugin(url string, pluginProtocolVersion uint) bool {
249 resp, err := httpClient.Head(url)
250 if err != nil {
251 log.Printf("[ERROR] error fetching plugin headers: %s", err)
252 return false
253 }
254
255 if resp.StatusCode != http.StatusOK {
256 log.Println("[ERROR] non-200 status fetching plugin headers:", resp.Status)
257 return false
258 }
259
260 proto := resp.Header.Get(protocolVersionHeader)
261 if proto == "" {
262 // The header isn't present, but we don't make this error fatal since
263 // the latest version will probably work.
264 log.Printf("[WARNING] missing %s from: %s", protocolVersionHeader, url)
265 return true
266 }
267
268 protoVersion, err := strconv.Atoi(proto)
269 if err != nil {
270 log.Printf("[ERROR] invalid ProtocolVersion: %s", proto)
271 return false
272 }
273
274 return protoVersion == int(pluginProtocolVersion)
275}
276
277// list the version available for the named plugin
278func (i *ProviderInstaller) listProviderVersions(name string) ([]Version, error) {
279 versions, err := listPluginVersions(i.providerVersionsURL(name))
280 if err != nil {
281 // listPluginVersions returns a verbose error message indicating
282 // what was being accessed and what failed
283 return nil, err
284 }
285 return versions, nil
286}
287
288var errVersionNotFound = errors.New("version not found")
289
290// take the list of available versions for a plugin, and filter out those that
291// don't fit the constraints.
292func allowedVersions(available []Version, required Constraints) []Version {
293 var allowed []Version
294
295 for _, v := range available {
296 if required.Allows(v) {
297 allowed = append(allowed, v)
298 }
299 }
300
301 return allowed
302}
303
304// return a list of the plugin versions at the given URL
305func listPluginVersions(url string) ([]Version, error) {
306 resp, err := httpClient.Get(url)
307 if err != nil {
308 // http library produces a verbose error message that includes the
309 // URL being accessed, etc.
310 return nil, err
311 }
312 defer resp.Body.Close()
313
314 if resp.StatusCode != http.StatusOK {
315 body, _ := ioutil.ReadAll(resp.Body)
316 log.Printf("[ERROR] failed to fetch plugin versions from %s\n%s\n%s", url, resp.Status, body)
317
318 switch resp.StatusCode {
319 case http.StatusNotFound, http.StatusForbidden:
320 // These are treated as indicative of the given name not being
321 // a valid provider name at all.
322 return nil, ErrorNoSuchProvider
323
324 default:
325 // All other errors are assumed to be operational problems.
326 return nil, fmt.Errorf("error accessing %s: %s", url, resp.Status)
327 }
328
329 }
330
331 body, err := html.Parse(resp.Body)
332 if err != nil {
333 log.Fatal(err)
334 }
335
336 names := []string{}
337
338 // all we need to do is list links on the directory listing page that look like plugins
339 var f func(*html.Node)
340 f = func(n *html.Node) {
341 if n.Type == html.ElementNode && n.Data == "a" {
342 c := n.FirstChild
343 if c != nil && c.Type == html.TextNode && strings.HasPrefix(c.Data, "terraform-") {
344 names = append(names, c.Data)
345 return
346 }
347 }
348 for c := n.FirstChild; c != nil; c = c.NextSibling {
349 f(c)
350 }
351 }
352 f(body)
353
354 return versionsFromNames(names), nil
355}
356
357// parse the list of directory names into a sorted list of available versions
358func versionsFromNames(names []string) []Version {
359 var versions []Version
360 for _, name := range names {
361 parts := strings.SplitN(name, "_", 2)
362 if len(parts) == 2 && parts[1] != "" {
363 v, err := VersionStr(parts[1]).Parse()
364 if err != nil {
365 // filter invalid versions scraped from the page
366 log.Printf("[WARN] invalid version found for %q: %s", name, err)
367 continue
368 }
369
370 versions = append(versions, v)
371 }
372 }
373
374 return versions
375}
376
377func checksumForFile(sums []byte, name string) string {
378 for _, line := range strings.Split(string(sums), "\n") {
379 parts := strings.Fields(line)
380 if len(parts) > 1 && parts[1] == name {
381 return parts[0]
382 }
383 }
384 return ""
385}
386
387// fetch the SHA256SUMS file provided, and verify its signature.
388func getPluginSHA256SUMs(sumsURL string) ([]byte, error) {
389 sigURL := sumsURL + ".sig"
390
391 sums, err := getFile(sumsURL)
392 if err != nil {
393 return nil, fmt.Errorf("error fetching checksums: %s", err)
394 }
395
396 sig, err := getFile(sigURL)
397 if err != nil {
398 return nil, fmt.Errorf("error fetching checksums signature: %s", err)
399 }
400
401 if err := verifySig(sums, sig); err != nil {
402 return nil, err
403 }
404
405 return sums, nil
406}
407
408func getFile(url string) ([]byte, error) {
409 resp, err := httpClient.Get(url)
410 if err != nil {
411 return nil, err
412 }
413 defer resp.Body.Close()
414
415 if resp.StatusCode != http.StatusOK {
416 return nil, fmt.Errorf("%s", resp.Status)
417 }
418
419 data, err := ioutil.ReadAll(resp.Body)
420 if err != nil {
421 return data, err
422 }
423 return data, nil
424}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/meta.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/meta.go
new file mode 100644
index 0000000..bdcebcb
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/meta.go
@@ -0,0 +1,41 @@
1package discovery
2
3import (
4 "crypto/sha256"
5 "io"
6 "os"
7)
8
9// PluginMeta is metadata about a plugin, useful for launching the plugin
10// and for understanding which plugins are available.
11type PluginMeta struct {
12 // Name is the name of the plugin, e.g. as inferred from the plugin
13 // binary's filename, or by explicit configuration.
14 Name string
15
16 // Version is the semver version of the plugin, expressed as a string
17 // that might not be semver-valid.
18 Version VersionStr
19
20 // Path is the absolute path of the executable that can be launched
21 // to provide the RPC server for this plugin.
22 Path string
23}
24
25// SHA256 returns a SHA256 hash of the content of the referenced executable
26// file, or an error if the file's contents cannot be read.
27func (m PluginMeta) SHA256() ([]byte, error) {
28 f, err := os.Open(m.Path)
29 if err != nil {
30 return nil, err
31 }
32 defer f.Close()
33
34 h := sha256.New()
35 _, err = io.Copy(h, f)
36 if err != nil {
37 return nil, err
38 }
39
40 return h.Sum(nil), nil
41}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go
new file mode 100644
index 0000000..181ea1f
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go
@@ -0,0 +1,195 @@
1package discovery
2
3// A PluginMetaSet is a set of PluginMeta objects meeting a certain criteria.
4//
5// Methods on this type allow filtering of the set to produce subsets that
6// meet more restrictive criteria.
7type PluginMetaSet map[PluginMeta]struct{}
8
9// Add inserts the given PluginMeta into the receiving set. This is a no-op
10// if the given meta is already present.
11func (s PluginMetaSet) Add(p PluginMeta) {
12 s[p] = struct{}{}
13}
14
15// Remove removes the given PluginMeta from the receiving set. This is a no-op
16// if the given meta is not already present.
17func (s PluginMetaSet) Remove(p PluginMeta) {
18 delete(s, p)
19}
20
21// Has returns true if the given meta is in the receiving set, or false
22// otherwise.
23func (s PluginMetaSet) Has(p PluginMeta) bool {
24 _, ok := s[p]
25 return ok
26}
27
28// Count returns the number of metas in the set
29func (s PluginMetaSet) Count() int {
30 return len(s)
31}
32
33// ValidateVersions returns two new PluginMetaSets, separating those with
34// versions that have syntax-valid semver versions from those that don't.
35//
36// Eliminating invalid versions from consideration (and possibly warning about
37// them) is usually the first step of working with a meta set after discovery
38// has completed.
39func (s PluginMetaSet) ValidateVersions() (valid, invalid PluginMetaSet) {
40 valid = make(PluginMetaSet)
41 invalid = make(PluginMetaSet)
42 for p := range s {
43 if _, err := p.Version.Parse(); err == nil {
44 valid.Add(p)
45 } else {
46 invalid.Add(p)
47 }
48 }
49 return
50}
51
52// WithName returns the subset of metas that have the given name.
53func (s PluginMetaSet) WithName(name string) PluginMetaSet {
54 ns := make(PluginMetaSet)
55 for p := range s {
56 if p.Name == name {
57 ns.Add(p)
58 }
59 }
60 return ns
61}
62
63// WithVersion returns the subset of metas that have the given version.
64//
65// This should be used only with the "valid" result from ValidateVersions;
66// it will ignore any plugin metas that have a invalid version strings.
67func (s PluginMetaSet) WithVersion(version Version) PluginMetaSet {
68 ns := make(PluginMetaSet)
69 for p := range s {
70 gotVersion, err := p.Version.Parse()
71 if err != nil {
72 continue
73 }
74 if gotVersion.Equal(version) {
75 ns.Add(p)
76 }
77 }
78 return ns
79}
80
81// ByName groups the metas in the set by their Names, returning a map.
82func (s PluginMetaSet) ByName() map[string]PluginMetaSet {
83 ret := make(map[string]PluginMetaSet)
84 for p := range s {
85 if _, ok := ret[p.Name]; !ok {
86 ret[p.Name] = make(PluginMetaSet)
87 }
88 ret[p.Name].Add(p)
89 }
90 return ret
91}
92
93// Newest returns the one item from the set that has the newest Version value.
94//
95// The result is meaningful only if the set is already filtered such that
96// all of the metas have the same Name.
97//
98// If there isn't at least one meta in the set then this function will panic.
99// Use Count() to ensure that there is at least one value before calling.
100//
101// If any of the metas have invalid version strings then this function will
102// panic. Use ValidateVersions() first to filter out metas with invalid
103// versions.
104//
105// If two metas have the same Version then one is arbitrarily chosen. This
106// situation should be avoided by pre-filtering the set.
107func (s PluginMetaSet) Newest() PluginMeta {
108 if len(s) == 0 {
109 panic("can't call NewestStable on empty PluginMetaSet")
110 }
111
112 var first = true
113 var winner PluginMeta
114 var winnerVersion Version
115 for p := range s {
116 version, err := p.Version.Parse()
117 if err != nil {
118 panic(err)
119 }
120
121 if first == true || version.NewerThan(winnerVersion) {
122 winner = p
123 winnerVersion = version
124 first = false
125 }
126 }
127
128 return winner
129}
130
131// ConstrainVersions takes a set of requirements and attempts to
132// return a map from name to a set of metas that have the matching
133// name and an appropriate version.
134//
135// If any of the given requirements match *no* plugins then its PluginMetaSet
136// in the returned map will be empty.
137//
138// All viable metas are returned, so the caller can apply any desired filtering
139// to reduce down to a single option. For example, calling Newest() to obtain
140// the highest available version.
141//
142// If any of the metas in the set have invalid version strings then this
143// function will panic. Use ValidateVersions() first to filter out metas with
144// invalid versions.
145func (s PluginMetaSet) ConstrainVersions(reqd PluginRequirements) map[string]PluginMetaSet {
146 ret := make(map[string]PluginMetaSet)
147 for p := range s {
148 name := p.Name
149 allowedVersions, ok := reqd[name]
150 if !ok {
151 continue
152 }
153 if _, ok := ret[p.Name]; !ok {
154 ret[p.Name] = make(PluginMetaSet)
155 }
156 version, err := p.Version.Parse()
157 if err != nil {
158 panic(err)
159 }
160 if allowedVersions.Allows(version) {
161 ret[p.Name].Add(p)
162 }
163 }
164 return ret
165}
166
167// OverridePaths returns a new set where any existing plugins with the given
168// names are removed and replaced with the single path given in the map.
169//
170// This is here only to continue to support the legacy way of overriding
171// plugin binaries in the .terraformrc file. It treats all given plugins
172// as pre-versioning (version 0.0.0). This mechanism will eventually be
173// phased out, with vendor directories being the intended replacement.
174func (s PluginMetaSet) OverridePaths(paths map[string]string) PluginMetaSet {
175 ret := make(PluginMetaSet)
176 for p := range s {
177 if _, ok := paths[p.Name]; ok {
178 // Skip plugins that we're overridding
179 continue
180 }
181
182 ret.Add(p)
183 }
184
185 // Now add the metadata for overriding plugins
186 for name, path := range paths {
187 ret.Add(PluginMeta{
188 Name: name,
189 Version: VersionZero,
190 Path: path,
191 })
192 }
193
194 return ret
195}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/requirements.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/requirements.go
new file mode 100644
index 0000000..75430fd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/requirements.go
@@ -0,0 +1,105 @@
1package discovery
2
3import (
4 "bytes"
5)
6
7// PluginRequirements describes a set of plugins (assumed to be of a consistent
8// kind) that are required to exist and have versions within the given
9// corresponding sets.
10type PluginRequirements map[string]*PluginConstraints
11
12// PluginConstraints represents an element of PluginRequirements describing
13// the constraints for a single plugin.
14type PluginConstraints struct {
15 // Specifies that the plugin's version must be within the given
16 // constraints.
17 Versions Constraints
18
19 // If non-nil, the hash of the on-disk plugin executable must exactly
20 // match the SHA256 hash given here.
21 SHA256 []byte
22}
23
24// Allows returns true if the given version is within the receiver's version
25// constraints.
26func (s *PluginConstraints) Allows(v Version) bool {
27 return s.Versions.Allows(v)
28}
29
30// AcceptsSHA256 returns true if the given executable SHA256 hash is acceptable,
31// either because it matches the constraint or because there is no such
32// constraint.
33func (s *PluginConstraints) AcceptsSHA256(digest []byte) bool {
34 if s.SHA256 == nil {
35 return true
36 }
37 return bytes.Equal(s.SHA256, digest)
38}
39
40// Merge takes the contents of the receiver and the other given requirements
41// object and merges them together into a single requirements structure
42// that satisfies both sets of requirements.
43//
44// Note that it doesn't make sense to merge two PluginRequirements with
45// differing required plugin SHA256 hashes, since the result will never
46// match any plugin.
47func (r PluginRequirements) Merge(other PluginRequirements) PluginRequirements {
48 ret := make(PluginRequirements)
49 for n, c := range r {
50 ret[n] = &PluginConstraints{
51 Versions: Constraints{}.Append(c.Versions),
52 SHA256: c.SHA256,
53 }
54 }
55 for n, c := range other {
56 if existing, exists := ret[n]; exists {
57 ret[n].Versions = ret[n].Versions.Append(c.Versions)
58
59 if existing.SHA256 != nil {
60 if c.SHA256 != nil && !bytes.Equal(c.SHA256, existing.SHA256) {
61 // If we've been asked to merge two constraints with
62 // different SHA256 hashes then we'll produce a dummy value
63 // that can never match anything. This is a silly edge case
64 // that no reasonable caller should hit.
65 ret[n].SHA256 = []byte(invalidProviderHash)
66 }
67 } else {
68 ret[n].SHA256 = c.SHA256 // might still be nil
69 }
70 } else {
71 ret[n] = &PluginConstraints{
72 Versions: Constraints{}.Append(c.Versions),
73 SHA256: c.SHA256,
74 }
75 }
76 }
77 return ret
78}
79
80// LockExecutables applies additional constraints to the receiver that
81// require plugin executables with specific SHA256 digests. This modifies
82// the receiver in-place, since it's intended to be applied after
83// version constraints have been resolved.
84//
85// The given map must include a key for every plugin that is already
86// required. If not, any missing keys will cause the corresponding plugin
87// to never match, though the direct caller doesn't necessarily need to
88// guarantee this as long as the downstream code _applying_ these constraints
89// is able to deal with the non-match in some way.
90func (r PluginRequirements) LockExecutables(sha256s map[string][]byte) {
91 for name, cons := range r {
92 digest := sha256s[name]
93
94 if digest == nil {
95 // Prevent any match, which will then presumably cause the
96 // downstream consumer of this requirements to report an error.
97 cons.SHA256 = []byte(invalidProviderHash)
98 continue
99 }
100
101 cons.SHA256 = digest
102 }
103}
104
105const invalidProviderHash = "<invalid>"
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/signature.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/signature.go
new file mode 100644
index 0000000..b6686a5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/signature.go
@@ -0,0 +1,53 @@
1package discovery
2
3import (
4 "bytes"
5 "log"
6 "strings"
7
8 "golang.org/x/crypto/openpgp"
9)
10
11// Verify the data using the provided openpgp detached signature and the
12// embedded hashicorp public key.
13func verifySig(data, sig []byte) error {
14 el, err := openpgp.ReadArmoredKeyRing(strings.NewReader(hashiPublicKey))
15 if err != nil {
16 log.Fatal(err)
17 }
18
19 _, err = openpgp.CheckDetachedSignature(el, bytes.NewReader(data), bytes.NewReader(sig))
20 return err
21}
22
23// this is the public key that signs the checksums file for releases.
24const hashiPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
25Version: GnuPG v1
26
27mQENBFMORM0BCADBRyKO1MhCirazOSVwcfTr1xUxjPvfxD3hjUwHtjsOy/bT6p9f
28W2mRPfwnq2JB5As+paL3UGDsSRDnK9KAxQb0NNF4+eVhr/EJ18s3wwXXDMjpIifq
29fIm2WyH3G+aRLTLPIpscUNKDyxFOUbsmgXAmJ46Re1fn8uKxKRHbfa39aeuEYWFA
303drdL1WoUngvED7f+RnKBK2G6ZEpO+LDovQk19xGjiMTtPJrjMjZJ3QXqPvx5wca
31KSZLr4lMTuoTI/ZXyZy5bD4tShiZz6KcyX27cD70q2iRcEZ0poLKHyEIDAi3TM5k
32SwbbWBFd5RNPOR0qzrb/0p9ksKK48IIfH2FvABEBAAG0K0hhc2hpQ29ycCBTZWN1
33cml0eSA8c2VjdXJpdHlAaGFzaGljb3JwLmNvbT6JATgEEwECACIFAlMORM0CGwMG
34CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFGFLYc0j/xMyWIIAIPhcVqiQ59n
35Jc07gjUX0SWBJAxEG1lKxfzS4Xp+57h2xxTpdotGQ1fZwsihaIqow337YHQI3q0i
36SqV534Ms+j/tU7X8sq11xFJIeEVG8PASRCwmryUwghFKPlHETQ8jJ+Y8+1asRydi
37psP3B/5Mjhqv/uOK+Vy3zAyIpyDOMtIpOVfjSpCplVRdtSTFWBu9Em7j5I2HMn1w
38sJZnJgXKpybpibGiiTtmnFLOwibmprSu04rsnP4ncdC2XRD4wIjoyA+4PKgX3sCO
39klEzKryWYBmLkJOMDdo52LttP3279s7XrkLEE7ia0fXa2c12EQ0f0DQ1tGUvyVEW
40WmJVccm5bq25AQ0EUw5EzQEIANaPUY04/g7AmYkOMjaCZ6iTp9hB5Rsj/4ee/ln9
41wArzRO9+3eejLWh53FoN1rO+su7tiXJA5YAzVy6tuolrqjM8DBztPxdLBbEi4V+j
422tK0dATdBQBHEh3OJApO2UBtcjaZBT31zrG9K55D+CrcgIVEHAKY8Cb4kLBkb5wM
43skn+DrASKU0BNIV1qRsxfiUdQHZfSqtp004nrql1lbFMLFEuiY8FZrkkQ9qduixo
44mTT6f34/oiY+Jam3zCK7RDN/OjuWheIPGj/Qbx9JuNiwgX6yRj7OE1tjUx6d8g9y
450H1fmLJbb3WZZbuuGFnK6qrE3bGeY8+AWaJAZ37wpWh1p0cAEQEAAYkBHwQYAQIA
46CQUCUw5EzQIbDAAKCRBRhS2HNI/8TJntCAClU7TOO/X053eKF1jqNW4A1qpxctVc
47z8eTcY8Om5O4f6a/rfxfNFKn9Qyja/OG1xWNobETy7MiMXYjaa8uUx5iFy6kMVaP
480BXJ59NLZjMARGw6lVTYDTIvzqqqwLxgliSDfSnqUhubGwvykANPO+93BBx89MRG
49unNoYGXtPlhNFrAsB1VR8+EyKLv2HQtGCPSFBhrjuzH3gxGibNDDdFQLxxuJWepJ
50EK1UbTS4ms0NgZ2Uknqn1WRU1Ki7rE4sTy68iZtWpKQXZEJa0IGnuI2sSINGcXCJ
51oEIgXTMyCILo34Fa/C6VCm2WBgz9zZO8/rHIiQm1J5zqz0DrDwKBUM9C
52=LYpS
53-----END PGP PUBLIC KEY BLOCK-----`
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/version.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/version.go
new file mode 100644
index 0000000..8fad58d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/version.go
@@ -0,0 +1,72 @@
1package discovery
2
3import (
4 "fmt"
5 "sort"
6
7 version "github.com/hashicorp/go-version"
8)
9
10const VersionZero = "0.0.0"
11
12// A VersionStr is a string containing a possibly-invalid representation
13// of a semver version number. Call Parse on it to obtain a real Version
14// object, or discover that it is invalid.
15type VersionStr string
16
17// Parse transforms a VersionStr into a Version if it is
18// syntactically valid. If it isn't then an error is returned instead.
19func (s VersionStr) Parse() (Version, error) {
20 raw, err := version.NewVersion(string(s))
21 if err != nil {
22 return Version{}, err
23 }
24 return Version{raw}, nil
25}
26
27// MustParse transforms a VersionStr into a Version if it is
28// syntactically valid. If it isn't then it panics.
29func (s VersionStr) MustParse() Version {
30 ret, err := s.Parse()
31 if err != nil {
32 panic(err)
33 }
34 return ret
35}
36
37// Version represents a version number that has been parsed from
38// a semver string and known to be valid.
39type Version struct {
40 // We wrap this here just because it avoids a proliferation of
41 // direct go-version imports all over the place, and keeps the
42 // version-processing details within this package.
43 raw *version.Version
44}
45
46func (v Version) String() string {
47 return v.raw.String()
48}
49
50func (v Version) NewerThan(other Version) bool {
51 return v.raw.GreaterThan(other.raw)
52}
53
54func (v Version) Equal(other Version) bool {
55 return v.raw.Equal(other.raw)
56}
57
58// MinorUpgradeConstraintStr returns a ConstraintStr that would permit
59// minor upgrades relative to the receiving version.
60func (v Version) MinorUpgradeConstraintStr() ConstraintStr {
61 segments := v.raw.Segments()
62 return ConstraintStr(fmt.Sprintf("~> %d.%d", segments[0], segments[1]))
63}
64
65type Versions []Version
66
67// Sort sorts version from newest to oldest.
68func (v Versions) Sort() {
69 sort.Slice(v, func(i, j int) bool {
70 return v[i].NewerThan(v[j])
71 })
72}
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/version_set.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/version_set.go
new file mode 100644
index 0000000..0aefd75
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/version_set.go
@@ -0,0 +1,84 @@
1package discovery
2
3import (
4 "sort"
5
6 version "github.com/hashicorp/go-version"
7)
8
9// A ConstraintStr is a string containing a possibly-invalid representation
10// of a version constraint provided in configuration. Call Parse on it to
11// obtain a real Constraint object, or discover that it is invalid.
12type ConstraintStr string
13
14// Parse transforms a ConstraintStr into a Constraints if it is
15// syntactically valid. If it isn't then an error is returned instead.
16func (s ConstraintStr) Parse() (Constraints, error) {
17 raw, err := version.NewConstraint(string(s))
18 if err != nil {
19 return Constraints{}, err
20 }
21 return Constraints{raw}, nil
22}
23
24// MustParse is like Parse but it panics if the constraint string is invalid.
25func (s ConstraintStr) MustParse() Constraints {
26 ret, err := s.Parse()
27 if err != nil {
28 panic(err)
29 }
30 return ret
31}
32
33// Constraints represents a set of versions which any given Version is either
34// a member of or not.
35type Constraints struct {
36 raw version.Constraints
37}
38
39// AllVersions is a Constraints containing all versions
40var AllVersions Constraints
41
42func init() {
43 AllVersions = Constraints{
44 raw: make(version.Constraints, 0),
45 }
46}
47
48// Allows returns true if the given version permitted by the receiving
49// constraints set.
50func (s Constraints) Allows(v Version) bool {
51 return s.raw.Check(v.raw)
52}
53
54// Append combines the receiving set with the given other set to produce
55// a set that is the intersection of both sets, which is to say that resulting
56// constraints contain only the versions that are members of both.
57func (s Constraints) Append(other Constraints) Constraints {
58 raw := make(version.Constraints, 0, len(s.raw)+len(other.raw))
59
60 // Since "raw" is a list of constraints that remove versions from the set,
61 // "Intersection" is implemented by concatenating together those lists,
62 // thus leaving behind only the versions not removed by either list.
63 raw = append(raw, s.raw...)
64 raw = append(raw, other.raw...)
65
66 // while the set is unordered, we sort these lexically for consistent output
67 sort.Slice(raw, func(i, j int) bool {
68 return raw[i].String() < raw[j].String()
69 })
70
71 return Constraints{raw}
72}
73
74// String returns a string representation of the set members as a set
75// of range constraints.
76func (s Constraints) String() string {
77 return s.raw.String()
78}
79
80// Unconstrained returns true if and only if the receiver is an empty
81// constraint set.
82func (s Constraints) Unconstrained() bool {
83 return len(s.raw) == 0
84}