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