Skip to content

Commit eb8b37f

Browse files
committed
handle for_each blocks for param defaults
1 parent dd4ec08 commit eb8b37f

File tree

2 files changed

+168
-58
lines changed

2 files changed

+168
-58
lines changed

hclext/merge.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,16 @@ func InsertTupleElement(list cty.Value, idx int, val cty.Value) cty.Value {
5454
return list
5555
}
5656

57+
// Create a new list of the correct length, copying in the old list
58+
// values for matching indices.
5759
newList := make([]cty.Value, max(idx+1, list.LengthInt()))
58-
for i := 0; i < len(newList); i++ {
59-
newList[i] = cty.NilVal // Always insert a nil by default
60-
61-
if i < list.LengthInt() { // keep the original
62-
newList[i] = list.Index(cty.NumberIntVal(int64(i)))
63-
}
64-
65-
if i == idx { // add the new value
66-
newList[i] = val
67-
}
60+
for it := list.ElementIterator(); it.Next(); {
61+
key, elem := it.Element()
62+
elemIdx, _ := key.AsBigFloat().Int64()
63+
newList[elemIdx] = elem
6864
}
65+
// Insert the new value.
66+
newList[idx] = val
6967

7068
return cty.TupleVal(newList)
7169
}

paramhook.go

Lines changed: 160 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,6 @@ func ParameterContextsEvalHook(input Input) func(ctx *tfcontext.Context, blocks
2626
continue // Wow a value exists?!. This feels like a bug.
2727
}
2828

29-
countAttr, countExists := block.Attributes()["count"]
30-
if countExists {
31-
// Omit count = 0 values!
32-
countVal := countAttr.Value()
33-
if !countVal.Type().Equals(cty.Number) {
34-
continue // Probably unknown
35-
}
36-
v, _ := countVal.AsBigFloat().Int64()
37-
if v < 1 {
38-
// Non-one counts are incorrect
39-
// Zero counts are ignored as the blocks are omitted
40-
continue
41-
}
42-
}
43-
4429
nameAttr := block.GetAttribute("name")
4530
nameVal := nameAttr.Value()
4631
if !nameVal.Type().Equals(cty.String) {
@@ -64,54 +49,181 @@ func ParameterContextsEvalHook(input Input) func(ctx *tfcontext.Context, blocks
6449
}
6550
}
6651

67-
// Set the default value as the 'value' attribute
6852
path := []string{
6953
"data",
7054
"coder_parameter",
7155
block.Reference().NameLabel(),
7256
}
73-
if countExists {
74-
// Append to the existing tuple
75-
existing := ctx.Get(path...)
76-
if existing.IsNull() {
77-
continue
78-
}
57+
existing := ctx.Get(path...)
58+
obj, ok := mergeParamInstanceValues(block, existing, value)
59+
if !ok {
60+
continue
61+
}
62+
ctx.Set(obj, path...)
63+
//
64+
//ref := block.Reference()
65+
//refKey := ref.RawKey()
66+
//
67+
//fmt.Println(refKey)
68+
//
69+
//countAttr, countExists := block.Attributes()["count"]
70+
//if countExists {
71+
// // Omit count = 0 values!
72+
// countVal := countAttr.Value()
73+
// if !countVal.Type().Equals(cty.Number) {
74+
// continue // Probably unknown
75+
// }
76+
// v, _ := countVal.AsBigFloat().Int64()
77+
// if v < 1 {
78+
// // Non-one counts are incorrect
79+
// // Zero counts are ignored as the blocks are omitted
80+
// continue
81+
// }
82+
//}
83+
//
84+
//nameAttr := block.GetAttribute("name")
85+
//nameVal := nameAttr.Value()
86+
//if !nameVal.Type().Equals(cty.String) {
87+
// continue // Ignore the errors at this point
88+
//}
89+
//
90+
//// Set the default value as the 'value' attribute
91+
//path := []string{
92+
// "data",
93+
// "coder_parameter",
94+
// block.Reference().NameLabel(),
95+
//}
96+
//if countExists {
97+
// // Append to the existing tuple
98+
// existing := ctx.Get(path...)
99+
// if existing.IsNull() {
100+
// continue
101+
// }
102+
//
103+
// if !existing.Type().IsTupleType() {
104+
// continue
105+
// }
106+
//
107+
// if existing.LengthInt() > 1 {
108+
// // coder_parameters can only ever have a count of 0 or 1.
109+
// // More than that is invalid. So ignore invalid blocks.
110+
// continue
111+
// }
112+
//
113+
// it := existing.ElementIterator()
114+
// if !it.Next() {
115+
// continue
116+
// }
117+
//
118+
// _, v := it.Element()
119+
// merged := hclext.MergeObjects(v, cty.ObjectVal(map[string]cty.Value{
120+
// "value": value,
121+
// }))
122+
//
123+
// // Since our count can only equal 1, we can safely set the
124+
// // value to a tuple of length 1 in all cases.
125+
// ctx.Set(cty.TupleVal([]cty.Value{merged}), path...)
126+
// continue
127+
//}
128+
//
129+
//path = append(path, "value")
130+
//// The current context is in the `coder_parameter` block.
131+
//// Use the parent context to "export" the value
132+
//ctx.Set(value, path...)
133+
//block.Context().Parent().Set(value, path...)
134+
}
135+
}
136+
}
79137

