github.com/bigkraig/terraform@v0.6.4-0.20151219155159-c90d1b074e31/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  	// ConfigureFunc is a function for configuring the provider. If the
    37  	// provider doesn't need to be configured, this can be omitted.
    38  	//
    39  	// See the ConfigureFunc documentation for more information.
    40  	ConfigureFunc ConfigureFunc
    41  
    42  	meta interface{}
    43  }
    44  
    45  // ConfigureFunc is the function used to configure a Provider.
    46  //
    47  // The interface{} value returned by this function is stored and passed into
    48  // the subsequent resources as the meta parameter. This return value is
    49  // usually used to pass along a configured API client, a configuration
    50  // structure, etc.
    51  type ConfigureFunc func(*ResourceData) (interface{}, error)
    52  
    53  // InternalValidate should be called to validate the structure
    54  // of the provider.
    55  //
    56  // This should be called in a unit test for any provider to verify
    57  // before release that a provider is properly configured for use with
    58  // this library.
    59  func (p *Provider) InternalValidate() error {
    60  	if p == nil {
    61  		return errors.New("provider is nil")
    62  	}
    63  
    64  	sm := schemaMap(p.Schema)
    65  	if err := sm.InternalValidate(sm); err != nil {
    66  		return err
    67  	}
    68  
    69  	for k, r := range p.ResourcesMap {
    70  		if err := r.InternalValidate(nil); err != nil {
    71  			return fmt.Errorf("%s: %s", k, err)
    72  		}
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  // Meta returns the metadata associated with this provider that was
    79  // returned by the Configure call. It will be nil until Configure is called.
    80  func (p *Provider) Meta() interface{} {
    81  	return p.meta
    82  }
    83  
    84  // SetMeta can be used to forcefully set the Meta object of the provider.
    85  // Note that if Configure is called the return value will override anything
    86  // set here.
    87  func (p *Provider) SetMeta(v interface{}) {
    88  	p.meta = v
    89  }
    90  
    91  // Input implementation of terraform.ResourceProvider interface.
    92  func (p *Provider) Input(
    93  	input terraform.UIInput,
    94  	c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
    95  	return schemaMap(p.Schema).Input(input, c)
    96  }
    97  
    98  // Validate implementation of terraform.ResourceProvider interface.
    99  func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
   100  	if err := p.InternalValidate(); err != nil {
   101  		return nil, []error{fmt.Errorf(
   102  			"Internal validation of the provider failed! This is always a bug\n"+
   103  				"with the provider itself, and not a user issue. Please report\n"+
   104  				"this bug:\n\n%s", err)}
   105  	}
   106  
   107  	return schemaMap(p.Schema).Validate(c)
   108  }
   109  
   110  // ValidateResource implementation of terraform.ResourceProvider interface.
   111  func (p *Provider) ValidateResource(
   112  	t string, c *terraform.ResourceConfig) ([]string, []error) {
   113  	r, ok := p.ResourcesMap[t]
   114  	if !ok {
   115  		return nil, []error{fmt.Errorf(
   116  			"Provider doesn't support resource: %s", t)}
   117  	}
   118  
   119  	return r.Validate(c)
   120  }
   121  
   122  // Configure implementation of terraform.ResourceProvider interface.
   123  func (p *Provider) Configure(c *terraform.ResourceConfig) error {
   124  	// No configuration
   125  	if p.ConfigureFunc == nil {
   126  		return nil
   127  	}
   128  
   129  	sm := schemaMap(p.Schema)
   130  
   131  	// Get a ResourceData for this configuration. To do this, we actually
   132  	// generate an intermediary "diff" although that is never exposed.
   133  	diff, err := sm.Diff(nil, c)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	data, err := sm.Data(nil, diff)
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	meta, err := p.ConfigureFunc(data)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	p.meta = meta
   149  	return nil
   150  }
   151  
   152  // Apply implementation of terraform.ResourceProvider interface.
   153  func (p *Provider) Apply(
   154  	info *terraform.InstanceInfo,
   155  	s *terraform.InstanceState,
   156  	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   157  	r, ok := p.ResourcesMap[info.Type]
   158  	if !ok {
   159  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   160  	}
   161  
   162  	return r.Apply(s, d, p.meta)
   163  }
   164  
   165  // Diff implementation of terraform.ResourceProvider interface.
   166  func (p *Provider) Diff(
   167  	info *terraform.InstanceInfo,
   168  	s *terraform.InstanceState,
   169  	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   170  	r, ok := p.ResourcesMap[info.Type]
   171  	if !ok {
   172  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   173  	}
   174  
   175  	return r.Diff(s, c)
   176  }
   177  
   178  // Refresh implementation of terraform.ResourceProvider interface.
   179  func (p *Provider) Refresh(
   180  	info *terraform.InstanceInfo,
   181  	s *terraform.InstanceState) (*terraform.InstanceState, error) {
   182  	r, ok := p.ResourcesMap[info.Type]
   183  	if !ok {
   184  		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
   185  	}
   186  
   187  	return r.Refresh(s, p.meta)
   188  }
   189  
   190  // Resources implementation of terraform.ResourceProvider interface.
   191  func (p *Provider) Resources() []terraform.ResourceType {
   192  	keys := make([]string, 0, len(p.ResourcesMap))
   193  	for k, _ := range p.ResourcesMap {
   194  		keys = append(keys, k)
   195  	}
   196  	sort.Strings(keys)
   197  
   198  	result := make([]terraform.ResourceType, 0, len(keys))
   199  	for _, k := range keys {
   200  		result = append(result, terraform.ResourceType{
   201  			Name: k,
   202  		})
   203  	}
   204  
   205  	return result
   206  }