github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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": {
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  			},
    29  
    30  			"zone": {
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  				ForceNew: true,
    34  			},
    35  
    36  			"description": {
    37  				Type:     schema.TypeString,
    38  				Optional: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"instances": {
    43  				Type:     schema.TypeSet,
    44  				Optional: true,
    45  				Computed: true,
    46  				Elem:     &schema.Schema{Type: schema.TypeString},
    47  				Set:      schema.HashString,
    48  			},
    49  
    50  			"named_port": {
    51  				Type:     schema.TypeList,
    52  				Optional: true,
    53  				Elem: &schema.Resource{
    54  					Schema: map[string]*schema.Schema{
    55  						"name": {
    56  							Type:     schema.TypeString,
    57  							Required: true,
    58  						},
    59  
    60  						"port": {
    61  							Type:     schema.TypeInt,
    62  							Required: true,
    63  						},
    64  					},
    65  				},
    66  			},
    67  
    68  			"network": {
    69  				Type:     schema.TypeString,
    70  				Optional: true,
    71  				Computed: true,
    72  				ForceNew: true,
    73  			},
    74  
    75  			"project": {
    76  				Type:     schema.TypeString,
    77  				Optional: true,
    78  				ForceNew: true,
    79  			},
    80  
    81  			"self_link": {
    82  				Type:     schema.TypeString,
    83  				Computed: true,
    84  			},
    85  
    86  			"size": {
    87  				Type:     schema.TypeInt,
    88  				Computed: true,
    89  			},
    90  		},
    91  	}
    92  }
    93  
    94  func getInstanceReferences(instanceUrls []string) (refs []*compute.InstanceReference) {
    95  	for _, v := range instanceUrls {
    96  		refs = append(refs, &compute.InstanceReference{
    97  			Instance: v,
    98  		})
    99  	}
   100  	return refs
   101  }
   102  
   103  func validInstanceURLs(instanceUrls []string) bool {
   104  	for _, v := range instanceUrls {
   105  		if !strings.HasPrefix(v, "https://www.googleapis.com/compute/v1/") {
   106  			return false
   107  		}
   108  	}
   109  	return true
   110  }
   111  
   112  func resourceComputeInstanceGroupCreate(d *schema.ResourceData, meta interface{}) error {
   113  	config := meta.(*Config)
   114  
   115  	project, err := getProject(d, config)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	// Build the parameter
   121  	instanceGroup := &compute.InstanceGroup{
   122  		Name: d.Get("name").(string),
   123  	}
   124  
   125  	// Set optional fields
   126  	if v, ok := d.GetOk("description"); ok {
   127  		instanceGroup.Description = v.(string)
   128  	}
   129  
   130  	if v, ok := d.GetOk("named_port"); ok {
   131  		instanceGroup.NamedPorts = getNamedPorts(v.([]interface{}))
   132  	}
   133  
   134  	if v, ok := d.GetOk("network"); ok {
   135  		instanceGroup.Network = v.(string)
   136  	}
   137  
   138  	log.Printf("[DEBUG] InstanceGroup insert request: %#v", instanceGroup)
   139  	op, err := config.clientCompute.InstanceGroups.Insert(
   140  		project, d.Get("zone").(string), instanceGroup).Do()
   141  	if err != nil {
   142  		return fmt.Errorf("Error creating InstanceGroup: %s", err)
   143  	}
   144  
   145  	// It probably maybe worked, so store the ID now
   146  	d.SetId(instanceGroup.Name)
   147  
   148  	// Wait for the operation to complete
   149  	err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating InstanceGroup")
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	if v, ok := d.GetOk("instances"); ok {
   155  		instanceUrls := convertStringArr(v.(*schema.Set).List())
   156  		if !validInstanceURLs(instanceUrls) {
   157  			return fmt.Errorf("Error invalid instance URLs: %v", instanceUrls)
   158  		}
   159  
   160  		addInstanceReq := &compute.InstanceGroupsAddInstancesRequest{
   161  			Instances: getInstanceReferences(instanceUrls),
   162  		}
   163  
   164  		log.Printf("[DEBUG] InstanceGroup add instances request: %#v", addInstanceReq)
   165  		op, err := config.clientCompute.InstanceGroups.AddInstances(
   166  			project, d.Get("zone").(string), d.Id(), addInstanceReq).Do()
   167  		if err != nil {
   168  			return fmt.Errorf("Error adding instances to InstanceGroup: %s", err)
   169  		}
   170  
   171  		// Wait for the operation to complete
   172  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Adding instances to InstanceGroup")
   173  		if err != nil {
   174  			return err
   175  		}
   176  	}
   177  
   178  	return resourceComputeInstanceGroupRead(d, meta)
   179  }
   180  
   181  func resourceComputeInstanceGroupRead(d *schema.ResourceData, meta interface{}) error {
   182  	config := meta.(*Config)
   183  
   184  	project, err := getProject(d, config)
   185  	if err != nil {
   186  		return err
   187  	}
   188  
   189  	// retreive instance group
   190  	instanceGroup, err := config.clientCompute.InstanceGroups.Get(
   191  		project, d.Get("zone").(string), d.Id()).Do()
   192  	if err != nil {
   193  		return handleNotFoundError(err, d, fmt.Sprintf("Instance Group %q", d.Get("name").(string)))
   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  }