diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/eval_validate.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/eval_validate.go | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go new file mode 100644 index 00000000..478aa640 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go @@ -0,0 +1,227 @@ +package terraform + +import ( + "fmt" + + "github.com/hashicorp/terraform/config" + "github.com/mitchellh/mapstructure" +) + +// 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) +} + +// EvalValidateCount is an EvalNode implementation that validates +// the count of a resource. +type EvalValidateCount struct { + Resource *config.Resource +} + +// TODO: test +func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) { + 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 { + // 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 + } + err = nil + + if count < 0 { + errs = append(errs, fmt.Errorf( + "Count is less than zero: %d", count)) + } + +RETURN: + if len(errs) != 0 { + err = &EvalValidateError{ + Errors: errs, + } + } + return nil, err +} + +// EvalValidateProvider is an EvalNode implementation that validates +// the configuration of a resource. +type EvalValidateProvider struct { + Provider *ResourceProvider + Config **ResourceConfig +} + +func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) { + provider := *n.Provider + config := *n.Config + + warns, errs := provider.Validate(config) + if len(warns) == 0 && len(errs) == 0 { + return nil, nil + } + + return nil, &EvalValidateError{ + Warnings: warns, + Errors: errs, + } +} + +// EvalValidateProvisioner is an EvalNode implementation that validates +// the configuration of a resource. +type EvalValidateProvisioner struct { + Provisioner *ResourceProvisioner + Config **ResourceConfig + ConnConfig **ResourceConfig +} + +func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) { + provisioner := *n.Provisioner + config := *n.Config + var warns []string + var errs []error + + { + // Validate the provisioner's own config first + w, e := provisioner.Validate(config) + warns = append(warns, w...) + errs = append(errs, e...) + } + + { + // Now validate the connection config, which might either be from + // the provisioner block itself or inherited from the resource's + // shared connection info. + w, e := n.validateConnConfig(*n.ConnConfig) + warns = append(warns, w...) + errs = append(errs, e...) + } + + if len(warns) == 0 && len(errs) == 0 { + return nil, nil + } + + return nil, &EvalValidateError{ + Warnings: warns, + Errors: errs, + } +} + +func (n *EvalValidateProvisioner) validateConnConfig(connConfig *ResourceConfig) (warns []string, errs []error) { + // We can't comprehensively validate the connection config since its + // final structure is decided by the communicator and we can't instantiate + // that until we have a complete instance state. However, we *can* catch + // configuration keys that are not valid for *any* communicator, catching + // typos early rather than waiting until we actually try to run one of + // the resource's provisioners. + + type connConfigSuperset struct { + // All attribute types are interface{} here because at this point we + // may still have unresolved interpolation expressions, which will + // appear as strings regardless of the final goal type. + + Type interface{} `mapstructure:"type"` + User interface{} `mapstructure:"user"` + Password interface{} `mapstructure:"password"` + Host interface{} `mapstructure:"host"` + Port interface{} `mapstructure:"port"` + Timeout interface{} `mapstructure:"timeout"` + ScriptPath interface{} `mapstructure:"script_path"` + + // For type=ssh only (enforced in ssh communicator) + PrivateKey interface{} `mapstructure:"private_key"` + Agent interface{} `mapstructure:"agent"` + BastionHost interface{} `mapstructure:"bastion_host"` + BastionPort interface{} `mapstructure:"bastion_port"` + BastionUser interface{} `mapstructure:"bastion_user"` + BastionPassword interface{} `mapstructure:"bastion_password"` + BastionPrivateKey interface{} `mapstructure:"bastion_private_key"` + + // For type=winrm only (enforced in winrm communicator) + HTTPS interface{} `mapstructure:"https"` + Insecure interface{} `mapstructure:"insecure"` + CACert interface{} `mapstructure:"cacert"` + } + + var metadata mapstructure.Metadata + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Metadata: &metadata, + Result: &connConfigSuperset{}, // result is disregarded; we only care about unused keys + }) + if err != nil { + // should never happen + errs = append(errs, err) + return + } + + if err := decoder.Decode(connConfig.Config); err != nil { + errs = append(errs, err) + return + } + + for _, attrName := range metadata.Unused { + errs = append(errs, fmt.Errorf("unknown 'connection' argument %q", attrName)) + } + return +} + +// EvalValidateResource is an EvalNode implementation that validates +// the configuration of a resource. +type EvalValidateResource struct { + Provider *ResourceProvider + Config **ResourceConfig + ResourceName string + ResourceType string + ResourceMode config.ResourceMode + + // IgnoreWarnings means that warnings will not be passed through. This allows + // "just-in-time" passes of validation to continue execution through warnings. + IgnoreWarnings bool +} + +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) + } + + // If the resource name doesn't match the name regular + // expression, show an error. + if !config.NameRegexp.Match([]byte(n.ResourceName)) { + errs = append(errs, fmt.Errorf( + "%s: resource name can only contain letters, numbers, "+ + "dashes, and underscores.", n.ResourceName)) + } + + if (len(warns) == 0 || n.IgnoreWarnings) && len(errs) == 0 { + return nil, nil + } + + return nil, &EvalValidateError{ + Warnings: warns, + Errors: errs, + } +} |