github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/google/resource_compute_instance_template.go (about)

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