helper/schema: nested resource fields should be zero-valued on get
This commit is contained in:
parent
942a988ac2
commit
f0af1c36f5
|
@ -31,6 +31,24 @@ type FieldReadResult struct {
|
|||
Computed bool
|
||||
}
|
||||
|
||||
// ValueOrZero returns the value of this result or the zero value of the
|
||||
// schema type, ensuring a consistent non-nil return value.
|
||||
func (r *FieldReadResult) ValueOrZero(s *Schema) interface{} {
|
||||
if r.Value != nil {
|
||||
return r.Value
|
||||
}
|
||||
|
||||
result := s.Type.Zero()
|
||||
|
||||
// The zero value of a set is nil, but we want it
|
||||
// to actually be an empty set object...
|
||||
if s.Type == TypeSet && result == nil {
|
||||
result = &Set{F: s.Set}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// addrToSchema finds the final element schema for the given address
|
||||
// and the given schema. It returns all the schemas that led to the final
|
||||
// schema. These are in order of the address (out to in).
|
||||
|
@ -182,7 +200,8 @@ func readObjectField(
|
|||
addr []string,
|
||||
schema map[string]*Schema) (FieldReadResult, error) {
|
||||
result := make(map[string]interface{})
|
||||
for field, _ := range schema {
|
||||
exists := false
|
||||
for field, s := range schema {
|
||||
addrRead := make([]string, len(addr), len(addr)+1)
|
||||
copy(addrRead, addr)
|
||||
addrRead = append(addrRead, field)
|
||||
|
@ -190,16 +209,16 @@ func readObjectField(
|
|||
if err != nil {
|
||||
return FieldReadResult{}, err
|
||||
}
|
||||
if !rawResult.Exists {
|
||||
continue
|
||||
if rawResult.Exists {
|
||||
exists = true
|
||||
}
|
||||
|
||||
result[field] = rawResult.Value
|
||||
result[field] = rawResult.ValueOrZero(s)
|
||||
}
|
||||
|
||||
return FieldReadResult{
|
||||
Value: result,
|
||||
Exists: len(schema) > 0 && len(result) > 0,
|
||||
Exists: exists,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -362,13 +362,7 @@ func (d *ResourceData) get(
|
|||
if result.Value == nil {
|
||||
if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
|
||||
schema := schemaL[len(schemaL)-1]
|
||||
result.Value = schema.Type.Zero()
|
||||
|
||||
// The zero value of a set is nil, but we want it
|
||||
// to actually be an empty set object...
|
||||
if schema.Type == TypeSet && result.Value == nil {
|
||||
result.Value = &Set{F: schema.Set}
|
||||
}
|
||||
result.Value = result.ValueOrZero(schema)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -998,8 +998,12 @@ func TestResourceDataSet(t *testing.T) {
|
|||
Err bool
|
||||
GetKey string
|
||||
GetValue interface{}
|
||||
|
||||
// GetPreProcess can be set to munge the return value before being
|
||||
// compared to GetValue
|
||||
GetPreProcess func(interface{}) interface{}
|
||||
}{
|
||||
// Basic good
|
||||
// #0: Basic good
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"availability_zone": &Schema{
|
||||
|
@ -1021,7 +1025,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: "foo",
|
||||
},
|
||||
|
||||
// Basic int
|
||||
// #1: Basic int
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"port": &Schema{
|
||||
|
@ -1043,7 +1047,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: 80,
|
||||
},
|
||||
|
||||
// Basic bool
|
||||
// #2: Basic bool
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"vpc": &Schema{
|
||||
|
@ -1063,6 +1067,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: true,
|
||||
},
|
||||
|
||||
// #3
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"vpc": &Schema{
|
||||
|
@ -1082,7 +1087,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: false,
|
||||
},
|
||||
|
||||
// Invalid type
|
||||
// #4: Invalid type
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"availability_zone": &Schema{
|
||||
|
@ -1105,7 +1110,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: "",
|
||||
},
|
||||
|
||||
// List of primitives, set list
|
||||
// #5: List of primitives, set list
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"ports": &Schema{
|
||||
|
@ -1126,7 +1131,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: []interface{}{1, 2, 5},
|
||||
},
|
||||
|
||||
// List of primitives, set list with error
|
||||
// #6: List of primitives, set list with error
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"ports": &Schema{
|
||||
|
@ -1148,7 +1153,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: []interface{}{},
|
||||
},
|
||||
|
||||
// Set a list of maps
|
||||
// #7: Set a list of maps
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"config_vars": &Schema{
|
||||
|
@ -1187,7 +1192,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
},
|
||||
},
|
||||
|
||||
// Set, with list
|
||||
// #8: Set, with list
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"ports": &Schema{
|
||||
|
@ -1217,7 +1222,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: []interface{}{100, 125},
|
||||
},
|
||||
|
||||
// Set, with Set
|
||||
// #9: Set, with Set
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"ports": &Schema{
|
||||
|
@ -1252,7 +1257,7 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetValue: []interface{}{1, 2},
|
||||
},
|
||||
|
||||
// Set single item
|
||||
// #10: Set single item
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"ports": &Schema{
|
||||
|
@ -1281,6 +1286,74 @@ func TestResourceDataSet(t *testing.T) {
|
|||
GetKey: "ports",
|
||||
GetValue: []interface{}{80, 100},
|
||||
},
|
||||
|
||||
// #11: Set with nested set
|
||||
{
|
||||
Schema: map[string]*Schema{
|
||||
"ports": &Schema{
|
||||
Type: TypeSet,
|
||||
Elem: &Resource{
|
||||
Schema: map[string]*Schema{
|
||||
"port": &Schema{
|
||||
Type: TypeInt,
|
||||
},
|
||||
|
||||
"set": &Schema{
|
||||
Type: TypeSet,
|
||||
Elem: &Schema{Type: TypeInt},
|
||||
Set: func(a interface{}) int {
|
||||
return a.(int)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Set: func(a interface{}) int {
|
||||
return a.(map[string]interface{})["port"].(int)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
State: nil,
|
||||
|
||||
Key: "ports",
|
||||
Value: []interface{}{
|
||||
map[string]interface{}{
|
||||
"port": 80,
|
||||
},
|
||||
},
|
||||
|
||||
GetKey: "ports",
|
||||
GetValue: []interface{}{
|
||||
map[string]interface{}{
|
||||
"port": 80,
|
||||
"set": []interface{}{},
|
||||
},
|
||||
},
|
||||
|
||||
GetPreProcess: func(v interface{}) interface{} {
|
||||
if v == nil {
|
||||
return v
|
||||
}
|
||||
s, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
for _, v := range s {
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if m["set"] == nil {
|
||||
continue
|
||||
}
|
||||
if s, ok := m["set"].(*Set); ok {
|
||||
m["set"] = s.List()
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
|
@ -1298,6 +1371,11 @@ func TestResourceDataSet(t *testing.T) {
|
|||
if s, ok := v.(*Set); ok {
|
||||
v = s.List()
|
||||
}
|
||||
|
||||
if tc.GetPreProcess != nil {
|
||||
v = tc.GetPreProcess(v)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(v, tc.GetValue) {
|
||||
t.Fatalf("Get Bad: %d\n\n%#v", i, v)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue