202 lines
4.9 KiB
Go
202 lines
4.9 KiB
Go
|
package common
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"log"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"path/filepath"
|
||
|
"runtime"
|
||
|
|
||
|
"github.com/hashicorp/packer/helper/builder/localexec"
|
||
|
"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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd := retrieveCommandForOS(s.Label, rootFolder, CDPath)
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func retrieveCommandForOS(label string, source string, dest string) *exec.Cmd {
|
||
|
switch runtime.GOOS {
|
||
|
case "windows":
|
||
|
cmd := exec.Command("oscdimg")
|
||
|
args := []string{"-j1", "-o", "-m", "-l" + label, source, dest}
|
||
|
cmd.Args = append(cmd.Args, args...)
|
||
|
return cmd
|
||
|
case "darwin":
|
||
|
cmd := exec.Command("hdiutil")
|
||
|
args := []string{"makehybrid", "-o", dest, "-hfs",
|
||
|
"-joliet", "-iso", "-default-volume-name", label, source}
|
||
|
cmd.Args = append(cmd.Args, args...)
|
||
|
return cmd
|
||
|
default:
|
||
|
cmd := exec.Command("mkisofs")
|
||
|
args := []string{"-joliet", "-volid", label, "-o", dest, source}
|
||
|
cmd.Args = append(cmd.Args, args...)
|
||
|
return cmd
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// Add a directory and its subdirectories
|
||
|
visit := func(pathname string, fi os.FileInfo, err error) error {
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// add a file
|
||
|
if !fi.IsDir() {
|
||
|
inputF, err := os.Open(pathname)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer inputF.Close()
|
||
|
|
||
|
fileDst, err := os.Create(filepath.Join(dst, pathname))
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("Error opening file %s on CD", src)
|
||
|
}
|
||
|
defer fileDst.Close()
|
||
|
nBytes, err := io.Copy(fileDst, inputF)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("Error copying %s to CD", src)
|
||
|
}
|
||
|
s.filesAdded[pathname] = true
|
||
|
log.Printf("Wrote %d bytes to %s", nBytes, pathname)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if fi.Mode().IsDir() {
|
||
|
// create the directory on the CD, continue walk.
|
||
|
err := os.Mkdir(filepath.Join(dst, pathname), fi.Mode())
|
||
|
if err != nil {
|
||
|
err = fmt.Errorf("error creating new directory %s: %s",
|
||
|
filepath.Join(dst, pathname), err)
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return filepath.Walk(src, visit)
|
||
|
}
|