mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Add support for unknown/computed values in the structured renderer (#32378)
* 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
This commit is contained in:
parent
6ab277f6ba
commit
b8b1a8d430
29
internal/command/jsonformat/change/computed.go
Normal file
29
internal/command/jsonformat/change/computed.go
Normal file
@ -0,0 +1,29 @@
|
||||
package change
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
func Computed(before Change) Renderer {
|
||||
return &computedRenderer{
|
||||
before: before,
|
||||
}
|
||||
}
|
||||
|
||||
type computedRenderer struct {
|
||||
NoWarningsRenderer
|
||||
|
||||
before Change
|
||||
}
|
||||
|
||||
func (renderer computedRenderer) Render(change Change, indent int, opts RenderOpts) string {
|
||||
if change.action == plans.Create {
|
||||
return "(known after apply)"
|
||||
}
|
||||
|
||||
// Never render null suffix for children of computed changes.
|
||||
opts.overrideNullSuffix = true
|
||||
return fmt.Sprintf("%s -> (known after apply)", renderer.before.Render(indent, opts))
|
||||
}
|
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func Primitive(before, after *string) Renderer {
|
||||
return primitiveRenderer{
|
||||
return &primitiveRenderer{
|
||||
before: before,
|
||||
after: after,
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Primitive(nil, strptr("1")),
|
||||
action: plans.Create,
|
||||
replace: false,
|
||||
},
|
||||
expected: "1",
|
||||
},
|
||||
@ -36,7 +35,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Primitive(strptr("1"), nil),
|
||||
action: plans.Delete,
|
||||
replace: false,
|
||||
},
|
||||
expected: "1 -> null",
|
||||
},
|
||||
@ -44,7 +42,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Primitive(strptr("1"), nil),
|
||||
action: plans.Delete,
|
||||
replace: false,
|
||||
},
|
||||
opts: RenderOpts{overrideNullSuffix: true},
|
||||
expected: "1",
|
||||
@ -53,7 +50,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Primitive(strptr("1"), nil),
|
||||
action: plans.Update,
|
||||
replace: false,
|
||||
},
|
||||
expected: "1 -> null",
|
||||
},
|
||||
@ -61,7 +57,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Primitive(nil, strptr("1")),
|
||||
action: plans.Update,
|
||||
replace: false,
|
||||
},
|
||||
expected: "null -> 1",
|
||||
},
|
||||
@ -69,7 +64,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Primitive(strptr("0"), strptr("1")),
|
||||
action: plans.Update,
|
||||
replace: false,
|
||||
},
|
||||
expected: "0 -> 1",
|
||||
},
|
||||
@ -85,7 +79,6 @@ func TestRenderers(t *testing.T) {
|
||||
change: Change{
|
||||
renderer: Sensitive("0", "1", true, true),
|
||||
action: plans.Update,
|
||||
replace: false,
|
||||
},
|
||||
expected: "(sensitive)",
|
||||
},
|
||||
@ -97,6 +90,23 @@ func TestRenderers(t *testing.T) {
|
||||
},
|
||||
expected: "(sensitive) # forces replacement",
|
||||
},
|
||||
"computed_create": {
|
||||
change: Change{
|
||||
renderer: Computed(Change{}),
|
||||
action: plans.Create,
|
||||
},
|
||||
expected: "(known after apply)",
|
||||
},
|
||||
"computed_update": {
|
||||
change: Change{
|
||||
renderer: Computed(Change{
|
||||
renderer: Primitive(strptr("0"), nil),
|
||||
action: plans.Delete,
|
||||
}),
|
||||
action: plans.Update,
|
||||
},
|
||||
expected: "0 -> (known after apply)",
|
||||
},
|
||||
}
|
||||
for name, tc := range tcs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func Sensitive(before, after interface{}, beforeSensitive, afterSensitive bool) Renderer {
|
||||
return sensitiveRenderer{
|
||||
return &sensitiveRenderer{
|
||||
before: before,
|
||||
after: after,
|
||||
beforeSensitive: beforeSensitive,
|
||||
|
@ -20,7 +20,7 @@ func ValidateChange(t *testing.T, f ValidateChangeFunc, change Change, expectedA
|
||||
|
||||
func ValidatePrimitive(before, after *string) ValidateChangeFunc {
|
||||
return func(t *testing.T, change Change) {
|
||||
primitive, ok := change.renderer.(primitiveRenderer)
|
||||
primitive, ok := change.renderer.(*primitiveRenderer)
|
||||
if !ok {
|
||||
t.Fatalf("invalid renderer type: %T", change.renderer)
|
||||
}
|
||||
@ -36,7 +36,7 @@ func ValidatePrimitive(before, after *string) ValidateChangeFunc {
|
||||
|
||||
func ValidateSensitive(before, after interface{}, beforeSensitive, afterSensitive bool) ValidateChangeFunc {
|
||||
return func(t *testing.T, change Change) {
|
||||
sensitive, ok := change.renderer.(sensitiveRenderer)
|
||||
sensitive, ok := change.renderer.(*sensitiveRenderer)
|
||||
if !ok {
|
||||
t.Fatalf("invalid renderer type: %T", change.renderer)
|
||||
}
|
||||
@ -53,3 +53,25 @@ func ValidateSensitive(before, after interface{}, beforeSensitive, afterSensitiv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateComputed(before ValidateChangeFunc) ValidateChangeFunc {
|
||||
return func(t *testing.T, change Change) {
|
||||
computed, ok := change.renderer.(*computedRenderer)
|
||||
if !ok {
|
||||
t.Fatalf("invalid renderer type: %T", change.renderer)
|
||||
}
|
||||
|
||||
if before == nil {
|
||||
if computed.before.renderer != nil {
|
||||
t.Fatalf("did not expect a before renderer, but found one")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if computed.before.renderer == nil {
|
||||
t.Fatalf("expected a before renderer, but found none")
|
||||
}
|
||||
|
||||
before(t, computed.before)
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,14 @@ func (v Value) ComputeChangeForAttribute(attribute *jsonprovider.Attribute) chan
|
||||
|
||||
func (v Value) ComputeChangeForType(ctyType cty.Type) change.Change {
|
||||
|
||||
if sensitive, ok := v.CheckForSensitive(); ok {
|
||||
if sensitive, ok := v.checkForSensitive(); ok {
|
||||
return sensitive
|
||||
}
|
||||
|
||||
if computed, ok := v.checkForComputed(ctyType); ok {
|
||||
return computed
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctyType.IsPrimitiveType():
|
||||
return v.computeAttributeChangeAsPrimitive(ctyType)
|
||||
|
41
internal/command/jsonformat/differ/computed.go
Normal file
41
internal/command/jsonformat/differ/computed.go
Normal file
@ -0,0 +1,41 @@
|
||||
package differ
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/change"
|
||||
)
|
||||
|
||||
func (v Value) checkForComputed(ctyType cty.Type) (change.Change, bool) {
|
||||
unknown := v.isUnknown()
|
||||
|
||||
if !unknown {
|
||||
return change.Change{}, false
|
||||
}
|
||||
|
||||
// No matter what we do here, we want to treat the after value as explicit.
|
||||
// This is because it is going to be null in the value, and we don't want
|
||||
// the functions in this package to assume this means it has been deleted.
|
||||
v.AfterExplicit = true
|
||||
|
||||
if v.Before == nil {
|
||||
return v.AsChange(change.Computed(change.Change{})), true
|
||||
}
|
||||
|
||||
// If we get here, then we have a before value. We're going to model a
|
||||
// delete operation and our renderer later can render the overall change
|
||||
// accurately.
|
||||
|
||||
beforeValue := Value{
|
||||
Before: v.Before,
|
||||
BeforeSensitive: v.BeforeSensitive,
|
||||
}
|
||||
return v.AsChange(change.Computed(beforeValue.ComputeChangeForType(ctyType))), true
|
||||
}
|
||||
|
||||
func (v Value) isUnknown() bool {
|
||||
if unknown, ok := v.Unknown.(bool); ok {
|
||||
return unknown
|
||||
}
|
||||
return false
|
||||
}
|
@ -2,7 +2,7 @@ package differ
|
||||
|
||||
import "github.com/hashicorp/terraform/internal/command/jsonformat/change"
|
||||
|
||||
func (v Value) CheckForSensitive() (change.Change, bool) {
|
||||
func (v Value) checkForSensitive() (change.Change, bool) {
|
||||
beforeSensitive := v.isBeforeSensitive()
|
||||
afterSensitive := v.isAfterSensitive()
|
||||
|
||||
|
@ -24,7 +24,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Create,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidatePrimitive(nil, strptr("\"new\"")),
|
||||
},
|
||||
"primitive_delete": {
|
||||
@ -35,7 +34,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Delete,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidatePrimitive(strptr("\"old\""), nil),
|
||||
},
|
||||
"primitive_update": {
|
||||
@ -47,7 +45,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Update,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\"")),
|
||||
},
|
||||
"primitive_set_explicit_null": {
|
||||
@ -60,7 +57,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Update,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidatePrimitive(strptr("\"old\""), nil),
|
||||
},
|
||||
"primitive_unset_explicit_null": {
|
||||
@ -73,7 +69,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Update,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidatePrimitive(nil, strptr("\"new\"")),
|
||||
},
|
||||
"primitive_create_sensitive": {
|
||||
@ -86,7 +81,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Create,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidateSensitive(nil, "new", false, true),
|
||||
},
|
||||
"primitive_delete_sensitive": {
|
||||
@ -99,7 +93,6 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Delete,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidateSensitive("old", nil, true, false),
|
||||
},
|
||||
"primitive_update_sensitive": {
|
||||
@ -113,9 +106,32 @@ func TestValue_Attribute(t *testing.T) {
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Update,
|
||||
expectedReplace: false,
|
||||
validateChange: change.ValidateSensitive("old", "new", true, true),
|
||||
},
|
||||
"primitive_create_computed": {
|
||||
input: Value{
|
||||
Before: nil,
|
||||
After: nil,
|
||||
Unknown: true,
|
||||
},
|
||||
attribute: &jsonprovider.Attribute{
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Create,
|
||||
validateChange: change.ValidateComputed(nil),
|
||||
},
|
||||
"primitive_update_computed": {
|
||||
input: Value{
|
||||
Before: "old",
|
||||
After: nil,
|
||||
Unknown: true,
|
||||
},
|
||||
attribute: &jsonprovider.Attribute{
|
||||
AttributeType: []byte("\"string\""),
|
||||
},
|
||||
expectedAction: plans.Update,
|
||||
validateChange: change.ValidateComputed(change.ValidatePrimitive(strptr("\"old\""), nil)),
|
||||
},
|
||||
}
|
||||
for name, tc := range tcs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user