github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/terraform/eval_validate.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/config"
     7  	"github.com/mitchellh/mapstructure"
     8  )
     9  
    10  // EvalValidateError is the error structure returned if there were
    11  // validation errors.
    12  type EvalValidateError struct {
    13  	Warnings []string
    14  	Errors   []error
    15  }
    16  
    17  func (e *EvalValidateError) Error() string {
    18  	return fmt.Sprintf("Warnings: %s. Errors: %s", e.Warnings, e.Errors)
    19  }
    20  
    21  // EvalValidateCount is an EvalNode implementation that validates
    22  // the count of a resource.
    23  type EvalValidateCount struct {
    24  	Resource *config.Resource
    25  }
    26  
    27  // TODO: test
    28  func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) {
    29  	var count int
    30  	var errs []error
    31  	var err error
    32  	if _, err := ctx.Interpolate(n.Resource.RawCount, nil); err != nil {
    33  		errs = append(errs, fmt.Errorf(
    34  			"Failed to interpolate count: %s", err))
    35  		goto RETURN
    36  	}
    37  
    38  	count, err = n.Resource.Count()
    39  	if err != nil {
    40  		// If we can't get the count during validation, then
    41  		// just replace it with the number 1.
    42  		c := n.Resource.RawCount.Config()
    43  		c[n.Resource.RawCount.Key] = "1"
    44  		count = 1
    45  	}
    46  	err = nil
    47  
    48  	if count < 0 {
    49  		errs = append(errs, fmt.Errorf(
    50  			"Count is less than zero: %d", count))
    51  	}
    52  
    53  RETURN:
    54  	if len(errs) != 0 {
    55  		err = &EvalValidateError{
    56  			Errors: errs,
    57  		}
    58  	}
    59  	return nil, err
    60  }
    61  
    62  // EvalValidateProvider is an EvalNode implementation that validates
    63  // the configuration of a resource.
    64  type EvalValidateProvider struct {
    65  	Provider *ResourceProvider
    66  	Config   **ResourceConfig
    67  }
    68  
    69  func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
    70  	provider := *n.Provider
    71  	config := *n.Config
    72  
    73  	warns, errs := provider.Validate(config)
    74  	if len(warns) == 0 && len(errs) == 0 {
    75  		return nil, nil
    76  	}
    77  
    78  	return nil, &EvalValidateError{
    79  		Warnings: warns,
    80  		Errors:   errs,
    81  	}
    82  }
    83  
    84  // EvalValidateProvisioner is an EvalNode implementation that validates
    85  // the configuration of a resource.
    86  type EvalValidateProvisioner struct {
    87  	Provisioner *ResourceProvisioner
    88  	Config      **ResourceConfig
    89  	ConnConfig  **ResourceConfig
    90  }
    91  
    92  func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
    93  	provisioner := *n.Provisioner
    94  	config := *n.Config
    95  	var warns []string
    96  	var errs []error
    97  
    98  	{
    99  		// Validate the provisioner's own config first
   100  		w, e := provisioner.Validate(config)
   101  		warns = append(warns, w...)
   102  		errs = append(errs, e...)
   103  	}
   104  
   105  	{
   106  		// Now validate the connection config, which might either be from
   107  		// the provisioner block itself or inherited from the resource's
   108  		// shared connection info.
   109  		w, e := n.validateConnConfig(*n.ConnConfig)
   110  		warns = append(warns, w...)
   111  		errs = append(errs, e...)
   112  	}
   113  
   114  	if len(warns) == 0 && len(errs) == 0 {
   115  		return nil, nil
   116  	}
   117  
   118  	return nil, &EvalValidateError{
   119  		Warnings: warns,
   120  		Errors:   errs,
   121  	}
   122  }
   123  
   124  func (n *EvalValidateProvisioner) validateConnConfig(connConfig *ResourceConfig) (warns []string, errs []error) {
   125  	// We can't comprehensively validate the connection config since its
   126  	// final structure is decided by the communicator and we can't instantiate
   127  	// that until we have a complete instance state. However, we *can* catch
   128  	// configuration keys that are not valid for *any* communicator, catching
   129  	// typos early rather than waiting until we actually try to run one of
   130  	// the resource's provisioners.
   131  
   132  	type connConfigSuperset struct {
   133  		// All attribute types are interface{} here because at this point we
   134  		// may still have unresolved interpolation expressions, which will
   135  		// appear as strings regardless of the final goal type.
   136  
   137  		Type       interface{} `mapstructure:"type"`
   138  		User       interface{} `mapstructure:"user"`
   139  		Password   interface{} `mapstructure:"password"`
   140  		Host       interface{} `mapstructure:"host"`
   141  		Port       interface{} `mapstructure:"port"`
   142  		Timeout    interface{} `mapstructure:"timeout"`
   143  		ScriptPath interface{} `mapstructure:"script_path"`
   144  
   145  		// For type=ssh only (enforced in ssh communicator)
   146  		PrivateKey        interface{} `mapstructure:"private_key"`
   147  		Agent             interface{} `mapstructure:"agent"`
   148  		BastionHost       interface{} `mapstructure:"bastion_host"`
   149  		BastionPort       interface{} `mapstructure:"bastion_port"`
   150  		BastionUser       interface{} `mapstructure:"bastion_user"`
   151  		BastionPassword   interface{} `mapstructure:"bastion_password"`
   152  		BastionPrivateKey interface{} `mapstructure:"bastion_private_key"`
   153  
   154  		// For type=winrm only (enforced in winrm communicator)
   155  		HTTPS    interface{} `mapstructure:"https"`
   156  		Insecure interface{} `mapstructure:"insecure"`
   157  		CACert   interface{} `mapstructure:"cacert"`
   158  	}
   159  
   160  	var metadata mapstructure.Metadata
   161  	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   162  		Metadata: &metadata,
   163  		Result:   &connConfigSuperset{}, // result is disregarded; we only care about unused keys
   164  	})
   165  	if err != nil {
   166  		// should never happen
   167  		errs = append(errs, err)
   168  		return
   169  	}
   170  
   171  	if err := decoder.Decode(connConfig.Config); err != nil {
   172  		errs = append(errs, err)
   173  		return
   174  	}
   175  
   176  	for _, attrName := range metadata.Unused {
   177  		errs = append(errs, fmt.Errorf("unknown 'connection' argument %q", attrName))
   178  	}
   179  	return
   180  }
   181  
   182  // EvalValidateResource is an EvalNode implementation that validates
   183  // the configuration of a resource.
   184  type EvalValidateResource struct {
   185  	Provider     *ResourceProvider
   186  	Config       **ResourceConfig
   187  	ResourceName string
   188  	ResourceType string
   189  	ResourceMode config.ResourceMode
   190  
   191  	// IgnoreWarnings means that warnings will not be passed through. This allows
   192  	// "just-in-time" passes of validation to continue execution through warnings.
   193  	IgnoreWarnings bool
   194  }
   195  
   196  func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
   197  	provider := *n.Provider
   198  	cfg := *n.Config
   199  	var warns []string
   200  	var errs []error
   201  	// Provider entry point varies depending on resource mode, because
   202  	// managed resources and data resources are two distinct concepts
   203  	// in the provider abstraction.
   204  	switch n.ResourceMode {
   205  	case config.ManagedResourceMode:
   206  		warns, errs = provider.ValidateResource(n.ResourceType, cfg)
   207  	case config.DataResourceMode:
   208  		warns, errs = provider.ValidateDataSource(n.ResourceType, cfg)
   209  	}
   210  
   211  	// If the resource name doesn't match the name regular
   212  	// expression, show an error.
   213  	if !config.NameRegexp.Match([]byte(n.ResourceName)) {
   214  		errs = append(errs, fmt.Errorf(
   215  			"%s: resource name can only contain letters, numbers, "+
   216  				"dashes, and underscores.", n.ResourceName))
   217  	}
   218  
   219  	if (len(warns) == 0 || n.IgnoreWarnings) && len(errs) == 0 {
   220  		return nil, nil
   221  	}
   222  
   223  	return nil, &EvalValidateError{
   224  		Warnings: warns,
   225  		Errors:   errs,
   226  	}
   227  }