-
Notifications
You must be signed in to change notification settings - Fork 8
[AV-127651] Add legacy backup/schedule + snapshot schedule acceptance tests; harden bucket setup #583
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
Merged
Merged
[AV-127651] Add legacy backup/schedule + snapshot schedule acceptance tests; harden bucket setup #583
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
23c4eee
[AV-127651] Add acceptance tests for legacy backup, backup schedule, …
panigrahisubhrajit c90e059
[AV-127651] Validate, discover, or create bucket in acceptance test s…
panigrahisubhrajit 5f8d8d3
[AV-127651] Fix golangci-lint: errorlint and nestif
panigrahisubhrajit d4f5e98
[AV-127651] Add snapshot cluster env vars for forward compat
panigrahisubhrajit b7e68b2
[AV-127651] Drop snapshot env-var plumbing from this branch
panigrahisubhrajit 8c0f461
[AV-127651] Make TestAccDatasourceBackups read-only to avoid 60m timeout
panigrahisubhrajit 8dbc8c1
[AV-127651] Fold backups data source into TestAccBackupResource
panigrahisubhrajit f23d2fd
[AV-127651] Fix 4 P1 issues flagged by factory-droid
panigrahisubhrajit aeb08ae
[AV-127651] Recover from backend 500 on cluster creation (AV-129960)
panigrahisubhrajit d85bd99
[AV-127651] Read TF_VAR_bucket_name env var in test setup
panigrahisubhrajit b23ee1e
Merge origin/main into AV-127651-fix-legacy-backup
panigrahisubhrajit 8d01bc4
[AV-127651] Track bucket lifecycle; restrict 5xx adoption fallback
panigrahisubhrajit b8e6d07
[AV-127651] Assert datasource returns created backup's ID via AttrPair
panigrahisubhrajit b0884d6
[AV-127651] Only mark cluster for cleanup when createCluster succeede…
panigrahisubhrajit c1696f9
[AV-127651] Reuse existing project on quota error so concurrent CI ru…
panigrahisubhrajit 5259390
[AV-127651] Scan full backup list to verify created backup is present
panigrahisubhrajit 95daa2f
[AV-127651] Serialize singleton-schedule tests; assert copy_to_regions
panigrahisubhrajit 2ab7540
[AV-127651] Fix errcheck lint; restore ParallelTest; assert copy_to_r…
panigrahisubhrajit 7d2f790
[AV-127651] Set globalBucketCreated before bucketWait to avoid leaks
panigrahisubhrajit aee7730
[AV-127651] Bump backup wait timeout to 90m, poll every 30s
panigrahisubhrajit 56937ab
ci: retrigger CodeQL Default Setup (GitHub codeload.github.com 429)
panigrahisubhrajit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| package acceptance_tests | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
| "regexp" | ||
| "strconv" | ||
| "testing" | ||
|
|
||
| "github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
| "github.com/hashicorp/terraform-plugin-testing/terraform" | ||
|
|
||
| "github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/api" | ||
| backupapi "github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/api/backup" | ||
| "github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/errors" | ||
| providerschema "github.com/couchbasecloud/terraform-provider-couchbase-capella/internal/schema" | ||
| ) | ||
|
|
||
| // TestAccBackupResource creates a backup, verifies its attributes, then | ||
| // layers the couchbase-capella_backups data source on top to confirm the | ||
| // backups list returns the just-created backup. The data source step | ||
| // intentionally reuses the same backup resource (no new backup is created) | ||
| // because Capella's legacy bucket backup endpoint serialises manual | ||
| // backups per bucket and a back-to-back second backup gets stuck in a | ||
| // non-terminal state until the per-bucket spacing window elapses. | ||
| func TestAccBackupResource(t *testing.T) { | ||
| resourceName := randomStringWithPrefix("tf_acc_backup_") | ||
| resourceReference := "couchbase-capella_backup." + resourceName | ||
| dsName := randomStringWithPrefix("tf_acc_backups_ds_") | ||
| dsReference := "data.couchbase-capella_backups." + dsName | ||
|
|
||
| resource.ParallelTest(t, resource.TestCase{ | ||
| ProtoV6ProviderFactories: globalProtoV6ProviderFactory, | ||
| Steps: []resource.TestStep{ | ||
| { | ||
| Config: testAccBackupResourceConfig(resourceName), | ||
| Check: resource.ComposeAggregateTestCheckFunc( | ||
| testAccExistsBackupResource(t, resourceReference), | ||
| resource.TestCheckResourceAttr(resourceReference, "organization_id", globalOrgId), | ||
| resource.TestCheckResourceAttr(resourceReference, "project_id", globalProjectId), | ||
| resource.TestCheckResourceAttr(resourceReference, "cluster_id", globalClusterId), | ||
| resource.TestCheckResourceAttr(resourceReference, "bucket_id", globalBucketId), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "id"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "cycle_id"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "date"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "status"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "method"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "bucket_name"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "source"), | ||
| resource.TestCheckResourceAttrSet(resourceReference, "cloud_provider"), | ||
| ), | ||
| }, | ||
| { | ||
| Config: testAccBackupWithDatasourceConfig(resourceName, dsName), | ||
| Check: resource.ComposeAggregateTestCheckFunc( | ||
| resource.TestCheckResourceAttr(dsReference, "organization_id", globalOrgId), | ||
| resource.TestCheckResourceAttr(dsReference, "project_id", globalProjectId), | ||
| resource.TestCheckResourceAttr(dsReference, "cluster_id", globalClusterId), | ||
| resource.TestCheckResourceAttr(dsReference, "bucket_id", globalBucketId), | ||
| testAccCheckDataSourceContainsBackup(dsReference, resourceReference), | ||
| ), | ||
|
panigrahisubhrajit marked this conversation as resolved.
panigrahisubhrajit marked this conversation as resolved.
panigrahisubhrajit marked this conversation as resolved.
|
||
| }, | ||
| { | ||
| ResourceName: resourceReference, | ||
| ImportStateIdFunc: generateBackupImportIdForResource(resourceReference), | ||
| ImportState: true, | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| func TestAccBackupResourceInvalidBucket(t *testing.T) { | ||
| resourceName := randomStringWithPrefix("tf_acc_backup_invalid_") | ||
|
|
||
| resource.ParallelTest(t, resource.TestCase{ | ||
| ProtoV6ProviderFactories: globalProtoV6ProviderFactory, | ||
| Steps: []resource.TestStep{ | ||
| { | ||
| Config: testAccBackupResourceConfigWithBucketID(resourceName, "00000000-0000-0000-0000-000000000000"), | ||
| ExpectError: regexp.MustCompile("Error getting latest bucket backup|There is an error during backup creation"), | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| func TestAccBackupResourceInvalidProject(t *testing.T) { | ||
| resourceName := randomStringWithPrefix("tf_acc_backup_invalid_proj_") | ||
|
|
||
| resource.ParallelTest(t, resource.TestCase{ | ||
| ProtoV6ProviderFactories: globalProtoV6ProviderFactory, | ||
| Steps: []resource.TestStep{ | ||
| { | ||
| Config: fmt.Sprintf(` | ||
| %[1]s | ||
|
|
||
| resource "couchbase-capella_backup" "%[2]s" { | ||
| organization_id = "%[3]s" | ||
| project_id = "00000000-0000-0000-0000-000000000000" | ||
| cluster_id = "%[4]s" | ||
| bucket_id = "%[5]s" | ||
| } | ||
| `, globalProviderBlock, resourceName, globalOrgId, globalClusterId, globalBucketId), | ||
| ExpectError: regexp.MustCompile("Error getting latest bucket backup|There is an error during backup creation"), | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| func TestAccBackupResourceRestoreTimesOnCreate(t *testing.T) { | ||
| resourceName := randomStringWithPrefix("tf_acc_backup_invalid_rt_") | ||
|
|
||
| resource.ParallelTest(t, resource.TestCase{ | ||
| ProtoV6ProviderFactories: globalProtoV6ProviderFactory, | ||
| Steps: []resource.TestStep{ | ||
| { | ||
| Config: fmt.Sprintf(` | ||
| %[1]s | ||
|
|
||
| resource "couchbase-capella_backup" "%[2]s" { | ||
| organization_id = "%[3]s" | ||
| project_id = "%[4]s" | ||
| cluster_id = "%[5]s" | ||
| bucket_id = "%[6]s" | ||
| restore_times = 1 | ||
| } | ||
| `, globalProviderBlock, resourceName, globalOrgId, globalProjectId, globalClusterId, globalBucketId), | ||
| ExpectError: regexp.MustCompile("restore times must not be set while create backup"), | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| func testAccBackupResourceConfig(resourceName string) string { | ||
| return testAccBackupResourceConfigWithBucketID(resourceName, globalBucketId) | ||
| } | ||
|
|
||
| func testAccBackupResourceConfigWithBucketID(resourceName, bucketID string) string { | ||
| return fmt.Sprintf(` | ||
| %[1]s | ||
|
|
||
| resource "couchbase-capella_backup" "%[2]s" { | ||
| organization_id = "%[3]s" | ||
| project_id = "%[4]s" | ||
| cluster_id = "%[5]s" | ||
| bucket_id = "%[6]s" | ||
| } | ||
| `, globalProviderBlock, resourceName, globalOrgId, globalProjectId, globalClusterId, bucketID) | ||
| } | ||
|
|
||
| func testAccBackupWithDatasourceConfig(resourceName, dsName string) string { | ||
| return fmt.Sprintf(` | ||
| %[1]s | ||
|
|
||
| resource "couchbase-capella_backup" "%[2]s" { | ||
| organization_id = "%[3]s" | ||
| project_id = "%[4]s" | ||
| cluster_id = "%[5]s" | ||
| bucket_id = "%[6]s" | ||
| } | ||
|
|
||
| data "couchbase-capella_backups" "%[7]s" { | ||
| organization_id = "%[3]s" | ||
| project_id = "%[4]s" | ||
| cluster_id = "%[5]s" | ||
| bucket_id = "%[6]s" | ||
| depends_on = [couchbase-capella_backup.%[2]s] | ||
| } | ||
| `, globalProviderBlock, resourceName, globalOrgId, globalProjectId, globalClusterId, globalBucketId, dsName) | ||
| } | ||
|
|
||
| func generateBackupImportIdForResource(resourceReference string) resource.ImportStateIdFunc { | ||
| return func(state *terraform.State) (string, error) { | ||
| var rawState map[string]string | ||
| for _, m := range state.Modules { | ||
| if len(m.Resources) > 0 { | ||
| if v, ok := m.Resources[resourceReference]; ok { | ||
| rawState = v.Primary.Attributes | ||
| } | ||
| } | ||
| } | ||
| return fmt.Sprintf("id=%s,cluster_id=%s,project_id=%s,organization_id=%s", | ||
| rawState["id"], rawState["cluster_id"], rawState["project_id"], rawState["organization_id"]), nil | ||
| } | ||
| } | ||
|
|
||
| func retrieveBackupFromServer(data *providerschema.Data, organizationId, projectId, clusterId, backupId string) error { | ||
| url := fmt.Sprintf("%s/v4/organizations/%s/projects/%s/clusters/%s/backups/%s", | ||
| data.HostURL, organizationId, projectId, clusterId, backupId) | ||
| cfg := api.EndpointCfg{Url: url, Method: http.MethodGet, SuccessStatus: http.StatusOK} | ||
| response, err := data.ClientV1.ExecuteWithRetry( | ||
| context.Background(), | ||
| cfg, | ||
| nil, | ||
| data.Token, | ||
| nil, | ||
| ) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| backupResp := backupapi.GetBackupResponse{} | ||
| if err := json.Unmarshal(response.Body, &backupResp); err != nil { | ||
| return err | ||
| } | ||
| if backupResp.Id == "" { | ||
| return errors.ErrNotFound | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func testAccExistsBackupResource(t *testing.T, resourceReference string) resource.TestCheckFunc { | ||
| return func(s *terraform.State) error { | ||
| var rawState map[string]string | ||
| for _, m := range s.Modules { | ||
| if len(m.Resources) > 0 { | ||
| if v, ok := m.Resources[resourceReference]; ok { | ||
| rawState = v.Primary.Attributes | ||
| } | ||
| } | ||
| } | ||
| data := newTestClient(t) | ||
| return retrieveBackupFromServer(data, rawState["organization_id"], rawState["project_id"], rawState["cluster_id"], rawState["id"]) | ||
| } | ||
| } | ||
|
|
||
| // testAccCheckDataSourceContainsBackup verifies that the backups data source | ||
| // contains the specific backup created by resourceReference, regardless of | ||
| // its position in the list (older backups may appear at lower indices). | ||
| func testAccCheckDataSourceContainsBackup(dsReference, resourceReference string) resource.TestCheckFunc { | ||
| return func(s *terraform.State) error { | ||
| ds := s.RootModule().Resources[dsReference] | ||
| if ds == nil { | ||
| return fmt.Errorf("datasource %s not found in state", dsReference) | ||
| } | ||
| res := s.RootModule().Resources[resourceReference] | ||
| if res == nil { | ||
| return fmt.Errorf("resource %s not found in state", resourceReference) | ||
| } | ||
| expectedID := res.Primary.Attributes["id"] | ||
| count, _ := strconv.Atoi(ds.Primary.Attributes["data.#"]) | ||
| for i := 0; i < count; i++ { | ||
| if ds.Primary.Attributes[fmt.Sprintf("data.%d.id", i)] == expectedID { | ||
| return nil | ||
| } | ||
| } | ||
| return fmt.Errorf("datasource %s does not contain backup id=%s (checked %d items)", dsReference, expectedID, count) | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.