mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-01 16:21:06 +00:00
Use new serviceusage API for google_project_service[s]
(#1522)
* vendor service usage api * use serviceusage api instead of servicemanagement for project services * add bigquery-json to test * add import for project service * add serviceusage_operation.go
This commit is contained in:
parent
ed36405b02
commit
20616e424d
@ -37,6 +37,7 @@ import (
|
||||
"google.golang.org/api/redis/v1beta1"
|
||||
"google.golang.org/api/runtimeconfig/v1beta1"
|
||||
"google.golang.org/api/servicemanagement/v1"
|
||||
"google.golang.org/api/serviceusage/v1beta1"
|
||||
"google.golang.org/api/sourcerepo/v1"
|
||||
"google.golang.org/api/spanner/v1"
|
||||
"google.golang.org/api/sqladmin/v1beta4"
|
||||
@ -78,6 +79,7 @@ type Config struct {
|
||||
clientSqlAdmin *sqladmin.Service
|
||||
clientIAM *iam.Service
|
||||
clientServiceMan *servicemanagement.APIService
|
||||
clientServiceUsage *serviceusage.APIService
|
||||
clientBigQuery *bigquery.Service
|
||||
clientCloudFunctions *cloudfunctions.Service
|
||||
clientCloudIoT *cloudiot.Service
|
||||
@ -274,6 +276,13 @@ func (c *Config) loadAndValidate() error {
|
||||
}
|
||||
c.clientServiceMan.UserAgent = userAgent
|
||||
|
||||
log.Printf("[INFO] Instantiating Google Cloud Service Usage Client...")
|
||||
c.clientServiceUsage, err = serviceusage.New(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientServiceUsage.UserAgent = userAgent
|
||||
|
||||
log.Printf("[INFO] Instantiating Google Cloud Billing Client...")
|
||||
c.clientBilling, err = cloudbilling.New(client)
|
||||
if err != nil {
|
||||
|
@ -15,6 +15,10 @@ func resourceGoogleProjectService() *schema.Resource {
|
||||
Delete: resourceGoogleProjectServiceDelete,
|
||||
Update: resourceGoogleProjectServiceUpdate,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"service": {
|
||||
Type: schema.TypeString,
|
||||
@ -57,22 +61,17 @@ func resourceGoogleProjectServiceCreate(d *schema.ResourceData, meta interface{}
|
||||
func resourceGoogleProjectServiceRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
project, err := getProject(d, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := parseProjectServiceId(d.Id())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
services, err := getApiServices(project, config, map[string]struct{}{})
|
||||
services, err := getApiServices(id.project, config, map[string]struct{}{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set("project", project)
|
||||
d.Set("project", id.project)
|
||||
|
||||
for _, s := range services {
|
||||
if s == id.service {
|
||||
@ -94,17 +93,13 @@ func resourceGoogleProjectServiceDelete(d *schema.ResourceData, meta interface{}
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
project, err := getProject(d, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := parseProjectServiceId(d.Id())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = disableService(id.service, project, config); err != nil {
|
||||
if err = disableService(id.service, id.project, config); err != nil {
|
||||
return fmt.Errorf("Error disabling service: %s", err)
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,18 @@ func TestAccProjectService_basic(t *testing.T) {
|
||||
testAccCheckProjectService(services, pid, true),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
ResourceName: "google_project_service.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"disable_on_destroy"},
|
||||
},
|
||||
resource.TestStep{
|
||||
ResourceName: "google_project_service.test2",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"disable_on_destroy"},
|
||||
},
|
||||
// Use a separate TestStep rather than a CheckDestroy because we need the project to still exist.
|
||||
resource.TestStep{
|
||||
Config: testAccProject_create(pid, pname, org),
|
||||
|
@ -3,9 +3,10 @@ package google
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"google.golang.org/api/servicemanagement/v1"
|
||||
"google.golang.org/api/serviceusage/v1beta1"
|
||||
)
|
||||
|
||||
func resourceGoogleProjectServices() *schema.Resource {
|
||||
@ -156,11 +157,13 @@ func reconcileServices(cfgServices, apiServices []string, config *Config, pid st
|
||||
}
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(cfgMap))
|
||||
for k, _ := range cfgMap {
|
||||
err := enableService(k, pid, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
err := enableServices(keys, pid, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -181,13 +184,16 @@ func getApiServices(pid string, config *Config, ignore map[string]struct{}) ([]s
|
||||
// Get services from the API
|
||||
token := ""
|
||||
for paginate := true; paginate; {
|
||||
svcResp, err := config.clientServiceMan.Services.List().ConsumerId("project:" + pid).PageToken(token).Do()
|
||||
svcResp, err := config.clientServiceUsage.Services.List("projects/" + pid).PageToken(token).Filter("state:ENABLED").Do()
|
||||
if err != nil {
|
||||
return apiServices, err
|
||||
}
|
||||
for _, v := range svcResp.Services {
|
||||
if _, ok := ignore[v.ServiceName]; !ok {
|
||||
apiServices = append(apiServices, v.ServiceName)
|
||||
// names are returned as projects/{project-number}/services/{service-name}
|
||||
nameParts := strings.Split(v.Name, "/")
|
||||
name := nameParts[len(nameParts)-1]
|
||||
if _, ok := ignore[name]; !ok {
|
||||
apiServices = append(apiServices, name)
|
||||
}
|
||||
}
|
||||
token = svcResp.NextPageToken
|
||||
@ -197,13 +203,27 @@ func getApiServices(pid string, config *Config, ignore map[string]struct{}) ([]s
|
||||
}
|
||||
|
||||
func enableService(s, pid string, config *Config) error {
|
||||
esr := newEnableServiceRequest(pid)
|
||||
return enableServices([]string{s}, pid, config)
|
||||
}
|
||||
|
||||
func enableServices(s []string, pid string, config *Config) error {
|
||||
err := retryTime(func() error {
|
||||
sop, err := config.clientServiceMan.Services.Enable(s, esr).Do()
|
||||
var sop *serviceusage.Operation
|
||||
var err error
|
||||
if len(s) > 1 {
|
||||
req := &serviceusage.BatchEnableServicesRequest{ServiceIds: s}
|
||||
sop, err = config.clientServiceUsage.Services.BatchEnable("projects/"+pid, req).Do()
|
||||
} else if len(s) == 1 {
|
||||
name := fmt.Sprintf("projects/%s/services/%s", pid, s[0])
|
||||
sop, err = config.clientServiceUsage.Services.Enable(name, &serviceusage.EnableServiceRequest{}).Do()
|
||||
} else {
|
||||
// No services to enable
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, waitErr := serviceManagementOperationWait(config, sop, "api to enable")
|
||||
_, waitErr := serviceUsageOperationWait(config, sop, "api to enable")
|
||||
if waitErr != nil {
|
||||
return waitErr
|
||||
}
|
||||
@ -216,14 +236,14 @@ func enableService(s, pid string, config *Config) error {
|
||||
}
|
||||
|
||||
func disableService(s, pid string, config *Config) error {
|
||||
dsr := newDisableServiceRequest(pid)
|
||||
err := retryTime(func() error {
|
||||
sop, err := config.clientServiceMan.Services.Disable(s, dsr).Do()
|
||||
name := fmt.Sprintf("projects/%s/services/%s", pid, s)
|
||||
sop, err := config.clientServiceUsage.Services.Disable(name, &serviceusage.DisableServiceRequest{}).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Wait for the operation to complete
|
||||
_, waitErr := serviceManagementOperationWait(config, sop, "api to disable")
|
||||
_, waitErr := serviceUsageOperationWait(config, sop, "api to disable")
|
||||
if waitErr != nil {
|
||||
return waitErr
|
||||
}
|
||||
@ -235,14 +255,6 @@ func disableService(s, pid string, config *Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newEnableServiceRequest(pid string) *servicemanagement.EnableServiceRequest {
|
||||
return &servicemanagement.EnableServiceRequest{ConsumerId: "project:" + pid}
|
||||
}
|
||||
|
||||
func newDisableServiceRequest(pid string) *servicemanagement.DisableServiceRequest {
|
||||
return &servicemanagement.DisableServiceRequest{ConsumerId: "project:" + pid}
|
||||
}
|
||||
|
||||
func resourceServices(d *schema.ResourceData) []string {
|
||||
// Calculate the tags
|
||||
var services []string
|
||||
|
@ -161,6 +161,7 @@ func TestAccProjectServices_ignoreUnenablableServices(t *testing.T) {
|
||||
"storage-api.googleapis.com",
|
||||
"pubsub.googleapis.com",
|
||||
"oslogin.googleapis.com",
|
||||
"bigquery-json.googleapis.com",
|
||||
}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
|
68
google/serviceusage_operation.go
Normal file
68
google/serviceusage_operation.go
Normal file
@ -0,0 +1,68 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/serviceusage/v1beta1"
|
||||
)
|
||||
|
||||
type serviceUsageOperationWaiter struct {
|
||||
Service *serviceusage.APIService
|
||||
Op *serviceusage.Operation
|
||||
}
|
||||
|
||||
func (w *serviceUsageOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
var op *serviceusage.Operation
|
||||
var err error
|
||||
|
||||
op, err = w.Service.Operations.Get(w.Op.Name).Do()
|
||||
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Got %v while polling for operation %s's 'done' status", op.Done, w.Op.Name)
|
||||
|
||||
return op, fmt.Sprint(op.Done), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *serviceUsageOperationWaiter) Conf() *resource.StateChangeConf {
|
||||
return &resource.StateChangeConf{
|
||||
Pending: []string{"false"},
|
||||
Target: []string{"true"},
|
||||
Refresh: w.RefreshFunc(),
|
||||
}
|
||||
}
|
||||
|
||||
func serviceUsageOperationWait(config *Config, op *serviceusage.Operation, activity string) (googleapi.RawMessage, error) {
|
||||
return serviceUsageOperationWaitTime(config, op, activity, 10)
|
||||
}
|
||||
|
||||
func serviceUsageOperationWaitTime(config *Config, op *serviceusage.Operation, activity string, timeoutMin int) (googleapi.RawMessage, error) {
|
||||
w := &serviceUsageOperationWaiter{
|
||||
Service: config.clientServiceUsage,
|
||||
Op: op,
|
||||
}
|
||||
|
||||
state := w.Conf()
|
||||
state.Delay = 10 * time.Second
|
||||
state.Timeout = time.Duration(timeoutMin) * time.Minute
|
||||
state.MinTimeout = 2 * time.Second
|
||||
opRaw, err := state.WaitForState()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error waiting for %s: %s", activity, err)
|
||||
}
|
||||
|
||||
op = opRaw.(*serviceusage.Operation)
|
||||
if op.Error != nil {
|
||||
return nil, fmt.Errorf("Error code %v, message: %s", op.Error.Code, op.Error.Message)
|
||||
}
|
||||
|
||||
return op.Response, nil
|
||||
}
|
2313
vendor/google.golang.org/api/serviceusage/v1beta1/serviceusage-api.json
generated
vendored
Normal file
2313
vendor/google.golang.org/api/serviceusage/v1beta1/serviceusage-api.json
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
5799
vendor/google.golang.org/api/serviceusage/v1beta1/serviceusage-gen.go
generated
vendored
Normal file
5799
vendor/google.golang.org/api/serviceusage/v1beta1/serviceusage-gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@ -1410,6 +1410,12 @@
|
||||
"revision": "2c6d8d196bf34219b5c8f453dd2ec079b7f1ab0c",
|
||||
"revisionTime": "2018-01-05T00:03:37Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Ld0E3QmFqSpUWOWccbP2Sy29YeA=",
|
||||
"path": "google.golang.org/api/serviceusage/v1beta1",
|
||||
"revision": "4f7dd2b006a4ffd9fd683c1c734d2fe91ca0ea1c",
|
||||
"revisionTime": "2018-05-19T00:08:34Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ZfAt+uQ95CQfoJZEjmuzgx+bAIg=",
|
||||
"path": "google.golang.org/api/sourcerepo/v1",
|
||||
|
@ -34,3 +34,11 @@ The following arguments are supported:
|
||||
* `project` - (Optional) The project ID. If not provided, the provider project is used.
|
||||
|
||||
* `disable_on_destroy` - (Optional) If true, disable the service when the terraform resource is destroyed. Defaults to true. May be useful in the event that a project is long-lived but the infrastructure running in that project changes frequently.
|
||||
|
||||
## Import
|
||||
|
||||
Project services can be imported using the `project_id` and `service`, e.g.
|
||||
|
||||
```
|
||||
$ terraform import google_project_services.my_project your-project-id/iam.googleapis.com
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user