github.com/subuk/terraform@v0.6.14-0.20160317140351-de1567c2e732/builtin/providers/google/resource_compute_instance_group.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"google.golang.org/api/compute/v1"
     9  	"google.golang.org/api/googleapi"
    10  
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  func resourceComputeInstanceGroup() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceComputeInstanceGroupCreate,
    17  		Read:   resourceComputeInstanceGroupRead,
    18  		Update: resourceComputeInstanceGroupUpdate,
    19  		Delete: resourceComputeInstanceGroupDelete,
    20  
    21  		Schema: map[string]*schema.Schema{
    22  			"name": &schema.Schema{
    23  				Type:     schema.TypeString,
    24  				Required: true,
    25  				ForceNew: true,
    26  			},
    27  
    28  			"description": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Optional: true,
    31  				ForceNew: true,
    32  			},
    33  
    34  			"named_port": &schema.Schema{
    35  				Type:     schema.TypeList,
    36  				Optional: true,
    37  				Elem: &schema.Resource{
    38  					Schema: map[string]*schema.Schema{
    39  						"name": &schema.Schema{
    40  							Type:     schema.TypeString,
    41  							Required: true,
    42  						},
    43  
    44  						"port": &schema.Schema{
    45  							Type:     schema.TypeInt,
    46  							Required: true,
    47  						},
    48  					},
    49  				},
    50  			},
    51  
    52  			"instances": &schema.Schema{
    53  				Type:     schema.TypeList,
    54  				Optional: true,
    55  				Elem:     &schema.Schema{Type: schema.TypeString},
    56  			},
    57  
    58  			"network": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Computed: true,
    61  			},
    62  
    63  			"size": &schema.Schema{
    64  				Type:     schema.TypeInt,
    65  				Computed: true,
    66  			},
    67  
    68  			"zone": &schema.Schema{
    69  				Type:     schema.TypeString,
    70  				Required: true,
    71  				ForceNew: true,
    72  			},
    73  
    74  			"self_link": &schema.Schema{
    75  				Type:     schema.TypeString,
    76  				Computed: true,
    77  			},
    78  		},
    79  	}
    80  }
    81  
    82  func getInstanceReferences(instanceUrls []string) (refs []*compute.InstanceReference) {
    83  	for _, v := range instanceUrls {
    84  		refs = append(refs, &compute.InstanceReference{
    85  			Instance: v,
    86  		})
    87  	}
    88  	return refs
    89  }
    90  
    91  func validInstanceURLs(instanceUrls []string) bool {
    92  	for _, v := range instanceUrls {
    93  		if !strings.HasPrefix(v, "https://www.googleapis.com/compute/v1/") {
    94  			return false
    95  		}
    96  	}
    97  	return true
    98  }
    99  
   100  func resourceComputeInstanceGroupCreate(d *schema.ResourceData, meta interface{}) error {
   101  	config := meta.(*Config)
   102  
   103  	// Build the parameter
   104  	instanceGroup := &compute.InstanceGroup{
   105  		Name: d.Get("name").(string),
   106  	}
   107  
   108  	// Set optional fields
   109  	if v, ok := d.GetOk("description"); ok {
   110  		instanceGroup.Description = v.(string)
   111  	}
   112  
   113  	if v, ok := d.GetOk("named_port"); ok {
   114  		instanceGroup.NamedPorts = getNamedPorts(v.([]interface{}))
   115  	}
   116  
   117  	log.Printf("[DEBUG] InstanceGroup insert request: %#v", instanceGroup)
   118  	op, err := config.clientCompute.InstanceGroups.Insert(
   119  		config.Project, d.Get("zone").(string), instanceGroup).Do()
   120  	if err != nil {
   121  		return fmt.Errorf("Error creating InstanceGroup: %s", err)
   122  	}
   123  
   124  	// It probably maybe worked, so store the ID now
   125  	d.SetId(instanceGroup.Name)
   126  
   127  	// Wait for the operation to complete
   128  	err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Creating InstanceGroup")
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	if v, ok := d.GetOk("instances"); ok {
   134  		instanceUrls := convertStringArr(v.([]interface{}))
   135  		if !validInstanceURLs(instanceUrls) {
   136  			return fmt.Errorf("Error invalid instance URLs: %v", instanceUrls)
   137  		}
   138  
   139  		addInstanceReq := &compute.InstanceGroupsAddInstancesRequest{
   140  			Instances: getInstanceReferences(instanceUrls),
   141  		}
   142  
   143  		log.Printf("[DEBUG] InstanceGroup add instances request: %#v", addInstanceReq)
   144  		op, err := config.clientCompute.InstanceGroups.AddInstances(
   145  			config.Project, d.Get("zone").(string), d.Id(), addInstanceReq).Do()
   146  		if err != nil {
   147  			return fmt.Errorf("Error adding instances to InstanceGroup: %s", err)
   148  		}
   149  
   150  		// Wait for the operation to complete
   151  		err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Adding instances to InstanceGroup")
   152  		if err != nil {
   153  			return err
   154  		}
   155  	}
   156  
   157  	return resourceComputeInstanceGroupRead(d, meta)
   158  }
   159  
   160  func resourceComputeInstanceGroupRead(d *schema.ResourceData, meta interface{}) error {
   161  	config := meta.(*Config)
   162  
   163  	// retreive instance group
   164  	instanceGroup, err := config.clientCompute.InstanceGroups.Get(
   165  		config.Project, d.Get("zone").(string), d.Id()).Do()
   166  	if err != nil {
   167  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   168  			// The resource doesn't exist anymore
   169  			d.SetId("")
   170  
   171  			return nil
   172  		}
   173  
   174  		return fmt.Errorf("Error reading InstanceGroup: %s", err)
   175  	}
   176  
   177  	// retreive instance group members
   178  	var memberUrls []string
   179  	members, err := config.clientCompute.InstanceGroups.ListInstances(
   180  		config.Project, d.Get("zone").(string), d.Id(), &compute.InstanceGroupsListInstancesRequest{
   181  			InstanceState: "ALL",
   182  		}).Do()
   183  	if err != nil {
   184  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   185  			// The resource doesn't have any instances
   186  			d.Set("instances", nil)
   187  		} else {
   188  			// any other errors return them
   189  			return fmt.Errorf("Error reading InstanceGroup Members: %s", err)
   190  		}
   191  	} else {
   192  		for _, member := range members.Items {
   193  			memberUrls = append(memberUrls, member.Instance)
   194  		}
   195  		log.Printf("[DEBUG] InstanceGroup members: %v", memberUrls)
   196  		d.Set("instances", memberUrls)
   197  	}
   198  
   199  	// Set computed fields
   200  	d.Set("network", instanceGroup.Network)
   201  	d.Set("size", instanceGroup.Size)
   202  	d.Set("self_link", instanceGroup.SelfLink)
   203  
   204  	return nil
   205  }
   206  func resourceComputeInstanceGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   207  	config := meta.(*Config)
   208  
   209  	// refresh the state incase referenced instances have been removed earlier in the run
   210  	err := resourceComputeInstanceGroupRead(d, meta)
   211  	if err != nil {
   212  		return fmt.Errorf("Error reading InstanceGroup: %s", err)
   213  	}
   214  
   215  	d.Partial(true)
   216  
   217  	if d.HasChange("instances") {
   218  		// to-do check for no instances
   219  		from_, to_ := d.GetChange("instances")
   220  
   221  		from := convertStringArr(from_.([]interface{}))
   222  		to := convertStringArr(to_.([]interface{}))
   223  
   224  		if !validInstanceURLs(from) {
   225  			return fmt.Errorf("Error invalid instance URLs: %v", from)
   226  		}
   227  		if !validInstanceURLs(to) {
   228  			return fmt.Errorf("Error invalid instance URLs: %v", from)
   229  		}
   230  
   231  		add, remove := calcAddRemove(from, to)
   232  
   233  		if len(remove) > 0 {
   234  			removeReq := &compute.InstanceGroupsRemoveInstancesRequest{
   235  				Instances: getInstanceReferences(remove),
   236  			}
   237  
   238  			log.Printf("[DEBUG] InstanceGroup remove instances request: %#v", removeReq)
   239  			removeOp, err := config.clientCompute.InstanceGroups.RemoveInstances(
   240  				config.Project, d.Get("zone").(string), d.Id(), removeReq).Do()
   241  			if err != nil {
   242  				return fmt.Errorf("Error removing instances from InstanceGroup: %s", err)
   243  			}
   244  
   245  			// Wait for the operation to complete
   246  			err = computeOperationWaitZone(config, removeOp, d.Get("zone").(string), "Updating InstanceGroup")
   247  			if err != nil {
   248  				return err
   249  			}
   250  		}
   251  
   252  		if len(add) > 0 {
   253  
   254  			addReq := &compute.InstanceGroupsAddInstancesRequest{
   255  				Instances: getInstanceReferences(add),
   256  			}
   257  
   258  			log.Printf("[DEBUG] InstanceGroup adding instances request: %#v", addReq)
   259  			addOp, err := config.clientCompute.InstanceGroups.AddInstances(
   260  				config.Project, d.Get("zone").(string), d.Id(), addReq).Do()
   261  			if err != nil {
   262  				return fmt.Errorf("Error adding instances from InstanceGroup: %s", err)
   263  			}
   264  
   265  			// Wait for the operation to complete
   266  			err = computeOperationWaitZone(config, addOp, d.Get("zone").(string), "Updating InstanceGroup")
   267  			if err != nil {
   268  				return err
   269  			}
   270  		}
   271  
   272  		d.SetPartial("instances")
   273  	}
   274  
   275  	if d.HasChange("named_port") {
   276  		namedPorts := getNamedPorts(d.Get("named_port").([]interface{}))
   277  
   278  		namedPortsReq := &compute.InstanceGroupsSetNamedPortsRequest{
   279  			NamedPorts: namedPorts,
   280  		}
   281  
   282  		log.Printf("[DEBUG] InstanceGroup updating named ports request: %#v", namedPortsReq)
   283  		op, err := config.clientCompute.InstanceGroups.SetNamedPorts(
   284  			config.Project, d.Get("zone").(string), d.Id(), namedPortsReq).Do()
   285  		if err != nil {
   286  			return fmt.Errorf("Error updating named ports for InstanceGroup: %s", err)
   287  		}
   288  
   289  		err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Updating InstanceGroup")
   290  		if err != nil {
   291  			return err
   292  		}
   293  		d.SetPartial("named_port")
   294  	}
   295  
   296  	d.Partial(false)
   297  
   298  	return resourceComputeInstanceGroupRead(d, meta)
   299  }
   300  
   301  func resourceComputeInstanceGroupDelete(d *schema.ResourceData, meta interface{}) error {
   302  	config := meta.(*Config)
   303  
   304  	zone := d.Get("zone").(string)
   305  	op, err := config.clientCompute.InstanceGroups.Delete(config.Project, zone, d.Id()).Do()
   306  	if err != nil {
   307  		return fmt.Errorf("Error deleting InstanceGroup: %s", err)
   308  	}
   309  
   310  	err = computeOperationWaitZone(config, op, zone, "Deleting InstanceGroup")
   311  	if err != nil {
   312  		return err
   313  	}
   314  
   315  	d.SetId("")
   316  	return nil
   317  }