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