80-
if !existing.Type().IsTupleType() {
81-
continue
82-
}
138+
func mergeParamInstanceValues(b *terraform.Block, existing cty.Value, value cty.Value) (cty.Value, bool) {
139+
if existing.IsNull() {
140+
return existing, false
141+
}
83142

84-
if existing.LengthInt() > 1 {
85-
// coder_parameters can only ever have a count of 0 or 1.
86-
// More than that is invalid. So ignore invalid blocks.
87-
continue
88-
}
143+
ref := b.Reference()
144+
key := ref.RawKey()
89145

90-
it := existing.ElementIterator()
91-
if !it.Next() {
92-
continue
93-
}
146+
switch {
147+
case key.Type().Equals(cty.Number) && b.GetAttribute("count") != nil:
148+
if !existing.Type().IsTupleType() {
149+
return existing, false
150+
}
94151

95-
_, v := it.Element()
96-
merged := hclext.MergeObjects(v, cty.ObjectVal(map[string]cty.Value{
97-
"value": value,
98-
}))
152+
idx, _ := key.AsBigFloat().Int64()
153+
elem := existing.Index(key)
154+
if elem.IsNull() || !elem.IsKnown() {
155+
return existing, false
156+
}
99157

100-
// Since our count can only equal 1, we can safely set the
101-
// value to a tuple of length 1 in all cases.
102-
ctx.Set(cty.TupleVal([]cty.Value{merged}), path...)
103-
continue
104-
}
158+
obj, ok := setObjectField(elem, "value", value)
159+
if !ok {
160+
return existing, false
161+
}
105162

106-
path = append(path, "value")
107-
// The current context is in the `coder_parameter` block.
108-
// Use the parent context to "export" the value
109-
ctx.Set(value, path...)
110-
//block.Context().Parent().Set(value, path...)
163+
return hclext.InsertTupleElement(existing, int(idx), obj), true
164+
case isForEachKey(key) && b.GetAttribute("for_each") != nil:
165+
keyStr := ref.Key()
166+
if !existing.Type().IsObjectType() {
167+
return existing, false
168+
}
169+
170+
if !existing.CanIterateElements() {
171+
return existing, false
172+
}
173+
174+
instances := existing.AsValueMap()
175+
if instances == nil {
176+
return existing, false
177+
}
178+
179+
instance, ok := instances[keyStr]
180+
if !ok {
181+
return existing, false
182+
}
183+
184+
instance, ok = setObjectField(instance, "value", value)
185+
if !ok {
186+
return existing, false
187+
}
188+
189+
instances[keyStr] = instance
190+
return cty.ObjectVal(instances), true
191+
192+
default:
193+
obj, ok := setObjectField(existing, "value", value)
194+
if !ok {
195+
return existing, false
111196
}
197+
return obj, true
112198
}
113199
}
114200

201+
func setObjectField(object cty.Value, field string, value cty.Value) (cty.Value, bool) {
202+
if object.IsNull() {
203+
return object, false
204+
}
205+
206+
if !object.Type().IsObjectType() {
207+
return object, false
208+
}
209+
210+
if !object.CanIterateElements() {
211+
return object, false
212+
}
213+
214+
instances := object.AsValueMap()
215+
if instances == nil {
216+
return object, false
217+
}
218+
219+
instances[field] = value
220+
return cty.ObjectVal(instances), true
221+
}
222+
223+
func isForEachKey(key cty.Value) bool {
224+
return key.Type().Equals(cty.Number) || key.Type().Equals(cty.String)
225+
}
226+
115227
func evaluateCoderParameterDefault(b *terraform.Block) (cty.Value, bool) {
116228
attributes := b.Attributes()
117229

0 commit comments

Comments
 (0)