diff --git a/mmv1/products/vmwareengine/ExternalAccessRule.yaml b/mmv1/products/vmwareengine/ExternalAccessRule.yaml index 1d088ef0a775..990ab6c84c60 100644 --- a/mmv1/products/vmwareengine/ExternalAccessRule.yaml +++ b/mmv1/products/vmwareengine/ExternalAccessRule.yaml @@ -42,6 +42,8 @@ async: !ruby/object:Api::OpAsync import_format: ["{{%parent}}/externalAccessRules/{{name}}"] id_format: "{{parent}}/externalAccessRules/{{name}}" autogen_async: true +# There is a handwritten sweeper that provides a list of locations to sweep +skip_sweeper: true examples: - !ruby/object:Provider::Terraform::Examples diff --git a/mmv1/products/vmwareengine/ExternalAddress.yaml b/mmv1/products/vmwareengine/ExternalAddress.yaml index f6ba9cdc6bb9..6ac5848f90ca 100644 --- a/mmv1/products/vmwareengine/ExternalAddress.yaml +++ b/mmv1/products/vmwareengine/ExternalAddress.yaml @@ -46,6 +46,8 @@ import_format: ["{{%parent}}/externalAddresses/{{name}}"] id_format: "{{parent}}/externalAddresses/{{name}}" error_retry_predicates: ['transport_tpg.ExternalIpServiceNotActive'] autogen_async: true +# There is a handwritten sweeper that provides a list of locations to sweep +skip_sweeper: true examples: - !ruby/object:Provider::Terraform::Examples diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_sweeper.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_sweeper.go index 71afd7f546f9..3eb7ac7ae317 100644 --- a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_sweeper.go +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_sweeper.go @@ -2,9 +2,7 @@ package vmwareengine import ( "context" - "fmt" "log" - "strings" "testing" "github.com/hashicorp/terraform-provider-google/google/envvar" @@ -57,15 +55,26 @@ func testSweepVmwareengineCluster(region string) error { } log.Printf("[INFO][SWEEPER_LOG] looking for parent resources in location '%s'.", location) - privateCloudNames, err := listPrivateCloudsInLocation(d, config) + + parentResponseField := "privateClouds" + parentListUrlTemplate := "https://vmwareengine.googleapis.com/v1/projects/{{project}}/locations/{{location}}/privateClouds" + parentNames, err := sweeper.ListParentResourcesInLocation(d, config, parentListUrlTemplate, parentResponseField) if err != nil { log.Printf("[INFO][SWEEPER_LOG] error finding parental resources in location %s: %s", location, err) continue } - for _, parent := range privateCloudNames { + for _, parent := range parentNames { // `parent` will be string of form projects/my-project/locations/us-central1-a/privateClouds/my-cloud - listUrl := fmt.Sprintf("https://vmwareengine.googleapis.com/v1/projects/%s/clusters", parent) + // Change on each loop, so new value used in tpgresource.ReplaceVars + d.Set("parent", parent) + + listTemplate := "https://vmwareengine.googleapis.com/v1/{{parent}}/clusters" + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + continue + } res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ Config: config, @@ -134,41 +143,3 @@ func testSweepVmwareengineCluster(region string) error { } return nil } - -func listPrivateCloudsInLocation(d *tpgresource.ResourceDataMock, config *transport_tpg.Config) ([]string, error) { - listTemplate := strings.Split("https://vmwareengine.googleapis.com/v1/projects/{{project}}/locations/{{location}}/privateClouds", "?")[0] - listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) - return nil, err - } - - res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ - Config: config, - Method: "GET", - Project: config.Project, - RawURL: listUrl, - UserAgent: config.UserAgent, - }) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) - return nil, err - } - - resourceList, ok := res["privateClouds"] - if !ok { - log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") - return nil, fmt.Errorf("nothing found in response") - } - - rl := resourceList.([]interface{}) - privateCloudNames := []string{} - for _, r := range rl { - resource := r.(map[string]interface{}) - if name, ok := resource["name"]; ok { - privateCloudNames = append(privateCloudNames, name.(string)) - } - - } - return privateCloudNames, nil -} diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_sweeper.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_sweeper.go new file mode 100644 index 000000000000..620cb74c1420 --- /dev/null +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_access_rule_sweeper.go @@ -0,0 +1,145 @@ +package vmwareengine + +import ( + "context" + "log" + "testing" + + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/sweeper" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func init() { + sweeper.AddTestSweepers("VmwareengineExternalAccessRule", testSweepVmwareengineExternalAccessRule) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepVmwareengineExternalAccessRule(region string) error { + resourceName := "VmwareengineExternalAccessRule" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sweeper.SharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := envvar.GetTestBillingAccountFromEnv(t) + + // List of location values includes: + // * zones used for this resource type's acc tests in the past + // * the 'region' passed to the sweeper + locations := []string{region, "us-central1", "southamerica-west1", "me-west1"} + log.Printf("[INFO][SWEEPER_LOG] Sweeping will include these locations: %v.", locations) + for _, location := range locations { + + // Setup variables to replace in list template + d := &tpgresource.ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": location, + "location": location, + "zone": location, + "billing_account": billingId, + }, + } + + log.Printf("[INFO][SWEEPER_LOG] looking for parent resources in location '%s'.", location) + + parentResponseField := "networkPolicies" + parentListUrlTemplate := "https://vmwareengine.googleapis.com/v1/projects/{{project}}/locations/{{location}}/networkPolicies" + parentNames, err := sweeper.ListParentResourcesInLocation(d, config, parentListUrlTemplate, parentResponseField) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error finding parental resources in location %s: %s", location, err) + continue + } + + for _, parent := range parentNames { + + // `parent` will be string of form projects/my-project/locations/us-central1-a/privateClouds/my-cloud + // Change on each loop, so new value used in tpgresource.ReplaceVars + d.Set("parent", parent) + + listTemplate := "https://vmwareengine.googleapis.com/v1/{{parent}}/externalAccessRules" + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: config.Project, + RawURL: listUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["externalAccessRules"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := tpgresource.GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !sweeper.IsSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://vmwareengine.googleapis.com/v1/{{parent}}/externalAccessRules/{{name}}" + deleteUrl, err := tpgresource.ReplaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: config.Project, + RawURL: deleteUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + } + } + return nil +} diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_sweeper.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_sweeper.go new file mode 100644 index 000000000000..b26b26bfb9ae --- /dev/null +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_external_address_sweeper.go @@ -0,0 +1,145 @@ +package vmwareengine + +import ( + "context" + "log" + "testing" + + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/sweeper" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func init() { + sweeper.AddTestSweepers("VmwareengineExternalAddress", testSweepVmwareengineExternalAddress) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepVmwareengineExternalAddress(region string) error { + resourceName := "VmwareengineExternalAddress" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sweeper.SharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := envvar.GetTestBillingAccountFromEnv(t) + + // List of location values includes: + // * zones used for this resource type's acc tests in the past + // * the 'region' passed to the sweeper + locations := []string{region, "us-central1-a", "us-central1-b", "southamerica-west1-a", "southamerica-west1-b", "me-west1-a", "me-west1-b"} + log.Printf("[INFO][SWEEPER_LOG] Sweeping will include these locations: %v.", locations) + for _, location := range locations { + + // Setup variables to replace in list template + d := &tpgresource.ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": location, + "location": location, + "zone": location, + "billing_account": billingId, + "parent": "", // Set in loop below + }, + } + + log.Printf("[INFO][SWEEPER_LOG] looking for parent resources in location '%s'.", location) + + parentResponseField := "privateClouds" + parentListUrlTemplate := "https://vmwareengine.googleapis.com/v1/projects/{{project}}/locations/{{location}}/privateClouds" + parentNames, err := sweeper.ListParentResourcesInLocation(d, config, parentListUrlTemplate, parentResponseField) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error finding parental resources in location %s: %s", location, err) + continue + } + for _, parent := range parentNames { + + // `parent` will be string of form projects/my-project/locations/us-central1-a/privateClouds/my-cloud + // Change on each loop, so new value used in tpgresource.ReplaceVars + d.Set("parent", parent) + + listTemplate := "https://vmwareengine.googleapis.com/v1/{{parent}}/externalAddresses" + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + continue + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: config.Project, + RawURL: listUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + continue + } + + resourceList, ok := res["externalAddresses"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + continue + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + continue + } + + name := tpgresource.GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !sweeper.IsSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://vmwareengine.googleapis.com/v1/{{parent}}/externalAddresses/{{name}}" + deleteUrl, err := tpgresource.ReplaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + continue + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: config.Project, + RawURL: deleteUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + } + } + return nil +} diff --git a/mmv1/third_party/terraform/sweeper/gcp_sweeper.go b/mmv1/third_party/terraform/sweeper/gcp_sweeper.go index f128c65620cb..4cde53947aa8 100644 --- a/mmv1/third_party/terraform/sweeper/gcp_sweeper.go +++ b/mmv1/third_party/terraform/sweeper/gcp_sweeper.go @@ -4,12 +4,14 @@ import ( "encoding/hex" "fmt" "hash/crc32" + "log" "runtime" "strings" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" ) @@ -60,6 +62,45 @@ func IsSweepableTestResource(resourceName string) bool { return false } +// ListParentResourcesInLocation calls a provided list endpoint and returns the names of any resources found in the response. +// This function is intended to be used in sweepers where the resources being swept can only be found with knowledge about existing parental resources. +func ListParentResourcesInLocation(d *tpgresource.ResourceDataMock, config *transport_tpg.Config, listTemplate, responseField string) ([]string, error) { + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil, err + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: config.Project, + RawURL: listUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil, err + } + + resourceList, ok := res[responseField] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil, fmt.Errorf("nothing found in response") + } + + rl := resourceList.([]interface{}) + names := []string{} + for _, r := range rl { + resource := r.(map[string]interface{}) + if name, ok := resource["name"]; ok { + names = append(names, name.(string)) + } + + } + return names, nil +} + func AddTestSweepers(name string, sweeper func(region string) error) { _, filename, _, _ := runtime.Caller(0) hash := crc32.NewIEEE()