terraform: verify version requirements from configuration

This commit is contained in:
Mitchell Hashimoto 2016-11-12 16:50:26 -08:00
parent 85d3439fa0
commit 2c467e0f74
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
6 changed files with 159 additions and 0 deletions

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1 @@
terraform { required_version = ">= 0.5.0" }

View File

@ -0,0 +1,3 @@
module "child" {
source = "./child"
}

View File

@ -0,0 +1 @@
terraform {}

View File

@ -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
}