]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/eval_validate.go
3e5a84ce65e670ddd1ccfbb070b86593c4a41523
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / eval_validate.go
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 }