packer-cn/builder/azure/chroot/diskattacher.go

223 lines
5.5 KiB
Go
Raw Normal View History

2019-04-25 19:12:18 -04:00
package chroot
import (
"context"
"errors"
"fmt"
2019-06-03 01:27:33 -04:00
"log"
2019-04-25 19:12:18 -04:00
"os"
"path/filepath"
"strings"
2019-06-03 01:27:33 -04:00
"syscall"
2019-04-25 19:12:18 -04:00
"time"
"github.com/hashicorp/packer/builder/azure/common/client"
2020-03-25 15:15:46 -04:00
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
2019-04-25 19:12:18 -04:00
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
)
type DiskAttacher interface {
AttachDisk(ctx context.Context, disk string) (lun int32, err error)
2019-06-03 01:27:33 -04:00
DiskPathForLun(lun int32) string
2019-06-03 19:01:53 -04:00
WaitForDevice(ctx context.Context, i int32) (device string, err error)
DetachDisk(ctx context.Context, disk string) (err error)
WaitForDetach(ctx context.Context, diskID string) error
2019-04-25 19:12:18 -04:00
}
2019-10-04 15:00:22 -04:00
var NewDiskAttacher = func(azureClient client.AzureClientSet) DiskAttacher {
2019-06-03 01:27:33 -04:00
return &diskAttacher{
azcli: azureClient,
}
2019-04-25 19:12:18 -04:00
}
type diskAttacher struct {
azcli client.AzureClientSet
2019-06-03 01:27:33 -04:00
vm *client.ComputeInfo // store info about this VM so that we don't have to ask metadata service on every call
}
func (diskAttacher) DiskPathForLun(lun int32) string {
return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun)
2019-04-25 19:12:18 -04:00
}
2019-06-03 01:27:33 -04:00
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
path := da.DiskPathForLun(lun)
2019-04-25 19:12:18 -04:00
for {
2019-06-03 01:27:33 -04:00
link, err := os.Readlink(path)
2019-04-25 19:12:18 -04:00
if err == nil {
2019-06-03 01:27:33 -04:00
return filepath.Abs("/dev/disk/azure/scsi1/" + link)
2019-10-15 17:13:07 -04:00
} else if err != os.ErrNotExist {
2019-06-03 01:27:33 -04:00
if pe, ok := err.(*os.PathError); ok && pe.Err != syscall.ENOENT {
return "", err
}
2019-04-25 19:12:18 -04:00
}
2019-06-03 01:27:33 -04:00
2019-04-25 19:12:18 -04:00
select {
case <-time.After(100 * time.Millisecond):
// continue
case <-ctx.Done():
return "", ctx.Err()
}
}
}
2019-06-03 01:27:33 -04:00
func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error {
log.Println("Fetching list of disks currently attached to VM")
2019-04-25 19:12:18 -04:00
currentDisks, err := da.getDisks(ctx)
if err != nil {
return err
}
2019-06-03 01:27:33 -04:00
log.Printf("Removing %q from list of disks currently attached to VM", diskID)
2019-04-25 19:12:18 -04:00
newDisks := []compute.DataDisk{}
for _, disk := range currentDisks {
if disk.ManagedDisk != nil &&
!strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
newDisks = append(newDisks, disk)
}
}
if len(currentDisks) == len(newDisks) {
return DiskNotFoundError
}
2019-06-03 01:27:33 -04:00
log.Println("Updating new list of disks attached to VM")
err = da.setDisks(ctx, newDisks)
if err != nil {
return err
}
2019-06-03 19:01:53 -04:00
return nil
}
func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
2019-06-03 01:27:33 -04:00
for { // loop until disk is not attached, timeout or error
list, err := da.getDisks(ctx)
if err != nil {
return err
}
if findDiskInList(list, diskID) == nil {
log.Println("Disk is no longer in VM model, assuming detached")
return nil
}
select {
case <-time.After(time.Second): //continue
case <-ctx.Done():
return ctx.Err()
}
}
2019-04-25 19:12:18 -04:00
}
var DiskNotFoundError = errors.New("Disk not found")
2019-06-03 01:27:33 -04:00
func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) {
2019-04-25 19:12:18 -04:00
dataDisks, err := da.getDisks(ctx)
if err != nil {
return -1, err
}
// check to see if disk is already attached, remember lun if found
2019-06-03 01:27:33 -04:00
if disk := findDiskInList(dataDisks, diskID); disk != nil {
// disk is already attached, just take this lun
if disk.Lun == nil {
return -1, errors.New("disk is attached, but lun was not set in VM model (possibly an error in the Azure APIs)")
2019-04-25 19:12:18 -04:00
}
2019-06-03 01:27:33 -04:00
return to.Int32(disk.Lun), nil
2019-04-25 19:12:18 -04:00
}
2019-06-03 01:27:33 -04:00
// disk was not found on VM, go and actually attach it
2019-04-25 19:12:18 -04:00
2019-06-03 01:27:33 -04:00
var lun int32 = -1
findFreeLun:
for lun = 0; lun < 64; lun++ {
for _, v := range dataDisks {
if to.Int32(v.Lun) == lun {
continue findFreeLun
2019-04-25 19:12:18 -04:00
}
}
2019-06-03 01:27:33 -04:00
// no datadisk is using this lun
break
}
2019-04-25 19:12:18 -04:00
2019-06-03 01:27:33 -04:00
// append new data disk to collection
dataDisks = append(dataDisks, compute.DataDisk{
CreateOption: compute.DiskCreateOptionTypesAttach,
ManagedDisk: &compute.ManagedDiskParameters{
ID: to.StringPtr(diskID),
},
Lun: to.Int32Ptr(lun),
})
// prepare resource object for update operation
err = da.setDisks(ctx, dataDisks)
if err != nil {
return -1, err
2019-04-25 19:12:18 -04:00
}
2019-06-03 01:27:33 -04:00
2019-04-25 19:12:18 -04:00
return lun, nil
}
2019-06-03 01:27:33 -04:00
func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) {
2019-04-25 19:12:18 -04:00
// getting resource info for this VM
2019-06-03 01:27:33 -04:00
if da.vm == nil {
vm, err := da.azcli.MetadataClient().GetComputeInfo()
if err != nil {
return compute.VirtualMachine{}, err
}
da.vm = vm
2019-04-25 19:12:18 -04:00
}
// retrieve actual VM
2019-06-03 01:27:33 -04:00
vmResource, err := da.azcli.VirtualMachinesClient().Get(ctx, da.vm.ResourceGroupName, da.vm.Name, "")
2019-04-25 19:12:18 -04:00
if err != nil {
return compute.VirtualMachine{}, err
}
if vmResource.StorageProfile == nil {
return compute.VirtualMachine{}, errors.New("properties.storageProfile is not set on VM, this is unexpected")
}
return vmResource, nil
}
func (da diskAttacher) getDisks(ctx context.Context) ([]compute.DataDisk, error) {
vmResource, err := da.getThisVM(ctx)
if err != nil {
return []compute.DataDisk{}, err
}
return *vmResource.StorageProfile.DataDisks, nil
}
func (da diskAttacher) setDisks(ctx context.Context, disks []compute.DataDisk) error {
vmResource, err := da.getThisVM(ctx)
if err != nil {
return err
}
id, err := azure.ParseResourceID(to.String(vmResource.ID))
if err != nil {
return err
}
vmResource.StorageProfile.DataDisks = &disks
vmResource.Resources = nil
2019-06-03 01:27:33 -04:00
// update the VM resource, attach disk
_, err = da.azcli.VirtualMachinesClient().CreateOrUpdate(ctx, id.ResourceGroup, id.ResourceName, vmResource)
2019-04-25 19:12:18 -04:00
return err
}
2019-06-03 01:27:33 -04:00
func findDiskInList(list []compute.DataDisk, diskID string) *compute.DataDisk {
for _, disk := range list {
if disk.ManagedDisk != nil &&
strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
return &disk
}
}
return nil
}