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