Skip to content

Commit

Permalink
Merge pull request #3838 from terraform-providers/f/storage-vm
Browse files Browse the repository at this point in the history
r/virtual_machine: switching over to the new Storage SDK
  • Loading branch information
tombuildsstuff authored Jul 12, 2019
2 parents 1150855 + 163d682 commit efc89db
Show file tree
Hide file tree
Showing 41 changed files with 4,469 additions and 110 deletions.
13 changes: 13 additions & 0 deletions azurerm/internal/services/storage/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/authorizers"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/directories"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/file/shares"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/queue/queues"
Expand Down Expand Up @@ -60,6 +61,18 @@ func (client Client) FindResourceGroup(ctx context.Context, accountName string)
return resourceGroup, nil
}

func (client Client) BlobsClient(ctx context.Context, resourceGroup, accountName string) (*blobs.Client, error) {
accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName)
if err != nil {
return nil, fmt.Errorf("Error retrieving Account Key: %s", err)
}

storageAuth := authorizers.NewSharedKeyLiteAuthorizer(accountName, *accountKey)
blobsClient := blobs.New()
blobsClient.Client.Authorizer = storageAuth
return &blobsClient, nil
}

func (client Client) FileShareDirectoriesClient(ctx context.Context, resourceGroup, accountName string) (*directories.Client, error) {
accountKey, err := client.findAccountKey(ctx, resourceGroup, accountName)
if err != nil {
Expand Down
182 changes: 87 additions & 95 deletions azurerm/resource_arm_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import (
"bytes"
"fmt"
"log"
"net/url"
"strings"

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-12-01/network"
"github.com/Azure/azure-sdk-for-go/storage"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
intStor "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs"
"golang.org/x/net/context"
)

Expand All @@ -27,6 +27,7 @@ func resourceArmVirtualMachine() *schema.Resource {
Read: resourceArmVirtualMachineRead,
Update: resourceArmVirtualMachineCreateUpdate,
Delete: resourceArmVirtualMachineDelete,
// TODO: use a custom importer so that `delete_os_disk_on_termination` and `delete_data_disks_on_termination` are set
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Expand Down Expand Up @@ -848,117 +849,133 @@ func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) e
azureRMLockByName(name, virtualMachineResourceName)
defer azureRMUnlockByName(name, virtualMachineResourceName)

virtualMachine, err := client.Get(ctx, resGroup, name, "")
if err != nil {
return fmt.Errorf("Error retrieving Virtual Machine %q (Resource Group %q): %s", name, resGroup, err)
}

future, err := client.Delete(ctx, resGroup, name)
if err != nil {
return err
return fmt.Errorf("Error deleting Virtual Machine %q (Resource Group %q): %s", name, resGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return err
return fmt.Errorf("Error waiting for deletion of Virtual Machine %q (Resource Group %q): %s", name, resGroup, err)
}

// delete OS Disk if opted in
if deleteOsDisk := d.Get("delete_os_disk_on_termination").(bool); deleteOsDisk {
log.Printf("[INFO] delete_os_disk_on_termination is enabled, deleting disk from %s", name)
deleteOsDisk := d.Get("delete_os_disk_on_termination").(bool)
deleteDataDisks := d.Get("delete_data_disks_on_termination").(bool)

osDisk, err := expandAzureRmVirtualMachineOsDisk(d)
if err != nil {
return fmt.Errorf("Error expanding OS Disk: %s", err)
if deleteOsDisk || deleteDataDisks {
storageClient := meta.(*ArmClient).storage

props := virtualMachine.VirtualMachineProperties
if props == nil {
return fmt.Errorf("Error deleting Disks for Virtual Machine %q - `props` was nil", name)
}
storageProfile := props.StorageProfile
if storageProfile == nil {
return fmt.Errorf("Error deleting Disks for Virtual Machine %q - `storageProfile` was nil", name)
}

if osDisk.Vhd != nil {
if osDisk.Vhd.URI != nil {
if err = resourceArmVirtualMachineDeleteVhd(*osDisk.Vhd.URI, meta); err != nil {
if deleteOsDisk {
log.Printf("[INFO] delete_os_disk_on_termination is enabled, deleting disk from %s", name)
osDisk := storageProfile.OsDisk
if osDisk == nil {
return fmt.Errorf("Error deleting OS Disk for Virtual Machine %q - `osDisk` was nil", name)
}
if osDisk.Vhd == nil && osDisk.ManagedDisk == nil {
return fmt.Errorf("Unable to determine OS Disk Type to Delete it for Virtual Machine %q", name)
}

if osDisk.Vhd != nil {
if err = resourceArmVirtualMachineDeleteVhd(ctx, storageClient, osDisk.Vhd); err != nil {
return fmt.Errorf("Error deleting OS Disk VHD: %+v", err)
}
}
} else if osDisk.ManagedDisk != nil {
if osDisk.ManagedDisk.ID != nil {
if err = resourceArmVirtualMachineDeleteManagedDisk(*osDisk.ManagedDisk.ID, meta); err != nil {
} else if osDisk.ManagedDisk != nil {
if err = resourceArmVirtualMachineDeleteManagedDisk(osDisk.ManagedDisk, meta); err != nil {
return fmt.Errorf("Error deleting OS Managed Disk: %+v", err)
}
}
} else {
return fmt.Errorf("Unable to locate OS managed disk properties from %s", name)
}
}

// delete Data disks if opted in
if deleteDataDisks := d.Get("delete_data_disks_on_termination").(bool); deleteDataDisks {
log.Printf("[INFO] delete_data_disks_on_termination is enabled, deleting each data disk from %s", name)
// delete Data disks if opted in
if deleteDataDisks {
log.Printf("[INFO] delete_data_disks_on_termination is enabled, deleting each data disk from %q", name)

disks, err := expandAzureRmVirtualMachineDataDisk(d)
if err != nil {
return fmt.Errorf("Error expanding Data Disks: %s", err)
}
dataDisks := storageProfile.DataDisks
if dataDisks == nil {
return fmt.Errorf("Error deleting Data Disks for Virtual Machine %q: `dataDisks` was nil", name)
}

for _, disk := range disks {
if disk.Vhd != nil {
if err = resourceArmVirtualMachineDeleteVhd(*disk.Vhd.URI, meta); err != nil {
return fmt.Errorf("Error deleting Data Disk VHD: %+v", err)
for _, disk := range *dataDisks {
if disk.Vhd == nil && disk.ManagedDisk == nil {
return fmt.Errorf("Unable to determine Data Disk Type to Delete it for Virtual Machine %q / Disk %q", name, *disk.Name)
}
} else if disk.ManagedDisk != nil {
if err = resourceArmVirtualMachineDeleteManagedDisk(*disk.ManagedDisk.ID, meta); err != nil {
return fmt.Errorf("Error deleting Data Managed Disk: %+v", err)

if disk.Vhd != nil {
if err = resourceArmVirtualMachineDeleteVhd(ctx, storageClient, disk.Vhd); err != nil {
return fmt.Errorf("Error deleting Data Disk VHD: %+v", err)
}
} else if disk.ManagedDisk != nil {
if err = resourceArmVirtualMachineDeleteManagedDisk(disk.ManagedDisk, meta); err != nil {
return fmt.Errorf("Error deleting Data Managed Disk: %+v", err)
}
}
} else {
return fmt.Errorf("Unable to locate data managed disk properties from %s", name)
}
}
}

return nil
}

func resourceArmVirtualMachineDeleteVhd(uri string, meta interface{}) error {
armClient := meta.(*ArmClient)
ctx := armClient.StopContext
environment := armClient.environment

vhdURL, err := url.Parse(uri)
if err != nil {
return fmt.Errorf("Cannot parse Disk VHD URI: %s", err)
func resourceArmVirtualMachineDeleteVhd(ctx context.Context, storageClient *intStor.Client, vhd *compute.VirtualHardDisk) error {
if vhd == nil {
return fmt.Errorf("`vhd` was nil`")
}

blobDomainSuffix := environment.StorageEndpointSuffix
if !strings.HasSuffix(strings.ToLower(vhdURL.Host), strings.ToLower(blobDomainSuffix)) {
return fmt.Errorf("Error: Disk VHD URI %q doesn't appear to be a Blob Storage URI (%q) - expected a suffix of %q)", uri, vhdURL.Host, blobDomainSuffix)
if vhd.URI == nil {
return fmt.Errorf("`vhd.URI` was nil`")
}

// VHD URI is in the form: https://storageAccountName.blob.core.windows.net/containerName/blobName
storageAccountName := strings.Split(vhdURL.Host, ".")[0]
path := strings.Split(strings.TrimPrefix(vhdURL.Path, "/"), "/")
containerName := path[0]
blobName := path[1]

resourceGroupName, err := findStorageAccountResourceGroup(meta, storageAccountName)
uri := *vhd.URI
id, err := blobs.ParseResourceID(uri)
if err != nil {
return fmt.Errorf("Error finding resource group for storage account %s: %+v", storageAccountName, err)
return fmt.Errorf("Error parsing %q: %s", uri, err)
}

blobClient, saExists, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroupName, storageAccountName)
resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName)
if err != nil {
return fmt.Errorf("Error creating blob store client for VHD deletion: %+v", err)
return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err)
}

if !saExists {
log.Printf("[INFO] Storage Account %q in resource group %q doesn't exist so the VHD blob won't exist", storageAccountName, resourceGroupName)
return nil
if resourceGroup == nil {
return fmt.Errorf("Unable to locate Resource Group for Storage Account %q (Disk %q)!", id.AccountName, uri)
}

log.Printf("[INFO] Deleting VHD blob %s", blobName)
container := blobClient.GetContainerReference(containerName)
blob := container.GetBlobReference(blobName)
options := &storage.DeleteBlobOptions{}
err = blob.Delete(options)
blobsClient, err := storageClient.BlobsClient(ctx, *resourceGroup, id.AccountName)
if err != nil {
return fmt.Errorf("Error deleting VHD blob: %+v", err)
return fmt.Errorf("Error building Blobs Client: %s", err)
}

input := blobs.DeleteInput{
DeleteSnapshots: false,
}
if _, err := blobsClient.Delete(ctx, id.AccountName, id.ContainerName, id.BlobName, input); err != nil {
return fmt.Errorf("Error deleting Blob %q (Container %q / Account %q / Resource Group %q): %s", id.BlobName, id.ContainerName, id.AccountName, *resourceGroup, err)
}

return nil
}

func resourceArmVirtualMachineDeleteManagedDisk(managedDiskID string, meta interface{}) error {
func resourceArmVirtualMachineDeleteManagedDisk(disk *compute.ManagedDiskParameters, meta interface{}) error {
if disk == nil {
return fmt.Errorf("`disk` was nil`")
}
if disk.ID == nil {
return fmt.Errorf("`disk.ID` was nil`")
}
managedDiskID := *disk.ID

client := meta.(*ArmClient).diskClient
ctx := meta.(*ArmClient).StopContext

Expand All @@ -971,11 +988,11 @@ func resourceArmVirtualMachineDeleteManagedDisk(managedDiskID string, meta inter

future, err := client.Delete(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error deleting Managed Disk (%s %s) %+v", name, resGroup, err)
return fmt.Errorf("Error deleting Managed Disk %q (Resource Group %q) %+v", name, resGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error deleting Managed Disk (%s %s) %+v", name, resGroup, err)
return fmt.Errorf("Error waiting for deletion of Managed Disk %q (Resource Group %q) %+v", name, resGroup, err)
}

return nil
Expand Down Expand Up @@ -1747,31 +1764,6 @@ func expandAzureRmVirtualMachineOsDisk(d *schema.ResourceData) (*compute.OSDisk,
return osDisk, nil
}

func findStorageAccountResourceGroup(meta interface{}, storageAccountName string) (string, error) {
client := meta.(*ArmClient).resourcesClient
ctx := meta.(*ArmClient).StopContext
filter := fmt.Sprintf("name eq '%s' and resourceType eq 'Microsoft.Storage/storageAccounts'", storageAccountName)
expand := ""
var pager *int32

rf, err := client.List(ctx, filter, expand, pager)
if err != nil {
return "", fmt.Errorf("Error making resource request for query %s: %+v", filter, err)
}

results := rf.Values()
if len(results) != 1 {
return "", fmt.Errorf("Wrong number of results making resource request for query %s: %d", filter, len(results))
}

id, err := parseAzureResourceID(*results[0].ID)
if err != nil {
return "", err
}

return id.ResourceGroup, nil
}

func resourceArmVirtualMachineStorageOsProfileHash(v interface{}) int {
var buf bytes.Buffer

Expand Down
38 changes: 23 additions & 15 deletions azurerm/resource_arm_virtual_machine_unmanaged_disks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs"
)

func TestAccAzureRMVirtualMachine_basicLinuxMachine(t *testing.T) {
Expand Down Expand Up @@ -3010,35 +3012,41 @@ resource "azurerm_virtual_machine" "test" {
`, rInt, location, rInt, rInt, rInt, rInt, rInt)
}

func testCheckAzureRMVirtualMachineVHDExistence(name string, shouldExist bool) resource.TestCheckFunc {
func testCheckAzureRMVirtualMachineVHDExistence(blobName string, shouldExist bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "azurerm_storage_container" {
continue
}

// fetch storage account and container name
resourceGroup := rs.Primary.Attributes["resource_group_name"]
storageAccountName := rs.Primary.Attributes["storage_account_name"]
accountName := rs.Primary.Attributes["storage_account_name"]
containerName := rs.Primary.Attributes["name"]
armClient := testAccProvider.Meta().(*ArmClient)
ctx := armClient.StopContext
storageClient, _, err := armClient.getBlobStorageClientForStorageAccount(ctx, resourceGroup, storageAccountName)

storageClient := testAccProvider.Meta().(*ArmClient).storage
ctx := testAccProvider.Meta().(*ArmClient).StopContext

client, err := storageClient.BlobsClient(ctx, resourceGroup, accountName)
if err != nil {
return fmt.Errorf("Error creating Blob storage client: %+v", err)
return fmt.Errorf("Error building Blobs Client: %s", err)
}

container := storageClient.GetContainerReference(containerName)
blob := container.GetBlobReference(name)
exists, err := blob.Exists()
input := blobs.GetPropertiesInput{}
props, err := client.GetProperties(ctx, accountName, containerName, blobName, input)
if err != nil {
return fmt.Errorf("Error checking if Disk VHD Blob exists: %+v", err)
if utils.ResponseWasNotFound(props.Response) {
if !shouldExist {
return nil
}

return fmt.Errorf("The Blob for the Unmanaged Disk %q should exist in the Container %q but it didn't!", blobName, containerName)
}

return fmt.Errorf("Error retrieving properties for Blob %q (Container %q): %s", blobName, containerName, err)
}

if exists && !shouldExist {
return fmt.Errorf("Disk VHD Blob still exists %s %s", containerName, name)
} else if !exists && shouldExist {
return fmt.Errorf("Disk VHD Blob should exist %s %s", containerName, name)
if !shouldExist {
return fmt.Errorf("The Blob for the Unmanaged Disk %q shouldn't exist in the Container %q but it did!", blobName, containerName)
}
}

Expand Down
Loading

0 comments on commit efc89db

Please sign in to comment.