Skip to content

Commit 2d74a3c

Browse files
committed
helper/schema: basic set
1 parent bf6ad07 commit 2d74a3c

2 files changed

Lines changed: 184 additions & 2 deletions

File tree

helper/schema/resource_data.go

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import (
66
"strings"
77

88
"github.com/hashicorp/terraform/terraform"
9+
"github.com/mitchellh/mapstructure"
910
)
1011

1112
// ResourceData is used to query and set the attributes of a resource.
1213
type ResourceData struct {
1314
schema map[string]*Schema
1415
state *terraform.ResourceState
1516
diff *terraform.ResourceDiff
17+
set map[string]string
1618
}
1719

1820
// Get returns the data for the given key, or nil if the key doesn't exist.
@@ -29,6 +31,19 @@ func (d *ResourceData) Get(key string) interface{} {
2931
return d.getObject("", parts, d.schema)
3032
}
3133

34+
// Set sets the value for the given key.
35+
//
36+
// If the key is invalid or the value is not a correct type, an error
37+
// will be returned.
38+
func (d *ResourceData) Set(key string, value interface{}) error {
39+
if d.set == nil {
40+
d.set = make(map[string]string)
41+
}
42+
43+
parts := strings.Split(key, ".")
44+
return d.setObject("", parts, d.schema, value)
45+
}
46+
3247
func (d *ResourceData) get(
3348
k string,
3449
parts []string,
@@ -85,7 +100,12 @@ func (d *ResourceData) getList(
85100
// Special case if we're accessing the count of the list
86101
if idx == "#" {
87102
schema := &Schema{Type: TypeInt}
88-
return d.get(k+".#", parts, schema)
103+
result := d.get(k+".#", parts, schema)
104+
if result == nil {
105+
result = 0
106+
}
107+
108+
return result
89109
}
90110

91111
key := fmt.Sprintf("%s.%s", k, idx)
@@ -112,17 +132,30 @@ func (d *ResourceData) getPrimitive(
112132
parts []string,
113133
schema *Schema) interface{} {
114134
var result string
135+
var resultSet bool
115136
if d.state != nil {
116-
result = d.state.Attributes[k]
137+
result, resultSet = d.state.Attributes[k]
117138
}
118139

119140
if d.diff != nil {
120141
attrD, ok := d.diff.Attributes[k]
121142
if ok {
122143
result = attrD.New
144+
resultSet = true
123145
}
124146
}
125147

148+
if d.set != nil {
149+
if v, ok := d.set[k]; ok {
150+
result = v
151+
resultSet = true
152+
}
153+
}
154+
155+
if !resultSet {
156+
return nil
157+
}
158+
126159
switch schema.Type {
127160
case TypeString:
128161
// Use the value as-is. We just put this case here to be explicit.
@@ -142,3 +175,55 @@ func (d *ResourceData) getPrimitive(
142175
panic(fmt.Sprintf("Unknown type: %s", schema.Type))
143176
}
144177
}
178+
179+
func (d *ResourceData) setObject(
180+
k string,
181+
parts []string,
182+
schema map[string]*Schema,
183+
value interface{}) error {
184+
if len(parts) > 0 {
185+
// We're setting a specific key in an object
186+
key := parts[0]
187+
parts = parts[1:]
188+
189+
s, ok := schema[key]
190+
if !ok {
191+
return fmt.Errorf("%s (internal): unknown key to set: %s", k, key)
192+
}
193+
194+
if k != "" {
195+
// If we're not at the root, then we need to append
196+
// the key to get the full key path.
197+
key = fmt.Sprintf("%s.%s", k, key)
198+
}
199+
200+
return d.setPrimitive(key, s, value)
201+
}
202+
203+
panic("can't set full object yet")
204+
}
205+
206+
func (d *ResourceData) setPrimitive(
207+
k string,
208+
schema *Schema,
209+
v interface{}) error {
210+
var set string
211+
switch schema.Type {
212+
case TypeString:
213+
if err := mapstructure.Decode(v, &set); err != nil {
214+
return fmt.Errorf("%s: %s", k, err)
215+
}
216+
case TypeInt:
217+
var n int
218+
if err := mapstructure.Decode(v, &n); err != nil {
219+
return fmt.Errorf("%s: %s", k, err)
220+
}
221+
222+
set = strconv.FormatInt(int64(n), 10)
223+
default:
224+
return fmt.Errorf("Unknown type: %s", schema.Type)
225+
}
226+
227+
d.set[k] = set
228+
return nil
229+
}

helper/schema/resource_data_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,100 @@ func TestResourceDataGet(t *testing.T) {
314314
}
315315
}
316316
}
317+
318+
func TestResourceDataSet(t *testing.T) {
319+
cases := []struct {
320+
Schema map[string]*Schema
321+
State *terraform.ResourceState
322+
Diff *terraform.ResourceDiff
323+
Key string
324+
Value interface{}
325+
Err bool
326+
GetKey string
327+
GetValue interface{}
328+
}{
329+
// Basic good
330+
{
331+
Schema: map[string]*Schema{
332+
"availability_zone": &Schema{
333+
Type: TypeString,
334+
Optional: true,
335+
Computed: true,
336+
ForceNew: true,
337+
},
338+
},
339+
340+
State: nil,
341+
342+
Diff: nil,
343+
344+
Key: "availability_zone",
345+
Value: "foo",
346+
347+
GetKey: "availability_zone",
348+
GetValue: "foo",
349+
},
350+
351+
// Basic int
352+
{
353+
Schema: map[string]*Schema{
354+
"port": &Schema{
355+
Type: TypeInt,
356+
Optional: true,
357+
Computed: true,
358+
ForceNew: true,
359+
},
360+
},
361+
362+
State: nil,
363+
364+
Diff: nil,
365+
366+
Key: "port",
367+
Value: 80,
368+
369+
GetKey: "port",
370+
GetValue: 80,
371+
},
372+
373+
// Invalid type
374+
{
375+
Schema: map[string]*Schema{
376+
"availability_zone": &Schema{
377+
Type: TypeString,
378+
Optional: true,
379+
Computed: true,
380+
ForceNew: true,
381+
},
382+
},
383+
384+
State: nil,
385+
386+
Diff: nil,
387+
388+
Key: "availability_zone",
389+
Value: 80,
390+
Err: true,
391+
392+
GetKey: "availability_zone",
393+
GetValue: nil,
394+
},
395+
}
396+
397+
for i, tc := range cases {
398+
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
399+
if err != nil {
400+
t.Fatalf("err: %s", err)
401+
}
402+
403+
err = d.Set(tc.Key, tc.Value)
404+
if (err != nil) != tc.Err {
405+
t.Fatalf("%d err: %s", i, err)
406+
}
407+
408+
v := d.Get(tc.GetKey)
409+
if !reflect.DeepEqual(v, tc.GetValue) {
410+
t.Fatalf("Get Bad: %d\n\n%#v", i, v)
411+
}
412+
}
413+
}

0 commit comments

Comments
 (0)