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 }