Skip to content

Commit

Permalink
ephemeral: render write-only attributes in plan UI
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielMSchmidt committed Dec 17, 2024
1 parent 572d12b commit 11ae302
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 39 deletions.
33 changes: 33 additions & 0 deletions internal/command/jsonformat/computed/renderers/write_only.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package renderers

import (
"fmt"

"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
)

var _ computed.DiffRenderer = (*writeOnlyRenderer)(nil)

func WriteOnly(sensitive bool) computed.DiffRenderer {
return &writeOnlyRenderer{
sensitive,
}
}

type writeOnlyRenderer struct {
sensitive bool
}

func (renderer writeOnlyRenderer) RenderHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) string {
if renderer.sensitive {
return fmt.Sprintf("(sensitive, write-only attribute)%s%s", nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
}
return fmt.Sprintf("(write-only attribute)%s%s", nullSuffix(diff.Action, opts), forcesReplacement(diff.Replace, opts))
}

func (renderer writeOnlyRenderer) WarningsHuman(diff computed.Diff, indent int, opts computed.RenderHumanOpts) []string {
return []string{}
}
20 changes: 19 additions & 1 deletion internal/command/jsonformat/differ/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package differ

import (
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
"github.com/hashicorp/terraform/internal/plans"
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"

Expand All @@ -13,10 +15,14 @@ import (
"github.com/hashicorp/terraform/internal/command/jsonprovider"
)

func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute) computed.Diff {
func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute, blockAction plans.Action) computed.Diff {
if attribute.AttributeNestedType != nil {
return computeDiffForNestedAttribute(change, attribute.AttributeNestedType)
}
if attribute.WriteOnly {
return computeDiffForWriteOnlyAttribute(change, blockAction)
}

return ComputeDiffForType(change, unmarshalAttribute(attribute))
}

Expand All @@ -43,6 +49,18 @@ func computeDiffForNestedAttribute(change structured.Change, nested *jsonprovide
}
}

func computeDiffForWriteOnlyAttribute(change structured.Change, blockAction plans.Action) computed.Diff {
renderer := renderers.WriteOnly(change.IsBeforeSensitive() || change.IsAfterSensitive())
replacePathMatches := change.ReplacePaths.Matches()
// Write-only diffs should always copy the behavior of the block they are in, except for updates
// since we don't want them to be always highlighted.
if blockAction == plans.Update {
return computed.NewDiff(renderer, plans.NoOp, replacePathMatches)
}
return computed.NewDiff(renderer, blockAction, replacePathMatches)

}

func ComputeDiffForType(change structured.Change, ctype cty.Type) computed.Diff {
if !change.NonLegacySchema {
// Empty strings in blocks should be considered null, because the legacy
Expand Down
7 changes: 4 additions & 3 deletions internal/command/jsonformat/differ/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co
current := change.GetDefaultActionForIteration()

blockValue := change.AsMap()
blockAction := change.CalculateAction()

attributes := make(map[string]computed.Diff)
for key, attr := range block.Attributes {
Expand All @@ -42,9 +43,9 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co
childValue.BeforeExplicit = false
childValue.AfterExplicit = false

childChange := ComputeDiffForAttribute(childValue, attr)
if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
// Don't record nil values at all in blocks.
childChange := ComputeDiffForAttribute(childValue, attr, blockAction)
if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil && !attr.WriteOnly {
// Don't record nil values at all in blocks except if they are write-only.
continue
}

Expand Down
68 changes: 34 additions & 34 deletions internal/command/jsonformat/differ/differ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -779,17 +779,17 @@ func TestValue_ObjectAttributes(t *testing.T) {
}

if tc.validateObject != nil {
tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute))
tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute))
tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update))
return
}

validate := renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace)
validate(t, ComputeDiffForAttribute(tc.input, attribute))
validate(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update))
})

