2019-02-08 02:24:36 +01:00
|
|
|
package plugin
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
|
|
|
// SetUnknowns takes a cty.Value, and compares it to the schema setting any null
|
2019-02-08 20:46:29 +01:00
|
|
|
// values which are computed to unknown.
|
2019-02-08 02:24:36 +01:00
|
|
|
func SetUnknowns(val cty.Value, schema *configschema.Block) cty.Value {
|
2019-02-08 20:46:29 +01:00
|
|
|
if !val.IsKnown() {
|
2019-02-08 02:24:36 +01:00
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2019-02-08 20:46:29 +01:00
|
|
|
// If the object was null, we still need to handle the top level attributes
|
|
|
|
// which might be computed, but we don't need to expand the blocks.
|
|
|
|
if val.IsNull() {
|
|
|
|
objMap := map[string]cty.Value{}
|
|
|
|
allNull := true
|
|
|
|
for name, attr := range schema.Attributes {
|
|
|
|
switch {
|
|
|
|
case attr.Computed:
|
|
|
|
objMap[name] = cty.UnknownVal(attr.Type)
|
|
|
|
allNull = false
|
|
|
|
default:
|
|
|
|
objMap[name] = cty.NullVal(attr.Type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this object has no unknown attributes, then we can leave it null.
|
|
|
|
if allNull {
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
|
|
|
return cty.ObjectVal(objMap)
|
|
|
|
}
|
|
|
|
|
2019-02-08 02:24:36 +01:00
|
|
|
valMap := val.AsValueMap()
|
|
|
|
newVals := make(map[string]cty.Value)
|
|
|
|
|
|
|
|
for name, attr := range schema.Attributes {
|
|
|
|
v := valMap[name]
|
|
|
|
|
|
|
|
if attr.Computed && v.IsNull() {
|
|
|
|
newVals[name] = cty.UnknownVal(attr.Type)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
newVals[name] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, blockS := range schema.BlockTypes {
|
|
|
|
blockVal := valMap[name]
|
|
|
|
if blockVal.IsNull() || !blockVal.IsKnown() {
|
|
|
|
newVals[name] = blockVal
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-02-08 20:46:29 +01:00
|
|
|
blockValType := blockVal.Type()
|
|
|
|
blockElementType := blockS.Block.ImpliedType()
|
2019-02-08 02:24:36 +01:00
|
|
|
|
2019-02-08 20:46:29 +01:00
|
|
|
// This switches on the value type here, so we can correctly switch
|
|
|
|
// between Tuples/Lists and Maps/Objects.
|
|
|
|
switch {
|
|
|
|
case blockS.Nesting == configschema.NestingSingle:
|
|
|
|
// NestingSingle is the only exception here, where we treat the
|
|
|
|
// block directly as an object
|
2019-02-08 02:24:36 +01:00
|
|
|
newVals[name] = SetUnknowns(blockVal, &blockS.Block)
|
2019-02-08 20:46:29 +01:00
|
|
|
|
|
|
|
case blockValType.IsSetType(), blockValType.IsListType(), blockValType.IsTupleType():
|
2019-02-08 02:24:36 +01:00
|
|
|
listVals := blockVal.AsValueSlice()
|
|
|
|
newListVals := make([]cty.Value, 0, len(listVals))
|
|
|
|
|
|
|
|
for _, v := range listVals {
|
|
|
|
newListVals = append(newListVals, SetUnknowns(v, &blockS.Block))
|
|
|
|
}
|
|
|
|
|
2019-02-08 20:46:29 +01:00
|
|
|
switch {
|
|
|
|
case blockValType.IsSetType():
|
2019-02-08 02:24:36 +01:00
|
|
|
switch len(newListVals) {
|
|
|
|
case 0:
|
2019-02-08 20:46:29 +01:00
|
|
|
newVals[name] = cty.SetValEmpty(blockElementType)
|
2019-02-08 02:24:36 +01:00
|
|
|
default:
|
|
|
|
newVals[name] = cty.SetVal(newListVals)
|
|
|
|
}
|
2019-02-08 20:46:29 +01:00
|
|
|
case blockValType.IsListType():
|
2019-02-08 02:24:36 +01:00
|
|
|
switch len(newListVals) {
|
|
|
|
case 0:
|
2019-02-08 20:46:29 +01:00
|
|
|
newVals[name] = cty.ListValEmpty(blockElementType)
|
2019-02-08 02:24:36 +01:00
|
|
|
default:
|
|
|
|
newVals[name] = cty.ListVal(newListVals)
|
|
|
|
}
|
2019-02-08 20:46:29 +01:00
|
|
|
case blockValType.IsTupleType():
|
|
|
|
newVals[name] = cty.TupleVal(newListVals)
|
2019-02-08 02:24:36 +01:00
|
|
|
}
|
|
|
|
|
2019-02-08 20:46:29 +01:00
|
|
|
case blockValType.IsMapType(), blockValType.IsObjectType():
|
2019-02-08 02:24:36 +01:00
|
|
|
mapVals := blockVal.AsValueMap()
|
|
|
|
newMapVals := make(map[string]cty.Value)
|
|
|
|
|
|
|
|
for k, v := range mapVals {
|
|
|
|
newMapVals[k] = SetUnknowns(v, &blockS.Block)
|
|
|
|
}
|
|
|
|
|
2019-02-08 20:46:29 +01:00
|
|
|
switch {
|
|
|
|
case blockValType.IsMapType():
|
|
|
|
switch len(newMapVals) {
|
|
|
|
case 0:
|
|
|
|
newVals[name] = cty.MapValEmpty(blockElementType)
|
|
|
|
default:
|
|
|
|
newVals[name] = cty.MapVal(newMapVals)
|
|
|
|
}
|
|
|
|
case blockValType.IsObjectType():
|
|
|
|
if len(newMapVals) == 0 {
|
|
|
|
// We need to populate empty values to make a valid object.
|
|
|
|
for attr, ty := range blockElementType.AttributeTypes() {
|
|
|
|
newMapVals[attr] = cty.NullVal(ty)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newVals[name] = cty.ObjectVal(newMapVals)
|
2019-02-08 02:24:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2019-02-08 20:46:29 +01:00
|
|
|
panic(fmt.Sprintf("failed to set unknown values for nested block %q:%#v", name, blockValType))
|
2019-02-08 02:24:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cty.ObjectVal(newVals)
|
|
|
|
}
|