Skip to content

Commit e116744

Browse files
modular-magiciannat-henderson
authored andcommitted
google_project_services is deprecated - use google_project_service (#4599)
Signed-off-by: Modular Magician <[email protected]>
1 parent 95e4db5 commit e116744

7 files changed

+214
-232
lines changed

google/resource_google_project.go

+67
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/hashicorp/errwrap"
1112
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
1213
"google.golang.org/api/cloudbilling/v1"
1314
"google.golang.org/api/cloudresourcemanager/v1"
@@ -579,3 +580,69 @@ func readGoogleProject(d *schema.ResourceData, config *Config) (*cloudresourcema
579580
}, d.Timeout(schema.TimeoutRead))
580581
return p, err
581582
}
583+
584+
// Enables services. WARNING: Use BatchRequestEnableServices for better batching if possible.
585+
func enableServiceUsageProjectServices(services []string, project string, config *Config, timeout time.Duration) error {
586+
// ServiceUsage does not allow more than 20 services to be enabled per
587+
// batchEnable API call. See
588+
// https://cloud.google.com/service-usage/docs/reference/rest/v1/services/batchEnable
589+
for i := 0; i < len(services); i += maxServiceUsageBatchSize {
590+
j := i + maxServiceUsageBatchSize
591+
if j > len(services) {
592+
j = len(services)
593+
}
594+
nextBatch := services[i:j]
595+
if len(nextBatch) == 0 {
596+
// All batches finished, return.
597+
return nil
598+
}
599+
600+
if err := doEnableServicesRequest(nextBatch, project, config, timeout); err != nil {
601+
return err
602+
}
603+
log.Printf("[DEBUG] Finished enabling next batch of %d project services: %+v", len(nextBatch), nextBatch)
604+
}
605+
606+
log.Printf("[DEBUG] Verifying that all services are enabled")
607+
return waitForServiceUsageEnabledServices(services, project, config, timeout)
608+
}
609+
610+
// waitForServiceUsageEnabledServices doesn't resend enable requests - it just
611+
// waits for service enablement status to propagate. Essentially, it waits until
612+
// all services show up as enabled when listing services on the project.
613+
func waitForServiceUsageEnabledServices(services []string, project string, config *Config, timeout time.Duration) error {
614+
missing := make([]string, 0, len(services))
615+
delay := time.Duration(0)
616+
interval := time.Second
617+
err := retryTimeDuration(func() error {
618+
// Get the list of services that are enabled on the project
619+
enabledServices, err := listCurrentlyEnabledServices(project, config, timeout)
620+
if err != nil {
621+
return err
622+
}
623+
624+
missing := make([]string, 0, len(services))
625+
for _, s := range services {
626+
if _, ok := enabledServices[s]; !ok {
627+
missing = append(missing, s)
628+
}
629+
}
630+
if len(missing) > 0 {
631+
log.Printf("[DEBUG] waiting %v before reading project %s services...", delay, project)
632+
time.Sleep(delay)
633+
delay += interval
634+
interval += delay
635+
636+
// Spoof a googleapi Error so retryTime will try again
637+
return &googleapi.Error{
638+
Code: 503,
639+
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),
640+
}
641+
}
642+
return nil
643+
}, timeout)
644+
if err != nil {
645+
return errwrap.Wrap(err, fmt.Errorf("failed to enable some service(s) %q for project %s", missing, project))
646+
}
647+
return nil
648+
}

google/resource_google_project_service.go

+29
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import (
1111
"time"
1212
)
1313

14+
var ignoredProjectServices = []string{"dataproc-control.googleapis.com", "source.googleapis.com", "stackdriverprovisioning.googleapis.com"}
15+
16+
// These services can only be enabled as a side-effect of enabling other services,
17+
// so don't bother storing them in the config or using them for diffing.
18+
var ignoredProjectServicesSet = golangSetFromStringSlice(ignoredProjectServices)
19+
1420
func resourceGoogleProjectService() *schema.Resource {
1521
return &schema.Resource{
1622
Create: resourceGoogleProjectServiceCreate,
@@ -171,3 +177,26 @@ func isServiceEnabled(project, serviceName string, config *Config) (bool, error)
171177
}
172178
return srv.State == "ENABLED", nil
173179
}
180+
181+
// Disables a project service.
182+
func disableServiceUsageProjectService(service, project string, d *schema.ResourceData, config *Config, disableDependentServices bool) error {
183+
err := retryTimeDuration(func() error {
184+
name := fmt.Sprintf("projects/%s/services/%s", project, service)
185+
sop, err := config.clientServiceUsage.Services.Disable(name, &serviceusage.DisableServiceRequest{
186+
DisableDependentServices: disableDependentServices,
187+
}).Do()
188+
if err != nil {
189+
return err
190+
}
191+
// Wait for the operation to complete
192+
waitErr := serviceUsageOperationWait(config, sop, "api to disable")
193+
if waitErr != nil {
194+
return waitErr
195+
}
196+
return nil
197+
}, d.Timeout(schema.TimeoutDelete))
198+
if err != nil {
199+
return fmt.Errorf("Error disabling service %q for project %q: %v", service, project, err)
200+
}
201+
return nil
202+
}

