github.com/magodo/terraform@v0.11.12-beta1/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  }