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