helper/schema: Set object

This commit is contained in:
Mitchell Hashimoto 2014-08-17 11:38:16 -07:00
parent 2d74a3cadd
commit 06d30a559a
2 changed files with 301 additions and 8 deletions

View File

@ -14,7 +14,7 @@ type ResourceData struct {
schema map[string]*Schema schema map[string]*Schema
state *terraform.ResourceState state *terraform.ResourceState
diff *terraform.ResourceDiff diff *terraform.ResourceDiff
set map[string]string setMap map[string]string
} }
// Get returns the data for the given key, or nil if the key doesn't exist. // Get returns the data for the given key, or nil if the key doesn't exist.
@ -36,8 +36,8 @@ func (d *ResourceData) Get(key string) interface{} {
// If the key is invalid or the value is not a correct type, an error // If the key is invalid or the value is not a correct type, an error
// will be returned. // will be returned.
func (d *ResourceData) Set(key string, value interface{}) error { func (d *ResourceData) Set(key string, value interface{}) error {
if d.set == nil { if d.setMap == nil {
d.set = make(map[string]string) d.setMap = make(map[string]string)
} }
parts := strings.Split(key, ".") parts := strings.Split(key, ".")
@ -145,8 +145,8 @@ func (d *ResourceData) getPrimitive(
} }
} }
if d.set != nil { if d.setMap != nil {
if v, ok := d.set[k]; ok { if v, ok := d.setMap[k]; ok {
result = v result = v
resultSet = true resultSet = true
} }
@ -176,6 +176,70 @@ func (d *ResourceData) getPrimitive(
} }
} }
func (d *ResourceData) set(
k string,
parts []string,
schema *Schema,
value interface{}) error {
switch schema.Type {
case TypeList:
return d.setList(k, parts, schema, value)
default:
return d.setPrimitive(k, schema, value)
}
}
func (d *ResourceData) setList(
k string,
parts []string,
schema *Schema,
value interface{}) error {
if len(parts) > 0 {
// We're setting a specific element
idx := parts[0]
parts = parts[1:]
// Special case if we're accessing the count of the list
if idx == "#" {
return fmt.Errorf("%s: can't set count of list", k)
}
key := fmt.Sprintf("%s.%s", k, idx)
switch t := schema.Elem.(type) {
case *Resource:
return d.setObject(key, parts, t.Schema, value)
case *Schema:
return d.set(key, parts, t, value)
}
}
var vs []interface{}
if err := mapstructure.Decode(value, &vs); err != nil {
return fmt.Errorf("%s: %s", k, err)
}
// Set the entire list.
var err error
for i, elem := range vs {
is := strconv.FormatInt(int64(i), 10)
err = d.setList(k, []string{is}, schema, elem)
if err != nil {
break
}
}
if err != nil {
for i, _ := range vs {
is := strconv.FormatInt(int64(i), 10)
d.setList(k, []string{is}, schema, nil)
}
return err
}
d.setMap[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
return nil
}
func (d *ResourceData) setObject( func (d *ResourceData) setObject(
k string, k string,
parts []string, parts []string,
@ -197,16 +261,41 @@ func (d *ResourceData) setObject(
key = fmt.Sprintf("%s.%s", k, key) key = fmt.Sprintf("%s.%s", k, key)
} }
return d.setPrimitive(key, s, value) return d.set(key, parts, s, value)
} }
panic("can't set full object yet") // Set the entire object. First decode into a proper structure
var v map[string]interface{}
if err := mapstructure.Decode(value, &v); err != nil {
return fmt.Errorf("%s: %s", k, err)
}
// Set each element in turn
var err error
for k1, v1 := range v {
err = d.setObject(k, []string{k1}, schema, v1)
if err != nil {
break
}
}
if err != nil {
for k1, _ := range v {
d.setObject(k, []string{k1}, schema, nil)
}
}
return err
} }
func (d *ResourceData) setPrimitive( func (d *ResourceData) setPrimitive(
k string, k string,
schema *Schema, schema *Schema,
v interface{}) error { v interface{}) error {
if v == nil {
delete(d.setMap, k)
return nil
}
var set string var set string
switch schema.Type { switch schema.Type {
case TypeString: case TypeString:
@ -224,6 +313,6 @@ func (d *ResourceData) setPrimitive(
return fmt.Errorf("Unknown type: %s", schema.Type) return fmt.Errorf("Unknown type: %s", schema.Type)
} }
d.set[k] = set d.setMap[k] = set
return nil return nil
} }

View File

@ -392,6 +392,210 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone", GetKey: "availability_zone",
GetValue: nil, GetValue: nil,
}, },
// List of primitives, set element
{
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeList,
Computed: true,
Elem: &Schema{Type: TypeInt},
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"ports.#": "3",
"ports.0": "1",
"ports.1": "2",
"ports.2": "5",
},
},
Diff: nil,
Key: "ports.1",
Value: 3,
GetKey: "ports",
GetValue: []interface{}{1, 3, 5},
},
// List of primitives, set list
{
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeList,
Computed: true,
Elem: &Schema{Type: TypeInt},
},
},
State: nil,
Diff: nil,
Key: "ports",
Value: []int{1, 2, 5},
GetKey: "ports",
GetValue: []interface{}{1, 2, 5},
},
// List of primitives, set list with error
{
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeList,
Computed: true,
Elem: &Schema{Type: TypeInt},
},
},
State: nil,
Diff: nil,
Key: "ports",
Value: []interface{}{1, "NOPE", 5},
Err: true,
GetKey: "ports",
GetValue: []interface{}{},
},
// List of resource, set element
{
Schema: map[string]*Schema{
"ingress": &Schema{
Type: TypeList,
Computed: true,
Elem: &Resource{
Schema: map[string]*Schema{
"from": &Schema{
Type: TypeInt,
},
},
},
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"ingress.#": "2",
"ingress.0.from": "80",
"ingress.1.from": "8080",
},
},
Diff: nil,
Key: "ingress.1.from",
Value: 9000,
GetKey: "ingress",
GetValue: []interface{}{
map[string]interface{}{
"from": 80,
},
map[string]interface{}{
"from": 9000,
},
},
},
// List of resource, set full resource element
{
Schema: map[string]*Schema{
"ingress": &Schema{
Type: TypeList,
Computed: true,
Elem: &Resource{
Schema: map[string]*Schema{
"from": &Schema{
Type: TypeInt,
},
},
},
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"ingress.#": "2",
"ingress.0.from": "80",
"ingress.1.from": "8080",
},
},
Diff: nil,
Key: "ingress.1",
Value: map[string]interface{}{
"from": 9000,
},
GetKey: "ingress",
GetValue: []interface{}{
map[string]interface{}{
"from": 80,
},
map[string]interface{}{
"from": 9000,
},
},
},
// List of resource, set full resource element, with error
{
Schema: map[string]*Schema{
"ingress": &Schema{
Type: TypeList,
Computed: true,
Elem: &Resource{
Schema: map[string]*Schema{
"from": &Schema{
Type: TypeInt,
},
"to": &Schema{
Type: TypeInt,
},
},
},
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"ingress.#": "2",
"ingress.0.from": "80",
"ingress.0.to": "10",
"ingress.1.from": "8080",
"ingress.1.to": "8080",
},
},
Diff: nil,
Key: "ingress.1",
Value: map[string]interface{}{
"from": 9000,
"to": "bar",
},
Err: true,
GetKey: "ingress",
GetValue: []interface{}{
map[string]interface{}{
"from": 80,
"to": 10,
},
map[string]interface{}{
"from": 8080,
"to": 8080,
},
},
},
} }
for i, tc := range cases { for i, tc := range cases {