/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go
https://github.com/alibaba/terraform-provider · Go · 228 lines · 159 code · 33 blank · 36 comment · 27 complexity · 39e7cd08a204e0ad8caaa04aec895f45 MD5 · raw file
- 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"`
- AgentIdentity interface{} `mapstructure:"agent_identity"`
- // 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,
- }
- }