diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index b0364ceb2..df57330e6 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -7,7 +7,6 @@ import ( "log" "runtime" - "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute" "github.com/hashicorp/packer/builder/amazon/chroot" azcommon "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/client" @@ -16,6 +15,8 @@ import ( "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" + + "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute" ) type Config struct { @@ -32,8 +33,12 @@ type Config struct { ChrootMounts [][]string `mapstructure:"chroot_mounts"` CopyFiles []string `mapstructure:"copy_files"` - OSDiskSizeGB int32 `mapstructure:"osdisk_size_gb"` - OSDiskStorageAccountType string `mapstructure:"osdisk_storageaccounttype"` + OSDiskSizeGB int32 `mapstructure:"os_disk_size_gb"` + OSDiskStorageAccountType string `mapstructure:"os_disk_storage_account_type"` + OSDiskCacheType string `mapstructure:"os_disk_cache_type"` + + ImageResourceID string `mapstructure:"image_resource_id"` + ImageOSState string `mapstructure:"image_os_state"` ctx interpolate.Context } @@ -60,11 +65,48 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, raws...) - // defaults + // Defaults + if b.config.ChrootMounts == nil { + b.config.ChrootMounts = make([][]string, 0) + } + + if len(b.config.ChrootMounts) == 0 { + b.config.ChrootMounts = [][]string{ + {"proc", "proc", "/proc"}, + {"sysfs", "sysfs", "/sys"}, + {"bind", "/dev", "/dev"}, + {"devpts", "devpts", "/dev/pts"}, + {"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"}, + } + } + + // set default copy file if we're not giving our own + if b.config.CopyFiles == nil { + if !b.config.FromScratch { + b.config.CopyFiles = []string{"/etc/resolv.conf"} + } + } + + if b.config.CommandWrapper == "" { + b.config.CommandWrapper = "{{.Command}}" + } + + if b.config.MountPath == "" { + b.config.MountPath = "/mnt/packer-amazon-chroot-volumes/{{.Device}}" + } + + if b.config.MountPartition == "" { + b.config.MountPartition = "1" + } + if b.config.OSDiskStorageAccountType == "" { b.config.OSDiskStorageAccountType = string(compute.PremiumLRS) } + if b.config.OSDiskCacheType == "" { + b.config.OSDiskCacheType = string(compute.CachingTypesReadOnly) + } + // checks, accumulate any errors or warnings var errs *packer.MultiError var warns []string @@ -91,6 +133,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack return nil, errors.New("the azure-chroot builder only works on Linux environments") } + // todo: instantiate Azure client var azcli client.AzureClientSet wrappedCommand := func(command string) (string, error) { @@ -104,6 +147,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("config", &b.config) state.Put("hook", hook) state.Put("ui", ui) + state.Put("azureclient", azcli) state.Put("wrappedCommand", chroot.CommandWrapper(wrappedCommand)) info, err := azcli.MetadataClient().GetComputeInfo() @@ -142,11 +186,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } steps = append(steps, - &StepAttachDisk{ // sets 'device' in stateBag - SubscriptionID: info.SubscriptionID, - ResourceGroup: info.ResourceGroupName, - DiskName: osDiskName, - }, + &StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag &chroot.StepPreMountCommands{ Commands: b.config.PreMountCommands, }, @@ -166,6 +206,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &chroot.StepChrootProvision{}, &chroot.StepEarlyCleanup{}, + &StepCreateImage{ + ImageResourceID: b.config.ImageResourceID, + ImageOSState: b.config.ImageOSState, + OSDiskCacheType: b.config.OSDiskCacheType, + OSDiskStorageAccountType: b.config.OSDiskStorageAccountType, + }, ) // Run! diff --git a/builder/azure/chroot/step_attach_disk.go b/builder/azure/chroot/step_attach_disk.go index c090f2e42..87f4e5c9b 100644 --- a/builder/azure/chroot/step_attach_disk.go +++ b/builder/azure/chroot/step_attach_disk.go @@ -3,27 +3,24 @@ package chroot import ( "context" "fmt" + "log" + "time" + "github.com/hashicorp/packer/builder/azure/common/client" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "log" - "time" ) var _ multistep.Step = &StepAttachDisk{} type StepAttachDisk struct { - SubscriptionID, ResourceGroup, DiskName string } 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("os_disk_resource_id").(string) - diskResourceID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s", - s.SubscriptionID, - s.ResourceGroup, - s.DiskName) ui.Say(fmt.Sprintf("Attaching disk '%s'", diskResourceID)) da := NewDiskAttacher(azcli) @@ -58,11 +55,8 @@ func (s StepAttachDisk) Cleanup(state multistep.StateBag) { func (s *StepAttachDisk) CleanupFunc(state multistep.StateBag) error { azcli := state.Get("azureclient").(client.AzureClientSet) ui := state.Get("ui").(packer.Ui) + diskResourceID := state.Get("os_disk_resource_id").(string) - diskResourceID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/disks/%s", - s.SubscriptionID, - s.ResourceGroup, - s.DiskName) ui.Say(fmt.Sprintf("Detaching disk '%s'", diskResourceID)) da := NewDiskAttacher(azcli) diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go new file mode 100644 index 000000000..ad05b85ac --- /dev/null +++ b/builder/azure/chroot/step_create_image.go @@ -0,0 +1,101 @@ +package chroot + +import ( + "context" + "fmt" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/packer/builder/azure/common/client" + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" + "log" +) + +var _ multistep.Step = &StepCreateImage{} + +type StepCreateImage struct { + ImageResourceID string + ImageOSState string + OSDiskStorageAccountType string + OSDiskCacheType string + + imageResource azure.Resource +} + +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("os_disk_resource_id").(string) + + ui.Say(fmt.Sprintf("Creating image %s\n using %s for os disk.", + s.ImageResourceID, + diskResourceID)) + + var err error + s.imageResource, err = azure.ParseResourceID(s.ImageResourceID) + + if err != nil { + log.Printf("StepCreateImage.Run: error: %+v", err) + err := fmt.Errorf( + "error parsing image resource id '%s': %v", s.ImageResourceID, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + image := compute.Image{ + ImageProperties: &compute.ImageProperties{ + StorageProfile: &compute.ImageStorageProfile{ + OsDisk: &compute.ImageOSDisk{ + OsType: "Linux", + OsState: compute.OperatingSystemStateTypes(s.ImageOSState), + ManagedDisk: &compute.SubResource{ + ID: &diskResourceID, + }, + Caching: compute.CachingTypes(s.OSDiskCacheType), + StorageAccountType: compute.StorageAccountTypes(s.OSDiskStorageAccountType), + }, + // DataDisks: nil, + // ZoneResilient: nil, + }, + }, + // Tags: nil, + } + f, err := azcli.ImagesClient().CreateOrUpdate( + ctx, + s.imageResource.ResourceGroup, + s.imageResource.ResourceName, + image) + if err == nil { + err = f.WaitForCompletionRef(ctx, azcli.PollClient()) + } + if err != nil { + log.Printf("StepCreateImage.Run: error: %+v", err) + err := fmt.Errorf( + "error creating image '%s': %v", s.ImageResourceID, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepCreateImage) Cleanup(state multistep.StateBag) { + azcli := state.Get("azureclient").(client.AzureClientSet) + ui := state.Get("ui").(packer.Ui) + + ctx := context.Background() + f, err := azcli.ImagesClient().Delete( + ctx, + s.imageResource.ResourceGroup, + s.imageResource.ResourceName) + if err == nil { + err = f.WaitForCompletionRef(ctx, azcli.PollClient()) + } + if err != nil { + log.Printf("StepCreateImage.Cleanup: error: %+v", err) + ui.Error(fmt.Sprintf( + "error deleting image '%s': %v", s.ImageResourceID, err)) + } +}