]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/plugin/discovery/get.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / plugin / discovery / get.go
1 package discovery
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "log"
9 "net/http"
10 "os"
11 "path/filepath"
12 "runtime"
13 "strconv"
14 "strings"
15
16 "github.com/hashicorp/errwrap"
17 getter "github.com/hashicorp/go-getter"
18 multierror "github.com/hashicorp/go-multierror"
19 "github.com/hashicorp/terraform/httpclient"
20 "github.com/hashicorp/terraform/registry"
21 "github.com/hashicorp/terraform/registry/regsrc"
22 "github.com/hashicorp/terraform/registry/response"
23 "github.com/hashicorp/terraform/svchost/disco"
24 "github.com/hashicorp/terraform/tfdiags"
25 tfversion "github.com/hashicorp/terraform/version"
26 "github.com/mitchellh/cli"
27 )
28
29 // Releases are located by querying the terraform registry.
30
31 const protocolVersionHeader = "x-terraform-protocol-version"
32
33 var httpClient *http.Client
34
35 var errVersionNotFound = errors.New("version not found")
36
37 func init() {
38 httpClient = httpclient.New()
39
40 httpGetter := &getter.HttpGetter{
41 Client: httpClient,
42 Netrc: true,
43 }
44
45 getter.Getters["http"] = httpGetter
46 getter.Getters["https"] = httpGetter
47 }
48
49 // An Installer maintains a local cache of plugins by downloading plugins
50 // from an online repository.
51 type Installer interface {
52 Get(name string, req Constraints) (PluginMeta, tfdiags.Diagnostics, error)
53 PurgeUnused(used map[string]PluginMeta) (removed PluginMetaSet, err error)
54 }
55
56 // ProviderInstaller is an Installer implementation that knows how to
57 // download Terraform providers from the official HashiCorp releases service
58 // into a local directory. The files downloaded are compliant with the
59 // naming scheme expected by FindPlugins, so the target directory of a
60 // provider installer can be used as one of several plugin discovery sources.
61 type ProviderInstaller struct {
62 Dir string
63
64 // Cache is used to access and update a local cache of plugins if non-nil.
65 // Can be nil to disable caching.
66 Cache PluginCache
67
68 PluginProtocolVersion uint
69
70 // OS and Arch specify the OS and architecture that should be used when
71 // installing plugins. These use the same labels as the runtime.GOOS and
72 // runtime.GOARCH variables respectively, and indeed the values of these
73 // are used as defaults if either of these is the empty string.
74 OS string
75 Arch string
76
77 // Skip checksum and signature verification
78 SkipVerify bool
79
80 Ui cli.Ui // Ui for output
81
82 // Services is a required *disco.Disco, which may have services and
83 // credentials pre-loaded.
84 Services *disco.Disco
85
86 // registry client
87 registry *registry.Client
88 }
89
90 // Get is part of an implementation of type Installer, and attempts to download
91 // and install a Terraform provider matching the given constraints.
92 //
93 // This method may return one of a number of sentinel errors from this
94 // package to indicate issues that are likely to be resolvable via user action:
95 //
96 // ErrorNoSuchProvider: no provider with the given name exists in the repository.
97 // ErrorNoSuitableVersion: the provider exists but no available version matches constraints.
98 // ErrorNoVersionCompatible: a plugin was found within the constraints but it is
99 // incompatible with the current Terraform version.
100 //
101 // These errors should be recognized and handled as special cases by the caller
102 // to present a suitable user-oriented error message.
103 //
104 // All other errors indicate an internal problem that is likely _not_ solvable
105 // through user action, or at least not within Terraform's scope. Error messages
106 // are produced under the assumption that if presented to the user they will
107 // be presented alongside context about what is being installed, and thus the
108 // error messages do not redundantly include such information.
109 func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, tfdiags.Diagnostics, error) {
110 var diags tfdiags.Diagnostics
111
112 // a little bit of initialization.
113 if i.OS == "" {
114 i.OS = runtime.GOOS
115 }
116 if i.Arch == "" {
117 i.Arch = runtime.GOARCH
118 }
119 if i.registry == nil {
120 i.registry = registry.NewClient(i.Services, nil)
121 }
122
123 // get a full listing of versions for the requested provider
124 allVersions, err := i.listProviderVersions(provider)
125
126 // TODO: return multiple errors
127 if err != nil {
128 log.Printf("[DEBUG] %s", err)
129 if registry.IsServiceUnreachable(err) {
130 registryHost, err := i.hostname()
131 if err == nil && registryHost == regsrc.PublicRegistryHost.Raw {
132 return PluginMeta{}, diags, ErrorPublicRegistryUnreachable
133 }
134 return PluginMeta{}, diags, ErrorServiceUnreachable
135 }
136 if registry.IsServiceNotProvided(err) {
137 return PluginMeta{}, diags, err
138 }
139 return PluginMeta{}, diags, ErrorNoSuchProvider
140 }
141
142 // Add any warnings from the response to diags
143 for _, warning := range allVersions.Warnings {
144 hostname, err := i.hostname()
145 if err != nil {
146 return PluginMeta{}, diags, err
147 }
148 diag := tfdiags.SimpleWarning(fmt.Sprintf("%s: %s", hostname, warning))
149 diags = diags.Append(diag)
150 }
151
152 if len(allVersions.Versions) == 0 {
153 return PluginMeta{}, diags, ErrorNoSuitableVersion
154 }
155 providerSource := allVersions.ID
156
157 // Filter the list of plugin versions to those which meet the version constraints
158 versions := allowedVersions(allVersions, req)
159 if len(versions) == 0 {
160 return PluginMeta{}, diags, ErrorNoSuitableVersion
161 }
162
163 // sort them newest to oldest. The newest version wins!
164 response.ProviderVersionCollection(versions).Sort()
165
166 // if the chosen provider version does not support the requested platform,
167 // filter the list of acceptable versions to those that support that platform
168 if err := i.checkPlatformCompatibility(versions[0]); err != nil {
169 versions = i.platformCompatibleVersions(versions)
170 if len(versions) == 0 {
171 return PluginMeta{}, diags, ErrorNoVersionCompatibleWithPlatform
172 }
173 }
174
175 // we now have a winning platform-compatible version
176 versionMeta := versions[0]
177 v := VersionStr(versionMeta.Version).MustParse()
178
179 // check protocol compatibility
180 if err := i.checkPluginProtocol(versionMeta); err != nil {
181 closestMatch, err := i.findClosestProtocolCompatibleVersion(allVersions.Versions)
182 if err != nil {
183 // No operation here if we can't find a version with compatible protocol
184 return PluginMeta{}, diags, err
185 }
186
187 // Prompt version suggestion to UI based on closest protocol match
188 var errMsg string
189 closestVersion := VersionStr(closestMatch.Version).MustParse()
190 if v.NewerThan(closestVersion) {
191 errMsg = providerProtocolTooNew
192 } else {
193 errMsg = providerProtocolTooOld
194 }
195
196 constraintStr := req.String()
197 if constraintStr == "" {
198 constraintStr = "(any version)"
199 }
200
201 return PluginMeta{}, diags, errwrap.Wrap(ErrorVersionIncompatible, fmt.Errorf(fmt.Sprintf(
202 errMsg, provider, v.String(), tfversion.String(),
203 closestVersion.String(), closestVersion.MinorUpgradeConstraintStr(), constraintStr)))
204 }
205
206 downloadURLs, err := i.listProviderDownloadURLs(providerSource, versionMeta.Version)
207 providerURL := downloadURLs.DownloadURL
208
209 if !i.SkipVerify {
210 // Terraform verifies the integrity of a provider release before downloading
211 // the plugin binary. The digital signature (SHA256SUMS.sig) on the
212 // release distribution (SHA256SUMS) is verified with the public key of the
213 // publisher provided in the Terraform Registry response, ensuring that
214 // everything is as intended by the publisher. The checksum of the provider
215 // plugin is expected in the SHA256SUMS file and is double checked to match
216 // the checksum of the original published release to the Registry. This
217 // enforces immutability of releases between the Registry and the plugin's
218 // host location. Lastly, the integrity of the binary is verified upon
219 // download matches the Registry and signed checksum.
220 sha256, err := i.getProviderChecksum(downloadURLs)
221 if err != nil {
222 return PluginMeta{}, diags, err
223 }
224
225 // add the checksum parameter for go-getter to verify the download for us.
226 if sha256 != "" {
227 providerURL = providerURL + "?checksum=sha256:" + sha256
228 }
229 }
230
231 printedProviderName := fmt.Sprintf("%q (%s)", provider, providerSource)
232 i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %s %s...", printedProviderName, versionMeta.Version))
233 log.Printf("[DEBUG] getting provider %s version %q", printedProviderName, versionMeta.Version)
234 err = i.install(provider, v, providerURL)
235 if err != nil {
236 return PluginMeta{}, diags, err
237 }
238
239 // Find what we just installed
240 // (This is weird, because go-getter doesn't directly return
241 // information about what was extracted, and we just extracted
242 // the archive directly into a shared dir here.)
243 log.Printf("[DEBUG] looking for the %s %s plugin we just installed", provider, versionMeta.Version)
244 metas := FindPlugins("provider", []string{i.Dir})
245 log.Printf("[DEBUG] all plugins found %#v", metas)
246 metas, _ = metas.ValidateVersions()
247 metas = metas.WithName(provider).WithVersion(v)
248 log.Printf("[DEBUG] filtered plugins %#v", metas)
249 if metas.Count() == 0 {
250 // This should never happen. Suggests that the release archive
251 // contains an executable file whose name doesn't match the
252 // expected convention.
253 return PluginMeta{}, diags, fmt.Errorf(
254 "failed to find installed plugin version %s; this is a bug in Terraform and should be reported",
255 versionMeta.Version,
256 )
257 }
258
259 if metas.Count() > 1 {
260 // This should also never happen, and suggests that a
261 // particular version was re-released with a different
262 // executable filename. We consider releases as immutable, so
263 // this is an error.
264 return PluginMeta{}, diags, fmt.Errorf(
265 "multiple plugins installed for version %s; this is a bug in Terraform and should be reported",
266 versionMeta.Version,
267 )
268 }
269
270 // By now we know we have exactly one meta, and so "Newest" will
271 // return that one.
272 return metas.Newest(), diags, nil
273 }
274
275 func (i *ProviderInstaller) install(provider string, version Version, url string) error {
276 if i.Cache != nil {
277 log.Printf("[DEBUG] looking for provider %s %s in plugin cache", provider, version)
278 cached := i.Cache.CachedPluginPath("provider", provider, version)
279 if cached == "" {
280 log.Printf("[DEBUG] %s %s not yet in cache, so downloading %s", provider, version, url)
281 err := getter.Get(i.Cache.InstallDir(), url)
282 if err != nil {
283 return err
284 }
285 // should now be in cache
286 cached = i.Cache.CachedPluginPath("provider", provider, version)
287 if cached == "" {
288 // should never happen if the getter is behaving properly
289 // and the plugins are packaged properly.
290 return fmt.Errorf("failed to find downloaded plugin in cache %s", i.Cache.InstallDir())
291 }
292 }
293
294 // Link or copy the cached binary into our install dir so the
295 // normal resolution machinery can find it.
296 filename := filepath.Base(cached)
297 targetPath := filepath.Join(i.Dir, filename)
298 // check if the target dir exists, and create it if not
299 var err error
300 if _, StatErr := os.Stat(i.Dir); os.IsNotExist(StatErr) {
301 err = os.MkdirAll(i.Dir, 0700)
302 }
303 if err != nil {
304 return err
305 }
306
307 log.Printf("[DEBUG] installing %s %s to %s from local cache %s", provider, version, targetPath, cached)
308
309 // Delete if we can. If there's nothing there already then no harm done.
310 // This is important because we can't create a link if there's
311 // already a file of the same name present.
312 // (any other error here we'll catch below when we try to write here)
313 os.Remove(targetPath)
314
315 // We don't attempt linking on Windows because links are not
316 // comprehensively supported by all tools/apps in Windows and
317 // so we choose to be conservative to avoid creating any
318 // weird issues for Windows users.
319 linkErr := errors.New("link not supported for Windows") // placeholder error, never actually returned
320 if runtime.GOOS != "windows" {
321 // Try hard linking first. Hard links are preferable because this
322 // creates a self-contained directory that doesn't depend on the
323 // cache after install.
324 linkErr = os.Link(cached, targetPath)
325
326 // If that failed, try a symlink. This _does_ depend on the cache
327 // after install, so the user must manage the cache more carefully
328 // in this case, but avoids creating redundant copies of the
329 // plugins on disk.
330 if linkErr != nil {
331 linkErr = os.Symlink(cached, targetPath)
332 }
333 }
334
335 // If we still have an error then we'll try a copy as a fallback.
336 // In this case either the OS is Windows or the target filesystem
337 // can't support symlinks.
338 if linkErr != nil {
339 srcFile, err := os.Open(cached)
340 if err != nil {
341 return fmt.Errorf("failed to open cached plugin %s: %s", cached, err)
342 }
343 defer srcFile.Close()
344
345 destFile, err := os.OpenFile(targetPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, os.ModePerm)
346 if err != nil {
347 return fmt.Errorf("failed to create %s: %s", targetPath, err)
348 }
349
350 _, err = io.Copy(destFile, srcFile)
351 if err != nil {
352 destFile.Close()
353 return fmt.Errorf("failed to copy cached plugin from %s to %s: %s", cached, targetPath, err)
354 }
355
356 err = destFile.Close()
357 if err != nil {
358 return fmt.Errorf("error creating %s: %s", targetPath, err)
359 }
360 }
361
362 // One way or another, by the time we get here we should have either
363 // a link or a copy of the cached plugin within i.Dir, as expected.
364 } else {
365 log.Printf("[DEBUG] plugin cache is disabled, so downloading %s %s from %s", provider, version, url)
366 err := getter.Get(i.Dir, url)
367 if err != nil {
368 return err
369 }
370 }
371 return nil
372 }
373
374 func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) {
375 purge := make(PluginMetaSet)
376
377 present := FindPlugins("provider", []string{i.Dir})
378 for meta := range present {
379 chosen, ok := used[meta.Name]
380 if !ok {
381 purge.Add(meta)
382 }
383 if chosen.Path != meta.Path {
384 purge.Add(meta)
385 }
386 }
387
388 removed := make(PluginMetaSet)
389 var errs error
390 for meta := range purge {
391 path := meta.Path
392 err := os.Remove(path)
393 if err != nil {
394 errs = multierror.Append(errs, fmt.Errorf(
395 "failed to remove unused provider plugin %s: %s",
396 path, err,
397 ))
398 } else {
399 removed.Add(meta)
400 }
401 }
402
403 return removed, errs
404 }
405
406 func (i *ProviderInstaller) getProviderChecksum(resp *response.TerraformProviderPlatformLocation) (string, error) {
407 // Get SHA256SUMS file.
408 shasums, err := getFile(resp.ShasumsURL)
409 if err != nil {
410 log.Printf("[ERROR] error fetching checksums from %q: %s", resp.ShasumsURL, err)
411 return "", ErrorMissingChecksumVerification
412 }
413
414 // Get SHA256SUMS.sig file.
415 signature, err := getFile(resp.ShasumsSignatureURL)
416 if err != nil {
417 log.Printf("[ERROR] error fetching checksums signature from %q: %s", resp.ShasumsSignatureURL, err)
418 return "", ErrorSignatureVerification
419 }
420
421 // Verify the GPG signature returned from the Registry.
422 asciiArmor := resp.SigningKeys.GPGASCIIArmor()
423 signer, err := verifySig(shasums, signature, asciiArmor)
424 if err != nil {
425 log.Printf("[ERROR] error verifying signature: %s", err)
426 return "", ErrorSignatureVerification
427 }
428
429 // Also verify the GPG signature against the HashiCorp public key. This is
430 // a temporary additional check until a more robust key verification
431 // process is added in a future release.
432 _, err = verifySig(shasums, signature, HashicorpPublicKey)
433 if err != nil {
434 log.Printf("[ERROR] error verifying signature against HashiCorp public key: %s", err)
435 return "", ErrorSignatureVerification
436 }
437
438 // Display identity for GPG key which succeeded verifying the signature.
439 // This could also be used to display to the user with i.Ui.Info().
440 identities := []string{}
441 for k := range signer.Identities {
442 identities = append(identities, k)
443 }
444 identity := strings.Join(identities, ", ")
445 log.Printf("[DEBUG] verified GPG signature with key from %s", identity)
446
447 // Extract checksum for this os/arch platform binary and verify against Registry
448 checksum := checksumForFile(shasums, resp.Filename)
449 if checksum == "" {
450 log.Printf("[ERROR] missing checksum for %s from source %s", resp.Filename, resp.ShasumsURL)
451 return "", ErrorMissingChecksumVerification
452 } else if checksum != resp.Shasum {
453 log.Printf("[ERROR] unexpected checksum for %s from source %q", resp.Filename, resp.ShasumsURL)
454 return "", ErrorChecksumVerification
455 }
456
457 return checksum, nil
458 }
459
460 func (i *ProviderInstaller) hostname() (string, error) {
461 provider := regsrc.NewTerraformProvider("", i.OS, i.Arch)
462 svchost, err := provider.SvcHost()
463 if err != nil {
464 return "", err
465 }
466
467 return svchost.ForDisplay(), nil
468 }
469
470 // list all versions available for the named provider
471 func (i *ProviderInstaller) listProviderVersions(name string) (*response.TerraformProviderVersions, error) {
472 provider := regsrc.NewTerraformProvider(name, i.OS, i.Arch)
473 versions, err := i.registry.TerraformProviderVersions(provider)
474 return versions, err
475 }
476
477 func (i *ProviderInstaller) listProviderDownloadURLs(name, version string) (*response.TerraformProviderPlatformLocation, error) {
478 urls, err := i.registry.TerraformProviderLocation(regsrc.NewTerraformProvider(name, i.OS, i.Arch), version)
479 if urls == nil {
480 return nil, fmt.Errorf("No download urls found for provider %s", name)
481 }
482 return urls, err
483 }
484
485 // findClosestProtocolCompatibleVersion searches for the provider version with the closest protocol match.
486 // Prerelease versions are filtered.
487 func (i *ProviderInstaller) findClosestProtocolCompatibleVersion(versions []*response.TerraformProviderVersion) (*response.TerraformProviderVersion, error) {
488 // Loop through all the provider versions to find the earliest and latest
489 // versions that match the installer protocol to then select the closest of the two
490 var latest, earliest *response.TerraformProviderVersion
491 for _, version := range versions {
492 // Prereleases are filtered and will not be suggested
493 v, err := VersionStr(version.Version).Parse()
494 if err != nil || v.IsPrerelease() {
495 continue
496 }
497
498 if err := i.checkPluginProtocol(version); err == nil {
499 if earliest == nil {
500 // Found the first provider version with compatible protocol
501 earliest = version
502 }
503 // Update the latest protocol compatible version
504 latest = version
505 }
506 }
507 if earliest == nil {
508 // No compatible protocol was found for any version
509 return nil, ErrorNoVersionCompatible
510 }
511
512 // Convert protocols to comparable types
513 protoString := strconv.Itoa(int(i.PluginProtocolVersion))
514 protocolVersion, err := VersionStr(protoString).Parse()
515 if err != nil {
516 return nil, fmt.Errorf("invalid plugin protocol version: %q", i.PluginProtocolVersion)
517 }
518
519 earliestVersionProtocol, err := VersionStr(earliest.Protocols[0]).Parse()
520 if err != nil {
521 return nil, err
522 }
523
524 // Compare installer protocol version with the first protocol listed of the earliest match
525 // [A, B] where A is assumed the earliest compatible major version of the protocol pair
526 if protocolVersion.NewerThan(earliestVersionProtocol) {
527 // Provider protocols are too old, the closest version is the earliest compatible version
528 return earliest, nil
529 }
530
531 // Provider protocols are too new, the closest version is the latest compatible version
532 return latest, nil
533 }
534
535 func (i *ProviderInstaller) checkPluginProtocol(versionMeta *response.TerraformProviderVersion) error {
536 // TODO: should this be a different error? We should probably differentiate between
537 // no compatible versions and no protocol versions listed at all
538 if len(versionMeta.Protocols) == 0 {
539 return fmt.Errorf("no plugin protocol versions listed")
540 }
541
542 protoString := strconv.Itoa(int(i.PluginProtocolVersion))
543 protocolVersion, err := VersionStr(protoString).Parse()
544 if err != nil {
545 return fmt.Errorf("invalid plugin protocol version: %q", i.PluginProtocolVersion)
546 }
547 protocolConstraint, err := protocolVersion.MinorUpgradeConstraintStr().Parse()
548 if err != nil {
549 // This should not fail if the preceding function succeeded.
550 return fmt.Errorf("invalid plugin protocol version: %q", protocolVersion.String())
551 }
552
553 for _, p := range versionMeta.Protocols {
554 proPro, err := VersionStr(p).Parse()
555 if err != nil {
556 // invalid protocol reported by the registry. Move along.
557 log.Printf("[WARN] invalid provider protocol version %q found in the registry", versionMeta.Version)
558 continue
559 }
560 // success!
561 if protocolConstraint.Allows(proPro) {
562 return nil
563 }
564 }
565
566 return ErrorNoVersionCompatible
567 }
568
569 // REVIEWER QUESTION (again): this ends up swallowing a bunch of errors from
570 // checkPluginProtocol. Do they need to be percolated up better, or would
571 // debug messages would suffice in these situations?
572 func (i *ProviderInstaller) findPlatformCompatibleVersion(versions []*response.TerraformProviderVersion) (*response.TerraformProviderVersion, error) {
573 for _, version := range versions {
574 if err := i.checkPlatformCompatibility(version); err == nil {
575 return version, nil
576 }
577 }
578
579 return nil, ErrorNoVersionCompatibleWithPlatform
580 }
581
582 // platformCompatibleVersions returns a list of provider versions that are
583 // compatible with the requested platform.
584 func (i *ProviderInstaller) platformCompatibleVersions(versions []*response.TerraformProviderVersion) []*response.TerraformProviderVersion {
585 var v []*response.TerraformProviderVersion
586 for _, version := range versions {
587 if err := i.checkPlatformCompatibility(version); err == nil {
588 v = append(v, version)
589 }
590 }
591 return v
592 }
593
594 func (i *ProviderInstaller) checkPlatformCompatibility(versionMeta *response.TerraformProviderVersion) error {
595 if len(versionMeta.Platforms) == 0 {
596 return fmt.Errorf("no supported provider platforms listed")
597 }
598 for _, p := range versionMeta.Platforms {
599 if p.Arch == i.Arch && p.OS == i.OS {
600 return nil
601 }
602 }
603 return fmt.Errorf("version %s does not support the requested platform %s_%s", versionMeta.Version, i.OS, i.Arch)
604 }
605
606 // take the list of available versions for a plugin, and filter out those that
607 // don't fit the constraints.
608 func allowedVersions(available *response.TerraformProviderVersions, required Constraints) []*response.TerraformProviderVersion {
609 var allowed []*response.TerraformProviderVersion
610
611 for _, v := range available.Versions {
612 version, err := VersionStr(v.Version).Parse()
613 if err != nil {
614 log.Printf("[WARN] invalid version found for %q: %s", available.ID, err)
615 continue
616 }
617 if required.Allows(version) {
618 allowed = append(allowed, v)
619 }
620 }
621 return allowed
622 }
623
624 func checksumForFile(sums []byte, name string) string {
625 for _, line := range strings.Split(string(sums), "\n") {
626 parts := strings.Fields(line)
627 if len(parts) > 1 && parts[1] == name {
628 return parts[0]
629 }
630 }
631 return ""
632 }
633
634 func getFile(url string) ([]byte, error) {
635 resp, err := httpClient.Get(url)
636 if err != nil {
637 return nil, err
638 }
639 defer resp.Body.Close()
640
641 if resp.StatusCode != http.StatusOK {
642 return nil, fmt.Errorf("%s", resp.Status)
643 }
644
645 data, err := ioutil.ReadAll(resp.Body)
646 if err != nil {
647 return data, err
648 }
649 return data, nil
650 }
651
652 // providerProtocolTooOld is a message sent to the CLI UI if the provider's
653 // supported protocol versions are too old for the user's version of terraform,
654 // but an older version of the provider is compatible.
655 const providerProtocolTooOld = `
656 [reset][bold][red]Provider %q v%s is not compatible with Terraform %s.[reset][red]
657
658 Provider version %s is the earliest compatible version. Select it with
659 the following version constraint:
660
661 version = %q
662
663 Terraform checked all of the plugin versions matching the given constraint:
664 %s
665
666 Consult the documentation for this provider for more information on
667 compatibility between provider and Terraform versions.
668 `
669
670 // providerProtocolTooNew is a message sent to the CLI UI if the provider's
671 // supported protocol versions are too new for the user's version of terraform,
672 // and the user could either upgrade terraform or choose an older version of the
673 // provider
674 const providerProtocolTooNew = `
675 [reset][bold][red]Provider %q v%s is not compatible with Terraform %s.[reset][red]
676
677 Provider version %s is the latest compatible version. Select it with
678 the following constraint:
679
680 version = %q
681
682 Terraform checked all of the plugin versions matching the given constraint:
683 %s
684
685 Consult the documentation for this provider for more information on
686 compatibility between provider and Terraform versions.
687
688 Alternatively, upgrade to the latest version of Terraform for compatibility with newer provider releases.
689 `