github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/google/resource_container_cluster.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 "net" 7 "regexp" 8 "time" 9 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 "google.golang.org/api/container/v1" 13 ) 14 15 func resourceContainerCluster() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceContainerClusterCreate, 18 Read: resourceContainerClusterRead, 19 Update: resourceContainerClusterUpdate, 20 Delete: resourceContainerClusterDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "zone": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 29 "node_version": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 Computed: true, 33 }, 34 35 "cluster_ipv4_cidr": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 ForceNew: true, 40 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 41 value := v.(string) 42 _, ipnet, err := net.ParseCIDR(value) 43 44 if err != nil || ipnet == nil || value != ipnet.String() { 45 errors = append(errors, fmt.Errorf( 46 "%q must contain a valid CIDR", k)) 47 } 48 return 49 }, 50 }, 51 52 "description": &schema.Schema{ 53 Type: schema.TypeString, 54 Optional: true, 55 ForceNew: true, 56 }, 57 58 "endpoint": &schema.Schema{ 59 Type: schema.TypeString, 60 Computed: true, 61 }, 62 63 "logging_service": &schema.Schema{ 64 Type: schema.TypeString, 65 Optional: true, 66 Computed: true, 67 ForceNew: true, 68 }, 69 70 "monitoring_service": &schema.Schema{ 71 Type: schema.TypeString, 72 Optional: true, 73 Computed: true, 74 ForceNew: true, 75 }, 76 77 "master_auth": &schema.Schema{ 78 Type: schema.TypeList, 79 Required: true, 80 ForceNew: true, 81 Elem: &schema.Resource{ 82 Schema: map[string]*schema.Schema{ 83 "client_certificate": &schema.Schema{ 84 Type: schema.TypeString, 85 Computed: true, 86 }, 87 "client_key": &schema.Schema{ 88 Type: schema.TypeString, 89 Computed: true, 90 }, 91 "cluster_ca_certificate": &schema.Schema{ 92 Type: schema.TypeString, 93 Computed: true, 94 }, 95 96 "password": &schema.Schema{ 97 Type: schema.TypeString, 98 Required: true, 99 ForceNew: true, 100 }, 101 102 "username": &schema.Schema{ 103 Type: schema.TypeString, 104 Required: true, 105 ForceNew: true, 106 }, 107 }, 108 }, 109 }, 110 111 "name": &schema.Schema{ 112 Type: schema.TypeString, 113 Required: true, 114 ForceNew: true, 115 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 116 value := v.(string) 117 118 if len(value) > 40 { 119 errors = append(errors, fmt.Errorf( 120 "%q cannot be longer than 40 characters", k)) 121 } 122 if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(value) { 123 errors = append(errors, fmt.Errorf( 124 "%q can only contain lowercase letters, numbers and hyphens", k)) 125 } 126 if !regexp.MustCompile("^[a-z]").MatchString(value) { 127 errors = append(errors, fmt.Errorf( 128 "%q must start with a letter", k)) 129 } 130 if !regexp.MustCompile("[a-z0-9]$").MatchString(value) { 131 errors = append(errors, fmt.Errorf( 132 "%q must end with a number or a letter", k)) 133 } 134 return 135 }, 136 }, 137 138 "network": &schema.Schema{ 139 Type: schema.TypeString, 140 Optional: true, 141 Default: "default", 142 ForceNew: true, 143 }, 144 145 "node_config": &schema.Schema{ 146 Type: schema.TypeList, 147 Optional: true, 148 Computed: true, 149 ForceNew: true, 150 Elem: &schema.Resource{ 151 Schema: map[string]*schema.Schema{ 152 "machine_type": &schema.Schema{ 153 Type: schema.TypeString, 154 Optional: true, 155 Computed: true, 156 ForceNew: true, 157 }, 158 159 "disk_size_gb": &schema.Schema{ 160 Type: schema.TypeInt, 161 Optional: true, 162 Computed: true, 163 ForceNew: true, 164 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 165 value := v.(int) 166 167 if value < 10 { 168 errors = append(errors, fmt.Errorf( 169 "%q cannot be less than 10", k)) 170 } 171 return 172 }, 173 }, 174 175 "oauth_scopes": &schema.Schema{ 176 Type: schema.TypeList, 177 Elem: &schema.Schema{Type: schema.TypeString}, 178 Optional: true, 179 Computed: true, 180 ForceNew: true, 181 }, 182 }, 183 }, 184 }, 185 186 "initial_node_count": &schema.Schema{ 187 Type: schema.TypeInt, 188 Required: true, 189 ForceNew: true, 190 }, 191 192 "instance_group_urls": &schema.Schema{ 193 Type: schema.TypeList, 194 Computed: true, 195 Elem: &schema.Schema{Type: schema.TypeString}, 196 }, 197 }, 198 } 199 } 200 201 func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error { 202 config := meta.(*Config) 203 204 zoneName := d.Get("zone").(string) 205 clusterName := d.Get("name").(string) 206 207 masterAuths := d.Get("master_auth").([]interface{}) 208 if len(masterAuths) > 1 { 209 return fmt.Errorf("Cannot specify more than one master_auth.") 210 } 211 masterAuth := masterAuths[0].(map[string]interface{}) 212 213 cluster := &container.Cluster{ 214 MasterAuth: &container.MasterAuth{ 215 Password: masterAuth["password"].(string), 216 Username: masterAuth["username"].(string), 217 }, 218 Name: clusterName, 219 InitialNodeCount: int64(d.Get("initial_node_count").(int)), 220 } 221 222 if v, ok := d.GetOk("cluster_ipv4_cidr"); ok { 223 cluster.ClusterIpv4Cidr = v.(string) 224 } 225 226 if v, ok := d.GetOk("description"); ok { 227 cluster.Description = v.(string) 228 } 229 230 if v, ok := d.GetOk("logging_service"); ok { 231 cluster.LoggingService = v.(string) 232 } 233 234 if v, ok := d.GetOk("monitoring_service"); ok { 235 cluster.MonitoringService = v.(string) 236 } 237 238 if v, ok := d.GetOk("network"); ok { 239 cluster.Network = v.(string) 240 } 241 242 if v, ok := d.GetOk("node_config"); ok { 243 nodeConfigs := v.([]interface{}) 244 if len(nodeConfigs) > 1 { 245 return fmt.Errorf("Cannot specify more than one node_config.") 246 } 247 nodeConfig := nodeConfigs[0].(map[string]interface{}) 248 249 cluster.NodeConfig = &container.NodeConfig{} 250 251 if v, ok = nodeConfig["machine_type"]; ok { 252 cluster.NodeConfig.MachineType = v.(string) 253 } 254 255 if v, ok = nodeConfig["disk_size_gb"]; ok { 256 cluster.NodeConfig.DiskSizeGb = int64(v.(int)) 257 } 258 259 if v, ok := nodeConfig["oauth_scopes"]; ok { 260 scopesList := v.([]interface{}) 261 scopes := []string{} 262 for _, v := range scopesList { 263 scopes = append(scopes, v.(string)) 264 } 265 266 cluster.NodeConfig.OauthScopes = scopes 267 } 268 } 269 270 req := &container.CreateClusterRequest{ 271 Cluster: cluster, 272 } 273 274 op, err := config.clientContainer.Projects.Zones.Clusters.Create( 275 config.Project, zoneName, req).Do() 276 if err != nil { 277 return err 278 } 279 280 // Wait until it's created 281 wait := resource.StateChangeConf{ 282 Pending: []string{"PENDING", "RUNNING"}, 283 Target: "DONE", 284 Timeout: 30 * time.Minute, 285 MinTimeout: 3 * time.Second, 286 Refresh: func() (interface{}, string, error) { 287 resp, err := config.clientContainer.Projects.Zones.Operations.Get( 288 config.Project, zoneName, op.Name).Do() 289 log.Printf("[DEBUG] Progress of creating GKE cluster %s: %s", 290 clusterName, resp.Status) 291 return resp, resp.Status, err 292 }, 293 } 294 295 _, err = wait.WaitForState() 296 if err != nil { 297 return err 298 } 299 300 log.Printf("[INFO] GKE cluster %s has been created", clusterName) 301 302 d.SetId(clusterName) 303 304 return resourceContainerClusterRead(d, meta) 305 } 306 307 func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) error { 308 config := meta.(*Config) 309 310 zoneName := d.Get("zone").(string) 311 312 cluster, err := config.clientContainer.Projects.Zones.Clusters.Get( 313 config.Project, zoneName, d.Get("name").(string)).Do() 314 if err != nil { 315 return err 316 } 317 318 d.Set("name", cluster.Name) 319 d.Set("zone", cluster.Zone) 320 d.Set("endpoint", cluster.Endpoint) 321 322 masterAuth := []map[string]interface{}{ 323 map[string]interface{}{ 324 "username": cluster.MasterAuth.Username, 325 "password": cluster.MasterAuth.Password, 326 "client_certificate": cluster.MasterAuth.ClientCertificate, 327 "client_key": cluster.MasterAuth.ClientKey, 328 "cluster_ca_certificate": cluster.MasterAuth.ClusterCaCertificate, 329 }, 330 } 331 d.Set("master_auth", masterAuth) 332 333 d.Set("initial_node_count", cluster.InitialNodeCount) 334 d.Set("node_version", cluster.CurrentNodeVersion) 335 d.Set("cluster_ipv4_cidr", cluster.ClusterIpv4Cidr) 336 d.Set("description", cluster.Description) 337 d.Set("logging_service", cluster.LoggingService) 338 d.Set("monitoring_service", cluster.MonitoringService) 339 d.Set("network", cluster.Network) 340 d.Set("node_config", flattenClusterNodeConfig(cluster.NodeConfig)) 341 d.Set("instance_group_urls", cluster.InstanceGroupUrls) 342 343 return nil 344 } 345 346 func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) error { 347 config := meta.(*Config) 348 349 zoneName := d.Get("zone").(string) 350 clusterName := d.Get("name").(string) 351 desiredNodeVersion := d.Get("node_version").(string) 352 353 req := &container.UpdateClusterRequest{ 354 Update: &container.ClusterUpdate{ 355 DesiredNodeVersion: desiredNodeVersion, 356 }, 357 } 358 op, err := config.clientContainer.Projects.Zones.Clusters.Update( 359 config.Project, zoneName, clusterName, req).Do() 360 if err != nil { 361 return err 362 } 363 364 // Wait until it's updated 365 wait := resource.StateChangeConf{ 366 Pending: []string{"PENDING", "RUNNING"}, 367 Target: "DONE", 368 Timeout: 10 * time.Minute, 369 MinTimeout: 2 * time.Second, 370 Refresh: func() (interface{}, string, error) { 371 log.Printf("[DEBUG] Checking if GKE cluster %s is updated", clusterName) 372 resp, err := config.clientContainer.Projects.Zones.Operations.Get( 373 config.Project, zoneName, op.Name).Do() 374 log.Printf("[DEBUG] Progress of updating GKE cluster %s: %s", 375 clusterName, resp.Status) 376 return resp, resp.Status, err 377 }, 378 } 379 380 _, err = wait.WaitForState() 381 if err != nil { 382 return err 383 } 384 385 log.Printf("[INFO] GKE cluster %s has been updated to %s", d.Id(), 386 desiredNodeVersion) 387 388 return resourceContainerClusterRead(d, meta) 389 } 390 391 func resourceContainerClusterDelete(d *schema.ResourceData, meta interface{}) error { 392 config := meta.(*Config) 393 394 zoneName := d.Get("zone").(string) 395 clusterName := d.Get("name").(string) 396 397 log.Printf("[DEBUG] Deleting GKE cluster %s", d.Get("name").(string)) 398 op, err := config.clientContainer.Projects.Zones.Clusters.Delete( 399 config.Project, zoneName, clusterName).Do() 400 if err != nil { 401 return err 402 } 403 404 // Wait until it's deleted 405 wait := resource.StateChangeConf{ 406 Pending: []string{"PENDING", "RUNNING"}, 407 Target: "DONE", 408 Timeout: 10 * time.Minute, 409 MinTimeout: 3 * time.Second, 410 Refresh: func() (interface{}, string, error) { 411 log.Printf("[DEBUG] Checking if GKE cluster %s is deleted", clusterName) 412 resp, err := config.clientContainer.Projects.Zones.Operations.Get( 413 config.Project, zoneName, op.Name).Do() 414 log.Printf("[DEBUG] Progress of deleting GKE cluster %s: %s", 415 clusterName, resp.Status) 416 return resp, resp.Status, err 417 }, 418 } 419 420 _, err = wait.WaitForState() 421 if err != nil { 422 return err 423 } 424 425 log.Printf("[INFO] GKE cluster %s has been deleted", d.Id()) 426 427 d.SetId("") 428 429 return nil 430 } 431 432 func flattenClusterNodeConfig(c *container.NodeConfig) []map[string]interface{} { 433 config := []map[string]interface{}{ 434 map[string]interface{}{ 435 "machine_type": c.MachineType, 436 "disk_size_gb": c.DiskSizeGb, 437 }, 438 } 439 440 if len(c.OauthScopes) > 0 { 441 config[0]["oauth_scopes"] = c.OauthScopes 442 } 443 444 return config 445 }