terraform/helper/schema/resource.go

136 lines
3.5 KiB
Go
Raw Normal View History

2014-08-13 23:23:22 +02:00
package schema
import (
"errors"
"fmt"
"github.com/hashicorp/terraform/terraform"
2014-08-13 23:23:22 +02:00
)
// The functions below are the CRUD function types for a Resource.
2014-08-18 04:45:26 +02:00
//
// 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
2014-08-13 23:23:22 +02:00
// Resource represents a thing in Terraform that has a set of configurable
// attributes and generally also has 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 map[string]*Schema
Create CreateFunc
Read ReadFunc
Update UpdateFunc
Delete DeleteFunc
}
// Apply creates, updates, and/or deletes a resource.
func (r *Resource) Apply(
s *terraform.ResourceState,
d *terraform.ResourceDiff,
meta interface{}) (*terraform.ResourceState, error) {
data, err := schemaMap(r.Schema).Data(s, d)
if err != nil {
return s, err
}
if s == nil {
// The Terraform API dictates that this should never happen, but
// it doesn't hurt to be safe in this case.
s = new(terraform.ResourceState)
}
if d.Destroy || d.RequiresNew() {
if s.ID != "" {
// Destroy the resource since it is created
if err := r.Delete(data, meta); err != nil {
return data.State(), err
}
// Reset the data to be empty
data, err = schemaMap(r.Schema).Data(nil, d)
if err != nil {
return nil, err
}
}
// If we're only destroying, and not creating, then return
// now since we're done!
if !d.RequiresNew() {
return nil, nil
}
}
err = nil
if s.ID == "" {
// We're creating, it is a new resource.
err = r.Create(data, meta)
} else {
if r.Update == nil {
return s, fmt.Errorf("%s doesn't support update", s.Type)
}
err = r.Update(data, meta)
}
// Always set the ID attribute if it is set. We also always collapse
// the state since even partial states need to be returned.
state := data.State()
if state.ID != "" {
if state.Attributes == nil {
state.Attributes = make(map[string]string)
}
state.Attributes["id"] = state.ID
}
return state, err
}
// Diff returns a diff of this resource and is API compatible with the
// ResourceProvider interface.
func (r *Resource) Diff(
s *terraform.ResourceState,
c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
return schemaMap(r.Schema).Diff(s, c)
}
2014-08-16 07:00:16 +02:00
// Validate validates the resource configuration against the schema.
func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) {
return schemaMap(r.Schema).Validate(c)
}
2014-08-18 04:45:26 +02:00
// Refresh refreshes the state of the resource.
func (r *Resource) Refresh(
s *terraform.ResourceState,
meta interface{}) (*terraform.ResourceState, error) {
data, err := schemaMap(r.Schema).Data(s, nil)
if err != nil {
return nil, err
}
err = r.Read(data, meta)
return data.State(), err
}
2014-08-13 23:23:22 +02:00
// InternalValidate should be called to validate the structure
// of the resource.
//
// 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.
func (r *Resource) InternalValidate() error {
if r == nil {
return errors.New("resource is nil")
}
return schemaMap(r.Schema).InternalValidate()
2014-08-13 23:23:22 +02:00
}