github.com/rjeczalik/terraform@v0.6.7-0.20160812060014-e251d5c7bd39/helper/schema/provider.go (about)

     1  package schema
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/hashicorp/terraform/terraform"
     9  )
    10  
    11  // Provider represents a resource provider in Terraform, and properly
    12  // implements all of the ResourceProvider API.
    13  //
    14  // By defining a schema for the configuration of the provider, the
    15  // map of supporting resources, and a configuration function, the schema
    16  // framework takes over and handles all the provider operations for you.
    17  //
    18  // After defining the provider structure, it is unlikely that you'll require any
    19  // of the methods on Provider itself.
    20  type Provider struct {
    21  	// Schema is the schema for the configuration of this provider. If this
    22  	// provider has no configuration, this can be omitted.
    23  	//
    24  	// The keys of this map are the configuration keys, and the value is
    25  	// the schema describing the value of the configuration.
    26  	Schema map[string]*Schema
    27  
    28  	// ResourcesMap is the list of available resources that this provider
    29  	// can manage, along with their Resource structure defining their
    30  	// own schemas and CRUD operations.
    31  	//
    32  	// Provider automatically handles routing operations such as Apply,
    33  	// Diff, etc. to the proper resource.
    34  	ResourcesMap map[string]*Resource
    35  
    36  	// DataSourcesMap is the collection of available data sources that
    37  	// this provider implements, with a Resource instance defining
    38  	// the schema and Read operation of each.
    39  	//
    40  	// Resource instances for data sources must have a Read function
    41  	// and must *not* implement Create, Update or Delete.
    42  	DataSourcesMap map[string]*Resource
    43  
    44  	// ConfigureFunc is a function for configuring the provider. If the
    45  	// provider doesn't need to be configured, this can be omitted.
    46  	//
    47  	// See the ConfigureFunc documentation for more information.
    48  	ConfigureFunc ConfigureFunc
    49  
    50  	meta interface{}
    51  }
    52  
    53  // ConfigureFunc is the function used to configure a Provider.
    54  //
    55  // The interface{} value returned by this function is stored and passed into
    56  // the subsequent resources as the meta parameter. This return value is
    57  // usually used to pass along a configured API client, a configuration
    58  // structure, etc.
    59  type ConfigureFunc func(*ResourceData) (interface{}, error)
    60  
    61  // InternalValidate should be called to validate the structure
    62  // of the provider.
    63  //
    64  // This should be called in a unit test for any provider to verify
    65  // before release that a provider is properly configured for use with
    66  // this library.
    67  func (p *Provider) InternalValidate() error {
    68  	if p == nil {
    69  		return errors.New("provider is nil")
    70  	}
    71  
    72  	sm := schemaMap(p.Schema)
    73  	if err := sm.InternalValidate(sm); err != nil {
    74  		return err
    75  	}
    76  
    77  	for k, r := range p.ResourcesMap {
    78  		if err := r.InternalValidate(nil, true); err != nil {
    79  			return fmt.Errorf("resource %s: %s", k, err)
    80  		}
    81  	}
    82  
    83  	for k, r := range p.DataSourcesMap {
    84  		if err := r.InternalValidate(nil, false); err != nil {
    85  			return fmt.Errorf("data source %s: %s", k, err)
    86  		}
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // Meta returns the metadata associated with this provider that was
    93  // returned by the Configure call. It will be nil until Configure is called.
    94  func (p *Provider) Meta() interface{} {
    95  	return p.meta
    96  }
    97  
    98  // SetMeta can be used to forcefully set the Meta object of the provider.
    99  // Note that if Configure is called the return value will override anything
   100  // set here.
   101  func (p *Provider) SetMeta(v interface{}) {
   102  	p.meta = v
   103  }
   104  
   105  // Input implementation of terraform.ResourceProvider interface.
   106  func (p *Provider) Input(
   107  	input terraform.UIInput,
   108  	c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
   109  	return schemaMap(p.Schema).Input(input, c)
   110  }
   111  
   112  // Validate implementation of terraform.ResourceProvider interface.
   113  func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
   114  	if err := p.InternalValidate(); err != nil {
   115  		return nil, []error{fmt.Errorf(
   116  			"Internal validation of the provider failed! This is always a bug\n"+
   117  				"with the provider itself, and not a user issue. Please report\n"+
   118  				"this bug:\n\n%s", err)}
   119  	}
   120  
   121  	return schemaMap(p.Schema).Validate(c)
   122  }
   123  
   124  // ValidateResource implementation of terraform.ResourceProvider interface.
   125  func (p *Provider) ValidateResource(
   126  	t string, c *terraform.ResourceConfig) ([]string, []error) {
   127  	r, ok := p.ResourcesMap[t]
   128  	if !ok {
   129  		return nil, []error{fmt.Errorf(
   130  			"Provider doesn't support resource: %s", t)}
   131  	}
   132  
   133  	return r.Validate(c)
   134  }
   135  
   136  // Configure implementation of terraform.ResourceProvider interface.
   137  func (p *Provider) Configure(c *terraform.ResourceConfig) error {
   138  	// No configuration
   139  	if p.ConfigureFunc == nil {
   140  		return nil
   141  	}
   142  
   143  	sm := schemaMap(p.Schema)
   144  
   145  	// Get a ResourceData for this configuration. To do this, we actually
   146  	// generate an intermediary "diff" although that is never exposed.
   147  	diff, err := sm.Diff(nil, c)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	data, err := sm.Data(nil, diff)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	meta, err := p.ConfigureFunc(data)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	p.meta = meta
   163  	return nil
   164  }
   165  
   166  // Apply implementation of terraform.ResourceProvider interface.
   167  func (p *Provider) Apply(
   168  	info *terraform.InstanceInfo,
   169  	s *terraform.InstanceState,
   170  	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   171  	r, ok := p.ResourcesMap[info.Type]
   172  	if !ok {
   173  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   174  	}
   175  
   176  	return r.Apply(s, d, p.meta)
   177  }
   178  
   179  // Diff implementation of terraform.ResourceProvider interface.
   180  func (p *Provider) Diff(
   181  	info *terraform.InstanceInfo,
   182  	s *terraform.InstanceState,
   183  	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   184  	r, ok := p.ResourcesMap[info.Type]
   185  	if !ok {
   186  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   187  	}
   188  
   189  	return r.Diff(s, c)
   190  }
   191  
   192  // Refresh implementation of terraform.ResourceProvider interface.
   193  func (p *Provider) Refresh(
   194  	info *terraform.InstanceInfo,
   195  	s *terraform.InstanceState) (*terraform.InstanceState, error) {
   196  	r, ok := p.ResourcesMap[info.Type]
   197  	if !ok {
   198  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   199  	}
   200  
   201  	return r.Refresh(s, p.meta)
   202  }
   203  
   204  // Resources implementation of terraform.ResourceProvider interface.
   205  func (p *Provider) Resources() []terraform.ResourceType {
   206  	keys := make([]string, 0, len(p.ResourcesMap))
   207  	for k, _ := range p.ResourcesMap {
   208  		keys = append(keys, k)
   209  	}
   210  	sort.Strings(keys)
   211  
   212  	result := make([]terraform.ResourceType, 0, len(keys))
   213  	for _, k := range keys {
   214  		resource := p.ResourcesMap[k]
   215  
   216  		// This isn't really possible (it'd fail InternalValidate), but
   217  		// we do it anyways to avoid a panic.
   218  		if resource == nil {
   219  			resource = &Resource{}
   220  		}
   221  
   222  		result = append(result, terraform.ResourceType{
   223  			Name:       k,
   224  			Importable: resource.Importer != nil,
   225  		})
   226  	}
   227  
   228  	return result
   229  }
   230  
   231  func (p *Provider) ImportState(
   232  	info *terraform.InstanceInfo,
   233  	id string) ([]*terraform.InstanceState, error) {
   234  	// Find the resource
   235  	r, ok := p.ResourcesMap[info.Type]
   236  	if !ok {
   237  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   238  	}
   239  
   240  	// If it doesn't support import, error
   241  	if r.Importer == nil {
   242  		return nil, fmt.Errorf("resource %s doesn't support import", info.Type)
   243  	}
   244  
   245  	// Create the data
   246  	data := r.Data(nil)
   247  	data.SetId(id)
   248  	data.SetType(info.Type)
   249  
   250  	// Call the import function
   251  	results := []*ResourceData{data}
   252  	if r.Importer.State != nil {
   253  		var err error
   254  		results, err = r.Importer.State(data, p.meta)
   255  		if err != nil {
   256  			return nil, err
   257  		}
   258  	}
   259  
   260  	// Convert the results to InstanceState values and return it
   261  	states := make([]*terraform.InstanceState, len(results))
   262  	for i, r := range results {
   263  		states[i] = r.State()
   264  	}
   265  
   266  	// Verify that all are non-nil. If there are any nil the error
   267  	// isn't obvious so we circumvent that with a friendlier error.
   268  	for _, s := range states {
   269  		if s == nil {
   270  			return nil, fmt.Errorf(
   271  				"nil entry in ImportState results. This is always a bug with\n" +
   272  					"the resource that is being imported. Please report this as\n" +
   273  					"a bug to Terraform.")
   274  		}
   275  	}
   276  
   277  	return states, nil
   278  }
   279  
   280  // ValidateDataSource implementation of terraform.ResourceProvider interface.
   281  func (p *Provider) ValidateDataSource(
   282  	t string, c *terraform.ResourceConfig) ([]string, []error) {
   283  	r, ok := p.DataSourcesMap[t]
   284  	if !ok {
   285  		return nil, []error{fmt.Errorf(
   286  			"Provider doesn't support data source: %s", t)}
   287  	}
   288  
   289  	return r.Validate(c)
   290  }
   291  
   292  // ReadDataDiff implementation of terraform.ResourceProvider interface.
   293  func (p *Provider) ReadDataDiff(
   294  	info *terraform.InstanceInfo,
   295  	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   296  
   297  	r, ok := p.DataSourcesMap[info.Type]
   298  	if !ok {
   299  		return nil, fmt.Errorf("unknown data source: %s", info.Type)
   300  	}
   301  
   302  	return r.Diff(nil, c)
   303  }
   304  
   305  // RefreshData implementation of terraform.ResourceProvider interface.
   306  func (p *Provider) ReadDataApply(
   307  	info *terraform.InstanceInfo,
   308  	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   309  
   310  	r, ok := p.DataSourcesMap[info.Type]
   311  	if !ok {
   312  		return nil, fmt.Errorf("unknown data source: %s", info.Type)
   313  	}
   314  
   315  	return r.ReadDataApply(d, p.meta)
   316  }
   317  
   318  // DataSources implementation of terraform.ResourceProvider interface.
   319  func (p *Provider) DataSources() []terraform.DataSource {
   320  	keys := make([]string, 0, len(p.DataSourcesMap))
   321  	for k, _ := range p.DataSourcesMap {
   322  		keys = append(keys, k)
   323  	}
   324  	sort.Strings(keys)
   325  
   326  	result := make([]terraform.DataSource, 0, len(keys))
   327  	for _, k := range keys {
   328  		result = append(result, terraform.DataSource{
   329  			Name: k,
   330  		})
   331  	}
   332  
   333  	return result
   334  }