mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Fixes: show insensitive plan details when resource in set with sensitive value (#1313)
Signed-off-by: Zejun Chen <tibazq@gmail.com> Signed-off-by: chenzj <tibazq@gmail.com> Co-authored-by: Oleksandr Levchenkov <ollevche@gmail.com>
This commit is contained in:
parent
261b966562
commit
1ecb2dcae3
@ -19,7 +19,8 @@ BUG FIXES:
|
|||||||
* Fixed inmem backend crash due to missing struct field. ([#1619](https://github.com/opentofu/opentofu/pull/1619))
|
* Fixed inmem backend crash due to missing struct field. ([#1619](https://github.com/opentofu/opentofu/pull/1619))
|
||||||
* Added a check in the `tofu test` to validate that the names of test run blocks do not contain spaces. ([#1489](https://github.com/opentofu/opentofu/pull/1489))
|
* Added a check in the `tofu test` to validate that the names of test run blocks do not contain spaces. ([#1489](https://github.com/opentofu/opentofu/pull/1489))
|
||||||
* `tofu test` now supports accessing module outputs when the module has no resources. ([#1409](https://github.com/opentofu/opentofu/pull/1409))
|
* `tofu test` now supports accessing module outputs when the module has no resources. ([#1409](https://github.com/opentofu/opentofu/pull/1409))
|
||||||
* Fixed support for provider functions in tests. ([#1603](https://github.com/opentofu/opentofu/pull/1603))
|
* Fixed support for provider functions in tests ([#1603](https://github.com/opentofu/opentofu/pull/1603))
|
||||||
|
* Only hide sensitive attributes in plan detail when plan on a set of resources ([#1313](https://github.com/opentofu/opentofu/pull/1313))
|
||||||
* Added a better error message on `for_each` block with sensitive value of unsuitable type. ([#1485](https://github.com/opentofu/opentofu/pull/1485))
|
* Added a better error message on `for_each` block with sensitive value of unsuitable type. ([#1485](https://github.com/opentofu/opentofu/pull/1485))
|
||||||
* Fix race condition on locking in gcs backend ([#1342](https://github.com/opentofu/opentofu/pull/1342))
|
* Fix race condition on locking in gcs backend ([#1342](https://github.com/opentofu/opentofu/pull/1342))
|
||||||
* Fix bug where provider functions were unusable in variables and outputs ([#1689](https://github.com/opentofu/opentofu/pull/1689))
|
* Fix bug where provider functions were unusable in variables and outputs ([#1689](https://github.com/opentofu/opentofu/pull/1689))
|
||||||
|
@ -435,7 +435,7 @@ func MarshalResourceChanges(resources []*plans.ResourceInstanceChangeSrc, schema
|
|||||||
if schema.ContainsSensitive() {
|
if schema.ContainsSensitive() {
|
||||||
marks = append(marks, schema.ValueMarks(changeV.Before, nil)...)
|
marks = append(marks, schema.ValueMarks(changeV.Before, nil)...)
|
||||||
}
|
}
|
||||||
bs := jsonstate.SensitiveAsBool(changeV.Before.MarkWithPaths(marks))
|
bs := jsonstate.SensitiveAsBoolWithPathValueMarks(changeV.Before, marks)
|
||||||
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
|
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -464,7 +464,7 @@ func MarshalResourceChanges(resources []*plans.ResourceInstanceChangeSrc, schema
|
|||||||
if schema.ContainsSensitive() {
|
if schema.ContainsSensitive() {
|
||||||
marks = append(marks, schema.ValueMarks(changeV.After, nil)...)
|
marks = append(marks, schema.ValueMarks(changeV.After, nil)...)
|
||||||
}
|
}
|
||||||
as := jsonstate.SensitiveAsBool(changeV.After.MarkWithPaths(marks))
|
as := jsonstate.SensitiveAsBoolWithPathValueMarks(changeV.After, marks)
|
||||||
afterSensitive, err = ctyjson.Marshal(as, as.Type())
|
afterSensitive, err = ctyjson.Marshal(as, as.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -417,7 +417,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||||||
if schema.ContainsSensitive() {
|
if schema.ContainsSensitive() {
|
||||||
marks = append(marks, schema.ValueMarks(value, nil)...)
|
marks = append(marks, schema.ValueMarks(value, nil)...)
|
||||||
}
|
}
|
||||||
s := SensitiveAsBool(value.MarkWithPaths(marks))
|
s := SensitiveAsBoolWithPathValueMarks(value, marks)
|
||||||
v, err := ctyjson.Marshal(s, s.Type())
|
v, err := ctyjson.Marshal(s, s.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -496,15 +496,45 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SensitiveAsBool(val cty.Value) cty.Value {
|
func SensitiveAsBool(val cty.Value) cty.Value {
|
||||||
|
return SensitiveAsBoolWithPathValueMarks(val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SensitiveAsBoolWithPathValueMarks(val cty.Value, pvms []cty.PathValueMarks) cty.Value {
|
||||||
|
var sensitiveMarks []cty.PathValueMarks
|
||||||
|
for _, pvm := range pvms {
|
||||||
|
if _, ok := pvm.Marks[marks.Sensitive]; ok {
|
||||||
|
sensitiveMarks = append(sensitiveMarks, pvm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sensitiveAsBoolWithPathValueMarks(val, cty.Path{}, sensitiveMarks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sensitiveAsBoolWithPathValueMarks(val cty.Value, path cty.Path, pvms []cty.PathValueMarks) cty.Value {
|
||||||
if val.HasMark(marks.Sensitive) {
|
if val.HasMark(marks.Sensitive) {
|
||||||
return cty.True
|
return cty.True
|
||||||
}
|
}
|
||||||
|
for _, pvm := range pvms {
|
||||||
|
if path.Equals(pvm.Path) {
|
||||||
|
return cty.True
|
||||||
|
}
|
||||||
|
}
|
||||||
ty := val.Type()
|
ty := val.Type()
|
||||||
switch {
|
switch {
|
||||||
case val.IsNull(), ty.IsPrimitiveType(), ty.Equals(cty.DynamicPseudoType):
|
case val.IsNull(), ty.IsPrimitiveType(), ty.Equals(cty.DynamicPseudoType):
|
||||||
return cty.False
|
return cty.False
|
||||||
case ty.IsListType() || ty.IsTupleType() || ty.IsSetType():
|
case ty.IsListType() || ty.IsTupleType() || ty.IsSetType():
|
||||||
|
return sensitiveCollectionAsBool(val, path, pvms)
|
||||||
|
case ty.IsMapType():
|
||||||
|
return sensitiveMapAsBool(val, path, pvms)
|
||||||
|
case ty.IsObjectType():
|
||||||
|
return sensitiveObjectAsBool(val, path, pvms)
|
||||||
|
default:
|
||||||
|
// Should never happen, since the above should cover all types
|
||||||
|
panic(fmt.Sprintf("sensitiveAsBoolWithPathValueMarks cannot handle %#v", val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sensitiveCollectionAsBool(val cty.Value, path []cty.PathStep, pvms []cty.PathValueMarks) cty.Value {
|
||||||
if !val.IsKnown() {
|
if !val.IsKnown() {
|
||||||
// If the collection is unknown we can't say anything about the
|
// If the collection is unknown we can't say anything about the
|
||||||
// sensitivity of its contents
|
// sensitivity of its contents
|
||||||
@ -518,8 +548,12 @@ func SensitiveAsBool(val cty.Value) cty.Value {
|
|||||||
vals := make([]cty.Value, 0, length)
|
vals := make([]cty.Value, 0, length)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
_, v := it.Element()
|
kv, ev := it.Element()
|
||||||
vals = append(vals, SensitiveAsBool(v))
|
path = append(path, cty.IndexStep{
|
||||||
|
Key: kv,
|
||||||
|
})
|
||||||
|
vals = append(vals, sensitiveAsBoolWithPathValueMarks(ev, path, pvms))
|
||||||
|
path = path[0 : len(path)-1]
|
||||||
}
|
}
|
||||||
// The above transform may have changed the types of some of the
|
// The above transform may have changed the types of some of the
|
||||||
// elements, so we'll always use a tuple here in case we've now made
|
// elements, so we'll always use a tuple here in case we've now made
|
||||||
@ -527,32 +561,33 @@ func SensitiveAsBool(val cty.Value) cty.Value {
|
|||||||
// marshal to JSON anyway, and all of these sequence types are
|
// marshal to JSON anyway, and all of these sequence types are
|
||||||
// indistinguishable in JSON.
|
// indistinguishable in JSON.
|
||||||
return cty.TupleVal(vals)
|
return cty.TupleVal(vals)
|
||||||
case ty.IsMapType() || ty.IsObjectType():
|
}
|
||||||
|
|
||||||
|
func sensitiveMapAsBool(val cty.Value, path []cty.PathStep, pvms []cty.PathValueMarks) cty.Value {
|
||||||
if !val.IsKnown() {
|
if !val.IsKnown() {
|
||||||
// If the map/object is unknown we can't say anything about the
|
// If the map/object is unknown we can't say anything about the
|
||||||
// sensitivity of its attributes
|
// sensitivity of its attributes
|
||||||
return cty.EmptyObjectVal
|
return cty.EmptyObjectVal
|
||||||
}
|
}
|
||||||
var length int
|
length := val.LengthInt()
|
||||||
switch {
|
|
||||||
case ty.IsMapType():
|
|
||||||
length = val.LengthInt()
|
|
||||||
default:
|
|
||||||
length = len(val.Type().AttributeTypes())
|
|
||||||
}
|
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
// If there are no elements then we can't have sensitive values
|
// If there are no elements then we can't have sensitive values
|
||||||
return cty.EmptyObjectVal
|
return cty.EmptyObjectVal
|
||||||
}
|
}
|
||||||
|
|
||||||
vals := make(map[string]cty.Value)
|
vals := make(map[string]cty.Value)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
k, v := it.Element()
|
kv, ev := it.Element()
|
||||||
s := SensitiveAsBool(v)
|
path = append(path, cty.IndexStep{
|
||||||
|
Key: kv,
|
||||||
|
})
|
||||||
|
s := sensitiveAsBoolWithPathValueMarks(ev, path, pvms)
|
||||||
|
path = path[0 : len(path)-1]
|
||||||
// Omit all of the "false"s for non-sensitive values for more
|
// Omit all of the "false"s for non-sensitive values for more
|
||||||
// compact serialization
|
// compact serialization
|
||||||
if !s.RawEquals(cty.False) {
|
if !s.RawEquals(cty.False) {
|
||||||
vals[k.AsString()] = s
|
vals[kv.AsString()] = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The above transform may have changed the types of some of the
|
// The above transform may have changed the types of some of the
|
||||||
@ -561,8 +596,30 @@ func SensitiveAsBool(val cty.Value) cty.Value {
|
|||||||
// marshal to JSON anyway, and all of these mapping types are
|
// marshal to JSON anyway, and all of these mapping types are
|
||||||
// indistinguishable in JSON.
|
// indistinguishable in JSON.
|
||||||
return cty.ObjectVal(vals)
|
return cty.ObjectVal(vals)
|
||||||
default:
|
}
|
||||||
// Should never happen, since the above should cover all types
|
|
||||||
panic(fmt.Sprintf("sensitiveAsBool cannot handle %#v", val))
|
func sensitiveObjectAsBool(val cty.Value, path []cty.PathStep, pvms []cty.PathValueMarks) cty.Value {
|
||||||
|
if !val.IsKnown() {
|
||||||
|
// If the map/object is unknown we can't say anything about the
|
||||||
|
// sensitivity of its attributes
|
||||||
|
return cty.EmptyObjectVal
|
||||||
|
}
|
||||||
|
ty := val.Type()
|
||||||
|
if len(ty.AttributeTypes()) == 0 {
|
||||||
|
// If there are no elements then we can't have sensitive values
|
||||||
|
return cty.EmptyObjectVal
|
||||||
|
}
|
||||||
|
vals := make(map[string]cty.Value)
|
||||||
|
for name := range ty.AttributeTypes() {
|
||||||
|
av := val.GetAttr(name)
|
||||||
|
path = append(path, cty.GetAttrStep{
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
s := sensitiveAsBoolWithPathValueMarks(av, path, pvms)
|
||||||
|
path = path[0 : len(path)-1]
|
||||||
|
if !s.RawEquals(cty.False) {
|
||||||
|
vals[name] = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return cty.ObjectVal(vals)
|
||||||
|
}
|
||||||
|
@ -1054,3 +1054,210 @@ func TestSensitiveAsBool(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSensitiveAsBoolWithPathValueMarks(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Input cty.Value
|
||||||
|
Pvms []cty.PathValueMarks
|
||||||
|
Want cty.Value
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("friend"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.False,
|
||||||
|
cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("hello").Mark(marks.Sensitive),
|
||||||
|
cty.StringVal("friend"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.True,
|
||||||
|
cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("friend"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.False,
|
||||||
|
cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.StringVal("hello").Mark(marks.Sensitive),
|
||||||
|
cty.StringVal("friend"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.True,
|
||||||
|
cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("friend"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.StringVal("hello")}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.False,
|
||||||
|
cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"greeting": cty.StringVal("hello"),
|
||||||
|
"animal": cty.StringVal("horse"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.StringVal("animal")}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"animal": cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"greeting": cty.StringVal("hello"),
|
||||||
|
"animal": cty.StringVal("horse"),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.GetAttrStep{Name: "animal"}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"animal": cty.True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("known"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "a"}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.EmptyObjectVal,
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.True,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("known"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "a"}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.EmptyObjectVal,
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.True,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.MapValEmpty(cty.String),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("known"),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.IndexStep{Key: cty.StringVal("a")}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.EmptyObjectVal,
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.True,
|
||||||
|
}),
|
||||||
|
cty.EmptyObjectVal,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"greeting": cty.StringVal("hello"),
|
||||||
|
"animal": cty.StringVal("cat"),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"greeting": cty.StringVal("hello"),
|
||||||
|
"animal": cty.StringVal("horse"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[]cty.PathValueMarks{{
|
||||||
|
Path: cty.Path{
|
||||||
|
cty.IndexStep{Key: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"greeting": cty.StringVal("hello"),
|
||||||
|
"animal": cty.StringVal("cat"),
|
||||||
|
})},
|
||||||
|
cty.GetAttrStep{Name: "animal"}},
|
||||||
|
Marks: cty.NewValueMarks(marks.Sensitive)},
|
||||||
|
},
|
||||||
|
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"animal": cty.True,
|
||||||
|
}),
|
||||||
|
cty.EmptyObjectVal,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
got := SensitiveAsBoolWithPathValueMarks(test.Input, test.Pvms)
|
||||||
|
if !reflect.DeepEqual(got, test.Want) {
|
||||||
|
t.Errorf(
|
||||||
|
"wrong result\ninput: %#v\ngot: %#v\nwant: %#v",
|
||||||
|
test.Input, got, test.Want,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user