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