github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/resource_google_project_services.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/helper/schema"
     8  	"google.golang.org/api/servicemanagement/v1"
     9  )
    10  
    11  func resourceGoogleProjectServices() *schema.Resource {
    12  	return &schema.Resource{
    13  		Create: resourceGoogleProjectServicesCreate,
    14  		Read:   resourceGoogleProjectServicesRead,
    15  		Update: resourceGoogleProjectServicesUpdate,
    16  		Delete: resourceGoogleProjectServicesDelete,
    17  
    18  		Schema: map[string]*schema.Schema{
    19  			"project": &schema.Schema{
    20  				Type:     schema.TypeString,
    21  				Required: true,
    22  				ForceNew: true,
    23  			},
    24  			"services": {
    25  				Type:     schema.TypeSet,
    26  				Required: true,
    27  				Elem:     &schema.Schema{Type: schema.TypeString},
    28  				Set:      schema.HashString,
    29  			},
    30  		},
    31  	}
    32  }
    33  
    34  func resourceGoogleProjectServicesCreate(d *schema.ResourceData, meta interface{}) error {
    35  	config := meta.(*Config)
    36  	pid := d.Get("project").(string)
    37  
    38  	// Get services from config
    39  	cfgServices := getConfigServices(d)
    40  
    41  	// Get services from API
    42  	apiServices, err := getApiServices(pid, config)
    43  	if err != nil {
    44  		return fmt.Errorf("Error creating services: %v", err)
    45  	}
    46  
    47  	// This call disables any APIs that aren't defined in cfgServices,
    48  	// and enables all of those that are
    49  	err = reconcileServices(cfgServices, apiServices, config, pid)
    50  	if err != nil {
    51  		return fmt.Errorf("Error creating services: %v", err)
    52  	}
    53  
    54  	d.SetId(pid)
    55  	return resourceGoogleProjectServicesRead(d, meta)
    56  }
    57  
    58  func resourceGoogleProjectServicesRead(d *schema.ResourceData, meta interface{}) error {
    59  	config := meta.(*Config)
    60  
    61  	services, err := getApiServices(d.Id(), config)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	d.Set("services", services)
    67  	return nil
    68  }
    69  
    70  func resourceGoogleProjectServicesUpdate(d *schema.ResourceData, meta interface{}) error {
    71  	log.Printf("[DEBUG]: Updating google_project_services")
    72  	config := meta.(*Config)
    73  	pid := d.Get("project").(string)
    74  
    75  	// Get services from config
    76  	cfgServices := getConfigServices(d)
    77  
    78  	// Get services from API
    79  	apiServices, err := getApiServices(pid, config)
    80  	if err != nil {
    81  		return fmt.Errorf("Error updating services: %v", err)
    82  	}
    83  
    84  	// This call disables any APIs that aren't defined in cfgServices,
    85  	// and enables all of those that are
    86  	err = reconcileServices(cfgServices, apiServices, config, pid)
    87  	if err != nil {
    88  		return fmt.Errorf("Error updating services: %v", err)
    89  	}
    90  
    91  	return resourceGoogleProjectServicesRead(d, meta)
    92  }
    93  
    94  func resourceGoogleProjectServicesDelete(d *schema.ResourceData, meta interface{}) error {
    95  	log.Printf("[DEBUG]: Deleting google_project_services")
    96  	config := meta.(*Config)
    97  	services := resourceServices(d)
    98  	for _, s := range services {
    99  		disableService(s, d.Id(), config)
   100  	}
   101  	d.SetId("")
   102  	return nil
   103  }
   104  
   105  // This function ensures that the services enabled for a project exactly match that
   106  // in a config by disabling any services that are returned by the API but not present
   107  // in the config
   108  func reconcileServices(cfgServices, apiServices []string, config *Config, pid string) error {
   109  	// Helper to convert slice to map
   110  	m := func(vals []string) map[string]struct{} {
   111  		sm := make(map[string]struct{})
   112  		for _, s := range vals {
   113  			sm[s] = struct{}{}
   114  		}
   115  		return sm
   116  	}
   117  
   118  	cfgMap := m(cfgServices)
   119  	apiMap := m(apiServices)
   120  
   121  	for k, _ := range apiMap {
   122  		if _, ok := cfgMap[k]; !ok {
   123  			// The service in the API is not in the config; disable it.
   124  			err := disableService(k, pid, config)
   125  			if err != nil {
   126  				return err
   127  			}
   128  		} else {
   129  			// The service exists in the config and the API, so we don't need
   130  			// to re-enable it
   131  			delete(cfgMap, k)
   132  		}
   133  	}
   134  
   135  	for k, _ := range cfgMap {
   136  		err := enableService(k, pid, config)
   137  		if err != nil {
   138  			return err
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  // Retrieve services defined in a config
   145  func getConfigServices(d *schema.ResourceData) (services []string) {
   146  	if v, ok := d.GetOk("services"); ok {
   147  		for _, svc := range v.(*schema.Set).List() {
   148  			services = append(services, svc.(string))
   149  		}
   150  	}
   151  	return
   152  }
   153  
   154  // Retrieve a project's services from the API
   155  func getApiServices(pid string, config *Config) ([]string, error) {
   156  	apiServices := make([]string, 0)
   157  	// Get services from the API
   158  	svcResp, err := config.clientServiceMan.Services.List().ConsumerId("project:" + pid).Do()
   159  	if err != nil {
   160  		return apiServices, err
   161  	}
   162  	for _, v := range svcResp.Services {
   163  		apiServices = append(apiServices, v.ServiceName)
   164  	}
   165  	return apiServices, nil
   166  }
   167  
   168  func enableService(s, pid string, config *Config) error {
   169  	esr := newEnableServiceRequest(pid)
   170  	sop, err := config.clientServiceMan.Services.Enable(s, esr).Do()
   171  	if err != nil {
   172  		return fmt.Errorf("Error enabling service %q for project %q: %v", s, pid, err)
   173  	}
   174  	// Wait for the operation to complete
   175  	waitErr := serviceManagementOperationWait(config, sop, "api to enable")
   176  	if waitErr != nil {
   177  		return waitErr
   178  	}
   179  	return nil
   180  }
   181  func disableService(s, pid string, config *Config) error {
   182  	dsr := newDisableServiceRequest(pid)
   183  	sop, err := config.clientServiceMan.Services.Disable(s, dsr).Do()
   184  	if err != nil {
   185  		return fmt.Errorf("Error disabling service %q for project %q: %v", s, pid, err)
   186  	}
   187  	// Wait for the operation to complete
   188  	waitErr := serviceManagementOperationWait(config, sop, "api to disable")
   189  	if waitErr != nil {
   190  		return waitErr
   191  	}
   192  	return nil
   193  }
   194  
   195  func newEnableServiceRequest(pid string) *servicemanagement.EnableServiceRequest {
   196  	return &servicemanagement.EnableServiceRequest{ConsumerId: "project:" + pid}
   197  }
   198  
   199  func newDisableServiceRequest(pid string) *servicemanagement.DisableServiceRequest {
   200  	return &servicemanagement.DisableServiceRequest{ConsumerId: "project:" + pid}
   201  }
   202  
   203  func resourceServices(d *schema.ResourceData) []string {
   204  	// Calculate the tags
   205  	var services []string
   206  	if s := d.Get("services"); s != nil {
   207  		ss := s.(*schema.Set)
   208  		services = make([]string, ss.Len())
   209  		for i, v := range ss.List() {
   210  			services[i] = v.(string)
   211  		}
   212  	}
   213  	return services
   214  }