helper/schema: start the Diff function

This commit is contained in:
Mitchell Hashimoto 2014-08-14 19:55:47 -07:00
parent 01b6b5f48e
commit efa2335886
3 changed files with 214 additions and 1 deletions

View File

@ -2,3 +2,8 @@ package schema
// ResourceData is used to query and set the attributes of a resource.
type ResourceData struct{}
// Get returns the data for the given key, or nil if the key doesn't exist.
func (d *ResourceData) Get(key string) interface{} {
return nil
}

View File

@ -1,5 +1,12 @@
package schema
import (
"fmt"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/mapstructure"
)
// ValueType is an enum of the type that can be represented by a schema.
type ValueType int
@ -14,7 +21,7 @@ const (
// Schema is used to describe the structure of a value.
type Schema struct {
// Type is the type of the value and must be one of the ValueType values.
Type ValueType
Type ValueType
// If one of these is set, then this item can come from the configuration.
// Both cannot be set. If Optional is set, the value is optional. If
@ -34,3 +41,95 @@ type Schema struct {
// element type is a complex structure, potentially with its own lifecycle.
Elem interface{}
}
// schemaMap is a wrapper that adds nice functions on top of schemas.
type schemaMap map[string]*Schema
// Data returns a ResourceData for the given schema, state, and diff.
//
// The diff is optional.
func (m schemaMap) Data(
s *terraform.ResourceState,
d *terraform.ResourceDiff) (*ResourceData, error) {
return nil, nil
}
// Diff returns the diff for a resource given the schema map,
// state, and configuration.
func (m schemaMap) Diff(
s *terraform.ResourceState,
c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
result := new(terraform.ResourceDiff)
result.Attributes = make(map[string]*terraform.ResourceAttrDiff)
for k, schema := range m {
var attrD *terraform.ResourceAttrDiff
var err error
switch schema.Type {
case TypeString:
attrD, err = m.diffString(k, schema, s, c)
}
if err != nil {
return nil, err
}
if attrD == nil {
// There is no diff for this attribute so just carry on.
continue
}
if schema.ForceNew {
// We require a new one if we have a diff, which we do, so
// set the flag to true.
attrD.RequiresNew = true
}
result.Attributes[k] = attrD
}
return result, nil
}
func (m schemaMap) diffString(
k string,
schema *Schema,
s *terraform.ResourceState,
c *terraform.ResourceConfig) (*terraform.ResourceAttrDiff, error) {
var old, n string
if s != nil {
old = s.Attributes[k]
}
computed := false
v, ok := c.Get(k)
if !ok {
// We don't have a value, if it is required then it is an error
if schema.Required {
return nil, fmt.Errorf("%s: required field not set", k)
}
// We don't have a configuration value.
if schema.Computed {
computed = true
} else {
return nil, nil
}
} else {
if err := mapstructure.WeakDecode(v, &n); err != nil {
return nil, fmt.Errorf("%s: %s", k, err)
}
if old == n {
// They're the same value
return nil, nil
}
}
return &terraform.ResourceAttrDiff{
Old: old,
New: n,
NewComputed: computed,
}, nil
}

View File

@ -0,0 +1,109 @@
package schema
import (
"reflect"
"testing"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/terraform"
)
func TestSchemaMap_Diff(t *testing.T) {
cases := []struct {
Schema map[string]*Schema
State *terraform.ResourceState
Config map[string]interface{}
Diff *terraform.ResourceDiff
Err bool
}{
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
State: nil,
Config: map[string]interface{}{
"availability_zone": "foo",
},
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"availability_zone": &terraform.ResourceAttrDiff{
Old: "",
New: "foo",
RequiresNew: true,
},
},
},
Err: false,
},
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
State: nil,
Config: map[string]interface{}{},
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"availability_zone": &terraform.ResourceAttrDiff{
Old: "",
NewComputed: true,
RequiresNew: true,
},
},
},
Err: false,
},
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Required: true,
},
},
State: nil,
Config: map[string]interface{}{},
Diff: nil,
Err: true,
},
}
for i, tc := range cases {
c, err := config.NewRawConfig(tc.Config)
if err != nil {
t.Fatalf("err: %s", err)
}
d, err := schemaMap(tc.Schema).Diff(
tc.State, terraform.NewResourceConfig(c))
if (err != nil) != tc.Err {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(tc.Diff, d) {
t.Fatalf("#%d: bad:\n\n%#v", i, d)
}
}
}