packer-cn/post-processor/yandex-export/step-dump.go

95 lines
2.5 KiB
Go

package yandexexport
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"sync"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer/builder/yandex"
)
type StepDump struct {
ExtraSize bool
SizeLimit int64
}
const (
dumpCommand = "%sqemu-img convert -O qcow2 -o cluster_size=2M %s disk.qcow2 2>&1"
)
// Run reads the instance metadata and looks for the log entry
// indicating the cloud-init script finished.
func (s *StepDump) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
comm := state.Get("communicator").(packersdk.Communicator)
device := "/dev/disk/by-id/virtio-doexport"
cmdDumpCheckAccess := &packersdk.RemoteCmd{
Command: fmt.Sprintf("qemu-img info %s", device),
}
if err := comm.Start(ctx, cmdDumpCheckAccess); err != nil {
return yandex.StepHaltWithError(state, err)
}
sudo := ""
if cmdDumpCheckAccess.Wait() != 0 {
sudo = "sudo "
}
if s.ExtraSize && which(ctx, comm, "losetup") == nil {
ui.Say("Map loop device...")
buff := new(bytes.Buffer)
cmd := &packersdk.RemoteCmd{
Command: fmt.Sprintf("%slosetup --show -r --sizelimit %d -f %s", sudo, s.SizeLimit, device),
Stdout: buff,
}
if err := comm.Start(ctx, cmd); err != nil {
return yandex.StepHaltWithError(state, err)
}
if cmd.Wait() != 0 {
return yandex.StepHaltWithError(state, fmt.Errorf("Cannot losetup: %d", cmd.ExitStatus()))
}
device = strings.TrimSpace(buff.String())
if device == "" {
return yandex.StepHaltWithError(state, fmt.Errorf("Bad lo device"))
}
}
wg := new(sync.WaitGroup)
defer wg.Wait()
ctxWithCancel, cancel := context.WithCancel(ctx)
defer cancel()
wg.Add(1)
go func() {
defer wg.Done()
cmd := &packersdk.RemoteCmd{
Command: "while true ; do sleep 3; sudo kill -s SIGUSR1 $(pidof qemu-img); done",
}
err := cmd.RunWithUi(ctxWithCancel, comm, ui)
if err != nil && !errors.Is(err, context.Canceled) {
ui.Error("qemu-img signal sender error: " + err.Error())
return
}
}()
cmdDump := &packersdk.RemoteCmd{
Command: fmt.Sprintf(dumpCommand, sudo, device),
}
ui.Say("Dumping...")
if err := cmdDump.RunWithUi(ctx, comm, ui); err != nil {
return yandex.StepHaltWithError(state, err)
}
if cmdDump.ExitStatus() != 0 {
return yandex.StepHaltWithError(state, fmt.Errorf("Cannot dump disk, exit code: %d", cmdDump.ExitStatus()))
}
return multistep.ActionContinue
}
// Cleanup nothing
func (s *StepDump) Cleanup(state multistep.StateBag) {}