github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/google/resource_compute_disk.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"regexp"
     7  
     8  	"github.com/hashicorp/terraform/helper/schema"
     9  	"google.golang.org/api/compute/v1"
    10  	"google.golang.org/api/googleapi"
    11  )
    12  
    13  const (
    14  	computeDiskUserRegexString = "^(?:https://www.googleapis.com/compute/v1/projects/)?([-_a-zA-Z0-9]*)/zones/([-_a-zA-Z0-9]*)/instances/([-_a-zA-Z0-9]*)$"
    15  )
    16  
    17  var (
    18  	computeDiskUserRegex = regexp.MustCompile(computeDiskUserRegexString)
    19  )
    20  
    21  func resourceComputeDisk() *schema.Resource {
    22  	return &schema.Resource{
    23  		Create: resourceComputeDiskCreate,
    24  		Read:   resourceComputeDiskRead,
    25  		Delete: resourceComputeDiskDelete,
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"name": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Required: true,
    31  				ForceNew: true,
    32  			},
    33  
    34  			"zone": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  
    40  			"disk_encryption_key_raw": &schema.Schema{
    41  				Type:      schema.TypeString,
    42  				Optional:  true,
    43  				ForceNew:  true,
    44  				Sensitive: true,
    45  			},
    46  
    47  			"disk_encryption_key_sha256": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Computed: true,
    50  			},
    51  
    52  			"image": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Optional: true,
    55  				ForceNew: true,
    56  			},
    57  
    58  			"project": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				ForceNew: true,
    62  			},
    63  
    64  			"size": &schema.Schema{
    65  				Type:     schema.TypeInt,
    66  				Optional: true,
    67  				ForceNew: true,
    68  			},
    69  
    70  			"self_link": &schema.Schema{
    71  				Type:     schema.TypeString,
    72  				Computed: true,
    73  			},
    74  
    75  			"snapshot": &schema.Schema{
    76  				Type:     schema.TypeString,
    77  				Optional: true,
    78  				ForceNew: true,
    79  			},
    80  
    81  			"type": &schema.Schema{
    82  				Type:     schema.TypeString,
    83  				Optional: true,
    84  				ForceNew: true,
    85  			},
    86  			"users": &schema.Schema{
    87  				Type:     schema.TypeList,
    88  				Computed: true,
    89  				Elem:     &schema.Schema{Type: schema.TypeString},
    90  			},
    91  		},
    92  	}
    93  }
    94  
    95  func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error {
    96  	config := meta.(*Config)
    97  
    98  	project, err := getProject(d, config)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	// Get the zone
   104  	log.Printf("[DEBUG] Loading zone: %s", d.Get("zone").(string))
   105  	zone, err := config.clientCompute.Zones.Get(
   106  		project, d.Get("zone").(string)).Do()
   107  	if err != nil {
   108  		return fmt.Errorf(
   109  			"Error loading zone '%s': %s", d.Get("zone").(string), err)
   110  	}
   111  
   112  	// Build the disk parameter
   113  	disk := &compute.Disk{
   114  		Name:   d.Get("name").(string),
   115  		SizeGb: int64(d.Get("size").(int)),
   116  	}
   117  
   118  	// If we were given a source image, load that.
   119  	if v, ok := d.GetOk("image"); ok {
   120  		log.Printf("[DEBUG] Resolving image name: %s", v.(string))
   121  		imageUrl, err := resolveImage(config, v.(string))
   122  		if err != nil {
   123  			return fmt.Errorf(
   124  				"Error resolving image name '%s': %s",
   125  				v.(string), err)
   126  		}
   127  
   128  		disk.SourceImage = imageUrl
   129  		log.Printf("[DEBUG] Image name resolved to: %s", imageUrl)
   130  	}
   131  
   132  	if v, ok := d.GetOk("type"); ok {
   133  		log.Printf("[DEBUG] Loading disk type: %s", v.(string))
   134  		diskType, err := readDiskType(config, zone, v.(string))
   135  		if err != nil {
   136  			return fmt.Errorf(
   137  				"Error loading disk type '%s': %s",
   138  				v.(string), err)
   139  		}
   140  
   141  		disk.Type = diskType.SelfLink
   142  	}
   143  
   144  	if v, ok := d.GetOk("snapshot"); ok {
   145  		snapshotName := v.(string)
   146  		match, _ := regexp.MatchString("^https://www.googleapis.com/compute", snapshotName)
   147  		if match {
   148  			disk.SourceSnapshot = snapshotName
   149  		} else {
   150  			log.Printf("[DEBUG] Loading snapshot: %s", snapshotName)
   151  			snapshotData, err := config.clientCompute.Snapshots.Get(
   152  				project, snapshotName).Do()
   153  
   154  			if err != nil {
   155  				return fmt.Errorf(
   156  					"Error loading snapshot '%s': %s",
   157  					snapshotName, err)
   158  			}
   159  			disk.SourceSnapshot = snapshotData.SelfLink
   160  		}
   161  	}
   162  
   163  	if v, ok := d.GetOk("disk_encryption_key_raw"); ok {
   164  		disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{}
   165  		disk.DiskEncryptionKey.RawKey = v.(string)
   166  	}
   167  
   168  	op, err := config.clientCompute.Disks.Insert(
   169  		project, d.Get("zone").(string), disk).Do()
   170  	if err != nil {
   171  		return fmt.Errorf("Error creating disk: %s", err)
   172  	}
   173  
   174  	// It probably maybe worked, so store the ID now
   175  	d.SetId(disk.Name)
   176  
   177  	err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating Disk")
   178  	if err != nil {
   179  		return err
   180  	}
   181  	return resourceComputeDiskRead(d, meta)
   182  }
   183  
   184  func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
   185  	config := meta.(*Config)
   186  
   187  	project, err := getProject(d, config)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	disk, err := config.clientCompute.Disks.Get(
   193  		project, d.Get("zone").(string), d.Id()).Do()
   194  	if err != nil {
   195  		return handleNotFoundError(err, d, fmt.Sprintf("Disk %q", d.Get("name").(string)))
   196  	}
   197  
   198  	d.Set("self_link", disk.SelfLink)
   199  	if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
   200  		d.Set("disk_encryption_key_sha256", disk.DiskEncryptionKey.Sha256)
   201  	}
   202  	d.Set("users", disk.Users)
   203  
   204  	return nil
   205  }
   206  
   207  func resourceComputeDiskDelete(d *schema.ResourceData, meta interface{}) error {
   208  	config := meta.(*Config)
   209  
   210  	project, err := getProject(d, config)
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	// if disks are attached, they must be detached before the disk can be deleted
   216  	if instances, ok := d.Get("users").([]interface{}); ok {
   217  		type detachArgs struct{ project, zone, instance, deviceName string }
   218  		var detachCalls []detachArgs
   219  		self := d.Get("self_link").(string)
   220  		for _, instance := range instances {
   221  			if !computeDiskUserRegex.MatchString(instance.(string)) {
   222  				return fmt.Errorf("Unknown user %q of disk %q", instance, self)
   223  			}
   224  			matches := computeDiskUserRegex.FindStringSubmatch(instance.(string))
   225  			instanceProject := matches[1]
   226  			instanceZone := matches[2]
   227  			instanceName := matches[3]
   228  			i, err := config.clientCompute.Instances.Get(instanceProject, instanceZone, instanceName).Do()
   229  			if err != nil {
   230  				if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   231  					log.Printf("[WARN] instance %q not found, not bothering to detach disks", instance.(string))
   232  					continue
   233  				}
   234  				return fmt.Errorf("Error retrieving instance %s: %s", instance.(string), err.Error())
   235  			}
   236  			for _, disk := range i.Disks {
   237  				if disk.Source == self {
   238  					detachCalls = append(detachCalls, detachArgs{
   239  						project:    project,
   240  						zone:       i.Zone,
   241  						instance:   i.Name,
   242  						deviceName: disk.DeviceName,
   243  					})
   244  				}
   245  			}
   246  		}
   247  		for _, call := range detachCalls {
   248  			op, err := config.clientCompute.Instances.DetachDisk(call.project, call.zone, call.instance, call.deviceName).Do()
   249  			if err != nil {
   250  				return fmt.Errorf("Error detaching disk %s from instance %s/%s/%s: %s", call.deviceName, call.project,
   251  					call.zone, call.instance, err.Error())
   252  			}
   253  			err = computeOperationWaitZone(config, op, call.project, call.zone,
   254  				fmt.Sprintf("Detaching disk from %s/%s/%s", call.project, call.zone, call.instance))
   255  			if err != nil {
   256  				return err
   257  			}
   258  		}
   259  	}
   260  
   261  	// Delete the disk
   262  	op, err := config.clientCompute.Disks.Delete(
   263  		project, d.Get("zone").(string), d.Id()).Do()
   264  	if err != nil {
   265  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   266  			log.Printf("[WARN] Removing Disk %q because it's gone", d.Get("name").(string))
   267  			// The resource doesn't exist anymore
   268  			d.SetId("")
   269  			return nil
   270  		}
   271  		return fmt.Errorf("Error deleting disk: %s", err)
   272  	}
   273  
   274  	zone := d.Get("zone").(string)
   275  	err = computeOperationWaitZone(config, op, project, zone, "Deleting Disk")
   276  	if err != nil {
   277  		return err
   278  	}
   279  
   280  	d.SetId("")
   281  	return nil
   282  }