2014-08-13 23:23:22 +02:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2014-08-19 18:26:48 +02:00
|
|
|
"fmt"
|
2014-08-16 01:32:43 +02:00
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
2014-08-13 23:23:22 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Resource represents a thing in Terraform that has a set of configurable
|
2014-08-27 06:52:09 +02:00
|
|
|
// attributes and a lifecycle (create, read, update, delete).
|
2014-08-13 23:23:22 +02:00
|
|
|
//
|
|
|
|
// 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 {
|
2014-08-27 06:52:09 +02:00
|
|
|
// 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.
|
2014-08-13 23:23:22 +02:00
|
|
|
Schema map[string]*Schema
|
|
|
|
|
2014-08-27 06:52:09 +02:00
|
|
|
// 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.
|
2014-08-13 23:23:22 +02:00
|
|
|
Create CreateFunc
|
|
|
|
Read ReadFunc
|
|
|
|
Update UpdateFunc
|
|
|
|
Delete DeleteFunc
|
|
|
|
}
|
|
|
|
|
2014-08-27 06:52:09 +02:00
|
|
|
// 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
|
|
|
|
|
2014-08-18 05:20:11 +02:00
|
|
|
// Apply creates, updates, and/or deletes a resource.
|
|
|
|
func (r *Resource) Apply(
|
2014-09-17 02:07:13 +02:00
|
|
|
s *terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
d *terraform.InstanceDiff,
|
2014-09-17 02:07:13 +02:00
|
|
|
meta interface{}) (*terraform.InstanceState, error) {
|
2014-08-18 05:20:11 +02:00
|
|
|
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.
|
2014-09-17 02:07:13 +02:00
|
|
|
s = new(terraform.InstanceState)
|
2014-08-18 05:20:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-08-28 00:26:15 +02:00
|
|
|
// Make sure the ID is gone.
|
|
|
|
data.SetId("")
|
2014-08-18 05:20:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// If we're only destroying, and not creating, then return
|
|
|
|
// now since we're done!
|
|
|
|
if !d.RequiresNew() {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nil
|
2014-08-28 00:26:15 +02:00
|
|
|
if data.Id() == "" {
|
2014-08-18 05:20:11 +02:00
|
|
|
// We're creating, it is a new resource.
|
|
|
|
err = r.Create(data, meta)
|
|
|
|
} else {
|
2014-08-19 18:26:48 +02:00
|
|
|
if r.Update == nil {
|
2014-09-17 02:07:13 +02:00
|
|
|
return s, fmt.Errorf("doesn't support update")
|
2014-08-19 18:26:48 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 05:20:11 +02:00
|
|
|
err = r.Update(data, meta)
|
|
|
|
}
|
|
|
|
|
2014-08-20 19:37:59 +02:00
|
|
|
return data.State(), err
|
2014-08-18 05:20:11 +02:00
|
|
|
}
|
|
|
|
|
2014-08-16 01:32:43 +02:00
|
|
|
// Diff returns a diff of this resource and is API compatible with the
|
|
|
|
// ResourceProvider interface.
|
|
|
|
func (r *Resource) Diff(
|
2014-09-17 02:07:13 +02:00
|
|
|
s *terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
2014-08-16 01:32:43 +02:00
|
|
|
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(
|
2014-09-17 02:07:13 +02:00
|
|
|
s *terraform.InstanceState,
|
|
|
|
meta interface{}) (*terraform.InstanceState, error) {
|
2014-08-18 04:45:26 +02:00
|
|
|
data, err := schemaMap(r.Schema).Data(s, nil)
|
|
|
|
if err != nil {
|
2014-08-27 00:50:31 +02:00
|
|
|
return s, err
|
2014-08-18 04:45:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
err = r.Read(data, meta)
|
2014-08-20 03:19:02 +02:00
|
|
|
state := data.State()
|
2014-08-22 07:19:33 +02:00
|
|
|
if state != nil && state.ID == "" {
|
2014-08-20 03:19:02 +02:00
|
|
|
state = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return state, err
|
2014-08-18 04:45:26 +02:00
|
|
|
}
|
|
|
|
|
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.
|
2014-08-27 06:52:09 +02:00
|
|
|
//
|
|
|
|
// 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.
|
2014-08-13 23:23:22 +02:00
|
|
|
func (r *Resource) InternalValidate() error {
|
|
|
|
if r == nil {
|
|
|
|
return errors.New("resource is nil")
|
|
|
|
}
|
|
|
|
|
2014-08-17 23:50:44 +02:00
|
|
|
return schemaMap(r.Schema).InternalValidate()
|
2014-08-13 23:23:22 +02:00
|
|
|
}
|