github.com/acm1/terraform@v0.6.2-0.20150729164239-1f314444f45c/builtin/providers/google/resource_compute_instance_template.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/hashicorp/terraform/helper/hashcode"
     8  	"github.com/hashicorp/terraform/helper/schema"
     9  	"google.golang.org/api/compute/v1"
    10  	"google.golang.org/api/googleapi"
    11  )
    12  
    13  func resourceComputeInstanceTemplate() *schema.Resource {
    14  	return &schema.Resource{
    15  		Create: resourceComputeInstanceTemplateCreate,
    16  		Read:   resourceComputeInstanceTemplateRead,
    17  		Delete: resourceComputeInstanceTemplateDelete,
    18  
    19  		Schema: map[string]*schema.Schema{
    20  			"name": &schema.Schema{
    21  				Type:     schema.TypeString,
    22  				Required: true,
    23  				ForceNew: true,
    24  			},
    25  
    26  			"description": &schema.Schema{
    27  				Type:     schema.TypeString,
    28  				Optional: true,
    29  				ForceNew: true,
    30  			},
    31  
    32  			"can_ip_forward": &schema.Schema{
    33  				Type:     schema.TypeBool,
    34  				Optional: true,
    35  				Default:  false,
    36  				ForceNew: true,
    37  			},
    38  
    39  			"instance_description": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Optional: true,
    42  				ForceNew: true,
    43  			},
    44  
    45  			"machine_type": &schema.Schema{
    46  				Type:     schema.TypeString,
    47  				Required: true,
    48  				ForceNew: true,
    49  			},
    50  
    51  			"disk": &schema.Schema{
    52  				Type:     schema.TypeList,
    53  				Required: true,
    54  				ForceNew: true,
    55  				Elem: &schema.Resource{
    56  					Schema: map[string]*schema.Schema{
    57  						"auto_delete": &schema.Schema{
    58  							Type:     schema.TypeBool,
    59  							Optional: true,
    60  							Default:  true,
    61  							ForceNew: true,
    62  						},
    63  
    64  						"boot": &schema.Schema{
    65  							Type:     schema.TypeBool,
    66  							Optional: true,
    67  							ForceNew: true,
    68  						},
    69  
    70  						"device_name": &schema.Schema{
    71  							Type:     schema.TypeString,
    72  							Optional: true,
    73  							ForceNew: true,
    74  						},
    75  
    76  						"disk_name": &schema.Schema{
    77  							Type:     schema.TypeString,
    78  							Optional: true,
    79  							ForceNew: true,
    80  						},
    81  
    82  						"disk_size_gb": &schema.Schema{
    83  							Type:     schema.TypeInt,
    84  							Optional: true,
    85  							ForceNew: true,
    86  						},
    87  
    88  						"disk_type": &schema.Schema{
    89  							Type:     schema.TypeString,
    90  							Optional: true,
    91  							ForceNew: true,
    92  						},
    93  
    94  						"source_image": &schema.Schema{
    95  							Type:     schema.TypeString,
    96  							Optional: true,
    97  							ForceNew: true,
    98  						},
    99  
   100  						"interface": &schema.Schema{
   101  							Type:     schema.TypeString,
   102  							Optional: true,
   103  							ForceNew: true,
   104  						},
   105  
   106  						"mode": &schema.Schema{
   107  							Type:     schema.TypeString,
   108  							Optional: true,
   109  							ForceNew: true,
   110  						},
   111  
   112  						"source": &schema.Schema{
   113  							Type:     schema.TypeString,
   114  							Optional: true,
   115  							ForceNew: true,
   116  						},
   117  
   118  						"type": &schema.Schema{
   119  							Type:     schema.TypeString,
   120  							Optional: true,
   121  							ForceNew: true,
   122  						},
   123  					},
   124  				},
   125  			},
   126  
   127  			"metadata": &schema.Schema{
   128  				Type:     schema.TypeMap,
   129  				Optional: true,
   130  				ForceNew: true,
   131  			},
   132  
   133  			"network_interface": &schema.Schema{
   134  				Type:     schema.TypeList,
   135  				Optional: true,
   136  				ForceNew: true,
   137  				Elem: &schema.Resource{
   138  					Schema: map[string]*schema.Schema{
   139  						"network": &schema.Schema{
   140  							Type:     schema.TypeString,
   141  							Required: true,
   142  							ForceNew: true,
   143  						},
   144  
   145  						"access_config": &schema.Schema{
   146  							Type:     schema.TypeList,
   147  							Optional: true,
   148  							Elem: &schema.Resource{
   149  								Schema: map[string]*schema.Schema{
   150  									"nat_ip": &schema.Schema{
   151  										Type:     schema.TypeString,
   152  										Computed: true,
   153  										Optional: true,
   154  									},
   155  								},
   156  							},
   157  						},
   158  					},
   159  				},
   160  			},
   161  
   162  			"automatic_restart": &schema.Schema{
   163  				Type:     schema.TypeBool,
   164  				Optional: true,
   165  				Default:  true,
   166  				ForceNew: true,
   167  			},
   168  
   169  			"on_host_maintenance": &schema.Schema{
   170  				Type:     schema.TypeString,
   171  				Optional: true,
   172  				ForceNew: true,
   173  			},
   174  
   175  			"service_account": &schema.Schema{
   176  				Type:     schema.TypeList,
   177  				Optional: true,
   178  				ForceNew: true,
   179  				Elem: &schema.Resource{
   180  					Schema: map[string]*schema.Schema{
   181  						"email": &schema.Schema{
   182  							Type:     schema.TypeString,
   183  							Computed: true,
   184  							ForceNew: true,
   185  						},
   186  
   187  						"scopes": &schema.Schema{
   188  							Type:     schema.TypeList,
   189  							Required: true,
   190  							ForceNew: true,
   191  							Elem: &schema.Schema{
   192  								Type: schema.TypeString,
   193  								StateFunc: func(v interface{}) string {
   194  									return canonicalizeServiceScope(v.(string))
   195  								},
   196  							},
   197  						},
   198  					},
   199  				},
   200  			},
   201  
   202  			"tags": &schema.Schema{
   203  				Type:     schema.TypeSet,
   204  				Optional: true,
   205  				ForceNew: true,
   206  				Elem:     &schema.Schema{Type: schema.TypeString},
   207  				Set: func(v interface{}) int {
   208  					return hashcode.String(v.(string))
   209  				},
   210  			},
   211  
   212  			"metadata_fingerprint": &schema.Schema{
   213  				Type:     schema.TypeString,
   214  				Computed: true,
   215  			},
   216  
   217  			"tags_fingerprint": &schema.Schema{
   218  				Type:     schema.TypeString,
   219  				Computed: true,
   220  			},
   221  
   222  			"self_link": &schema.Schema{
   223  				Type:     schema.TypeString,
   224  				Computed: true,
   225  			},
   226  		},
   227  	}
   228  }
   229  
   230  func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) {
   231  	config := meta.(*Config)
   232  
   233  	disksCount := d.Get("disk.#").(int)
   234  
   235  	disks := make([]*compute.AttachedDisk, 0, disksCount)
   236  	for i := 0; i < disksCount; i++ {
   237  		prefix := fmt.Sprintf("disk.%d", i)
   238  
   239  		// Build the disk
   240  		var disk compute.AttachedDisk
   241  		disk.Type = "PERSISTENT"
   242  		disk.Mode = "READ_WRITE"
   243  		disk.Interface = "SCSI"
   244  		disk.Boot = i == 0
   245  		disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool)
   246  
   247  		if v, ok := d.GetOk(prefix + ".boot"); ok {
   248  			disk.Boot = v.(bool)
   249  		}
   250  
   251  		if v, ok := d.GetOk(prefix + ".device_name"); ok {
   252  			disk.DeviceName = v.(string)
   253  		}
   254  
   255  		if v, ok := d.GetOk(prefix + ".source"); ok {
   256  			disk.Source = v.(string)
   257  		} else {
   258  			disk.InitializeParams = &compute.AttachedDiskInitializeParams{}
   259  
   260  			if v, ok := d.GetOk(prefix + ".disk_name"); ok {
   261  				disk.InitializeParams.DiskName = v.(string)
   262  			}
   263  			if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok {
   264  				disk.InitializeParams.DiskSizeGb = v.(int64)
   265  			}
   266  			disk.InitializeParams.DiskType = "pd-standard"
   267  			if v, ok := d.GetOk(prefix + ".disk_type"); ok {
   268  				disk.InitializeParams.DiskType = v.(string)
   269  			}
   270  
   271  			if v, ok := d.GetOk(prefix + ".source_image"); ok {
   272  				imageName := v.(string)
   273  				imageUrl, err := resolveImage(config, imageName)
   274  				if err != nil {
   275  					return nil, fmt.Errorf(
   276  						"Error resolving image name '%s': %s",
   277  						imageName, err)
   278  				}
   279  				disk.InitializeParams.SourceImage = imageUrl
   280  			}
   281  		}
   282  
   283  		if v, ok := d.GetOk(prefix + ".interface"); ok {
   284  			disk.Interface = v.(string)
   285  		}
   286  
   287  		if v, ok := d.GetOk(prefix + ".mode"); ok {
   288  			disk.Mode = v.(string)
   289  		}
   290  
   291  		if v, ok := d.GetOk(prefix + ".type"); ok {
   292  			disk.Type = v.(string)
   293  		}
   294  
   295  		disks = append(disks, &disk)
   296  	}
   297  
   298  	return disks, nil
   299  }
   300  
   301  func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) {
   302  	// Build up the list of networks
   303  	networksCount := d.Get("network_interface.#").(int)
   304  	networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount)
   305  	for i := 0; i < networksCount; i++ {
   306  		prefix := fmt.Sprintf("network_interface.%d", i)
   307  
   308  		source := "global/networks/default"
   309  		if v, ok := d.GetOk(prefix + ".network"); ok {
   310  			if v.(string) != "default" {
   311  				source = v.(string)
   312  			}
   313  		}
   314  
   315  		// Build the networkInterface
   316  		var iface compute.NetworkInterface
   317  		iface.Network = source
   318  
   319  		accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
   320  		iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
   321  		for j := 0; j < accessConfigsCount; j++ {
   322  			acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
   323  			iface.AccessConfigs[j] = &compute.AccessConfig{
   324  				Type:  "ONE_TO_ONE_NAT",
   325  				NatIP: d.Get(acPrefix + ".nat_ip").(string),
   326  			}
   327  		}
   328  
   329  		networkInterfaces = append(networkInterfaces, &iface)
   330  	}
   331  	return nil, networkInterfaces
   332  }
   333  
   334  func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
   335  	config := meta.(*Config)
   336  
   337  	instanceProperties := &compute.InstanceProperties{}
   338  
   339  	instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool)
   340  	instanceProperties.Description = d.Get("instance_description").(string)
   341  	instanceProperties.MachineType = d.Get("machine_type").(string)
   342  	disks, err := buildDisks(d, meta)
   343  	if err != nil {
   344  		return err
   345  	}
   346  	instanceProperties.Disks = disks
   347  	metadata, err := resourceInstanceMetadata(d)
   348  	if err != nil {
   349  		return err
   350  	}
   351  	instanceProperties.Metadata = metadata
   352  	err, networks := buildNetworks(d, meta)
   353  	if err != nil {
   354  		return err
   355  	}
   356  	instanceProperties.NetworkInterfaces = networks
   357  
   358  	instanceProperties.Scheduling = &compute.Scheduling{
   359  		AutomaticRestart: d.Get("automatic_restart").(bool),
   360  	}
   361  	instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
   362  	if v, ok := d.GetOk("on_host_maintenance"); ok {
   363  		instanceProperties.Scheduling.OnHostMaintenance = v.(string)
   364  	}
   365  
   366  	serviceAccountsCount := d.Get("service_account.#").(int)
   367  	serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount)
   368  	for i := 0; i < serviceAccountsCount; i++ {
   369  		prefix := fmt.Sprintf("service_account.%d", i)
   370  
   371  		scopesCount := d.Get(prefix + ".scopes.#").(int)
   372  		scopes := make([]string, 0, scopesCount)
   373  		for j := 0; j < scopesCount; j++ {
   374  			scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string)
   375  			scopes = append(scopes, canonicalizeServiceScope(scope))
   376  		}
   377  
   378  		serviceAccount := &compute.ServiceAccount{
   379  			Email:  "default",
   380  			Scopes: scopes,
   381  		}
   382  
   383  		serviceAccounts = append(serviceAccounts, serviceAccount)
   384  	}
   385  	instanceProperties.ServiceAccounts = serviceAccounts
   386  
   387  	instanceProperties.Tags = resourceInstanceTags(d)
   388  
   389  	instanceTemplate := compute.InstanceTemplate{
   390  		Description: d.Get("description").(string),
   391  		Properties:  instanceProperties,
   392  		Name:        d.Get("name").(string),
   393  	}
   394  
   395  	op, err := config.clientCompute.InstanceTemplates.Insert(
   396  		config.Project, &instanceTemplate).Do()
   397  	if err != nil {
   398  		return fmt.Errorf("Error creating instance: %s", err)
   399  	}
   400  
   401  	// Store the ID now
   402  	d.SetId(instanceTemplate.Name)
   403  
   404  	// Wait for the operation to complete
   405  	w := &OperationWaiter{
   406  		Service: config.clientCompute,
   407  		Op:      op,
   408  		Project: config.Project,
   409  		Type:    OperationWaitGlobal,
   410  	}
   411  	state := w.Conf()
   412  	state.Delay = 10 * time.Second
   413  	state.Timeout = 10 * time.Minute
   414  	state.MinTimeout = 2 * time.Second
   415  	opRaw, err := state.WaitForState()
   416  	if err != nil {
   417  		return fmt.Errorf("Error waiting for instance template to create: %s", err)
   418  	}
   419  	op = opRaw.(*compute.Operation)
   420  	if op.Error != nil {
   421  		// The resource didn't actually create
   422  		d.SetId("")
   423  
   424  		// Return the error
   425  		return OperationError(*op.Error)
   426  	}
   427  
   428  	return resourceComputeInstanceTemplateRead(d, meta)
   429  }
   430  
   431  func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
   432  	config := meta.(*Config)
   433  
   434  	instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
   435  		config.Project, d.Id()).Do()
   436  	if err != nil {
   437  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   438  			// The resource doesn't exist anymore
   439  			d.SetId("")
   440  
   441  			return nil
   442  		}
   443  
   444  		return fmt.Errorf("Error reading instance template: %s", err)
   445  	}
   446  
   447  	// Set the metadata fingerprint if there is one.
   448  	if instanceTemplate.Properties.Metadata != nil {
   449  		d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint)
   450  	}
   451  
   452  	// Set the tags fingerprint if there is one.
   453  	if instanceTemplate.Properties.Tags != nil {
   454  		d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint)
   455  	}
   456  	d.Set("self_link", instanceTemplate.SelfLink)
   457  
   458  	return nil
   459  }
   460  
   461  func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
   462  	config := meta.(*Config)
   463  
   464  	op, err := config.clientCompute.InstanceTemplates.Delete(
   465  		config.Project, d.Id()).Do()
   466  	if err != nil {
   467  		return fmt.Errorf("Error deleting instance template: %s", err)
   468  	}
   469  
   470  	// Wait for the operation to complete
   471  	w := &OperationWaiter{
   472  		Service: config.clientCompute,
   473  		Op:      op,
   474  		Project: config.Project,
   475  		Type:    OperationWaitGlobal,
   476  	}
   477  	state := w.Conf()
   478  	state.Delay = 5 * time.Second
   479  	state.Timeout = 5 * time.Minute
   480  	state.MinTimeout = 2 * time.Second
   481  	opRaw, err := state.WaitForState()
   482  	if err != nil {
   483  		return fmt.Errorf("Error waiting for instance template to delete: %s", err)
   484  	}
   485  	op = opRaw.(*compute.Operation)
   486  	if op.Error != nil {
   487  		// Return the error
   488  		return OperationError(*op.Error)
   489  	}
   490  
   491  	d.SetId("")
   492  	return nil
   493  }