helper/schema: diff field reader should merge result with source

This commit is contained in:
Mitchell Hashimoto 2015-01-03 09:55:38 -05:00
parent 91a57b42e8
commit 0b1da37b20
2 changed files with 133 additions and 29 deletions

View File

@ -9,8 +9,25 @@ import (
)
// DiffFieldReader reads fields out of a diff structures.
//
// It also requires access to a Reader that reads fields from the structure
// that the diff was derived from. This is usually the state. This is required
// because a diff on its own doesn't have complete data about full objects
// such as maps.
//
// The Source MUST be the data that the diff was derived from. If it isn't,
// the behavior of this struct is undefined.
//
// Reading fields from a DiffFieldReader is identical to reading from
// Source except the diff will be applied to the end result.
//
// The "Exists" field on the result will be set to true if the complete
// field exists whether its from the source, diff, or a combination of both.
// It cannot be determined whether a retrieved value is composed of
// diff elements.
type DiffFieldReader struct {
Diff *terraform.InstanceDiff
Diff *terraform.InstanceDiff
Source FieldReader
}
func (r *DiffFieldReader) ReadField(
@ -27,7 +44,7 @@ func (r *DiffFieldReader) ReadField(
case TypeList:
return readListField(r, k, schema)
case TypeMap:
return r.readMap(k)
return r.readMap(k, schema)
case TypeSet:
return r.readSet(k, schema)
case typeObject:
@ -37,11 +54,23 @@ func (r *DiffFieldReader) ReadField(
}
}
func (r *DiffFieldReader) readMap(k string) (FieldReadResult, error) {
func (r *DiffFieldReader) readMap(
k string, schema *Schema) (FieldReadResult, error) {
result := make(map[string]interface{})
negresult := make(map[string]interface{})
resultSet := false
// First read the map from the underlying source
source, err := r.Source.ReadField([]string{k}, schema)
if err != nil {
return FieldReadResult{}, err
}
if source.Exists {
result = source.Value.(map[string]interface{})
resultSet = true
}
// Next, read all the elements we have in our diff, and apply
// the diff to our result.
prefix := k + "."
for k, v := range r.Diff.Attributes {
if !strings.HasPrefix(k, prefix) {
@ -51,7 +80,7 @@ func (r *DiffFieldReader) readMap(k string) (FieldReadResult, error) {
k = k[len(prefix):]
if v.NewRemoved {
negresult[k] = ""
delete(result, k)
continue
}
@ -64,39 +93,41 @@ func (r *DiffFieldReader) readMap(k string) (FieldReadResult, error) {
}
return FieldReadResult{
Value: resultVal,
NegValue: negresult,
Exists: resultSet,
Value: resultVal,
Exists: resultSet,
}, nil
}
func (r *DiffFieldReader) readPrimitive(
k string, schema *Schema) (FieldReadResult, error) {
attrD, ok := r.Diff.Attributes[k]
if !ok {
return FieldReadResult{}, nil
result, err := r.Source.ReadField([]string{k}, schema)
if err != nil {
return FieldReadResult{}, err
}
var result string
attrD, ok := r.Diff.Attributes[k]
if !ok {
return result, nil
}
var resultVal string
if !attrD.NewComputed {
result = attrD.New
resultVal = attrD.New
if attrD.NewExtra != nil {
if err := mapstructure.WeakDecode(attrD.NewExtra, &result); err != nil {
if err := mapstructure.WeakDecode(attrD.NewExtra, &resultVal); err != nil {
return FieldReadResult{}, err
}
}
}
returnVal, err := stringToPrimitive(result, false, schema)
result.Exists = true
result.Computed = attrD.NewComputed
result.Value, err = stringToPrimitive(resultVal, false, schema)
if err != nil {
return FieldReadResult{}, err
}
return FieldReadResult{
Value: returnVal,
Exists: true,
Computed: attrD.NewComputed,
}, nil
return result, nil
}
func (r *DiffFieldReader) readSet(

View File

@ -76,11 +76,6 @@ func TestDiffFieldReader(t *testing.T) {
New: "baz",
},
"mapRemove.foo": &terraform.ResourceAttrDiff{
Old: "",
New: "bar",
},
"mapRemove.bar": &terraform.ResourceAttrDiff{
NewRemoved: true,
},
@ -124,6 +119,31 @@ func TestDiffFieldReader(t *testing.T) {
Old: "",
New: "bar",
},
"listMap.0.bar": &terraform.ResourceAttrDiff{
NewRemoved: true,
},
"setChange.10.value": &terraform.ResourceAttrDiff{
Old: "50",
New: "80",
},
},
},
Source: &MapFieldReader{
Map: map[string]string{
"listMap.#": "2",
"listMap.0.foo": "bar",
"listMap.0.bar": "baz",
"listMap.1.baz": "baz",
"mapRemove.foo": "bar",
"mapRemove.bar": "bar",
"setChange.#": "1",
"setChange.10.index": "10",
"setChange.10.value": "50",
},
},
}
@ -231,7 +251,6 @@ func TestDiffFieldReader(t *testing.T) {
"foo": "bar",
"bar": "baz",
},
NegValue: map[string]interface{}{},
Exists: true,
Computed: false,
},
@ -256,9 +275,6 @@ func TestDiffFieldReader(t *testing.T) {
Value: map[string]interface{}{
"foo": "bar",
},
NegValue: map[string]interface{}{
"bar": "",
},
Exists: true,
Computed: false,
},
@ -312,6 +328,63 @@ func TestDiffFieldReader(t *testing.T) {
},
false,
},
"listMapRemoval": {
[]string{"listMap"},
&Schema{
Type: TypeList,
Elem: &Schema{
Type: TypeMap,
},
},
FieldReadResult{
Value: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"baz": "baz",
},
},
Exists: true,
},
false,
},
"setChange": {
[]string{"setChange"},
&Schema{
Type: TypeSet,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"index": &Schema{
Type: TypeInt,
Required: true,
},
"value": &Schema{
Type: TypeString,
Required: true,
},
},
},
Set: func(a interface{}) int {
m := a.(map[string]interface{})
return m["index"].(int)
},
},
FieldReadResult{
Value: []interface{}{
map[string]interface{}{
"index": 10,
"value": "80",
},
},
Exists: true,
},
false,
},
}
for name, tc := range cases {