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