mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-07 01:26:43 +00:00
230 lines
6.2 KiB
Go
230 lines
6.2 KiB
Go
package google
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
"google.golang.org/api/servicemanagement/v1"
|
|
)
|
|
|
|
func resourceGoogleProjectServices() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceGoogleProjectServicesCreate,
|
|
Read: resourceGoogleProjectServicesRead,
|
|
Update: resourceGoogleProjectServicesUpdate,
|
|
Delete: resourceGoogleProjectServicesDelete,
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"project": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
"services": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
Set: schema.HashString,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// These services can only be enabled as a side-effect of enabling other services,
|
|
// so don't bother storing them in the config or using them for diffing.
|
|
var ignore = map[string]struct{}{
|
|
"containeranalysis.googleapis.com": struct{}{},
|
|
"dataproc-control.googleapis.com": struct{}{},
|
|
"source.googleapis.com": struct{}{},
|
|
}
|
|
|
|
func resourceGoogleProjectServicesCreate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
pid := d.Get("project").(string)
|
|
|
|
// Get services from config
|
|
cfgServices := getConfigServices(d)
|
|
|
|
// Get services from API
|
|
apiServices, err := getApiServices(pid, config)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating services: %v", err)
|
|
}
|
|
|
|
// This call disables any APIs that aren't defined in cfgServices,
|
|
// and enables all of those that are
|
|
err = reconcileServices(cfgServices, apiServices, config, pid)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating services: %v", err)
|
|
}
|
|
|
|
d.SetId(pid)
|
|
return resourceGoogleProjectServicesRead(d, meta)
|
|
}
|
|
|
|
func resourceGoogleProjectServicesRead(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
services, err := getApiServices(d.Id(), config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d.Set("services", services)
|
|
return nil
|
|
}
|
|
|
|
func resourceGoogleProjectServicesUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
log.Printf("[DEBUG]: Updating google_project_services")
|
|
config := meta.(*Config)
|
|
pid := d.Get("project").(string)
|
|
|
|
// Get services from config
|
|
cfgServices := getConfigServices(d)
|
|
|
|
// Get services from API
|
|
apiServices, err := getApiServices(pid, config)
|
|
if err != nil {
|
|
return fmt.Errorf("Error updating services: %v", err)
|
|
}
|
|
|
|
// This call disables any APIs that aren't defined in cfgServices,
|
|
// and enables all of those that are
|
|
err = reconcileServices(cfgServices, apiServices, config, pid)
|
|
if err != nil {
|
|
return fmt.Errorf("Error updating services: %v", err)
|
|
}
|
|
|
|
return resourceGoogleProjectServicesRead(d, meta)
|
|
}
|
|
|
|
func resourceGoogleProjectServicesDelete(d *schema.ResourceData, meta interface{}) error {
|
|
log.Printf("[DEBUG]: Deleting google_project_services")
|
|
config := meta.(*Config)
|
|
services := resourceServices(d)
|
|
for _, s := range services {
|
|
disableService(s, d.Id(), config)
|
|
}
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
// This function ensures that the services enabled for a project exactly match that
|
|
// in a config by disabling any services that are returned by the API but not present
|
|
// in the config
|
|
func reconcileServices(cfgServices, apiServices []string, config *Config, pid string) error {
|
|
// Helper to convert slice to map
|
|
m := func(vals []string) map[string]struct{} {
|
|
sm := make(map[string]struct{})
|
|
for _, s := range vals {
|
|
sm[s] = struct{}{}
|
|
}
|
|
return sm
|
|
}
|
|
|
|
cfgMap := m(cfgServices)
|
|
apiMap := m(apiServices)
|
|
|
|
for k, _ := range apiMap {
|
|
if _, ok := cfgMap[k]; !ok {
|
|
// The service in the API is not in the config; disable it.
|
|
err := disableService(k, pid, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// The service exists in the config and the API, so we don't need
|
|
// to re-enable it
|
|
delete(cfgMap, k)
|
|
}
|
|
}
|
|
|
|
for k, _ := range cfgMap {
|
|
err := enableService(k, pid, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Retrieve services defined in a config
|
|
func getConfigServices(d *schema.ResourceData) (services []string) {
|
|
if v, ok := d.GetOk("services"); ok {
|
|
for _, svc := range v.(*schema.Set).List() {
|
|
services = append(services, svc.(string))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Retrieve a project's services from the API
|
|
func getApiServices(pid string, config *Config) ([]string, error) {
|
|
apiServices := make([]string, 0)
|
|
// Get services from the API
|
|
token := ""
|
|
for paginate := true; paginate; {
|
|
svcResp, err := config.clientServiceMan.Services.List().ConsumerId("project:" + pid).PageToken(token).Do()
|
|
if err != nil {
|
|
return apiServices, err
|
|
}
|
|
for _, v := range svcResp.Services {
|
|
if _, ok := ignore[v.ServiceName]; !ok {
|
|
apiServices = append(apiServices, v.ServiceName)
|
|
}
|
|
}
|
|
token = svcResp.NextPageToken
|
|
paginate = token != ""
|
|
}
|
|
return apiServices, nil
|
|
}
|
|
|
|
func enableService(s, pid string, config *Config) error {
|
|
esr := newEnableServiceRequest(pid)
|
|
sop, err := config.clientServiceMan.Services.Enable(s, esr).Do()
|
|
if err != nil {
|
|
return fmt.Errorf("Error enabling service %q for project %q: %v", s, pid, err)
|
|
}
|
|
// Wait for the operation to complete
|
|
waitErr := serviceManagementOperationWait(config, sop, "api to enable")
|
|
if waitErr != nil {
|
|
return waitErr
|
|
}
|
|
return nil
|
|
}
|
|
func disableService(s, pid string, config *Config) error {
|
|
dsr := newDisableServiceRequest(pid)
|
|
sop, err := config.clientServiceMan.Services.Disable(s, dsr).Do()
|
|
if err != nil {
|
|
return fmt.Errorf("Error disabling service %q for project %q: %v", s, pid, err)
|
|
}
|
|
// Wait for the operation to complete
|
|
waitErr := serviceManagementOperationWait(config, sop, "api to disable")
|
|
if waitErr != nil {
|
|
return waitErr
|
|
}
|
|
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
|
|
if s := d.Get("services"); s != nil {
|
|
ss := s.(*schema.Set)
|
|
services = make([]string, ss.Len())
|
|
for i, v := range ss.List() {
|
|
services[i] = v.(string)
|
|
}
|
|
}
|
|
return services
|
|
}
|