From e116744bd2f66cdc938f54f4b686c3233f636c56 Mon Sep 17 00:00:00 2001 From: The Magician Date: Wed, 2 Oct 2019 12:35:13 -0700 Subject: [PATCH] google_project_services is deprecated - use google_project_service (#4599) Signed-off-by: Modular Magician --- google/resource_google_project.go | 67 ++++++++ google/resource_google_project_service.go | 29 ++++ google/resource_google_project_services.go | 175 +++++---------------- google/resource_kms_crypto_key_iam_test.go | 50 +++--- google/resource_kms_crypto_key_test.go | 45 ++---- google/resource_kms_key_ring_iam_test.go | 66 ++++---- google/resource_kms_key_ring_test.go | 14 +- 7 files changed, 214 insertions(+), 232 deletions(-) diff --git a/google/resource_google_project.go b/google/resource_google_project.go index cb884e9b2fd..fff44dfed13 100644 --- a/google/resource_google_project.go +++ b/google/resource_google_project.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "google.golang.org/api/cloudbilling/v1" "google.golang.org/api/cloudresourcemanager/v1" @@ -579,3 +580,69 @@ func readGoogleProject(d *schema.ResourceData, config *Config) (*cloudresourcema }, d.Timeout(schema.TimeoutRead)) return p, err } + +// Enables services. WARNING: Use BatchRequestEnableServices for better batching if possible. +func enableServiceUsageProjectServices(services []string, project string, config *Config, timeout time.Duration) error { + // ServiceUsage does not allow more than 20 services to be enabled per + // batchEnable API call. See + // https://cloud.google.com/service-usage/docs/reference/rest/v1/services/batchEnable + for i := 0; i < len(services); i += maxServiceUsageBatchSize { + j := i + maxServiceUsageBatchSize + if j > len(services) { + j = len(services) + } + nextBatch := services[i:j] + if len(nextBatch) == 0 { + // All batches finished, return. + return nil + } + + if err := doEnableServicesRequest(nextBatch, project, config, timeout); err != nil { + return err + } + log.Printf("[DEBUG] Finished enabling next batch of %d project services: %+v", len(nextBatch), nextBatch) + } + + log.Printf("[DEBUG] Verifying that all services are enabled") + return waitForServiceUsageEnabledServices(services, project, config, timeout) +} + +// waitForServiceUsageEnabledServices doesn't resend enable requests - it just +// waits for service enablement status to propagate. Essentially, it waits until +// all services show up as enabled when listing services on the project. +func waitForServiceUsageEnabledServices(services []string, project string, config *Config, timeout time.Duration) error { + missing := make([]string, 0, len(services)) + delay := time.Duration(0) + interval := time.Second + err := retryTimeDuration(func() error { + // Get the list of services that are enabled on the project + enabledServices, err := listCurrentlyEnabledServices(project, config, timeout) + if err != nil { + return err + } + + missing := make([]string, 0, len(services)) + for _, s := range services { + if _, ok := enabledServices[s]; !ok { + missing = append(missing, s) + } + } + if len(missing) > 0 { + log.Printf("[DEBUG] waiting %v before reading project %s services...", delay, project) + time.Sleep(delay) + delay += interval + interval += delay + + // Spoof a googleapi Error so retryTime will try again + return &googleapi.Error{ + Code: 503, + Message: fmt.Sprintf("The service(s) %q are still being enabled for project %s. This isn't a real API error, this is just eventual consistency.", missing, project), + } + } + return nil + }, timeout) + if err != nil { + return errwrap.Wrap(err, fmt.Errorf("failed to enable some service(s) %q for project %s", missing, project)) + } + return nil +} diff --git a/google/resource_google_project_service.go b/google/resource_google_project_service.go index 93f7023d663..e0991b71358 100644 --- a/google/resource_google_project_service.go +++ b/google/resource_google_project_service.go @@ -11,6 +11,12 @@ import ( "time" ) +var ignoredProjectServices = []string{"dataproc-control.googleapis.com", "source.googleapis.com", "stackdriverprovisioning.googleapis.com"} + +// These services can only be enabled as a side-effect of enabling other services, +// so don't bother storing them in the config or using them for diffing. +var ignoredProjectServicesSet = golangSetFromStringSlice(ignoredProjectServices) + func resourceGoogleProjectService() *schema.Resource { return &schema.Resource{ Create: resourceGoogleProjectServiceCreate, @@ -171,3 +177,26 @@ func isServiceEnabled(project, serviceName string, config *Config) (bool, error) } return srv.State == "ENABLED", nil } + +// Disables a project service. +func disableServiceUsageProjectService(service, project string, d *schema.ResourceData, config *Config, disableDependentServices bool) error { + err := retryTimeDuration(func() error { + name := fmt.Sprintf("projects/%s/services/%s", project, service) + sop, err := config.clientServiceUsage.Services.Disable(name, &serviceusage.DisableServiceRequest{ + DisableDependentServices: disableDependentServices, + }).Do() + if err != nil { + return err + } + // Wait for the operation to complete + waitErr := serviceUsageOperationWait(config, sop, "api to disable") + if waitErr != nil { + return waitErr + } + return nil + }, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return fmt.Errorf("Error disabling service %q for project %q: %v", service, project, err) + } + return nil +} diff --git a/google/resource_google_project_services.go b/google/resource_google_project_services.go index 0141f64c781..975f29915cb 100644 --- a/google/resource_google_project_services.go +++ b/google/resource_google_project_services.go @@ -13,12 +13,6 @@ import ( const maxServiceUsageBatchSize = 20 -var ignoredProjectServices = []string{"dataproc-control.googleapis.com", "source.googleapis.com", "stackdriverprovisioning.googleapis.com"} - -// These services can only be enabled as a side-effect of enabling other services, -// so don't bother storing them in the config or using them for diffing. -var ignoredProjectServicesSet = golangSetFromStringSlice(ignoredProjectServices) - func resourceGoogleProjectServices() *schema.Resource { return &schema.Resource{ Create: resourceGoogleProjectServicesCreateUpdate, @@ -164,135 +158,6 @@ func setServiceUsageProjectEnabledServices(services []string, project string, d return nil } -// Disables a project service. -func disableServiceUsageProjectService(service, project string, d *schema.ResourceData, config *Config, disableDependentServices bool) error { - err := retryTimeDuration(func() error { - name := fmt.Sprintf("projects/%s/services/%s", project, service) - sop, err := config.clientServiceUsage.Services.Disable(name, &serviceusage.DisableServiceRequest{ - DisableDependentServices: disableDependentServices, - }).Do() - if err != nil { - return err - } - // Wait for the operation to complete - waitErr := serviceUsageOperationWait(config, sop, "api to disable") - if waitErr != nil { - return waitErr - } - return nil - }, d.Timeout(schema.TimeoutDelete)) - if err != nil { - return fmt.Errorf("Error disabling service %q for project %q: %v", service, project, err) - } - return nil -} - -// Retrieve a project's services from the API -func listCurrentlyEnabledServices(project string, config *Config, timeout time.Duration) (map[string]struct{}, error) { - // Verify project for services still exists - p, err := config.clientResourceManager.Projects.Get(project).Do() - if err != nil { - return nil, err - } - if p.LifecycleState == "DELETE_REQUESTED" { - // Construct a 404 error for handleNotFoundError - return nil, &googleapi.Error{ - Code: 404, - Message: "Project deletion was requested", - } - } - - log.Printf("[DEBUG] Listing enabled services for project %s", project) - apiServices := make(map[string]struct{}) - err = retryTimeDuration(func() error { - ctx := context.Background() - return config.clientServiceUsage.Services. - List(fmt.Sprintf("projects/%s", project)). - Fields("services/name,nextPageToken"). - Filter("state:ENABLED"). - Pages(ctx, func(r *serviceusage.ListServicesResponse) error { - for _, v := range r.Services { - // services are returned as "projects/PROJECT/services/NAME" - name := GetResourceNameFromSelfLink(v.Name) - if _, ok := ignoredProjectServicesSet[name]; !ok { - apiServices[name] = struct{}{} - } - } - return nil - }) - }, timeout) - if err != nil { - return nil, errwrap.Wrapf(fmt.Sprintf("Failed to list enabled services for project %s: {{err}}", project), err) - } - return apiServices, nil -} - -// Enables services. WARNING: Use BatchRequestEnableServices for better batching if possible. -func enableServiceUsageProjectServices(services []string, project string, config *Config, timeout time.Duration) error { - // ServiceUsage does not allow more than 20 services to be enabled per - // batchEnable API call. See - // https://cloud.google.com/service-usage/docs/reference/rest/v1/services/batchEnable - for i := 0; i < len(services); i += maxServiceUsageBatchSize { - j := i + maxServiceUsageBatchSize - if j > len(services) { - j = len(services) - } - nextBatch := services[i:j] - if len(nextBatch) == 0 { - // All batches finished, return. - return nil - } - - if err := doEnableServicesRequest(nextBatch, project, config, timeout); err != nil { - return err - } - log.Printf("[DEBUG] Finished enabling next batch of %d project services: %+v", len(nextBatch), nextBatch) - } - - log.Printf("[DEBUG] Verifying that all services are enabled") - return waitForServiceUsageEnabledServices(services, project, config, timeout) -} - -// waitForServiceUsageEnabledServices doesn't resend enable requests - it just -// waits for service enablement status to propagate. Essentially, it waits until -// all services show up as enabled when listing services on the project. -func waitForServiceUsageEnabledServices(services []string, project string, config *Config, timeout time.Duration) error { - missing := make([]string, 0, len(services)) - delay := time.Duration(0) - interval := time.Second - err := retryTimeDuration(func() error { - // Get the list of services that are enabled on the project - enabledServices, err := listCurrentlyEnabledServices(project, config, timeout) - if err != nil { - return err - } - - missing := make([]string, 0, len(services)) - for _, s := range services { - if _, ok := enabledServices[s]; !ok { - missing = append(missing, s) - } - } - if len(missing) > 0 { - log.Printf("[DEBUG] waiting %v before reading project %s services...", delay, project) - time.Sleep(delay) - delay += interval - interval += delay - - // Spoof a googleapi Error so retryTime will try again - return &googleapi.Error{ - Code: 503, - Message: fmt.Sprintf("The service(s) %q are still being enabled for project %s. This isn't a real API error, this is just eventual consistency.", missing, project), - } - } - return nil - }, timeout) - if err != nil { - return errwrap.Wrap(err, fmt.Errorf("failed to enable some service(s) %q for project %s", missing, project)) - } - return nil -} - func doEnableServicesRequest(services []string, project string, config *Config, timeout time.Duration) error { var op *serviceusage.Operation @@ -355,3 +220,43 @@ func expandServiceUsageProjectServicesServices(v interface{}, d TerraformResourc } return convertStringArr(v.(*schema.Set).List()), nil } + +// Retrieve a project's services from the API +func listCurrentlyEnabledServices(project string, config *Config, timeout time.Duration) (map[string]struct{}, error) { + // Verify project for services still exists + p, err := config.clientResourceManager.Projects.Get(project).Do() + if err != nil { + return nil, err + } + if p.LifecycleState == "DELETE_REQUESTED" { + // Construct a 404 error for handleNotFoundError + return nil, &googleapi.Error{ + Code: 404, + Message: "Project deletion was requested", + } + } + + log.Printf("[DEBUG] Listing enabled services for project %s", project) + apiServices := make(map[string]struct{}) + err = retryTimeDuration(func() error { + ctx := context.Background() + return config.clientServiceUsage.Services. + List(fmt.Sprintf("projects/%s", project)). + Fields("services/name,nextPageToken"). + Filter("state:ENABLED"). + Pages(ctx, func(r *serviceusage.ListServicesResponse) error { + for _, v := range r.Services { + // services are returned as "projects/PROJECT/services/NAME" + name := GetResourceNameFromSelfLink(v.Name) + if _, ok := ignoredProjectServicesSet[name]; !ok { + apiServices[name] = struct{}{} + } + } + return nil + }) + }, timeout) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Failed to list enabled services for project %s: {{err}}", project), err) + } + return apiServices, nil +} diff --git a/google/resource_kms_crypto_key_iam_test.go b/google/resource_kms_crypto_key_iam_test.go index f5ffc09ecd8..a797521d8e7 100644 --- a/google/resource_kms_crypto_key_iam_test.go +++ b/google/resource_kms_crypto_key_iam_test.go @@ -181,24 +181,24 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Crypto Key Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "us-central1" name = "%s" } @@ -225,30 +225,30 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Crypto Key Iam Testing Account" } resource "google_service_account" "test_account_2" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s-2" display_name = "Kms Crypto Key Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "us-central1" name = "%s" } @@ -278,24 +278,24 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Crypto Key Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "us-central1" name = "%s" } diff --git a/google/resource_kms_crypto_key_test.go b/google/resource_kms_crypto_key_test.go index 227f62f2f0c..7307dfa7b3e 100644 --- a/google/resource_kms_crypto_key_test.go +++ b/google/resource_kms_crypto_key_test.go @@ -371,16 +371,13 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - - services = [ - "cloudkms.googleapis.com", - ] + service = "cloudkms.googleapis.com" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.acceptance.project}" + project = "${google_project_service.acceptance.project}" name = "%s" location = "us-central1" } @@ -404,16 +401,13 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - - services = [ - "cloudkms.googleapis.com", - ] + service = "cloudkms.googleapis.com" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.acceptance.project}" + project = "${google_project_service.acceptance.project}" name = "%s" location = "us-central1" } @@ -435,16 +429,13 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - - services = [ - "cloudkms.googleapis.com", - ] + service = "cloudkms.googleapis.com" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.acceptance.project}" + project = "${google_project_service.acceptance.project}" name = "%s" location = "us-central1" } @@ -465,16 +456,13 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - - services = [ - "cloudkms.googleapis.com", - ] + service = "cloudkms.googleapis.com" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.acceptance.project}" + project = "${google_project_service.acceptance.project}" name = "%s" location = "us-central1" } @@ -500,16 +488,13 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - - services = [ - "cloudkms.googleapis.com", - ] + service = "cloudkms.googleapis.com" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.acceptance.project}" + project = "${google_project_service.acceptance.project}" name = "%s" location = "us-central1" } diff --git a/google/resource_kms_key_ring_iam_test.go b/google/resource_kms_key_ring_iam_test.go index c492560a98a..2f884db6c12 100644 --- a/google/resource_kms_key_ring_iam_test.go +++ b/google/resource_kms_key_ring_iam_test.go @@ -173,24 +173,24 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Key Ring Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "us-central1" name = "%s" } @@ -212,30 +212,30 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Key Ring Iam Testing Account" } resource "google_service_account" "test_account_2" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s-2" display_name = "Kms Key Ring Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "%s" name = "%s" } @@ -260,24 +260,24 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Key Ring Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "%s" name = "%s" } @@ -299,24 +299,24 @@ resource "google_project" "test_project" { billing_account = "%s" } -resource "google_project_services" "test_project" { +resource "google_project_service" "kms" { project = "${google_project.test_project.project_id}" + service = "cloudkms.googleapis.com" +} - services = [ - "cloudkms.googleapis.com", - "iam.googleapis.com", - "iamcredentials.googleapis.com", - ] +resource "google_project_service" "iam" { + project = "${google_project_service.kms.project}" + service = "iam.googleapis.com" } resource "google_service_account" "test_account" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" account_id = "%s" display_name = "Kms Key Ring Iam Testing Account" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.test_project.project}" + project = "${google_project_service.iam.project}" location = "%s" name = "%s" } diff --git a/google/resource_kms_key_ring_test.go b/google/resource_kms_key_ring_test.go index 86b7f6817f2..b0c5a087c80 100644 --- a/google/resource_kms_key_ring_test.go +++ b/google/resource_kms_key_ring_test.go @@ -129,15 +129,13 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - services = [ - "cloudkms.googleapis.com" - ] + service = "cloudkms.googleapis.com" } resource "google_kms_key_ring" "key_ring" { - project = "${google_project_services.acceptance.project}" + project = "${google_project_service.acceptance.project}" name = "%s" location = "us-central1" } @@ -153,11 +151,9 @@ resource "google_project" "acceptance" { billing_account = "%s" } -resource "google_project_services" "acceptance" { +resource "google_project_service" "acceptance" { project = "${google_project.acceptance.project_id}" - services = [ - "cloudkms.googleapis.com" - ] + service = "cloudkms.googleapis.com" } `, projectId, projectId, projectOrg, projectBillingAccount) }