helper/schema: can handle maps

This commit is contained in:
Mitchell Hashimoto 2014-08-18 14:00:03 -07:00
parent 66d7003e6f
commit 4c9271160e
3 changed files with 263 additions and 0 deletions

View File

@ -121,11 +121,74 @@ func (d *ResourceData) get(
switch schema.Type {
case TypeList:
return d.getList(k, parts, schema, source)
case TypeMap:
return d.getMap(k, parts, schema, source)
default:
return d.getPrimitive(k, parts, schema, source)
}
}
func (d *ResourceData) getMap(
k string,
parts []string,
schema *Schema,
source getSource) interface{} {
elemSchema := &Schema{Type: TypeString}
// Get the full map
var result map[string]interface{}
prefix := k + "."
// Try set first
if d.setMap != nil && source >= getSourceSet {
for k, _ := range d.setMap {
if !strings.HasPrefix(k, prefix) {
continue
}
single := k[len(prefix):]
if result == nil {
result = make(map[string]interface{})
}
result[single] = d.getPrimitive(k, nil, elemSchema, source)
}
}
if result == nil && d.diff != nil && source >= getSourceDiff {
for k, _ := range d.diff.Attributes {
if !strings.HasPrefix(k, prefix) {
continue
}
single := k[len(prefix):]
if result == nil {
result = make(map[string]interface{})
}
result[single] = d.getPrimitive(k, nil, elemSchema, source)
}
}
if result == nil && d.state != nil && source >= getSourceState {
for k, _ := range d.state.Attributes {
if !strings.HasPrefix(k, prefix) {
continue
}
single := k[len(prefix):]
if result == nil {
result = make(map[string]interface{})
}
result[single] = d.getPrimitive(k, nil, elemSchema, source)
}
}
return result
}
func (d *ResourceData) getObject(
k string,
parts []string,
@ -259,6 +322,8 @@ func (d *ResourceData) set(
switch schema.Type {
case TypeList:
return d.setList(k, parts, schema, value)
case TypeMap:
return d.setMapValue(k, parts, schema, value)
default:
return d.setPrimitive(k, schema, value)
}
@ -315,6 +380,27 @@ func (d *ResourceData) setList(
return nil
}
func (d *ResourceData) setMapValue(
k string,
parts []string,
schema *Schema,
value interface{}) error {
elemSchema := &Schema{Type: TypeString}
if len(parts) > 0 {
return fmt.Errorf("%s: full map must be set, no a single element", k)
}
vs := value.(map[string]interface{})
for subKey, v := range vs {
err := d.set(fmt.Sprintf("%s.%s", k, subKey), nil, elemSchema, v)
if err != nil {
return err
}
}
return nil
}
func (d *ResourceData) setObject(
k string,
parts []string,
@ -422,6 +508,26 @@ func (d *ResourceData) stateList(
return result
}
func (d *ResourceData) stateMap(
prefix string,
schema *Schema) map[string]string {
v := d.getMap(prefix, nil, schema, getSourceSet)
if v == nil {
return nil
}
elemSchema := &Schema{Type: TypeString}
result := make(map[string]string)
for mk, _ := range v.(map[string]interface{}) {
mp := fmt.Sprintf("%s.%s", prefix, mk)
for k, v := range d.stateSingle(mp, elemSchema) {
result[k] = v
}
}
return result
}
func (d *ResourceData) stateObject(
prefix string,
schema map[string]*Schema) map[string]string {
@ -469,6 +575,8 @@ func (d *ResourceData) stateSingle(
switch schema.Type {
case TypeList:
return d.stateList(prefix, schema)
case TypeMap:
return d.stateMap(prefix, schema)
default:
return d.statePrimitive(prefix, schema)
}

View File

@ -300,6 +300,85 @@ func TestResourceDataGet(t *testing.T) {
"availability_zone": "foo",
},
},
// List of maps
{
Schema: map[string]*Schema{
"config_vars": &Schema{
Type: TypeList,
Optional: true,
Computed: true,
Elem: &Schema{
Type: TypeMap,
},
},
},
State: nil,
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"config_vars.#": &terraform.ResourceAttrDiff{
Old: "0",
New: "2",
},
"config_vars.0.foo": &terraform.ResourceAttrDiff{
Old: "",
New: "bar",
},
"config_vars.1.bar": &terraform.ResourceAttrDiff{
Old: "",
New: "baz",
},
},
},
Key: "config_vars",
Value: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"bar": "baz",
},
},
},
// List of maps in state
{
Schema: map[string]*Schema{
"config_vars": &Schema{
Type: TypeList,
Optional: true,
Computed: true,
Elem: &Schema{
Type: TypeMap,
},
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"config_vars.#": "2",
"config_vars.0.foo": "baz",
"config_vars.1.bar": "bar",
},
},
Diff: nil,
Key: "config_vars",
Value: []interface{}{
map[string]interface{}{
"foo": "baz",
},
map[string]interface{}{
"bar": "bar",
},
},
},
}
for i, tc := range cases {
@ -762,6 +841,45 @@ func TestResourceDataSet(t *testing.T) {
},
},
},
// Set a list of maps
{
Schema: map[string]*Schema{
"config_vars": &Schema{
Type: TypeList,
Optional: true,
Computed: true,
Elem: &Schema{
Type: TypeMap,
},
},
},
State: nil,
Diff: nil,
Key: "config_vars",
Value: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"bar": "baz",
},
},
Err: false,
GetKey: "config_vars",
GetValue: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"bar": "baz",
},
},
},
}
for i, tc := range cases {
@ -942,6 +1060,42 @@ func TestResourceDataState(t *testing.T) {
},
},
},
// List of maps
{
Schema: map[string]*Schema{
"config_vars": &Schema{
Type: TypeList,
Optional: true,
Computed: true,
Elem: &Schema{
Type: TypeMap,
},
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"config_vars.#": "2",
"config_vars.0.foo": "bar",
"config_vars.1.bar": "baz",
},
},
Set: map[string]interface{}{
"config_vars.1": map[string]interface{}{
"baz": "bang",
},
},
Result: &terraform.ResourceState{
Attributes: map[string]string{
"config_vars.#": "2",
"config_vars.0.foo": "bar",
"config_vars.1.baz": "bang",
},
},
},
}
for i, tc := range cases {

View File

@ -17,6 +17,7 @@ const (
TypeInt
TypeString
TypeList
TypeMap
)
// Schema is used to describe the structure of a value.