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 }