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  `