github.com/candidpartners/terraform@v0.9.5-0.20171005231213-29f5f88820f6/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 svcResp, err := config.clientServiceMan.Services.List().ConsumerId("project:" + pid).Do() 167 if err != nil { 168 return apiServices, err 169 } 170 for _, v := range svcResp.Services { 171 if _, ok := ignore[v.ServiceName]; !ok { 172 apiServices = append(apiServices, v.ServiceName) 173 } 174 } 175 return apiServices, nil 176 } 177 178 func enableService(s, pid string, config *Config) error { 179 esr := newEnableServiceRequest(pid) 180 sop, err := config.clientServiceMan.Services.Enable(s, esr).Do() 181 if err != nil { 182 return fmt.Errorf("Error enabling service %q for project %q: %v", s, pid, err) 183 } 184 // Wait for the operation to complete 185 waitErr := serviceManagementOperationWait(config, sop, "api to enable") 186 if waitErr != nil { 187 return waitErr 188 } 189 return nil 190 } 191 func disableService(s, pid string, config *Config) error { 192 dsr := newDisableServiceRequest(pid) 193 sop, err := config.clientServiceMan.Services.Disable(s, dsr).Do() 194 if err != nil { 195 return fmt.Errorf("Error disabling service %q for project %q: %v", s, pid, err) 196 } 197 // Wait for the operation to complete 198 waitErr := serviceManagementOperationWait(config, sop, "api to disable") 199 if waitErr != nil { 200 return waitErr 201 } 202 return nil 203 } 204 205 func newEnableServiceRequest(pid string) *servicemanagement.EnableServiceRequest { 206 return &servicemanagement.EnableServiceRequest{ConsumerId: "project:" + pid} 207 } 208 209 func newDisableServiceRequest(pid string) *servicemanagement.DisableServiceRequest { 210 return &servicemanagement.DisableServiceRequest{ConsumerId: "project:" + pid} 211 } 212 213 func resourceServices(d *schema.ResourceData) []string { 214 // Calculate the tags 215 var services []string 216 if s := d.Get("services"); s != nil { 217 ss := s.(*schema.Set) 218 services = make([]string, ss.Len()) 219 for i, v := range ss.List() { 220 services[i] = v.(string) 221 } 222 } 223 return services 224 }