Add diff coverage for maps

Considers wholly marked maps as well
as map keys
This commit is contained in:
Pam Selle 2020-09-24 16:29:30 -04:00
parent 9c2556b5ad
commit 0520f143a2
2 changed files with 184 additions and 21 deletions

View File

@ -777,12 +777,9 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa
// However, these specialized implementations can apply only if both // However, these specialized implementations can apply only if both
// values are known and non-null. // values are known and non-null.
if old.IsKnown() && new.IsKnown() && !old.IsNull() && !new.IsNull() && typesEqual { if old.IsKnown() && new.IsKnown() && !old.IsNull() && !new.IsNull() && typesEqual {
// Create unmarked values for comparisons
unmarkedOld, oldMarks := old.UnmarkDeep()
unmarkedNew, newMarks := new.UnmarkDeep()
switch { switch {
case ty == cty.Bool || ty == cty.Number: case ty == cty.Bool || ty == cty.Number:
if len(oldMarks) > 0 || len(newMarks) > 0 { if old.IsMarked() || new.IsMarked() {
p.buf.WriteString("(sensitive)") p.buf.WriteString("(sensitive)")
return return
} }
@ -793,7 +790,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa
// For single-line strings that don't parse as JSON we just fall // For single-line strings that don't parse as JSON we just fall
// out of this switch block and do the default old -> new rendering. // out of this switch block and do the default old -> new rendering.
if len(oldMarks) > 0 || len(newMarks) > 0 { if old.IsMarked() || new.IsMarked() {
p.buf.WriteString("(sensitive)") p.buf.WriteString("(sensitive)")
return return
} }
@ -1079,6 +1076,18 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa
} }
p.buf.WriteString("\n") p.buf.WriteString("\n")
// If the whole map is marked, we need to show our
// sensitive block warning rather than attempt to iterate
// through a marked map
if old.IsMarked() || new.IsMarked() {
p.buf.WriteString(strings.Repeat(" ", indent+2))
p.buf.WriteString("# This map is (or was) sensitive and will not be displayed")
p.buf.WriteString("\n")
p.buf.WriteString(strings.Repeat(" ", indent))
p.buf.WriteString("}")
return
}
var allKeys []string var allKeys []string
keyLen := 0 keyLen := 0
for it := old.ElementIterator(); it.Next(); { for it := old.ElementIterator(); it.Next(); {
@ -1114,10 +1123,18 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa
action = plans.Create action = plans.Create
} else if new.HasIndex(kV).False() { } else if new.HasIndex(kV).False() {
action = plans.Delete action = plans.Delete
} else if eqV := unmarkedOld.Index(kV).Equals(unmarkedNew.Index(kV)); eqV.IsKnown() && eqV.True() { }
action = plans.NoOp
} else { // Use unmarked values for equality testing
action = plans.Update if old.HasIndex(kV).True() && new.HasIndex(kV).True() {
unmarkedOld, _ := old.Index(kV).Unmark()
unmarkedNew, _ := new.Index(kV).Unmark()
eqV := unmarkedOld.Equals(unmarkedNew)
if eqV.IsKnown() && eqV.True() {
action = plans.NoOp
} else {
action = plans.Update
}
} }
if action == plans.NoOp && p.concise { if action == plans.NoOp && p.concise {

View File

@ -3631,6 +3631,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
After: cty.ObjectVal(map[string]cty.Value{ After: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("i-02ae66f368e8518a9"), "id": cty.StringVal("i-02ae66f368e8518a9"),
"ami": cty.StringVal("ami-123"), "ami": cty.StringVal("ami-123"),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(2000),
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("pizza"),
"dinner": cty.StringVal("pizza"),
}),
"list_field": cty.ListVal([]cty.Value{ "list_field": cty.ListVal([]cty.Value{
cty.StringVal("hello"), cty.StringVal("hello"),
cty.StringVal("friends"), cty.StringVal("friends"),
@ -3646,6 +3654,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(1)}}, Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(1)}},
Marks: cty.NewValueMarks("sensitive"), Marks: cty.NewValueMarks("sensitive"),
}, },
{
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
Marks: cty.NewValueMarks("sensitive"),
},
{
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
Marks: cty.NewValueMarks("sensitive"),
},
}, },
RequiredReplace: cty.NewPathSet(), RequiredReplace: cty.NewPathSet(),
Tainted: false, Tainted: false,
@ -3653,6 +3669,8 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Attributes: map[string]*configschema.Attribute{ Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Optional: true, Computed: true}, "id": {Type: cty.String, Optional: true, Computed: true},
"ami": {Type: cty.String, Optional: true}, "ami": {Type: cty.String, Optional: true},
"map_whole": {Type: cty.Map(cty.String), Optional: true},
"map_key": {Type: cty.Map(cty.Number), Optional: true},
"list_field": {Type: cty.List(cty.String), Optional: true}, "list_field": {Type: cty.List(cty.String), Optional: true},
}, },
}, },
@ -3665,6 +3683,11 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
+ (sensitive), + (sensitive),
+ "!", + "!",
] ]
+ map_key = {
+ "breakfast" = 800
+ "dinner" = (sensitive)
}
+ map_whole = (sensitive)
} }
`, `,
}, },
@ -3681,6 +3704,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
cty.StringVal("friends"), cty.StringVal("friends"),
cty.StringVal("!"), cty.StringVal("!"),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(2000), // sensitive key
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("pizza"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
After: cty.ObjectVal(map[string]cty.Value{ After: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("i-02ae66f368e8518a9"), "id": cty.StringVal("i-02ae66f368e8518a9"),
@ -3692,6 +3723,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
cty.StringVal("friends"), cty.StringVal("friends"),
cty.StringVal("."), cty.StringVal("."),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(1900),
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("cereal"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
BeforeValMarks: []cty.PathValueMarks{ BeforeValMarks: []cty.PathValueMarks{
{ {
@ -3710,6 +3749,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(2)}}, Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(2)}},
Marks: cty.NewValueMarks("sensitive"), Marks: cty.NewValueMarks("sensitive"),
}, },
{
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
Marks: cty.NewValueMarks("sensitive"),
},
{
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
Marks: cty.NewValueMarks("sensitive"),
},
}, },
RequiredReplace: cty.NewPathSet(), RequiredReplace: cty.NewPathSet(),
Tainted: false, Tainted: false,
@ -3720,6 +3767,8 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
"list_field": {Type: cty.List(cty.String), Optional: true}, "list_field": {Type: cty.List(cty.String), Optional: true},
"special": {Type: cty.Bool, Optional: true}, "special": {Type: cty.Bool, Optional: true},
"some_number": {Type: cty.Number, Optional: true}, "some_number": {Type: cty.Number, Optional: true},
"map_key": {Type: cty.Map(cty.Number), Optional: true},
"map_whole": {Type: cty.Map(cty.String), Optional: true},
}, },
}, },
ExpectedOutput: ` # test_instance.example will be updated in-place ExpectedOutput: ` # test_instance.example will be updated in-place
@ -3734,6 +3783,17 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
- (sensitive), - (sensitive),
+ ".", + ".",
] ]
~ map_key = {
# Warning: this attribute value will no longer be marked as sensitive
# after applying this change
~ "dinner" = (sensitive)
# (1 unchanged element hidden)
}
# Warning: this attribute value will no longer be marked as sensitive
# after applying this change
~ map_whole = {
# This map is (or was) sensitive and will not be displayed
}
# Warning: this attribute value will no longer be marked as sensitive # Warning: this attribute value will no longer be marked as sensitive
# after applying this change # after applying this change
~ some_number = (sensitive) ~ some_number = (sensitive)
@ -3748,25 +3808,33 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Before: cty.ObjectVal(map[string]cty.Value{ Before: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("i-02ae66f368e8518a9"), "id": cty.StringVal("i-02ae66f368e8518a9"),
"tags": cty.MapVal(map[string]cty.Value{
"name": cty.StringVal("anna a"),
"address": cty.StringVal("123 Main St"),
}),
"list_field": cty.ListVal([]cty.Value{ "list_field": cty.ListVal([]cty.Value{
cty.StringVal("hello"), cty.StringVal("hello"),
cty.StringVal("friends"), cty.StringVal("friends"),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(2000), // sensitive key
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("pizza"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
After: cty.ObjectVal(map[string]cty.Value{ After: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("i-02ae66f368e8518a9"), "id": cty.StringVal("i-02ae66f368e8518a9"),
"tags": cty.MapVal(map[string]cty.Value{
"name": cty.StringVal("anna b"),
"address": cty.StringVal("123 Main Ave"),
}),
"list_field": cty.ListVal([]cty.Value{ "list_field": cty.ListVal([]cty.Value{
cty.StringVal("goodbye"), cty.StringVal("goodbye"),
cty.StringVal("friends"), cty.StringVal("friends"),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(700),
"dinner": cty.NumberIntVal(2100), // sensitive key
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("cereal"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
AfterValMarks: []cty.PathValueMarks{ AfterValMarks: []cty.PathValueMarks{
{ {
@ -3777,14 +3845,23 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}}, Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}},
Marks: cty.NewValueMarks("sensitive"), Marks: cty.NewValueMarks("sensitive"),
}, },
{
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
Marks: cty.NewValueMarks("sensitive"),
},
{
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
Marks: cty.NewValueMarks("sensitive"),
},
}, },
RequiredReplace: cty.NewPathSet(), RequiredReplace: cty.NewPathSet(),
Tainted: false, Tainted: false,
Schema: &configschema.Block{ Schema: &configschema.Block{
Attributes: map[string]*configschema.Attribute{ Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Optional: true, Computed: true}, "id": {Type: cty.String, Optional: true, Computed: true},
"tags": {Type: cty.Map(cty.String), Optional: true},
"list_field": {Type: cty.List(cty.String), Optional: true}, "list_field": {Type: cty.List(cty.String), Optional: true},
"map_key": {Type: cty.Map(cty.Number), Optional: true},
"map_whole": {Type: cty.Map(cty.String), Optional: true},
}, },
}, },
ExpectedOutput: ` # test_instance.example will be updated in-place ExpectedOutput: ` # test_instance.example will be updated in-place
@ -3795,11 +3872,16 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
+ (sensitive), + (sensitive),
"friends", "friends",
] ]
~ tags = { ~ map_key = {
~ "breakfast" = 800 -> 700
# Warning: this attribute value will be marked as sensitive and will # Warning: this attribute value will be marked as sensitive and will
# not display in UI output after applying this change # not display in UI output after applying this change
~ "address" = (sensitive) ~ "dinner" = (sensitive)
~ "name" = "anna a" -> "anna b" }
# Warning: this attribute value will be marked as sensitive and will
# not display in UI output after applying this change
~ map_whole = {
# This map is (or was) sensitive and will not be displayed
} }
} }
`, `,
@ -3814,6 +3896,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
cty.StringVal("hello"), cty.StringVal("hello"),
cty.StringVal("friends"), cty.StringVal("friends"),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(2000), // sensitive key
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("pizza"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
After: cty.ObjectVal(map[string]cty.Value{ After: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("i-02ae66f368e8518a9"), "id": cty.StringVal("i-02ae66f368e8518a9"),
@ -3822,6 +3912,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
cty.StringVal("goodbye"), cty.StringVal("goodbye"),
cty.StringVal("friends"), cty.StringVal("friends"),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(1800), // sensitive key
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("cereal"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
BeforeValMarks: []cty.PathValueMarks{ BeforeValMarks: []cty.PathValueMarks{
{ {
@ -3832,6 +3930,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}}, Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}},
Marks: cty.NewValueMarks("sensitive"), Marks: cty.NewValueMarks("sensitive"),
}, },
{
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
Marks: cty.NewValueMarks("sensitive"),
},
{
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
Marks: cty.NewValueMarks("sensitive"),
},
}, },
AfterValMarks: []cty.PathValueMarks{ AfterValMarks: []cty.PathValueMarks{
{ {
@ -3842,6 +3948,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}}, Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(0)}},
Marks: cty.NewValueMarks("sensitive"), Marks: cty.NewValueMarks("sensitive"),
}, },
{
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
Marks: cty.NewValueMarks("sensitive"),
},
{
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
Marks: cty.NewValueMarks("sensitive"),
},
}, },
RequiredReplace: cty.NewPathSet(), RequiredReplace: cty.NewPathSet(),
Tainted: false, Tainted: false,
@ -3850,6 +3964,8 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
"id": {Type: cty.String, Optional: true, Computed: true}, "id": {Type: cty.String, Optional: true, Computed: true},
"ami": {Type: cty.String, Optional: true}, "ami": {Type: cty.String, Optional: true},
"list_field": {Type: cty.List(cty.String), Optional: true}, "list_field": {Type: cty.List(cty.String), Optional: true},
"map_key": {Type: cty.Map(cty.Number), Optional: true},
"map_whole": {Type: cty.Map(cty.String), Optional: true},
}, },
}, },
ExpectedOutput: ` # test_instance.example will be updated in-place ExpectedOutput: ` # test_instance.example will be updated in-place
@ -3861,6 +3977,13 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
+ (sensitive), + (sensitive),
"friends", "friends",
] ]
~ map_key = {
~ "dinner" = (sensitive)
# (1 unchanged element hidden)
}
~ map_whole = {
# This map is (or was) sensitive and will not be displayed
}
} }
`, `,
}, },
@ -3874,6 +3997,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
cty.StringVal("hello"), cty.StringVal("hello"),
cty.StringVal("friends"), cty.StringVal("friends"),
}), }),
"map_key": cty.MapVal(map[string]cty.Value{
"breakfast": cty.NumberIntVal(800),
"dinner": cty.NumberIntVal(2000), // sensitive key
}),
"map_whole": cty.MapVal(map[string]cty.Value{
"breakfast": cty.StringVal("pizza"),
"dinner": cty.StringVal("pizza"),
}),
}), }),
After: cty.NullVal(cty.EmptyObject), After: cty.NullVal(cty.EmptyObject),
BeforeValMarks: []cty.PathValueMarks{ BeforeValMarks: []cty.PathValueMarks{
@ -3885,6 +4016,14 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(1)}}, Path: cty.Path{cty.GetAttrStep{Name: "list_field"}, cty.IndexStep{Key: cty.NumberIntVal(1)}},
Marks: cty.NewValueMarks("sensitive"), Marks: cty.NewValueMarks("sensitive"),
}, },
{
Path: cty.Path{cty.GetAttrStep{Name: "map_key"}, cty.IndexStep{Key: cty.StringVal("dinner")}},
Marks: cty.NewValueMarks("sensitive"),
},
{
Path: cty.Path{cty.GetAttrStep{Name: "map_whole"}},
Marks: cty.NewValueMarks("sensitive"),
},
}, },
RequiredReplace: cty.NewPathSet(), RequiredReplace: cty.NewPathSet(),
Tainted: false, Tainted: false,
@ -3893,6 +4032,8 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
"id": {Type: cty.String, Optional: true, Computed: true}, "id": {Type: cty.String, Optional: true, Computed: true},
"ami": {Type: cty.String, Optional: true}, "ami": {Type: cty.String, Optional: true},
"list_field": {Type: cty.List(cty.String), Optional: true}, "list_field": {Type: cty.List(cty.String), Optional: true},
"map_key": {Type: cty.Map(cty.Number), Optional: true},
"map_whole": {Type: cty.Map(cty.String), Optional: true},
}, },
}, },
ExpectedOutput: ` # test_instance.example will be destroyed ExpectedOutput: ` # test_instance.example will be destroyed
@ -3903,6 +4044,11 @@ func TestResourceChange_sensitiveVariable(t *testing.T) {
- "hello", - "hello",
- (sensitive), - (sensitive),
] -> null ] -> null
- map_key = {
- "breakfast" = 800
- "dinner" = (sensitive)
} -> null
- map_whole = (sensitive) -> null
} }
`, `,
}, },