github.com/LorbusChris/terraform@v0.11.12-beta1/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  		HostKey           interface{} `mapstructure:"host_key"`
   148  		Agent             interface{} `mapstructure:"agent"`
   149  		BastionHost       interface{} `mapstructure:"bastion_host"`
   150  		BastionHostKey    interface{} `mapstructure:"bastion_host_key"`
   151  		BastionPort       interface{} `mapstructure:"bastion_port"`
   152  		BastionUser       interface{} `mapstructure:"bastion_user"`
   153  		BastionPassword   interface{} `mapstructure:"bastion_password"`
   154  		BastionPrivateKey interface{} `mapstructure:"bastion_private_key"`
   155  		AgentIdentity     interface{} `mapstructure:"agent_identity"`
   156  
   157  		// For type=winrm only (enforced in winrm communicator)
   158  		HTTPS    interface{} `mapstructure:"https"`
   159  		Insecure interface{} `mapstructure:"insecure"`
   160  		NTLM     interface{} `mapstructure:"use_ntlm"`
   161  		CACert   interface{} `mapstructure:"cacert"`
   162  	}
   163  
   164  	var metadata mapstructure.Metadata
   165  	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   166  		Metadata: &metadata,
   167  		Result:   &connConfigSuperset{}, // result is disregarded; we only care about unused keys
   168  	})
   169  	if err != nil {
   170  		// should never happen
   171  		errs = append(errs, err)
   172  		return
   173  	}
   174  
   175  	if err := decoder.Decode(connConfig.Config); err != nil {
   176  		errs = append(errs, err)
   177  		return
   178  	}
   179  
   180  	for _, attrName := range metadata.Unused {
   181  		errs = append(errs, fmt.Errorf("unknown 'connection' argument %q", attrName))
   182  	}
   183  	return
   184  }
   185  
   186  // EvalValidateResource is an EvalNode implementation that validates
   187  // the configuration of a resource.
   188  type EvalValidateResource struct {
   189  	Provider     *ResourceProvider
   190  	Config       **ResourceConfig
   191  	ResourceName string
   192  	ResourceType string
   193  	ResourceMode config.ResourceMode
   194  
   195  	// IgnoreWarnings means that warnings will not be passed through. This allows
   196  	// "just-in-time" passes of validation to continue execution through warnings.
   197  	IgnoreWarnings bool
   198  }
   199  
   200  func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
   201  	provider := *n.Provider
   202  	cfg := *n.Config
   203  	var warns []string
   204  	var errs []error
   205  	// Provider entry point varies depending on resource mode, because
   206  	// managed resources and data resources are two distinct concepts
   207  	// in the provider abstraction.
   208  	switch n.ResourceMode {
   209  	case config.ManagedResourceMode:
   210  		warns, errs = provider.ValidateResource(n.ResourceType, cfg)
   211  	case config.DataResourceMode:
   212  		warns, errs = provider.ValidateDataSource(n.ResourceType, cfg)
   213  	}
   214  
   215  	// If the resource name doesn't match the name regular
   216  	// expression, show an error.
   217  	if !config.NameRegexp.Match([]byte(n.ResourceName)) {
   218  		errs = append(errs, fmt.Errorf(
   219  			"%s: resource name can only contain letters, numbers, "+
   220  				"dashes, and underscores.", n.ResourceName))
   221  	}
   222  
   223  	if (len(warns) == 0 || n.IgnoreWarnings) && len(errs) == 0 {
   224  		return nil, nil
   225  	}
   226  
   227  	return nil, &EvalValidateError{
   228  		Warnings: warns,
   229  		Errors:   errs,
   230  	}
   231  }