packer-cn/packer-plugin-sdk/commonsteps/step_create_cdrom.go

295 lines
7.1 KiB
Go

package commonsteps
import (
"context"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer-plugin-sdk/shell-local/localexec"
"github.com/hashicorp/packer/packer/tmp"
)
// StepCreateCD will create a CD disk with the given files.
type StepCreateCD struct {
// Files can be either files or directories. Any files provided here will
// be written to the root of the CD. Directories will be written to the
// root of the CD as well, but will retain their subdirectory structure.
Files []string
Label string
CDPath string
filesAdded map[string]bool
}
func (s *StepCreateCD) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if len(s.Files) == 0 {
log.Println("No CD files specified. CD disk will not be made.")
return multistep.ActionContinue
}
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating CD disk...")
if s.Label == "" {
s.Label = "packer"
} else {
log.Printf("CD label is set to %s", s.Label)
}
// Track what files are added. Used for testing step.
s.filesAdded = make(map[string]bool)
// Create a temporary file to be our CD drive
CDF, err := tmp.File("packer*.iso")
// Set the path so we can remove it later
CDPath := CDF.Name()
CDF.Close()
os.Remove(CDPath)
if err != nil {
state.Put("error",
fmt.Errorf("Error creating temporary file for CD: %s", err))
return multistep.ActionHalt
}
log.Printf("CD path: %s", CDPath)
s.CDPath = CDPath
// Consolidate all files provided into a single directory to become our
// "root" directory.
rootFolder, err := tmp.Dir("packer_to_cdrom")
if err != nil {
state.Put("error",
fmt.Errorf("Error creating temporary file for CD: %s", err))
return multistep.ActionHalt
}
for _, toAdd := range s.Files {
err = s.AddFile(rootFolder, toAdd)
if err != nil {
state.Put("error",
fmt.Errorf("Error creating temporary file for CD: %s", err))
return multistep.ActionHalt
}
}
cmd, err := retrieveCDISOCreationCommand(s.Label, rootFolder, CDPath)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
err = localexec.RunAndStream(cmd, ui, []string{})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
ui.Message("Done copying paths from CD_dirs")
// Set the path to the CD so it can be used later
state.Put("cd_path", CDPath)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepCreateCD) Cleanup(multistep.StateBag) {
if s.CDPath != "" {
log.Printf("Deleting CD disk: %s", s.CDPath)
os.Remove(s.CDPath)
}
}
type cdISOCreationCommand struct {
Name string
Command func(path string, label string, source string, dest string) *exec.Cmd
}
var supportedCDISOCreationCommands []cdISOCreationCommand = []cdISOCreationCommand{
{
"xorriso", func(path string, label string, source string, dest string) *exec.Cmd {
return exec.Command(
path,
"-as", "genisoimage",
"-rock",
"-joliet",
"-volid", label,
"-output", dest,
source)
},
},
{
"mkisofs", func(path string, label string, source string, dest string) *exec.Cmd {
return exec.Command(
path,
"-joliet",
"-volid", label,
"-o", dest,
source)
},
},
{
"hdiutil", func(path string, label string, source string, dest string) *exec.Cmd {
return exec.Command(
path,
"makehybrid",
"-o", dest,
"-hfs",
"-joliet",
"-iso",
"-default-volume-name", label,
source)
},
},
{
"oscdimg", func(path string, label string, source string, dest string) *exec.Cmd {
return exec.Command(
path,
"-j1",
"-o",
"-m",
"-l"+label,
source,
dest)
},
},
}
func isCygwinExecutable(path string) bool {
return runtime.GOOS == "windows" && strings.Contains(path, "\\usr\\bin\\")
}
func toCygwinPath(path string) (string, error) {
c := exec.Command("cygpath", path)
cygwinPath, err := c.Output()
return strings.TrimSpace(string(cygwinPath)), err
}
func retrieveCDISOCreationCommand(label string, source string, dest string) (*exec.Cmd, error) {
for _, c := range supportedCDISOCreationCommands {
path, err := exec.LookPath(c.Name)
if err != nil {
continue
}
// if we are running a cygwin/msys2 executable we must convert the
// native win32 path to a cygwin/msys2/unix style path.
if isCygwinExecutable(path) {
source, err = toCygwinPath(source)
if err != nil {
return nil, err
}
dest, err = toCygwinPath(dest)
if err != nil {
return nil, err
}
}
return c.Command(path, label, source, dest), nil
}
var commands = make([]string, 0, len(supportedCDISOCreationCommands))
for _, c := range supportedCDISOCreationCommands {
commands = append(commands, c.Name)
}
return nil, fmt.Errorf(
"could not find a supported CD ISO creation command (the supported commands are: %s)",
strings.Join(commands, ", "))
}
func (s *StepCreateCD) AddFile(dst, src string) error {
finfo, err := os.Stat(src)
if err != nil {
return fmt.Errorf("Error adding path to CD: %s", err)
}
// add a file
if !finfo.IsDir() {
inputF, err := os.Open(src)
if err != nil {
return err
}
defer inputF.Close()
// Create a new file in the root directory
dest, err := os.Create(filepath.Join(dst, finfo.Name()))
if err != nil {
return fmt.Errorf("Error opening file for copy %s to CD root", src)
}
defer dest.Close()
nBytes, err := io.Copy(dest, inputF)
if err != nil {
return fmt.Errorf("Error copying %s to CD root", src)
}
s.filesAdded[src] = true
log.Printf("Wrote %d bytes to %s", nBytes, finfo.Name())
return err
}
// file is a directory, so we need to parse the filename into a path to
// discard and a basename
discardPath, _ := filepath.Split(src)
// Add a directory and its subdirectories
visit := func(pathname string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
// Clean up pathing so that we preserve the base directory provided by
// the user but not the local pathing to that directory.
allDirs, base := filepath.Split(pathname)
intermediaryDirs := strings.Replace(allDirs, discardPath, "", 1)
dstPath := filepath.Join(dst, base)
if intermediaryDirs != "" {
dstPath = filepath.Join(dst, intermediaryDirs, base)
}
// add a file
if !fi.IsDir() {
inputF, err := os.Open(pathname)
if err != nil {
return err
}
defer inputF.Close()
fileDst, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("Error opening file %s on CD: %s", dstPath, err)
}
defer fileDst.Close()
nBytes, err := io.Copy(fileDst, inputF)
if err != nil {
return fmt.Errorf("Error copying %s to CD: %s", dstPath, err)
}
s.filesAdded[dstPath] = true
log.Printf("Wrote %d bytes to %s", nBytes, dstPath)
return err
}
if fi.Mode().IsDir() {
// create the directory on the CD, continue walk.
err := os.MkdirAll(dstPath, fi.Mode())
if err != nil {
err = fmt.Errorf("error creating new directory %s: %s",
dstPath, err)
}
return err
}
return err
}
return filepath.Walk(src, visit)
}