]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / resource / testing_import_state.go
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/hcl2/hcl"
11 "github.com/hashicorp/hcl2/hcl/hclsyntax"
12
13 "github.com/hashicorp/terraform/addrs"
14 "github.com/hashicorp/terraform/helper/schema"
15 "github.com/hashicorp/terraform/states"
16 "github.com/hashicorp/terraform/terraform"
17 )
18
19 // testStepImportState runs an imort state test step
20 func testStepImportState(
21 opts terraform.ContextOpts,
22 state *terraform.State,
23 step TestStep) (*terraform.State, error) {
24
25 // Determine the ID to import
26 var importId string
27 switch {
28 case step.ImportStateIdFunc != nil:
29 var err error
30 importId, err = step.ImportStateIdFunc(state)
31 if err != nil {
32 return state, err
33 }
34 case step.ImportStateId != "":
35 importId = step.ImportStateId
36 default:
37 resource, err := testResource(step, state)
38 if err != nil {
39 return state, err
40 }
41 importId = resource.Primary.ID
42 }
43
44 importPrefix := step.ImportStateIdPrefix
45 if importPrefix != "" {
46 importId = fmt.Sprintf("%s%s", importPrefix, importId)
47 }
48
49 // Setup the context. We initialize with an empty state. We use the
50 // full config for provider configurations.
51 cfg, err := testConfig(opts, step)
52 if err != nil {
53 return state, err
54 }
55
56 opts.Config = cfg
57
58 // import tests start with empty state
59 opts.State = states.NewState()
60
61 ctx, stepDiags := terraform.NewContext(&opts)
62 if stepDiags.HasErrors() {
63 return state, stepDiags.Err()
64 }
65
66 // The test step provides the resource address as a string, so we need
67 // to parse it to get an addrs.AbsResourceAddress to pass in to the
68 // import method.
69 traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(step.ResourceName), "", hcl.Pos{})
70 if hclDiags.HasErrors() {
71 return nil, hclDiags
72 }
73 importAddr, stepDiags := addrs.ParseAbsResourceInstance(traversal)
74 if stepDiags.HasErrors() {
75 return nil, stepDiags.Err()
76 }
77
78 // Do the import
79 importedState, stepDiags := ctx.Import(&terraform.ImportOpts{
80 // Set the module so that any provider config is loaded
81 Config: cfg,
82
83 Targets: []*terraform.ImportTarget{
84 &terraform.ImportTarget{
85 Addr: importAddr,
86 ID: importId,
87 },
88 },
89 })
90 if stepDiags.HasErrors() {
91 log.Printf("[ERROR] Test: ImportState failure: %s", stepDiags.Err())
92 return state, stepDiags.Err()
93 }
94
95 newState, err := shimNewState(importedState, step.providers)
96 if err != nil {
97 return nil, err
98 }
99
100 // Go through the new state and verify
101 if step.ImportStateCheck != nil {
102 var states []*terraform.InstanceState
103 for _, r := range newState.RootModule().Resources {
104 if r.Primary != nil {
105 is := r.Primary.DeepCopy()
106 is.Ephemeral.Type = r.Type // otherwise the check function cannot see the type
107 states = append(states, is)
108 }
109 }
110 if err := step.ImportStateCheck(states); err != nil {
111 return state, err
112 }
113 }
114
115 // Verify that all the states match
116 if step.ImportStateVerify {
117 new := newState.RootModule().Resources
118 old := state.RootModule().Resources
119 for _, r := range new {
120 // Find the existing resource
121 var oldR *terraform.ResourceState
122 for _, r2 := range old {
123 if r2.Primary != nil && r2.Primary.ID == r.Primary.ID && r2.Type == r.Type {
124 oldR = r2
125 break
126 }
127 }
128 if oldR == nil {
129 return state, fmt.Errorf(
130 "Failed state verification, resource with ID %s not found",
131 r.Primary.ID)
132 }
133
134 // We'll try our best to find the schema for this resource type
135 // so we can ignore Removed fields during validation. If we fail
136 // to find the schema then we won't ignore them and so the test
137 // will need to rely on explicit ImportStateVerifyIgnore, though
138 // this shouldn't happen in any reasonable case.
139 var rsrcSchema *schema.Resource
140 if providerAddr, diags := addrs.ParseAbsProviderConfigStr(r.Provider); !diags.HasErrors() {
141 providerType := providerAddr.ProviderConfig.Type
142 if provider, ok := step.providers[providerType]; ok {
143 if provider, ok := provider.(*schema.Provider); ok {
144 rsrcSchema = provider.ResourcesMap[r.Type]
145 }
146 }
147 }
148
149 // don't add empty flatmapped containers, so we can more easily
150 // compare the attributes
151 skipEmpty := func(k, v string) bool {
152 if strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%") {
153 if v == "0" {
154 return true
155 }
156 }
157 return false
158 }
159
160 // Compare their attributes
161 actual := make(map[string]string)
162 for k, v := range r.Primary.Attributes {
163 if skipEmpty(k, v) {
164 continue
165 }
166 actual[k] = v
167 }
168
169 expected := make(map[string]string)
170 for k, v := range oldR.Primary.Attributes {
171 if skipEmpty(k, v) {
172 continue
173 }
174 expected[k] = v
175 }
176
177 // Remove fields we're ignoring
178 for _, v := range step.ImportStateVerifyIgnore {
179 for k := range actual {
180 if strings.HasPrefix(k, v) {
181 delete(actual, k)
182 }
183 }
184 for k := range expected {
185 if strings.HasPrefix(k, v) {
186 delete(expected, k)
187 }
188 }
189 }
190
191 // Also remove any attributes that are marked as "Removed" in the
192 // schema, if we have a schema to check that against.
193 if rsrcSchema != nil {
194 for k := range actual {
195 for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) {
196 if schema.Removed != "" {
197 delete(actual, k)
198 break
199 }
200 }
201 }
202 for k := range expected {
203 for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) {
204 if schema.Removed != "" {
205 delete(expected, k)
206 break
207 }
208 }
209 }
210 }
211
212 if !reflect.DeepEqual(actual, expected) {
213 // Determine only the different attributes
214 for k, v := range expected {
215 if av, ok := actual[k]; ok && v == av {
216 delete(expected, k)
217 delete(actual, k)
218 }
219 }
220
221 spewConf := spew.NewDefaultConfig()
222 spewConf.SortKeys = true
223 return state, fmt.Errorf(
224 "ImportStateVerify attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
225 "\n\n%s\n\n%s",
226 spewConf.Sdump(actual), spewConf.Sdump(expected))
227 }
228 }
229 }
230
231 // Return the old state (non-imported) so we don't change anything.
232 return state, nil
233 }