mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-05-20 12:02:01 -04:00
Refactor of differ to make code reuse easier (#33054)
* refactor of differ to make code reuse easier * fix imports
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package differ
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
@@ -9,42 +10,42 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
)
|
||||
|
||||
func (change Change) ComputeDiffForAttribute(attribute *jsonprovider.Attribute) computed.Diff {
|
||||
func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute) computed.Diff {
|
||||
if attribute.AttributeNestedType != nil {
|
||||
return change.computeDiffForNestedAttribute(attribute.AttributeNestedType)
|
||||
return computeDiffForNestedAttribute(change, attribute.AttributeNestedType)
|
||||
}
|
||||
return change.ComputeDiffForType(unmarshalAttribute(attribute))
|
||||
return ComputeDiffForType(change, unmarshalAttribute(attribute))
|
||||
}
|
||||
|
||||
func (change Change) computeDiffForNestedAttribute(nested *jsonprovider.NestedType) computed.Diff {
|
||||
if sensitive, ok := change.checkForSensitiveNestedAttribute(nested); ok {
|
||||
func computeDiffForNestedAttribute(change structured.Change, nested *jsonprovider.NestedType) computed.Diff {
|
||||
if sensitive, ok := checkForSensitiveNestedAttribute(change, nested); ok {
|
||||
return sensitive
|
||||
}
|
||||
|
||||
if computed, ok := change.checkForUnknownNestedAttribute(nested); ok {
|
||||
if computed, ok := checkForUnknownNestedAttribute(change, nested); ok {
|
||||
return computed
|
||||
}
|
||||
|
||||
switch NestingMode(nested.NestingMode) {
|
||||
case nestingModeSingle, nestingModeGroup:
|
||||
return change.computeAttributeDiffAsNestedObject(nested.Attributes)
|
||||
return computeAttributeDiffAsNestedObject(change, nested.Attributes)
|
||||
case nestingModeMap:
|
||||
return change.computeAttributeDiffAsNestedMap(nested.Attributes)
|
||||
return computeAttributeDiffAsNestedMap(change, nested.Attributes)
|
||||
case nestingModeList:
|
||||
return change.computeAttributeDiffAsNestedList(nested.Attributes)
|
||||
return computeAttributeDiffAsNestedList(change, nested.Attributes)
|
||||
case nestingModeSet:
|
||||
return change.computeAttributeDiffAsNestedSet(nested.Attributes)
|
||||
return computeAttributeDiffAsNestedSet(change, nested.Attributes)
|
||||
default:
|
||||
panic("unrecognized nesting mode: " + nested.NestingMode)
|
||||
}
|
||||
}
|
||||
|
||||
func (change Change) ComputeDiffForType(ctype cty.Type) computed.Diff {
|
||||
if sensitive, ok := change.checkForSensitiveType(ctype); ok {
|
||||
func ComputeDiffForType(change structured.Change, ctype cty.Type) computed.Diff {
|
||||
if sensitive, ok := checkForSensitiveType(change, ctype); ok {
|
||||
return sensitive
|
||||
}
|
||||
|
||||
if computed, ok := change.checkForUnknownType(ctype); ok {
|
||||
if computed, ok := checkForUnknownType(change, ctype); ok {
|
||||
return computed
|
||||
}
|
||||
|
||||
@@ -56,19 +57,19 @@ func (change Change) ComputeDiffForType(ctype cty.Type) computed.Diff {
|
||||
// function computeChangeForDynamicValues(), but external callers will
|
||||
// only be in this situation when processing outputs so this function
|
||||
// is named for their benefit.
|
||||
return change.ComputeDiffForOutput()
|
||||
return ComputeDiffForOutput(change)
|
||||
case ctype.IsPrimitiveType():
|
||||
return change.computeAttributeDiffAsPrimitive(ctype)
|
||||
return computeAttributeDiffAsPrimitive(change, ctype)
|
||||
case ctype.IsObjectType():
|
||||
return change.computeAttributeDiffAsObject(ctype.AttributeTypes())
|
||||
return computeAttributeDiffAsObject(change, ctype.AttributeTypes())
|
||||
case ctype.IsMapType():
|
||||
return change.computeAttributeDiffAsMap(ctype.ElementType())
|
||||
return computeAttributeDiffAsMap(change, ctype.ElementType())
|
||||
case ctype.IsListType():
|
||||
return change.computeAttributeDiffAsList(ctype.ElementType())
|
||||
return computeAttributeDiffAsList(change, ctype.ElementType())
|
||||
case ctype.IsTupleType():
|
||||
return change.computeAttributeDiffAsTuple(ctype.TupleElementTypes())
|
||||
return computeAttributeDiffAsTuple(change, ctype.TupleElementTypes())
|
||||
case ctype.IsSetType():
|
||||
return change.computeAttributeDiffAsSet(ctype.ElementType())
|
||||
return computeAttributeDiffAsSet(change, ctype.ElementType())
|
||||
default:
|
||||
panic("unrecognized type: " + ctype.FriendlyName())
|
||||
}
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
package attribute_path
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Matcher provides an interface for stepping through changes following an
|
||||
// attribute path.
|
||||
//
|
||||
// GetChildWithKey and GetChildWithIndex will check if any of the internal paths
|
||||
// match the provided key or index, and return a new Matcher that will match
|
||||
// that children or potentially it's children.
|
||||
//
|
||||
// The caller of the above functions is required to know whether the next value
|
||||
// in the path is a list type or an object type and call the relevant function,
|
||||
// otherwise these functions will crash/panic.
|
||||
//
|
||||
// The Matches function returns true if the paths you have traversed until now
|
||||
// ends.
|
||||
type Matcher interface {
|
||||
// Matches returns true if we have reached the end of a path and found an
|
||||
// exact match.
|
||||
Matches() bool
|
||||
|
||||
// MatchesPartial returns true if the current attribute is part of a path
|
||||
// but not necessarily at the end of the path.
|
||||
MatchesPartial() bool
|
||||
|
||||
GetChildWithKey(key string) Matcher
|
||||
GetChildWithIndex(index int) Matcher
|
||||
}
|
||||
|
||||
// Parse accepts a json.RawMessage and outputs a formatted Matcher object.
|
||||
//
|
||||
// Parse expects the message to be a JSON array of JSON arrays containing
|
||||
// strings and floats. This function happily accepts a null input representing
|
||||
// none of the changes in this resource are causing a replacement. The propagate
|
||||
// argument tells the matcher to propagate any matches to the matched attributes
|
||||
// children.
|
||||
//
|
||||
// In general, this function is designed to accept messages that have been
|
||||
// produced by the lossy cty.Paths conversion functions within the jsonplan
|
||||
// package. There is nothing particularly special about that conversion process
|
||||
// though, it just produces the nested JSON arrays described above.
|
||||
func Parse(message json.RawMessage, propagate bool) Matcher {
|
||||
matcher := &PathMatcher{
|
||||
Propagate: propagate,
|
||||
}
|
||||
if message == nil {
|
||||
return matcher
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(message, &matcher.Paths); err != nil {
|
||||
panic("failed to unmarshal attribute paths: " + err.Error())
|
||||
}
|
||||
|
||||
return matcher
|
||||
}
|
||||
|
||||
// Empty returns an empty PathMatcher that will by default match nothing.
|
||||
//
|
||||
// We give direct access to the PathMatcher struct so a matcher can be built
|
||||
// in parts with the Append and AppendSingle functions.
|
||||
func Empty(propagate bool) *PathMatcher {
|
||||
return &PathMatcher{
|
||||
Propagate: propagate,
|
||||
}
|
||||
}
|
||||
|
||||
// Append accepts an existing PathMatcher and returns a new one that attaches
|
||||
// all the paths from message with the existing paths.
|
||||
//
|
||||
// The new PathMatcher is created fresh, and the existing one is unchanged.
|
||||
func Append(matcher *PathMatcher, message json.RawMessage) *PathMatcher {
|
||||
var values [][]interface{}
|
||||
if err := json.Unmarshal(message, &values); err != nil {
|
||||
panic("failed to unmarshal attribute paths: " + err.Error())
|
||||
}
|
||||
|
||||
return &PathMatcher{
|
||||
Propagate: matcher.Propagate,
|
||||
Paths: append(matcher.Paths, values...),
|
||||
}
|
||||
}
|
||||
|
||||
// AppendSingle accepts an existing PathMatcher and returns a new one that
|
||||
// attaches the single path from message with the existing paths.
|
||||
//
|
||||
// The new PathMatcher is created fresh, and the existing one is unchanged.
|
||||
func AppendSingle(matcher *PathMatcher, message json.RawMessage) *PathMatcher {
|
||||
var values []interface{}
|
||||
if err := json.Unmarshal(message, &values); err != nil {
|
||||
panic("failed to unmarshal attribute paths: " + err.Error())
|
||||
}
|
||||
|
||||
return &PathMatcher{
|
||||
Propagate: matcher.Propagate,
|
||||
Paths: append(matcher.Paths, values),
|
||||
}
|
||||
}
|
||||
|
||||
// PathMatcher contains a slice of paths that represent paths through the values
|
||||
// to relevant/tracked attributes.
|
||||
type PathMatcher struct {
|
||||
// We represent our internal paths as a [][]interface{} as the cty.Paths
|
||||
// conversion process is lossy. Since the type information is lost there
|
||||
// is no (easy) way to reproduce the original cty.Paths object. Instead,
|
||||
// we simply rely on the external callers to know the type information and
|
||||
// call the correct GetChild function.
|
||||
Paths [][]interface{}
|
||||
|
||||
// Propagate tells the matcher that it should propagate any matches it finds
|
||||
// onto the children of that match.
|
||||
Propagate bool
|
||||
}
|
||||
|
||||
func (p *PathMatcher) Matches() bool {
|
||||
for _, path := range p.Paths {
|
||||
if len(path) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PathMatcher) MatchesPartial() bool {
|
||||
return len(p.Paths) > 0
|
||||
}
|
||||
|
||||
func (p *PathMatcher) GetChildWithKey(key string) Matcher {
|
||||
child := &PathMatcher{
|
||||
Propagate: p.Propagate,
|
||||
}
|
||||
for _, path := range p.Paths {
|
||||
if len(path) == 0 {
|
||||
// This means that the current value matched, but not necessarily
|
||||
// it's child.
|
||||
|
||||
if p.Propagate {
|
||||
// If propagate is true, then our child match our matches
|
||||
child.Paths = append(child.Paths, path)
|
||||
}
|
||||
|
||||
// If not we would simply drop this path from our set of paths but
|
||||
// either way we just continue.
|
||||
continue
|
||||
}
|
||||
|
||||
if path[0].(string) == key {
|
||||
child.Paths = append(child.Paths, path[1:])
|
||||
}
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
func (p *PathMatcher) GetChildWithIndex(index int) Matcher {
|
||||
child := &PathMatcher{
|
||||
Propagate: p.Propagate,
|
||||
}
|
||||
for _, path := range p.Paths {
|
||||
if len(path) == 0 {
|
||||
// This means that the current value matched, but not necessarily
|
||||
// it's child.
|
||||
|
||||
if p.Propagate {
|
||||
// If propagate is true, then our child match our matches
|
||||
child.Paths = append(child.Paths, path)
|
||||
}
|
||||
|
||||
// If not we would simply drop this path from our set of paths but
|
||||
// either way we just continue.
|
||||
continue
|
||||
}
|
||||
|
||||
if int(path[0].(float64)) == index {
|
||||
child.Paths = append(child.Paths, path[1:])
|
||||
}
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
// AlwaysMatcher returns a matcher that will always match all paths.
|
||||
func AlwaysMatcher() Matcher {
|
||||
return &alwaysMatcher{}
|
||||
}
|
||||
|
||||
type alwaysMatcher struct{}
|
||||
|
||||
func (a *alwaysMatcher) Matches() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *alwaysMatcher) MatchesPartial() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *alwaysMatcher) GetChildWithKey(_ string) Matcher {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *alwaysMatcher) GetChildWithIndex(_ int) Matcher {
|
||||
return a
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
package attribute_path
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPathMatcher_FollowsPath(t *testing.T) {
|
||||
var matcher Matcher
|
||||
|
||||
matcher = &PathMatcher{
|
||||
Paths: [][]interface{}{
|
||||
{
|
||||
float64(0),
|
||||
"key",
|
||||
float64(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at base level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at base level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at first level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at first level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithKey("key")
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at second level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at second level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if !matcher.Matches() {
|
||||
t.Errorf("should have exact matched at leaf level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at leaf level")
|
||||
}
|
||||
}
|
||||
func TestPathMatcher_Propagates(t *testing.T) {
|
||||
var matcher Matcher
|
||||
|
||||
matcher = &PathMatcher{
|
||||
Paths: [][]interface{}{
|
||||
{
|
||||
float64(0),
|
||||
"key",
|
||||
},
|
||||
},
|
||||
Propagate: true,
|
||||
}
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at base level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at base level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at first level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at first level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithKey("key")
|
||||
|
||||
if !matcher.Matches() {
|
||||
t.Errorf("should have exact matched at second level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at second level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if !matcher.Matches() {
|
||||
t.Errorf("should have exact matched at leaf level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at leaf level")
|
||||
}
|
||||
}
|
||||
func TestPathMatcher_DoesNotPropagate(t *testing.T) {
|
||||
var matcher Matcher
|
||||
|
||||
matcher = &PathMatcher{
|
||||
Paths: [][]interface{}{
|
||||
{
|
||||
float64(0),
|
||||
"key",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at base level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at base level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at first level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at first level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithKey("key")
|
||||
|
||||
if !matcher.Matches() {
|
||||
t.Errorf("should have exact matched at second level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at second level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at leaf level")
|
||||
}
|
||||
if matcher.MatchesPartial() {
|
||||
t.Errorf("should not have partial matched at leaf level")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathMatcher_BreaksPath(t *testing.T) {
|
||||
var matcher Matcher
|
||||
|
||||
matcher = &PathMatcher{
|
||||
Paths: [][]interface{}{
|
||||
{
|
||||
float64(0),
|
||||
"key",
|
||||
float64(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at base level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at base level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at first level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at first level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithKey("invalid")
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at second level")
|
||||
}
|
||||
if matcher.MatchesPartial() {
|
||||
t.Errorf("should not have partial matched at second level")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathMatcher_MultiplePaths(t *testing.T) {
|
||||
var matcher Matcher
|
||||
|
||||
matcher = &PathMatcher{
|
||||
Paths: [][]interface{}{
|
||||
{
|
||||
float64(0),
|
||||
"key",
|
||||
float64(0),
|
||||
},
|
||||
{
|
||||
float64(0),
|
||||
"key",
|
||||
float64(1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at base level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at base level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithIndex(0)
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at first level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at first level")
|
||||
}
|
||||
|
||||
matcher = matcher.GetChildWithKey("key")
|
||||
|
||||
if matcher.Matches() {
|
||||
t.Errorf("should not have exact matched at second level")
|
||||
}
|
||||
if !matcher.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at second level")
|
||||
}
|
||||
|
||||
validZero := matcher.GetChildWithIndex(0)
|
||||
validOne := matcher.GetChildWithIndex(1)
|
||||
invalid := matcher.GetChildWithIndex(2)
|
||||
|
||||
if !validZero.Matches() {
|
||||
t.Errorf("should have exact matched at leaf level")
|
||||
}
|
||||
if !validZero.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at leaf level")
|
||||
}
|
||||
|
||||
if !validOne.Matches() {
|
||||
t.Errorf("should have exact matched at leaf level")
|
||||
}
|
||||
if !validOne.MatchesPartial() {
|
||||
t.Errorf("should have partial matched at leaf level")
|
||||
}
|
||||
|
||||
if invalid.Matches() {
|
||||
t.Errorf("should not have exact matched at leaf level")
|
||||
}
|
||||
if invalid.MatchesPartial() {
|
||||
t.Errorf("should not have partial matched at leaf level")
|
||||
}
|
||||
}
|
||||
@@ -4,26 +4,27 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Diff {
|
||||
if sensitive, ok := change.checkForSensitiveBlock(block); ok {
|
||||
func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) computed.Diff {
|
||||
if sensitive, ok := checkForSensitiveBlock(change, block); ok {
|
||||
return sensitive
|
||||
}
|
||||
|
||||
if unknown, ok := change.checkForUnknownBlock(block); ok {
|
||||
if unknown, ok := checkForUnknownBlock(change, block); ok {
|
||||
return unknown
|
||||
}
|
||||
|
||||
current := change.getDefaultActionForIteration()
|
||||
current := change.GetDefaultActionForIteration()
|
||||
|
||||
blockValue := change.asMap()
|
||||
blockValue := change.AsMap()
|
||||
|
||||
attributes := make(map[string]computed.Diff)
|
||||
for key, attr := range block.Attributes {
|
||||
childValue := blockValue.getChild(key)
|
||||
childValue := blockValue.GetChild(key)
|
||||
|
||||
if !childValue.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
@@ -43,7 +44,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif
|
||||
childValue.BeforeExplicit = false
|
||||
childValue.AfterExplicit = false
|
||||
|
||||
childChange := childValue.ComputeDiffForAttribute(attr)
|
||||
childChange := ComputeDiffForAttribute(childValue, attr)
|
||||
if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
||||
// Don't record nil values at all in blocks.
|
||||
continue
|
||||
@@ -64,20 +65,20 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif
|
||||
}
|
||||
|
||||
for key, blockType := range block.BlockTypes {
|
||||
childValue := blockValue.getChild(key)
|
||||
childValue := blockValue.GetChild(key)
|
||||
|
||||
if !childValue.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
childValue = childValue.AsNoOp()
|
||||
}
|
||||
|
||||
beforeSensitive := childValue.isBeforeSensitive()
|
||||
afterSensitive := childValue.isAfterSensitive()
|
||||
beforeSensitive := childValue.IsBeforeSensitive()
|
||||
afterSensitive := childValue.IsAfterSensitive()
|
||||
forcesReplacement := childValue.ReplacePaths.Matches()
|
||||
|
||||
switch NestingMode(blockType.NestingMode) {
|
||||
case nestingModeSet:
|
||||
diffs, action := childValue.computeBlockDiffsAsSet(blockType.Block)
|
||||
diffs, action := computeBlockDiffsAsSet(childValue, blockType.Block)
|
||||
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
||||
// Don't record nil values in blocks.
|
||||
continue
|
||||
@@ -85,7 +86,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif
|
||||
blocks.AddAllSetBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
|
||||
current = collections.CompareActions(current, action)
|
||||
case nestingModeList:
|
||||
diffs, action := childValue.computeBlockDiffsAsList(blockType.Block)
|
||||
diffs, action := computeBlockDiffsAsList(childValue, blockType.Block)
|
||||
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
||||
// Don't record nil values in blocks.
|
||||
continue
|
||||
@@ -93,7 +94,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif
|
||||
blocks.AddAllListBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
|
||||
current = collections.CompareActions(current, action)
|
||||
case nestingModeMap:
|
||||
diffs, action := childValue.computeBlockDiffsAsMap(blockType.Block)
|
||||
diffs, action := computeBlockDiffsAsMap(childValue, blockType.Block)
|
||||
if action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
||||
// Don't record nil values in blocks.
|
||||
continue
|
||||
@@ -101,7 +102,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif
|
||||
blocks.AddAllMapBlocks(key, diffs, forcesReplacement, beforeSensitive, afterSensitive)
|
||||
current = collections.CompareActions(current, action)
|
||||
case nestingModeSingle, nestingModeGroup:
|
||||
diff := childValue.ComputeDiffForBlock(blockType.Block)
|
||||
diff := ComputeDiffForBlock(childValue, blockType.Block)
|
||||
if diff.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
||||
// Don't record nil values in blocks.
|
||||
continue
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
package differ
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonplan"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonstate"
|
||||
viewsjson "github.com/hashicorp/terraform/internal/command/views/json"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
// Change contains the unmarshalled generic interface{} types that are output by
|
||||
// the JSON functions in the various json packages (such as jsonplan and
|
||||
// jsonprovider).
|
||||
//
|
||||
// A Change can be converted into a computed.Diff, ready for rendering, with the
|
||||
// ComputeDiffForAttribute, ComputeDiffForOutput, and ComputeDiffForBlock
|
||||
// functions.
|
||||
//
|
||||
// The Before and After fields are actually go-cty values, but we cannot convert
|
||||
// them directly because of the Terraform Cloud redacted endpoint. The redacted
|
||||
// endpoint turns sensitive values into strings regardless of their types.
|
||||
// Because of this, we cannot just do a direct conversion using the ctyjson
|
||||
// package. We would have to iterate through the schema first, find the
|
||||
// sensitive values and their mapped types, update the types inside the schema
|
||||
// to strings, and then go back and do the overall conversion. This isn't
|
||||
// including any of the more complicated parts around what happens if something
|
||||
// was sensitive before and isn't sensitive after or vice versa. This would mean
|
||||
// the type would need to change between the before and after value. It is in
|
||||
// fact just easier to iterate through the values as generic JSON interfaces.
|
||||
type Change struct {
|
||||
|
||||
// BeforeExplicit matches AfterExplicit except references the Before value.
|
||||
BeforeExplicit bool
|
||||
|
||||
// AfterExplicit refers to whether the After value is explicit or
|
||||
// implicit. It is explicit if it has been specified by the user, and
|
||||
// implicit if it has been set as a consequence of other changes.
|
||||
//
|
||||
// For example, explicitly setting a value to null in a list should result
|
||||
// in After being null and AfterExplicit being true. In comparison,
|
||||
// removing an element from a list should also result in After being null
|
||||
// and AfterExplicit being false. Without the explicit information our
|
||||
// functions would not be able to tell the difference between these two
|
||||
// cases.
|
||||
AfterExplicit bool
|
||||
|
||||
// Before contains the value before the proposed change.
|
||||
//
|
||||
// The type of the value should be informed by the schema and cast
|
||||
// appropriately when needed.
|
||||
Before interface{}
|
||||
|
||||
// After contains the value after the proposed change.
|
||||
//
|
||||
// The type of the value should be informed by the schema and cast
|
||||
// appropriately when needed.
|
||||
After interface{}
|
||||
|
||||
// Unknown describes whether the After value is known or unknown at the time
|
||||
// of the plan. In practice, this means the after value should be rendered
|
||||
// simply as `(known after apply)`.
|
||||
//
|
||||
// The concrete value could be a boolean describing whether the entirety of
|
||||
// the After value is unknown, or it could be a list or a map depending on
|
||||
// the schema describing whether specific elements or attributes within the
|
||||
// value are unknown.
|
||||
Unknown interface{}
|
||||
|
||||
// BeforeSensitive matches Unknown, but references whether the Before value
|
||||
// is sensitive.
|
||||
BeforeSensitive interface{}
|
||||
|
||||
// AfterSensitive matches Unknown, but references whether the After value is
|
||||
// sensitive.
|
||||
AfterSensitive interface{}
|
||||
|
||||
// ReplacePaths contains a set of paths that point to attributes/elements
|
||||
// that are causing the overall resource to be replaced rather than simply
|
||||
// updated.
|
||||
ReplacePaths attribute_path.Matcher
|
||||
|
||||
// RelevantAttributes contains a set of paths that point attributes/elements
|
||||
// that we should display. Any element/attribute not matched by this Matcher
|
||||
// should be skipped.
|
||||
RelevantAttributes attribute_path.Matcher
|
||||
}
|
||||
|
||||
// FromJsonChange unmarshals the raw []byte values in the jsonplan.Change
|
||||
// structs into generic interface{} types that can be reasoned about.
|
||||
func FromJsonChange(change jsonplan.Change, relevantAttributes attribute_path.Matcher) Change {
|
||||
return Change{
|
||||
Before: unmarshalGeneric(change.Before),
|
||||
After: unmarshalGeneric(change.After),
|
||||
Unknown: unmarshalGeneric(change.AfterUnknown),
|
||||
BeforeSensitive: unmarshalGeneric(change.BeforeSensitive),
|
||||
AfterSensitive: unmarshalGeneric(change.AfterSensitive),
|
||||
ReplacePaths: attribute_path.Parse(change.ReplacePaths, false),
|
||||
RelevantAttributes: relevantAttributes,
|
||||
}
|
||||
}
|
||||
|
||||
// FromJsonResource unmarshals the raw values in the jsonstate.Resource structs
|
||||
// into generic interface{} types that can be reasoned about.
|
||||
func FromJsonResource(resource jsonstate.Resource) Change {
|
||||
return Change{
|
||||
// We model resource formatting as NoOps.
|
||||
Before: unwrapAttributeValues(resource.AttributeValues),
|
||||
After: unwrapAttributeValues(resource.AttributeValues),
|
||||
|
||||
// We have some sensitive values, but we don't have any unknown values.
|
||||
Unknown: false,
|
||||
BeforeSensitive: unmarshalGeneric(resource.SensitiveValues),
|
||||
AfterSensitive: unmarshalGeneric(resource.SensitiveValues),
|
||||
|
||||
// We don't display replacement data for resources, and all attributes
|
||||
// are relevant.
|
||||
ReplacePaths: attribute_path.Empty(false),
|
||||
RelevantAttributes: attribute_path.AlwaysMatcher(),
|
||||
}
|
||||
}
|
||||
|
||||
// FromJsonOutput unmarshals the raw values in the jsonstate.Output structs into
|
||||
// generic interface{} types that can be reasoned about.
|
||||
func FromJsonOutput(output jsonstate.Output) Change {
|
||||
return Change{
|
||||
// We model resource formatting as NoOps.
|
||||
Before: unmarshalGeneric(output.Value),
|
||||
After: unmarshalGeneric(output.Value),
|
||||
|
||||
// We have some sensitive values, but we don't have any unknown values.
|
||||
Unknown: false,
|
||||
BeforeSensitive: output.Sensitive,
|
||||
AfterSensitive: output.Sensitive,
|
||||
|
||||
// We don't display replacement data for resources, and all attributes
|
||||
// are relevant.
|
||||
ReplacePaths: attribute_path.Empty(false),
|
||||
RelevantAttributes: attribute_path.AlwaysMatcher(),
|
||||
}
|
||||
}
|
||||
|
||||
// FromJsonViewsOutput unmarshals the raw values in the viewsjson.Output structs into
|
||||
// generic interface{} types that can be reasoned about.
|
||||
func FromJsonViewsOutput(output viewsjson.Output) Change {
|
||||
return Change{
|
||||
// We model resource formatting as NoOps.
|
||||
Before: unmarshalGeneric(output.Value),
|
||||
After: unmarshalGeneric(output.Value),
|
||||
|
||||
// We have some sensitive values, but we don't have any unknown values.
|
||||
Unknown: false,
|
||||
BeforeSensitive: output.Sensitive,
|
||||
AfterSensitive: output.Sensitive,
|
||||
|
||||
// We don't display replacement data for resources, and all attributes
|
||||
// are relevant.
|
||||
ReplacePaths: attribute_path.Empty(false),
|
||||
RelevantAttributes: attribute_path.AlwaysMatcher(),
|
||||
}
|
||||
}
|
||||
|
||||
func (change Change) asDiff(renderer computed.DiffRenderer) computed.Diff {
|
||||
return computed.NewDiff(renderer, change.calculateChange(), change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) calculateChange() plans.Action {
|
||||
if (change.Before == nil && !change.BeforeExplicit) && (change.After != nil || change.AfterExplicit) {
|
||||
return plans.Create
|
||||
}
|
||||
if (change.After == nil && !change.AfterExplicit) && (change.Before != nil || change.BeforeExplicit) {
|
||||
return plans.Delete
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(change.Before, change.After) && change.AfterExplicit == change.BeforeExplicit && change.isAfterSensitive() == change.isBeforeSensitive() {
|
||||
return plans.NoOp
|
||||
}
|
||||
|
||||
return plans.Update
|
||||
}
|
||||
|
||||
// getDefaultActionForIteration is used to guess what the change could be for
|
||||
// complex attributes (collections and objects) and blocks.
|
||||
//
|
||||
// You can't really tell the difference between a NoOp and an Update just by
|
||||
// looking at the attribute itself as you need to inspect the children.
|
||||
//
|
||||
// This function returns a Delete or a Create action if the before or after
|
||||
// values were null, and returns a NoOp for all other cases. It should be used
|
||||
// in conjunction with compareActions to calculate the actual action based on
|
||||
// the actions of the children.
|
||||
func (change Change) getDefaultActionForIteration() plans.Action {
|
||||
if change.Before == nil && change.After == nil {
|
||||
return plans.NoOp
|
||||
}
|
||||
|
||||
if change.Before == nil {
|
||||
return plans.Create
|
||||
}
|
||||
if change.After == nil {
|
||||
return plans.Delete
|
||||
}
|
||||
return plans.NoOp
|
||||
}
|
||||
|
||||
// AsNoOp returns the current change as if it is a NoOp operation.
|
||||
//
|
||||
// Basically it replaces all the after values with the before values.
|
||||
func (change Change) AsNoOp() Change {
|
||||
return Change{
|
||||
BeforeExplicit: change.BeforeExplicit,
|
||||
AfterExplicit: change.BeforeExplicit,
|
||||
Before: change.Before,
|
||||
After: change.Before,
|
||||
Unknown: false,
|
||||
BeforeSensitive: change.BeforeSensitive,
|
||||
AfterSensitive: change.BeforeSensitive,
|
||||
ReplacePaths: change.ReplacePaths,
|
||||
RelevantAttributes: change.RelevantAttributes,
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalGeneric(raw json.RawMessage) interface{} {
|
||||
if raw == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out interface{}
|
||||
if err := json.Unmarshal(raw, &out); err != nil {
|
||||
panic("unrecognized json type: " + err.Error())
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func unwrapAttributeValues(values jsonstate.AttributeValues) map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
for key, value := range values {
|
||||
out[key] = unmarshalGeneric(value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package differ
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
|
||||
)
|
||||
|
||||
// ChangeMap is a Change that represents a Map or an Object type, and has
|
||||
// converted the relevant interfaces into maps for easier access.
|
||||
type ChangeMap struct {
|
||||
// Before contains the value before the proposed change.
|
||||
Before map[string]interface{}
|
||||
|
||||
// After contains the value after the proposed change.
|
||||
After map[string]interface{}
|
||||
|
||||
// Unknown contains the unknown status of any elements/attributes of this
|
||||
// map/object.
|
||||
Unknown map[string]interface{}
|
||||
|
||||
// BeforeSensitive contains the before sensitive status of any
|
||||
// elements/attributes of this map/object.
|
||||
BeforeSensitive map[string]interface{}
|
||||
|
||||
// AfterSensitive contains the after sensitive status of any
|
||||
// elements/attributes of this map/object.
|
||||
AfterSensitive map[string]interface{}
|
||||
|
||||
// ReplacePaths matches the same attributes in Change exactly.
|
||||
ReplacePaths attribute_path.Matcher
|
||||
|
||||
// RelevantAttributes matches the same attributes in Change exactly.
|
||||
RelevantAttributes attribute_path.Matcher
|
||||
}
|
||||
|
||||
func (change Change) asMap() ChangeMap {
|
||||
return ChangeMap{
|
||||
Before: genericToMap(change.Before),
|
||||
After: genericToMap(change.After),
|
||||
Unknown: genericToMap(change.Unknown),
|
||||
BeforeSensitive: genericToMap(change.BeforeSensitive),
|
||||
AfterSensitive: genericToMap(change.AfterSensitive),
|
||||
ReplacePaths: change.ReplacePaths,
|
||||
RelevantAttributes: change.RelevantAttributes,
|
||||
}
|
||||
}
|
||||
|
||||
func (m ChangeMap) getChild(key string) Change {
|
||||
before, beforeExplicit := getFromGenericMap(m.Before, key)
|
||||
after, afterExplicit := getFromGenericMap(m.After, key)
|
||||
unknown, _ := getFromGenericMap(m.Unknown, key)
|
||||
beforeSensitive, _ := getFromGenericMap(m.BeforeSensitive, key)
|
||||
afterSensitive, _ := getFromGenericMap(m.AfterSensitive, key)
|
||||
|
||||
return Change{
|
||||
BeforeExplicit: beforeExplicit,
|
||||
AfterExplicit: afterExplicit,
|
||||
Before: before,
|
||||
After: after,
|
||||
Unknown: unknown,
|
||||
BeforeSensitive: beforeSensitive,
|
||||
AfterSensitive: afterSensitive,
|
||||
ReplacePaths: m.ReplacePaths.GetChildWithKey(key),
|
||||
RelevantAttributes: m.RelevantAttributes.GetChildWithKey(key),
|
||||
}
|
||||
}
|
||||
|
||||
func getFromGenericMap(generic map[string]interface{}, key string) (interface{}, bool) {
|
||||
if generic == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if child, ok := generic[key]; ok {
|
||||
return child, ok
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func genericToMap(generic interface{}) map[string]interface{} {
|
||||
if concrete, ok := generic.(map[string]interface{}); ok {
|
||||
return concrete
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package differ
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
|
||||
)
|
||||
|
||||
// ChangeSlice is a Change that represents a Tuple, Set, or List type, and has
|
||||
// converted the relevant interfaces into slices for easier access.
|
||||
type ChangeSlice struct {
|
||||
// Before contains the value before the proposed change.
|
||||
Before []interface{}
|
||||
|
||||
// After contains the value after the proposed change.
|
||||
After []interface{}
|
||||
|
||||
// Unknown contains the unknown status of any elements of this list/set.
|
||||
Unknown []interface{}
|
||||
|
||||
// BeforeSensitive contains the before sensitive status of any elements of
|
||||
//this list/set.
|
||||
BeforeSensitive []interface{}
|
||||
|
||||
// AfterSensitive contains the after sensitive status of any elements of
|
||||
//this list/set.
|
||||
AfterSensitive []interface{}
|
||||
|
||||
// ReplacePaths matches the same attributes in Change exactly.
|
||||
ReplacePaths attribute_path.Matcher
|
||||
|
||||
// RelevantAttributes matches the same attributes in Change exactly.
|
||||
RelevantAttributes attribute_path.Matcher
|
||||
}
|
||||
|
||||
func (change Change) asSlice() ChangeSlice {
|
||||
return ChangeSlice{
|
||||
Before: genericToSlice(change.Before),
|
||||
After: genericToSlice(change.After),
|
||||
Unknown: genericToSlice(change.Unknown),
|
||||
BeforeSensitive: genericToSlice(change.BeforeSensitive),
|
||||
AfterSensitive: genericToSlice(change.AfterSensitive),
|
||||
ReplacePaths: change.ReplacePaths,
|
||||
RelevantAttributes: change.RelevantAttributes,
|
||||
}
|
||||
}
|
||||
|
||||
func (s ChangeSlice) getChild(beforeIx, afterIx int) Change {
|
||||
before, beforeExplicit := getFromGenericSlice(s.Before, beforeIx)
|
||||
after, afterExplicit := getFromGenericSlice(s.After, afterIx)
|
||||
unknown, _ := getFromGenericSlice(s.Unknown, afterIx)
|
||||
beforeSensitive, _ := getFromGenericSlice(s.BeforeSensitive, beforeIx)
|
||||
afterSensitive, _ := getFromGenericSlice(s.AfterSensitive, afterIx)
|
||||
|
||||
mostRelevantIx := beforeIx
|
||||
if beforeIx < 0 || beforeIx >= len(s.Before) {
|
||||
mostRelevantIx = afterIx
|
||||
}
|
||||
|
||||
return Change{
|
||||
BeforeExplicit: beforeExplicit,
|
||||
AfterExplicit: afterExplicit,
|
||||
Before: before,
|
||||
After: after,
|
||||
Unknown: unknown,
|
||||
BeforeSensitive: beforeSensitive,
|
||||
AfterSensitive: afterSensitive,
|
||||
ReplacePaths: s.ReplacePaths.GetChildWithIndex(mostRelevantIx),
|
||||
RelevantAttributes: s.RelevantAttributes.GetChildWithIndex(mostRelevantIx),
|
||||
}
|
||||
}
|
||||
|
||||
func getFromGenericSlice(generic []interface{}, ix int) (interface{}, bool) {
|
||||
if generic == nil {
|
||||
return nil, false
|
||||
}
|
||||
if ix < 0 || ix >= len(generic) {
|
||||
return nil, false
|
||||
}
|
||||
return generic[ix], true
|
||||
}
|
||||
|
||||
func genericToSlice(generic interface{}) []interface{} {
|
||||
if concrete, ok := generic.([]interface{}); ok {
|
||||
return concrete
|
||||
}
|
||||
return nil
|
||||
}
|
||||
12
internal/command/jsonformat/differ/differ.go
Normal file
12
internal/command/jsonformat/differ/differ.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package differ
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
)
|
||||
|
||||
// asDiff is a helper function to abstract away some simple and common
|
||||
// functionality when converting a renderer into a concrete diff.
|
||||
func asDiff(change structured.Change, renderer computed.DiffRenderer) computed.Diff {
|
||||
return computed.NewDiff(renderer, change.CalculateAction(), change.ReplacePaths.Matches())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,16 +6,17 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
func (change Change) computeAttributeDiffAsList(elementType cty.Type) computed.Diff {
|
||||
sliceValue := change.asSlice()
|
||||
func computeAttributeDiffAsList(change structured.Change, elementType cty.Type) computed.Diff {
|
||||
sliceValue := change.AsSlice()
|
||||
|
||||
processIndices := func(beforeIx, afterIx int) computed.Diff {
|
||||
value := sliceValue.getChild(beforeIx, afterIx)
|
||||
value := sliceValue.GetChild(beforeIx, afterIx)
|
||||
|
||||
// It's actually really difficult to render the diffs when some indices
|
||||
// within a slice are relevant and others aren't. To make this simpler
|
||||
@@ -37,7 +38,7 @@ func (change Change) computeAttributeDiffAsList(elementType cty.Type) computed.D
|
||||
// after.
|
||||
value.RelevantAttributes = attribute_path.AlwaysMatcher()
|
||||
|
||||
return value.ComputeDiffForType(elementType)
|
||||
return ComputeDiffForType(value, elementType)
|
||||
}
|
||||
|
||||
isObjType := func(_ interface{}) bool {
|
||||
@@ -48,11 +49,11 @@ func (change Change) computeAttributeDiffAsList(elementType cty.Type) computed.D
|
||||
return computed.NewDiff(renderers.List(elements), current, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeAttributeDiffAsNestedList(attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
func computeAttributeDiffAsNestedList(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
var elements []computed.Diff
|
||||
current := change.getDefaultActionForIteration()
|
||||
change.processNestedList(func(value Change) {
|
||||
element := value.computeDiffForNestedAttribute(&jsonprovider.NestedType{
|
||||
current := change.GetDefaultActionForIteration()
|
||||
processNestedList(change, func(value structured.Change) {
|
||||
element := computeDiffForNestedAttribute(value, &jsonprovider.NestedType{
|
||||
Attributes: attributes,
|
||||
NestingMode: "single",
|
||||
})
|
||||
@@ -62,21 +63,21 @@ func (change Change) computeAttributeDiffAsNestedList(attributes map[string]*jso
|
||||
return computed.NewDiff(renderers.NestedList(elements), current, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeBlockDiffsAsList(block *jsonprovider.Block) ([]computed.Diff, plans.Action) {
|
||||
func computeBlockDiffsAsList(change structured.Change, block *jsonprovider.Block) ([]computed.Diff, plans.Action) {
|
||||
var elements []computed.Diff
|
||||
current := change.getDefaultActionForIteration()
|
||||
change.processNestedList(func(value Change) {
|
||||
element := value.ComputeDiffForBlock(block)
|
||||
current := change.GetDefaultActionForIteration()
|
||||
processNestedList(change, func(value structured.Change) {
|
||||
element := ComputeDiffForBlock(value, block)
|
||||
elements = append(elements, element)
|
||||
current = collections.CompareActions(current, element.Action)
|
||||
})
|
||||
return elements, current
|
||||
}
|
||||
|
||||
func (change Change) processNestedList(process func(value Change)) {
|
||||
sliceValue := change.asSlice()
|
||||
func processNestedList(change structured.Change, process func(value structured.Change)) {
|
||||
sliceValue := change.AsSlice()
|
||||
for ix := 0; ix < len(sliceValue.Before) || ix < len(sliceValue.After); ix++ {
|
||||
value := sliceValue.getChild(ix, ix)
|
||||
value := sliceValue.GetChild(ix, ix)
|
||||
if !value.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
value = value.AsNoOp()
|
||||
|
||||
@@ -6,12 +6,13 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
func (change Change) computeAttributeDiffAsMap(elementType cty.Type) computed.Diff {
|
||||
mapValue := change.asMap()
|
||||
func computeAttributeDiffAsMap(change structured.Change, elementType cty.Type) computed.Diff {
|
||||
mapValue := change.AsMap()
|
||||
|
||||
// The jsonplan package will have stripped out unknowns from our after value
|
||||
// so we're going to add them back in here.
|
||||
@@ -45,25 +46,25 @@ func (change Change) computeAttributeDiffAsMap(elementType cty.Type) computed.Di
|
||||
}
|
||||
|
||||
elements, current := collections.TransformMap(mapValue.Before, after, func(key string) computed.Diff {
|
||||
value := mapValue.getChild(key)
|
||||
value := mapValue.GetChild(key)
|
||||
if !value.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
value = value.AsNoOp()
|
||||
}
|
||||
return value.ComputeDiffForType(elementType)
|
||||
return ComputeDiffForType(value, elementType)
|
||||
})
|
||||
return computed.NewDiff(renderers.Map(elements), current, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeAttributeDiffAsNestedMap(attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
mapValue := change.asMap()
|
||||
func computeAttributeDiffAsNestedMap(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
mapValue := change.AsMap()
|
||||
elements, current := collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff {
|
||||
value := mapValue.getChild(key)
|
||||
value := mapValue.GetChild(key)
|
||||
if !value.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
value = value.AsNoOp()
|
||||
}
|
||||
return value.computeDiffForNestedAttribute(&jsonprovider.NestedType{
|
||||
return computeDiffForNestedAttribute(value, &jsonprovider.NestedType{
|
||||
Attributes: attributes,
|
||||
NestingMode: "single",
|
||||
})
|
||||
@@ -71,14 +72,14 @@ func (change Change) computeAttributeDiffAsNestedMap(attributes map[string]*json
|
||||
return computed.NewDiff(renderers.NestedMap(elements), current, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeBlockDiffsAsMap(block *jsonprovider.Block) (map[string]computed.Diff, plans.Action) {
|
||||
mapValue := change.asMap()
|
||||
func computeBlockDiffsAsMap(change structured.Change, block *jsonprovider.Block) (map[string]computed.Diff, plans.Action) {
|
||||
mapValue := change.AsMap()
|
||||
return collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff {
|
||||
value := mapValue.getChild(key)
|
||||
value := mapValue.GetChild(key)
|
||||
if !value.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
value = value.AsNoOp()
|
||||
}
|
||||
return value.ComputeDiffForBlock(block)
|
||||
return ComputeDiffForBlock(value, block)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,20 +6,21 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
func (change Change) computeAttributeDiffAsObject(attributes map[string]cty.Type) computed.Diff {
|
||||
attributeDiffs, action := processObject(change, attributes, func(value Change, ctype cty.Type) computed.Diff {
|
||||
return value.ComputeDiffForType(ctype)
|
||||
func computeAttributeDiffAsObject(change structured.Change, attributes map[string]cty.Type) computed.Diff {
|
||||
attributeDiffs, action := processObject(change, attributes, func(value structured.Change, ctype cty.Type) computed.Diff {
|
||||
return ComputeDiffForType(value, ctype)
|
||||
})
|
||||
return computed.NewDiff(renderers.Object(attributeDiffs), action, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeAttributeDiffAsNestedObject(attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
attributeDiffs, action := processObject(change, attributes, func(value Change, attribute *jsonprovider.Attribute) computed.Diff {
|
||||
return value.ComputeDiffForAttribute(attribute)
|
||||
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 computed.NewDiff(renderers.NestedObject(attributeDiffs), action, change.ReplacePaths.Matches())
|
||||
}
|
||||
@@ -35,13 +36,13 @@ func (change Change) computeAttributeDiffAsNestedObject(attributes map[string]*j
|
||||
// Also, as it generic we cannot make this function a method on Change as you
|
||||
// can't create generic methods on structs. Instead, we make this a generic
|
||||
// function that receives the value as an argument.
|
||||
func processObject[T any](v Change, attributes map[string]T, computeDiff func(Change, T) computed.Diff) (map[string]computed.Diff, plans.Action) {
|
||||
func processObject[T any](v structured.Change, attributes map[string]T, computeDiff func(structured.Change, T) computed.Diff) (map[string]computed.Diff, plans.Action) {
|
||||
attributeDiffs := make(map[string]computed.Diff)
|
||||
mapValue := v.asMap()
|
||||
mapValue := v.AsMap()
|
||||
|
||||
currentAction := v.getDefaultActionForIteration()
|
||||
currentAction := v.GetDefaultActionForIteration()
|
||||
for key, attribute := range attributes {
|
||||
attributeValue := mapValue.getChild(key)
|
||||
attributeValue := mapValue.GetChild(key)
|
||||
|
||||
if !attributeValue.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
|
||||
@@ -5,14 +5,15 @@ import (
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
)
|
||||
|
||||
func (change Change) ComputeDiffForOutput() computed.Diff {
|
||||
if sensitive, ok := change.checkForSensitiveType(cty.DynamicPseudoType); ok {
|
||||
func ComputeDiffForOutput(change structured.Change) computed.Diff {
|
||||
if sensitive, ok := checkForSensitiveType(change, cty.DynamicPseudoType); ok {
|
||||
return sensitive
|
||||
}
|
||||
|
||||
if unknown, ok := change.checkForUnknownType(cty.DynamicPseudoType); ok {
|
||||
if unknown, ok := checkForUnknownType(change, cty.DynamicPseudoType); ok {
|
||||
return unknown
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
)
|
||||
|
||||
func (change Change) computeAttributeDiffAsPrimitive(ctype cty.Type) computed.Diff {
|
||||
return change.asDiff(renderers.Primitive(change.Before, change.After, ctype))
|
||||
func computeAttributeDiffAsPrimitive(change structured.Change, ctype cty.Type) computed.Diff {
|
||||
return asDiff(change, renderers.Primitive(change.Before, change.After, ctype))
|
||||
}
|
||||
|
||||
@@ -5,33 +5,34 @@ import (
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
type CreateSensitiveRenderer func(computed.Diff, bool, bool) computed.DiffRenderer
|
||||
|
||||
func (change Change) checkForSensitiveType(ctype cty.Type) (computed.Diff, bool) {
|
||||
return change.checkForSensitive(renderers.Sensitive, func(value Change) computed.Diff {
|
||||
return value.ComputeDiffForType(ctype)
|
||||
func checkForSensitiveType(change structured.Change, ctype cty.Type) (computed.Diff, bool) {
|
||||
return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff {
|
||||
return ComputeDiffForType(value, ctype)
|
||||
})
|
||||
}
|
||||
|
||||
func (change Change) checkForSensitiveNestedAttribute(attribute *jsonprovider.NestedType) (computed.Diff, bool) {
|
||||
return change.checkForSensitive(renderers.Sensitive, func(value Change) computed.Diff {
|
||||
return value.computeDiffForNestedAttribute(attribute)
|
||||
func checkForSensitiveNestedAttribute(change structured.Change, attribute *jsonprovider.NestedType) (computed.Diff, bool) {
|
||||
return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff {
|
||||
return computeDiffForNestedAttribute(value, attribute)
|
||||
})
|
||||
}
|
||||
|
||||
func (change Change) checkForSensitiveBlock(block *jsonprovider.Block) (computed.Diff, bool) {
|
||||
return change.checkForSensitive(renderers.SensitiveBlock, func(value Change) computed.Diff {
|
||||
return value.ComputeDiffForBlock(block)
|
||||
func checkForSensitiveBlock(change structured.Change, block *jsonprovider.Block) (computed.Diff, bool) {
|
||||
return checkForSensitive(change, renderers.SensitiveBlock, func(value structured.Change) computed.Diff {
|
||||
return ComputeDiffForBlock(value, block)
|
||||
})
|
||||
}
|
||||
|
||||
func (change Change) checkForSensitive(create CreateSensitiveRenderer, computedDiff func(value Change) computed.Diff) (computed.Diff, bool) {
|
||||
beforeSensitive := change.isBeforeSensitive()
|
||||
afterSensitive := change.isAfterSensitive()
|
||||
func checkForSensitive(change structured.Change, create CreateSensitiveRenderer, computedDiff func(value structured.Change) computed.Diff) (computed.Diff, bool) {
|
||||
beforeSensitive := change.IsBeforeSensitive()
|
||||
afterSensitive := change.IsAfterSensitive()
|
||||
|
||||
if !beforeSensitive && !afterSensitive {
|
||||
return computed.Diff{}, false
|
||||
@@ -44,7 +45,7 @@ func (change Change) checkForSensitive(create CreateSensitiveRenderer, computedD
|
||||
// The change can choose what to do with this information, in most cases
|
||||
// it will just be ignored in favour of printing `(sensitive value)`.
|
||||
|
||||
value := Change{
|
||||
value := structured.Change{
|
||||
BeforeExplicit: change.BeforeExplicit,
|
||||
AfterExplicit: change.AfterExplicit,
|
||||
Before: change.Before,
|
||||
@@ -75,17 +76,3 @@ func (change Change) checkForSensitive(create CreateSensitiveRenderer, computedD
|
||||
|
||||
return computed.NewDiff(create(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches()), true
|
||||
}
|
||||
|
||||
func (change Change) isBeforeSensitive() bool {
|
||||
if sensitive, ok := change.BeforeSensitive.(bool); ok {
|
||||
return sensitive
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (change Change) isAfterSensitive() bool {
|
||||
if sensitive, ok := change.AfterSensitive.(bool); ok {
|
||||
return sensitive
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -8,27 +8,28 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
)
|
||||
|
||||
func (change Change) computeAttributeDiffAsSet(elementType cty.Type) computed.Diff {
|
||||
func computeAttributeDiffAsSet(change structured.Change, elementType cty.Type) computed.Diff {
|
||||
var elements []computed.Diff
|
||||
current := change.getDefaultActionForIteration()
|
||||
change.processSet(func(value Change) {
|
||||
element := value.ComputeDiffForType(elementType)
|
||||
current := change.GetDefaultActionForIteration()
|
||||
processSet(change, func(value structured.Change) {
|
||||
element := ComputeDiffForType(value, elementType)
|
||||
elements = append(elements, element)
|
||||
current = collections.CompareActions(current, element.Action)
|
||||
})
|
||||
return computed.NewDiff(renderers.Set(elements), current, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeAttributeDiffAsNestedSet(attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
func computeAttributeDiffAsNestedSet(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff {
|
||||
var elements []computed.Diff
|
||||
current := change.getDefaultActionForIteration()
|
||||
change.processSet(func(value Change) {
|
||||
element := value.computeDiffForNestedAttribute(&jsonprovider.NestedType{
|
||||
current := change.GetDefaultActionForIteration()
|
||||
processSet(change, func(value structured.Change) {
|
||||
element := computeDiffForNestedAttribute(value, &jsonprovider.NestedType{
|
||||
Attributes: attributes,
|
||||
NestingMode: "single",
|
||||
})
|
||||
@@ -38,19 +39,19 @@ func (change Change) computeAttributeDiffAsNestedSet(attributes map[string]*json
|
||||
return computed.NewDiff(renderers.NestedSet(elements), current, change.ReplacePaths.Matches())
|
||||
}
|
||||
|
||||
func (change Change) computeBlockDiffsAsSet(block *jsonprovider.Block) ([]computed.Diff, plans.Action) {
|
||||
func computeBlockDiffsAsSet(change structured.Change, block *jsonprovider.Block) ([]computed.Diff, plans.Action) {
|
||||
var elements []computed.Diff
|
||||
current := change.getDefaultActionForIteration()
|
||||
change.processSet(func(value Change) {
|
||||
element := value.ComputeDiffForBlock(block)
|
||||
current := change.GetDefaultActionForIteration()
|
||||
processSet(change, func(value structured.Change) {
|
||||
element := ComputeDiffForBlock(value, block)
|
||||
elements = append(elements, element)
|
||||
current = collections.CompareActions(current, element.Action)
|
||||
})
|
||||
return elements, current
|
||||
}
|
||||
|
||||
func (change Change) processSet(process func(value Change)) {
|
||||
sliceValue := change.asSlice()
|
||||
func processSet(change structured.Change, process func(value structured.Change)) {
|
||||
sliceValue := change.AsSlice()
|
||||
|
||||
foundInBefore := make(map[int]int)
|
||||
foundInAfter := make(map[int]int)
|
||||
@@ -67,8 +68,8 @@ func (change Change) processSet(process func(value Change)) {
|
||||
continue
|
||||
}
|
||||
|
||||
child := sliceValue.getChild(ix, jx)
|
||||
if reflect.DeepEqual(child.Before, child.After) && child.isBeforeSensitive() == child.isAfterSensitive() && !child.isUnknown() {
|
||||
child := sliceValue.GetChild(ix, jx)
|
||||
if reflect.DeepEqual(child.Before, child.After) && child.IsBeforeSensitive() == child.IsAfterSensitive() && !child.IsUnknown() {
|
||||
matched = true
|
||||
foundInBefore[ix] = jx
|
||||
foundInAfter[jx] = ix
|
||||
@@ -80,7 +81,7 @@ func (change Change) processSet(process func(value Change)) {
|
||||
}
|
||||
}
|
||||
|
||||
clearRelevantStatus := func(change Change) Change {
|
||||
clearRelevantStatus := func(change structured.Change) structured.Change {
|
||||
// It's actually really difficult to render the diffs when some indices
|
||||
// within a slice are relevant and others aren't. To make this simpler
|
||||
// we just treat all children of a relevant list or set as also
|
||||
@@ -112,11 +113,11 @@ func (change Change) processSet(process func(value Change)) {
|
||||
|
||||
for ix := 0; ix < len(sliceValue.Before); ix++ {
|
||||
if jx := foundInBefore[ix]; jx >= 0 {
|
||||
child := clearRelevantStatus(sliceValue.getChild(ix, jx))
|
||||
child := clearRelevantStatus(sliceValue.GetChild(ix, jx))
|
||||
process(child)
|
||||
continue
|
||||
}
|
||||
child := clearRelevantStatus(sliceValue.getChild(ix, len(sliceValue.After)))
|
||||
child := clearRelevantStatus(sliceValue.GetChild(ix, len(sliceValue.After)))
|
||||
process(child)
|
||||
}
|
||||
|
||||
@@ -125,7 +126,7 @@ func (change Change) processSet(process func(value Change)) {
|
||||
// Then this value was handled in the previous for loop.
|
||||
continue
|
||||
}
|
||||
child := clearRelevantStatus(sliceValue.getChild(len(sliceValue.Before), jx))
|
||||
child := clearRelevantStatus(sliceValue.GetChild(len(sliceValue.Before), jx))
|
||||
process(child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,20 @@ import (
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/collections"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
)
|
||||
|
||||
func (change Change) computeAttributeDiffAsTuple(elementTypes []cty.Type) computed.Diff {
|
||||
func computeAttributeDiffAsTuple(change structured.Change, elementTypes []cty.Type) computed.Diff {
|
||||
var elements []computed.Diff
|
||||
current := change.getDefaultActionForIteration()
|
||||
sliceValue := change.asSlice()
|
||||
current := change.GetDefaultActionForIteration()
|
||||
sliceValue := change.AsSlice()
|
||||
for ix, elementType := range elementTypes {
|
||||
childValue := sliceValue.getChild(ix, ix)
|
||||
childValue := sliceValue.GetChild(ix, ix)
|
||||
if !childValue.RelevantAttributes.MatchesPartial() {
|
||||
// Mark non-relevant attributes as unchanged.
|
||||
childValue = childValue.AsNoOp()
|
||||
}
|
||||
element := childValue.ComputeDiffForType(elementType)
|
||||
element := ComputeDiffForType(childValue, elementType)
|
||||
elements = append(elements, element)
|
||||
current = collections.CompareActions(current, element.Action)
|
||||
}
|
||||
|
||||
@@ -4,18 +4,17 @@ import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/structured"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonprovider"
|
||||
)
|
||||
|
||||
func (change Change) checkForUnknownType(ctype cty.Type) (computed.Diff, bool) {
|
||||
return change.checkForUnknown(false, func(value Change) computed.Diff {
|
||||
return value.ComputeDiffForType(ctype)
|
||||
func checkForUnknownType(change structured.Change, ctype cty.Type) (computed.Diff, bool) {
|
||||
return checkForUnknown(change, false, func(value structured.Change) computed.Diff {
|
||||
return ComputeDiffForType(value, ctype)
|
||||
})
|
||||
}
|
||||
func (change Change) checkForUnknownNestedAttribute(attribute *jsonprovider.NestedType) (computed.Diff, bool) {
|
||||
func checkForUnknownNestedAttribute(change structured.Change, attribute *jsonprovider.NestedType) (computed.Diff, bool) {
|
||||
|
||||
// We want our child attributes to show up as computed instead of deleted.
|
||||
// Let's populate that here.
|
||||
@@ -24,12 +23,12 @@ func (change Change) checkForUnknownNestedAttribute(attribute *jsonprovider.Nest
|
||||
childUnknown[key] = true
|
||||
}
|
||||
|
||||
return change.checkForUnknown(childUnknown, func(value Change) computed.Diff {
|
||||
return value.computeDiffForNestedAttribute(attribute)
|
||||
return checkForUnknown(change, childUnknown, func(value structured.Change) computed.Diff {
|
||||
return computeDiffForNestedAttribute(value, attribute)
|
||||
})
|
||||
}
|
||||
|
||||
func (change Change) checkForUnknownBlock(block *jsonprovider.Block) (computed.Diff, bool) {
|
||||
func checkForUnknownBlock(change structured.Change, block *jsonprovider.Block) (computed.Diff, bool) {
|
||||
|
||||
// We want our child attributes to show up as computed instead of deleted.
|
||||
// Let's populate that here.
|
||||
@@ -38,13 +37,13 @@ func (change Change) checkForUnknownBlock(block *jsonprovider.Block) (computed.D
|
||||
childUnknown[key] = true
|
||||
}
|
||||
|
||||
return change.checkForUnknown(childUnknown, func(value Change) computed.Diff {
|
||||
return value.ComputeDiffForBlock(block)
|
||||
return checkForUnknown(change, childUnknown, func(value structured.Change) computed.Diff {
|
||||
return ComputeDiffForBlock(value, block)
|
||||
})
|
||||
}
|
||||
|
||||
func (change Change) checkForUnknown(childUnknown interface{}, computeDiff func(value Change) computed.Diff) (computed.Diff, bool) {
|
||||
unknown := change.isUnknown()
|
||||
func checkForUnknown(change structured.Change, childUnknown interface{}, computeDiff func(value structured.Change) computed.Diff) (computed.Diff, bool) {
|
||||
unknown := change.IsUnknown()
|
||||
|
||||
if !unknown {
|
||||
return computed.Diff{}, false
|
||||
@@ -56,26 +55,19 @@ func (change Change) checkForUnknown(childUnknown interface{}, computeDiff func(
|
||||
change.AfterExplicit = true
|
||||
|
||||
if change.Before == nil {
|
||||
return change.asDiff(renderers.Unknown(computed.Diff{})), true
|
||||
return asDiff(change, renderers.Unknown(computed.Diff{})), 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 := Change{
|
||||
beforeValue := structured.Change{
|
||||
Before: change.Before,
|
||||
BeforeSensitive: change.BeforeSensitive,
|
||||
Unknown: childUnknown,
|
||||
ReplacePaths: change.ReplacePaths,
|
||||
RelevantAttributes: change.RelevantAttributes,
|
||||
}
|
||||
return change.asDiff(renderers.Unknown(computeDiff(beforeValue))), true
|
||||
}
|
||||
|
||||
func (change Change) isUnknown() bool {
|
||||
if unknown, ok := change.Unknown.(bool); ok {
|
||||
return unknown
|
||||
}
|
||||
return false
|
||||
return asDiff(change, renderers.Unknown(computeDiff(beforeValue))), true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user