Skip to content

Commit ea68d79

Browse files
committed
nested object values can be computed
While blocks were not allowed to be computed by the provider, nested objects can be. Remove the errors regarding blocks and verify unknown values are valid.
1 parent 9037970 commit ea68d79

2 files changed

Lines changed: 119 additions & 33 deletions

File tree

‎internal/plans/objchange/plan_valid.go‎

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ func AssertPlanValid(schema *configschema.Block, priorState, config, plannedStat
3939
func assertPlanValid(schema *configschema.Block, priorState, config, plannedState cty.Value, path cty.Path) []error {
4040
var errs []error
4141
if plannedState.IsNull() && !config.IsNull() {
42-
errs = append(errs, path.NewErrorf("planned for absense but config wants existence"))
42+
errs = append(errs, path.NewErrorf("planned for absence but config wants existence"))
4343
return errs
4444
}
4545
if config.IsNull() && !plannedState.IsNull() {
46-
errs = append(errs, path.NewErrorf("planned for existence but config wants absense"))
46+
errs = append(errs, path.NewErrorf("planned for existence but config wants absence"))
4747
return errs
4848
}
4949
if plannedState.IsNull() {
@@ -286,6 +286,11 @@ func assertPlannedValueValid(attrS *configschema.Attribute, priorV, configV, pla
286286
}
287287
return errs
288288
}
289+
} else {
290+
if attrS.Computed {
291+
errs = append(errs, path.NewErrorf("configuration present for computed attribute"))
292+
return errs
293+
}
289294
}
290295

291296
// If this attribute has a NestedType, validate the nested object
@@ -317,11 +322,11 @@ func assertPlannedObjectValid(schema *configschema.Object, prior, config, planne
317322
var errs []error
318323

319324
if planned.IsNull() && !config.IsNull() {
320-
errs = append(errs, path.NewErrorf("planned for absense but config wants existence"))
325+
errs = append(errs, path.NewErrorf("planned for absence but config wants existence"))
321326
return errs
322327
}
323328
if config.IsNull() && !planned.IsNull() {
324-
errs = append(errs, path.NewErrorf("planned for existence but config wants absense"))
329+
errs = append(errs, path.NewErrorf("planned for existence but config wants absence"))
325330
return errs
326331
}
327332
if planned.IsNull() {
@@ -349,10 +354,6 @@ func assertPlannedObjectValid(schema *configschema.Object, prior, config, planne
349354
for it := planned.ElementIterator(); it.Next(); {
350355
idx, plannedEV := it.Element()
351356
path := append(path, cty.IndexStep{Key: idx})
352-
if !plannedEV.IsKnown() {
353-
errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
354-
continue
355-
}
356357
if !config.HasIndex(idx).True() {
357358
continue // should never happen since we checked the lengths above
358359
}
@@ -387,16 +388,11 @@ func assertPlannedObjectValid(schema *configschema.Object, prior, config, planne
387388
for k, plannedEV := range plannedVals {
388389
configEV, ok := configVals[k]
389390
if !ok {
390-
errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k))
391+
errs = append(errs, path.NewErrorf("map key %q from plan is not present in config", k))
391392
continue
392393
}
393394
path := append(path, cty.GetAttrStep{Name: k})
394395

395-
if !plannedEV.IsKnown() {
396-
errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
397-
continue
398-
}
399-
400396
priorEV, ok := priorVals[k]
401397
if !ok {
402398
priorEV = cty.NullVal(schema.ImpliedType())
@@ -406,31 +402,22 @@ func assertPlannedObjectValid(schema *configschema.Object, prior, config, planne
406402
}
407403
for k := range configVals {
408404
if _, ok := plannedVals[k]; !ok {
409-
errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", k))
405+
errs = append(errs, path.NewErrorf("map key %q from config is not present in plan", k))
410406
continue
411407
}
412408
}
413409

414410
case configschema.NestingSet:
411+
plannedL := planned.LengthInt()
412+
configL := config.LengthInt()
413+
if plannedL != configL {
414+
errs = append(errs, path.NewErrorf("count in plan (%d) disagrees with count in config (%d)", plannedL, configL))
415+
return errs
416+
}
415417
// Because set elements have no identifier with which to correlate
416-
// them, we can't robustly validate the plan for a nested block
418+
// them, we can't robustly validate the plan for a nested object
417419
// backed by a set, and so unfortunately we need to just trust the
418-
// provider to do the right thing. :(
419-
//
420-
// (In principle we could correlate elements by matching the
421-
// subset of attributes explicitly set in config, except for the
422-
// special diff suppression rule which allows for there to be a
423-
// planned value that is constructed by mixing part of a prior
424-
// value with part of a config value, creating an entirely new
425-
// element that is not present in either prior nor config.)
426-
for it := planned.ElementIterator(); it.Next(); {
427-
idx, plannedEV := it.Element()
428-
path := append(path, cty.IndexStep{Key: idx})
429-
if !plannedEV.IsKnown() {
430-
errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
431-
continue
432-
}
433-
}
420+
// provider to do the right thing.
434421
}
435422

436423
return errs

‎internal/plans/objchange/plan_valid_test.go‎

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ func TestAssertPlanValid(t *testing.T) {
12221222
}),
12231223
[]string{`.bloop: planned value cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"blop":cty.StringVal("ok")})}) for a non-computed attribute`},
12241224
},
1225-
"computed in nested objects": {
1225+
"computed within nested objects": {
12261226
&configschema.Block{
12271227
Attributes: map[string]*configschema.Attribute{
12281228
"map": {
@@ -1353,6 +1353,105 @@ func TestAssertPlanValid(t *testing.T) {
13531353
}),
13541354
nil,
13551355
},
1356+
"computed nested objects": {
1357+
&configschema.Block{
1358+
Attributes: map[string]*configschema.Attribute{
1359+
"map": {
1360+
NestedType: &configschema.Object{
1361+
Nesting: configschema.NestingMap,
1362+
Attributes: map[string]*configschema.Attribute{
1363+
"name": {
1364+
Type: cty.String,
1365+
},
1366+
},
1367+
},
1368+
Computed: true,
1369+
},
1370+
"list": {
1371+
NestedType: &configschema.Object{
1372+
Nesting: configschema.NestingList,
1373+
Attributes: map[string]*configschema.Attribute{
1374+
"name": {
1375+
Type: cty.String,
1376+
},
1377+
},
1378+
},
1379+
Computed: true,
1380+
},
1381+
"set": {
1382+
NestedType: &configschema.Object{
1383+
Nesting: configschema.NestingSet,
1384+
Attributes: map[string]*configschema.Attribute{
1385+
"name": {
1386+
Type: cty.String,
1387+
},
1388+
},
1389+
},
1390+
Computed: true,
1391+
},
1392+
"single": {
1393+
NestedType: &configschema.Object{
1394+
Nesting: configschema.NestingSingle,
1395+
Attributes: map[string]*configschema.Attribute{
1396+
"name": {
1397+
Type: cty.DynamicPseudoType,
1398+
},
1399+
},
1400+
},
1401+
Computed: true,
1402+
},
1403+
},
1404+
},
1405+
cty.NullVal(cty.Object(map[string]cty.Type{
1406+
"map": cty.Map(cty.Object(map[string]cty.Type{
1407+
"name": cty.String,
1408+
})),
1409+
"list": cty.List(cty.Object(map[string]cty.Type{
1410+
"name": cty.String,
1411+
})),
1412+
"set": cty.Set(cty.Object(map[string]cty.Type{
1413+
"name": cty.String,
1414+
})),
1415+
"single": cty.Object(map[string]cty.Type{
1416+
"name": cty.String,
1417+
}),
1418+
})),
1419+
cty.ObjectVal(map[string]cty.Value{
1420+
"map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
1421+
"name": cty.String,
1422+
}))),
1423+
"list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{
1424+
"name": cty.String,
1425+
}))),
1426+
"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
1427+
"name": cty.String,
1428+
}))),
1429+
"single": cty.NullVal(cty.Object(map[string]cty.Type{
1430+
"name": cty.String,
1431+
})),
1432+
}),
1433+
cty.ObjectVal(map[string]cty.Value{
1434+
"map": cty.MapVal(map[string]cty.Value{
1435+
"one": cty.UnknownVal(cty.Object(map[string]cty.Type{
1436+
"name": cty.String,
1437+
})),
1438+
}),
1439+
"list": cty.ListVal([]cty.Value{
1440+
cty.UnknownVal(cty.Object(map[string]cty.Type{
1441+
"name": cty.String,
1442+
})),
1443+
}),
1444+
"set": cty.SetVal([]cty.Value{
1445+
cty.UnknownVal(cty.Object(map[string]cty.Type{
1446+
"name": cty.String,
1447+
})),
1448+
}),
1449+
"single": cty.UnknownVal(cty.Object(map[string]cty.Type{
1450+
"name": cty.String,
1451+
})),
1452+
}),
1453+
nil,
1454+
},
13561455
}
13571456

13581457
for name, test := range tests {

0 commit comments

Comments
 (0)