github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/terraform/resource_provider.go (about) 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 }