t.Run("map", func(t *testing.T) {
Expand All @@ -803,22 +803,22 @@ func TestValue_ObjectAttributes(t *testing.T) {
validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": tc.validateObject,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": tc.validateSingleDiff,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})

t.Run("list", func(t *testing.T) {
Expand All @@ -829,30 +829,30 @@ func TestValue_ObjectAttributes(t *testing.T) {
input := wrapChangeInSlice(tc.input)

if tc.validateList != nil {
tc.validateList(t, ComputeDiffForAttribute(input, attribute))
tc.validateList(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateObject != nil {
validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
tc.validateObject,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
tc.validateSingleDiff,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})

t.Run("set", func(t *testing.T) {
Expand All @@ -869,30 +869,30 @@ func TestValue_ObjectAttributes(t *testing.T) {
ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateObject))
return ret
}(), collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateObject != nil {
validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
tc.validateObject,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
tc.validateSingleDiff,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})
})

Expand All @@ -914,17 +914,17 @@ func TestValue_ObjectAttributes(t *testing.T) {
}

if tc.validateNestedObject != nil {
tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute))
tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute))
tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update))
return
}

validate := renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace)
validate(t, ComputeDiffForAttribute(tc.input, attribute))
validate(t, ComputeDiffForAttribute(tc.input, attribute, plans.Update))
})

t.Run("map", func(t *testing.T) {
Expand All @@ -949,22 +949,22 @@ func TestValue_ObjectAttributes(t *testing.T) {
validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": tc.validateNestedObject,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": tc.validateSingleDiff,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})

t.Run("list", func(t *testing.T) {
Expand All @@ -989,22 +989,22 @@ func TestValue_ObjectAttributes(t *testing.T) {
validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{
tc.validateNestedObject,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{
tc.validateSingleDiff,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{
renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})

t.Run("set", func(t *testing.T) {
Expand Down Expand Up @@ -1032,30 +1032,30 @@ func TestValue_ObjectAttributes(t *testing.T) {
ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateNestedObject))
return ret
}(), collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateNestedObject != nil {
validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
tc.validateNestedObject,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

if tc.validateSingleDiff != nil {
validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
tc.validateSingleDiff,
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
}, collectionDefaultAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})
})
}
Expand Down Expand Up @@ -1840,7 +1840,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) {
t.Run("direct", func(t *testing.T) {
tc.validateDiff(t, ComputeDiffForAttribute(tc.input, &jsonprovider.Attribute{
AttributeType: unmarshalType(t, tc.attribute),
}))
}, plans.Update))
})

t.Run("map", func(t *testing.T) {
Expand All @@ -1852,7 +1852,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) {
validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
"element": tc.validateDiff,
}, defaultCollectionsAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})

t.Run("list", func(t *testing.T) {
Expand All @@ -1863,14 +1863,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) {

if tc.validateSliceDiffs != nil {
validate := renderers.ValidateList(tc.validateSliceDiffs, defaultCollectionsAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
tc.validateDiff,
}, defaultCollectionsAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})

t.Run("set", func(t *testing.T) {
Expand All @@ -1881,14 +1881,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) {

if tc.validateSliceDiffs != nil {
validate := renderers.ValidateSet(tc.validateSetDiffs, defaultCollectionsAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
return
}

validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
tc.validateDiff,
}, defaultCollectionsAction, false)
validate(t, ComputeDiffForAttribute(input, attribute))
validate(t, ComputeDiffForAttribute(input, attribute, plans.Update))
})
})
}
Expand Down Expand Up @@ -2260,7 +2260,7 @@ func TestValue_CollectionAttributes(t *testing.T) {
}

t.Run(name, func(t *testing.T) {
tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute))
tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute, plans.Update))
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/command/jsonformat/differ/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func computeAttributeDiffAsObject(change structured.Change, attributes map[strin

func computeAttributeDiffAsNestedObject(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff {
attributeDiffs, action := processObject(change, attributes, func(value structured.Change, attribute *jsonprovider.Attribute) computed.Diff {
return ComputeDiffForAttribute(value, attribute)
return ComputeDiffForAttribute(value, attribute, change.CalculateAction())
})
return computed.NewDiff(renderers.NestedObject(attributeDiffs), action, change.ReplacePaths.Matches())
}
Expand Down
Loading

0 comments on commit 11ae302

Please sign in to comment.