github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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  // These services can only be enabled as a side-effect of enabling other services,
    35  // so don't bother storing them in the config or using them for diffing.
    36  var ignore = map[string]struct{}{
    37  	"containeranalysis.googleapis.com": struct{}{},
    38  	"dataproc-control.googleapis.com":  struct{}{},
    39  	"source.googleapis.com":            struct{}{},
    40  }
    41  
    42  func resourceGoogleProjectServicesCreate(d *schema.ResourceData, meta interface{}) error {
    43  	config := meta.(*Config)
    44  	pid := d.Get("project").(string)
    45  
    46  	// Get services from config
    47  	cfgServices := getConfigServices(d)
    48  
    49  	// Get services from API
    50  	apiServices, err := getApiServices(pid, config)
    51  	if err != nil {
    52  		return fmt.Errorf("Error creating services: %v", err)
    53  	}
    54  
    55  	// This call disables any APIs that aren't defined in cfgServices,
    56  	// and enables all of those that are
    57  	err = reconcileServices(cfgServices, apiServices, config, pid)
    58  	if err != nil {
    59  		return fmt.Errorf("Error creating services: %v", err)
    60  	}
    61  
    62  	d.SetId(pid)
    63  	return resourceGoogleProjectServicesRead(d, meta)
    64  }
    65  
    66  func resourceGoogleProjectServicesRead(d *schema.ResourceData, meta interface{}) error {
    67  	config := meta.(*Config)
    68  
    69  	services, err := getApiServices(d.Id(), config)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	d.Set("services", services)
    75  	return nil
    76  }
    77  
    78  func resourceGoogleProjectServicesUpdate(d *schema.ResourceData, meta interface{}) error {
    79  	log.Printf("[DEBUG]: Updating google_project_services")
    80  	config := meta.(*Config)
    81  	pid := d.Get("project").(string)
    82  
    83  	// Get services from config
    84  	cfgServices := getConfigServices(d)
    85  
    86  	// Get services from API
    87  	apiServices, err := getApiServices(pid, config)
    88  	if err != nil {
    89  		return fmt.Errorf("Error updating services: %v", err)
    90  	}
    91  
    92  	// This call disables any APIs that aren't defined in cfgServices,
    93  	// and enables all of those that are
    94  	err = reconcileServices(cfgServices, apiServices, config, pid)
    95  	if err != nil {
    96  		return fmt.Errorf("Error updating services: %v", err)
    97  	}
    98  
    99  	return resourceGoogleProjectServicesRead(d, meta)
   100  }
   101  
   102  func resourceGoogleProjectServicesDelete(d *schema.ResourceData, meta interface{}) error {
   103  	log.Printf("[DEBUG]: Deleting google_project_services")
   104  	config := meta.(*Config)
   105  	services := resourceServices(d)
   106  	for _, s := range services {
   107  		disableService(s, d.Id(), config)
   108  	}
   109  	d.SetId("")
   110  	return nil
   111  }
   112  
   113  // This function ensures that the services enabled for a project exactly match that
   114  // in a config by disabling any services that are returned by the API but not present
   115  // in the config
   116  func reconcileServices(cfgServices, apiServices []string, config *Config, pid string) error {
   117  	// Helper to convert slice to map
   118  	m := func(vals []string) map[string]struct{} {
   119  		sm := make(map[string]struct{})
   120  		for _, s := range vals {
   121  			sm[s] = struct{}{}
   122  		}
   123  		return sm
   124  	}
   125  
   126  	cfgMap := m(cfgServices)
   127  	apiMap := m(apiServices)
   128  
   129  	for k, _ := range apiMap {
   130  		if _, ok := cfgMap[k]; !ok {
   131  			// The service in the API is not in the config; disable it.
   132  			err := disableService(k, pid, config)
   133  			if err != nil {
   134  				return err
   135  			}
   136  		} else {
   137  			// The service exists in the config and the API, so we don't need
   138  			// to re-enable it
   139  			delete(cfgMap, k)
   140  		}
   141  	}
   142  
   143  	for k, _ := range cfgMap {
   144  		err := enableService(k, pid, config)
   145  		if err != nil {
   146  			return err
   147  		}
   148  	}
   149  	return nil
   150  }
   151  
   152  // Retrieve services defined in a config
   153  func getConfigServices(d *schema.ResourceData) (services []string) {
   154  	if v, ok := d.GetOk("services"); ok {
   155  		for _, svc := range v.(*schema.Set).List() {
   156  			services = append(services, svc.(string))
   157  		}
   158  	}
   159  	return
   160  }
   161  
   162  // Retrieve a project's services from the API
   163  func getApiServices(pid string, config *Config) ([]string, error) {
   164  	apiServices := make([]string, 0)
   165  	// Get services from the API
   166  	token := ""
   167  	for paginate := true; paginate; {
   168  		svcResp, err := config.clientServiceMan.Services.List().ConsumerId("project:" + pid).PageToken(token).Do()
   169  		if err != nil {
   170  			return apiServices, err
   171  		}
   172  		for _, v := range svcResp.Services {
   173  			if _, ok := ignore[v.ServiceName]; !ok {
   174  				apiServices = append(apiServices, v.ServiceName)
   175  			}
   176  		}
   177  		token = svcResp.NextPageToken
   178  		paginate = token != ""
   179  	}
   180  	return apiServices, nil
   181  }
   182  
   183  func enableService(s, pid string, config *Config) error {
   184  	esr := newEnableServiceRequest(pid)
   185  	sop, err := config.clientServiceMan.Services.Enable(s, esr).Do()
   186  	if err != nil {
   187  		return fmt.Errorf("Error enabling service %q for project %q: %v", s, pid, err)
   188  	}
   189  	// Wait for the operation to complete
   190  	waitErr := serviceManagementOperationWait(config, sop, "api to enable")
   191  	if waitErr != nil {
   192  		return waitErr
   193  	}
   194  	return nil
   195  }
   196  func disableService(s, pid string, config *Config) error {
   197  	dsr := newDisableServiceRequest(pid)
   198  	sop, err := config.clientServiceMan.Services.Disable(s, dsr).Do()
   199  	if err != nil {
   200  		return fmt.Errorf("Error disabling service %q for project %q: %v", s, pid, err)
   201  	}
   202  	// Wait for the operation to complete
   203  	waitErr := serviceManagementOperationWait(config, sop, "api to disable")
   204  	if waitErr != nil {
   205  		return waitErr
   206  	}
   207  	return nil
   208  }
   209  
   210  func newEnableServiceRequest(pid string) *servicemanagement.EnableServiceRequest {
   211  	return &servicemanagement.EnableServiceRequest{ConsumerId: "project:" + pid}
   212  }
   213  
   214  func newDisableServiceRequest(pid string) *servicemanagement.DisableServiceRequest {
   215  	return &servicemanagement.DisableServiceRequest{ConsumerId: "project:" + pid}
   216  }
   217  
   218  func resourceServices(d *schema.ResourceData) []string {
   219  	// Calculate the tags
   220  	var services []string
   221  	if s := d.Get("services"); s != nil {
   222  		ss := s.(*schema.Set)
   223  		services = make([]string, ss.Len())
   224  		for i, v := range ss.List() {
   225  			services[i] = v.(string)
   226  		}
   227  	}
   228  	return services
   229  }