-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: skip destroy for schematics resources #703
base: main
Are you sure you want to change the base?
Changes from 2 commits
d01de0b
e9ea313
827f691
014f158
49eb4f1
b393cda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,16 +17,19 @@ import ( | |
// 3. configure supplied test variables in workspace | ||
// 4. run PLAN/APPLY/DESTROY steps on workspace to provision and destroy resources | ||
// 5. delete the test workspace | ||
func (options *TestSchematicOptions) RunSchematicTest() error { | ||
|
||
// create new schematic service with authenticator | ||
var svc *SchematicsTestService | ||
|
||
func (options *TestSchematicOptions) RunSchematicTest() (*SchematicsTestService, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this change (creating package global for the service pointer and returning it from the Run function) is necessary. The original design was to create a new service object, only if not initially provided in options struct, and then store that pointer in the options struct to be used later if needed. The change that is likely needed here would be to make I would remove the global |
||
|
||
// WORKSPACE SETUP | ||
// In this section of the test we are setting up the workspace. | ||
// Any errors in this section will be considerd "unexpected" and returned to the calling unit test | ||
// to short-circuit and quit the test. | ||
// The official start of the unit test, with assertions, will begin AFTER workspace is properly created. | ||
|
||
// create new schematic service with authenticator, set pointer of service in options for use later | ||
var svc *SchematicsTestService | ||
// set pointer of service in options for use later | ||
if options.schematicsTestSvc == nil { | ||
svc = &SchematicsTestService{} | ||
} else { | ||
|
@@ -47,59 +50,59 @@ func (options *TestSchematicOptions) RunSchematicTest() error { | |
} else { | ||
svcErr := svc.InitializeSchematicsService() | ||
if svcErr != nil { | ||
return fmt.Errorf("error creating schematics sdk service: %w", svcErr) | ||
return nil, fmt.Errorf("error creating schematics sdk service: %w", svcErr) | ||
} | ||
} | ||
|
||
// get the root path of this project | ||
projectPath, pathErr := common.GitRootPath(".") | ||
if pathErr != nil { | ||
return fmt.Errorf("error getting root path of git project: %w", pathErr) | ||
return nil, fmt.Errorf("error getting root path of git project: %w", pathErr) | ||
} | ||
|
||
// create a new tar file for the project | ||
options.Testing.Log("[SCHEMATICS] Creating TAR file") | ||
tarballName, tarballErr := CreateSchematicTar(projectPath, &options.TarIncludePatterns) | ||
if tarballErr != nil { | ||
return fmt.Errorf("error creating tar file: %w", tarballErr) | ||
return nil, fmt.Errorf("error creating tar file: %w", tarballErr) | ||
} | ||
defer os.Remove(tarballName) // just to cleanup | ||
|
||
// create a new empty workspace, resulting in "draft" status | ||
options.Testing.Log("[SCHEMATICS] Creating Test Workspace") | ||
_, wsErr := svc.CreateTestWorkspace(options.Prefix, options.ResourceGroup, options.TemplateFolder, options.TerraformVersion, options.Tags) | ||
if wsErr != nil { | ||
return fmt.Errorf("error creating new schematic workspace: %w", wsErr) | ||
return nil, fmt.Errorf("error creating new schematic workspace: %w", wsErr) | ||
} | ||
|
||
options.Testing.Logf("[SCHEMATICS] Workspace Created: %s (%s)", svc.WorkspaceName, svc.WorkspaceID) | ||
// can be used in error messages to repeat workspace name | ||
workspaceNameString := fmt.Sprintf("[ %s (%s) ]", svc.WorkspaceName, svc.WorkspaceID) | ||
|
||
// since workspace is now created, always call the teardown to remove | ||
defer testTearDown(svc, options) | ||
defer testTearDown(options) | ||
|
||
// upload the terraform code | ||
options.Testing.Log("[SCHEMATICS] Uploading TAR file") | ||
uploadErr := svc.UploadTarToWorkspace(tarballName) | ||
if uploadErr != nil { | ||
return fmt.Errorf("error uploading tar file to workspace: %w - %s", uploadErr, workspaceNameString) | ||
return nil, fmt.Errorf("error uploading tar file to workspace: %w - %s", uploadErr, workspaceNameString) | ||
} | ||
|
||
// -------- UPLOAD TAR FILE ---------- | ||
// find the tar upload job | ||
uploadJob, uploadJobErr := svc.FindLatestWorkspaceJobByName(SchematicsJobTypeUpload) | ||
if uploadJobErr != nil { | ||
return fmt.Errorf("error finding the upload tar action: %w - %s", uploadJobErr, workspaceNameString) | ||
return nil, fmt.Errorf("error finding the upload tar action: %w - %s", uploadJobErr, workspaceNameString) | ||
} | ||
// wait for it to finish | ||
uploadJobStatus, uploadJobStatusErr := svc.WaitForFinalJobStatus(*uploadJob.ActionID) | ||
if uploadJobStatusErr != nil { | ||
return fmt.Errorf("error waiting for upload of tar to finish: %w - %s", uploadJobStatusErr, workspaceNameString) | ||
return nil, fmt.Errorf("error waiting for upload of tar to finish: %w - %s", uploadJobStatusErr, workspaceNameString) | ||
} | ||
// check if complete | ||
if uploadJobStatus != SchematicsJobStatusCompleted { | ||
return fmt.Errorf("tar upload has failed with status %s - %s", uploadJobStatus, workspaceNameString) | ||
return nil, fmt.Errorf("tar upload has failed with status %s - %s", uploadJobStatus, workspaceNameString) | ||
} | ||
|
||
// ------ FINAL WORKSPACE CONFIG ------ | ||
|
@@ -109,7 +112,7 @@ func (options *TestSchematicOptions) RunSchematicTest() error { | |
options.Testing.Log("[SCHEMATICS] Updating Workspace Variablestore") | ||
updateErr := svc.UpdateTestTemplateVars(options.TerraformVars) | ||
if updateErr != nil { | ||
return fmt.Errorf("error updating template with Variablestore: %w - %s", updateErr, workspaceNameString) | ||
return nil, fmt.Errorf("error updating template with Variablestore: %w - %s", updateErr, workspaceNameString) | ||
} | ||
|
||
// TERRAFORM TESTING BEGINS | ||
|
@@ -143,45 +146,57 @@ func (options *TestSchematicOptions) RunSchematicTest() error { | |
} | ||
} | ||
|
||
// ------ DESTROY ------ | ||
// only run destroy if we had potentially created resources | ||
if svc.TerraformResourcesCreated { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you are moving the resource destroy from this block, you need to still replace it with your new TestTearDown() or similar. Keep in mind thought that in this test case there are TWO different destroy that take place:
The block removed here is covering item 1, the destroy of the applied resources, NOT the deletion of the workspace. Perhaps a short meeting or slack conversation can explain this better. |
||
// Check if "DO_NOT_DESTROY_ON_FAILURE" is set | ||
envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") | ||
if options.Testing.Failed() && strings.ToLower(envVal) == "true" { | ||
options.Testing.Log("[SCHEMATICS] Schematics APPLY failed. Debug the Test and delete resources manually.") | ||
} else { | ||
destroyResponse, destroyErr := svc.CreateDestroyJob() | ||
if assert.NoErrorf(options.Testing, destroyErr, "error creating DESTROY - %s", workspaceNameString) { | ||
options.Testing.Log("[SCHEMATICS] Starting DESTROY job ...") | ||
destroyJobStatus, destroyStatusErr := svc.WaitForFinalJobStatus(*destroyResponse.Activityid) | ||
if assert.NoErrorf(options.Testing, destroyStatusErr, "error waiting for DESTROY to finish - %s", workspaceNameString) { | ||
assert.Equalf(options.Testing, SchematicsJobStatusCompleted, destroyJobStatus, "DESTROY has failed with status %s - %s", destroyJobStatus, workspaceNameString) | ||
} | ||
} | ||
} | ||
} | ||
return svc, nil | ||
} | ||
|
||
return nil | ||
// Function to destroy all resources. Resources are not destroyed if tests failed and "DO_NOT_DESTROY_ON_FAILURE" environment variable is true. | ||
func (options *TestSchematicOptions) TestTearDown() { | ||
options.SkipTestTearDown = false | ||
defer testTearDown(options) | ||
} | ||
|
||
// testTearDown is a helper function, typically called via golang "defer", that will clean up and remove any existing resources that were | ||
// created for the test. | ||
// The removal of some resources may be influenced by certain conditions or optional settings. | ||
func testTearDown(svc *SchematicsTestService, options *TestSchematicOptions) { | ||
// ------ DELETE WORKSPACE ------ | ||
// only delete workspace if one of these is true: | ||
// * terraform hasn't been started yet | ||
// * no failures | ||
// * failed and DeleteWorkspaceOnFail is true | ||
if !svc.TerraformTestStarted || | ||
!options.Testing.Failed() || | ||
(options.Testing.Failed() && options.DeleteWorkspaceOnFail) { | ||
|
||
options.Testing.Log("[SCHEMATICS] Deleting Workspace") | ||
_, deleteWsErr := svc.DeleteWorkspace() | ||
if deleteWsErr != nil { | ||
options.Testing.Logf("[SCHEMATICS] WARNING: Schematics WORKSPACE DELETE failed! Remove manually if required. Name: %s (%s)", svc.WorkspaceName, svc.WorkspaceID) | ||
func testTearDown(options *TestSchematicOptions) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old You are replacing it and combining the destruction of applied resources AND the delete of the workspace. This may alter how this works, as you still need to call the destroy of resources in RunSchematicsTest if the skip is not set, and keep seperate the destruction of the workspace (which was done on purpose to leave workspace around on failed test due to logs only existing there). This might have to be split into two functions. Perhaps further explanation can be done on a schematics thread or short meeting. |
||
|
||
if !options.SkipTestTearDown { | ||
workspaceNameString := fmt.Sprintf("[ %s (%s) ]", svc.WorkspaceName, svc.WorkspaceID) | ||
// ------ DESTROY ------ | ||
// only run destroy if we had potentially created resources | ||
if svc.TerraformResourcesCreated { | ||
// Check if "DO_NOT_DESTROY_ON_FAILURE" is set | ||
envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") | ||
if options.Testing.Failed() && strings.ToLower(envVal) == "true" { | ||
options.Testing.Log("[SCHEMATICS] Schematics APPLY failed. Debug the Test and delete resources manually.") | ||
} else { | ||
destroyResponse, destroyErr := svc.CreateDestroyJob() | ||
if assert.NoErrorf(options.Testing, destroyErr, "error creating DESTROY - %s", workspaceNameString) { | ||
options.Testing.Log("[SCHEMATICS] Starting DESTROY job ...") | ||
destroyJobStatus, destroyStatusErr := svc.WaitForFinalJobStatus(*destroyResponse.Activityid) | ||
if assert.NoErrorf(options.Testing, destroyStatusErr, "error waiting for DESTROY to finish - %s", workspaceNameString) { | ||
assert.Equalf(options.Testing, SchematicsJobStatusCompleted, destroyJobStatus, "DESTROY has failed with status %s - %s", destroyJobStatus, workspaceNameString) | ||
} | ||
} | ||
} | ||
} | ||
|
||
// ------ DELETE WORKSPACE ------ | ||
// only delete workspace if one of these is true: | ||
// * terraform hasn't been started yet | ||
// * no failures | ||
// * failed and DeleteWorkspaceOnFail is true | ||
if !svc.TerraformTestStarted || | ||
!options.Testing.Failed() || | ||
(options.Testing.Failed() && options.DeleteWorkspaceOnFail) { | ||
|
||
options.Testing.Log("[SCHEMATICS] Deleting Workspace") | ||
_, deleteWsErr := svc.DeleteWorkspace() | ||
if deleteWsErr != nil { | ||
options.Testing.Logf("[SCHEMATICS] WARNING: Schematics WORKSPACE DELETE failed! Remove manually if required. Name: %s (%s)", svc.WorkspaceName, svc.WorkspaceID) | ||
} | ||
} | ||
} else { | ||
options.Testing.Log("Skipping automatic Test Teardown") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should only default to false if the value is not set in originalOptions (if it is set to true).
In golang, the default for a bool is already false, so setting this here is probably optional.