Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .web-docs/components/builder/chroot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ necessary for this build to succeed and can be found further down the page.
template where the .Device variable is replaced with the name of the
device where the volume is attached.

- `manual_mount_command` (string) - Manual Mount Command that is executed to manually mount the
root device, partition, and unmount. All other mount steps are skipped.
The device andmount path are provided by `{{.Device}}` and `{{.MountPath}}`.

- `post_mount_commands` ([]string) - As pre_mount_commands, but the commands are executed after mounting the
root device and before the extra mount and copy steps. The device and
mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
Expand Down
46 changes: 33 additions & 13 deletions builder/chroot/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ type Config struct {
// template where the .Device variable is replaced with the name of the
// device where the volume is attached.
MountPath string `mapstructure:"mount_path" required:"false"`
// Manual Mount Command that is executed to manually mount the
// root device, partition, and unmount. All other mount steps are skipped.
// The device andmount path are provided by `{{.Device}}` and `{{.MountPath}}`.
ManualMountCommand string `mapstructure:"manual_mount_command" required:"false"`
// As pre_mount_commands, but the commands are executed after mounting the
// root device and before the extra mount and copy steps. The device and
// mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
Expand Down Expand Up @@ -245,6 +249,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
"root_volume_tag",
"command_wrapper",
"post_mount_commands",
"manual_mount_command",
"pre_mount_commands",
"mount_path",
},
Expand Down Expand Up @@ -321,9 +326,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
errs = packersdk.MultiErrorAppend(
errs, errors.New("root_volume_size is required with from_scratch."))
}
if len(b.config.PreMountCommands) == 0 {
if b.config.ManualMountCommand == "" && len(b.config.PreMountCommands) == 0 {
errs = packersdk.MultiErrorAppend(
errs, errors.New("pre_mount_commands is required with from_scratch."))
errs, errors.New("pre_mount_commands or manual_mount_command is required with from_scratch."))
}
if b.config.AMIVirtType == "" {
errs = packersdk.MultiErrorAppend(
Expand Down Expand Up @@ -480,17 +485,32 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
PollingConfig: b.config.PollingConfig,
},
&StepEarlyUnflock{},
&chroot.StepPreMountCommands{
Commands: b.config.PreMountCommands,
},
&StepMountDevice{
MountOptions: b.config.MountOptions,
MountPartition: b.config.MountPartition,
GeneratedData: generatedData,
},
&chroot.StepPostMountCommands{
Commands: b.config.PostMountCommands,
},
)

if b.config.ManualMountCommand != "" {
steps = append(steps,
&StepManualMountCommand{
Command: b.config.ManualMountCommand,
GeneratedData: generatedData,
},
)
} else {
steps = append(steps,
&chroot.StepPreMountCommands{
Commands: b.config.PreMountCommands,
},
&StepMountDevice{
MountOptions: b.config.MountOptions,
MountPartition: b.config.MountPartition,
GeneratedData: generatedData,
},
&chroot.StepPostMountCommands{
Commands: b.config.PostMountCommands,
},
)
}

steps = append(steps,
&chroot.StepMountExtra{
ChrootMounts: b.config.ChrootMounts,
},
Expand Down
2 changes: 2 additions & 0 deletions builder/chroot/builder.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

117 changes: 117 additions & 0 deletions builder/chroot/step_manual_mount_command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package chroot

import (
"bytes"
"context"
"fmt"
"log"
"path/filepath"

"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)

type manualMountCommandData struct {
Device string
}

// StepManualMountCommand sets up the a new block device when building from scratch
type StepManualMountCommand struct {
Command string
mountPath string

GeneratedData *packerbuilderdata.GeneratedData
}

func (s *StepManualMountCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
device := state.Get("device").(string)
ui := state.Get("ui").(packersdk.Ui)

ui.Say("Running manual mount commands...")

if config.NVMEDevicePath != "" {
// customizable device path for mounting NVME block devices on c5 and m5 HVM
device = config.NVMEDevicePath
}
ui.Say(fmt.Sprintf("Command is: %s", s.Command))
if len(s.Command) == 0 {
return multistep.ActionContinue
}

ictx := config.GetContext()
ictx.Data = &manualMountCommandData{Device: filepath.Base(device)}
mountPath, err := interpolate.Render(config.MountPath, &ictx)

if err != nil {
err := fmt.Errorf("Error preparing mount directory: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

mountPath, err = filepath.Abs(mountPath)
if err != nil {
err := fmt.Errorf("Error preparing mount directory: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

ui.Say(fmt.Sprintf("Mount Path After ABS is: %s", mountPath))

log.Printf("Mount path: %s", mountPath)
stderr := new(bytes.Buffer)

wrappedCommand := state.Get("wrappedCommand").(common.CommandWrapper)

ui.Say("Running manual mount commands...")
mountCommand, err := wrappedCommand(s.Command)
if err != nil {
err := fmt.Errorf("Error creating mount command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

cmd := common.ShellCommand(mountCommand)
cmd.Stderr = stderr
if err := cmd.Run(); err != nil {
err := fmt.Errorf(
"Error mounting root volume: %s\nStderr: %s", err, stderr.String())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

// Set the mount path so we remember to unmount it later
s.mountPath = mountPath
state.Put("mount_path", s.mountPath)
s.GeneratedData.Put("MountPath", s.mountPath)
state.Put("mount_device_cleanup", s)

return multistep.ActionContinue
}

func (s *StepManualMountCommand) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packersdk.Ui)
if err := s.CleanupFunc(state); err != nil {
ui.Error(err.Error())
}
}

func (s *StepManualMountCommand) CleanupFunc(state multistep.StateBag) error {
if s.mountPath == "" {
return nil
}

ui := state.Get("ui").(packersdk.Ui)

ui.Say("Skipping UnMount Root Mount, it must be manually unmounted...")

s.mountPath = ""
return nil
}
4 changes: 4 additions & 0 deletions docs-partials/builder/chroot/Config-not-required.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
template where the .Device variable is replaced with the name of the
device where the volume is attached.

- `manual_mount_command` (string) - Manual Mount Command that is executed to manually mount the
root device, partition, and unmount. All other mount steps are skipped.
The device andmount path are provided by `{{.Device}}` and `{{.MountPath}}`.

- `post_mount_commands` ([]string) - As pre_mount_commands, but the commands are executed after mounting the
root device and before the extra mount and copy steps. The device and
mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
Expand Down
Loading