package google import ( "context" "fmt" "log" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "cloud.google.com/go/bigtable" ) func resourceBigtableInstance() *schema.Resource { return &schema.Resource{ Create: resourceBigtableInstanceCreate, Read: resourceBigtableInstanceRead, Delete: resourceBigtableInstanceDestroy, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, "cluster": { Type: schema.TypeSet, Required: true, ForceNew: true, MaxItems: 2, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cluster_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, "zone": { Type: schema.TypeString, Required: true, ForceNew: true, }, "num_nodes": { Type: schema.TypeInt, Optional: true, ForceNew: true, ValidateFunc: validation.IntAtLeast(3), }, "storage_type": { Type: schema.TypeString, Optional: true, Default: "SSD", ForceNew: true, ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false), }, }, }, }, "display_name": { Type: schema.TypeString, Optional: true, ForceNew: true, Computed: true, }, "instance_type": { Type: schema.TypeString, Optional: true, ForceNew: true, Default: "PRODUCTION", ValidateFunc: validation.StringInSlice([]string{"DEVELOPMENT", "PRODUCTION"}, false), }, "project": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, }, "cluster_id": { Type: schema.TypeString, Optional: true, Computed: true, Removed: "Use cluster instead.", }, "zone": { Type: schema.TypeString, Optional: true, Computed: true, Removed: "Use cluster instead.", }, "num_nodes": { Type: schema.TypeInt, Optional: true, Computed: true, Removed: "Use cluster instead.", }, "storage_type": { Type: schema.TypeString, Optional: true, Computed: true, Removed: "Use cluster instead.", }, }, } } func resourceBigtableInstanceCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) ctx := context.Background() project, err := getProject(d, config) if err != nil { return err } conf := &bigtable.InstanceWithClustersConfig{ InstanceID: d.Get("name").(string), } displayName, ok := d.GetOk("display_name") if !ok { displayName = conf.InstanceID } conf.DisplayName = displayName.(string) switch d.Get("instance_type").(string) { case "DEVELOPMENT": conf.InstanceType = bigtable.DEVELOPMENT case "PRODUCTION": conf.InstanceType = bigtable.PRODUCTION } conf.Clusters = expandBigtableClusters(d.Get("cluster").(*schema.Set).List(), conf.InstanceID) c, err := config.bigtableClientFactory.NewInstanceAdminClient(project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } defer c.Close() err = c.CreateInstanceWithClusters(ctx, conf) if err != nil { return fmt.Errorf("Error creating instance. %s", err) } d.SetId(conf.InstanceID) return resourceBigtableInstanceRead(d, meta) } func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) ctx := context.Background() project, err := getProject(d, config) if err != nil { return err } c, err := config.bigtableClientFactory.NewInstanceAdminClient(project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } defer c.Close() instance, err := c.InstanceInfo(ctx, d.Id()) if err != nil { log.Printf("[WARN] Removing %s because it's gone", d.Id()) d.SetId("") return fmt.Errorf("Error retrieving instance. Could not find %s. %s", d.Id(), err) } d.Set("project", project) clusters := d.Get("cluster").(*schema.Set).List() clusterState := []map[string]interface{}{} for _, cl := range clusters { cluster := cl.(map[string]interface{}) clus, err := c.GetCluster(ctx, instance.Name, cluster["cluster_id"].(string)) if err != nil { if isGoogleApiErrorWithCode(err, 404) { log.Printf("[WARN] Cluster %q not found, not setting it in state", cluster["cluster_id"].(string)) continue } return fmt.Errorf("Error retrieving cluster %q: %s", cluster["cluster_id"].(string), err.Error()) } clusterState = append(clusterState, flattenBigtableCluster(clus, cluster["storage_type"].(string))) } err = d.Set("cluster", clusterState) if err != nil { return fmt.Errorf("Error setting clusters in state: %s", err.Error()) } d.Set("name", instance.Name) d.Set("display_name", instance.DisplayName) return nil } func resourceBigtableInstanceDestroy(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) ctx := context.Background() project, err := getProject(d, config) if err != nil { return err } c, err := config.bigtableClientFactory.NewInstanceAdminClient(project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } defer c.Close() name := d.Id() err = c.DeleteInstance(ctx, name) if err != nil { return fmt.Errorf("Error deleting instance. %s", err) } d.SetId("") return nil } func flattenBigtableCluster(c *bigtable.ClusterInfo, storageType string) map[string]interface{} { return map[string]interface{}{ "zone": c.Zone, "num_nodes": c.ServeNodes, "cluster_id": c.Name, "storage_type": storageType, } } func expandBigtableClusters(clusters []interface{}, instanceID string) []bigtable.ClusterConfig { results := make([]bigtable.ClusterConfig, 0, len(clusters)) for _, c := range clusters { cluster := c.(map[string]interface{}) zone := cluster["zone"].(string) var storageType bigtable.StorageType switch cluster["storage_type"].(string) { case "SSD": storageType = bigtable.SSD case "HDD": storageType = bigtable.HDD } results = append(results, bigtable.ClusterConfig{ InstanceID: instanceID, Zone: zone, ClusterID: cluster["cluster_id"].(string), NumNodes: int32(cluster["num_nodes"].(int)), StorageType: storageType, }) } return results }