google/resource_google_project_services.go

+40-135
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,6 @@ import (
1313

1414
const maxServiceUsageBatchSize = 20
1515

16-
var ignoredProjectServices = []string{"dataproc-control.googleapis.com", "source.googleapis.com", "stackdriverprovisioning.googleapis.com"}
17-
18-
// These services can only be enabled as a side-effect of enabling other services,
19-
// so don't bother storing them in the config or using them for diffing.
20-
var ignoredProjectServicesSet = golangSetFromStringSlice(ignoredProjectServices)
21-
2216
func resourceGoogleProjectServices() *schema.Resource {
2317
return &schema.Resource{
2418
Create: resourceGoogleProjectServicesCreateUpdate,
@@ -164,135 +158,6 @@ func setServiceUsageProjectEnabledServices(services []string, project string, d
164158
return nil
165159
}
166160

167-
// Disables a project service.
168-
func disableServiceUsageProjectService(service, project string, d *schema.ResourceData, config *Config, disableDependentServices bool) error {
169-
err := retryTimeDuration(func() error {
170-
name := fmt.Sprintf("projects/%s/services/%s", project, service)
171-
sop, err := config.clientServiceUsage.Services.Disable(name, &serviceusage.DisableServiceRequest{
172-
DisableDependentServices: disableDependentServices,
173-
}).Do()
174-
if err != nil {
175-
return err
176-
}
177-
// Wait for the operation to complete
178-
waitErr := serviceUsageOperationWait(config, sop, "api to disable")
179-
if waitErr != nil {
180-
return waitErr
181-
}
182-
return nil
183-
}, d.Timeout(schema.TimeoutDelete))
184-
if err != nil {
185-
return fmt.Errorf("Error disabling service %q for project %q: %v", service, project, err)
186-
}
187-
return nil
188-
}
189-
190-
// Retrieve a project's services from the API
191-
func listCurrentlyEnabledServices(project string, config *Config, timeout time.Duration) (map[string]struct{}, error) {
192-
// Verify project for services still exists
193-
p, err := config.clientResourceManager.Projects.Get(project).Do()
194-
if err != nil {
195-
return nil, err
196-
}
197-
if p.LifecycleState == "DELETE_REQUESTED" {
198-
// Construct a 404 error for handleNotFoundError
199-
return nil, &googleapi.Error{
200-
Code: 404,
201-
Message: "Project deletion was requested",
202-
}
203-
}
204-
205-
log.Printf("[DEBUG] Listing enabled services for project %s", project)
206-
apiServices := make(map[string]struct{})
207-
err = retryTimeDuration(func() error {
208-
ctx := context.Background()
209-
return config.clientServiceUsage.Services.
210-
List(fmt.Sprintf("projects/%s", project)).
211-
Fields("services/name,nextPageToken").
212-
Filter("state:ENABLED").
213-
Pages(ctx, func(r *serviceusage.ListServicesResponse) error {
214-
for _, v := range r.Services {
215-
// services are returned as "projects/PROJECT/services/NAME"
216-
name := GetResourceNameFromSelfLink(v.Name)
217-
if _, ok := ignoredProjectServicesSet[name]; !ok {
218-
apiServices[name] = struct{}{}
219-
}
220-
}
221-
return nil
222-
})
223-
}, timeout)
224-
if err != nil {
225-
return nil, errwrap.Wrapf(fmt.Sprintf("Failed to list enabled services for project %s: {{err}}", project), err)
226-
}
227-
return apiServices, nil
228-
}
229-
230-
// Enables services. WARNING: Use BatchRequestEnableServices for better batching if possible.
231-
func enableServiceUsageProjectServices(services []string, project string, config *Config, timeout time.Duration) error {
232-
// ServiceUsage does not allow more than 20 services to be enabled per
233-
// batchEnable API call. See
234-
// https://cloud.google.com/service-usage/docs/reference/rest/v1/services/batchEnable
235-
for i := 0; i < len(services); i += maxServiceUsageBatchSize {
236-
j := i + maxServiceUsageBatchSize
237-
if j > len(services) {
238-
j = len(services)
239-
}
240-
nextBatch := services[i:j]
241-
if len(nextBatch) == 0 {
242-
// All batches finished, return.
243-
return nil
244-
}
245-
246-
if err := doEnableServicesRequest(nextBatch, project, config, timeout); err != nil {
247-
return err
248-
}
249-
log.Printf("[DEBUG] Finished enabling next batch of %d project services: %+v", len(nextBatch), nextBatch)
250-
}
251-
252-
log.Printf("[DEBUG] Verifying that all services are enabled")
253-
return waitForServiceUsageEnabledServices(services, project, config, timeout)
254-
}
255-
256-
// waitForServiceUsageEnabledServices doesn't resend enable requests - it just
257-
// waits for service enablement status to propagate. Essentially, it waits until
258-
// all services show up as enabled when listing services on the project.
259-
func waitForServiceUsageEnabledServices(services []string, project string, config *Config, timeout time.Duration) error {
260-
missing := make([]string, 0, len(services))
261-
delay := time.Duration(0)
262-
interval := time.Second
263-
err := retryTimeDuration(func() error {
264-
// Get the list of services that are enabled on the project
265-
enabledServices, err := listCurrentlyEnabledServices(project, config, timeout)
266-
if err != nil {
267-
return err
268-
}
269-
270-
missing := make([]string, 0, len(services))
271-
for _, s := range services {
272-
if _, ok := enabledServices[s]; !ok {
273-
missing = append(missing, s)
274-
}
275-
}
276-
if len(missing) > 0 {
277-
log.Printf("[DEBUG] waiting %v before reading project %s services...", delay, project)
278-
time.Sleep(delay)
279-
delay += interval
280-
interval += delay
281-
282-
// Spoof a googleapi Error so retryTime will try again
283-
return &googleapi.Error{
284-
Code: 503,
285-
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),
286-
}
287-
}
288-
return nil
289-
}, timeout)
290-
if err != nil {
291-
return errwrap.Wrap(err, fmt.Errorf("failed to enable some service(s) %q for project %s", missing, project))
292-
}
293-
return nil
294-
}
295-
296161
func doEnableServicesRequest(services []string, project string, config *Config, timeout time.Duration) error {
297162
var op *serviceusage.Operation
298163

@@ -355,3 +220,43 @@ func expandServiceUsageProjectServicesServices(v interface{}, d TerraformResourc
355220
}
356221
return convertStringArr(v.(*schema.Set).List()), nil
357222
}
223+
224+
// Retrieve a project's services from the API
225+
func listCurrentlyEnabledServices(project string, config *Config, timeout time.Duration) (map[string]struct{}, error) {
226+
// Verify project for services still exists
227+
p, err := config.clientResourceManager.Projects.Get(project).Do()
228+
if err != nil {
229+
return nil, err
230+
}
231+
if p.LifecycleState == "DELETE_REQUESTED" {
232+
// Construct a 404 error for handleNotFoundError
233+
return nil, &googleapi.Error{
234+
Code: 404,
235+
Message: "Project deletion was requested",
236+
}
237+
}
238+
239+
log.Printf("[DEBUG] Listing enabled services for project %s", project)
240+
apiServices := make(map[string]struct{})
241+
err = retryTimeDuration(func() error {
242+
ctx := context.Background()
243+
return config.clientServiceUsage.Services.
244+
List(fmt.Sprintf("projects/%s", project)).
245+
Fields("services/name,nextPageToken").
246+
Filter("state:ENABLED").
247+
Pages(ctx, func(r *serviceusage.ListServicesResponse) error {
248+
for _, v := range r.Services {
249+
// services are returned as "projects/PROJECT/services/NAME"
250+
name := GetResourceNameFromSelfLink(v.Name)
251+
if _, ok := ignoredProjectServicesSet[name]; !ok {
252+
apiServices[name] = struct{}{}
253+
}
254+
}
255+
return nil
256+
})
257+
}, timeout)
258+
if err != nil {
259+
return nil, errwrap.Wrapf(fmt.Sprintf("Failed to list enabled services for project %s: {{err}}", project), err)
260+
}
261+
return apiServices, nil
262+
}

0 commit comments

Comments
 (0)