2014-06-05 08:57:06 -05:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2017-02-06 19:40:44 -06:00
|
|
|
"fmt"
|
2014-10-10 15:46:44 -05:00
|
|
|
"reflect"
|
2018-11-16 14:26:16 -06:00
|
|
|
"strconv"
|
2014-06-10 13:22:32 -05:00
|
|
|
"strings"
|
|
|
|
"testing"
|
2018-05-04 21:24:06 -05:00
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
2014-06-05 08:57:06 -05:00
|
|
|
)
|
|
|
|
|
2014-09-24 16:37:24 -05:00
|
|
|
func TestDiffEmpty(t *testing.T) {
|
2016-09-12 20:37:19 -05:00
|
|
|
var diff *Diff
|
|
|
|
if !diff.Empty() {
|
|
|
|
t.Fatal("should be empty")
|
|
|
|
}
|
|
|
|
|
|
|
|
diff = new(Diff)
|
2014-09-24 16:37:24 -05:00
|
|
|
if !diff.Empty() {
|
|
|
|
t.Fatal("should be empty")
|
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
mod := diff.AddModule(addrs.RootModuleInstance)
|
2014-09-24 16:37:24 -05:00
|
|
|
mod.Resources["nodeA"] = &InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff.Empty() {
|
|
|
|
t.Fatal("should not be empty")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-12 17:20:09 -05:00
|
|
|
func TestDiffEmpty_taintedIsNotEmpty(t *testing.T) {
|
|
|
|
diff := new(Diff)
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
mod := diff.AddModule(addrs.RootModuleInstance)
|
2016-08-12 17:20:09 -05:00
|
|
|
mod.Resources["nodeA"] = &InstanceDiff{
|
|
|
|
DestroyTainted: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff.Empty() {
|
|
|
|
t.Fatal("should not be empty, since DestroyTainted was set")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-08 21:18:38 -05:00
|
|
|
func TestDiffEqual(t *testing.T) {
|
|
|
|
cases := map[string]struct {
|
|
|
|
D1, D2 *Diff
|
|
|
|
Equal bool
|
|
|
|
}{
|
|
|
|
"nil": {
|
|
|
|
nil,
|
|
|
|
new(Diff),
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
|
|
|
|
"empty": {
|
|
|
|
new(Diff),
|
|
|
|
new(Diff),
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"different module order": {
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}},
|
|
|
|
&ModuleDiff{Path: []string{"root", "bar"}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "bar"}},
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
},
|
2016-10-20 23:39:55 -05:00
|
|
|
|
|
|
|
"different module diff destroys": {
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}, Destroy: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}, Destroy: false},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
},
|
2016-10-08 21:18:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range cases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
actual := tc.D1.Equal(tc.D2)
|
|
|
|
if actual != tc.Equal {
|
|
|
|
t.Fatalf("expected: %v\n\n%#v\n\n%#v", tc.Equal, tc.D1, tc.D2)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 21:47:58 -05:00
|
|
|
func TestDiffPrune(t *testing.T) {
|
|
|
|
cases := map[string]struct {
|
|
|
|
D1, D2 *Diff
|
|
|
|
}{
|
|
|
|
"nil": {
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
|
|
|
|
"empty": {
|
|
|
|
new(Diff),
|
|
|
|
new(Diff),
|
|
|
|
},
|
|
|
|
|
|
|
|
"empty module": {
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&Diff{},
|
|
|
|
},
|
|
|
|
|
|
|
|
"destroy module": {
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}, Destroy: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{Path: []string{"root", "foo"}, Destroy: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range cases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
tc.D1.Prune()
|
|
|
|
if !tc.D1.Equal(tc.D2) {
|
|
|
|
t.Fatalf("bad:\n\n%#v\n\n%#v", tc.D1, tc.D2)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
func TestModuleDiff_ChangeType(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Diff *ModuleDiff
|
|
|
|
Result DiffChangeType
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
&ModuleDiff{},
|
|
|
|
DiffNone,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": &InstanceDiff{Destroy: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffDestroy,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": &InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffUpdate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": &InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffCreate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": &InstanceDiff{
|
|
|
|
Destroy: true,
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffUpdate,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range cases {
|
|
|
|
actual := tc.Diff.ChangeType()
|
|
|
|
if actual != tc.Result {
|
2014-11-02 06:56:44 -06:00
|
|
|
t.Fatalf("%d: %#v", i, actual)
|
2014-09-24 19:51:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-03 13:13:49 -05:00
|
|
|
func TestDiff_DeepCopy(t *testing.T) {
|
|
|
|
cases := map[string]*Diff{
|
|
|
|
"empty": &Diff{},
|
|
|
|
|
|
|
|
"basic diff": &Diff{
|
|
|
|
Modules: []*ModuleDiff{
|
|
|
|
&ModuleDiff{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"aws_instance.foo": &InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"num": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range cases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
dup := tc.DeepCopy()
|
|
|
|
if !reflect.DeepEqual(dup, tc) {
|
|
|
|
t.Fatalf("\n%#v\n\n%#v", dup, tc)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
func TestModuleDiff_Empty(t *testing.T) {
|
|
|
|
diff := new(ModuleDiff)
|
2014-06-19 16:57:36 -05:00
|
|
|
if !diff.Empty() {
|
|
|
|
t.Fatal("should be empty")
|
|
|
|
}
|
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
diff.Resources = map[string]*InstanceDiff{
|
|
|
|
"nodeA": &InstanceDiff{},
|
2014-06-19 16:57:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if !diff.Empty() {
|
|
|
|
t.Fatal("should be empty")
|
|
|
|
}
|
|
|
|
|
|
|
|
diff.Resources["nodeA"].Attributes = map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff.Empty() {
|
|
|
|
t.Fatal("should not be empty")
|
|
|
|
}
|
2014-06-26 00:19:27 -05:00
|
|
|
|
|
|
|
diff.Resources["nodeA"].Attributes = nil
|
|
|
|
diff.Resources["nodeA"].Destroy = true
|
|
|
|
|
|
|
|
if diff.Empty() {
|
|
|
|
t.Fatal("should not be empty")
|
|
|
|
}
|
2014-06-19 16:57:36 -05:00
|
|
|
}
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
func TestModuleDiff_String(t *testing.T) {
|
|
|
|
diff := &ModuleDiff{
|
2014-09-17 18:33:24 -05:00
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"nodeA": &InstanceDiff{
|
2014-06-10 13:22:32 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
"bar": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
2014-06-10 13:30:54 -05:00
|
|
|
"longfoo": &ResourceAttrDiff{
|
2014-06-10 13:33:59 -05:00
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
|
|
|
RequiresNew: true,
|
2014-06-10 13:30:54 -05:00
|
|
|
},
|
2016-05-27 19:00:59 -05:00
|
|
|
"secretfoo": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
|
|
|
Sensitive: true,
|
|
|
|
},
|
2014-06-10 13:22:32 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-06-05 08:57:06 -05:00
|
|
|
}
|
2014-06-05 22:17:03 -05:00
|
|
|
|
2014-06-10 13:22:32 -05:00
|
|
|
actual := strings.TrimSpace(diff.String())
|
2014-09-23 13:43:21 -05:00
|
|
|
expected := strings.TrimSpace(moduleDiffStrBasic)
|
2014-06-10 13:22:32 -05:00
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n%s", actual)
|
2014-06-05 08:57:06 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-10 13:22:32 -05:00
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
func TestInstanceDiff_ChangeType(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Diff *InstanceDiff
|
|
|
|
Result DiffChangeType
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
&InstanceDiff{},
|
|
|
|
DiffNone,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&InstanceDiff{Destroy: true},
|
|
|
|
DiffDestroy,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffUpdate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffCreate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Destroy: true,
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffDestroyCreate,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
DestroyTainted: true,
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
DiffDestroyCreate,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range cases {
|
|
|
|
actual := tc.Diff.ChangeType()
|
|
|
|
if actual != tc.Result {
|
2014-11-02 06:56:44 -06:00
|
|
|
t.Fatalf("%d: %#v", i, actual)
|
2014-09-24 19:51:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInstanceDiff_Empty(t *testing.T) {
|
2014-09-17 18:33:24 -05:00
|
|
|
var rd *InstanceDiff
|
2014-06-30 22:56:25 -05:00
|
|
|
|
|
|
|
if !rd.Empty() {
|
|
|
|
t.Fatal("should be empty")
|
|
|
|
}
|
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
rd = new(InstanceDiff)
|
2014-06-30 22:56:25 -05:00
|
|
|
|
|
|
|
if !rd.Empty() {
|
|
|
|
t.Fatal("should be empty")
|
|
|
|
}
|
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
rd = &InstanceDiff{Destroy: true}
|
2014-06-30 22:56:25 -05:00
|
|
|
|
|
|
|
if rd.Empty() {
|
|
|
|
t.Fatal("should not be empty")
|
|
|
|
}
|
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
rd = &InstanceDiff{
|
2014-06-30 22:56:25 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if rd.Empty() {
|
|
|
|
t.Fatal("should not be empty")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-10 15:46:44 -05:00
|
|
|
func TestModuleDiff_Instances(t *testing.T) {
|
|
|
|
yesDiff := &InstanceDiff{Destroy: true}
|
|
|
|
noDiff := &InstanceDiff{Destroy: true, DestroyTainted: true}
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
Diff *ModuleDiff
|
|
|
|
Id string
|
|
|
|
Result []*InstanceDiff
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": yesDiff,
|
|
|
|
"bar": noDiff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"foo",
|
|
|
|
[]*InstanceDiff{
|
|
|
|
yesDiff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": yesDiff,
|
|
|
|
"foo.0": yesDiff,
|
|
|
|
"bar": noDiff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"foo",
|
|
|
|
[]*InstanceDiff{
|
|
|
|
yesDiff,
|
|
|
|
yesDiff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
&ModuleDiff{
|
|
|
|
Resources: map[string]*InstanceDiff{
|
|
|
|
"foo": yesDiff,
|
|
|
|
"foo.0": yesDiff,
|
|
|
|
"foo_bar": noDiff,
|
|
|
|
"bar": noDiff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"foo",
|
|
|
|
[]*InstanceDiff{
|
|
|
|
yesDiff,
|
|
|
|
yesDiff,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range cases {
|
|
|
|
actual := tc.Diff.Instances(tc.Id)
|
|
|
|
if !reflect.DeepEqual(actual, tc.Result) {
|
|
|
|
t.Fatalf("%d: %#v", i, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
func TestInstanceDiff_RequiresNew(t *testing.T) {
|
2014-09-17 18:33:24 -05:00
|
|
|
rd := &InstanceDiff{
|
2014-06-17 20:10:38 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if rd.RequiresNew() {
|
|
|
|
t.Fatal("should not require new")
|
|
|
|
}
|
|
|
|
|
|
|
|
rd.Attributes["foo"].RequiresNew = true
|
|
|
|
|
|
|
|
if !rd.RequiresNew() {
|
|
|
|
t.Fatal("should require new")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
func TestInstanceDiff_RequiresNew_nil(t *testing.T) {
|
2014-09-17 18:33:24 -05:00
|
|
|
var rd *InstanceDiff
|
2014-06-17 20:10:38 -05:00
|
|
|
|
|
|
|
if rd.RequiresNew() {
|
|
|
|
t.Fatal("should not require new")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
func TestInstanceDiffSame(t *testing.T) {
|
2014-07-22 21:39:48 -05:00
|
|
|
cases := []struct {
|
2014-09-17 18:33:24 -05:00
|
|
|
One, Two *InstanceDiff
|
2014-07-22 21:39:48 -05:00
|
|
|
Same bool
|
2015-04-13 09:28:47 -05:00
|
|
|
Reason string
|
2014-07-22 21:39:48 -05:00
|
|
|
}{
|
|
|
|
{
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{},
|
|
|
|
&InstanceDiff{},
|
2014-07-22 21:39:48 -05:00
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{Destroy: false},
|
|
|
|
&InstanceDiff{Destroy: true},
|
2014-07-22 21:39:48 -05:00
|
|
|
false,
|
2015-04-13 09:28:47 -05:00
|
|
|
"diff: Destroy; old: false, new: true",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{Destroy: true},
|
|
|
|
&InstanceDiff{Destroy: true},
|
2014-07-22 21:39:48 -05:00
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{
|
2014-07-22 21:39:48 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
},
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{
|
2014-07-22 21:39:48 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{
|
2014-07-22 21:39:48 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"bar": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
},
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{
|
2014-07-22 21:39:48 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
2015-04-13 09:28:47 -05:00
|
|
|
"attribute mismatch: bar",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
|
|
|
|
2014-10-09 22:52:38 -05:00
|
|
|
// Extra attributes
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{},
|
|
|
|
"bar": &ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
2015-04-13 09:28:47 -05:00
|
|
|
"extra attributes: bar",
|
2014-10-09 22:52:38 -05:00
|
|
|
},
|
|
|
|
|
2014-07-22 21:39:48 -05:00
|
|
|
{
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{
|
2014-07-22 21:39:48 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{RequiresNew: true},
|
|
|
|
},
|
|
|
|
},
|
2014-09-17 18:33:24 -05:00
|
|
|
&InstanceDiff{
|
2014-07-22 21:39:48 -05:00
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{RequiresNew: false},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
false,
|
2015-04-13 09:28:47 -05:00
|
|
|
"diff RequiresNew; old: true, new: false",
|
2014-07-22 21:39:48 -05:00
|
|
|
},
|
2014-10-09 22:52:38 -05:00
|
|
|
|
helper/schema,terraform: handle computed primtives in diffs
Fixes #3309
There are two primary changes, one to how helper/schema creates diffs
and one to how Terraform compares diffs. Both require careful
understanding.
== 1. helper/schema Changes
helper/schema, given any primitive field (string, int, bool, etc.)
_used to_ create a basic diff when given a computed new value (i.e. from
an unkown interpolation). This would put in the plan that the old value
is whatever the old value was, and the new value was the actual
interpolation. For example, from #3309, the diff showed the following:
```
~ module.test.aws_eip.test-instance.0
instance: "<INSTANCE ID>" => "${element(aws_instance.test-instance.*.id, count.index)}"
```
Then, when running `apply`, the diff would be realized and you would get
a diff mismatch error because it would realize the final value is the
same and remove it from the diff.
**The change:** `helper/schema` now marks unknown primitive values with
`NewComputed` set to true. Semantically this is correct for the diff to
have this information.
== 2. Terraform Diff.Same Changes
Next, the way Terraform compares diffs needed to be updated
Specifically, the case where the diff from the plan had a NewComputed
primitive and the diff from the apply _no longer has that value_. This
is possible if the computed value ended up being the same as the old
value. This is allowed to pass through.
Together, these fix #3309.
2016-10-25 21:36:59 -05:00
|
|
|
// NewComputed on primitive
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "${var.foo}",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
|
|
|
// NewComputed on primitive, removed
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "${var.foo}",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
2016-11-01 11:53:53 -05:00
|
|
|
// NewComputed on set, removed
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.1": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "",
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
"foo.2": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
2014-10-09 22:52:38 -05:00
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{NewComputed: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.0": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "12",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-10-09 22:52:38 -05:00
|
|
|
},
|
2014-12-12 16:21:20 -06:00
|
|
|
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.~35964334.bar": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "${var.foo}",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.87654323.bar": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "12",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-12-12 16:21:20 -06:00
|
|
|
},
|
2014-12-16 12:15:07 -06:00
|
|
|
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
true,
|
2015-04-13 09:28:47 -05:00
|
|
|
"",
|
2014-12-16 12:15:07 -06:00
|
|
|
},
|
2015-04-13 18:56:48 -05:00
|
|
|
|
2016-11-15 13:26:42 -06:00
|
|
|
// Computed can change RequiresNew by removal, and that's okay
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
NewComputed: true,
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Computed can change Destroy by removal, and that's okay
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
NewComputed: true,
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Destroy: true,
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Computed can change Destroy by elements
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
NewComputed: true,
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Destroy: true,
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.12": &ResourceAttrDiff{
|
|
|
|
Old: "4",
|
|
|
|
New: "12",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Destroy: true,
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
2016-06-16 17:37:11 -05:00
|
|
|
// Computed sets may not contain all fields in the original diff, and
|
|
|
|
// because multiple entries for the same set can compute to the same
|
|
|
|
// hash before the values are computed or interpolated, the overall
|
|
|
|
// count can change as well.
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.~35964334.bar": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "${var.foo}",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "2",
|
|
|
|
},
|
|
|
|
"foo.87654323.bar": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "12",
|
|
|
|
},
|
|
|
|
"foo.87654325.bar": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "12",
|
|
|
|
},
|
|
|
|
"foo.87654325.baz": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "12",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
2016-06-20 17:18:25 -05:00
|
|
|
// Computed values in maps will fail the "Same" check as well
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.%": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.%": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
NewComputed: false,
|
|
|
|
},
|
|
|
|
"foo.val": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "something",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
2015-04-13 18:56:48 -05:00
|
|
|
// In a DESTROY/CREATE scenario, the plan diff will be run against the
|
|
|
|
// state of the old instance, while the apply diff will be run against an
|
|
|
|
// empty state (because the state is cleared when the destroy runs.)
|
|
|
|
// For complex attributes, this can result in keys that seem to disappear
|
|
|
|
// between the two diffs, when in reality everything is working just fine.
|
|
|
|
//
|
|
|
|
// Same() needs to take into account this scenario by analyzing NewRemoved
|
|
|
|
// and treating as "Same" a diff that does indeed have that key removed.
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"somemap.oldkey": &ResourceAttrDiff{
|
|
|
|
Old: "long ago",
|
|
|
|
New: "",
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
"somemap.newkey": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "brave new world",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"somemap.newkey": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "brave new world",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
2016-01-19 15:28:48 -06:00
|
|
|
|
|
|
|
// Another thing that can occur in DESTROY/CREATE scenarios is that list
|
|
|
|
// values that are going to zero have diffs that show up at plan time but
|
|
|
|
// are gone at apply time. The NewRemoved handling catches the fields and
|
|
|
|
// treats them as OK, but it also needs to treat the .# field itself as
|
|
|
|
// okay to be present in the old diff but not in the new one.
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"reqnew": &ResourceAttrDiff{
|
|
|
|
Old: "old",
|
|
|
|
New: "new",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
"somemap.#": &ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "0",
|
|
|
|
},
|
|
|
|
"somemap.oldkey": &ResourceAttrDiff{
|
|
|
|
Old: "long ago",
|
|
|
|
New: "",
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"reqnew": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "new",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
2016-06-16 18:22:21 -05:00
|
|
|
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"reqnew": &ResourceAttrDiff{
|
|
|
|
Old: "old",
|
|
|
|
New: "new",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
"somemap.%": &ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "0",
|
|
|
|
},
|
|
|
|
"somemap.oldkey": &ResourceAttrDiff{
|
|
|
|
Old: "long ago",
|
|
|
|
New: "",
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"reqnew": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "new",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
2016-11-21 19:31:47 -06:00
|
|
|
|
|
|
|
// Innner computed set should allow outer change in key
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.~1.outer_val": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
"foo.~1.inner.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.~1.inner.~2.value": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "${var.bar}",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.12.outer_val": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
"foo.12.inner.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.12.inner.42.value": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "baz",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Innner computed list should allow outer change in key
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.~1.outer_val": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
"foo.~1.inner.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.~1.inner.0.value": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "${var.bar}",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.12.outer_val": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
"foo.12.inner.#": &ResourceAttrDiff{
|
|
|
|
Old: "0",
|
|
|
|
New: "1",
|
|
|
|
},
|
|
|
|
"foo.12.inner.0.value": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "baz",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
2017-02-06 19:40:44 -06:00
|
|
|
|
|
|
|
// When removing all collection items, the diff is allowed to contain
|
|
|
|
// nothing when re-creating the resource. This should be the "Same"
|
|
|
|
// since we said we were going from 1 to 0.
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.%": &ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "0",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
"foo.bar": &ResourceAttrDiff{
|
|
|
|
Old: "baz",
|
|
|
|
New: "",
|
|
|
|
NewRemoved: true,
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo.#": &ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "0",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
"foo.0": &ResourceAttrDiff{
|
|
|
|
Old: "baz",
|
|
|
|
New: "",
|
|
|
|
NewRemoved: true,
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
2017-07-03 14:59:42 -05:00
|
|
|
|
|
|
|
// Make sure that DestroyTainted diffs pass as well, especially when diff
|
|
|
|
// two works off of no state.
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
DestroyTainted: true,
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
DestroyTainted: true,
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
// RequiresNew in different attribute
|
|
|
|
{
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
"bar": &ResourceAttrDiff{
|
|
|
|
Old: "bar",
|
|
|
|
New: "baz",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&InstanceDiff{
|
|
|
|
Attributes: map[string]*ResourceAttrDiff{
|
|
|
|
"foo": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
},
|
|
|
|
"bar": &ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "baz",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
"",
|
|
|
|
},
|
2014-07-22 21:39:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range cases {
|
2017-02-06 19:40:44 -06:00
|
|
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
|
|
|
same, reason := tc.One.Same(tc.Two)
|
|
|
|
if same != tc.Same {
|
|
|
|
t.Fatalf("%d: expected same: %t, got %t (%s)\n\n one: %#v\n\ntwo: %#v",
|
|
|
|
i, tc.Same, same, reason, tc.One, tc.Two)
|
|
|
|
}
|
|
|
|
if reason != tc.Reason {
|
|
|
|
t.Fatalf(
|
|
|
|
"%d: bad reason\n\nexpected: %#v\n\ngot: %#v", i, tc.Reason, reason)
|
|
|
|
}
|
|
|
|
})
|
2014-07-22 21:39:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
const moduleDiffStrBasic = `
|
2014-06-10 13:37:04 -05:00
|
|
|
CREATE: nodeA
|
2016-05-27 19:00:59 -05:00
|
|
|
bar: "foo" => "<computed>"
|
|
|
|
foo: "foo" => "bar"
|
|
|
|
longfoo: "foo" => "bar" (forces new resource)
|
|
|
|
secretfoo: "<sensitive>" => "<sensitive>" (attribute changed)
|
2014-06-10 13:22:32 -05:00
|
|
|
`
|
2018-11-16 14:26:16 -06:00
|
|
|
|
|
|
|
func TestCountFlatmapContainerValues(t *testing.T) {
|
|
|
|
for i, tc := range []struct {
|
|
|
|
attrs map[string]string
|
|
|
|
key string
|
|
|
|
count string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
attrs: map[string]string{"set.2.list.#": "9999", "set.2.list.0": "x", "set.2.list.0.z": "y", "set.2.attr": "bar", "set.#": "9999"},
|
|
|
|
key: "set.2.list.#",
|
|
|
|
count: "1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: map[string]string{"set.2.list.#": "9999", "set.2.list.0": "x", "set.2.list.0.z": "y", "set.2.attr": "bar", "set.#": "9999"},
|
|
|
|
key: "set.#",
|
|
|
|
count: "1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: map[string]string{"set.2.list.0": "x", "set.2.list.0.z": "y", "set.2.attr": "bar", "set.#": "9999"},
|
|
|
|
key: "set.#",
|
|
|
|
count: "1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: map[string]string{"map.#": "3", "map.a": "b", "map.a.#": "0", "map.b": "4"},
|
|
|
|
key: "map.#",
|
|
|
|
count: "2",
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
|
|
|
count := countFlatmapContainerValues(tc.key, tc.attrs)
|
|
|
|
if count != tc.count {
|
|
|
|
t.Fatalf("expected %q, got %q", tc.count, count)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|