return tfdiags.Diagnostics from validation methods

Validation is the best time to return detailed diagnostics
to the user since we're much more likely to have source
location information, etc than we are in later operations.

This change doesn't actually add any detail to the messages
yet, but it changes the interface so that we can gradually
introduce more detailed diagnostics over time.

While here there are some minor adjustments to some of the
messages to improve their consistency with terminology we
use elsewhere.
This commit is contained in:
Martin Atkins 2017-11-21 15:08:00 -08:00
parent 9f1a773655
commit ba0514106a
17 changed files with 426 additions and 530 deletions

View File

@ -2,12 +2,13 @@ package local
import (
"errors"
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/command/format"
"github.com/hashicorp/terraform/tfdiags"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
@ -91,27 +92,31 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, state.State,
// If validation is enabled, validate
if b.OpValidation {
// We ignore warnings here on purpose. We expect users to be listening
// to the terraform.Hook called after a validation.
ws, es := tfCtx.Validate()
if len(ws) > 0 {
// Log just in case the CLI isn't enabled
log.Printf("[WARN] backend/local: %d warnings: %v", len(ws), ws)
// If we have a CLI, output the warnings
if b.CLI != nil {
b.CLI.Warn(strings.TrimSpace(validateWarnHeader) + "\n")
for _, w := range ws {
b.CLI.Warn(fmt.Sprintf(" * %s", w))
}
// Make a newline before continuing
b.CLI.Output("")
diags := tfCtx.Validate()
if len(diags) > 0 {
if diags.HasErrors() {
// If there are warnings _and_ errors then we'll take this
// path and return them all together in this error.
return nil, nil, diags.Err()
}
}
if len(es) > 0 {
return nil, nil, multierror.Append(nil, es...)
// For now we can't propagate warnings any further without
// printing them directly to the UI, so we'll need to
// format them here ourselves.
for _, diag := range diags {
if diag.Severity() != tfdiags.Warning {
continue
}
if b.CLI != nil {
b.CLI.Warn(format.Diagnostic(diag, b.Colorize(), 72))
} else {
desc := diag.Description()
log.Printf("[WARN] backend/local: %s", desc.Summary)
}
}
// Make a newline before continuing
b.CLI.Output("")
}
}
}

View File

@ -7,7 +7,6 @@ import (
"runtime"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
)
// Set to true when we're testing
@ -82,39 +81,18 @@ func ModulePath(args []string) (string, error) {
return args[0], nil
}
func validateContext(ctx *terraform.Context, ui cli.Ui) bool {
func (m *Meta) validateContext(ctx *terraform.Context) bool {
log.Println("[INFO] Validating the context...")
ws, es := ctx.Validate()
log.Printf("[INFO] Validation result: %d warnings, %d errors", len(ws), len(es))
diags := ctx.Validate()
log.Printf("[INFO] Validation result: %d diagnostics", len(diags))
if len(ws) > 0 || len(es) > 0 {
ui.Output(
if len(diags) > 0 {
m.Ui.Output(
"There are warnings and/or errors related to your configuration. Please\n" +
"fix these before continuing.\n")
if len(ws) > 0 {
ui.Warn("Warnings:\n")
for _, w := range ws {
ui.Warn(fmt.Sprintf(" * %s", w))
}
if len(es) > 0 {
ui.Output("")
}
}
if len(es) > 0 {
ui.Error("Errors:\n")
for _, e := range es {
ui.Error(fmt.Sprintf(" * %s", e))
}
return false
} else {
ui.Warn(fmt.Sprintf("\n"+
"No errors found. Continuing with %d warning(s).\n", len(ws)))
return true
}
m.showDiagnostics(diags)
}
return true
return !diags.HasErrors()
}

View File

@ -280,7 +280,8 @@ func (c *InitCommand) getProviders(path string, state *terraform.State, upgrade
return err
}
if err := mod.Validate(); err != nil {
if diags := mod.Validate(); diags.HasErrors() {
err := diags.Err()
c.Ui.Error(fmt.Sprintf("Error getting plugins: %s", err))
return err
}

View File

@ -53,10 +53,11 @@ func (c *ProvidersCommand) Run(args []string) int {
}
// Validate the config (to ensure the version constraints are valid)
err = root.Validate()
if err != nil {
c.Ui.Error(err.Error())
return 1
if diags := root.Validate(); len(diags) != 0 {
c.showDiagnostics(diags)
if diags.HasErrors() {
return 1
}
}
// Load the backend

View File

@ -100,10 +100,11 @@ func (c *ValidateCommand) validate(dir string, checkVars bool) int {
c.showDiagnostics(err)
return 1
}
err = cfg.Validate()
if err != nil {
c.showDiagnostics(err)
return 1
if diags := cfg.Validate(); len(diags) != 0 {
c.showDiagnostics(diags)
if diags.HasErrors() {
return 1
}
}
if checkVars {
@ -122,7 +123,7 @@ func (c *ValidateCommand) validate(dir string, checkVars bool) int {
return 1
}
if !validateContext(tfCtx, c.Ui) {
if !c.validateContext(tfCtx) {
return 1
}
}

View File

@ -74,7 +74,7 @@ func TestValidateFailingCommandMissingVariable(t *testing.T) {
if code != 1 {
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
}
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "config: unknown variable referenced: 'description'. define it with 'variable' blocks") {
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "config: unknown variable referenced: 'description'; define it with a 'variable' block") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
}
@ -84,7 +84,7 @@ func TestSameProviderMutipleTimesShouldFail(t *testing.T) {
if code != 1 {
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
}
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "provider.aws: declared multiple times, you can only declare a provider once") {
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "provider.aws: multiple configurations present; only one configuration is allowed per provider") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
}
@ -94,7 +94,7 @@ func TestSameModuleMultipleTimesShouldFail(t *testing.T) {
if code != 1 {
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
}
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "multi_module: module repeated multiple times") {
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "module \"multi_module\": module repeated multiple times") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
}
@ -114,7 +114,7 @@ func TestOutputWithoutValueShouldFail(t *testing.T) {
if code != 1 {
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
}
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "output is missing required 'value' key") {
if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "output \"myvalue\": missing required 'value' argument") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
}
@ -125,7 +125,7 @@ func TestModuleWithIncorrectNameShouldFail(t *testing.T) {
t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String())
}
if !strings.Contains(ui.ErrorWriter.String(), "module name can only contain letters, numbers, dashes, and underscores") {
if !strings.Contains(ui.ErrorWriter.String(), "module name must be a letter or underscore followed by only letters, numbers, dashes, and underscores") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
if !strings.Contains(ui.ErrorWriter.String(), "module source cannot contain interpolations") {
@ -142,7 +142,7 @@ func TestWronglyUsedInterpolationShouldFail(t *testing.T) {
if !strings.Contains(ui.ErrorWriter.String(), "depends on value cannot contain interpolations") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
if !strings.Contains(ui.ErrorWriter.String(), "Variable 'vairable_with_interpolation': cannot contain interpolations") {
if !strings.Contains(ui.ErrorWriter.String(), "variable \"vairable_with_interpolation\": default may not contain interpolations") {
t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String())
}
}

View File

@ -8,10 +8,10 @@ import (
"strconv"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hil/ast"
"github.com/hashicorp/terraform/helper/hilmapstructure"
"github.com/hashicorp/terraform/plugin/discovery"
"github.com/hashicorp/terraform/tfdiags"
"github.com/mitchellh/reflectwalk"
)
@ -278,30 +278,35 @@ func ResourceProviderFullName(resourceType, explicitProvider string) string {
}
// Validate does some basic semantic checking of the configuration.
func (c *Config) Validate() error {
func (c *Config) Validate() tfdiags.Diagnostics {
if c == nil {
return nil
}
var errs []error
var diags tfdiags.Diagnostics
for _, k := range c.unknownKeys {
errs = append(errs, fmt.Errorf(
"Unknown root level key: %s", k))
diags = diags.Append(
fmt.Errorf("Unknown root level key: %s", k),
)
}
// Validate the Terraform config
if tf := c.Terraform; tf != nil {
errs = append(errs, c.Terraform.Validate()...)
errs := c.Terraform.Validate()
for _, err := range errs {
diags = diags.Append(err)
}
}
vars := c.InterpolatedVariables()
varMap := make(map[string]*Variable)
for _, v := range c.Variables {
if _, ok := varMap[v.Name]; ok {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"Variable '%s': duplicate found. Variable names must be unique.",
v.Name))
v.Name,
))
}
varMap[v.Name] = v
@ -309,17 +314,19 @@ func (c *Config) Validate() error {
for k, _ := range varMap {
if !NameRegexp.MatchString(k) {
errs = append(errs, fmt.Errorf(
"variable %q: variable name must match regular expresion %s",
k, NameRegexp))
diags = diags.Append(fmt.Errorf(
"variable %q: variable name must match regular expression %s",
k, NameRegexp,
))
}
}
for _, v := range c.Variables {
if v.Type() == VariableTypeUnknown {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"Variable '%s': must be a string or a map",
v.Name))
v.Name,
))
continue
}
@ -340,9 +347,10 @@ func (c *Config) Validate() error {
if v.Default != nil {
if err := reflectwalk.Walk(v.Default, w); err == nil {
if interp {
errs = append(errs, fmt.Errorf(
"Variable '%s': cannot contain interpolations",
v.Name))
diags = diags.Append(fmt.Errorf(
"variable %q: default may not contain interpolations",
v.Name,
))
}
}
}
@ -358,10 +366,11 @@ func (c *Config) Validate() error {
}
if _, ok := varMap[uv.Name]; !ok {
errs = append(errs, fmt.Errorf(
"%s: unknown variable referenced: '%s'. define it with 'variable' blocks",
diags = diags.Append(fmt.Errorf(
"%s: unknown variable referenced: '%s'; define it with a 'variable' block",
source,
uv.Name))
uv.Name,
))
}
}
}
@ -372,17 +381,19 @@ func (c *Config) Validate() error {
switch v := rawV.(type) {
case *CountVariable:
if v.Type == CountValueInvalid {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: invalid count variable: %s",
source,
v.FullKey()))
v.FullKey(),
))
}
case *PathVariable:
if v.Type == PathValueInvalid {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: invalid path variable: %s",
source,
v.FullKey()))
v.FullKey(),
))
}
}
}
@ -394,16 +405,17 @@ func (c *Config) Validate() error {
for _, p := range c.ProviderConfigs {
name := p.FullName()
if _, ok := providerSet[name]; ok {
errs = append(errs, fmt.Errorf(
"provider.%s: declared multiple times, you can only declare a provider once",
name))
diags = diags.Append(fmt.Errorf(
"provider.%s: multiple configurations present; only one configuration is allowed per provider",
name,
))
continue
}
if p.Version != "" {
_, err := discovery.ConstraintStr(p.Version).Parse()
if err != nil {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"provider.%s: invalid version constraint %q: %s",
name, p.Version, err,
))
@ -422,9 +434,10 @@ func (c *Config) Validate() error {
if _, ok := dupped[m.Id()]; !ok {
dupped[m.Id()] = struct{}{}
errs = append(errs, fmt.Errorf(
"%s: module repeated multiple times",
m.Id()))
diags = diags.Append(fmt.Errorf(
"module %q: module repeated multiple times",
m.Id(),
))
}
// Already seen this module, just skip it
@ -438,21 +451,23 @@ func (c *Config) Validate() error {
"root": m.Source,
})
if err != nil {
errs = append(errs, fmt.Errorf(
"%s: module source error: %s",
m.Id(), err))
diags = diags.Append(fmt.Errorf(
"module %q: module source error: %s",
m.Id(), err,
))
} else if len(rc.Interpolations) > 0 {
errs = append(errs, fmt.Errorf(
"%s: module source cannot contain interpolations",
m.Id()))
diags = diags.Append(fmt.Errorf(
"module %q: module source cannot contain interpolations",
m.Id(),
))
}
// Check that the name matches our regexp
if !NameRegexp.Match([]byte(m.Name)) {
errs = append(errs, fmt.Errorf(
"%s: module name can only contain letters, numbers, "+
"dashes, and underscores",
m.Id()))
diags = diags.Append(fmt.Errorf(
"module %q: module name must be a letter or underscore followed by only letters, numbers, dashes, and underscores",
m.Id(),
))
}
// Check that the configuration can all be strings, lists or maps
@ -476,36 +491,44 @@ func (c *Config) Validate() error {
continue
}
errs = append(errs, fmt.Errorf(
"%s: variable %s must be a string, list or map value",
m.Id(), k))
diags = diags.Append(fmt.Errorf(
"module %q: argument %s must have a string, list, or map value",
m.Id(), k,
))
}
// Check for invalid count variables
for _, v := range m.RawConfig.Variables {
switch v.(type) {
case *CountVariable:
errs = append(errs, fmt.Errorf(
"%s: count variables are only valid within resources", m.Name))
diags = diags.Append(fmt.Errorf(
"module %q: count variables are only valid within resources",
m.Name,
))
case *SelfVariable:
errs = append(errs, fmt.Errorf(
"%s: self variables are only valid within resources", m.Name))
diags = diags.Append(fmt.Errorf(
"module %q: self variables are only valid within resources",
m.Name,
))
}
}
// Update the raw configuration to only contain the string values
m.RawConfig, err = NewRawConfig(raw)
if err != nil {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: can't initialize configuration: %s",
m.Id(), err))
m.Id(), err,
))
}
// check that all named providers actually exist
for _, p := range m.Providers {
if !providerSet[p] {
errs = append(errs, fmt.Errorf(
"provider %q named in module %q does not exist", p, m.Name))
diags = diags.Append(fmt.Errorf(
"module %q: cannot pass non-existent provider %q",
m.Name, p,
))
}
}
@ -522,10 +545,10 @@ func (c *Config) Validate() error {
}
if _, ok := modules[mv.Name]; !ok {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: unknown module referenced: %s",
source,
mv.Name))
source, mv.Name,
))
}
}
}
@ -538,9 +561,10 @@ func (c *Config) Validate() error {
if _, ok := dupped[r.Id()]; !ok {
dupped[r.Id()] = struct{}{}
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: resource repeated multiple times",
r.Id()))
r.Id(),
))
}
}
@ -554,15 +578,15 @@ func (c *Config) Validate() error {
for _, v := range r.RawCount.Variables {
switch v.(type) {
case *CountVariable:
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: resource count can't reference count variable: %s",
n,
v.FullKey()))
n, v.FullKey(),
))
case *SimpleVariable:
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: resource count can't reference variable: %s",
n,
v.FullKey()))
n, v.FullKey(),
))
// Good
case *ModuleVariable:
@ -572,21 +596,24 @@ func (c *Config) Validate() error {
case *LocalVariable:
default:
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"Internal error. Unknown type in count var in %s: %T",
n, v))
n, v,
))
}
}
if !r.RawCount.couldBeInteger() {
errs = append(errs, fmt.Errorf(
"%s: resource count must be an integer",
n))
diags = diags.Append(fmt.Errorf(
"%s: resource count must be an integer", n,
))
}
r.RawCount.init()
// Validate DependsOn
errs = append(errs, c.validateDependsOn(n, r.DependsOn, resources, modules)...)
for _, err := range c.validateDependsOn(n, r.DependsOn, resources, modules) {
diags = diags.Append(err)
}
// Verify provisioners
for _, p := range r.Provisioners {
@ -600,9 +627,10 @@ func (c *Config) Validate() error {
}
if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name {
errs = append(errs, fmt.Errorf(
"%s: connection info cannot contain splat variable "+
"referencing itself", n))
diags = diags.Append(fmt.Errorf(
"%s: connection info cannot contain splat variable referencing itself",
n,
))
break
}
}
@ -614,9 +642,10 @@ func (c *Config) Validate() error {
}
if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name {
errs = append(errs, fmt.Errorf(
"%s: connection info cannot contain splat variable "+
"referencing itself", n))
diags = diags.Append(fmt.Errorf(
"%s: connection info cannot contain splat variable referencing itself",
n,
))
break
}
}
@ -624,21 +653,24 @@ func (c *Config) Validate() error {
// Check for invalid when/onFailure values, though this should be
// picked up by the loader we check here just in case.
if p.When == ProvisionerWhenInvalid {
errs = append(errs, fmt.Errorf(
"%s: provisioner 'when' value is invalid", n))
diags = diags.Append(fmt.Errorf(
"%s: provisioner 'when' value is invalid", n,
))
}
if p.OnFailure == ProvisionerOnFailureInvalid {
errs = append(errs, fmt.Errorf(
"%s: provisioner 'on_failure' value is invalid", n))
diags = diags.Append(fmt.Errorf(
"%s: provisioner 'on_failure' value is invalid", n,
))
}
}
// Verify ignore_changes contains valid entries
for _, v := range r.Lifecycle.IgnoreChanges {
if strings.Contains(v, "*") && v != "*" {
errs = append(errs, fmt.Errorf(
"%s: ignore_changes does not support using a partial string "+
"together with a wildcard: %s", n, v))
diags = diags.Append(fmt.Errorf(
"%s: ignore_changes does not support using a partial string together with a wildcard: %s",
n, v,
))
}
}
@ -647,21 +679,24 @@ func (c *Config) Validate() error {
"root": r.Lifecycle.IgnoreChanges,
})
if err != nil {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: lifecycle ignore_changes error: %s",
n, err))
n, err,
))
} else if len(rc.Interpolations) > 0 {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: lifecycle ignore_changes cannot contain interpolations",
n))
n,
))
}
// If it is a data source then it can't have provisioners
if r.Mode == DataResourceMode {
if _, ok := r.RawConfig.Raw["provisioner"]; ok {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: data sources cannot have provisioners",
n))
n,
))
}
}
}
@ -675,11 +710,12 @@ func (c *Config) Validate() error {
id := rv.ResourceId()
if _, ok := resources[id]; !ok {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: unknown resource '%s' referenced in variable %s",
source,
id,
rv.FullKey()))
rv.FullKey(),
))
continue
}
}
@ -690,7 +726,7 @@ func (c *Config) Validate() error {
found := make(map[string]struct{})
for _, l := range c.Locals {
if _, ok := found[l.Name]; ok {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"%s: duplicate local. local value names must be unique",
l.Name,
))
@ -700,7 +736,7 @@ func (c *Config) Validate() error {
for _, v := range l.RawConfig.Variables {
if _, ok := v.(*CountVariable); ok {
errs = append(errs, fmt.Errorf(
diags = diags.Append(fmt.Errorf(
"local %s: count variables are only valid within resources", l.Name,
))
}
@ -714,9 +750,10 @@ func (c *Config) Validate() error {
for _, o := range c.Outputs {
// Verify the output is new
if _, ok := found[o.Name]; ok {
errs = append(errs, fmt.Errorf(
"%s: duplicate output. output names must be unique.",
o.Name))
diags = diags.Append(fmt.Errorf(
"output %q: an output of this name was already defined",
o.Name,
))
continue
}
found[o.Name] = struct{}{}
@ -736,9 +773,10 @@ func (c *Config) Validate() error {
continue
}
errs = append(errs, fmt.Errorf(
"%s: value for 'sensitive' must be boolean",
o.Name))
diags = diags.Append(fmt.Errorf(
"output %q: value for 'sensitive' must be boolean",
o.Name,
))
continue
}
if k == "description" {
@ -747,29 +785,35 @@ func (c *Config) Validate() error {
continue
}
errs = append(errs, fmt.Errorf(
"%s: value for 'description' must be string",
o.Name))
diags = diags.Append(fmt.Errorf(
"output %q: value for 'description' must be string",
o.Name,
))
continue
}
invalidKeys = append(invalidKeys, k)
}
if len(invalidKeys) > 0 {
errs = append(errs, fmt.Errorf(
"%s: output has invalid keys: %s",
o.Name, strings.Join(invalidKeys, ", ")))
diags = diags.Append(fmt.Errorf(
"output %q: invalid keys: %s",
o.Name, strings.Join(invalidKeys, ", "),
))
}
if !valueKeyFound {
errs = append(errs, fmt.Errorf(
"%s: output is missing required 'value' key", o.Name))
diags = diags.Append(fmt.Errorf(
"output %q: missing required 'value' argument", o.Name,
))
}
for _, v := range o.RawConfig.Variables {
if _, ok := v.(*CountVariable); ok {
errs = append(errs, fmt.Errorf(
"%s: count variables are only valid within resources", o.Name))
diags = diags.Append(fmt.Errorf(
"output %q: count variables are only valid within resources",
o.Name,
))
}
}
}
}
@ -783,17 +827,15 @@ func (c *Config) Validate() error {
for _, v := range rc.Variables {
if _, ok := v.(*SelfVariable); ok {
errs = append(errs, fmt.Errorf(
"%s: cannot contain self-reference %s", source, v.FullKey()))
diags = diags.Append(fmt.Errorf(
"%s: cannot contain self-reference %s",
source, v.FullKey(),
))
}
}
}
if len(errs) > 0 {
return &multierror.Error{Errors: errs}
}
return nil
return diags
}
// InterpolatedVariables is a helper that returns a mapping of all the interpolated

View File

@ -223,20 +223,21 @@ func TestConfigValidate_table(t *testing.T) {
"invalid provider name in module block",
"validate-missing-provider",
true,
"does not exist",
"cannot pass non-existent provider",
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
c := testConfig(t, tc.Fixture)
err := c.Validate()
if (err != nil) != tc.Err {
t.Fatalf("err: %s", err)
diags := c.Validate()
if diags.HasErrors() != tc.Err {
t.Fatalf("err: %s", diags.Err().Error())
}
if err != nil {
if tc.ErrString != "" && !strings.Contains(err.Error(), tc.ErrString) {
t.Fatalf("expected err to contain: %s\n\ngot: %s", tc.ErrString, err)
if diags.HasErrors() {
gotErr := diags.Err().Error()
if tc.ErrString != "" && !strings.Contains(gotErr, tc.ErrString) {
t.Fatalf("expected err to contain: %s\n\ngot: %s", tc.ErrString, gotErr)
}
return
@ -291,15 +292,16 @@ func TestConfigValidate_countInt_HCL2(t *testing.T) {
func TestConfigValidate_countBadContext(t *testing.T) {
c := testConfig(t, "validate-count-bad-context")
err := c.Validate()
diags := c.Validate()
expected := []string{
"no_count_in_output: count variables are only valid within resources",
"no_count_in_module: count variables are only valid within resources",
"output \"no_count_in_output\": count variables are only valid within resources",
"module \"no_count_in_module\": count variables are only valid within resources",
}
for _, exp := range expected {
if !strings.Contains(err.Error(), exp) {
t.Fatalf("expected: %q,\nto contain: %q", err, exp)
errStr := diags.Err().Error()
if !strings.Contains(errStr, exp) {
t.Errorf("expected: %q,\nto contain: %q", errStr, exp)
}
}
}

View File

@ -9,6 +9,8 @@ import (
"strings"
"sync"
"github.com/hashicorp/terraform/tfdiags"
getter "github.com/hashicorp/go-getter"
"github.com/hashicorp/terraform/config"
)
@ -383,32 +385,35 @@ func (t *Tree) String() string {
// as verifying things such as parameters/outputs between the various modules.
//
// Load must be called prior to calling Validate or an error will be returned.
func (t *Tree) Validate() error {
if !t.Loaded() {
return fmt.Errorf("tree must be loaded before calling Validate")
}
func (t *Tree) Validate() tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
// If something goes wrong, here is our error template
newErr := &treeError{Name: []string{t.Name()}}
if !t.Loaded() {
diags = diags.Append(fmt.Errorf(
"tree must be loaded before calling Validate",
))
return diags
}
// Terraform core does not handle root module children named "root".
// We plan to fix this in the future but this bug was brought up in
// the middle of a release and we don't want to introduce wide-sweeping
// changes at that time.
if len(t.path) == 1 && t.name == "root" {
return fmt.Errorf("root module cannot contain module named 'root'")
diags = diags.Append(fmt.Errorf(
"root module cannot contain module named 'root'",
))
return diags
}
// Validate our configuration first.
if err := t.config.Validate(); err != nil {
newErr.Add(err)
}
diags = diags.Append(t.config.Validate())
// If we're the root, we do extra validation. This validation usually
// requires the entire tree (since children don't have parent pointers).
if len(t.path) == 0 {
if err := t.validateProviderAlias(); err != nil {
newErr.Add(err)
diags = diags.Append(err)
}
}
@ -417,20 +422,11 @@ func (t *Tree) Validate() error {
// Validate all our children
for _, c := range children {
err := c.Validate()
if err == nil {
childDiags := c.Validate()
diags = diags.Append(childDiags)
if diags.HasErrors() {
continue
}
verr, ok := err.(*treeError)
if !ok {
// Unknown error, just return...
return err
}
// Append ourselves to the error and then return
verr.Name = append(verr.Name, t.Name())
newErr.AddChild(verr)
}
// Go over all the modules and verify that any parameters are valid
@ -456,9 +452,10 @@ func (t *Tree) Validate() error {
// Compare to the keys in our raw config for the module
for k, _ := range m.RawConfig.Raw {
if _, ok := varMap[k]; !ok {
newErr.Add(fmt.Errorf(
"module %s: %s is not a valid parameter",
m.Name, k))
diags = diags.Append(fmt.Errorf(
"module %q: %q is not a valid argument",
m.Name, k,
))
}
// Remove the required
@ -467,9 +464,10 @@ func (t *Tree) Validate() error {
// If we have any required left over, they aren't set.
for k, _ := range requiredMap {
newErr.Add(fmt.Errorf(
"module %s: required variable %q not set",
m.Name, k))
diags = diags.Append(fmt.Errorf(
"module %q: missing required argument %q",
m.Name, k,
))
}
}
@ -484,9 +482,10 @@ func (t *Tree) Validate() error {
tree, ok := children[mv.Name]
if !ok {
newErr.Add(fmt.Errorf(
"%s: undefined module referenced %s",
source, mv.Name))
diags = diags.Append(fmt.Errorf(
"%s: reference to undefined module %q",
source, mv.Name,
))
continue
}
@ -498,14 +497,15 @@ func (t *Tree) Validate() error {
}
}
if !found {
newErr.Add(fmt.Errorf(
"%s: %s is not a valid output for module %s",
source, mv.Field, mv.Name))
diags = diags.Append(fmt.Errorf(
"%s: %q is not a valid output for module %q",
source, mv.Field, mv.Name,
))
}
}
}
return newErr.ErrOrNil()
return diags
}
// versionedPathKey returns a path string with every levels full name, version

View File

@ -404,15 +404,15 @@ func TestTreeValidate_table(t *testing.T) {
t.Fatalf("err: %s", err)
}
err := tree.Validate()
if (err != nil) != (tc.Err != "") {
t.Fatalf("err: %s", err)
diags := tree.Validate()
if (diags.HasErrors()) != (tc.Err != "") {
t.Fatalf("err: %s", diags.Err())
}
if err == nil {
if len(diags) == 0 {
return
}
if !strings.Contains(err.Error(), tc.Err) {
t.Fatalf("err should contain %q: %s", tc.Err, err)
if !strings.Contains(diags.Err().Error(), tc.Err) {
t.Fatalf("err should contain %q: %s", tc.Err, diags.Err().Error())
}
})
}
@ -519,13 +519,13 @@ func TestTreeValidate_requiredChildVar(t *testing.T) {
t.Fatalf("err: %s", err)
}
err := tree.Validate()
if err == nil {
diags := tree.Validate()
if !diags.HasErrors() {
t.Fatal("should error")
}
// ensure both variables are mentioned in the output
errMsg := err.Error()
errMsg := diags.Err().Error()
for _, v := range []string{"feature", "memory"} {
if !strings.Contains(errMsg, v) {
t.Fatalf("no mention of missing variable %q", v)

View File

@ -15,6 +15,7 @@ import (
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/logutils"
"github.com/hashicorp/terraform/config/module"
@ -662,18 +663,12 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r
if err != nil {
return err
}
if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
if len(es) > 0 {
estrs := make([]string, len(es))
for i, e := range es {
estrs[i] = e.Error()
}
return fmt.Errorf(
"Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
ws, estrs)
if diags := ctx.Validate(); len(diags) > 0 {
if diags.HasErrors() {
return errwrap.Wrapf("config is invalid: {{err}}", diags.Err())
}
log.Printf("[WARN] Config warnings: %#v", ws)
log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error())
}
// Refresh!

View File

@ -5,6 +5,7 @@ import (
"log"
"strings"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/terraform"
)
@ -33,17 +34,12 @@ func testStep(
if err != nil {
return state, fmt.Errorf("Error initializing context: %s", err)
}
if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
if len(es) > 0 {
estrs := make([]string, len(es))
for i, e := range es {
estrs[i] = e.Error()
}
return state, fmt.Errorf(
"Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
ws, estrs)
if diags := ctx.Validate(); len(diags) > 0 {
if diags.HasErrors() {
return nil, errwrap.Wrapf("config is invalid: {{err}}", diags.Err())
}
log.Printf("[WARN] Config warnings: %#v", ws)
log.Printf("[WARN] Config warnings:\n%s", diags)
}
// Refresh!

View File

@ -8,6 +8,8 @@ import (
"strings"
"sync"
"github.com/hashicorp/terraform/tfdiags"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl"
"github.com/hashicorp/terraform/config"
@ -671,29 +673,27 @@ func (c *Context) Stop() {
}
// Validate validates the configuration and returns any warnings or errors.
func (c *Context) Validate() ([]string, []error) {
func (c *Context) Validate() tfdiags.Diagnostics {
defer c.acquireRun("validate")()
var errs error
var diags tfdiags.Diagnostics
// Validate the configuration itself
if err := c.module.Validate(); err != nil {
errs = multierror.Append(errs, err)
}
diags = diags.Append(c.module.Validate())
// This only needs to be done for the root module, since inter-module
// variables are validated in the module tree.
if config := c.module.Config(); config != nil {
// Validate the user variables
if err := smcUserVariables(config, c.variables); len(err) > 0 {
errs = multierror.Append(errs, err...)
for _, err := range smcUserVariables(config, c.variables) {
diags = diags.Append(err)
}
}
// If we have errors at this point, the graphing has no chance,
// so just bail early.
if errs != nil {
return nil, []error{errs}
if diags.HasErrors() {
return diags
}
// Build the graph so we can walk it and run Validate on nodes.
@ -702,24 +702,29 @@ func (c *Context) Validate() ([]string, []error) {
// graph again later after Planning.
graph, err := c.Graph(GraphTypeValidate, nil)
if err != nil {
return nil, []error{err}
diags = diags.Append(err)
return diags
}
// Walk
walker, err := c.walk(graph, walkValidate)
if err != nil {
return nil, multierror.Append(errs, err).Errors
diags = diags.Append(err)
}
// Return the result
rerrs := multierror.Append(errs, walker.ValidationErrors...)
sort.Strings(walker.ValidationWarnings)
sort.Slice(rerrs.Errors, func(i, j int) bool {
return rerrs.Errors[i].Error() < rerrs.Errors[j].Error()
sort.Slice(walker.ValidationErrors, func(i, j int) bool {
return walker.ValidationErrors[i].Error() < walker.ValidationErrors[j].Error()
})
return walker.ValidationWarnings, rerrs.Errors
for _, warn := range walker.ValidationWarnings {
diags = diags.Append(tfdiags.SimpleWarning(warn))
}
for _, err := range walker.ValidationErrors {
diags = diags.Append(err)
}
return diags
}
// Module returns the module tree associated with this context.

View File

@ -7966,12 +7966,9 @@ func TestContext2Apply_vars(t *testing.T) {
},
})
w, e := ctx.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := ctx.Validate()
if len(diags) != 0 {
t.Fatalf("bad: %#v", diags)
}
if _, err := ctx.Plan(); err != nil {
@ -8009,12 +8006,9 @@ func TestContext2Apply_varsEnv(t *testing.T) {
),
})
w, e := ctx.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := ctx.Validate()
if len(diags) != 0 {
t.Fatalf("bad: %#v", diags)
}
if _, err := ctx.Plan(); err != nil {

View File

@ -3499,12 +3499,9 @@ func TestContext2Plan_resourceNestedCount(t *testing.T) {
State: s,
})
w, e := ctx.Validate()
if len(w) > 0 {
t.Fatalf("warnings generated on validate: %#v", w)
}
if len(e) > 0 {
t.Fatalf("errors generated on validate: %#v", e)
diags := ctx.Validate()
if len(diags) != 0 {
t.Fatalf("bad: %#v", diags)
}
_, err := ctx.Refresh()
@ -3582,12 +3579,9 @@ output "out" {
})
// if this ever fails to pass validate, add a resource to reference in the config
w, e := ctx.Validate()
if len(w) > 0 {
t.Fatalf("warnings generated on validate: %#v", w)
}
if len(e) > 0 {
t.Fatalf("errors generated on validate: %v", e)
diags := ctx.Validate()
if len(diags) != 0 {
t.Fatalf("bad: %#v", diags)
}
_, err := ctx.Refresh()
@ -3630,12 +3624,9 @@ resource "aws_instance" "foo" {
})
// if this ever fails to pass validate, add a resource to reference in the config
w, e := ctx.Validate()
if len(w) > 0 {
t.Fatalf("warnings generated on validate: %#v", w)
}
if len(e) > 0 {
t.Fatalf("errors generated on validate: %v", e)
diags := ctx.Validate()
if len(diags) != 0 {
t.Fatalf("bad: %#v", diags)
}
_, err := ctx.Refresh()

View File

@ -1042,12 +1042,9 @@ func TestContext2Validate(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if len(diags) != 0 {
t.Fatalf("bad: %#v", diags)
}
}

View File

@ -18,12 +18,9 @@ func TestContext2Validate_badCount(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -39,12 +36,9 @@ func TestContext2Validate_badVar(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -63,12 +57,9 @@ func TestContext2Validate_varMapOverrideOld(t *testing.T) {
},
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -78,12 +69,9 @@ func TestContext2Validate_varNoDefaultExplicitType(t *testing.T) {
Module: m,
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -112,14 +100,9 @@ func TestContext2Validate_computedVar(t *testing.T) {
return fmt.Errorf("Configure should not be called for provider")
}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
for _, err := range e {
t.Errorf("bad: %s", err)
}
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -138,12 +121,9 @@ func TestContext2Validate_countComputed(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -159,12 +139,9 @@ func TestContext2Validate_countNegative(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -180,12 +157,9 @@ func TestContext2Validate_countVariable(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -201,12 +175,9 @@ func TestContext2Validate_countVariableNoDefault(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) != 1 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -224,12 +195,9 @@ func TestContext2Validate_cycle(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("expected no warns, got: %#v", w)
}
if len(e) != 1 {
t.Fatalf("expected 1 err, got: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
*/
@ -246,12 +214,9 @@ func TestContext2Validate_moduleBadOutput(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -267,12 +232,9 @@ func TestContext2Validate_moduleGood(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -290,12 +252,9 @@ func TestContext2Validate_moduleBadResource(t *testing.T) {
p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -311,13 +270,9 @@ func TestContext2Validate_moduleDepsShouldNotCycle(t *testing.T) {
),
})
w, e := ctx.Validate()
if len(w) > 0 {
t.Fatalf("expected no warnings, got: %s", w)
}
if len(e) > 0 {
t.Fatalf("expected no errors, got: %s", e)
diags := ctx.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -360,12 +315,9 @@ func TestContext2Validate_moduleProviderInheritOrphan(t *testing.T) {
return nil, nil
}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -388,12 +340,9 @@ func TestContext2Validate_moduleProviderVar(t *testing.T) {
return nil, c.CheckSet([]string{"foo"})
}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -413,12 +362,9 @@ func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) {
return nil, c.CheckSet([]string{"foo"})
}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -455,12 +401,9 @@ func TestContext2Validate_orphans(t *testing.T) {
return nil, c.CheckSet([]string{"foo"})
}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -478,15 +421,12 @@ func TestContext2Validate_providerConfig_bad(t *testing.T) {
p.ValidateReturnErrors = []error{fmt.Errorf("bad")}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
diags := c.Validate()
if len(diags) != 1 {
t.Fatalf("wrong number of diagnostics %d; want %d", len(diags), 1)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
}
if !strings.Contains(fmt.Sprintf("%s", e), "bad") {
t.Fatalf("bad: %s", e)
if !strings.Contains(diags.Err().Error(), "bad") {
t.Fatalf("bad: %s", diags.Err().Error())
}
}
@ -504,12 +444,9 @@ func TestContext2Validate_providerConfig_badEmpty(t *testing.T) {
p.ValidateReturnErrors = []error{fmt.Errorf("bad")}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -525,12 +462,9 @@ func TestContext2Validate_providerConfig_good(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -552,12 +486,9 @@ func TestContext2Validate_provisionerConfig_bad(t *testing.T) {
pr.ValidateReturnErrors = []error{fmt.Errorf("bad")}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -583,12 +514,9 @@ func TestContext2Validate_provisionerConfig_good(t *testing.T) {
},
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -604,12 +532,9 @@ func TestContext2Validate_requiredVar(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -627,12 +552,9 @@ func TestContext2Validate_resourceConfig_bad(t *testing.T) {
p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -648,12 +570,9 @@ func TestContext2Validate_resourceConfig_good(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -669,12 +588,9 @@ func TestContext2Validate_resourceNameSymbol(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -690,12 +606,9 @@ func TestContext2Validate_selfRef(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %s", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -711,12 +624,9 @@ func TestContext2Validate_selfRefMulti(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -732,12 +642,9 @@ func TestContext2Validate_selfRefMultiAll(t *testing.T) {
),
})
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) == 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -775,12 +682,9 @@ func TestContext2Validate_tainted(t *testing.T) {
return nil, c.CheckSet([]string{"foo"})
}
w, e := c.Validate()
if len(w) > 0 {
t.Fatalf("bad: %#v", w)
}
if len(e) > 0 {
t.Fatalf("bad: %#v", e)
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -815,16 +719,9 @@ func TestContext2Validate_targetedDestroy(t *testing.T) {
Destroy: true,
})
w, e := ctx.Validate()
if len(w) > 0 {
warnStr := ""
for _, v := range w {
warnStr = warnStr + " " + v
}
t.Fatalf("bad: %s", warnStr)
}
if len(e) > 0 {
t.Fatalf("bad: %s", e)
diags := ctx.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -875,12 +772,9 @@ func TestContext2Validate_interpolateVar(t *testing.T) {
UIInput: input,
})
w, e := ctx.Validate()
if w != nil {
t.Log("warnings:", w)
}
if e != nil {
t.Fatal("err:", e)
diags := ctx.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -904,12 +798,9 @@ func TestContext2Validate_interpolateComputedModuleVarDef(t *testing.T) {
UIInput: input,
})
w, e := ctx.Validate()
if w != nil {
t.Log("warnings:", w)
}
if e != nil {
t.Fatal("err:", e)
diags := ctx.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}
@ -932,12 +823,9 @@ func TestContext2Validate_interpolateMap(t *testing.T) {
UIInput: input,
})
w, e := ctx.Validate()
if w != nil {
t.Log("warnings:", w)
}
if e != nil {
t.Fatal("err:", e)
diags := ctx.Validate()
if diags.HasErrors() {
t.Fatalf("bad: %#v", diags)
}
}