mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Add support for the replace paths data in the structured renderer (#32392)
* prep for processing the structured run output * undo unwanted change to a json key * Add skeleton functions and API for refactored renderer * goimports * Fix documentation of the RenderOpts struct * Add rendering functionality for primitives to the structured renderer * add test case for override * Add support for parsing and rendering sensitive values in the renderer * Add support for unknown/computed values in the structured renderer * delete missing unit tests * Add support for object attributes in the structured renderer * goimports * Add support for the replace paths data in the structured renderer
This commit is contained in:
parent
1eebcf875f
commit
b097d8873d
@ -82,7 +82,7 @@ type Value struct {
|
||||
// ReplacePaths generally contains nested slices that describe paths to
|
||||
// elements or attributes that are causing the overall resource to be
|
||||
// replaced.
|
||||
ReplacePaths interface{}
|
||||
ReplacePaths []interface{}
|
||||
}
|
||||
|
||||
// ValueFromJsonChange unmarshals the raw []byte values in the jsonplan.Change
|
||||
@ -94,7 +94,7 @@ func ValueFromJsonChange(change jsonplan.Change) Value {
|
||||
Unknown: unmarshalGeneric(change.AfterUnknown),
|
||||
BeforeSensitive: unmarshalGeneric(change.BeforeSensitive),
|
||||
AfterSensitive: unmarshalGeneric(change.AfterSensitive),
|
||||
ReplacePaths: unmarshalGeneric(change.ReplacePaths),
|
||||
ReplacePaths: decodePaths(unmarshalGeneric(change.ReplacePaths)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,8 +129,10 @@ func (v Value) AsChange(renderer change.Renderer) change.Change {
|
||||
}
|
||||
|
||||
func (v Value) replacePath() bool {
|
||||
if replace, ok := v.ReplacePaths.(bool); ok {
|
||||
return replace
|
||||
for _, path := range v.ReplacePaths {
|
||||
if len(path.([]interface{})) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -198,3 +200,10 @@ func unmarshalGeneric(raw json.RawMessage) interface{} {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func decodePaths(paths interface{}) []interface{} {
|
||||
if paths == nil {
|
||||
return nil
|
||||
}
|
||||
return paths.([]interface{})
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ type ValueMap struct {
|
||||
// AfterSensitive contains the after sensitive status of any
|
||||
// elements/attributes of this map/object.
|
||||
AfterSensitive map[string]interface{}
|
||||
|
||||
// ReplacePaths matches the same attributes in Value exactly.
|
||||
ReplacePaths []interface{}
|
||||
}
|
||||
|
||||
func (v Value) asMap() ValueMap {
|
||||
@ -29,6 +32,7 @@ func (v Value) asMap() ValueMap {
|
||||
Unknown: genericToMap(v.Unknown),
|
||||
BeforeSensitive: genericToMap(v.BeforeSensitive),
|
||||
AfterSensitive: genericToMap(v.AfterSensitive),
|
||||
ReplacePaths: v.ReplacePaths,
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,9 +51,26 @@ func (m ValueMap) getChild(key string) Value {
|
||||
Unknown: unknown,
|
||||
BeforeSensitive: beforeSensitive,
|
||||
AfterSensitive: afterSensitive,
|
||||
ReplacePaths: m.processReplacePaths(key),
|
||||
}
|
||||
}
|
||||
|
||||
func (m ValueMap) processReplacePaths(key string) []interface{} {
|
||||
var ret []interface{}
|
||||
for _, p := range m.ReplacePaths {
|
||||
path := p.([]interface{})
|
||||
|
||||
if len(path) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if path[0].(string) == key {
|
||||
ret = append(ret, path[1:])
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func getFromGenericMap(generic map[string]interface{}, key string) (interface{}, bool) {
|
||||
if generic == nil {
|
||||
return nil, false
|
||||
|
@ -317,6 +317,48 @@ func TestValue_ObjectAttributes(t *testing.T) {
|
||||
validateAction: plans.NoOp,
|
||||
validateReplace: false,
|
||||
},
|
||||
"object_update_replace_self": {
|
||||
input: Value{
|
||||
Before: map[string]interface{}{
|
||||
"attribute_one": "old",
|
||||
},
|
||||
After: map[string]interface{}{
|
||||
"attribute_one": "new",
|
||||
},
|
||||
ReplacePaths: []interface{}{
|
||||
[]interface{}{},
|
||||
},
|
||||
},
|
||||
attributes: map[string]cty.Type{
|
||||
"attribute_one": cty.String,
|
||||
},
|
||||
validateChanges: map[string]change.ValidateChangeFunc{
|
||||
"attribute_one": change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, false),
|
||||
},
|
||||
validateAction: plans.Update,
|
||||
validateReplace: true,
|
||||
},
|
||||
"object_update_replace_attribute": {
|
||||
input: Value{
|
||||
Before: map[string]interface{}{
|
||||
"attribute_one": "old",
|
||||
},
|
||||
After: map[string]interface{}{
|
||||
"attribute_one": "new",
|
||||
},
|
||||
ReplacePaths: []interface{}{
|
||||
[]interface{}{"attribute_one"},
|
||||
},
|
||||
},
|
||||
attributes: map[string]cty.Type{
|
||||
"attribute_one": cty.String,
|
||||
},
|
||||
validateChanges: map[string]change.ValidateChangeFunc{
|
||||
"attribute_one": change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, true),
|
||||
},
|
||||
validateAction: plans.Update,
|
||||
validateReplace: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tcs {
|
||||
@ -484,6 +526,19 @@ func TestValue_Attribute(t *testing.T) {
|
||||
},
|
||||
validateChange: change.ValidateComputed(change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false), plans.Update, false),
|
||||
},
|
||||
"primitive_update_replace": {
|
||||
input: Value{
|
||||
Before: "old",
|
||||
After: "new",
|
||||
ReplacePaths: []interface{}{
|
||||
[]interface{}{}, // An empty path suggests this attribute should be true.
|
||||
},
|
||||
},
|
||||
attribute: &jsonprovider.Attribute{
|
||||
AttributeType: unmarshalType(t, cty.String),
|
||||
},
|
||||
validateChange: change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, true),
|
||||
},
|
||||
}
|
||||
for name, tc := range tcs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user