helper/schema: GetChange

This commit is contained in:
Mitchell Hashimoto 2014-08-18 09:58:44 -07:00
parent 199b72d798
commit f1f4836c99
2 changed files with 136 additions and 19 deletions

View File

@ -9,6 +9,17 @@ import (
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
) )
// 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).
type getSource byte
const (
getSourceState getSource = iota
getSourceDiff
getSourceSet
)
// ResourceData is used to query and set the attributes of a resource. // ResourceData is used to query and set the attributes of a resource.
type ResourceData struct { type ResourceData struct {
schema map[string]*Schema schema map[string]*Schema
@ -30,7 +41,21 @@ func (d *ResourceData) Get(key string) interface{} {
parts = strings.Split(key, ".") parts = strings.Split(key, ".")
} }
return d.getObject("", parts, d.schema) return d.getObject("", parts, d.schema, getSourceSet)
}
// 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.
func (d *ResourceData) GetChange(key string) (interface{}, interface{}) {
var parts []string
if key != "" {
parts = strings.Split(key, ".")
}
o := d.getObject("", parts, d.schema, getSourceState)
n := d.getObject("", parts, d.schema, getSourceDiff)
return o, n
} }
// Set sets the value for the given key. // Set sets the value for the given key.
@ -84,19 +109,21 @@ func (d *ResourceData) State() *terraform.ResourceState {
func (d *ResourceData) get( func (d *ResourceData) get(
k string, k string,
parts []string, parts []string,
schema *Schema) interface{} { schema *Schema,
source getSource) interface{} {
switch schema.Type { switch schema.Type {
case TypeList: case TypeList:
return d.getList(k, parts, schema) return d.getList(k, parts, schema, source)
default: default:
return d.getPrimitive(k, parts, schema) return d.getPrimitive(k, parts, schema, source)
} }
} }
func (d *ResourceData) getObject( func (d *ResourceData) getObject(
k string, k string,
parts []string, parts []string,
schema map[string]*Schema) interface{} { schema map[string]*Schema,
source getSource) interface{} {
if len(parts) > 0 { if len(parts) > 0 {
// We're requesting a specific key in an object // We're requesting a specific key in an object
key := parts[0] key := parts[0]
@ -112,13 +139,13 @@ func (d *ResourceData) getObject(
key = fmt.Sprintf("%s.%s", k, key) key = fmt.Sprintf("%s.%s", k, key)
} }
return d.get(key, parts, s) return d.get(key, parts, s, source)
} }
// Get the entire object // Get the entire object
result := make(map[string]interface{}) result := make(map[string]interface{})
for field, _ := range schema { for field, _ := range schema {
result[field] = d.getObject(k, []string{field}, schema) result[field] = d.getObject(k, []string{field}, schema, source)
} }
return result return result
@ -127,7 +154,8 @@ func (d *ResourceData) getObject(
func (d *ResourceData) getList( func (d *ResourceData) getList(
k string, k string,
parts []string, parts []string,
schema *Schema) interface{} { schema *Schema,
source getSource) interface{} {
if len(parts) > 0 { if len(parts) > 0 {
// We still have parts left over meaning we're accessing an // We still have parts left over meaning we're accessing an
// element of this list. // element of this list.
@ -137,7 +165,7 @@ func (d *ResourceData) getList(
// Special case if we're accessing the count of the list // Special case if we're accessing the count of the list
if idx == "#" { if idx == "#" {
schema := &Schema{Type: TypeInt} schema := &Schema{Type: TypeInt}
result := d.get(k+".#", parts, schema) result := d.get(k+".#", parts, schema, source)
if result == nil { if result == nil {
result = 0 result = 0
} }
@ -148,17 +176,19 @@ func (d *ResourceData) getList(
key := fmt.Sprintf("%s.%s", k, idx) key := fmt.Sprintf("%s.%s", k, idx)
switch t := schema.Elem.(type) { switch t := schema.Elem.(type) {
case *Resource: case *Resource:
return d.getObject(key, parts, t.Schema) return d.getObject(key, parts, t.Schema, source)
case *Schema: case *Schema:
return d.get(key, parts, t) return d.get(key, parts, t, source)
} }
} }
// Get the entire list. // Get the entire list.
result := make([]interface{}, d.getList(k, []string{"#"}, schema).(int)) result := make(
[]interface{},
d.getList(k, []string{"#"}, schema, source).(int))
for i, _ := range result { for i, _ := range result {
is := strconv.FormatInt(int64(i), 10) is := strconv.FormatInt(int64(i), 10)
result[i] = d.getList(k, []string{is}, schema) result[i] = d.getList(k, []string{is}, schema, source)
} }
return result return result
@ -167,14 +197,15 @@ func (d *ResourceData) getList(
func (d *ResourceData) getPrimitive( func (d *ResourceData) getPrimitive(
k string, k string,
parts []string, parts []string,
schema *Schema) interface{} { schema *Schema,
source getSource) interface{} {
var result string var result string
var resultSet bool var resultSet bool
if d.state != nil { if d.state != nil && source >= getSourceState {
result, resultSet = d.state.Attributes[k] result, resultSet = d.state.Attributes[k]
} }
if d.diff != nil { if d.diff != nil && source >= getSourceDiff {
attrD, ok := d.diff.Attributes[k] attrD, ok := d.diff.Attributes[k]
if ok { if ok {
result = attrD.New result = attrD.New
@ -182,7 +213,7 @@ func (d *ResourceData) getPrimitive(
} }
} }
if d.setMap != nil { if d.setMap != nil && source >= getSourceSet {
if v, ok := d.setMap[k]; ok { if v, ok := d.setMap[k]; ok {
result = v result = v
resultSet = true resultSet = true
@ -357,7 +388,7 @@ func (d *ResourceData) setPrimitive(
func (d *ResourceData) stateList( func (d *ResourceData) stateList(
prefix string, prefix string,
schema *Schema) map[string]string { schema *Schema) map[string]string {
countRaw := d.get(prefix, []string{"#"}, schema) countRaw := d.get(prefix, []string{"#"}, schema, getSourceSet)
if countRaw == nil { if countRaw == nil {
return nil return nil
} }
@ -405,7 +436,7 @@ func (d *ResourceData) stateObject(
func (d *ResourceData) statePrimitive( func (d *ResourceData) statePrimitive(
prefix string, prefix string,
schema *Schema) map[string]string { schema *Schema) map[string]string {
v := d.getPrimitive(prefix, nil, schema) v := d.getPrimitive(prefix, nil, schema, getSourceSet)
if v == nil { if v == nil {
return nil return nil
} }

View File

@ -315,6 +315,92 @@ func TestResourceDataGet(t *testing.T) {
} }
} }
func TestResourceDataGetChange(t *testing.T) {
cases := []struct {
Schema map[string]*Schema
State *terraform.ResourceState
Diff *terraform.ResourceDiff
Key string
OldValue interface{}
NewValue interface{}
}{
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
State: nil,
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"availability_zone": &terraform.ResourceAttrDiff{
Old: "",
New: "foo",
RequiresNew: true,
},
},
},
Key: "availability_zone",
OldValue: nil,
NewValue: "foo",
},
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"availability_zone": "foo",
},
},
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"availability_zone": &terraform.ResourceAttrDiff{
Old: "",
New: "foo",
RequiresNew: true,
},
},
},
Key: "availability_zone",
OldValue: "foo",
NewValue: "foo",
},
}
for i, tc := range cases {
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
if err != nil {
t.Fatalf("err: %s", err)
}
o, n := d.GetChange(tc.Key)
if !reflect.DeepEqual(o, tc.OldValue) {
t.Fatalf("Old Bad: %d\n\n%#v", i, o)
}
if !reflect.DeepEqual(n, tc.NewValue) {
t.Fatalf("New Bad: %d\n\n%#v", i, n)
}
}
}
func TestResourceDataSet(t *testing.T) { func TestResourceDataSet(t *testing.T) {
cases := []struct { cases := []struct {
Schema map[string]*Schema Schema map[string]*Schema