Using Golang to list, enable and disable Google APIs

Ivo Calado
9 min readNov 12, 2023

--

Introduction to Google ServiceUsage API

In Google Cloud Platform (GCP), APIs play a fundamental role in enabling communication and interaction between various services and applications. These APIs serve as the gateway, facilitating access to GCP’s diverse array of functionalities, from computing and storage to machine learning and beyond. The API calls are formulated using HTTP requests, specifying the desired action and endpoint within the GCP infrastructure. Proper usage of these APIs allows seamless integration and utilization of GCP’s extensive suite of tools, empowering developers to create innovative solutions and leverage the full potential of Google’s cloud services. We can access the list of available APIs by accessing APIs & Services -> Library in the Google Console.

Accessing the list of API Libraries

For each API in the list of libraries, we can manually enable the API by simply clicking the ‘enable’ button.

Manually enabling a GCP Library

Additionally, apart from accessing it through the web console, we can programmatically manage the list of enabled/disabled APIs. This functionality is covered by the ServiceUsage API (along with its corresponding Golang library), which offers several use cases, such as:

  • Listing the APIs, possibly filtering by status (enabled or disabled)
  • Enabling an API (a single API or a batch)
  • Disabling an API (a single API or a batch)

Next, we’ll explore three use cases for using this API: filtering enabled APIs, enabling an API, and disabling an API.

Let’s get our hands dirty and dive into the task!

Listing enabled APIs

The aim of this example is to showcase how to retrieve the list of enabled APIs. To do this, we assume that you aware about the process of creating a service key. Besides that, we must assign the role roles/serviceusage.serviceUsageViewer (for more details, refer to GCP roles and permissions) the service account or user account that is going to be used for accessing the API.

Let’s begin by creating a new Golang module. Type the following command in your working directory:

go mod init 01-list-apis

The command above will create a file called go.mod with the following content:

module 01-list-apis

go 1.19

Next, we need to import the ServiceUsage library. You can do this from the command line by typing the following command:

go get google.golang.org/api/serviceusage/v1

go get is a command in the Go programming language that simplifies the process of fetching, building, and installing packages or dependencies directly from version control repositories like GitHub, GitLab, or Bitbucket. This command allows developers to swiftly retrieve the source code for a specific package or library and automatically install it into their Go workspace. By referencing the import path of a package, go get resolves dependencies, fetches the code, and places it in the correct directory structure within the workspace. It streamlines the workflow, aiding in package management and making it convenient for Go developers to incorporate external libraries, effortlessly integrating them into their projects.

When you import a library with go get , Go will automatically generate a new file named go.sum, containing hash values for all the dependencies of the ServiceUsage library. This file has a structure similar to this:

cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0=
cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64
... more entries

The command go get will also update the file go.mod with the list of required APIs similar to this:

module 01-list-apis

go 1.19

