github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/builtin/providers/google/resource_compute_instance_template.go (about)

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