github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/google/resource_compute_instance_template.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/helper/schema"
     8  	"google.golang.org/api/compute/v1"
     9  	"google.golang.org/api/googleapi"
    10  )
    11  
    12  func resourceComputeInstanceTemplate() *schema.Resource {
    13  	return &schema.Resource{
    14  		Create: resourceComputeInstanceTemplateCreate,
    15  		Read:   resourceComputeInstanceTemplateRead,
    16  		Delete: resourceComputeInstanceTemplateDelete,
    17  
    18  		Schema: map[string]*schema.Schema{
    19  			"disk": &schema.Schema{
    20  				Type:     schema.TypeList,
    21  				Required: true,
    22  				ForceNew: true,
    23  				Elem: &schema.Resource{
    24  					Schema: map[string]*schema.Schema{
    25  						"auto_delete": &schema.Schema{
    26  							Type:     schema.TypeBool,
    27  							Optional: true,
    28  							Default:  true,
    29  							ForceNew: true,
    30  						},
    31  
    32  						"boot": &schema.Schema{
    33  							Type:     schema.TypeBool,
    34  							Optional: true,
    35  							ForceNew: true,
    36  						},
    37  
    38  						"device_name": &schema.Schema{
    39  							Type:     schema.TypeString,
    40  							Optional: true,
    41  							ForceNew: true,
    42  						},
    43  
    44  						"disk_name": &schema.Schema{
    45  							Type:     schema.TypeString,
    46  							Optional: true,
    47  							ForceNew: true,
    48  						},
    49  
    50  						"disk_size_gb": &schema.Schema{
    51  							Type:     schema.TypeInt,
    52  							Optional: true,
    53  							ForceNew: true,
    54  						},
    55  
    56  						"disk_type": &schema.Schema{
    57  							Type:     schema.TypeString,
    58  							Optional: true,
    59  							ForceNew: true,
    60  						},
    61  
    62  						"source_image": &schema.Schema{
    63  							Type:     schema.TypeString,
    64  							Optional: true,
    65  							ForceNew: true,
    66  						},
    67  
    68  						"interface": &schema.Schema{
    69  							Type:     schema.TypeString,
    70  							Optional: true,
    71  							ForceNew: true,
    72  						},
    73  
    74  						"mode": &schema.Schema{
    75  							Type:     schema.TypeString,
    76  							Optional: true,
    77  							ForceNew: true,
    78  						},
    79  
    80  						"source": &schema.Schema{
    81  							Type:     schema.TypeString,
    82  							Optional: true,
    83  							ForceNew: true,
    84  						},
    85  
    86  						"type": &schema.Schema{
    87  							Type:     schema.TypeString,
    88  							Optional: true,
    89  							ForceNew: true,
    90  						},
    91  					},
    92  				},
    93  			},
    94  
    95  			"machine_type": &schema.Schema{
    96  				Type:     schema.TypeString,
    97  				Required: true,
    98  				ForceNew: true,
    99  			},
   100  
   101  			"name": &schema.Schema{
   102  				Type:     schema.TypeString,
   103  				Required: true,
   104  				ForceNew: true,
   105  			},
   106  
   107  			"automatic_restart": &schema.Schema{
   108  				Type:       schema.TypeBool,
   109  				Optional:   true,
   110  				Default:    true,
   111  				ForceNew:   true,
   112  				Deprecated: "Please use `scheduling.automatic_restart` instead",
   113  			},
   114  
   115  			"can_ip_forward": &schema.Schema{
   116  				Type:     schema.TypeBool,
   117  				Optional: true,
   118  				Default:  false,
   119  				ForceNew: true,
   120  			},
   121  
   122  			"description": &schema.Schema{
   123  				Type:     schema.TypeString,
   124  				Optional: true,
   125  				ForceNew: true,
   126  			},
   127  
   128  			"instance_description": &schema.Schema{
   129  				Type:     schema.TypeString,
   130  				Optional: true,
   131  				ForceNew: true,
   132  			},
   133  
   134  			"metadata": &schema.Schema{
   135  				Type:     schema.TypeMap,
   136  				Optional: true,
   137  				ForceNew: true,
   138  			},
   139  
   140  			"metadata_fingerprint": &schema.Schema{
   141  				Type:     schema.TypeString,
   142  				Computed: true,
   143  			},
   144  
   145  			"network_interface": &schema.Schema{
   146  				Type:     schema.TypeList,
   147  				Optional: true,
   148  				ForceNew: true,
   149  				Elem: &schema.Resource{
   150  					Schema: map[string]*schema.Schema{
   151  						"network": &schema.Schema{
   152  							Type:     schema.TypeString,
   153  							Optional: true,
   154  							ForceNew: true,
   155  						},
   156  
   157  						"subnetwork": &schema.Schema{
   158  							Type:     schema.TypeString,
   159  							Optional: true,
   160  							ForceNew: true,
   161  						},
   162  
   163  						"access_config": &schema.Schema{
   164  							Type:     schema.TypeList,
   165  							Optional: true,
   166  							Elem: &schema.Resource{
   167  								Schema: map[string]*schema.Schema{
   168  									"nat_ip": &schema.Schema{
   169  										Type:     schema.TypeString,
   170  										Computed: true,
   171  										Optional: true,
   172  									},
   173  								},
   174  							},
   175  						},
   176  					},
   177  				},
   178  			},
   179  
   180  			"on_host_maintenance": &schema.Schema{
   181  				Type:       schema.TypeString,
   182  				Optional:   true,
   183  				ForceNew:   true,
   184  				Deprecated: "Please use `scheduling.on_host_maintenance` instead",
   185  			},
   186  
   187  			"project": &schema.Schema{
   188  				Type:     schema.TypeString,
   189  				Optional: true,
   190  				ForceNew: true,
   191  			},
   192  
   193  			"region": &schema.Schema{
   194  				Type:     schema.TypeString,
   195  				Optional: true,
   196  				ForceNew: true,
   197  			},
   198  
   199  			"scheduling": &schema.Schema{
   200  				Type:     schema.TypeList,
   201  				Optional: true,
   202  				ForceNew: true,
   203  				Elem: &schema.Resource{
   204  					Schema: map[string]*schema.Schema{
   205  						"preemptible": &schema.Schema{
   206  							Type:     schema.TypeBool,
   207  							Optional: true,
   208  							ForceNew: true,
   209  						},
   210  
   211  						"automatic_restart": &schema.Schema{
   212  							Type:     schema.TypeBool,
   213  							Optional: true,
   214  							Default:  true,
   215  							ForceNew: true,
   216  						},
   217  
   218  						"on_host_maintenance": &schema.Schema{
   219  							Type:     schema.TypeString,
   220  							Optional: true,
   221  							ForceNew: true,
   222  						},
   223  					},
   224  				},
   225  			},
   226  
   227  			"self_link": &schema.Schema{
   228  				Type:     schema.TypeString,
   229  				Computed: true,
   230  			},
   231  
   232  			"service_account": &schema.Schema{
   233  				Type:     schema.TypeList,
   234  				Optional: true,
   235  				ForceNew: true,
   236  				Elem: &schema.Resource{
   237  					Schema: map[string]*schema.Schema{
   238  						"email": &schema.Schema{
   239  							Type:     schema.TypeString,
   240  							Computed: true,
   241  							ForceNew: true,
   242  						},
   243  
   244  						"scopes": &schema.Schema{
   245  							Type:     schema.TypeList,
   246  							Required: true,
   247  							ForceNew: true,
   248  							Elem: &schema.Schema{
   249  								Type: schema.TypeString,
   250  								StateFunc: func(v interface{}) string {
   251  									return canonicalizeServiceScope(v.(string))
   252  								},
   253  							},
   254  						},
   255  					},
   256  				},
   257  			},
   258  
   259  			"tags": &schema.Schema{
   260  				Type:     schema.TypeSet,
   261  				Optional: true,
   262  				ForceNew: true,
   263  				Elem:     &schema.Schema{Type: schema.TypeString},
   264  				Set:      schema.HashString,
   265  			},
   266  
   267  			"tags_fingerprint": &schema.Schema{
   268  				Type:     schema.TypeString,
   269  				Computed: true,
   270  			},
   271  		},
   272  	}
   273  }
   274  
   275  func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) {
   276  	config := meta.(*Config)
   277  
   278  	disksCount := d.Get("disk.#").(int)
   279  
   280  	disks := make([]*compute.AttachedDisk, 0, disksCount)
   281  	for i := 0; i < disksCount; i++ {
   282  		prefix := fmt.Sprintf("disk.%d", i)
   283  
   284  		// Build the disk
   285  		var disk compute.AttachedDisk
   286  		disk.Type = "PERSISTENT"
   287  		disk.Mode = "READ_WRITE"
   288  		disk.Interface = "SCSI"
   289  		disk.Boot = i == 0
   290  		disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool)
   291  
   292  		if v, ok := d.GetOk(prefix + ".boot"); ok {
   293  			disk.Boot = v.(bool)
   294  		}
   295  
   296  		if v, ok := d.GetOk(prefix + ".device_name"); ok {
   297  			disk.DeviceName = v.(string)
   298  		}
   299  
   300  		if v, ok := d.GetOk(prefix + ".source"); ok {
   301  			disk.Source = v.(string)
   302  		} else {
   303  			disk.InitializeParams = &compute.AttachedDiskInitializeParams{}
   304  
   305  			if v, ok := d.GetOk(prefix + ".disk_name"); ok {
   306  				disk.InitializeParams.DiskName = v.(string)
   307  			}
   308  			if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok {
   309  				disk.InitializeParams.DiskSizeGb = int64(v.(int))
   310  			}
   311  			disk.InitializeParams.DiskType = "pd-standard"
   312  			if v, ok := d.GetOk(prefix + ".disk_type"); ok {
   313  				disk.InitializeParams.DiskType = v.(string)
   314  			}
   315  
   316  			if v, ok := d.GetOk(prefix + ".source_image"); ok {
   317  				imageName := v.(string)
   318  				imageUrl, err := resolveImage(config, imageName)
   319  				if err != nil {
   320  					return nil, fmt.Errorf(
   321  						"Error resolving image name '%s': %s",
   322  						imageName, err)
   323  				}
   324  				disk.InitializeParams.SourceImage = imageUrl
   325  			}
   326  		}
   327  
   328  		if v, ok := d.GetOk(prefix + ".interface"); ok {
   329  			disk.Interface = v.(string)
   330  		}
   331  
   332  		if v, ok := d.GetOk(prefix + ".mode"); ok {
   333  			disk.Mode = v.(string)
   334  		}
   335  
   336  		if v, ok := d.GetOk(prefix + ".type"); ok {
   337  			disk.Type = v.(string)
   338  		}
   339  
   340  		disks = append(disks, &disk)
   341  	}
   342  
   343  	return disks, nil
   344  }
   345  
   346  func buildNetworks(d *schema.ResourceData, meta interface{}) ([]*compute.NetworkInterface, error) {
   347  	// Build up the list of networks
   348  	config := meta.(*Config)
   349  
   350  	project, err := getProject(d, config)
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	networksCount := d.Get("network_interface.#").(int)
   356  	networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount)
   357  	for i := 0; i < networksCount; i++ {
   358  		prefix := fmt.Sprintf("network_interface.%d", i)
   359  
   360  		var networkName, subnetworkName string
   361  		if v, ok := d.GetOk(prefix + ".network"); ok {
   362  			networkName = v.(string)
   363  		}
   364  		if v, ok := d.GetOk(prefix + ".subnetwork"); ok {
   365  			subnetworkName = v.(string)
   366  		}
   367  
   368  		if networkName == "" && subnetworkName == "" {
   369  			return nil, fmt.Errorf("network or subnetwork must be provided")
   370  		}
   371  		if networkName != "" && subnetworkName != "" {
   372  			return nil, fmt.Errorf("network or subnetwork must not both be provided")
   373  		}
   374  
   375  		var networkLink, subnetworkLink string
   376  		if networkName != "" {
   377  			network, err := config.clientCompute.Networks.Get(
   378  				project, networkName).Do()
   379  			if err != nil {
   380  				return nil, fmt.Errorf("Error referencing network '%s': %s",
   381  					networkName, err)
   382  			}
   383  			networkLink = network.SelfLink
   384  		} else {
   385  			// lookup subnetwork link using region and subnetwork name
   386  			region, err := getRegion(d, config)
   387  			if err != nil {
   388  				return nil, err
   389  			}
   390  			subnetwork, err := config.clientCompute.Subnetworks.Get(
   391  				project, region, subnetworkName).Do()
   392  			if err != nil {
   393  				return nil, fmt.Errorf(
   394  					"Error referencing subnetwork '%s' in region '%s': %s",
   395  					subnetworkName, region, err)
   396  			}
   397  			subnetworkLink = subnetwork.SelfLink
   398  		}
   399  
   400  		// Build the networkInterface
   401  		var iface compute.NetworkInterface
   402  		iface.Network = networkLink
   403  		iface.Subnetwork = subnetworkLink
   404  
   405  		accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
   406  		iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
   407  		for j := 0; j < accessConfigsCount; j++ {
   408  			acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
   409  			iface.AccessConfigs[j] = &compute.AccessConfig{
   410  				Type:  "ONE_TO_ONE_NAT",
   411  				NatIP: d.Get(acPrefix + ".nat_ip").(string),
   412  			}
   413  		}
   414  
   415  		networkInterfaces = append(networkInterfaces, &iface)
   416  	}
   417  	return networkInterfaces, nil
   418  }
   419  
   420  func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
   421  	config := meta.(*Config)
   422  
   423  	project, err := getProject(d, config)
   424  	if err != nil {
   425  		return err
   426  	}
   427  
   428  	instanceProperties := &compute.InstanceProperties{}
   429  
   430  	instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool)
   431  	instanceProperties.Description = d.Get("instance_description").(string)
   432  	instanceProperties.MachineType = d.Get("machine_type").(string)
   433  	disks, err := buildDisks(d, meta)
   434  	if err != nil {
   435  		return err
   436  	}
   437  	instanceProperties.Disks = disks
   438  	metadata, err := resourceInstanceMetadata(d)
   439  	if err != nil {
   440  		return err
   441  	}
   442  	instanceProperties.Metadata = metadata
   443  	networks, err := buildNetworks(d, meta)
   444  	if err != nil {
   445  		return err
   446  	}
   447  	instanceProperties.NetworkInterfaces = networks
   448  
   449  	instanceProperties.Scheduling = &compute.Scheduling{}
   450  	instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
   451  
   452  	if v, ok := d.GetOk("automatic_restart"); ok {
   453  		instanceProperties.Scheduling.AutomaticRestart = v.(bool)
   454  	}
   455  
   456  	if v, ok := d.GetOk("on_host_maintenance"); ok {
   457  		instanceProperties.Scheduling.OnHostMaintenance = v.(string)
   458  	}
   459  
   460  	forceSendFieldsScheduling := make([]string, 0, 3)
   461  	var hasSendMaintenance bool
   462  	hasSendMaintenance = false
   463  	if v, ok := d.GetOk("scheduling"); ok {
   464  		_schedulings := v.([]interface{})
   465  		if len(_schedulings) > 1 {
   466  			return fmt.Errorf("Error, at most one `scheduling` block can be defined")
   467  		}
   468  		_scheduling := _schedulings[0].(map[string]interface{})
   469  
   470  		if vp, okp := _scheduling["automatic_restart"]; okp {
   471  			instanceProperties.Scheduling.AutomaticRestart = vp.(bool)
   472  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart")
   473  		}
   474  
   475  		if vp, okp := _scheduling["on_host_maintenance"]; okp {
   476  			instanceProperties.Scheduling.OnHostMaintenance = vp.(string)
   477  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   478  			hasSendMaintenance = true
   479  		}
   480  
   481  		if vp, okp := _scheduling["preemptible"]; okp {
   482  			instanceProperties.Scheduling.Preemptible = vp.(bool)
   483  			forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible")
   484  			if vp.(bool) && !hasSendMaintenance {
   485  				instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE"
   486  				forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
   487  			}
   488  		}
   489  	}
   490  	instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling
   491  
   492  	serviceAccountsCount := d.Get("service_account.#").(int)
   493  	serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount)
   494  	for i := 0; i < serviceAccountsCount; i++ {
   495  		prefix := fmt.Sprintf("service_account.%d", i)
   496  
   497  		scopesCount := d.Get(prefix + ".scopes.#").(int)
   498  		scopes := make([]string, 0, scopesCount)
   499  		for j := 0; j < scopesCount; j++ {
   500  			scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string)
   501  			scopes = append(scopes, canonicalizeServiceScope(scope))
   502  		}
   503  
   504  		serviceAccount := &compute.ServiceAccount{
   505  			Email:  "default",
   506  			Scopes: scopes,
   507  		}
   508  
   509  		serviceAccounts = append(serviceAccounts, serviceAccount)
   510  	}
   511  	instanceProperties.ServiceAccounts = serviceAccounts
   512  
   513  	instanceProperties.Tags = resourceInstanceTags(d)
   514  
   515  	instanceTemplate := compute.InstanceTemplate{
   516  		Description: d.Get("description").(string),
   517  		Properties:  instanceProperties,
   518  		Name:        d.Get("name").(string),
   519  	}
   520  
   521  	op, err := config.clientCompute.InstanceTemplates.Insert(
   522  		project, &instanceTemplate).Do()
   523  	if err != nil {
   524  		return fmt.Errorf("Error creating instance: %s", err)
   525  	}
   526  
   527  	// Store the ID now
   528  	d.SetId(instanceTemplate.Name)
   529  
   530  	err = computeOperationWaitGlobal(config, op, "Creating Instance Template")
   531  	if err != nil {
   532  		return err
   533  	}
   534  
   535  	return resourceComputeInstanceTemplateRead(d, meta)
   536  }
   537  
   538  func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
   539  	config := meta.(*Config)
   540  
   541  	project, err := getProject(d, config)
   542  	if err != nil {
   543  		return err
   544  	}
   545  
   546  	instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
   547  		project, d.Id()).Do()
   548  	if err != nil {
   549  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   550  			log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string))
   551  			// The resource doesn't exist anymore
   552  			d.SetId("")
   553  
   554  			return nil
   555  		}
   556  
   557  		return fmt.Errorf("Error reading instance template: %s", err)
   558  	}
   559  
   560  	// Set the metadata fingerprint if there is one.
   561  	if instanceTemplate.Properties.Metadata != nil {
   562  		d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint)
   563  	}
   564  
   565  	// Set the tags fingerprint if there is one.
   566  	if instanceTemplate.Properties.Tags != nil {
   567  		d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint)
   568  	}
   569  	d.Set("self_link", instanceTemplate.SelfLink)
   570  
   571  	return nil
   572  }
   573  
   574  func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
   575  	config := meta.(*Config)
   576  
   577  	project, err := getProject(d, config)
   578  	if err != nil {
   579  		return err
   580  	}
   581  
   582  	op, err := config.clientCompute.InstanceTemplates.Delete(
   583  		project, d.Id()).Do()
   584  	if err != nil {
   585  		return fmt.Errorf("Error deleting instance template: %s", err)
   586  	}
   587  
   588  	err = computeOperationWaitGlobal(config, op, "Deleting Instance Template")
   589  	if err != nil {
   590  		return err
   591  	}
   592  
   593  	d.SetId("")
   594  	return nil
   595  }