github.com/bendemaree/terraform@v0.5.4-0.20150613200311-f50d97d6eee6/builtin/providers/azure/resource_azure_data_disk.go (about)

     1  package azure
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/Azure/azure-sdk-for-go/management"
     9  	"github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  )
    12  
    13  const dataDiskBlobStorageURL = "http://%s.blob.core.windows.net/disks/%s.vhd"
    14  
    15  func resourceAzureDataDisk() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceAzureDataDiskCreate,
    18  		Read:   resourceAzureDataDiskRead,
    19  		Update: resourceAzureDataDiskUpdate,
    20  		Delete: resourceAzureDataDiskDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"name": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Optional: true,
    26  				Computed: true,
    27  				ForceNew: true,
    28  			},
    29  
    30  			"label": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Optional: true,
    33  				Computed: true,
    34  				ForceNew: true,
    35  			},
    36  
    37  			"lun": &schema.Schema{
    38  				Type:     schema.TypeInt,
    39  				Required: true,
    40  			},
    41  
    42  			"size": &schema.Schema{
    43  				Type:     schema.TypeInt,
    44  				Optional: true,
    45  			},
    46  
    47  			"caching": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				Default:  "None",
    51  			},
    52  
    53  			"storage_service_name": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Optional: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"media_link": &schema.Schema{
    60  				Type:     schema.TypeString,
    61  				Optional: true,
    62  				Computed: true,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"source_media_link": &schema.Schema{
    67  				Type:     schema.TypeString,
    68  				Optional: true,
    69  				ForceNew: true,
    70  			},
    71  
    72  			"virtual_machine": &schema.Schema{
    73  				Type:     schema.TypeString,
    74  				Required: true,
    75  			},
    76  		},
    77  	}
    78  }
    79  
    80  func resourceAzureDataDiskCreate(d *schema.ResourceData, meta interface{}) error {
    81  	mc := meta.(*Client).mgmtClient
    82  
    83  	if err := verifyDataDiskParameters(d); err != nil {
    84  		return err
    85  	}
    86  
    87  	lun := d.Get("lun").(int)
    88  	vm := d.Get("virtual_machine").(string)
    89  
    90  	label := d.Get("label").(string)
    91  	if label == "" {
    92  		label = fmt.Sprintf("%s-%d", vm, lun)
    93  	}
    94  
    95  	p := virtualmachinedisk.CreateDataDiskParameters{
    96  		DiskLabel:           label,
    97  		Lun:                 lun,
    98  		LogicalDiskSizeInGB: d.Get("size").(int),
    99  		HostCaching:         hostCaching(d),
   100  		MediaLink:           mediaLink(d),
   101  		SourceMediaLink:     d.Get("source_media_link").(string),
   102  	}
   103  
   104  	if name, ok := d.GetOk("name"); ok {
   105  		p.DiskName = name.(string)
   106  	}
   107  
   108  	log.Printf("[DEBUG] Adding data disk %d to instance: %s", lun, vm)
   109  	req, err := virtualmachinedisk.NewClient(mc).AddDataDisk(vm, vm, vm, p)
   110  	if err != nil {
   111  		return fmt.Errorf("Error adding data disk %d to instance %s: %s", lun, vm, err)
   112  	}
   113  
   114  	// Wait until the data disk is added
   115  	if err := mc.WaitForOperation(req, nil); err != nil {
   116  		return fmt.Errorf(
   117  			"Error waiting for data disk %d to be added to instance %s: %s", lun, vm, err)
   118  	}
   119  
   120  	log.Printf("[DEBUG] Retrieving data disk %d from instance %s", lun, vm)
   121  	disk, err := virtualmachinedisk.NewClient(mc).GetDataDisk(vm, vm, vm, lun)
   122  	if err != nil {
   123  		return fmt.Errorf("Error retrieving data disk %d from instance %s: %s", lun, vm, err)
   124  	}
   125  
   126  	d.SetId(disk.DiskName)
   127  
   128  	return resourceAzureDataDiskRead(d, meta)
   129  }
   130  
   131  func resourceAzureDataDiskRead(d *schema.ResourceData, meta interface{}) error {
   132  	mc := meta.(*Client).mgmtClient
   133  
   134  	lun := d.Get("lun").(int)
   135  	vm := d.Get("virtual_machine").(string)
   136  
   137  	log.Printf("[DEBUG] Retrieving data disk: %s", d.Id())
   138  	datadisk, err := virtualmachinedisk.NewClient(mc).GetDataDisk(vm, vm, vm, lun)
   139  	if err != nil {
   140  		if management.IsResourceNotFoundError(err) {
   141  			d.SetId("")
   142  			return nil
   143  		}
   144  		return fmt.Errorf("Error retrieving data disk %s: %s", d.Id(), err)
   145  	}
   146  
   147  	d.Set("name", datadisk.DiskName)
   148  	d.Set("label", datadisk.DiskLabel)
   149  	d.Set("lun", datadisk.Lun)
   150  	d.Set("size", datadisk.LogicalDiskSizeInGB)
   151  	d.Set("caching", datadisk.HostCaching)
   152  	d.Set("media_link", datadisk.MediaLink)
   153  
   154  	log.Printf("[DEBUG] Retrieving disk: %s", d.Id())
   155  	disk, err := virtualmachinedisk.NewClient(mc).GetDisk(d.Id())
   156  	if err != nil {
   157  		return fmt.Errorf("Error retrieving disk %s: %s", d.Id(), err)
   158  	}
   159  
   160  	d.Set("virtual_machine", disk.AttachedTo.RoleName)
   161  
   162  	return nil
   163  }
   164  
   165  func resourceAzureDataDiskUpdate(d *schema.ResourceData, meta interface{}) error {
   166  	mc := meta.(*Client).mgmtClient
   167  	diskClient := virtualmachinedisk.NewClient(mc)
   168  
   169  	lun := d.Get("lun").(int)
   170  	vm := d.Get("virtual_machine").(string)
   171  
   172  	if d.HasChange("lun") || d.HasChange("size") || d.HasChange("virtual_machine") {
   173  		olun, _ := d.GetChange("lun")
   174  		ovm, _ := d.GetChange("virtual_machine")
   175  
   176  		log.Printf("[DEBUG] Detaching data disk: %s", d.Id())
   177  		req, err := diskClient.
   178  			DeleteDataDisk(ovm.(string), ovm.(string), ovm.(string), olun.(int), false)
   179  		if err != nil {
   180  			return fmt.Errorf("Error detaching data disk %s: %s", d.Id(), err)
   181  		}
   182  
   183  		// Wait until the data disk is detached
   184  		if err := mc.WaitForOperation(req, nil); err != nil {
   185  			return fmt.Errorf(
   186  				"Error waiting for data disk %s to be detached: %s", d.Id(), err)
   187  		}
   188  
   189  		log.Printf("[DEBUG] Verifying data disk %s is properly detached...", d.Id())
   190  		for i := 0; i < 6; i++ {
   191  			disk, err := diskClient.GetDisk(d.Id())
   192  			if err != nil {
   193  				return fmt.Errorf("Error retrieving disk %s: %s", d.Id(), err)
   194  			}
   195  
   196  			// Check if the disk is really detached
   197  			if disk.AttachedTo.RoleName == "" {
   198  				break
   199  			}
   200  
   201  			// If not, wait 30 seconds and try it again...
   202  			time.Sleep(time.Duration(30 * time.Second))
   203  		}
   204  
   205  		if d.HasChange("size") {
   206  			p := virtualmachinedisk.UpdateDiskParameters{
   207  				Name:            d.Id(),
   208  				Label:           d.Get("label").(string),
   209  				ResizedSizeInGB: d.Get("size").(int),
   210  			}
   211  
   212  			log.Printf("[DEBUG] Updating disk: %s", d.Id())
   213  			req, err := diskClient.UpdateDisk(d.Id(), p)
   214  			if err != nil {
   215  				return fmt.Errorf("Error updating disk %s: %s", d.Id(), err)
   216  			}
   217  
   218  			// Wait until the disk is updated
   219  			if err := mc.WaitForOperation(req, nil); err != nil {
   220  				return fmt.Errorf(
   221  					"Error waiting for disk %s to be updated: %s", d.Id(), err)
   222  			}
   223  		}
   224  
   225  		p := virtualmachinedisk.CreateDataDiskParameters{
   226  			DiskName:    d.Id(),
   227  			Lun:         lun,
   228  			HostCaching: hostCaching(d),
   229  			MediaLink:   mediaLink(d),
   230  		}
   231  
   232  		log.Printf("[DEBUG] Attaching data disk: %s", d.Id())
   233  		req, err = diskClient.AddDataDisk(vm, vm, vm, p)
   234  		if err != nil {
   235  			return fmt.Errorf("Error attaching data disk %s to instance %s: %s", d.Id(), vm, err)
   236  		}
   237  
   238  		// Wait until the data disk is attached
   239  		if err := mc.WaitForOperation(req, nil); err != nil {
   240  			return fmt.Errorf(
   241  				"Error waiting for data disk %s to be attached to instance %s: %s", d.Id(), vm, err)
   242  		}
   243  
   244  		// Make sure we return here since all possible changes are
   245  		// already updated if we reach this point
   246  		return nil
   247  	}
   248  
   249  	if d.HasChange("caching") {
   250  		p := virtualmachinedisk.UpdateDataDiskParameters{
   251  			DiskName:    d.Id(),
   252  			Lun:         lun,
   253  			HostCaching: hostCaching(d),
   254  			MediaLink:   mediaLink(d),
   255  		}
   256  
   257  		log.Printf("[DEBUG] Updating data disk: %s", d.Id())
   258  		req, err := diskClient.UpdateDataDisk(vm, vm, vm, lun, p)
   259  		if err != nil {
   260  			return fmt.Errorf("Error updating data disk %s: %s", d.Id(), err)
   261  		}
   262  
   263  		// Wait until the data disk is updated
   264  		if err := mc.WaitForOperation(req, nil); err != nil {
   265  			return fmt.Errorf(
   266  				"Error waiting for data disk %s to be updated: %s", d.Id(), err)
   267  		}
   268  	}
   269  
   270  	return resourceAzureDataDiskRead(d, meta)
   271  }
   272  
   273  func resourceAzureDataDiskDelete(d *schema.ResourceData, meta interface{}) error {
   274  	mc := meta.(*Client).mgmtClient
   275  
   276  	lun := d.Get("lun").(int)
   277  	vm := d.Get("virtual_machine").(string)
   278  
   279  	// If a name was not supplied, it means we created a new emtpy disk and we now want to
   280  	// delete that disk again. Otherwise we only want to detach the disk and keep the blob.
   281  	_, removeBlob := d.GetOk("name")
   282  
   283  	log.Printf("[DEBUG] Detaching data disk %s with removeBlob = %t", d.Id(), removeBlob)
   284  	req, err := virtualmachinedisk.NewClient(mc).DeleteDataDisk(vm, vm, vm, lun, removeBlob)
   285  	if err != nil {
   286  		return fmt.Errorf(
   287  			"Error detaching data disk %s with removeBlob = %t: %s", d.Id(), removeBlob, err)
   288  	}
   289  
   290  	// Wait until the data disk is detached and optionally deleted
   291  	if err := mc.WaitForOperation(req, nil); err != nil {
   292  		return fmt.Errorf(
   293  			"Error waiting for data disk %s to be detached with removeBlob = %t: %s",
   294  			d.Id(), removeBlob, err)
   295  	}
   296  
   297  	d.SetId("")
   298  
   299  	return nil
   300  }
   301  
   302  func hostCaching(d *schema.ResourceData) virtualmachinedisk.HostCachingType {
   303  	switch d.Get("caching").(string) {
   304  	case "ReadOnly":
   305  		return virtualmachinedisk.HostCachingTypeReadOnly
   306  	case "ReadWrite":
   307  		return virtualmachinedisk.HostCachingTypeReadWrite
   308  	default:
   309  		return virtualmachinedisk.HostCachingTypeNone
   310  	}
   311  }
   312  
   313  func mediaLink(d *schema.ResourceData) string {
   314  	mediaLink, ok := d.GetOk("media_link")
   315  	if ok {
   316  		return mediaLink.(string)
   317  	}
   318  
   319  	name, ok := d.GetOk("name")
   320  	if !ok {
   321  		name = fmt.Sprintf("%s-%d", d.Get("virtual_machine").(string), d.Get("lun").(int))
   322  	}
   323  
   324  	return fmt.Sprintf(dataDiskBlobStorageURL, d.Get("storage_service_name").(string), name.(string))
   325  }
   326  
   327  func verifyDataDiskParameters(d *schema.ResourceData) error {
   328  	caching := d.Get("caching").(string)
   329  	if caching != "None" && caching != "ReadOnly" && caching != "ReadWrite" {
   330  		return fmt.Errorf(
   331  			"Invalid caching type %s! Valid options are 'None', 'ReadOnly' and 'ReadWrite'.", caching)
   332  	}
   333  
   334  	if _, ok := d.GetOk("media_link"); !ok {
   335  		if _, ok := d.GetOk("storage_service_name"); !ok {
   336  			return fmt.Errorf("If not supplying 'media_link', you must supply 'storage'.")
   337  		}
   338  	}
   339  
   340  	return nil
   341  }