command init: show suggested constraints for unconstrained providers
When running "terraform init" with providers that are unconstrained, we will now produce information to help the user update configuration to constrain for the particular providers that were chosen, to prevent inadvertently drifting onto a newer major release that might contain breaking changes. A ~> constraint is used here because pinning to a single specific version is expected to create dependency hell when using child modules. By using this constraint mode, which allows minor version upgrades, we avoid the need for users to constantly adjust version constraints across many modules, but make major version upgrades still be opt-in. Any constraint at all in the configuration will prevent the display of these suggestions, so users are free to use stronger or weaker constraints if desired, ignoring the recommendation.
This commit is contained in:
parent
f70318097a
commit
4ba20f9c1c
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
|
@ -187,6 +188,10 @@ func (c *InitCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(c.Colorize().Color(
|
||||
"[reset][bold]Initializing provider plugins...",
|
||||
))
|
||||
|
||||
err = c.getProviders(path, sMgr.State())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
|
@ -249,6 +254,37 @@ func (c *InitCommand) getProviders(path string, state *terraform.State) error {
|
|||
return fmt.Errorf("failed to save provider manifest: %s", err)
|
||||
}
|
||||
|
||||
// If any providers have "floating" versions (completely unconstrained)
|
||||
// we'll suggest the user constrain with a pessimistic constraint to
|
||||
// avoid implicitly adopting a later major release.
|
||||
constraintSuggestions := make(map[string]discovery.ConstraintStr)
|
||||
for name, meta := range chosen {
|
||||
req := requirements[name]
|
||||
if req == nil {
|
||||
// should never happen, but we don't want to crash here, so we'll
|
||||
// be cautious.
|
||||
continue
|
||||
}
|
||||
|
||||
if req.Versions.Unconstrained() {
|
||||
// meta.Version.MustParse is safe here because our "chosen" metas
|
||||
// were already filtered for validity of versions.
|
||||
constraintSuggestions[name] = meta.Version.MustParse().MinorUpgradeConstraintStr()
|
||||
}
|
||||
}
|
||||
if len(constraintSuggestions) != 0 {
|
||||
names := make([]string, 0, len(constraintSuggestions))
|
||||
for name := range constraintSuggestions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
c.Ui.Output(outputInitProvidersUnconstrained)
|
||||
for _, name := range names {
|
||||
c.Ui.Output(fmt.Sprintf("* provider.%s: version = %q", name, constraintSuggestions[name]))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -361,3 +397,13 @@ If you ever set or change modules or backend configuration for Terraform,
|
|||
rerun this command to reinitialize your environment. If you forget, other
|
||||
commands will detect it and remind you to do so if necessary.
|
||||
`
|
||||
|
||||
const outputInitProvidersUnconstrained = `
|
||||
The following providers do not have any version constraints in configuration,
|
||||
so the latest version was installed.
|
||||
|
||||
To prevent automatic upgrades to new major versions that may contain breaking
|
||||
changes, it is recommended to add version = "..." constraints to the
|
||||
corresponding provider blocks in configuration, with the constraint strings
|
||||
suggested below.
|
||||
`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package discovery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
version "github.com/hashicorp/go-version"
|
||||
|
@ -48,6 +49,13 @@ func (v Version) NewerThan(other Version) bool {
|
|||
return v.raw.GreaterThan(other.raw)
|
||||
}
|
||||
|
||||
// MinorUpgradeConstraintStr returns a ConstraintStr that would permit
|
||||
// minor upgrades relative to the receiving version.
|
||||
func (v Version) MinorUpgradeConstraintStr() ConstraintStr {
|
||||
segments := v.raw.Segments()
|
||||
return ConstraintStr(fmt.Sprintf("~> %d.%d", segments[0], segments[1]))
|
||||
}
|
||||
|
||||
type Versions []Version
|
||||
|
||||
// Sort sorts version from newest to oldest.
|
||||
|
|
|
@ -69,3 +69,9 @@ func (s Constraints) Append(other Constraints) Constraints {
|
|||
func (s Constraints) String() string {
|
||||
return s.raw.String()
|
||||
}
|
||||
|
||||
// Unconstrained returns true if and only if the receiver is an empty
|
||||
// constraint set.
|
||||
func (s Constraints) Unconstrained() bool {
|
||||
return len(s.raw) == 0
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue