github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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  			"cloud_service_name": &schema.Schema{
    78  				Type:     schema.TypeString,
    79  				Optional: true,
    80  				ForceNew: true,
    81  			},
    82  
    83  			"deployment_name": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Optional: true,
    86  				ForceNew: true,
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func resourceAzureDataDiskCreate(d *schema.ResourceData, meta interface{}) error {
    93  	mc := meta.(*Client).mgmtClient
    94  	vmDiskClient := meta.(*Client).vmDiskClient
    95  	vmClient := meta.(*Client).vmClient
    96  
    97  	if err := verifyDataDiskParameters(d); err != nil {
    98  		return err
    99  	}
   100  
   101  	lun := d.Get("lun").(int)
   102  	vm := d.Get("virtual_machine").(string)
   103  
   104  	label := d.Get("label").(string)
   105  	if label == "" {
   106  		label = fmt.Sprintf("%s-%d", vm, lun)
   107  	}
   108  
   109  	p := virtualmachinedisk.CreateDataDiskParameters{
   110  		DiskLabel:           label,
   111  		Lun:                 lun,
   112  		LogicalDiskSizeInGB: d.Get("size").(int),
   113  		HostCaching:         hostCaching(d),
   114  		MediaLink:           mediaLink(d),
   115  		SourceMediaLink:     d.Get("source_media_link").(string),
   116  	}
   117  
   118  	if name, ok := d.GetOk("name"); ok {
   119  		p.DiskName = name.(string)
   120  	}
   121  	
   122  	cloudServiceName := d.Get("cloud_service_name").(string)
   123  	if cloudServiceName == "" {
   124  		cloudServiceName = vm
   125  	}
   126  
   127  	deploymentName := d.Get("deployment_name").(string)
   128  	if deploymentName == "" {
   129  		deptName, err := vmClient.GetDeploymentName(cloudServiceName)
   130  		if err != nil {
   131  			return fmt.Errorf("Error creating data disk %d for instance %s while getting deployment name from cloud service %s: %s", lun, vm, cloudServiceName, err)
   132  		}
   133  		deploymentName = deptName
   134  	}
   135  	if deploymentName == "" {
   136  		return fmt.Errorf("Error creating data disk %d for instance %s while getting deployment name from cloud service %s: Deployment Name is blank", lun, vm, cloudServiceName)
   137  	}
   138  
   139  	log.Printf("[DEBUG] Adding data disk %d to instance: %s", lun, vm)
   140  	req, err := vmDiskClient.AddDataDisk(cloudServiceName, deploymentName, vm, p)
   141  	if err != nil {
   142  		return fmt.Errorf("Error adding data disk %d to instance %s: %s", lun, vm, err)
   143  	}
   144  
   145  	// Wait until the data disk is added
   146  	if err := mc.WaitForOperation(req, nil); err != nil {
   147  		return fmt.Errorf(
   148  			"Error waiting for data disk %d to be added to instance %s: %s", lun, vm, err)
   149  	}
   150  
   151  	log.Printf("[DEBUG] Retrieving data disk %d from instance %s", lun, vm)
   152  	disk, err := vmDiskClient.GetDataDisk(cloudServiceName, deploymentName, vm, lun)
   153  	if err != nil {
   154  		return fmt.Errorf("Error retrieving data disk %d from instance %s: %s", lun, vm, err)
   155  	}
   156  
   157  	d.SetId(disk.DiskName)
   158  
   159  	return resourceAzureDataDiskRead(d, meta)
   160  }
   161  
   162  func resourceAzureDataDiskRead(d *schema.ResourceData, meta interface{}) error {
   163  	vmDiskClient := meta.(*Client).vmDiskClient
   164  	vmClient := meta.(*Client).vmClient
   165  
   166  	lun := d.Get("lun").(int)
   167  	vm := d.Get("virtual_machine").(string)
   168  	cloudServiceName := d.Get("cloud_service_name").(string)
   169  	if cloudServiceName == "" {
   170  		cloudServiceName = vm
   171  	}
   172  
   173  	deploymentName := d.Get("deployment_name").(string)
   174  	if deploymentName == "" {
   175  		deptName, err := vmClient.GetDeploymentName(cloudServiceName)
   176  		if err != nil {
   177  			return fmt.Errorf("Error reading data disk %d for instance %s while getting deployment name from cloud service %s: %s", lun, vm, cloudServiceName, err)
   178  		}
   179  		deploymentName = deptName
   180  	}
   181  	if deploymentName == "" {
   182  		return fmt.Errorf("Error reading data disk %d for instance %s while getting deployment name from cloud service %s: Deployment Name is blank", lun, vm, cloudServiceName)
   183  	}
   184  
   185  	log.Printf("[DEBUG] Retrieving data disk: %s", d.Id())
   186  	datadisk, err := vmDiskClient.GetDataDisk(cloudServiceName, deploymentName, vm, lun)
   187  	if err != nil {
   188  		if management.IsResourceNotFoundError(err) {
   189  			d.SetId("")
   190  			return nil
   191  		}
   192  		return fmt.Errorf("Error retrieving data disk %s: %s", d.Id(), err)
   193  	}
   194  
   195  	d.Set("name", datadisk.DiskName)
   196  	d.Set("label", datadisk.DiskLabel)
   197  	d.Set("lun", datadisk.Lun)
   198  	d.Set("size", datadisk.LogicalDiskSizeInGB)
   199  	d.Set("caching", datadisk.HostCaching)
   200  	d.Set("media_link", datadisk.MediaLink)
   201  
   202  	log.Printf("[DEBUG] Retrieving disk: %s", d.Id())
   203  	disk, err := vmDiskClient.GetDisk(d.Id())
   204  	if err != nil {
   205  		return fmt.Errorf("Error retrieving disk %s: %s", d.Id(), err)
   206  	}
   207  
   208  	d.Set("virtual_machine", disk.AttachedTo.RoleName)
   209  	d.Set("deployment_name", disk.AttachedTo.DeploymentName)
   210  	d.Set("cloud_service_name", disk.AttachedTo.HostedServiceName)
   211  
   212  	return nil
   213  }
   214  
   215  func resourceAzureDataDiskUpdate(d *schema.ResourceData, meta interface{}) error {
   216  	mc := meta.(*Client).mgmtClient
   217  	vmDiskClient := meta.(*Client).vmDiskClient
   218  	vmClient := meta.(*Client).vmClient
   219  
   220  	lun := d.Get("lun").(int)
   221  	vm := d.Get("virtual_machine").(string)
   222  
   223  	cloudServiceName := d.Get("cloud_service_name").(string)
   224  	if cloudServiceName == "" {
   225  		cloudServiceName = vm
   226  	}
   227  
   228  	deploymentName := d.Get("deployment_name").(string)
   229  	if deploymentName == "" {
   230  		deptName, err := vmClient.GetDeploymentName(cloudServiceName)
   231  		if err != nil {
   232  			return fmt.Errorf("Error updating data disk %d for instance %s while getting deployment name from cloud service %s: %s", lun, vm, cloudServiceName, err)
   233  		}
   234  		deploymentName = deptName
   235  	}
   236  	if deploymentName == "" {
   237  		return fmt.Errorf("Error updating data disk %d for instance %s while getting deployment name from cloud service %s: Deployment Name is blank", lun, vm, cloudServiceName)
   238  	}
   239  
   240  	if d.HasChange("lun") || d.HasChange("size") || d.HasChange("virtual_machine") {
   241  		olun, _ := d.GetChange("lun")
   242  		ovm, _ := d.GetChange("virtual_machine")
   243  		ocloudServiceName, _ := d.GetChange("cloud_service_name")
   244  		if ocloudServiceName == "" {
   245  			ocloudServiceName = ovm
   246  		}
   247  		odeploymentName, _ := d.GetChange("deployment_name")
   248  		if odeploymentName == "" {
   249  			odeptName, err := vmClient.GetDeploymentName(ocloudServiceName.(string))
   250  			if err != nil {
   251  				return fmt.Errorf("Error updating data disk %d for instance %s while getting deployment name from cloud service %s: %s", lun, ovm, ocloudServiceName, err)
   252  			}
   253  			odeploymentName = odeptName
   254  		}
   255  		if odeploymentName == "" {
   256  			return fmt.Errorf("Error updating data disk %d for instance %s while getting deployment name from cloud service %s: Deployment Name is blank", lun, ovm, ocloudServiceName)
   257  		}
   258  
   259  		log.Printf("[DEBUG] Detaching data disk: %s", d.Id())
   260  		req, err := vmDiskClient.
   261  			DeleteDataDisk(ocloudServiceName.(string), odeploymentName.(string), ovm.(string), olun.(int), false)
   262  		if err != nil {
   263  			return fmt.Errorf("Error detaching data disk %s: %s", d.Id(), err)
   264  		}
   265  
   266  		// Wait until the data disk is detached
   267  		if err := mc.WaitForOperation(req, nil); err != nil {
   268  			return fmt.Errorf(
   269  				"Error waiting for data disk %s to be detached: %s", d.Id(), err)
   270  		}
   271  
   272  		log.Printf("[DEBUG] Verifying data disk %s is properly detached...", d.Id())
   273  		for i := 0; i < 6; i++ {
   274  			disk, err := vmDiskClient.GetDisk(d.Id())
   275  			if err != nil {
   276  				return fmt.Errorf("Error retrieving disk %s: %s", d.Id(), err)
   277  			}
   278  
   279  			// Check if the disk is really detached
   280  			if disk.AttachedTo.RoleName == "" {
   281  				break
   282  			}
   283  
   284  			// If not, wait 30 seconds and try it again...
   285  			time.Sleep(time.Duration(30 * time.Second))
   286  		}
   287  
   288  		if d.HasChange("size") {
   289  			p := virtualmachinedisk.UpdateDiskParameters{
   290  				Name:            d.Id(),
   291  				Label:           d.Get("label").(string),
   292  				ResizedSizeInGB: d.Get("size").(int),
   293  			}
   294  
   295  			log.Printf("[DEBUG] Updating disk: %s", d.Id())
   296  			req, err := vmDiskClient.UpdateDisk(d.Id(), p)
   297  			if err != nil {
   298  				return fmt.Errorf("Error updating disk %s: %s", d.Id(), err)
   299  			}
   300  
   301  			// Wait until the disk is updated
   302  			if err := mc.WaitForOperation(req, nil); err != nil {
   303  				return fmt.Errorf(
   304  					"Error waiting for disk %s to be updated: %s", d.Id(), err)
   305  			}
   306  		}
   307  
   308  		p := virtualmachinedisk.CreateDataDiskParameters{
   309  			DiskName:    d.Id(),
   310  			Lun:         lun,
   311  			HostCaching: hostCaching(d),
   312  			MediaLink:   mediaLink(d),
   313  		}
   314  
   315  		log.Printf("[DEBUG] Attaching data disk: %s", d.Id())
   316  		req, err = vmDiskClient.AddDataDisk(cloudServiceName, deploymentName, vm, p)
   317  		if err != nil {
   318  			return fmt.Errorf("Error attaching data disk %s to instance %s: %s", d.Id(), vm, err)
   319  		}
   320  
   321  		// Wait until the data disk is attached
   322  		if err := mc.WaitForOperation(req, nil); err != nil {
   323  			return fmt.Errorf(
   324  				"Error waiting for data disk %s to be attached to instance %s: %s", d.Id(), vm, err)
   325  		}
   326  
   327  		// Make sure we return here since all possible changes are
   328  		// already updated if we reach this point
   329  		return nil
   330  	}
   331  
   332  	if d.HasChange("caching") {
   333  		p := virtualmachinedisk.UpdateDataDiskParameters{
   334  			DiskName:    d.Id(),
   335  			Lun:         lun,
   336  			HostCaching: hostCaching(d),
   337  			MediaLink:   mediaLink(d),
   338  		}
   339  
   340  		log.Printf("[DEBUG] Updating data disk: %s", d.Id())
   341  		req, err := vmDiskClient.UpdateDataDisk(cloudServiceName, deploymentName, vm, lun, p)
   342  		if err != nil {
   343  			return fmt.Errorf("Error updating data disk %s: %s", d.Id(), err)
   344  		}
   345  
   346  		// Wait until the data disk is updated
   347  		if err := mc.WaitForOperation(req, nil); err != nil {
   348  			return fmt.Errorf(
   349  				"Error waiting for data disk %s to be updated: %s", d.Id(), err)
   350  		}
   351  	}
   352  
   353  	return resourceAzureDataDiskRead(d, meta)
   354  }
   355  
   356  func resourceAzureDataDiskDelete(d *schema.ResourceData, meta interface{}) error {
   357  	mc := meta.(*Client).mgmtClient
   358  	vmDiskClient := meta.(*Client).vmDiskClient
   359  	vmClient := meta.(*Client).vmClient
   360  
   361  	lun := d.Get("lun").(int)
   362  	vm := d.Get("virtual_machine").(string)
   363  	
   364  	cloudServiceName := d.Get("cloud_service_name").(string)
   365  	if cloudServiceName == "" {
   366  		cloudServiceName = vm
   367  	}
   368  
   369  	deploymentName := d.Get("deployment_name").(string)
   370  	if deploymentName == "" {
   371  		deptName, err := vmClient.GetDeploymentName(cloudServiceName)
   372  		if err != nil {
   373  			return fmt.Errorf("Error deleting data disk %d for instance %s while getting deployment name from cloud service %s: %s", lun, vm, cloudServiceName, err)
   374  		}
   375  		deploymentName = deptName
   376  	}
   377  	if deploymentName == "" {
   378  		return fmt.Errorf("Error deleting data disk %d for instance %s while getting deployment name from cloud service %s: Deployment Name is blank", lun, vm, cloudServiceName)
   379  	}
   380  
   381  	// If a name was not supplied, it means we created a new emtpy disk and we now want to
   382  	// delete that disk again. Otherwise we only want to detach the disk and keep the blob.
   383  	_, removeBlob := d.GetOk("name")
   384  
   385  	log.Printf("[DEBUG] Detaching data disk %s with removeBlob = %t", d.Id(), removeBlob)
   386  	req, err := vmDiskClient.DeleteDataDisk(cloudServiceName, deploymentName, vm, lun, removeBlob)
   387  	if err != nil {
   388  		return fmt.Errorf(
   389  			"Error detaching data disk %s with removeBlob = %t: %s", d.Id(), removeBlob, err)
   390  	}
   391  
   392  	// Wait until the data disk is detached and optionally deleted
   393  	if err := mc.WaitForOperation(req, nil); err != nil {
   394  		return fmt.Errorf(
   395  			"Error waiting for data disk %s to be detached with removeBlob = %t: %s",
   396  			d.Id(), removeBlob, err)
   397  	}
   398  
   399  	d.SetId("")
   400  
   401  	return nil
   402  }
   403  
   404  func hostCaching(d *schema.ResourceData) virtualmachinedisk.HostCachingType {
   405  	switch d.Get("caching").(string) {
   406  	case "ReadOnly":
   407  		return virtualmachinedisk.HostCachingTypeReadOnly
   408  	case "ReadWrite":
   409  		return virtualmachinedisk.HostCachingTypeReadWrite
   410  	default:
   411  		return virtualmachinedisk.HostCachingTypeNone
   412  	}
   413  }
   414  
   415  func mediaLink(d *schema.ResourceData) string {
   416  	mediaLink, ok := d.GetOk("media_link")
   417  	if ok {
   418  		return mediaLink.(string)
   419  	}
   420  
   421  	name, ok := d.GetOk("name")
   422  	if !ok {
   423  		name = fmt.Sprintf("%s-%d", d.Get("virtual_machine").(string), d.Get("lun").(int))
   424  	}
   425  
   426  	return fmt.Sprintf(dataDiskBlobStorageURL, d.Get("storage_service_name").(string), name.(string))
   427  }
   428  
   429  func verifyDataDiskParameters(d *schema.ResourceData) error {
   430  	caching := d.Get("caching").(string)
   431  	if caching != "None" && caching != "ReadOnly" && caching != "ReadWrite" {
   432  		return fmt.Errorf(
   433  			"Invalid caching type %s! Valid options are 'None', 'ReadOnly' and 'ReadWrite'.", caching)
   434  	}
   435  
   436  	if _, ok := d.GetOk("media_link"); !ok {
   437  		if _, ok := d.GetOk("storage_service_name"); !ok {
   438  			return fmt.Errorf("If not supplying 'media_link', you must supply 'storage'.")
   439  		}
   440  	}
   441  
   442  	return nil
   443  }