]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - terraform/resource_provider.go
Merge branch 'fix_read_test' of github.com:alexandreFre/terraform-provider-statuscake
[github/fretlink/terraform-provider-statuscake.git] / terraform / resource_provider.go
1 package terraform
2
3 import (
4 "fmt"
5
6 multierror "github.com/hashicorp/go-multierror"
7 "github.com/hashicorp/terraform/plugin/discovery"
8 )
9
10 // ResourceProvider is an interface that must be implemented by any
11 // resource provider: the thing that creates and manages the resources in
12 // a Terraform configuration.
13 //
14 // Important implementation note: All returned pointers, such as
15 // *ResourceConfig, *InstanceState, *InstanceDiff, etc. must not point to
16 // shared data. Terraform is highly parallel and assumes that this data is safe
17 // to read/write in parallel so it must be unique references. Note that it is
18 // safe to return arguments as results, however.
19 type ResourceProvider interface {
20 /*********************************************************************
21 * Functions related to the provider
22 *********************************************************************/
23
24 // ProviderSchema returns the config schema for the main provider
25 // configuration, as would appear in a "provider" block in the
26 // configuration files.
27 //
28 // Currently not all providers support schema. Callers must therefore
29 // first call Resources and DataSources and ensure that at least one
30 // resource or data source has the SchemaAvailable flag set.
31 GetSchema(*ProviderSchemaRequest) (*ProviderSchema, error)
32
33 // Input is called to ask the provider to ask the user for input
34 // for completing the configuration if necesarry.
35 //
36 // This may or may not be called, so resource provider writers shouldn't
37 // rely on this being available to set some default values for validate
38 // later. Example of a situation where this wouldn't be called is if
39 // the user is not using a TTY.
40 Input(UIInput, *ResourceConfig) (*ResourceConfig, error)
41
42 // Validate is called once at the beginning with the raw configuration
43 // (no interpolation done) and can return a list of warnings and/or
44 // errors.
45 //
46 // This is called once with the provider configuration only. It may not
47 // be called at all if no provider configuration is given.
48 //
49 // This should not assume that any values of the configurations are valid.
50 // The primary use case of this call is to check that required keys are
51 // set.
52 Validate(*ResourceConfig) ([]string, []error)
53
54 // Configure configures the provider itself with the configuration
55 // given. This is useful for setting things like access keys.
56 //
57 // This won't be called at all if no provider configuration is given.
58 //
59 // Configure returns an error if it occurred.
60 Configure(*ResourceConfig) error
61
62 // Resources returns all the available resource types that this provider
63 // knows how to manage.
64 Resources() []ResourceType
65
66 // Stop is called when the provider should halt any in-flight actions.
67 //
68 // This can be used to make a nicer Ctrl-C experience for Terraform.
69 // Even if this isn't implemented to do anything (just returns nil),
70 // Terraform will still cleanly stop after the currently executing
71 // graph node is complete. However, this API can be used to make more
72 // efficient halts.
73 //
74 // Stop doesn't have to and shouldn't block waiting for in-flight actions
75 // to complete. It should take any action it wants and return immediately
76 // acknowledging it has received the stop request. Terraform core will
77 // automatically not make any further API calls to the provider soon
78 // after Stop is called (technically exactly once the currently executing
79 // graph nodes are complete).
80 //
81 // The error returned, if non-nil, is assumed to mean that signaling the
82 // stop somehow failed and that the user should expect potentially waiting
83 // a longer period of time.
84 Stop() error
85
86 /*********************************************************************
87 * Functions related to individual resources
88 *********************************************************************/
89
90 // ValidateResource is called once at the beginning with the raw
91 // configuration (no interpolation done) and can return a list of warnings
92 // and/or errors.
93 //
94 // This is called once per resource.
95 //
96 // This should not assume any of the values in the resource configuration
97 // are valid since it is possible they have to be interpolated still.
98 // The primary use case of this call is to check that the required keys
99 // are set and that the general structure is correct.
100 ValidateResource(string, *ResourceConfig) ([]string, []error)
101
102 // Apply applies a diff to a specific resource and returns the new
103 // resource state along with an error.
104 //
105 // If the resource state given has an empty ID, then a new resource
106 // is expected to be created.
107 Apply(
108 *InstanceInfo,
109 *InstanceState,
110 *InstanceDiff) (*InstanceState, error)
111
112 // Diff diffs a resource versus a desired state and returns
113 // a diff.
114 Diff(
115 *InstanceInfo,
116 *InstanceState,
117 *ResourceConfig) (*InstanceDiff, error)
118
119 // Refresh refreshes a resource and updates all of its attributes
120 // with the latest information.
121 Refresh(*InstanceInfo, *InstanceState) (*InstanceState, error)
122
123 /*********************************************************************
124 * Functions related to importing
125 *********************************************************************/
126
127 // ImportState requests that the given resource be imported.
128 //
129 // The returned InstanceState only requires ID be set. Importing
130 // will always call Refresh after the state to complete it.
131 //
132 // IMPORTANT: InstanceState doesn't have the resource type attached
133 // to it. A type must be specified on the state via the Ephemeral
134 // field on the state.
135 //
136 // This function can return multiple states. Normally, an import
137 // will map 1:1 to a physical resource. However, some resources map
138 // to multiple. For example, an AWS security group may contain many rules.
139 // Each rule is represented by a separate resource in Terraform,
140 // therefore multiple states are returned.
141 ImportState(*InstanceInfo, string) ([]*InstanceState, error)
142
143 /*********************************************************************
144 * Functions related to data resources
145 *********************************************************************/
146
147 // ValidateDataSource is called once at the beginning with the raw
148 // configuration (no interpolation done) and can return a list of warnings
149 // and/or errors.
150 //
151 // This is called once per data source instance.
152 //
153 // This should not assume any of the values in the resource configuration
154 // are valid since it is possible they have to be interpolated still.
155 // The primary use case of this call is to check that the required keys
156 // are set and that the general structure is correct.
157 ValidateDataSource(string, *ResourceConfig) ([]string, []error)
158
159 // DataSources returns all of the available data sources that this
160 // provider implements.
161 DataSources() []DataSource
162
163 // ReadDataDiff produces a diff that represents the state that will
164 // be produced when the given data source is read using a later call
165 // to ReadDataApply.
166 ReadDataDiff(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error)
167
168 // ReadDataApply initializes a data instance using the configuration
169 // in a diff produced by ReadDataDiff.
170 ReadDataApply(*InstanceInfo, *InstanceDiff) (*InstanceState, error)
171 }
172
173 // ResourceProviderError may be returned when creating a Context if the
174 // required providers cannot be satisfied. This error can then be used to
175 // format a more useful message for the user.
176 type ResourceProviderError struct {
177 Errors []error
178 }
179
180 func (e *ResourceProviderError) Error() string {
181 // use multierror to format the default output
182 return multierror.Append(nil, e.Errors...).Error()
183 }
184
185 // ResourceProviderCloser is an interface that providers that can close
186 // connections that aren't needed anymore must implement.
187 type ResourceProviderCloser interface {
188 Close() error
189 }
190
191 // ResourceType is a type of resource that a resource provider can manage.
192 type ResourceType struct {
193 Name string // Name of the resource, example "instance" (no provider prefix)
194 Importable bool // Whether this resource supports importing
195
196 // SchemaAvailable is set if the provider supports the ProviderSchema,
197 // ResourceTypeSchema and DataSourceSchema methods. Although it is
198 // included on each resource type, it's actually a provider-wide setting
199 // that's smuggled here only because that avoids a breaking change to
200 // the plugin protocol.
201 SchemaAvailable bool
202 }
203
204 // DataSource is a data source that a resource provider implements.
205 type DataSource struct {
206 Name string
207
208 // SchemaAvailable is set if the provider supports the ProviderSchema,
209 // ResourceTypeSchema and DataSourceSchema methods. Although it is
210 // included on each resource type, it's actually a provider-wide setting
211 // that's smuggled here only because that avoids a breaking change to
212 // the plugin protocol.
213 SchemaAvailable bool
214 }
215
216 // ResourceProviderResolver is an interface implemented by objects that are
217 // able to resolve a given set of resource provider version constraints
218 // into ResourceProviderFactory callbacks.
219 type ResourceProviderResolver interface {
220 // Given a constraint map, return a ResourceProviderFactory for each
221 // requested provider. If some or all of the constraints cannot be
222 // satisfied, return a non-nil slice of errors describing the problems.
223 ResolveProviders(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error)
224 }
225
226 // ResourceProviderResolverFunc wraps a callback function and turns it into
227 // a ResourceProviderResolver implementation, for convenience in situations
228 // where a function and its associated closure are sufficient as a resolver
229 // implementation.
230 type ResourceProviderResolverFunc func(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error)
231
232 // ResolveProviders implements ResourceProviderResolver by calling the
233 // wrapped function.
234 func (f ResourceProviderResolverFunc) ResolveProviders(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error) {
235 return f(reqd)
236 }
237
238 // ResourceProviderResolverFixed returns a ResourceProviderResolver that
239 // has a fixed set of provider factories provided by the caller. The returned
240 // resolver ignores version constraints entirely and just returns the given
241 // factory for each requested provider name.
242 //
243 // This function is primarily used in tests, to provide mock providers or
244 // in-process providers under test.
245 func ResourceProviderResolverFixed(factories map[string]ResourceProviderFactory) ResourceProviderResolver {
246 return ResourceProviderResolverFunc(func(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error) {
247 ret := make(map[string]ResourceProviderFactory, len(reqd))
248 var errs []error
249 for name := range reqd {
250 if factory, exists := factories[name]; exists {
251 ret[name] = factory
252 } else {
253 errs = append(errs, fmt.Errorf("provider %q is not available", name))
254 }
255 }
256 return ret, errs
257 })
258 }
259
260 // ResourceProviderFactory is a function type that creates a new instance
261 // of a resource provider.
262 type ResourceProviderFactory func() (ResourceProvider, error)
263
264 // ResourceProviderFactoryFixed is a helper that creates a
265 // ResourceProviderFactory that just returns some fixed provider.
266 func ResourceProviderFactoryFixed(p ResourceProvider) ResourceProviderFactory {
267 return func() (ResourceProvider, error) {
268 return p, nil
269 }
270 }
271
272 func ProviderHasResource(p ResourceProvider, n string) bool {
273 for _, rt := range p.Resources() {
274 if rt.Name == n {
275 return true
276 }
277 }
278
279 return false
280 }
281
282 func ProviderHasDataSource(p ResourceProvider, n string) bool {
283 for _, rt := range p.DataSources() {
284 if rt.Name == n {
285 return true
286 }
287 }
288
289 return false
290 }
291
292 // resourceProviderFactories matches available plugins to the given version
293 // requirements to produce a map of compatible provider plugins if possible,
294 // or an error if the currently-available plugins are insufficient.
295 //
296 // This should be called only with configurations that have passed calls
297 // to config.Validate(), which ensures that all of the given version
298 // constraints are valid. It will panic if any invalid constraints are present.
299 func resourceProviderFactories(resolver ResourceProviderResolver, reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, error) {
300 ret, errs := resolver.ResolveProviders(reqd)
301 if errs != nil {
302 return nil, &ResourceProviderError{
303 Errors: errs,
304 }
305 }
306
307 return ret, nil
308 }