Skip to content

Commit 27d098d

Browse files
committed
Add Terraform E2E test for imports
This provides some E2E coverage for the terraform import command which the acceptance tests can't cover. Acceptance tests currently validate in-memory imported state only, which doesn't exercise Terraform logic that may modify the imported state before it is written to state file.
1 parent c250ed1 commit 27d098d

File tree

2 files changed

+111
-9
lines changed

2 files changed

+111
-9
lines changed

examples/networking/vcn/vcn.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ resource "oci_core_vcn" "vcn1" {
2121
compartment_id = "${var.compartment_ocid}"
2222
display_name = "vcn1"
2323
}
24+
25+
output "vcn_id" {
26+
value = "${oci_core_vcn.vcn1.id}"
27+
}

oci/examples_test.go

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,23 @@ func TestTerraformVersions(t *testing.T) {
8989
}
9090
}
9191

92+
/*
93+
* Test for end-to-end Terraform import
94+
* The acceptance import tests have a test gap, because they do not cover Terraform's resource state to state file
95+
* normalization logic. Import tests only verify that in-memory state is correct.
96+
*/
97+
func TestTerraformImport(t *testing.T) {
98+
if httpreplay.ModeRecordReplay() {
99+
t.Skip("Skipping TestTerraformImport")
100+
}
101+
if strings.Contains(getEnvSettingWithBlankDefault("suppressed_tests"), "TestTerraformImport") {
102+
t.Skip("Skipping TestTerraformImport")
103+
}
104+
if RunConfigAndImport(t) {
105+
log.Printf("Successfully ran all Terraform import tests")
106+
}
107+
}
108+
92109
func RunExamples(t *testing.T, planOnly bool) {
93110
rootPath := getEnvSettingWithDefault("examples_root", "../examples")
94111
log.Printf("Testing examples under %v", rootPath)
@@ -106,7 +123,7 @@ func RunExamples(t *testing.T, planOnly bool) {
106123
}
107124
}
108125

