helper/schema: field readers no longer take a schema as arg
This commit is contained in:
parent
3ff859d734
commit
7e379cb1a1
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue