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