109-
func GetTerraformBinaries(binPath string) ([]string, error) {
126+
func GetTerraformBinaries(binPath string, targetPrefixes []string) ([]string, error) {
110127
results := []string{}
111128

112129
entries, err := ioutil.ReadDir(binPath)
@@ -117,7 +134,16 @@ func GetTerraformBinaries(binPath string) ([]string, error) {
117134
for _, entry := range entries {
118135
// Include any binaries that start with "terraform" prefix
119136
if name := entry.Name(); !entry.IsDir() && strings.HasPrefix(name, defaultTerraformBinary) {
120-
results = append(results, name)
137+
if len(targetPrefixes) == 0 {
138+
results = append(results, name)
139+
} else {
140+
for _, prefix := range targetPrefixes {
141+
if strings.HasPrefix(name, prefix) {
142+
results = append(results, name)
143+
break
144+
}
145+
}
146+
}
121147
}
122148
}
123149

@@ -185,7 +211,7 @@ func shouldSkip(dir string) bool {
185211
}
186212

187213
func RunConfigOnAllTerraformVersions(t *testing.T, path string, planOnly bool) bool {
188-
terraformBinaries, err := GetTerraformBinaries(localBinPath)
214+
terraformBinaries, err := GetTerraformBinaries(localBinPath, []string{})
189215
if err != nil {
190216
t.Errorf("Error retrieving terraform binaries: %v", err)
191217
return false
@@ -199,9 +225,8 @@ func RunConfigOnAllTerraformVersions(t *testing.T, path string, planOnly bool) b
199225
result := true
200226
for _, tfBin := range terraformBinaries {
201227
log.Printf("=== Terraform Version ('%s'), Config Path ('%s') ===\n", tfBin, path)
202-
if !runCommandWithOutputOptions(t, path, fmt.Sprintf("%s version", tfBin), true) {
228+
if _, result = runCommandWithOutputOptions(t, path, fmt.Sprintf("%s version", tfBin), true); !result {
203229
log.Printf("Unable to run version command. Skipping test for %s.\n", tfBin)
204-
result = false
205230
continue
206231
}
207232

@@ -249,11 +274,84 @@ func RunConfig(t *testing.T, path string, planOnly bool, terraformBinary string)
249274
}
250275
}
251276

277+
func RunConfigAndImport(t *testing.T) bool {
278+
path := vcnExamplePath
279+
terraformBinaries, err := GetTerraformBinaries(localBinPath, []string{"terraform-0.11", "terraform-0.12"})
280+
if err != nil {
281+
t.Errorf("Error retrieving terraform binaries: %v", err)
282+
return false
283+
}
284+
285+
if len(terraformBinaries) == 0 {
286+
t.Errorf("Did not find any terraform binaries")
287+
return false
288+
}
289+
290+
result := true
291+
for _, tfBin := range terraformBinaries {
292+
var output []byte
293+
var vcnId string
294+
log.Printf("=== Terraform Version ('%s'), Config Path ('%s') ===\n", tfBin, path)
295+
if _, result := runCommandWithOutputOptions(t, path, fmt.Sprintf("%s version", tfBin), true); !result {
296+
log.Printf("Unable to run version command. Skipping test for %s.\n", tfBin)
297+
continue
298+
}
299+
300+
if !RunCommand(t, path, fmt.Sprintf("%s init", tfBin)) {
301+
log.Printf("Unable to init terraform. Skipping test for %s.\n", tfBin)
302+
return false
303+
}
304+
305+
// Create a VCN resource
306+
if result = RunCommand(t, path, fmt.Sprintf("%s apply -auto-approve -state=%v", tfBin, examplesTestStateFile)); !result {
307+
goto cleanup
308+
}
309+
310+
// Get the ID of the VCN
311+
if output, result = runCommandWithOutputOptions(t, path, fmt.Sprintf("%s output -state=%v vcn_id", tfBin, examplesTestStateFile), true); !result {
312+
goto cleanup
313+
}
314+
vcnId = string(output)
315+
316+
// Remove VCN from state file
317+
if result = RunCommand(t, path, fmt.Sprintf("%s state rm -state=%v oci_core_vcn.vcn1", tfBin, examplesTestStateFile)); !result {
318+
goto cleanup
319+
}
320+
321+
// Import the VCN
322+
if result = RunCommand(t, path, fmt.Sprintf("%s import -state=%v oci_core_vcn.vcn1 %v", tfBin, examplesTestStateFile, vcnId)); !result {
323+
goto cleanup
324+
}
325+
326+
// Plan should show no differences.
327+
if result = RunCommand(t, path, fmt.Sprintf("%s plan -detailed-exitcode -state=%v", tfBin, examplesTestStateFile)); !result {
328+
goto cleanup
329+
}
330+
331+
cleanup:
332+
// Regardless of the result, attempt to destroy.
333+
if RunCommand(t, path, fmt.Sprintf("%s destroy -force -state=%v", tfBin, examplesTestStateFile)) {
334+
// Only remove the state file if destroy was successful. Otherwise, leave it in place so that further
335+
// cleanup can be done manually.
336+
result = RunCommand(t, path, fmt.Sprintf("rm %v*", examplesTestStateFile)) && result
337+
} else {
338+
result = false
339+
}
340+
341+
if !result {
342+
break
343+
}
344+
}
345+
346+
return result
347+
}
348+
252349
func RunCommand(t *testing.T, path, command string) bool {
253-
return runCommandWithOutputOptions(t, path, command, false)
350+
_, result := runCommandWithOutputOptions(t, path, command, false)
351+
return result
254352
}
255353

256-
func runCommandWithOutputOptions(t *testing.T, path, command string, displayOutputOnSuccess bool) bool {
354+
func runCommandWithOutputOptions(t *testing.T, path, command string, displayOutputOnSuccess bool) ([]byte, bool) {
257355
log.Printf("Running command '%v' at %v", command, path)
258356

259357
env := make([]string, len(examplesTestAllowedEnvironmentVariables))
@@ -269,12 +367,12 @@ func runCommandWithOutputOptions(t *testing.T, path, command string, displayOutp
269367
if err != nil {
270368
log.Printf("Error: Command Failed. Output: %s", output)
271369
t.Errorf("Error running command %v at %v: %v", command, path, err)
272-
return false
370+
return output, false
273371
}
274372

275373
if displayOutputOnSuccess {
276374
log.Printf("Output: %s", output)
277375
}
278376

279-
return true
377+
return output, true
280378
}

0 commit comments

Comments
 (0)