terraform/terraform/eval_validate.go

149 lines
3.6 KiB
Go
Raw Normal View History

2015-02-05 00:44:23 +01:00
package terraform
2015-02-09 02:20:46 +01:00
import (
"fmt"
"github.com/hashicorp/terraform/config"
)
2015-02-05 00:44:23 +01:00
// EvalValidateError is the error structure returned if there were
// validation errors.
type EvalValidateError struct {
Warnings []string
Errors []error
}
func (e *EvalValidateError) Error() string {
return fmt.Sprintf("Warnings: %s. Errors: %s", e.Warnings, e.Errors)
2015-02-05 00:44:23 +01:00
}
2015-02-09 02:20:46 +01:00
// EvalValidateCount is an EvalNode implementation that validates
// the count of a resource.
type EvalValidateCount struct {
Resource *config.Resource
}
// TODO: test
2015-02-14 07:58:41 +01:00
func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) {
2015-02-09 02:20:46 +01:00
var count int
var errs []error
var err error
if _, err := ctx.Interpolate(n.Resource.RawCount, nil); err != nil {
errs = append(errs, fmt.Errorf(
"Failed to interpolate count: %s", err))
goto RETURN
}
count, err = n.Resource.Count()
if err != nil {
2015-02-10 21:16:55 +01:00
// If we can't get the count during validation, then
// just replace it with the number 1.
c := n.Resource.RawCount.Config()
c[n.Resource.RawCount.Key] = "1"
count = 1
2015-02-09 02:20:46 +01:00
}
if count < 0 {
errs = append(errs, fmt.Errorf(
"Count is less than zero: %d", count))
}
RETURN:
if len(errs) != 0 {
err = &EvalValidateError{
Errors: errs,
}
2015-02-09 02:20:46 +01:00
}
return nil, err
2015-02-09 02:20:46 +01:00
}
2015-02-05 02:23:26 +01:00
// EvalValidateProvider is an EvalNode implementation that validates
// the configuration of a resource.
type EvalValidateProvider struct {
Provider *ResourceProvider
Config **ResourceConfig
2015-02-05 02:23:26 +01:00
}
2015-02-14 07:58:41 +01:00
func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
provider := *n.Provider
config := *n.Config
2015-02-05 02:23:26 +01:00
warns, errs := provider.Validate(config)
if len(warns) == 0 && len(errs) == 0 {
return nil, nil
}
return nil, &EvalValidateError{
Warnings: warns,
Errors: errs,
}
}
2015-02-09 20:15:54 +01:00
// EvalValidateProvisioner is an EvalNode implementation that validates
// the configuration of a resource.
type EvalValidateProvisioner struct {
2015-02-14 07:58:41 +01:00
Provisioner *ResourceProvisioner
Config **ResourceConfig
2015-02-09 20:15:54 +01:00
}
2015-02-14 07:58:41 +01:00
func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
provisioner := *n.Provisioner
config := *n.Config
warns, errs := provisioner.Validate(config)
2015-02-09 20:15:54 +01:00
if len(warns) == 0 && len(errs) == 0 {
return nil, nil
}
return nil, &EvalValidateError{
Warnings: warns,
Errors: errs,
}
}
2015-02-05 00:44:23 +01:00
// EvalValidateResource is an EvalNode implementation that validates
// the configuration of a resource.
type EvalValidateResource struct {
2015-02-14 07:58:41 +01:00
Provider *ResourceProvider
Config **ResourceConfig
2015-02-09 18:50:20 +01:00
ResourceName string
ResourceType string
ResourceMode config.ResourceMode
core: rerun resource validation before plan and apply In #7170 we found two scenarios where the type checking done during the `context.Validate()` graph walk was circumvented, and the subsequent assumption of type safety in the provider's `Diff()` implementation caused panics. Both scenarios have to do with interpolations that reference Computed values. The sentinel we use to indicate that a value is Computed does not carry any type information with it yet. That means that an incorrect reference to a list or a map in a string attribute can "sneak through" validation only to crop up... 1. ...during Plan for Data Source References 2. ...during Apply for Resource references In order to address this, we: * add high-level tests for each of these two scenarios in `provider/test` * add context-level tests for the same two scenarios in `terraform` (these tests proved _really_ tricky to write!) * place an `EvalValidateResource` just before `EvalDiff` and `EvalApply` to catch these errors * add some plumbing to `Plan()` and `Apply()` to return validation errors, which were previously only generated during `Validate()` * wrap unit-tests around `EvalValidateResource` * add an `IgnoreWarnings` option to `EvalValidateResource` to prevent active warnings from halting execution on the second-pass validation Eventually, we might be able to attach type information to Computed values, which would allow for these errors to be caught earlier. For now, this solution keeps us safe from panics and raises the proper errors to the user. Fixes #7170
2016-07-01 01:22:20 +02:00
// IgnoreWarnings means that warnings will not be passed through. This allows
// "just-in-time" passes of validation to continue execution through warnings.
IgnoreWarnings bool
2015-02-05 00:44:23 +01:00
}
2015-02-14 07:58:41 +01:00
func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
provider := *n.Provider
cfg := *n.Config
var warns []string
var errs []error
// Provider entry point varies depending on resource mode, because
// managed resources and data resources are two distinct concepts
// in the provider abstraction.
switch n.ResourceMode {
case config.ManagedResourceMode:
warns, errs = provider.ValidateResource(n.ResourceType, cfg)
case config.DataResourceMode:
warns, errs = provider.ValidateDataSource(n.ResourceType, cfg)
}
2015-02-09 18:50:20 +01:00
core: rerun resource validation before plan and apply In #7170 we found two scenarios where the type checking done during the `context.Validate()` graph walk was circumvented, and the subsequent assumption of type safety in the provider's `Diff()` implementation caused panics. Both scenarios have to do with interpolations that reference Computed values. The sentinel we use to indicate that a value is Computed does not carry any type information with it yet. That means that an incorrect reference to a list or a map in a string attribute can "sneak through" validation only to crop up... 1. ...during Plan for Data Source References 2. ...during Apply for Resource references In order to address this, we: * add high-level tests for each of these two scenarios in `provider/test` * add context-level tests for the same two scenarios in `terraform` (these tests proved _really_ tricky to write!) * place an `EvalValidateResource` just before `EvalDiff` and `EvalApply` to catch these errors * add some plumbing to `Plan()` and `Apply()` to return validation errors, which were previously only generated during `Validate()` * wrap unit-tests around `EvalValidateResource` * add an `IgnoreWarnings` option to `EvalValidateResource` to prevent active warnings from halting execution on the second-pass validation Eventually, we might be able to attach type information to Computed values, which would allow for these errors to be caught earlier. For now, this solution keeps us safe from panics and raises the proper errors to the user. Fixes #7170
2016-07-01 01:22:20 +02:00
// If the resource name doesn't match the name regular
// expression, show an error.
2015-02-09 18:50:20 +01:00
if !config.NameRegexp.Match([]byte(n.ResourceName)) {
errs = append(errs, fmt.Errorf(
2015-02-09 18:50:20 +01:00
"%s: resource name can only contain letters, numbers, "+
core: rerun resource validation before plan and apply In #7170 we found two scenarios where the type checking done during the `context.Validate()` graph walk was circumvented, and the subsequent assumption of type safety in the provider's `Diff()` implementation caused panics. Both scenarios have to do with interpolations that reference Computed values. The sentinel we use to indicate that a value is Computed does not carry any type information with it yet. That means that an incorrect reference to a list or a map in a string attribute can "sneak through" validation only to crop up... 1. ...during Plan for Data Source References 2. ...during Apply for Resource references In order to address this, we: * add high-level tests for each of these two scenarios in `provider/test` * add context-level tests for the same two scenarios in `terraform` (these tests proved _really_ tricky to write!) * place an `EvalValidateResource` just before `EvalDiff` and `EvalApply` to catch these errors * add some plumbing to `Plan()` and `Apply()` to return validation errors, which were previously only generated during `Validate()` * wrap unit-tests around `EvalValidateResource` * add an `IgnoreWarnings` option to `EvalValidateResource` to prevent active warnings from halting execution on the second-pass validation Eventually, we might be able to attach type information to Computed values, which would allow for these errors to be caught earlier. For now, this solution keeps us safe from panics and raises the proper errors to the user. Fixes #7170
2016-07-01 01:22:20 +02:00
"dashes, and underscores.", n.ResourceName))
2015-02-09 18:50:20 +01:00
}
core: rerun resource validation before plan and apply In #7170 we found two scenarios where the type checking done during the `context.Validate()` graph walk was circumvented, and the subsequent assumption of type safety in the provider's `Diff()` implementation caused panics. Both scenarios have to do with interpolations that reference Computed values. The sentinel we use to indicate that a value is Computed does not carry any type information with it yet. That means that an incorrect reference to a list or a map in a string attribute can "sneak through" validation only to crop up... 1. ...during Plan for Data Source References 2. ...during Apply for Resource references In order to address this, we: * add high-level tests for each of these two scenarios in `provider/test` * add context-level tests for the same two scenarios in `terraform` (these tests proved _really_ tricky to write!) * place an `EvalValidateResource` just before `EvalDiff` and `EvalApply` to catch these errors * add some plumbing to `Plan()` and `Apply()` to return validation errors, which were previously only generated during `Validate()` * wrap unit-tests around `EvalValidateResource` * add an `IgnoreWarnings` option to `EvalValidateResource` to prevent active warnings from halting execution on the second-pass validation Eventually, we might be able to attach type information to Computed values, which would allow for these errors to be caught earlier. For now, this solution keeps us safe from panics and raises the proper errors to the user. Fixes #7170
2016-07-01 01:22:20 +02:00
if (len(warns) == 0 || n.IgnoreWarnings) && len(errs) == 0 {
return nil, nil
}
return nil, &EvalValidateError{
Warnings: warns,
Errors: errs,
}
2015-02-05 00:44:23 +01:00
}