2020-08-19 17:09:08 -04:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2020-09-17 02:25:00 -04:00
|
|
|
"runtime"
|
2020-09-11 14:11:19 -04:00
|
|
|
"strings"
|
2020-08-19 17:09:08 -04:00
|
|
|
|
2020-11-04 14:56:46 -05:00
|
|
|
"github.com/hashicorp/packer/common/shell-local/localexec"
|
2020-08-19 17:09:08 -04:00
|
|
|
"github.com/hashicorp/packer/helper/multistep"
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
"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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-11 14:11:19 -04:00
|
|
|
cmd, err := retrieveCDISOCreationCommand(s.Label, rootFolder, CDPath)
|
|
|
|
if err != nil {
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2020-08-19 17:09:08 -04:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-11 14:11:19 -04:00
|
|
|
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,
|
2020-09-14 15:42:11 -04:00
|
|
|
"makehybrid",
|
2020-09-11 14:11:19 -04:00
|
|
|
"-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)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-09-17 02:25:00 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-09-11 14:11:19 -04:00
|
|
|
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
|
|
|
|
}
|
2020-09-17 02:25:00 -04:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
2020-09-11 14:11:19 -04:00
|
|
|
return c.Command(path, label, source, dest), nil
|
|
|
|
}
|
|
|
|
var commands = make([]string, 0, len(supportedCDISOCreationCommands))
|
|
|
|
for _, c := range supportedCDISOCreationCommands {
|
|
|
|
commands = append(commands, c.Name)
|
2020-08-19 17:09:08 -04:00
|
|
|
}
|
2020-09-11 14:11:19 -04:00
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"could not find a supported CD ISO creation command (the supported commands are: %s)",
|
|
|
|
strings.Join(commands, ", "))
|
2020-08-19 17:09:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-09-30 18:07:26 -04:00
|
|
|
// file is a directory, so we need to parse the filename into a path to
|
2020-10-02 12:17:29 -04:00
|
|
|
// discard and a basename
|
2020-09-30 18:07:26 -04:00
|
|
|
discardPath, _ := filepath.Split(src)
|
|
|
|
|
2020-08-19 17:09:08 -04:00
|
|
|
// Add a directory and its subdirectories
|
|
|
|
visit := func(pathname string, fi os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-30 18:07:26 -04:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2020-08-19 17:09:08 -04:00
|
|
|
// add a file
|
|
|
|
if !fi.IsDir() {
|
|
|
|
inputF, err := os.Open(pathname)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer inputF.Close()
|
|
|
|
|
2020-09-30 18:07:26 -04:00
|
|
|
fileDst, err := os.Create(dstPath)
|
2020-08-19 17:09:08 -04:00
|
|
|
if err != nil {
|
2020-09-30 18:07:26 -04:00
|
|
|
return fmt.Errorf("Error opening file %s on CD: %s", dstPath, err)
|
2020-08-19 17:09:08 -04:00
|
|
|
}
|
|
|
|
defer fileDst.Close()
|
|
|
|
nBytes, err := io.Copy(fileDst, inputF)
|
|
|
|
if err != nil {
|
2020-09-30 18:07:26 -04:00
|
|
|
return fmt.Errorf("Error copying %s to CD: %s", dstPath, err)
|
2020-08-19 17:09:08 -04:00
|
|
|
}
|
2020-09-30 18:07:26 -04:00
|
|
|
s.filesAdded[dstPath] = true
|
|
|
|
log.Printf("Wrote %d bytes to %s", nBytes, dstPath)
|
2020-08-19 17:09:08 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if fi.Mode().IsDir() {
|
|
|
|
// create the directory on the CD, continue walk.
|
2020-09-30 18:07:26 -04:00
|
|
|
err := os.MkdirAll(dstPath, fi.Mode())
|
2020-08-19 17:09:08 -04:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("error creating new directory %s: %s",
|
2020-09-30 18:07:26 -04:00
|
|
|
dstPath, err)
|
2020-08-19 17:09:08 -04:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return filepath.Walk(src, visit)
|
|
|
|
}
|