You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
terraform/internal/configs/configschema/write_only.go

137 lines
4.6 KiB

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package configschema
import (
"fmt"
"slices"
"github.com/zclconf/go-cty/cty"
)
// WARNING: WriteOnlyPaths must exactly mirror the SensitivePaths method, since
// they both use the same process just for different attribute types. Any fixes
// here must be made in SensitivePaths, and vice versa.
// WriteOnlyPaths returns a set of paths into the given value that should
// be marked as write-only based on the static declarations in the schema.
func (b *Block) WriteOnlyPaths(val cty.Value, basePath cty.Path) []cty.Path {
var ret []cty.Path
// a block cannot be write-only, so nothing to return
if val.IsNull() || !val.IsKnown() {
return ret
}
for name, attrS := range b.Attributes {
if attrS.WriteOnly {
attrPath := slices.Concat(basePath, cty.GetAttrPath(name))
ret = append(ret, attrPath)
}
}
// Extract paths for marks from nested attribute type values
for name, attrS := range b.Attributes {
// If the attribute has no nested type, or the nested type doesn't
// contain any write-only attributes, skip inspecting it
if attrS.NestedType == nil || !attrS.NestedType.ContainsWriteOnly() {
continue
}
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
attrPath := slices.Concat(basePath, cty.GetAttrPath(name))
ret = append(ret, attrS.NestedType.writeOnlyPaths(val.GetAttr(name), attrPath)...)
}
// Extract paths for marks from nested blocks
for name, blockS := range b.BlockTypes {
// If our block doesn't contain any write-only attributes, skip inspecting it
if !blockS.Block.ContainsWriteOnly() {
continue
}
blockV := val.GetAttr(name)
if blockV.IsNull() || !blockV.IsKnown() {
continue
}
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
blockPath := slices.Concat(basePath, cty.GetAttrPath(name))
switch blockS.Nesting {
case NestingSingle, NestingGroup:
ret = append(ret, blockS.Block.WriteOnlyPaths(blockV, blockPath)...)
case NestingList, NestingMap, NestingSet:
blockV, _ = blockV.Unmark() // peel off one level of marking so we can iterate
for it := blockV.ElementIterator(); it.Next(); {
idx, blockEV := it.Element()
// Create a copy of the path, with this block instance's index
// step added, to add to our PathValueMarks slice
blockInstancePath := slices.Concat(blockPath, cty.IndexPath(idx))
morePaths := blockS.Block.WriteOnlyPaths(blockEV, blockInstancePath)
ret = append(ret, morePaths...)
}
default:
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
}
}
return ret
}
// writeOnlyPaths returns a set of paths into the given value that should be
// marked as write-only based on the static declarations in the schema.
func (o *Object) writeOnlyPaths(val cty.Value, basePath cty.Path) []cty.Path {
var ret []cty.Path
if val.IsNull() || !val.IsKnown() {
return ret
}
for name, attrS := range o.Attributes {
// Skip attributes which can never produce write-only path value marks
if !attrS.WriteOnly && (attrS.NestedType == nil || !attrS.NestedType.ContainsWriteOnly()) {
continue
}
switch o.Nesting {
case NestingSingle, NestingGroup:
// Create a path to this attribute
attrPath := slices.Concat(basePath, cty.GetAttrPath(name))
if attrS.WriteOnly {
// If the entire attribute is write-only, mark it so
ret = append(ret, attrPath)
} else {
// The attribute has a nested type which contains write-only
// attributes, so recurse
ret = append(ret, attrS.NestedType.writeOnlyPaths(val.GetAttr(name), attrPath)...)
}
case NestingList, NestingMap, NestingSet:
// For nested attribute types which have a non-single nesting mode,
// we add path value marks for each element of the collection
val, _ = val.Unmark() // peel off one level of marking so we can iterate
for it := val.ElementIterator(); it.Next(); {
idx, attrEV := it.Element()
attrV := attrEV.GetAttr(name)
// Create a path to this element of the attribute's collection. Note
// that the path is extended in opposite order to the iteration order
// of the loops: index into the collection, then the contained
// attribute name. This is because we have one type
// representing multiple collection elements.
attrPath := slices.Concat(basePath, cty.IndexPath(idx).GetAttr(name))
if attrS.WriteOnly {
// If the entire attribute is write-only, mark it so
ret = append(ret, attrPath)
} else {
ret = append(ret, attrS.NestedType.writeOnlyPaths(attrV, attrPath)...)
}
}
default:
panic(fmt.Sprintf("unsupported nesting mode %s", o.Nesting))
}
}
return ret
}