require (
cloud.google.com/go/compute v1.23.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/api v0.150.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

After creating a Golang module and importing the necessary APIs, we’ll proceed to create an implementation file (in my example, main.go ). This file is divided into four sections:

  • Importing the required packages
  • Instantiaging the ServiceUsage object
  • Call the List method to retrieve the list of enabled APIs
  • Iterate over the retrieved list and print the results.

For the imported packages, we need to import the following:

import (
"context"
"fmt"
"log"
"google.golang.org/api/serviceusage/v1"
)

To create an instance of the ServiceUsage object, the service usage library offers a function named NewService:

 ctx := context.Background()

// Create a serviceusage client with the appropriate credentials
svc, err := serviceusage.NewService(ctx)
if err != nil {
log.Fatalf("Failed to create serviceusage client: %v", err)
return
}

The aforementioned function accepts a context object (for more details refers this) and eventually a option.ClientOption. This option can be used to pass the GCP service key, which authenticates the principal (user or service account). If we do not explicitily indicate the service key (as I didn't) the library will use the Application Default Credentials (ADC) to identify the appropriate service keys.

As is customary in Golang, for almost every function call, we need to handle scenarios where the call might fail. This is typically achieved using an if-block as we can see above.

In the sequence, we need to use the methods List, Filter and Do to respectively create a caller for the List endpoint, add a filter to retrieve only the enabled APIs, and ultimately execute the call operation:

 // Call the services.list method to get a list of all enabled services
resp, err := svc.Services.List(fmt.Sprintf("projects/compute-engine-examples", projectID)).Filter("state:ENABLED").Do()
if err != nil {
log.Fatalf("Failed to list services: %v", err)
return
}

Finally, after calling the list endpoint, we simply need to iterate through the response to retrieve the list of enabled APIs:

 // Print the name of each enabled service
for _, service := range resp.Services {
fmt.Println(service.Name)
}

Bringing everything together, we have the following code example:

package main

import (
"context"
"fmt"
"log"
"google.golang.org/api/serviceusage/v1"
)

func main() {
projectID := "compute-engine-examples"

ctx := context.Background()

// Create a serviceusage client with the appropriate credentials
svc, err := serviceusage.NewService(ctx)
if err != nil {
log.Fatalf("Failed to create serviceusage client: %v", err)
return
}

// Call the services.list method to get a list of all enabled services
resp, err := svc.Services.List(fmt.Sprintf("projects/%s", projectID)).Filter("state:ENABLED").Do()
if err != nil {
log.Fatalf("Failed to list services: %v", err)
return
}

// Print the name of each enabled service
for _, service := range resp.Services {
fmt.Println(service.Name)
}
}

To execute the aforementioned code, use the command go run preceded by the GOOGLE_APPLICATION_CREDENTIALS environment variable, like this:

➜  01-list-apis git:(master) ✗ GOOGLE_APPLICATION_CREDENTIALS=path/to/service-key-json-file go run main.go

projects/<project_number>/services/bigquery.googleapis.com
projects/<project_number>/services/bigquerymigration.googleapis.com
projects/<project_number>/services/bigquerystorage.googleapis.com
projects/<project_number>/services/cloudapis.googleapis.com
projects/<project_number>/services/cloudtrace.googleapis.com
projects/<project_number>/services/datastore.googleapis.com
projects/<project_number>/services/logging.googleapis.com
projects/<project_number>/services/monitoring.googleapis.com
projects/<project_number>/services/oslogin.googleapis.com
projects/<project_number>/services/servicemanagement.googleapis.com
projects/<project_number>/services/serviceusage.googleapis.com
projects/<project_number>/services/sql-component.googleapis.com
projects/<project_number>/services/storage-api.googleapis.com
projects/<project_number>/services/storage-component.googleapis.com
projects/<project_number>/services/storage.googleapis.com

Enabling an API

Enabling a new API follows a process similar to listing. The key distinction is that enabling an API involves a non-blocking call due to the time GCP requires to fully activate an API. Therefore, the call returns an instance of Operation immediately, indicating the current status through the Done attribute. We then need to poll (for example, every 10 seconds in my example) to verify when the operation is completely finished. Furthermore, to make changes in the project, we need to elevate the permissions of the user/service account to roles/serviceusage.serviceUsageAdmin.

 // Enable the API
op, err := svc.Services.Enable(fmt.Sprintf("projects/%s/services/%s", projectID, apiName), &serviceusage.EnableServiceRequest{}).Do()
if err != nil {
log.Fatalf("Failed to enable API: %v", err)
}

for {
// Check operation status
op, err = svc.Operations.Get(op.Name).Do()
if err != nil {
log.Fatalf("Failed to get operation: %v", err)
}
if op.Done {
break
}
fmt.Printf("Waiting for operation to complete: %v\n", op.Name)
time.Sleep(10 * time.Second)
}

When we run the code, it becomes evident that it takes some time for the API to be effectively enabled:

➜  02-enable-api git:(master) ✗ GOOGLE_APPLICATION_CREDENTIALS=path/to/service-key-json-file go run main.go
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
Waiting for operation to complete: operations/acf.p2-261740404770-c8623ca0-8ff5-497a-8bc4-97e2d5e67027
API compute.googleapis.com has been enabled for project compute-engine-examples

After executing the aforementioned example, we can go in Google console and validate that the API was correctly enabled:

The following example provides the complete code to disable an API.

package main

import (
"context"
"fmt"
"log"
"time"

//Run go get google.golang.org/api/serviceusage/v1 to retrieve the serviceusage package.
"google.golang.org/api/serviceusage/v1"
)

// Enable roles/serviceusage.serviceUsageAdmin
func main() {
projectID := "compute-engine-examples"
apiName := "compute.googleapis.com"

ctx := context.Background()

// Create a serviceusage client with default credentials
svc, err := serviceusage.NewService(ctx)
if err != nil {
log.Fatalf("Failed to create serviceusage client: %v", err)
}

// Enable the API
op, err := svc.Services.Enable(fmt.Sprintf("projects/%s/services/%s", projectID, apiName), &serviceusage.EnableServiceRequest{}).Do()
if err != nil {
log.Fatalf("Failed to enable API: %v", err)
}

for {
// Check operation status
op, err = svc.Operations.Get(op.Name).Do()
if err != nil {
log.Fatalf("Failed to get operation: %v", err)
}
if op.Done {
break
}
fmt.Printf("Waiting for operation to complete: %v\n", op.Name)
time.Sleep(10 * time.Second)
}

fmt.Printf("API %s has been enabled for project %s\n", apiName, projectID)
}

Disabling an API

The process of disabling an API is quite similar to enabling one where we have a non-blocking call. We just need to use the method Disable passing an instance of DisableServiceRequest, specifying the type of usage service check we want to perform:

op, err := svc.Services.Disable(fmt.Sprintf("projects/%s/services/%s", projectID, apiName), &serviceusage.DisableServiceRequest{
CheckIfServiceHasUsage: "CHECK",
}).Do()

// CheckIfServiceHasUsage: Defines the behavior for checking service
// usage when disabling a service.
//
// Possible values:
// "CHECK_IF_SERVICE_HAS_USAGE_UNSPECIFIED" - When unset, the default
// behavior is used, which is SKIP.
// "SKIP" - If set, skip checking service usage when disabling a
// service.
// "CHECK" - If set, service usage is checked when disabling the
// service. If a service, or its dependents, has usage in the last 30
// days, the request returns a FAILED_PRECONDITION error.

When attempting to disable an API, once again, we’ll observe a polling process to obtain the current status of the API:

➜  03-disable-api git:(master) ✗ GOOGLE_APPLICATION_CREDENTIALS=path/to/service-key-json-file go run main.go
Waiting for operation to complete: operations/acf.p17-261740404770-829c5cef-9813-4eca-932e-d5dd3a0f3c66
API compute.googleapis.com has been disabled for project compute-engine-examples

The following example provides the complete code to disable an API.

package main

import (
"context"
"fmt"
"log"
"time"
"google.golang.org/api/serviceusage/v1"
)

func main() {
projectID := "compute-engine-examples"
apiName := "compute.googleapis.com"

ctx := context.Background()

// Create a serviceusage client with default credentials
svc, err := serviceusage.NewService(ctx)
if err != nil {
log.Fatalf("Failed to create serviceusage client: %v", err)
}

// Disable the API
op, err := svc.Services.Disable(fmt.Sprintf("projects/%s/services/%s", projectID, apiName), &serviceusage.DisableServiceRequest{
CheckIfServiceHasUsage: "CHECK",
}).Do()
if err != nil {
log.Fatalf("Failed to disable API: %v", err)
return
}

for {
// Check operation status
op, err = svc.Operations.Get(op.Name).Do()
if err != nil {
log.Fatalf("Failed to get operation: %v", err)
return
}
if op.Done {
break
}
fmt.Printf("Waiting for operation to complete: %v\n", op.Name)
time.Sleep(10 * time.Second)
}

fmt.Printf("API %s has been disabled for project %s\n", apiName, projectID)
}

Conclusion

In this post we saw how we can use Golang to interact with the Google ServiceUsage API. Depending of the call we can have block and unblock operations which eventually increses the complexity. You can explore the full set of examples in this GitHub repository or connect with me on LinkedIn for more insights and discussions.

--

--