google-api-go-client icon indicating copy to clipboard operation
google-api-go-client copied to clipboard

BatchEnable Operation's response is not working even when APIs are enabled

Open dunefro opened this issue 2 years ago • 1 comments

Environment details

  • Programming language: golang
  • OS: macOS darwin 22.6.0
  • Language runtime version: go version go1.21.0 darwin/arm64
  • Package version: 0.142.0

Steps to reproduce

I am trying to enable certain APIs for my project

  1. Below is the code
func enableRequiredAPIS(projectName string) error {
	ctx := context.Background()
	projectParentName := fmt.Sprintf("projects/%s", projectName)
	serviceusageServiceClient, err := serviceusage.NewService(ctx)
	if err != nil {
		return fmt.Errorf("enableRequiredAPIS: Error getting serviceusage.NewService type: %w", err)
	}
	serviceUsageServicesServiceClient := serviceusage.NewServicesService(serviceusageServiceClient)

	// get project information
	projectBatchGet := serviceUsageServicesServiceClient.BatchGet(projectParentName)

	// all the required services we want to check and enable
	allServices := []string{
		fmt.Sprintf("%s/services/compute.googleapis.com", projectParentName),
		fmt.Sprintf("%s/services/container.googleapis.com", projectParentName),
		fmt.Sprintf("%s/services/secretmanager.googleapis.com", projectParentName),
		fmt.Sprintf("%s/services/artifactregistry.googleapis.com", projectParentName),
	}
	// assigning service names
	projectBatchGet.Names(allServices...)
	resp, err := projectBatchGet.Do()
	if err != nil {
		return fmt.Errorf("enableRequiredAPIS: Error getting batch GET for services %w", err)
	}
	var serviceToEnable []string
	for _, service := range resp.Services {
		if service.State == "DISABLED" {
			serviceName := strings.Replace(service.Name, fmt.Sprintf("%s/services/", service.Parent), "", 1)
			utils.PrintInfoln(fmt.Sprintf("service %s not enabled for project %s", serviceName, projectName))
			serviceToEnable = append(serviceToEnable, serviceName)
		}
	}
	if len(serviceToEnable) != 0 {
		enableOperation, err := serviceUsageServicesServiceClient.BatchEnable(projectParentName, &serviceusage.BatchEnableServicesRequest{
			ServiceIds: serviceToEnable,
		}).Do()
		if err != nil {
			return fmt.Errorf("enableRequiredAPIS: Error enabling services %w", err)
		}
		for {
			fmt.Println("Waiting for the services to get enabled...")
			fmt.Println("done", enableOperation.Done)
			fmt.Println("statuscode", enableOperation.ServerResponse.HTTPStatusCode)
			if enableOperation.Done {
				fmt.Println("Successfully enabled all the services")
				return nil
			} else if opErr := enableOperation.Error; opErr != nil {
				fmt.Println(fmt.Sprintf("Failed to enable services. error: %s %d", opErr.Message, opErr.Code))
				return fmt.Errorf("enableRequiredAPIS: error enabling services, error %s %d", opErr.Message, opErr.Code)
			} else {
				fmt.Println(string(enableOperation.Response))
				time.Sleep(3 * time.Second)
			}
		}
	}
	return nil
}
  1. When I am trying to execute the code to enable the APIs which are disabled, the for loop is running indefinitely.
  2. When I check the API from the console it gets enabled after certain time which is the ideal behaviour.
  3. The operation should return target and not ret, but I am not perfectly sure of this.

What should happen

I should either get an error or operation.Done should be marked as true

dunefro avatar Sep 22 '23 20:09 dunefro

Hello @dunefro, thanks for the issue. I don't think there is a bug in the client, but rather in the code that you've shared.

The *Operation returned by the ServicesBatchEnableCall.Do does not automatically poll the Operation. So, the code shared above is checking the same Operation from the initial response, thus looping forever.

Instead, you need to include a polling request via OperationsService.Get in your for loop after your time.Sleep call to refresh the local Operation state.

  	operations = NewOperationsService(serviceusageServiceClient)
  	// ...
  	for {
  		// ...
  		} else {
  			fmt.Println(string(enableOperation.Response))
  			time.Sleep(3 * time.Second)
  			// Poll the operation state, loop again to same operation evaluation logic.
  			enableOperation, err = operations.Get(enableOperation.Name).Do()
  			if err != nil {
  				fmt.Println("error polling operation:", err)
  				return err
  			}
  		}
  	}

I can't find a section in our top-level of docs that explain this, so I will add one.

Please close this if it works for you or I will close it in a few days as resolved.

noahdietz avatar Sep 25 '23 18:09 noahdietz

Closing due to lack of response.

codyoss avatar Apr 04 '25 14:04 codyoss