Merge pull request #10139 from hashicorp/b-schema-diff-destroy

helper/schema: mark diff as forcenew if element is computed
This commit is contained in:
Mitchell Hashimoto 2016-11-15 11:50:12 -08:00 committed by GitHub
commit 97943acbf8
4 changed files with 150 additions and 13 deletions

View File

@ -925,6 +925,13 @@ func (m schemaMap) diffSet(
oldStr := strconv.Itoa(oldLen) oldStr := strconv.Itoa(oldLen)
newStr := strconv.Itoa(newLen) newStr := strconv.Itoa(newLen)
// Build a schema for our count
countSchema := &Schema{
Type: TypeInt,
Computed: schema.Computed,
ForceNew: schema.ForceNew,
}
// If the set computed then say that the # is computed // If the set computed then say that the # is computed
if computedSet || schema.Computed && !nSet { if computedSet || schema.Computed && !nSet {
// If # already exists, equals 0 and no new set is supplied, there // If # already exists, equals 0 and no new set is supplied, there
@ -941,22 +948,16 @@ func (m schemaMap) diffSet(
countStr = "" countStr = ""
} }
diff.Attributes[k+".#"] = &terraform.ResourceAttrDiff{ diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{
Old: countStr, Old: countStr,
NewComputed: true, NewComputed: true,
} })
return nil return nil
} }
// If the counts are not the same, then record that diff // If the counts are not the same, then record that diff
changed := oldLen != newLen changed := oldLen != newLen
if changed || all { if changed || all {
countSchema := &Schema{
Type: TypeInt,
Computed: schema.Computed,
ForceNew: schema.ForceNew,
}
diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{
Old: oldStr, Old: oldStr,
New: newStr, New: newStr,

View File

@ -2608,6 +2608,49 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
{
Name: "Set ForceNew marks count as ForceNew if computed",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeSet,
Required: true,
ForceNew: true,
Elem: &Schema{Type: TypeInt},
Set: func(a interface{}) int {
return a.(int)
},
},
},
State: &terraform.InstanceState{
Attributes: map[string]string{
"ports.#": "3",
"ports.1": "1",
"ports.2": "2",
"ports.4": "4",
},
},
Config: map[string]interface{}{
"ports": []interface{}{"${var.foo}", 2, 1},
},
ConfigVariables: map[string]ast.Variable{
"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
},
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"ports.#": &terraform.ResourceAttrDiff{
Old: "3",
New: "",
NewComputed: true,
RequiresNew: true,
},
},
},
},
} }
for i, tc := range cases { for i, tc := range cases {

View File

@ -606,14 +606,37 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
if d.Destroy != d2.GetDestroy() { // If we're going from requiring new to NOT requiring new, then we have
// to see if all required news were computed. If so, it is allowed since
// computed may also mean "same value and therefore not new".
oldNew := d.requiresNew()
newNew := d2.RequiresNew()
if oldNew && !newNew {
oldNew = false
for _, rd := range d.Attributes {
// If the field is requires new and NOT computed, then what
// we have is a diff mismatch for sure. We set that the old
// diff does REQUIRE a ForceNew.
if rd != nil && rd.RequiresNew && !rd.NewComputed {
oldNew = true
break
}
}
}
if oldNew != newNew {
return false, fmt.Sprintf(
"diff RequiresNew; old: %t, new: %t", oldNew, newNew)
}
// Verify that destroy matches. The second boolean here allows us to
// have mismatching Destroy if we're moving from RequiresNew true
// to false above. Therefore, the second boolean will only pass if
// we're moving from Destroy: true to false as well.
if d.Destroy != d2.GetDestroy() && d.requiresNew() == oldNew {
return false, fmt.Sprintf( return false, fmt.Sprintf(
"diff: Destroy; old: %t, new: %t", d.Destroy, d2.GetDestroy()) "diff: Destroy; old: %t, new: %t", d.Destroy, d2.GetDestroy())
} }
if d.requiresNew() != d2.RequiresNew() {
return false, fmt.Sprintf(
"diff RequiresNew; old: %t, new: %t", d.requiresNew(), d2.RequiresNew())
}
// Go through the old diff and make sure the new diff has all the // Go through the old diff and make sure the new diff has all the
// same attributes. To start, build up the check map to be all the keys. // same attributes. To start, build up the check map to be all the keys.

View File

@ -750,6 +750,76 @@ func TestInstanceDiffSame(t *testing.T) {
"", "",
}, },
// 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,
"",
},
// Computed sets may not contain all fields in the original diff, and // Computed sets may not contain all fields in the original diff, and
// because multiple entries for the same set can compute to the same // because multiple entries for the same set can compute to the same
// hash before the values are computed or interpolated, the overall // hash before the values are computed or interpolated, the overall