From 7e379cb1a182e09f1548252e3ee3f22aea8cbe82 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Jan 2015 12:13:46 -0500 Subject: [PATCH] helper/schema: field readers no longer take a schema as arg --- helper/schema/field_reader.go | 108 ++++++++++++++---- helper/schema/field_reader_config.go | 18 +-- helper/schema/field_reader_config_test.go | 70 ++++++------ helper/schema/field_reader_diff.go | 49 ++++---- helper/schema/field_reader_diff_test.go | 130 +++++++++++----------- helper/schema/field_reader_map.go | 28 ++--- helper/schema/field_reader_map_test.go | 70 ++++++------ helper/schema/field_reader_multi.go | 13 +-- helper/schema/field_reader_multi_test.go | 85 ++++++++------ helper/schema/field_reader_test.go | 48 ++++++++ 10 files changed, 370 insertions(+), 249 deletions(-) create mode 100644 helper/schema/field_reader_test.go diff --git a/helper/schema/field_reader.go b/helper/schema/field_reader.go index 6c6e0d5a4..ce5f38543 100644 --- a/helper/schema/field_reader.go +++ b/helper/schema/field_reader.go @@ -9,7 +9,7 @@ import ( // the proper typed representation. ResourceData uses this to query data // out of multiple sources: config, state, diffs, etc. type FieldReader interface { - ReadField([]string, *Schema) (FieldReadResult, error) + ReadField([]string) (FieldReadResult, error) } // FieldReadResult encapsulates all the resulting data from reading @@ -31,14 +31,92 @@ type FieldReadResult struct { Computed bool } +// addrToSchema finds the final element schema for the given address +// and the given schema. +func addrToSchema(addr []string, schemaMap map[string]*Schema) *Schema { + var result *Schema + var lastType ValueType + current := &Schema{ + Type: typeObject, + Elem: schemaMap, + } + + for len(addr) > 0 { + k := addr[0] + addr = addr[1:] + + REPEAT: + if len(addr) == 0 { + result = current + } + + currentType := current.Type + switch current.Type { + case TypeBool: + fallthrough + case TypeInt: + fallthrough + case TypeString: + if len(addr) > 0 { + return nil + } + case TypeList: + fallthrough + case TypeSet: + switch v := current.Elem.(type) { + case *Resource: + current = &Schema{ + Type: typeObject, + Elem: v.Schema, + } + case *Schema: + current = v + default: + return nil + } + + if len(addr) > 0 && addr[0] == "#" { + current = &Schema{Type: TypeInt} + } + case TypeMap: + if len(addr) > 0 { + current = &Schema{Type: TypeString} + } + case typeObject: + if lastType == TypeSet || lastType == TypeList { + // We just ignore sets/lists since they don't access + // objects the same way. + break + } + + m := current.Elem.(map[string]*Schema) + val, ok := m[k] + if !ok { + return nil + } + + current = val + goto REPEAT + } + + lastType = currentType + } + + return result +} + // readListField is a generic method for reading a list field out of a // a FieldReader. It does this based on the assumption that there is a key // "foo.#" for a list "foo" and that the indexes are "foo.0", "foo.1", etc. // after that point. func readListField( - r FieldReader, k string, schema *Schema) (FieldReadResult, error) { + r FieldReader, addr []string, schema *Schema) (FieldReadResult, error) { + addrPadded := make([]string, len(addr)+1) + copy(addrPadded, addr) + addrPadded[len(addrPadded)-1] = "#" + // Get the number of elements in the list - countResult, err := r.ReadField([]string{k + ".#"}, &Schema{Type: TypeInt}) + countResult, err := r.ReadField(addrPadded) if err != nil { return FieldReadResult{}, err } @@ -56,23 +134,12 @@ func readListField( }, nil } - // Get the schema for the elements - var elemSchema *Schema - switch t := schema.Elem.(type) { - case *Resource: - elemSchema = &Schema{ - Type: typeObject, - Elem: t.Schema, - } - case *Schema: - elemSchema = t - } - // Go through each count, and get the item value out of it result := make([]interface{}, countResult.Value.(int)) for i, _ := range result { is := strconv.FormatInt(int64(i), 10) - rawResult, err := r.ReadField([]string{k, is}, elemSchema) + addrPadded[len(addrPadded)-1] = is + rawResult, err := r.ReadField(addrPadded) if err != nil { return FieldReadResult{}, err } @@ -97,11 +164,14 @@ func readListField( // will result in the proper field data. func readObjectField( r FieldReader, - k string, + addr []string, schema map[string]*Schema) (FieldReadResult, error) { result := make(map[string]interface{}) - for field, schema := range schema { - rawResult, err := r.ReadField([]string{k, field}, schema) + for field, _ := range schema { + addrRead := make([]string, len(addr), len(addr)+1) + copy(addrRead, addr) + addrRead = append(addrRead, field) + rawResult, err := r.ReadField(addrRead) if err != nil { return FieldReadResult{}, err } diff --git a/helper/schema/field_reader_config.go b/helper/schema/field_reader_config.go index 146aaef6e..167d8bf71 100644 --- a/helper/schema/field_reader_config.go +++ b/helper/schema/field_reader_config.go @@ -12,11 +12,15 @@ import ( // the best of its ability. type ConfigFieldReader struct { Config *terraform.ResourceConfig + Schema map[string]*Schema } -func (r *ConfigFieldReader) ReadField( - address []string, schema *Schema) (FieldReadResult, error) { +func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) { k := strings.Join(address, ".") + schema := addrToSchema(address, r.Schema) + if schema == nil { + return FieldReadResult{}, nil + } switch schema.Type { case TypeBool: @@ -26,13 +30,13 @@ func (r *ConfigFieldReader) ReadField( case TypeString: return r.readPrimitive(k, schema) case TypeList: - return readListField(r, k, schema) + return readListField(r, address, schema) case TypeMap: return r.readMap(k) case TypeSet: - return r.readSet(k, schema) + return r.readSet(address, schema) case typeObject: - return readObjectField(r, k, schema.Elem.(map[string]*Schema)) + return readObjectField(r, address, schema.Elem.(map[string]*Schema)) default: panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) } @@ -96,8 +100,8 @@ func (r *ConfigFieldReader) readPrimitive( } func (r *ConfigFieldReader) readSet( - k string, schema *Schema) (FieldReadResult, error) { - raw, err := readListField(r, k, schema) + address []string, schema *Schema) (FieldReadResult, error) { + raw, err := readListField(r, address, schema) if err != nil { return FieldReadResult{}, err } diff --git a/helper/schema/field_reader_config_test.go b/helper/schema/field_reader_config_test.go index d27f74f36..5eda033b2 100644 --- a/helper/schema/field_reader_config_test.go +++ b/helper/schema/field_reader_config_test.go @@ -14,6 +14,40 @@ func TestConfigFieldReader_impl(t *testing.T) { func TestConfigFieldReader(t *testing.T) { r := &ConfigFieldReader{ + Schema: map[string]*Schema{ + "bool": &Schema{Type: TypeBool}, + "int": &Schema{Type: TypeInt}, + "string": &Schema{Type: TypeString}, + "list": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeString}, + }, + "listInt": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + "map": &Schema{Type: TypeMap}, + "set": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + "setDeep": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{Type: TypeInt}, + "value": &Schema{Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + }, + Config: testConfig(t, map[string]interface{}{ "bool": true, "int": 42, @@ -45,7 +79,6 @@ func TestConfigFieldReader(t *testing.T) { cases := map[string]struct { Addr []string - Schema *Schema Out interface{} OutOk bool OutComputed bool @@ -53,7 +86,6 @@ func TestConfigFieldReader(t *testing.T) { }{ "noexist": { []string{"boolNOPE"}, - &Schema{Type: TypeBool}, nil, false, false, @@ -62,7 +94,6 @@ func TestConfigFieldReader(t *testing.T) { "bool": { []string{"bool"}, - &Schema{Type: TypeBool}, true, true, false, @@ -71,7 +102,6 @@ func TestConfigFieldReader(t *testing.T) { "int": { []string{"int"}, - &Schema{Type: TypeInt}, 42, true, false, @@ -80,7 +110,6 @@ func TestConfigFieldReader(t *testing.T) { "string": { []string{"string"}, - &Schema{Type: TypeString}, "string", true, false, @@ -89,10 +118,6 @@ func TestConfigFieldReader(t *testing.T) { "list": { []string{"list"}, - &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeString}, - }, []interface{}{ "foo", "bar", @@ -104,10 +129,6 @@ func TestConfigFieldReader(t *testing.T) { "listInt": { []string{"listInt"}, - &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - }, []interface{}{ 21, 42, @@ -119,7 +140,6 @@ func TestConfigFieldReader(t *testing.T) { "map": { []string{"map"}, - &Schema{Type: TypeMap}, map[string]interface{}{ "foo": "bar", "bar": "baz", @@ -131,7 +151,6 @@ func TestConfigFieldReader(t *testing.T) { "mapelem": { []string{"map", "foo"}, - &Schema{Type: TypeString}, "bar", true, false, @@ -140,13 +159,6 @@ func TestConfigFieldReader(t *testing.T) { "set": { []string{"set"}, - &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, []interface{}{10, 50}, true, false, @@ -155,18 +167,6 @@ func TestConfigFieldReader(t *testing.T) { "setDeep": { []string{"setDeep"}, - &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{Type: TypeInt}, - "value": &Schema{Type: TypeString}, - }, - }, - Set: func(a interface{}) int { - return a.(map[string]interface{})["index"].(int) - }, - }, []interface{}{ map[string]interface{}{ "index": 10, @@ -184,7 +184,7 @@ func TestConfigFieldReader(t *testing.T) { } for name, tc := range cases { - out, err := r.ReadField(tc.Addr, tc.Schema) + out, err := r.ReadField(tc.Addr) if (err != nil) != tc.OutErr { t.Fatalf("%s: err: %s", name, err) } diff --git a/helper/schema/field_reader_diff.go b/helper/schema/field_reader_diff.go index 28b5ffc88..337bcc5c9 100644 --- a/helper/schema/field_reader_diff.go +++ b/helper/schema/field_reader_diff.go @@ -28,11 +28,14 @@ import ( type DiffFieldReader struct { Diff *terraform.InstanceDiff Source FieldReader + Schema map[string]*Schema } -func (r *DiffFieldReader) ReadField( - address []string, schema *Schema) (FieldReadResult, error) { - k := strings.Join(address, ".") +func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { + schema := addrToSchema(address, r.Schema) + if schema == nil { + return FieldReadResult{}, nil + } switch schema.Type { case TypeBool: @@ -40,27 +43,27 @@ func (r *DiffFieldReader) ReadField( case TypeInt: fallthrough case TypeString: - return r.readPrimitive(k, schema) + return r.readPrimitive(address, schema) case TypeList: - return readListField(r, k, schema) + return readListField(r, address, schema) case TypeMap: - return r.readMap(k, schema) + return r.readMap(address, schema) case TypeSet: - return r.readSet(k, schema) + return r.readSet(address, schema) case typeObject: - return readObjectField(r, k, schema.Elem.(map[string]*Schema)) + return readObjectField(r, address, schema.Elem.(map[string]*Schema)) default: panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) } } func (r *DiffFieldReader) readMap( - k string, schema *Schema) (FieldReadResult, error) { + address []string, schema *Schema) (FieldReadResult, error) { result := make(map[string]interface{}) resultSet := false // First read the map from the underlying source - source, err := r.Source.ReadField([]string{k}, schema) + source, err := r.Source.ReadField(address) if err != nil { return FieldReadResult{}, err } @@ -71,7 +74,7 @@ func (r *DiffFieldReader) readMap( // Next, read all the elements we have in our diff, and apply // the diff to our result. - prefix := k + "." + prefix := strings.Join(address, ".") + "." for k, v := range r.Diff.Attributes { if !strings.HasPrefix(k, prefix) { continue @@ -99,13 +102,13 @@ func (r *DiffFieldReader) readMap( } func (r *DiffFieldReader) readPrimitive( - k string, schema *Schema) (FieldReadResult, error) { - result, err := r.Source.ReadField([]string{k}, schema) + address []string, schema *Schema) (FieldReadResult, error) { + result, err := r.Source.ReadField(address) if err != nil { return FieldReadResult{}, err } - attrD, ok := r.Diff.Attributes[k] + attrD, ok := r.Diff.Attributes[strings.Join(address, ".")] if !ok { return result, nil } @@ -131,24 +134,12 @@ func (r *DiffFieldReader) readPrimitive( } func (r *DiffFieldReader) readSet( - k string, schema *Schema) (FieldReadResult, error) { + address []string, schema *Schema) (FieldReadResult, error) { // Create the set that will be our result set := &Set{F: schema.Set} - // Get the schema for the elements - var elemSchema *Schema - switch t := schema.Elem.(type) { - case *Resource: - elemSchema = &Schema{ - Type: typeObject, - Elem: t.Schema, - } - case *Schema: - elemSchema = t - } - // Go through the map and find all the set items - prefix := k + "." + prefix := strings.Join(address, ".") + "." for k, _ := range r.Diff.Attributes { if !strings.HasPrefix(k, prefix) { continue @@ -162,7 +153,7 @@ func (r *DiffFieldReader) readSet( parts := strings.Split(k[len(prefix):], ".") idx := parts[0] - raw, err := r.ReadField([]string{prefix + idx}, elemSchema) + raw, err := r.ReadField(append(address, idx)) if err != nil { return FieldReadResult{}, err } diff --git a/helper/schema/field_reader_diff_test.go b/helper/schema/field_reader_diff_test.go index 0d552a256..0e64e5b5c 100644 --- a/helper/schema/field_reader_diff_test.go +++ b/helper/schema/field_reader_diff_test.go @@ -12,7 +12,71 @@ func TestDiffFieldReader_impl(t *testing.T) { } func TestDiffFieldReader(t *testing.T) { + schema := map[string]*Schema{ + "bool": &Schema{Type: TypeBool}, + "int": &Schema{Type: TypeInt}, + "string": &Schema{Type: TypeString}, + "stringComputed": &Schema{Type: TypeString}, + "list": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeString}, + }, + "listInt": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + "listMap": &Schema{ + Type: TypeList, + Elem: &Schema{ + Type: TypeMap, + }, + }, + "map": &Schema{Type: TypeMap}, + "mapRemove": &Schema{Type: TypeMap}, + "set": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + "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) + }, + }, + "setDeep": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{Type: TypeInt}, + "value": &Schema{Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + } + r := &DiffFieldReader{ + Schema: schema, Diff: &terraform.InstanceDiff{ Attributes: map[string]*terraform.ResourceAttrDiff{ "bool": &terraform.ResourceAttrDiff{ @@ -132,6 +196,7 @@ func TestDiffFieldReader(t *testing.T) { }, Source: &MapFieldReader{ + Schema: schema, Map: map[string]string{ "listMap.#": "2", "listMap.0.foo": "bar", @@ -150,13 +215,11 @@ func TestDiffFieldReader(t *testing.T) { cases := map[string]struct { Addr []string - Schema *Schema Result FieldReadResult Err bool }{ "noexist": { []string{"boolNOPE"}, - &Schema{Type: TypeBool}, FieldReadResult{ Value: nil, Exists: false, @@ -167,7 +230,6 @@ func TestDiffFieldReader(t *testing.T) { "bool": { []string{"bool"}, - &Schema{Type: TypeBool}, FieldReadResult{ Value: true, Exists: true, @@ -178,7 +240,6 @@ func TestDiffFieldReader(t *testing.T) { "int": { []string{"int"}, - &Schema{Type: TypeInt}, FieldReadResult{ Value: 42, Exists: true, @@ -189,7 +250,6 @@ func TestDiffFieldReader(t *testing.T) { "string": { []string{"string"}, - &Schema{Type: TypeString}, FieldReadResult{ Value: "string", Exists: true, @@ -200,7 +260,6 @@ func TestDiffFieldReader(t *testing.T) { "stringComputed": { []string{"stringComputed"}, - &Schema{Type: TypeString}, FieldReadResult{ Value: "", Exists: true, @@ -211,10 +270,6 @@ func TestDiffFieldReader(t *testing.T) { "list": { []string{"list"}, - &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeString}, - }, FieldReadResult{ Value: []interface{}{ "foo", @@ -228,10 +283,6 @@ func TestDiffFieldReader(t *testing.T) { "listInt": { []string{"listInt"}, - &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - }, FieldReadResult{ Value: []interface{}{ 21, @@ -245,7 +296,6 @@ func TestDiffFieldReader(t *testing.T) { "map": { []string{"map"}, - &Schema{Type: TypeMap}, FieldReadResult{ Value: map[string]interface{}{ "foo": "bar", @@ -259,7 +309,6 @@ func TestDiffFieldReader(t *testing.T) { "mapelem": { []string{"map", "foo"}, - &Schema{Type: TypeString}, FieldReadResult{ Value: "bar", Exists: true, @@ -270,7 +319,6 @@ func TestDiffFieldReader(t *testing.T) { "mapRemove": { []string{"mapRemove"}, - &Schema{Type: TypeMap}, FieldReadResult{ Value: map[string]interface{}{ "foo": "bar", @@ -283,13 +331,6 @@ func TestDiffFieldReader(t *testing.T) { "set": { []string{"set"}, - &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, FieldReadResult{ Value: []interface{}{10, 50}, Exists: true, @@ -300,18 +341,6 @@ func TestDiffFieldReader(t *testing.T) { "setDeep": { []string{"setDeep"}, - &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{Type: TypeInt}, - "value": &Schema{Type: TypeString}, - }, - }, - Set: func(a interface{}) int { - return a.(map[string]interface{})["index"].(int) - }, - }, FieldReadResult{ Value: []interface{}{ map[string]interface{}{ @@ -331,12 +360,6 @@ func TestDiffFieldReader(t *testing.T) { "listMapRemoval": { []string{"listMap"}, - &Schema{ - Type: TypeList, - Elem: &Schema{ - Type: TypeMap, - }, - }, FieldReadResult{ Value: []interface{}{ map[string]interface{}{ @@ -353,27 +376,6 @@ func TestDiffFieldReader(t *testing.T) { "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{}{ @@ -388,7 +390,7 @@ func TestDiffFieldReader(t *testing.T) { } for name, tc := range cases { - out, err := r.ReadField(tc.Addr, tc.Schema) + out, err := r.ReadField(tc.Addr) if (err != nil) != tc.Err { t.Fatalf("%s: err: %s", name, err) } diff --git a/helper/schema/field_reader_map.go b/helper/schema/field_reader_map.go index 8eafdd13f..f06a7170a 100644 --- a/helper/schema/field_reader_map.go +++ b/helper/schema/field_reader_map.go @@ -8,12 +8,16 @@ import ( // MapFieldReader reads fields out of an untyped map[string]string to // the best of its ability. type MapFieldReader struct { - Map map[string]string + Map map[string]string + Schema map[string]*Schema } -func (r *MapFieldReader) ReadField( - address []string, schema *Schema) (FieldReadResult, error) { +func (r *MapFieldReader) ReadField(address []string) (FieldReadResult, error) { k := strings.Join(address, ".") + schema := addrToSchema(address, r.Schema) + if schema == nil { + return FieldReadResult{}, nil + } switch schema.Type { case TypeBool: @@ -23,13 +27,13 @@ func (r *MapFieldReader) ReadField( case TypeString: return r.readPrimitive(k, schema) case TypeList: - return readListField(r, k, schema) + return readListField(r, address, schema) case TypeMap: return r.readMap(k) case TypeSet: return r.readSet(k, schema) case typeObject: - return readObjectField(r, k, schema.Elem.(map[string]*Schema)) + return readObjectField(r, address, schema.Elem.(map[string]*Schema)) default: panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) } @@ -102,18 +106,6 @@ func (r *MapFieldReader) readSet( }, nil } - // Get the schema for the elements - var elemSchema *Schema - switch t := schema.Elem.(type) { - case *Resource: - elemSchema = &Schema{ - Type: typeObject, - Elem: t.Schema, - } - case *Schema: - elemSchema = t - } - // Go through the map and find all the set items prefix := k + "." for k, _ := range r.Map { @@ -129,7 +121,7 @@ func (r *MapFieldReader) readSet( parts := strings.Split(k[len(prefix):], ".") idx := parts[0] - raw, err := r.ReadField([]string{prefix + idx}, elemSchema) + raw, err := r.ReadField([]string{prefix[:len(prefix)-1], idx}) if err != nil { return FieldReadResult{}, err } diff --git a/helper/schema/field_reader_map_test.go b/helper/schema/field_reader_map_test.go index 4334c0a5a..9734d7f7a 100644 --- a/helper/schema/field_reader_map_test.go +++ b/helper/schema/field_reader_map_test.go @@ -11,6 +11,40 @@ func TestMapFieldReader_impl(t *testing.T) { func TestMapFieldReader(t *testing.T) { r := &MapFieldReader{ + Schema: map[string]*Schema{ + "bool": &Schema{Type: TypeBool}, + "int": &Schema{Type: TypeInt}, + "string": &Schema{Type: TypeString}, + "list": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeString}, + }, + "listInt": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + "map": &Schema{Type: TypeMap}, + "set": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + "setDeep": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{Type: TypeInt}, + "value": &Schema{Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + }, + Map: map[string]string{ "bool": "true", "int": "42", @@ -41,7 +75,6 @@ func TestMapFieldReader(t *testing.T) { cases := map[string]struct { Addr []string - Schema *Schema Out interface{} OutOk bool OutComputed bool @@ -49,7 +82,6 @@ func TestMapFieldReader(t *testing.T) { }{ "noexist": { []string{"boolNOPE"}, - &Schema{Type: TypeBool}, nil, false, false, @@ -58,7 +90,6 @@ func TestMapFieldReader(t *testing.T) { "bool": { []string{"bool"}, - &Schema{Type: TypeBool}, true, true, false, @@ -67,7 +98,6 @@ func TestMapFieldReader(t *testing.T) { "int": { []string{"int"}, - &Schema{Type: TypeInt}, 42, true, false, @@ -76,7 +106,6 @@ func TestMapFieldReader(t *testing.T) { "string": { []string{"string"}, - &Schema{Type: TypeString}, "string", true, false, @@ -85,10 +114,6 @@ func TestMapFieldReader(t *testing.T) { "list": { []string{"list"}, - &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeString}, - }, []interface{}{ "foo", "bar", @@ -100,10 +125,6 @@ func TestMapFieldReader(t *testing.T) { "listInt": { []string{"listInt"}, - &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - }, []interface{}{ 21, 42, @@ -115,7 +136,6 @@ func TestMapFieldReader(t *testing.T) { "map": { []string{"map"}, - &Schema{Type: TypeMap}, map[string]interface{}{ "foo": "bar", "bar": "baz", @@ -127,7 +147,6 @@ func TestMapFieldReader(t *testing.T) { "mapelem": { []string{"map", "foo"}, - &Schema{Type: TypeString}, "bar", true, false, @@ -136,13 +155,6 @@ func TestMapFieldReader(t *testing.T) { "set": { []string{"set"}, - &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, []interface{}{10, 50}, true, false, @@ -151,18 +163,6 @@ func TestMapFieldReader(t *testing.T) { "setDeep": { []string{"setDeep"}, - &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{Type: TypeInt}, - "value": &Schema{Type: TypeString}, - }, - }, - Set: func(a interface{}) int { - return a.(map[string]interface{})["index"].(int) - }, - }, []interface{}{ map[string]interface{}{ "index": 10, @@ -180,7 +180,7 @@ func TestMapFieldReader(t *testing.T) { } for name, tc := range cases { - out, err := r.ReadField(tc.Addr, tc.Schema) + out, err := r.ReadField(tc.Addr) if (err != nil) != tc.OutErr { t.Fatalf("%s: err: %s", name, err) } diff --git a/helper/schema/field_reader_multi.go b/helper/schema/field_reader_multi.go index b8f9726cf..3e2134708 100644 --- a/helper/schema/field_reader_multi.go +++ b/helper/schema/field_reader_multi.go @@ -16,20 +16,19 @@ type MultiLevelFieldReader struct { Levels []string } -func (r *MultiLevelFieldReader) ReadField( - address []string, schema *Schema) (FieldReadResult, error) { - return r.ReadFieldMerge(address, schema, r.Levels[len(r.Levels)-1]) +func (r *MultiLevelFieldReader) ReadField(address []string) (FieldReadResult, error) { + return r.ReadFieldMerge(address, r.Levels[len(r.Levels)-1]) } func (r *MultiLevelFieldReader) ReadFieldExact( - address []string, schema *Schema, level string) (FieldReadResult, error) { + address []string, level string) (FieldReadResult, error) { reader, ok := r.Readers[level] if !ok { return FieldReadResult{}, fmt.Errorf( "Unknown reader level: %s", level) } - result, err := reader.ReadField(address, schema) + result, err := reader.ReadField(address) if err != nil { return FieldReadResult{}, fmt.Errorf( "Error reading level %s: %s", level, err) @@ -39,7 +38,7 @@ func (r *MultiLevelFieldReader) ReadFieldExact( } func (r *MultiLevelFieldReader) ReadFieldMerge( - address []string, schema *Schema, level string) (FieldReadResult, error) { + address []string, level string) (FieldReadResult, error) { var result FieldReadResult for _, l := range r.Levels { r, ok := r.Readers[l] @@ -47,7 +46,7 @@ func (r *MultiLevelFieldReader) ReadFieldMerge( continue } - out, err := r.ReadField(address, schema) + out, err := r.ReadField(address) if err != nil { return FieldReadResult{}, fmt.Errorf( "Error reading level %s: %s", l, err) diff --git a/helper/schema/field_reader_multi_test.go b/helper/schema/field_reader_multi_test.go index 0236b2849..915fb1447 100644 --- a/helper/schema/field_reader_multi_test.go +++ b/helper/schema/field_reader_multi_test.go @@ -11,7 +11,6 @@ import ( func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { cases := map[string]struct { Addr []string - Schema *Schema Readers []FieldReader Level string Result FieldReadResult @@ -19,22 +18,27 @@ func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { "specific": { Addr: []string{"foo"}, - Schema: &Schema{ - Type: TypeString, - }, - Readers: []FieldReader{ &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, Map: map[string]string{ "foo": "bar", }, }, &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, Map: map[string]string{ "foo": "baz", }, }, &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, Map: map[string]string{}, }, }, @@ -61,7 +65,7 @@ func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { Levels: levels, } - out, err := r.ReadFieldExact(tc.Addr, tc.Schema, tc.Level) + out, err := r.ReadFieldExact(tc.Addr, tc.Level) if err != nil { t.Fatalf("%s: err: %s", name, err) } @@ -75,23 +79,22 @@ func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { cases := map[string]struct { Addr []string - Schema *Schema Readers []FieldReader Result FieldReadResult }{ "stringInDiff": { Addr: []string{"availability_zone"}, - Schema: &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - Readers: []FieldReader{ &DiffFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, Map: map[string]string{ "availability_zone": "foo", }, @@ -118,22 +121,27 @@ func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { "lastLevelComputed": { Addr: []string{"availability_zone"}, - Schema: &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - Readers: []FieldReader{ &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + Map: map[string]string{ "availability_zone": "foo", }, }, &DiffFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + Map: map[string]string{ "availability_zone": "foo", }, @@ -161,18 +169,23 @@ func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { "list of maps with removal in diff": { Addr: []string{"config_vars"}, - Schema: &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - Readers: []FieldReader{ &DiffFieldReader{ + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + Map: map[string]string{ "config_vars.#": "2", "config_vars.0.foo": "bar", @@ -207,17 +220,19 @@ func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { "first level only": { Addr: []string{"foo"}, - Schema: &Schema{ - Type: TypeString, - }, - Readers: []FieldReader{ &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, Map: map[string]string{ "foo": "bar", }, }, &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, Map: map[string]string{}, }, }, @@ -243,7 +258,7 @@ func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { Levels: levels, } - out, err := r.ReadFieldMerge(tc.Addr, tc.Schema, levels[len(levels)-1]) + out, err := r.ReadFieldMerge(tc.Addr, levels[len(levels)-1]) if err != nil { t.Fatalf("%s: err: %s", name, err) } diff --git a/helper/schema/field_reader_test.go b/helper/schema/field_reader_test.go new file mode 100644 index 000000000..7498b05f6 --- /dev/null +++ b/helper/schema/field_reader_test.go @@ -0,0 +1,48 @@ +package schema + +import ( + "reflect" + "testing" +) + +func TestAddrToSchema(t *testing.T) { + cases := map[string]struct { + Addr []string + Schema map[string]*Schema + Result *Schema + }{ + "mapElem": { + []string{"map", "foo"}, + map[string]*Schema{ + "map": &Schema{Type: TypeMap}, + }, + &Schema{Type: TypeString}, + }, + + "setDeep": { + []string{"set", "50", "index"}, + map[string]*Schema{ + "set": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{Type: TypeInt}, + "value": &Schema{Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + }, + &Schema{Type: TypeInt}, + }, + } + + for name, tc := range cases { + result := addrToSchema(tc.Addr, tc.Schema) + if !reflect.DeepEqual(result, tc.Result) { + t.Fatalf("%s: %#v", name, result) + } + } +}