"strings"
"github.com/davecgh/go-spew/spew"
+ "github.com/hashicorp/hcl2/hcl"
+ "github.com/hashicorp/hcl2/hcl/hclsyntax"
+
+ "github.com/hashicorp/terraform/addrs"
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/terraform"
)
opts terraform.ContextOpts,
state *terraform.State,
step TestStep) (*terraform.State, error) {
+
// Determine the ID to import
var importId string
switch {
// Setup the context. We initialize with an empty state. We use the
// full config for provider configurations.
- mod, err := testModule(opts, step)
+ cfg, err := testConfig(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
+ opts.Config = cfg
+
+ // import tests start with empty state
+ opts.State = states.NewState()
+
+ ctx, stepDiags := terraform.NewContext(&opts)
+ if stepDiags.HasErrors() {
+ return state, stepDiags.Err()
}
- // Do the import!
- newState, err := ctx.Import(&terraform.ImportOpts{
+ // The test step provides the resource address as a string, so we need
+ // to parse it to get an addrs.AbsResourceAddress to pass in to the
+ // import method.
+ traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(step.ResourceName), "", hcl.Pos{})
+ if hclDiags.HasErrors() {
+ return nil, hclDiags
+ }
+ importAddr, stepDiags := addrs.ParseAbsResourceInstance(traversal)
+ if stepDiags.HasErrors() {
+ return nil, stepDiags.Err()
+ }
+
+ // Do the import
+ importedState, stepDiags := ctx.Import(&terraform.ImportOpts{
// Set the module so that any provider config is loaded
- Module: mod,
+ Config: cfg,
Targets: []*terraform.ImportTarget{
&terraform.ImportTarget{
- Addr: step.ResourceName,
+ Addr: importAddr,
ID: importId,
},
},
})
+ if stepDiags.HasErrors() {
+ log.Printf("[ERROR] Test: ImportState failure: %s", stepDiags.Err())
+ return state, stepDiags.Err()
+ }
+
+ newState, err := shimNewState(importedState, step.providers)
if err != nil {
- log.Printf("[ERROR] Test: ImportState failure: %s", err)
- return state, err
+ return nil, err
}
// Go through the new state and verify
var states []*terraform.InstanceState
for _, r := range newState.RootModule().Resources {
if r.Primary != nil {
- states = append(states, r.Primary)
+ is := r.Primary.DeepCopy()
+ is.Ephemeral.Type = r.Type // otherwise the check function cannot see the type
+ states = append(states, is)
}
}
if err := step.ImportStateCheck(states); err != nil {
r.Primary.ID)
}
+ // We'll try our best to find the schema for this resource type
+ // so we can ignore Removed fields during validation. If we fail
+ // to find the schema then we won't ignore them and so the test
+ // will need to rely on explicit ImportStateVerifyIgnore, though
+ // this shouldn't happen in any reasonable case.
+ var rsrcSchema *schema.Resource
+ if providerAddr, diags := addrs.ParseAbsProviderConfigStr(r.Provider); !diags.HasErrors() {
+ providerType := providerAddr.ProviderConfig.Type
+ if provider, ok := step.providers[providerType]; ok {
+ if provider, ok := provider.(*schema.Provider); ok {
+ rsrcSchema = provider.ResourcesMap[r.Type]
+ }
+ }
+ }
+
+ // don't add empty flatmapped containers, so we can more easily
+ // compare the attributes
+ skipEmpty := func(k, v string) bool {
+ if strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%") {
+ if v == "0" {
+ return true
+ }
+ }
+ return false
+ }
+
// Compare their attributes
actual := make(map[string]string)
for k, v := range r.Primary.Attributes {
+ if skipEmpty(k, v) {
+ continue
+ }
actual[k] = v
}
+
expected := make(map[string]string)
for k, v := range oldR.Primary.Attributes {
+ if skipEmpty(k, v) {
+ continue
+ }
expected[k] = v
}
// Remove fields we're ignoring
for _, v := range step.ImportStateVerifyIgnore {
- for k, _ := range actual {
+ for k := range actual {
if strings.HasPrefix(k, v) {
delete(actual, k)
}
}
- for k, _ := range expected {
+ for k := range expected {
if strings.HasPrefix(k, v) {
delete(expected, k)
}
}
}
+ // Also remove any attributes that are marked as "Removed" in the
+ // schema, if we have a schema to check that against.
+ if rsrcSchema != nil {
+ for k := range actual {
+ for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) {
+ if schema.Removed != "" {
+ delete(actual, k)
+ break
+ }
+ }
+ }
+ for k := range expected {
+ for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) {
+ if schema.Removed != "" {
+ delete(expected, k)
+ break
+ }
+ }
+ }
+ }
+
if !reflect.DeepEqual(actual, expected) {
// Determine only the different attributes
for k, v := range expected {