Move to using a diskset

Need to store lun numbers for data disks as well
This commit is contained in:
Paul Meyer 2020-04-15 22:58:06 +00:00
parent 3a3e6e525b
commit 12f746b2b5
11 changed files with 120 additions and 68 deletions

View File

@ -432,8 +432,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
artifact.Resources = append(artifact.Resources, b.config.SharedImageGalleryDestination.ResourceID(info.SubscriptionID))
}
if b.config.SkipCleanup {
if d, ok := state.GetOk(stateBagKey_OSDiskResourceID); ok {
artifact.Resources = append(artifact.Resources, d.(string))
if d, ok := state.GetOk(stateBagKey_Diskset); ok {
for _, disk := range d.([]client.Resource) {
artifact.Resources = append(artifact.Resources, disk.String())
}
}
if d, ok := state.GetOk(stateBagKey_OSDiskSnapshotResourceID); ok {
artifact.Resources = append(artifact.Resources, d.(string))

View File

@ -1,6 +1,6 @@
package chroot
const (
stateBagKey_OSDiskResourceID = "os_disk_resource_id"
stateBagKey_Diskset = "diskset"
stateBagKey_OSDiskSnapshotResourceID = "os_disk_snapshot_resource_id"
)

View File

@ -0,0 +1,23 @@
package chroot
import "github.com/hashicorp/packer/builder/azure/common/client"
// Diskset represents all of the disks or snapshots associated with an image.
// It maps lun to resource ids. The OS disk is stored with lun=-1.
type Diskset map[int]client.Resource
// OS return the OS disk resource ID or nil if it is not assigned
func (ds Diskset) OS() *client.Resource {
if r, ok := ds[-1]; ok {
return &r
}
return nil
}
// Data return the data disk resource ID or nil if it is not assigned
func (ds Diskset) Data(lun int) *client.Resource {
if r, ok := ds[lun]; ok {
return &r
}
return nil
}

View File

@ -0,0 +1,16 @@
package chroot
import "github.com/hashicorp/packer/builder/azure/common/client"
// diskset easily creates a diskset for testing
func diskset(ids ...string) Diskset {
diskset := make(Diskset)
for i, id := range ids {
r, err := client.ParseResourceID(id)
if err != nil {
panic(err)
}
diskset[i-1] = r
}
return diskset
}

View File

@ -20,7 +20,8 @@ type StepAttachDisk struct {
func (s *StepAttachDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui)
diskResourceID := state.Get(stateBagKey_OSDiskResourceID).(string)
diskset := state.Get(stateBagKey_Diskset).(Diskset)
diskResourceID := diskset.OS().String()
ui.Say(fmt.Sprintf("Attaching disk '%s'", diskResourceID))
@ -67,7 +68,8 @@ func (s *StepAttachDisk) CleanupFunc(state multistep.StateBag) error {
if s.attached {
azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui)
diskResourceID := state.Get(stateBagKey_OSDiskResourceID).(string)
diskset := state.Get(stateBagKey_Diskset).(Diskset)
diskResourceID := diskset.OS().String()
ui.Say(fmt.Sprintf("Detaching disk '%s'", diskResourceID))

View File

@ -78,7 +78,7 @@ func TestStepAttachDisk_Run(t *testing.T) {
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{})
state.Put("ui", ui)
state.Put(stateBagKey_OSDiskResourceID, "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1")
state.Put(stateBagKey_Diskset, diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"))
got := s.Run(context.TODO(), state)
if !reflect.DeepEqual(got, tt.want) {

View File

@ -28,7 +28,8 @@ type StepCreateImage struct {
func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui)
diskResourceID := state.Get(stateBagKey_OSDiskResourceID).(string)
diskset := state.Get(stateBagKey_Diskset).(Diskset)
diskResourceID := diskset.OS().String()
ui.Say(fmt.Sprintf("Creating image %s\n using %s for os disk.",
s.ImageResourceID,

View File

@ -7,12 +7,12 @@ import (
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
"github.com/hashicorp/packer/builder/azure/common/client"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
"github.com/Azure/go-autorest/autorest/to"
)
var _ multistep.Step = &StepCreateNewDiskset{}
@ -22,7 +22,7 @@ type StepCreateNewDiskset struct {
OSDiskSizeGB int32 // optional, ignored if 0
OSDiskStorageAccountType string // from compute.DiskStorageAccountTypes
subscriptionID, resourceGroup, diskName string // split out resource id
disks Diskset
HyperVGeneration string // For OS disk
@ -39,38 +39,56 @@ type StepCreateNewDiskset struct {
SkipCleanup bool
}
func parseDiskResourceID(resourceID string) (subscriptionID, resourceGroup, diskName string, err error) {
r, err := azure.ParseResourceID(resourceID)
if err != nil {
return "", "", "", err
}
if !strings.EqualFold(r.Provider, "Microsoft.Compute") ||
!strings.EqualFold(r.ResourceType, "disks") {
return "", "", "", fmt.Errorf("Resource %q is not of type Microsoft.Compute/disks", resourceID)
}
return r.SubscriptionID, r.ResourceGroup, r.ResourceName, nil
}
func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui)
state.Put(stateBagKey_OSDiskResourceID, s.OSDiskID)
ui.Say(fmt.Sprintf("Creating disk '%s'", s.OSDiskID))
s.disks = make(map[int]client.Resource)
var err error
s.subscriptionID, s.resourceGroup, s.diskName, err = parseDiskResourceID(s.OSDiskID)
if err != nil {
log.Printf("StepCreateNewDisk.Run: error: %+v", err)
err := fmt.Errorf(
"error parsing resource id '%s': %v", s.OSDiskID, err)
errorMessage := func(format string, params ...interface{}) multistep.StepAction {
err := fmt.Errorf("StepCreateNewDisk.Run: error: "+format, params...)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// we always have an OS disk
osDisk, err := client.ParseResourceID(s.OSDiskID)
if err != nil {
return errorMessage("error parsing resource id '%s': %v", s.OSDiskID, err)
}
if !strings.EqualFold(osDisk.Provider, "Microsoft.Compute") ||
!strings.EqualFold(osDisk.ResourceType.String(), "disks") {
return errorMessage("Resource %q is not of type Microsoft.Compute/disks", s.OSDiskID)
}
// transform step config to disk model
disk := s.getOSDiskDefinition(azcli)
// Initiate disk creation
f, err := azcli.DisksClient().CreateOrUpdate(ctx, osDisk.ResourceGroup, osDisk.ResourceName.String(), disk)
if err != nil {
return errorMessage("Failed to initiate resource creation: %q", osDisk)
}
s.disks[-1] = osDisk // save the resoure we just create in our disk set
state.Put(stateBagKey_Diskset, s.disks) // update the statebag
ui.Say(fmt.Sprintf("Creating disk %q", s.OSDiskID))
// Wait for completion
{
cli := azcli.PollClient() // quick polling for quick operations
cli.PollingDelay = time.Second
err = f.WaitForCompletionRef(ctx, cli)
if err != nil {
return errorMessage(
"error creating new disk '%s': %v", s.OSDiskID, err)
}
}
return multistep.ActionContinue
}
func (s *StepCreateNewDiskset) getOSDiskDefinition(azcli client.AzureClientSet) compute.Disk {
disk := compute.Disk{
Location: to.StringPtr(s.Location),
DiskProperties: &compute.DiskProperties{
@ -99,7 +117,8 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag
disk.CreationData.ImageReference = &compute.ImageDiskReference{
ID: to.StringPtr(fmt.Sprintf(
"/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s",
s.subscriptionID, s.Location, s.SourcePlatformImage.Publisher, s.SourcePlatformImage.Offer, s.SourcePlatformImage.Sku, s.SourcePlatformImage.Version)),
azcli.SubscriptionID(), s.Location,
s.SourcePlatformImage.Publisher, s.SourcePlatformImage.Offer, s.SourcePlatformImage.Sku, s.SourcePlatformImage.Version)),
}
case s.SourceOSDiskResourceID != "":
disk.CreationData.CreateOption = compute.Copy
@ -112,23 +131,7 @@ func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag
default:
disk.CreationData.CreateOption = compute.Empty
}
f, err := azcli.DisksClient().CreateOrUpdate(ctx, s.resourceGroup, s.diskName, disk)
if err == nil {
cli := azcli.PollClient() // quick polling for quick operations
cli.PollingDelay = time.Second
err = f.WaitForCompletionRef(ctx, cli)
}
if err != nil {
log.Printf("StepCreateNewDisk.Run: error: %+v", err)
err := fmt.Errorf(
"error creating new disk '%s': %v", s.OSDiskID, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
return disk
}
func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) {
@ -136,21 +139,24 @@ func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) {
azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui)
ui.Say(fmt.Sprintf("Waiting for disk %q detach to complete", s.OSDiskID))
err := NewDiskAttacher(azcli).WaitForDetach(context.Background(), s.OSDiskID)
if err != nil {
ui.Error(fmt.Sprintf("error detaching disk %q: %s", s.OSDiskID, err))
}
for _, d := range s.disks {
ui.Say(fmt.Sprintf("Deleting disk %q", s.OSDiskID))
ui.Say(fmt.Sprintf("Waiting for disk %q detach to complete", d))
err := NewDiskAttacher(azcli).WaitForDetach(context.Background(), d.String())
if err != nil {
ui.Error(fmt.Sprintf("error detaching disk %q: %s", d, err))
}
f, err := azcli.DisksClient().Delete(context.TODO(), s.resourceGroup, s.diskName)
if err == nil {
err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient())
}
if err != nil {
log.Printf("StepCreateNewDisk.Cleanup: error: %+v", err)
ui.Error(fmt.Sprintf("error deleting disk '%s': %v.", s.OSDiskID, err))
ui.Say(fmt.Sprintf("Deleting disk %q", d))
f, err := azcli.DisksClient().Delete(context.TODO(), d.ResourceGroup, d.ResourceName.String())
if err == nil {
err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient())
}
if err != nil {
log.Printf("StepCreateNewDisk.Cleanup: error: %+v", err)
ui.Error(fmt.Sprintf("error deleting disk '%s': %v.", d, err))
}
}
}
}

View File

@ -128,7 +128,8 @@ func TestStepCreateNewDisk_Run(t *testing.T) {
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{
DisksClientMock: m,
SubscriptionIDMock: "SubscriptionID",
DisksClientMock: m,
})
state.Put("ui", packer.TestUi(t))

View File

@ -44,7 +44,8 @@ func parseSnapshotResourceID(resourceID string) (subscriptionID, resourceGroup,
func (s *StepCreateSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui)
osDiskResourceID := state.Get(stateBagKey_OSDiskResourceID).(string)
diskset := state.Get(stateBagKey_Diskset).(Diskset)
osDiskResourceID := diskset.OS().String()
state.Put(stateBagKey_OSDiskSnapshotResourceID, s.ResourceID)
ui.Say(fmt.Sprintf("Creating snapshot '%s'", s.ResourceID))

View File

@ -94,7 +94,7 @@ func TestStepCreateSnapshot_Run(t *testing.T) {
"properties": {
"creationData": {
"createOption": "Copy",
"sourceResourceId": "osdiskresourceid"
"sourceResourceId": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"
},
"incremental": false
}
@ -132,7 +132,7 @@ func TestStepCreateSnapshot_Run(t *testing.T) {
SnapshotsClientMock: m,
})
state.Put("ui", packer.TestUi(t))
state.Put(stateBagKey_OSDiskResourceID, "osdiskresourceid")
state.Put(stateBagKey_Diskset, diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"))
t.Run(tt.name, func(t *testing.T) {
s := &StepCreateSnapshot{