From 0c9b65f3f48f286b42aea5fc10e473d1baab39f0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 26 Aug 2014 21:52:09 -0700 Subject: [PATCH] helper/schema: documentation --- helper/schema/provider.go | 35 +++++++++++++++++----- helper/schema/resource.go | 53 +++++++++++++++++++++++++++------- helper/schema/resource_data.go | 53 +++++++++++++++++++++++----------- helper/schema/schema.go | 27 +++++++++++++++++ 4 files changed, 133 insertions(+), 35 deletions(-) diff --git a/helper/schema/provider.go b/helper/schema/provider.go index 6a9a0876b..f34bf0195 100644 --- a/helper/schema/provider.go +++ b/helper/schema/provider.go @@ -8,15 +8,35 @@ import ( "github.com/hashicorp/terraform/terraform" ) -// Provider represents a Resource provider in Terraform, and properly +// Provider represents a resource provider in Terraform, and properly // implements all of the ResourceProvider API. // -// This is a friendlier API than the core Terraform ResourceProvider API, -// and is recommended to be used over that. +// By defining a schema for the configuration of the provider, the +// map of supporting resources, and a configuration function, the schema +// framework takes over and handles all the provider operations for you. +// +// After defining the provider structure, it is unlikely that you'll require any +// of the methods on Provider itself. type Provider struct { + // Schema is the schema for the configuration of this provider. If this + // provider has no configuration, this can be omitted. + // + // The keys of this map are the configuration keys, and the value is + // the schema describing the value of the configuration. Schema map[string]*Schema + + // ResourcesMap is the list of available resources that this provider + // can manage, along with their Resource structure defining their + // own schemas and CRUD operations. + // + // Provider automatically handles routing operations such as Apply, + // Diff, etc. to the proper resource. ResourcesMap map[string]*Resource + // ConfigureFunc is a function for configuring the provider. If the + // provider doesn't need to be configured, this can be omitted. + // + // See the ConfigureFunc documentation for more information. ConfigureFunc ConfigureFunc meta interface{} @@ -25,7 +45,9 @@ type Provider struct { // ConfigureFunc is the function used to configure a Provider. // // The interface{} value returned by this function is stored and passed into -// the subsequent resources as the meta parameter. +// the subsequent resources as the meta parameter. This return value is +// usually used to pass along a configured API client, a configuration +// structure, etc. type ConfigureFunc func(*ResourceData) (interface{}, error) // InternalValidate should be called to validate the structure @@ -65,13 +87,12 @@ func (p *Provider) SetMeta(v interface{}) { p.meta = v } -// Validate validates the provider configuration against the schema. +// Validate implementation of terraform.ResourceProvider interface. func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) { return schemaMap(p.Schema).Validate(c) } -// ValidateResource validates the resource configuration against the -// proper schema. +// ValidateResource implementation of terraform.ResourceProvider interface. func (p *Provider) ValidateResource( t string, c *terraform.ResourceConfig) ([]string, []error) { r, ok := p.ResourcesMap[t] diff --git a/helper/schema/resource.go b/helper/schema/resource.go index bc81b1d31..8cef2c05f 100644 --- a/helper/schema/resource.go +++ b/helper/schema/resource.go @@ -7,31 +7,58 @@ import ( "github.com/hashicorp/terraform/terraform" ) -// The functions below are the CRUD function types for a Resource. -// -// The second parameter is the meta value sent to the resource when -// different operations are called. -type CreateFunc func(*ResourceData, interface{}) error -type ReadFunc func(*ResourceData, interface{}) error -type UpdateFunc func(*ResourceData, interface{}) error -type DeleteFunc func(*ResourceData, interface{}) error - // Resource represents a thing in Terraform that has a set of configurable -// attributes and generally also has a lifecycle (create, read, update, -// delete). +// attributes and a lifecycle (create, read, update, delete). // // The Resource schema is an abstraction that allows provider writers to // worry only about CRUD operations while off-loading validation, diff // generation, etc. to this higher level library. type Resource struct { + // Schema is the schema for the configuration of this resource. + // + // The keys of this map are the configuration keys, and the values + // describe the schema of the configuration value. + // + // The schema is used to represent both configurable data as well + // as data that might be computed in the process of creating this + // resource. Schema map[string]*Schema + // The functions below are the CRUD operations for this resource. + // + // The only optional operation is Update. If Update is not implemented, + // then updates will not be supported for this resource. + // + // The ResourceData parameter in the functions below are used to + // query configuration and changes for the resource as well as to set + // the ID, computed data, etc. + // + // The interface{} parameter is the result of the ConfigureFunc in + // the provider for this resource. If the provider does not define + // a ConfigureFunc, this will be nil. This parameter should be used + // to store API clients, configuration structures, etc. + // + // If any errors occur during each of the operation, an error should be + // returned. If a resource was partially updated, be careful to enable + // partial state mode for ResourceData and use it accordingly. Create CreateFunc Read ReadFunc Update UpdateFunc Delete DeleteFunc } +// See Resource documentation. +type CreateFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type ReadFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type UpdateFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type DeleteFunc func(*ResourceData, interface{}) error + // Apply creates, updates, and/or deletes a resource. func (r *Resource) Apply( s *terraform.ResourceState, @@ -121,6 +148,10 @@ func (r *Resource) Refresh( // This should be called in a unit test for any resource to verify // before release that a resource is properly configured for use with // this library. +// +// Provider.InternalValidate() will automatically call this for all of +// the resources it manages, so you don't need to call this manually if it +// is part of a Provider. func (r *Resource) InternalValidate() error { if r == nil { return errors.New("resource is nil") diff --git a/helper/schema/resource_data.go b/helper/schema/resource_data.go index 093c24a7b..5b6329457 100644 --- a/helper/schema/resource_data.go +++ b/helper/schema/resource_data.go @@ -11,6 +11,30 @@ import ( "github.com/mitchellh/mapstructure" ) +// ResourceData is used to query and set the attributes of a resource. +// +// ResourceData is the primary argument received for CRUD operations on +// a resource as well as configuration of a provider. It is a powerful +// structure that can be used to not only query data, but check for changes, +// define partial state updates, etc. +// +// The most relevant methods to take a look at are Get, Set, and Partial. +type ResourceData struct { + // Settable (internally) + schema map[string]*Schema + config *terraform.ResourceConfig + state *terraform.ResourceState + diff *terraform.ResourceDiff + diffing bool + + // Don't set + setMap map[string]string + newState *terraform.ResourceState + partial bool + partialMap map[string]struct{} + once sync.Once +} + // getSource represents the level we want to get for a value (internally). // Any source less than or equal to the level will be loaded (whichever // has a value first). @@ -34,21 +58,6 @@ type getResult struct { var getResultEmpty getResult -// ResourceData is used to query and set the attributes of a resource. -type ResourceData struct { - schema map[string]*Schema - config *terraform.ResourceConfig - state *terraform.ResourceState - diff *terraform.ResourceDiff - diffing bool - - setMap map[string]string - newState *terraform.ResourceState - partial bool - partialMap map[string]struct{} - once sync.Once -} - // Get returns the data for the given key, or nil if the key doesn't exist // in the schema. // @@ -56,7 +65,8 @@ type ResourceData struct { // then the default value for that type will be returned. For strings, this is // "", for numbers it is 0, etc. // -// If you also want to test if something is set at all, use GetOk. +// If you want to test if something is set at all in the configuration, +// use GetOk. func (d *ResourceData) Get(key string) interface{} { v, _ := d.GetOk(key) return v @@ -64,7 +74,10 @@ func (d *ResourceData) Get(key string) interface{} { // GetChange returns the old and new value for a given key. // -// If there is no change, then old and new will simply be the same. +// HasChange should be used to check if a change exists. It is possible +// that both the old and new value are the same if the old value was not +// set and the new value is. This is common, for example, for boolean +// fields which have a zero value of false. func (d *ResourceData) GetChange(key string) (interface{}, interface{}) { o, n := d.getChange(key, getSourceConfig, getSourceDiff) return o.Value, n.Value @@ -73,6 +86,9 @@ func (d *ResourceData) GetChange(key string) (interface{}, interface{}) { // GetOk returns the data for the given key and whether or not the key // existed or not in the configuration. The second boolean result will also // be false if a key is given that isn't in the schema at all. +// +// The first result will not necessarilly be nil if the value doesn't exist. +// The second result should be checked to determine this information. func (d *ResourceData) GetOk(key string) (interface{}, bool) { r := d.getRaw(key, getSourceSet) return r.Value, r.Exists @@ -98,6 +114,9 @@ func (d *ResourceData) HasChange(key string) bool { // When partial state mode is enabled, then only key prefixes specified // by SetPartial will be in the final state. This allows providers to return // partial states for partially applied resources (when errors occur). +// +// When partial state mode is toggled, the map of enabled partial states +// (by SetPartial) is reset. func (d *ResourceData) Partial(on bool) { d.partial = on if on { diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 592fc58b9..62ca204a1 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -1,3 +1,14 @@ +// schema is a high-level framework for easily writing new providers +// for Terraform. Usage of schema is recommended over attempting to write +// to the low-level plugin interfaces manually. +// +// schema breaks down provider creation into simple CRUD operations for +// resources. The logic of diffing, destroying before creating, updating +// or creating, etc. is all handled by the framework. The plugin author +// only needs to implement a configuration schema and the CRUD operations and +// everything else is meant to just work. +// +// A good starting point is to view the Provider structure. package schema import ( @@ -24,8 +35,22 @@ const ( ) // Schema is used to describe the structure of a value. +// +// Read the documentation of the struct elements for important details. type Schema struct { // Type is the type of the value and must be one of the ValueType values. + // + // This type not only determines what type is expected/valid in configuring + // this value, but also what type is returned when ResourceData.Get is + // called. The types returned by Get are: + // + // TypeBool - bool + // TypeInt - int + // TypeString - string + // TypeList - []interface{} + // TypeMap - map[string]interface{} + // TypeSet - *schema.Set + // Type ValueType // If one of these is set, then this item can come from the configuration. @@ -70,6 +95,8 @@ type Schema struct { // ComputedWhen is a set of queries on the configuration. Whenever any // of these things is changed, it will require a recompute (this requires // that Computed is set to true). + // + // NOTE: This currently does not work. ComputedWhen []string }