helper/schema: ResourceData, starting tests
This commit is contained in:
parent
660dc68a86
commit
31067ee8f6
|
@ -3,6 +3,8 @@ package schema
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The functions below are the CRUD function types for a Resource.
|
// The functions below are the CRUD function types for a Resource.
|
||||||
|
@ -27,6 +29,14 @@ type Resource struct {
|
||||||
Delete DeleteFunc
|
Delete DeleteFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diff returns a diff of this resource and is API compatible with the
|
||||||
|
// ResourceProvider interface.
|
||||||
|
func (r *Resource) Diff(
|
||||||
|
s *terraform.ResourceState,
|
||||||
|
c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
|
||||||
|
return schemaMap(r.Schema).Diff(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
// InternalValidate should be called to validate the structure
|
// InternalValidate should be called to validate the structure
|
||||||
// of the resource.
|
// of the resource.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,9 +1,119 @@
|
||||||
package schema
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
// ResourceData is used to query and set the attributes of a resource.
|
// ResourceData is used to query and set the attributes of a resource.
|
||||||
type ResourceData struct{}
|
type ResourceData struct {
|
||||||
|
schema map[string]*Schema
|
||||||
|
state *terraform.ResourceState
|
||||||
|
diff *terraform.ResourceDiff
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
func (d *ResourceData) Get(key string) interface{} {
|
func (d *ResourceData) Get(key string) interface{} {
|
||||||
|
parts := strings.Split(key, ".")
|
||||||
|
return d.getObject("", parts, d.schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ResourceData) get(
|
||||||
|
k string,
|
||||||
|
parts []string,
|
||||||
|
schema *Schema) interface{} {
|
||||||
|
switch schema.Type {
|
||||||
|
case TypeList:
|
||||||
|
return d.getList(k, parts, schema)
|
||||||
|
default:
|
||||||
|
return d.getPrimitive(k, parts, schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ResourceData) getObject(
|
||||||
|
k string,
|
||||||
|
parts []string,
|
||||||
|
schema map[string]*Schema) interface{} {
|
||||||
|
key := parts[0]
|
||||||
|
parts = parts[1:]
|
||||||
|
s, ok := schema[key]
|
||||||
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return d.get(key, parts, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ResourceData) getList(
|
||||||
|
k string,
|
||||||
|
parts []string,
|
||||||
|
schema *Schema) interface{} {
|
||||||
|
if len(parts) > 0 {
|
||||||
|
// We still have parts left over meaning we're accessing an
|
||||||
|
// element of this list.
|
||||||
|
idx := parts[0]
|
||||||
|
parts = parts[1:]
|
||||||
|
|
||||||
|
// Special case if we're accessing the count of the list
|
||||||
|
if idx == "#" {
|
||||||
|
schema := &Schema{Type: TypeInt}
|
||||||
|
return d.get(k+".#", parts, schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := fmt.Sprintf("%s.%s", k, idx)
|
||||||
|
switch t := schema.Elem.(type) {
|
||||||
|
case *Resource:
|
||||||
|
return d.getObject(key, parts, t.Schema)
|
||||||
|
case *Schema:
|
||||||
|
return d.get(key, parts, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the entire list.
|
||||||
|
result := make([]interface{}, d.getList(k, []string{"#"}, schema).(int))
|
||||||
|
for i, _ := range result {
|
||||||
|
is := strconv.FormatInt(int64(i), 10)
|
||||||
|
result[i] = d.getList(k, []string{is}, schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ResourceData) getPrimitive(
|
||||||
|
k string,
|
||||||
|
parts []string,
|
||||||
|
schema *Schema) interface{} {
|
||||||
|
var result string
|
||||||
|
if d.state != nil {
|
||||||
|
result = d.state.Attributes[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.diff != nil {
|
||||||
|
attrD, ok := d.diff.Attributes[k]
|
||||||
|
if ok {
|
||||||
|
result = attrD.New
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch schema.Type {
|
||||||
|
case TypeString:
|
||||||
|
// Use the value as-is. We just put this case here to be explicit.
|
||||||
|
return result
|
||||||
|
case TypeInt:
|
||||||
|
if result == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := strconv.ParseInt(result, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown type: %s", schema.Type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResourceDataGet(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Schema map[string]*Schema
|
||||||
|
State *terraform.ResourceState
|
||||||
|
Diff *terraform.ResourceDiff
|
||||||
|
Key string
|
||||||
|
Value interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"availability_zone": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: nil,
|
||||||
|
|
||||||
|
Diff: &terraform.ResourceDiff{
|
||||||
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
||||||
|
"availability_zone": &terraform.ResourceAttrDiff{
|
||||||
|
Old: "",
|
||||||
|
New: "foo",
|
||||||
|
RequiresNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Key: "availability_zone",
|
||||||
|
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"availability_zone": &Schema{
|
||||||
|
Type: TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: &terraform.ResourceState{
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"availability_zone": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Diff: nil,
|
||||||
|
|
||||||
|
Key: "availability_zone",
|
||||||
|
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"port": &Schema{
|
||||||
|
Type: TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: &terraform.ResourceState{
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"port": "80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Diff: nil,
|
||||||
|
|
||||||
|
Key: "port",
|
||||||
|
|
||||||
|
Value: 80,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"ports": &Schema{
|
||||||
|
Type: TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &Schema{Type: TypeInt},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: &terraform.ResourceState{
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"ports.#": "3",
|
||||||
|
"ports.0": "1",
|
||||||
|
"ports.1": "2",
|
||||||
|
"ports.2": "5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Key: "ports.1",
|
||||||
|
|
||||||
|
Value: 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"ports": &Schema{
|
||||||
|
Type: TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &Schema{Type: TypeInt},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: &terraform.ResourceState{
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"ports.#": "3",
|
||||||
|
"ports.0": "1",
|
||||||
|
"ports.1": "2",
|
||||||
|
"ports.2": "5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Key: "ports.#",
|
||||||
|
|
||||||
|
Value: 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"ports": &Schema{
|
||||||
|
Type: TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &Schema{Type: TypeInt},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: nil,
|
||||||
|
|
||||||
|
Key: "ports.#",
|
||||||
|
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Schema: map[string]*Schema{
|
||||||
|
"ports": &Schema{
|
||||||
|
Type: TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &Schema{Type: TypeInt},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
State: &terraform.ResourceState{
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"ports.#": "3",
|
||||||
|
"ports.0": "1",
|
||||||
|
"ports.1": "2",
|
||||||
|
"ports.2": "5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Key: "ports",
|
||||||
|
|
||||||
|
Value: []interface{}{1, 2, 5},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := d.Get(tc.Key)
|
||||||
|
if !reflect.DeepEqual(v, tc.Value) {
|
||||||
|
t.Fatalf("Bad: %d\n\n%#v", i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,8 @@ type Schema struct {
|
||||||
Computed bool
|
Computed bool
|
||||||
ForceNew bool
|
ForceNew bool
|
||||||
|
|
||||||
|
// The following fields are only set for a TypeList Type.
|
||||||
|
//
|
||||||
// Elem must be either a *Schema or a *Resource only if the Type is
|
// Elem must be either a *Schema or a *Resource only if the Type is
|
||||||
// TypeList, and represents what the element type is. If it is *Schema,
|
// TypeList, and represents what the element type is. If it is *Schema,
|
||||||
// the element type is just a simple value. If it is *Resource, the
|
// the element type is just a simple value. If it is *Resource, the
|
||||||
|
@ -70,7 +72,11 @@ type schemaMap map[string]*Schema
|
||||||
func (m schemaMap) Data(
|
func (m schemaMap) Data(
|
||||||
s *terraform.ResourceState,
|
s *terraform.ResourceState,
|
||||||
d *terraform.ResourceDiff) (*ResourceData, error) {
|
d *terraform.ResourceDiff) (*ResourceData, error) {
|
||||||
return nil, nil
|
return &ResourceData{
|
||||||
|
schema: m,
|
||||||
|
state: s,
|
||||||
|
diff: d,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diff returns the diff for a resource given the schema map,
|
// Diff returns the diff for a resource given the schema map,
|
||||||
|
|
Loading…
Reference in New Issue