gcloud/serviceusage.go (143 lines of code) (raw):
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gcloud
import (
"fmt"
"strings"
"time"
"google.golang.org/api/serviceusage/v1"
)
// Service is an enum that makes it easy to reference Google Cloud Services
type Service int64
const (
// Compute is the service name for enabling Compute Engine
Compute Service = iota + 1
// CloudBilling is the service name for enabling CloudBilling
CloudBilling
// CloudBuild is the service name for enabling Cloud Build
CloudBuild
// CloudFunctions is the service name for enabling Cloud Functions
CloudFunctions
// CloudResourceManager is the service name for enabling
// CloudResourceManager this is ultimately about manipulating projects
CloudResourceManager
// CloudScheduler is the service name for enabling Cloud Scheduler
CloudScheduler
// Domains is the service name for enabling Cloud Domains
Domains
// IAM is the service name for enabling Cloud IAM
IAM
// Run is the service name for enabling Cloud Run
Run
// ServiceUsage is the service name for manipulating and enabling services
ServiceUsage
// SecretManager is the service name for enabling Cloud Secret Manager
SecretManager
// Storage is the service name for enabling Cloud Storage
Storage
// Vault is the service name for enabling Cloud Vault
Vault
)
func (s Service) String() string {
apistring := "googleapis.com"
svc := ""
switch s {
case CloudBilling:
svc = "cloudbilling"
case CloudBuild:
svc = "cloudbuild"
case CloudFunctions:
svc = "cloudfunctions"
case CloudResourceManager:
svc = "cloudresourcemanager"
case CloudScheduler:
svc = "cloudscheduler"
case Compute:
svc = "compute"
case Domains:
svc = "domains"
case IAM:
svc = "iam"
case Run:
svc = "run"
case ServiceUsage:
svc = "serviceusage"
case SecretManager:
svc = "secretmanager"
case Storage:
svc = "storage"
case Vault:
svc = "vault"
default:
svc = "unknown"
}
return fmt.Sprintf("%s.%s", svc, apistring)
}
// ErrorServiceNotExistOrNotAllowed occurs when the user running this code doesn't have
// permission to enable the service in the project or it's a nonexistent service name.
var ErrorServiceNotExistOrNotAllowed = fmt.Errorf("Not found or permission denied for service")
// ErrorProjectRequired communicates that am empty project string has been passed
var ErrorProjectRequired = fmt.Errorf("Project may not be an empty string")
func (c *Client) getServiceUsageService() (*serviceusage.Service, error) {
var err error
svc := c.services.serviceUsage
if svc != nil {
return svc, nil
}
svc, err = serviceusage.NewService(c.ctx, c.opts)
if err != nil {
return nil, fmt.Errorf("could not retrieve service: %w", err)
}
svc.UserAgent = c.userAgent
c.services.serviceUsage = svc
return svc, nil
}
// ServiceEnable enable a service in the selected project so that query calls
// to various lists will work.
func (c *Client) ServiceEnable(project string, service Service) error {
if _, ok := c.enabledServices[service.String()]; ok {
return nil
}
svc, err := c.getServiceUsageService()
if err != nil {
return fmt.Errorf("could not getServiceUsageService: %s", err)
}
enabled, err := c.ServiceIsEnabled(project, service)
if err != nil {
return fmt.Errorf("could not confirm if service is already enabled: %w", err)
}
if enabled {
c.enabledServices[service.String()] = true
return nil
}
s := fmt.Sprintf("projects/%s/services/%s", project, service)
op, err := svc.Services.Enable(s, &serviceusage.EnableServiceRequest{}).Do()
if err != nil {
return fmt.Errorf("could not enable service: %s", err)
}
if !strings.Contains(string(op.Response), "ENABLED") {
for i := 0; i < 60; i++ {
enabled, err = c.ServiceIsEnabled(project, service)
if err != nil {
return err
}
if enabled {
c.enabledServices[service.String()] = true
return nil
}
time.Sleep(1 * time.Second)
}
}
c.enabledServices[service.String()] = true
return nil
}
// ServiceIsEnabled checks to see if the existing service is already enabled
// in the project we are trying to enable it in.
func (c *Client) ServiceIsEnabled(project string, service Service) (bool, error) {
svc, err := c.getServiceUsageService()
if project == "" {
return false, ErrorProjectRequired
}
s := fmt.Sprintf("projects/%s/services/%s", project, service)
current, err := svc.Services.Get(s).Do()
if err != nil {
if strings.Contains(err.Error(), "Not found or permission denied for service") {
return false, ErrorServiceNotExistOrNotAllowed
}
return false, fmt.Errorf("cannot get the service for resource (%s): %w", s, err)
}
if current.State == "ENABLED" {
return true, nil
}
return false, nil
}
// ServiceDisable disables a service in the selected project
func (c *Client) ServiceDisable(project string, service Service) error {
svc, err := c.getServiceUsageService()
if err != nil {
return err
}
s := fmt.Sprintf("projects/%s/services/%s", project, service)
if _, err := svc.Services.Disable(s, &serviceusage.DisableServiceRequest{}).Do(); err != nil {
if strings.Contains(err.Error(), "Not found or permission denied for service") {
return ErrorServiceNotExistOrNotAllowed
}
return fmt.Errorf("could not disable service: %s", err)
}
return nil
}