diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 7c7f0f31..b7722ff7 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -231,8 +231,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) NewStepPowerOffCompute(azureClient, ui), NewStepSnapshotOSDisk(azureClient, ui, &b.config), NewStepSnapshotDataDisks(azureClient, ui, &b.config), - NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), + } } else if b.config.OSType == constants.Target_Windows { steps = []multistep.Step{ @@ -278,13 +277,19 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) NewStepPowerOffCompute(azureClient, ui), NewStepSnapshotOSDisk(azureClient, ui, &b.config), NewStepSnapshotDataDisks(azureClient, ui, &b.config), - NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), ) } else { return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) } + captureSteps := b.config.CaptureSteps( + ui.Say, + NewStepCaptureImage(azureClient, ui), + NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), + ) + + steps = append(steps, captureSteps...) + if b.config.PackerDebug { ui.Message(fmt.Sprintf("temp admin user: '%s'", b.config.UserName)) ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password)) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 6c443cd7..0096f522 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -103,6 +103,8 @@ type SharedImageGalleryDestination struct { type Config struct { common.PackerConfig `mapstructure:",squash"` + azcommon.Config `mapstructure:",squash"` + // Authentication via OAUTH ClientConfig client.Config `mapstructure:",squash"` diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index 51dfd9d4..520c50e6 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -19,6 +19,7 @@ type FlatConfig struct { PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"` CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` MetadataHost *string `mapstructure:"metadata_host" required:"false" cty:"metadata_host" hcl:"metadata_host"` ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` @@ -153,6 +154,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false}, "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, "metadata_host": &hcldec.AttrSpec{Name: "metadata_host", Type: cty.String, Required: false}, "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 9359d937..1ad76aab 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -40,6 +40,8 @@ const BuilderID = "azure.chroot" type Config struct { common.PackerConfig `mapstructure:",squash"` + azcommon.Config `mapstructure:",squash"` + ClientConfig client.Config `mapstructure:",squash"` // When set to `true`, starts with an empty, unpartitioned disk. Defaults to `false`. @@ -441,7 +443,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) state.Put("instance", info) // Build the step array from the config - steps := buildsteps(b.config, info, &generatedData) + steps := buildsteps(b.config, info, &generatedData, ui.Say) // Run! b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui) @@ -480,7 +482,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) return artifact, nil } -func buildsteps(config Config, info *client.ComputeInfo, generatedData *packerbuilderdata.GeneratedData) []multistep.Step { +func buildsteps( + config Config, + info *client.ComputeInfo, + generatedData *packerbuilderdata.GeneratedData, + say func(string), +) []multistep.Step { // Build the steps var steps []multistep.Step addSteps := func(s ...multistep.Step) { // convenience function @@ -615,23 +622,32 @@ func buildsteps(config Config, info *client.ComputeInfo, generatedData *packerbu &chroot.StepEarlyCleanup{}, ) + var captureSteps []multistep.Step + if config.ImageResourceID != "" { - addSteps(&StepCreateImage{ - ImageResourceID: config.ImageResourceID, - ImageOSState: string(compute.Generalized), - OSDiskCacheType: config.OSDiskCacheType, - OSDiskStorageAccountType: config.OSDiskStorageAccountType, - Location: info.Location, - }) + captureSteps = append( + captureSteps, + &StepCreateImage{ + ImageResourceID: config.ImageResourceID, + ImageOSState: string(compute.Generalized), + OSDiskCacheType: config.OSDiskCacheType, + OSDiskStorageAccountType: config.OSDiskStorageAccountType, + Location: info.Location, + }, + ) } if hasValidSharedImage { - addSteps( + captureSteps = append( + captureSteps, &StepCreateSnapshotset{ OSDiskSnapshotID: config.TemporaryOSDiskSnapshotID, DataDiskSnapshotIDPrefix: config.TemporaryDataDiskSnapshotIDPrefix, Location: info.Location, SkipCleanup: config.SkipCleanup, }, + ) + captureSteps = append( + captureSteps, &StepCreateSharedImageVersion{ Destination: config.SharedImageGalleryDestination, OSDiskCacheType: config.OSDiskCacheType, @@ -640,5 +656,7 @@ func buildsteps(config Config, info *client.ComputeInfo, generatedData *packerbu ) } + addSteps(config.CaptureSteps(say, captureSteps...)...) + return steps } diff --git a/builder/azure/chroot/builder.hcl2spec.go b/builder/azure/chroot/builder.hcl2spec.go index 8915b549..c6b28d90 100644 --- a/builder/azure/chroot/builder.hcl2spec.go +++ b/builder/azure/chroot/builder.hcl2spec.go @@ -18,6 +18,7 @@ type FlatConfig struct { PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"` CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` MetadataHost *string `mapstructure:"metadata_host" required:"false" cty:"metadata_host" hcl:"metadata_host"` ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` @@ -74,6 +75,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false}, "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, "metadata_host": &hcldec.AttrSpec{Name: "metadata_host", Type: cty.String, Required: false}, "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, diff --git a/builder/azure/chroot/builder_test.go b/builder/azure/chroot/builder_test.go index 41506fa3..f3c39013 100644 --- a/builder/azure/chroot/builder_test.go +++ b/builder/azure/chroot/builder_test.go @@ -226,7 +226,7 @@ func Test_buildsteps(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { withMetadataStub(func() { // ensure that values are taken from info, instead of retrieved again - got := buildsteps(tt.config, info, &packerbuilderdata.GeneratedData{}) + got := buildsteps(tt.config, info, &packerbuilderdata.GeneratedData{}, func(string) {}) tt.verify(got, t) }) }) diff --git a/builder/azure/common/config.go b/builder/azure/common/config.go new file mode 100644 index 00000000..ea359c7c --- /dev/null +++ b/builder/azure/common/config.go @@ -0,0 +1,33 @@ +//go:generate packer-sdc struct-markdown + +package common + +import ( + "github.com/hashicorp/packer-plugin-sdk/multistep" +) + +const ( + SkippingImageCreation = "Skipping image creation..." +) + +type Config struct { + // Skip creating the image. + // Useful for setting to `true` during a build test stage. + // Defaults to `false`. + SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"` +} + +// CaptureSteps returns the steps unless `SkipCreateImage` is `true`. In that case it returns +// a step that to inform the user that image capture is being skipped. +func (config Config) CaptureSteps(say func(string), steps ...multistep.Step) []multistep.Step { + if !config.SkipCreateImage { + return steps + } + + return []multistep.Step{ + &StepNotify{ + message: SkippingImageCreation, + say: say, + }, + } +} diff --git a/builder/azure/common/config_test.go b/builder/azure/common/config_test.go new file mode 100644 index 00000000..82061080 --- /dev/null +++ b/builder/azure/common/config_test.go @@ -0,0 +1,44 @@ +package common_test + +import ( + "context" + "testing" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + "github.com/stretchr/testify/assert" + + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" +) + +func TestSkipCreateImage(t *testing.T) { + var said []string + + say := func(what string) { + said = append(said, what) + } + + config := common.Config{} + message := "Capture Image" + + steps := config.CaptureSteps(say, common.NewStepNotify(message, say)) + state := &multistep.BasicStateBag{} + + ctx := context.Background() + + for _, step := range steps { + step.Run(ctx, state) + } + + assert.Equal(t, said, []string{message}) + + said = nil + config.SkipCreateImage = true + + steps = config.CaptureSteps(say, common.NewStepNotify(message, say)) + + for _, step := range steps { + step.Run(ctx, state) + } + + assert.Equal(t, said, []string{common.SkippingImageCreation}) +} diff --git a/builder/azure/common/step_notify.go b/builder/azure/common/step_notify.go new file mode 100644 index 00000000..4b3f725b --- /dev/null +++ b/builder/azure/common/step_notify.go @@ -0,0 +1,31 @@ +package common + +import ( + "context" + + "github.com/hashicorp/packer-plugin-sdk/multistep" +) + +type StepNotify struct { + message string + say func(string) +} + +func NewStepNotify(message string, say func(string)) *StepNotify { + return &StepNotify{ + message: message, + say: say, + } +} + +func (step *StepNotify) Run( + ctx context.Context, + state multistep.StateBag, +) multistep.StepAction { + step.say(step.message) + return multistep.ActionContinue +} + +func (step *StepNotify) Cleanup(state multistep.StateBag) {} + +var _ multistep.Step = (*StepNotify)(nil) diff --git a/builder/azure/common/step_notify_test.go b/builder/azure/common/step_notify_test.go new file mode 100644 index 00000000..d7c8515a --- /dev/null +++ b/builder/azure/common/step_notify_test.go @@ -0,0 +1,30 @@ +package common_test + +import ( + "context" + "testing" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + "github.com/stretchr/testify/assert" + + "github.com/hashicorp/packer-plugin-azure/builder/azure/common" +) + +func TestStepNotify(t *testing.T) { + var said []string + + say := func(what string) { + said = append(said, what) + } + + message := "Notify Step" + + step := common.NewStepNotify(message, say) + state := &multistep.BasicStateBag{} + + ctx := context.Background() + + step.Run(ctx, state) + + assert.Equal(t, said, []string{message}) +} diff --git a/builder/azure/dtl/builder.go b/builder/azure/dtl/builder.go index 5721d00b..30833ba9 100644 --- a/builder/azure/dtl/builder.go +++ b/builder/azure/dtl/builder.go @@ -191,9 +191,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Comm: &b.config.Comm, }, NewStepPowerOffCompute(azureClient, ui, &b.config), - NewStepCaptureImage(azureClient, ui, &b.config), - NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), - NewStepDeleteVirtualMachine(azureClient, ui, &b.config), } } else if b.config.OSType == constants.Target_Windows { steps = []multistep.Step{ @@ -216,14 +213,20 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) }, &commonsteps.StepProvision{}, NewStepPowerOffCompute(azureClient, ui, &b.config), - NewStepCaptureImage(azureClient, ui, &b.config), - NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), - NewStepDeleteVirtualMachine(azureClient, ui, &b.config), } } else { return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) } + captureSteps := b.config.CaptureSteps( + ui.Say, + NewStepCaptureImage(azureClient, ui, &b.config), + NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), + ) + + steps = append(steps, captureSteps...) + steps = append(steps, NewStepDeleteVirtualMachine(azureClient, ui, &b.config)) + if b.config.PackerDebug { ui.Message(fmt.Sprintf("temp admin user: '%s'", b.config.UserName)) ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password)) diff --git a/builder/azure/dtl/config.go b/builder/azure/dtl/config.go index 26624988..da02dd7b 100644 --- a/builder/azure/dtl/config.go +++ b/builder/azure/dtl/config.go @@ -98,6 +98,8 @@ type ArtifactParameter struct { type Config struct { common.PackerConfig `mapstructure:",squash"` + azcommon.Config `mapstructure:",squash"` + // Authentication via OAUTH ClientConfig client.Config `mapstructure:",squash"` diff --git a/builder/azure/dtl/config.hcl2spec.go b/builder/azure/dtl/config.hcl2spec.go index befa7e57..25cd400d 100644 --- a/builder/azure/dtl/config.hcl2spec.go +++ b/builder/azure/dtl/config.hcl2spec.go @@ -45,6 +45,7 @@ type FlatConfig struct { PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"` CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"` MetadataHost *string `mapstructure:"metadata_host" required:"false" cty:"metadata_host" hcl:"metadata_host"` ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` @@ -162,6 +163,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false}, "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, "metadata_host": &hcldec.AttrSpec{Name: "metadata_host", Type: cty.String, Required: false}, "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, diff --git a/docs-partials/builder/azure/common/Config-not-required.mdx b/docs-partials/builder/azure/common/Config-not-required.mdx new file mode 100644 index 00000000..915597be --- /dev/null +++ b/docs-partials/builder/azure/common/Config-not-required.mdx @@ -0,0 +1,7 @@ + + +- `skip_create_image` (bool) - Skip creating the image. + Useful for setting to `true` during a build test stage. + Defaults to `false`. + + diff --git a/docs/builders/arm.mdx b/docs/builders/arm.mdx index 7d22c957..c3682886 100644 --- a/docs/builders/arm.mdx +++ b/docs/builders/arm.mdx @@ -150,6 +150,8 @@ Providing `temp_resource_group_name` or `location` in combination with @include 'builder/azure/common/client/Config-not-required.mdx' +@include 'builder/azure/common/Config-not-required.mdx' + ## Build Shared Information Variables This builder generates data that are shared with provisioner and post-processor via build function of [template engine](https://packer.io/docs/templates/legacy_json_templates/engine) for JSON and [contextual variables](https://packer.io/docs/templates/hcl_templates/contextual-variables) for HCL2. diff --git a/docs/builders/chroot.mdx b/docs/builders/chroot.mdx index 30b980e4..b08c3aaa 100644 --- a/docs/builders/chroot.mdx +++ b/docs/builders/chroot.mdx @@ -70,6 +70,8 @@ information. @include 'builder/azure/chroot/Config-not-required.mdx' +@include 'builder/azure/common/Config-not-required.mdx' + #### Output options: At least one of these options needs to be specified: diff --git a/docs/builders/dtl.mdx b/docs/builders/dtl.mdx index c0eda1bb..55b7f7e8 100644 --- a/docs/builders/dtl.mdx +++ b/docs/builders/dtl.mdx @@ -69,6 +69,8 @@ you should specify `subscription_id`, `client_id` and one of `client_secret`, @include 'builder/azure/dtl/Config-not-required.mdx' +@include 'builder/azure/common/Config-not-required.mdx' + #### DtlArtifact @include 'provisioner/azure-dtlartifact/DtlArtifact-not-required.mdx'