package resource
import (
"fmt"
"log"
"reflect"
"strings"
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/terraform/terraform"
)
// testStepImportState runs an imort state test step
func testStepImportState(
opts terraform.ContextOpts,
state *terraform.State,
step TestStep) (*terraform.State, error) {
// Determine the ID to import
importId := step.ImportStateId
if importId == "" {
resource, err := testResource(step, state)
if err != nil {
return state, err
}
importId = resource.Primary.ID
}
importPrefix := step.ImportStateIdPrefix
if importPrefix != "" {
importId = fmt.Sprintf("%s%s", importPrefix, importId)
}
// Setup the context. We initialize with an empty state. We use the
// full config for provider configurations.
mod, err := testModule(opts, step)
if err != nil {
return state, err
}
opts.Module = mod
opts.State = terraform.NewState()
ctx, err := terraform.NewContext(&opts)
if err != nil {
return state, err
}
// Do the import!
newState, err := ctx.Import(&terraform.ImportOpts{
// Set the module so that any provider config is loaded
Module: mod,
Targets: []*terraform.ImportTarget{
&terraform.ImportTarget{
Addr: step.ResourceName,
ID: importId,
},
},
})
if err != nil {
log.Printf("[ERROR] Test: ImportState failure: %s", err)
return state, err
}
// Go through the new state and verify
if step.ImportStateCheck != nil {
var states []*terraform.InstanceState
for _, r := range newState.RootModule().Resources {
if r.Primary != nil {
states = append(states, r.Primary)
}
}
if err := step.ImportStateCheck(states); err != nil {
return state, err
}
}
// 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 && r2.Type == r.Type {
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 := make(map[string]string)
for k, v := range r.Primary.Attributes {
actual[k] = v
}
expected := make(map[string]string)
for k, v := range oldR.Primary.Attributes {
expected[k] = v
}
// Remove fields we're ignoring
for _, v := range step.ImportStateVerifyIgnore {
for k, _ := range actual {
if strings.HasPrefix(k, v) {
delete(actual, k)
}
}
for k, _ := range expected {
if strings.HasPrefix(k, v) {
delete(expected, k)
}
}
}
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(
"ImportStateVerify 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 state, nil
}