]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package resource |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "log" | |
6 | "reflect" | |
7 | "strings" | |
8 | ||
9 | "github.com/davecgh/go-spew/spew" | |
10 | "github.com/hashicorp/terraform/terraform" | |
11 | ) | |
12 | ||
13 | // testStepImportState runs an imort state test step | |
14 | func testStepImportState( | |
15 | opts terraform.ContextOpts, | |
16 | state *terraform.State, | |
17 | step TestStep) (*terraform.State, error) { | |
18 | // Determine the ID to import | |
19 | importId := step.ImportStateId | |
20 | if importId == "" { | |
21 | resource, err := testResource(step, state) | |
22 | if err != nil { | |
23 | return state, err | |
24 | } | |
25 | ||
26 | importId = resource.Primary.ID | |
27 | } | |
28 | importPrefix := step.ImportStateIdPrefix | |
29 | if importPrefix != "" { | |
30 | importId = fmt.Sprintf("%s%s", importPrefix, importId) | |
31 | } | |
32 | ||
33 | // Setup the context. We initialize with an empty state. We use the | |
34 | // full config for provider configurations. | |
35 | mod, err := testModule(opts, step) | |
36 | if err != nil { | |
37 | return state, err | |
38 | } | |
39 | ||
40 | opts.Module = mod | |
41 | opts.State = terraform.NewState() | |
42 | ctx, err := terraform.NewContext(&opts) | |
43 | if err != nil { | |
44 | return state, err | |
45 | } | |
46 | ||
47 | // Do the import! | |
48 | newState, err := ctx.Import(&terraform.ImportOpts{ | |
49 | // Set the module so that any provider config is loaded | |
50 | Module: mod, | |
51 | ||
52 | Targets: []*terraform.ImportTarget{ | |
53 | &terraform.ImportTarget{ | |
54 | Addr: step.ResourceName, | |
55 | ID: importId, | |
56 | }, | |
57 | }, | |
58 | }) | |
59 | if err != nil { | |
60 | log.Printf("[ERROR] Test: ImportState failure: %s", err) | |
61 | return state, err | |
62 | } | |
63 | ||
64 | // Go through the new state and verify | |
65 | if step.ImportStateCheck != nil { | |
66 | var states []*terraform.InstanceState | |
67 | for _, r := range newState.RootModule().Resources { | |
68 | if r.Primary != nil { | |
69 | states = append(states, r.Primary) | |
70 | } | |
71 | } | |
72 | if err := step.ImportStateCheck(states); err != nil { | |
73 | return state, err | |
74 | } | |
75 | } | |
76 | ||
77 | // Verify that all the states match | |
78 | if step.ImportStateVerify { | |
79 | new := newState.RootModule().Resources | |
80 | old := state.RootModule().Resources | |
81 | for _, r := range new { | |
82 | // Find the existing resource | |
83 | var oldR *terraform.ResourceState | |
84 | for _, r2 := range old { | |
85 | if r2.Primary != nil && r2.Primary.ID == r.Primary.ID && r2.Type == r.Type { | |
86 | oldR = r2 | |
87 | break | |
88 | } | |
89 | } | |
90 | if oldR == nil { | |
91 | return state, fmt.Errorf( | |
92 | "Failed state verification, resource with ID %s not found", | |
93 | r.Primary.ID) | |
94 | } | |
95 | ||
96 | // Compare their attributes | |
97 | actual := make(map[string]string) | |
98 | for k, v := range r.Primary.Attributes { | |
99 | actual[k] = v | |
100 | } | |
101 | expected := make(map[string]string) | |
102 | for k, v := range oldR.Primary.Attributes { | |
103 | expected[k] = v | |
104 | } | |
105 | ||
106 | // Remove fields we're ignoring | |
107 | for _, v := range step.ImportStateVerifyIgnore { | |
108 | for k, _ := range actual { | |
109 | if strings.HasPrefix(k, v) { | |
110 | delete(actual, k) | |
111 | } | |
112 | } | |
113 | for k, _ := range expected { | |
114 | if strings.HasPrefix(k, v) { | |
115 | delete(expected, k) | |
116 | } | |
117 | } | |
118 | } | |
119 | ||
120 | if !reflect.DeepEqual(actual, expected) { | |
121 | // Determine only the different attributes | |
122 | for k, v := range expected { | |
123 | if av, ok := actual[k]; ok && v == av { | |
124 | delete(expected, k) | |
125 | delete(actual, k) | |
126 | } | |
127 | } | |
128 | ||
129 | spewConf := spew.NewDefaultConfig() | |
130 | spewConf.SortKeys = true | |
131 | return state, fmt.Errorf( | |
132 | "ImportStateVerify attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+ | |
133 | "\n\n%s\n\n%s", | |
134 | spewConf.Sdump(actual), spewConf.Sdump(expected)) | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | // Return the old state (non-imported) so we don't change anything. | |
140 | return state, nil | |
141 | } |