helper/schema: generate a full diff in destroy/create cycle

This commit is contained in:
Mitchell Hashimoto 2014-08-27 15:26:15 -07:00
parent 84fda6a5ad
commit c9a541d95b
4 changed files with 160 additions and 6 deletions

View File

@ -91,6 +91,7 @@ func resourceAwsInstance() *schema.Resource {
"security_groups": &schema.Schema{ "security_groups": &schema.Schema{
Type: schema.TypeSet, Type: schema.TypeSet,
Optional: true, Optional: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int { Set: func(v interface{}) int {
return hashcode.String(v.(string)) return hashcode.String(v.(string))

View File

@ -82,11 +82,8 @@ func (r *Resource) Apply(
return data.State(), err return data.State(), err
} }
// Reset the data to be empty // Make sure the ID is gone.
data, err = schemaMap(r.Schema).Data(nil, d) data.SetId("")
if err != nil {
return nil, err
}
} }
// If we're only destroying, and not creating, then return // If we're only destroying, and not creating, then return
@ -97,7 +94,7 @@ func (r *Resource) Apply(
} }
err = nil err = nil
if s.ID == "" { if data.Id() == "" {
// We're creating, it is a new resource. // We're creating, it is a new resource.
err = r.Create(data, meta) err = r.Create(data, meta)
} else { } else {

View File

@ -177,6 +177,64 @@ func (m schemaMap) Diff(
} }
} }
// If the diff requires a new resource, then we recompute the diff
// so we have the complete new resource diff, and preserve the
// RequiresNew fields where necessary so the user knows exactly what
// caused that.
if result.RequiresNew() {
// Create the new diff
result2 := new(terraform.ResourceDiff)
result2.Attributes = make(map[string]*terraform.ResourceAttrDiff)
// Reset the data to not contain state
d.state = nil
// Perform the diff again
for k, schema := range m {
err := m.diff(k, schema, result2, d)
if err != nil {
return nil, err
}
}
// Force all the fields to not force a new since we know what we
// want to force new.
for k, attr := range result2.Attributes {
if attr == nil {
continue
}
if attr.RequiresNew {
attr.RequiresNew = false
}
if s != nil {
attr.Old = s.Attributes[k]
}
}
// Now copy in all the requires new diffs...
for k, attr := range result.Attributes {
if attr == nil {
continue
}
newAttr, ok := result2.Attributes[k]
if !ok {
newAttr = attr
}
if attr.RequiresNew {
newAttr.RequiresNew = true
}
result2.Attributes[k] = newAttr
}
// And set the diff!
result = result2
}
// Remove any nil diffs just to keep things clean // Remove any nil diffs just to keep things clean
for k, v := range result.Attributes { for k, v := range result.Attributes {
if v == nil { if v == nil {

View File

@ -764,6 +764,104 @@ func TestSchemaMap_Diff(t *testing.T) {
Err: false, Err: false,
}, },
/*
* ForceNews
*/
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Optional: true,
ForceNew: true,
},
"address": &Schema{
Type: TypeString,
Optional: true,
Computed: true,
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"availability_zone": "bar",
"address": "foo",
},
},
Config: map[string]interface{}{
"availability_zone": "foo",
},
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"availability_zone": &terraform.ResourceAttrDiff{
Old: "bar",
New: "foo",
RequiresNew: true,
},
"address": &terraform.ResourceAttrDiff{
Old: "foo",
New: "",
NewComputed: true,
},
},
},
Err: false,
},
// Set
{
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
Optional: true,
ForceNew: true,
},
"ports": &Schema{
Type: TypeSet,
Optional: true,
Computed: true,
Elem: &Schema{Type: TypeInt},
Set: func(v interface{}) int { return v.(int) },
},
},
State: &terraform.ResourceState{
Attributes: map[string]string{
"availability_zone": "bar",
"ports.#": "1",
"ports.0": "80",
},
},
Config: map[string]interface{}{
"availability_zone": "foo",
},
Diff: &terraform.ResourceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"availability_zone": &terraform.ResourceAttrDiff{
Old: "bar",
New: "foo",
RequiresNew: true,
},
"ports.#": &terraform.ResourceAttrDiff{
Old: "1",
New: "",
NewComputed: true,
},
},
},
Err: false,
},
} }
for i, tc := range cases { for i, tc := range cases {