/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

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