terraform: verify version requirements from configuration
This commit is contained in:
parent
85d3439fa0
commit
2c467e0f74
|
@ -102,6 +102,13 @@ type Context struct {
|
|||
// should not be mutated in any way, since the pointers are copied, not
|
||||
// the values themselves.
|
||||
func NewContext(opts *ContextOpts) (*Context, error) {
|
||||
// Validate the version requirement if it is given
|
||||
if opts.Module != nil {
|
||||
if err := checkRequiredVersion(opts.Module); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all the hooks and add our stop hook. We don't append directly
|
||||
// to the Config so that we're not modifying that in-place.
|
||||
sh := new(stopHook)
|
||||
|
|
|
@ -6,9 +6,87 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/terraform/flatmap"
|
||||
)
|
||||
|
||||
func TestNewContextRequiredVersion(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Module string
|
||||
Version string
|
||||
Value string
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
"no requirement",
|
||||
"",
|
||||
"0.1.0",
|
||||
"",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"doesn't match",
|
||||
"",
|
||||
"0.1.0",
|
||||
"> 0.6.0",
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"matches",
|
||||
"",
|
||||
"0.7.0",
|
||||
"> 0.6.0",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"module matches",
|
||||
"context-required-version-module",
|
||||
"0.5.0",
|
||||
"",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"module doesn't match",
|
||||
"context-required-version-module",
|
||||
"0.4.0",
|
||||
"",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||
// Reset the version for the tests
|
||||
old := SemVersion
|
||||
SemVersion = version.Must(version.NewVersion(tc.Version))
|
||||
defer func() { SemVersion = old }()
|
||||
|
||||
name := "context-required-version"
|
||||
if tc.Module != "" {
|
||||
name = tc.Module
|
||||
}
|
||||
mod := testModule(t, name)
|
||||
if tc.Value != "" {
|
||||
mod.Config().Terraform.RequiredVersion = tc.Value
|
||||
}
|
||||
_, err := NewContext(&ContextOpts{
|
||||
Module: mod,
|
||||
})
|
||||
if (err != nil) != tc.Err {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewContextState(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Input *ContextOpts
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
terraform { required_version = ">= 0.5.0" }
|
|
@ -0,0 +1,3 @@
|
|||
module "child" {
|
||||
source = "./child"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
terraform {}
|
|
@ -0,0 +1,69 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
)
|
||||
|
||||
// checkRequiredVersion verifies that any version requirements specified by
|
||||
// the configuration are met.
|
||||
//
|
||||
// This checks the root module as well as any additional version requirements
|
||||
// from child modules.
|
||||
//
|
||||
// This is tested in context_test.go.
|
||||
func checkRequiredVersion(m *module.Tree) error {
|
||||
// Check any children
|
||||
for _, c := range m.Children() {
|
||||
if err := checkRequiredVersion(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var tf *config.Terraform
|
||||
if c := m.Config(); c != nil {
|
||||
tf = c.Terraform
|
||||
}
|
||||
|
||||
// If there is no Terraform config or the required version isn't set,
|
||||
// we move on.
|
||||
if tf == nil || tf.RequiredVersion == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Path for errors
|
||||
module := "root"
|
||||
if path := normalizeModulePath(m.Path()); len(path) > 1 {
|
||||
module = modulePrefixStr(path)
|
||||
}
|
||||
|
||||
// Check this version requirement of this module
|
||||
cs, err := version.NewConstraint(tf.RequiredVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"%s: terraform.required_version %q syntax error: %s",
|
||||
module,
|
||||
tf.RequiredVersion, err)
|
||||
}
|
||||
|
||||
if !cs.Check(SemVersion) {
|
||||
return fmt.Errorf(
|
||||
"The currently running version of Terraform doesn't meet the\n"+
|
||||
"version requirements explicitly specified by the configuration.\n"+
|
||||
"Please use the required version or update the configuration.\n"+
|
||||
"Note that version requirements are usually set for a reason, so\n"+
|
||||
"we recommend verifying with whoever set the version requirements\n"+
|
||||
"prior to making any manual changes.\n\n"+
|
||||
" Module: %s\n"+
|
||||
" Required version: %s\n"+
|
||||
" Current version: %s",
|
||||
module,
|
||||
tf.RequiredVersion,
|
||||
SemVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue