helper/resource: ImportState test can verify states

This commit is contained in:
Mitchell Hashimoto 2016-05-10 09:25:54 -07:00
parent 27452f0043
commit 6a675b4a15
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 183 additions and 3 deletions

View File

@ -149,6 +149,11 @@ type TestStep struct {
// used to verify that the resulting value of ImportState has the // used to verify that the resulting value of ImportState has the
// proper resources, IDs, and attributes. // proper resources, IDs, and attributes.
ImportStateCheck ImportStateCheckFunc ImportStateCheck ImportStateCheckFunc
// ImportStateVerify, if true, will also check that the state values
// that are finally put into the state after import match for all the
// IDs returned by the Import.
ImportStateVerify bool
} }
// Test performs an acceptance test on a resource. // Test performs an acceptance test on a resource.

View File

@ -1,8 +1,11 @@
package resource package resource
import ( import (
"fmt"
"log" "log"
"reflect"
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -36,11 +39,11 @@ func testStepImportState(
return state, err return state, err
} }
// TODO: ImportOpts needs a flag to read a config module so it
// can load our provider config without env vars.
// Do the import! // Do the import!
newState, err := ctx.Import(&terraform.ImportOpts{ newState, err := ctx.Import(&terraform.ImportOpts{
// Set the module so that any provider config is loaded
Module: mod,
Targets: []*terraform.ImportTarget{ Targets: []*terraform.ImportTarget{
&terraform.ImportTarget{ &terraform.ImportTarget{
Addr: step.ResourceName, Addr: step.ResourceName,
@ -66,6 +69,47 @@ func testStepImportState(
} }
} }
// Verify that all the states match
if step.ImportStateVerify {
new := newState.RootModule().Resources
old := state.RootModule().Resources
for _, r := range new {
// Find the existing resource
var oldR *terraform.ResourceState
for _, r2 := range old {
if r2.Primary != nil && r2.Primary.ID == r.Primary.ID {
oldR = r2
break
}
}
if oldR == nil {
return state, fmt.Errorf(
"Failed state verification, resource with ID %s not found",
r.Primary.ID)
}
// Compare their attributes
actual := r.Primary.Attributes
expected := oldR.Primary.Attributes
if !reflect.DeepEqual(actual, expected) {
// Determine only the different attributes
for k, v := range expected {
if av, ok := actual[k]; ok && v == av {
delete(expected, k)
delete(actual, k)
}
}
spewConf := spew.NewDefaultConfig()
spewConf.SortKeys = true
return state, fmt.Errorf(
"Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
"\n\n%s\n\n%s",
spewConf.Sdump(actual), spewConf.Sdump(expected))
}
}
}
// Return the old state (non-imported) so we don't change anything. // Return the old state (non-imported) so we don't change anything.
return state, nil return state, nil
} }

View File

@ -177,3 +177,134 @@ func TestTest_importStateDetectId(t *testing.T) {
t.Fatal("didn't call check") t.Fatal("didn't call check")
} }
} }
func TestTest_importStateVerify(t *testing.T) {
mp := testProvider()
mp.DiffReturn = nil
mp.ApplyFn = func(
info *terraform.InstanceInfo,
state *terraform.InstanceState,
diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
if !diff.Destroy {
return &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"foo": "bar",
},
}, nil
}
return nil, nil
}
mp.RefreshFn = func(
i *terraform.InstanceInfo,
s *terraform.InstanceState) (*terraform.InstanceState, error) {
if len(s.Attributes) == 0 {
s.Attributes = map[string]string{
"id": s.ID,
"foo": "bar",
}
}
return s, nil
}
mp.ImportStateFn = func(
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
if id != "foo" {
return nil, fmt.Errorf("bad import ID: %s", id)
}
return []*terraform.InstanceState{
&terraform.InstanceState{
ID: "foo",
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
},
}, nil
}
mt := new(mockT)
Test(mt, TestCase{
Providers: map[string]terraform.ResourceProvider{
"test": mp,
},
Steps: []TestStep{
TestStep{
Config: testConfigStr,
},
TestStep{
ResourceName: "test_instance.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
if mt.failed() {
t.Fatalf("test failed: %s", mt.failMessage())
}
}
func TestTest_importStateVerifyFail(t *testing.T) {
mp := testProvider()
mp.DiffReturn = nil
mp.ApplyFn = func(
info *terraform.InstanceInfo,
state *terraform.InstanceState,
diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
if !diff.Destroy {
return &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"foo": "bar",
},
}, nil
}
return nil, nil
}
mp.RefreshFn = func(
i *terraform.InstanceInfo,
s *terraform.InstanceState) (*terraform.InstanceState, error) {
return s, nil
}
mp.ImportStateFn = func(
info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) {
if id != "foo" {
return nil, fmt.Errorf("bad import ID: %s", id)
}
return []*terraform.InstanceState{
&terraform.InstanceState{
ID: "foo",
Ephemeral: terraform.EphemeralState{Type: "test_instance"},
},
}, nil
}
mt := new(mockT)
Test(mt, TestCase{
Providers: map[string]terraform.ResourceProvider{
"test": mp,
},
Steps: []TestStep{
TestStep{
Config: testConfigStr,
},
TestStep{
ResourceName: "test_instance.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
if !mt.failed() {
t.Fatalf("test should fail")
}
}