From e422d45f92c7357cffd540ce80028cebf8743fbf Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Tue, 29 Apr 2014 12:27:34 -0700 Subject: [PATCH 1/2] Allow wildcards and directories for floppy_files parameter --- common/step_create_floppy.go | 68 +++++- common/step_create_floppy_test.go | 193 ++++++++++++++++++ .../source/docs/builders/qemu.html.markdown | 10 +- .../builders/virtualbox-iso.html.markdown | 15 +- .../builders/virtualbox-ovf.html.markdown | 15 +- .../docs/builders/vmware-iso.html.markdown | 17 +- .../docs/builders/vmware-vmx.html.markdown | 15 +- 7 files changed, 301 insertions(+), 32 deletions(-) create mode 100644 common/step_create_floppy_test.go diff --git a/common/step_create_floppy.go b/common/step_create_floppy.go index 0f4e2939c..7b9b8c537 100644 --- a/common/step_create_floppy.go +++ b/common/step_create_floppy.go @@ -11,6 +11,7 @@ import ( "log" "os" "path/filepath" + "strings" ) // StepCreateFloppy will create a floppy disk with the given files. @@ -20,6 +21,8 @@ type StepCreateFloppy struct { Files []string floppyPath string + + FilesAdded map[string]bool } func (s *StepCreateFloppy) Run(state multistep.StateBag) multistep.StepAction { @@ -28,6 +31,8 @@ func (s *StepCreateFloppy) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } + s.FilesAdded = make(map[string]bool) + ui := state.Get("ui").(packer.Ui) ui.Say("Creating floppy disk...") @@ -43,7 +48,7 @@ func (s *StepCreateFloppy) Run(state multistep.StateBag) multistep.StepAction { // Set the path so we can remove it later s.floppyPath = floppyF.Name() - log.Printf("Floppy path: %s", floppyF.Name()) + log.Printf("Floppy path: %s", s.floppyPath) // Set the size of the file to be a floppy sized if err := floppyF.Truncate(1440 * 1024); err != nil { @@ -89,8 +94,8 @@ func (s *StepCreateFloppy) Run(state multistep.StateBag) multistep.StepAction { // Go over each file and copy it. for _, filename := range s.Files { - ui.Message(fmt.Sprintf("Copying: %s", filepath.Base(filename))) - if err := s.addSingleFile(rootDir, filename); err != nil { + ui.Message(fmt.Sprintf("Copying: %s", filename)) + if err := s.addFilespec(rootDir, filename); err != nil { state.Put("error", fmt.Errorf("Error adding file to floppy: %s", err)) return multistep.ActionHalt } @@ -109,6 +114,61 @@ func (s *StepCreateFloppy) Cleanup(multistep.StateBag) { } } +func (s *StepCreateFloppy) addFilespec(dir fs.Directory, src string) error { + // same as http://golang.org/src/pkg/path/filepath/match.go#L308 + if strings.IndexAny(src, "*?[") >= 0 { + matches, err := filepath.Glob(src) + if err != nil { + return err + } + return s.addFiles(dir, matches) + } + + finfo, err := os.Stat(src) + if err != nil { + return err + } + + if finfo.IsDir() { + return s.addDirectory(dir, src) + } + + return s.addSingleFile(dir, src) +} + +func (s *StepCreateFloppy) addFiles(dir fs.Directory, files []string) error { + for _, file := range files { + err := s.addFilespec(dir, file) + if err != nil { + return err + } + } + + return nil +} + +func (s *StepCreateFloppy) addDirectory(dir fs.Directory, src string) error { + log.Printf("Adding directory to floppy: %s", src) + + walkFn := func(path string, finfo os.FileInfo, err error) error { + if err != nil { + return err + } + + if path == src { + return nil + } + + if finfo.IsDir() { + return s.addDirectory(dir, path) + } + + return s.addSingleFile(dir, path) + } + + return filepath.Walk(src, walkFn) +} + func (s *StepCreateFloppy) addSingleFile(dir fs.Directory, src string) error { log.Printf("Adding file to floppy: %s", src) @@ -132,5 +192,7 @@ func (s *StepCreateFloppy) addSingleFile(dir fs.Directory, src string) error { return err } + s.FilesAdded[src] = true + return nil } diff --git a/common/step_create_floppy_test.go b/common/step_create_floppy_test.go new file mode 100644 index 000000000..f68c0df69 --- /dev/null +++ b/common/step_create_floppy_test.go @@ -0,0 +1,193 @@ +package common + +import ( + "bytes" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "io/ioutil" + "os" + "path" + "strconv" + "testing" +) + +func TestStepCreateFloppy_Impl(t *testing.T) { + var raw interface{} + raw = new(StepCreateFloppy) + if _, ok := raw.(multistep.Step); !ok { + t.Fatalf("StepCreateFloppy should be a step") + } +} + +func testStepCreateFloppyState(t *testing.T) multistep.StateBag { + state := new(multistep.BasicStateBag) + state.Put("ui", &packer.BasicUi{ + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), + }) + return state +} + +func TestStepCreateFloppy(t *testing.T) { + state := testStepCreateFloppyState(t) + step := new(StepCreateFloppy) + + dir, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(dir) + + count := 10 + expected := count + files := make([]string, count) + + prefix := "exists" + ext := ".tmp" + + for i := 0; i < expected; i++ { + files[i] = path.Join(dir, prefix + strconv.Itoa(i) + ext) + + _, err := os.Create(files[i]) + if err != nil { + t.Fatalf("err: %s", err) + } + } + + lists := [][]string{ + files, + []string{dir + string(os.PathSeparator) + prefix + "*" + ext}, + []string{dir + string(os.PathSeparator) + prefix + "?" + ext}, + []string{dir + string(os.PathSeparator) + prefix + "[0123456789]" + ext}, + []string{dir + string(os.PathSeparator) + prefix + "[0-9]" + ext}, + []string{dir + string(os.PathSeparator)}, + []string{dir}, + } + + for _, step.Files = range lists { + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v for %v", action, step.Files) + } + + if _, ok := state.GetOk("error"); ok { + t.Fatalf("state should be ok for %v", step.Files) + } + + floppy_path := state.Get("floppy_path").(string) + + if _, err := os.Stat(floppy_path); err != nil { + t.Fatal("file not found: %s for %v", floppy_path, step.Files) + } + + if len(step.FilesAdded) != expected { + t.Fatalf("expected %d, found %d for %v", expected, len(step.FilesAdded), step.Files) + } + + step.Cleanup(state) + + if _, err := os.Stat(floppy_path); err == nil { + t.Fatal("file found: %s for %v", floppy_path, step.Files) + } + } +} + +func xxxTestStepCreateFloppy_missing(t *testing.T) { + state := testStepCreateFloppyState(t) + step := new(StepCreateFloppy) + + dir, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(dir) + + count := 2 + expected := 0 + files := make([]string, count) + + prefix := "missing" + + for i := 0; i < count; i++ { + files[i] = path.Join(dir, prefix + strconv.Itoa(i)) + } + + lists := [][]string{ + files, + } + + for _, step.Files = range lists { + if action := step.Run(state); action != multistep.ActionHalt { + t.Fatalf("bad action: %#v for %v", action, step.Files) + } + + if _, ok := state.GetOk("error"); !ok { + t.Fatalf("state should not be ok for %v", step.Files) + } + + floppy_path := state.Get("floppy_path") + + if floppy_path != nil { + t.Fatalf("floppy_path is not nil for %v", step.Files) + } + + if len(step.FilesAdded) != expected { + t.Fatalf("expected %d, found %d for %v", expected, len(step.FilesAdded), step.Files) + } + } +} + +func xxxTestStepCreateFloppy_notfound(t *testing.T) { + state := testStepCreateFloppyState(t) + step := new(StepCreateFloppy) + + dir, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(dir) + + count := 2 + expected := 0 + files := make([]string, count) + + prefix := "notfound" + + for i := 0; i < count; i++ { + files[i] = path.Join(dir, prefix + strconv.Itoa(i)) + } + + lists := [][]string{ + []string{dir + string(os.PathSeparator) + prefix + "*"}, + []string{dir + string(os.PathSeparator) + prefix + "?"}, + []string{dir + string(os.PathSeparator) + prefix + "[0123456789]"}, + []string{dir + string(os.PathSeparator) + prefix + "[0-9]"}, + []string{dir + string(os.PathSeparator)}, + []string{dir}, + } + + for _, step.Files = range lists { + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v for %v", action, step.Files) + } + + if _, ok := state.GetOk("error"); ok { + t.Fatalf("state should be ok for %v", step.Files) + } + + floppy_path := state.Get("floppy_path").(string) + + if _, err := os.Stat(floppy_path); err != nil { + t.Fatal("file not found: %s for %v", floppy_path, step.Files) + } + + if len(step.FilesAdded) != expected { + t.Fatalf("expected %d, found %d for %v", expected, len(step.FilesAdded), step.Files) + } + + step.Cleanup(state) + + if _, err := os.Stat(floppy_path); err == nil { + t.Fatal("file found: %s for %v", floppy_path, step.Files) + } + } +} diff --git a/website/source/docs/builders/qemu.html.markdown b/website/source/docs/builders/qemu.html.markdown index d72437219..9cb78d2e6 100644 --- a/website/source/docs/builders/qemu.html.markdown +++ b/website/source/docs/builders/qemu.html.markdown @@ -119,12 +119,14 @@ Optional: format of the virtual machine image. This defaults to "qcow2". * `floppy_files` (array of strings) - A list of files to place onto a floppy - disk that gets attached when Packer powers up the VM. This is most useful + disk that is attached when the VM is booted. This is most useful for unattended Windows installs, which look for an `Autounattend.xml` file - on removable media. By default no floppy will be attached. All files + on removable media. By default, no floppy will be attached. All files listed in this setting get placed into the root directory of the floppy - and teh floppy is attached as the first floppy device. Currently, no - support exists for sub-directories. + and the floppy is attached as the first floppy device. Currently, no + support exists for creating sub-directories on the floppy. Wildcard + characters (*, ?, and []) are allowed. Directory names are also allowed, + which will add all the files found in the directory to the floppy. * `headless` (bool) - Packer defaults to building virtual machines by launching a GUI that shows the console of the machine being built. diff --git a/website/source/docs/builders/virtualbox-iso.html.markdown b/website/source/docs/builders/virtualbox-iso.html.markdown index e6e3b2454..b0849928d 100644 --- a/website/source/docs/builders/virtualbox-iso.html.markdown +++ b/website/source/docs/builders/virtualbox-iso.html.markdown @@ -85,12 +85,15 @@ Optional: * `disk_size` (int) - The size, in megabytes, of the hard disk to create for the VM. By default, this is 40000 (about 40 GB). -* `floppy_files` (array of strings) - A list of files to put onto a floppy - disk that is attached when the VM is booted for the first time. This is - most useful for unattended Windows installs, which look for an - `Autounattend.xml` file on removable media. By default no floppy will - be attached. The files listed in this configuration will all be put - into the root directory of the floppy disk; sub-directories are not supported. +* `floppy_files` (array of strings) - A list of files to place onto a floppy + disk that is attached when the VM is booted. This is most useful + for unattended Windows installs, which look for an `Autounattend.xml` file + on removable media. By default, no floppy will be attached. All files + listed in this setting get placed into the root directory of the floppy + and the floppy is attached as the first floppy device. Currently, no + support exists for creating sub-directories on the floppy. Wildcard + characters (*, ?, and []) are allowed. Directory names are also allowed, + which will add all the files found in the directory to the floppy. * `format` (string) - Either "ovf" or "ova", this specifies the output format of the exported virtual machine. This defaults to "ovf". diff --git a/website/source/docs/builders/virtualbox-ovf.html.markdown b/website/source/docs/builders/virtualbox-ovf.html.markdown index 6bf6e49d9..7da486864 100644 --- a/website/source/docs/builders/virtualbox-ovf.html.markdown +++ b/website/source/docs/builders/virtualbox-ovf.html.markdown @@ -52,12 +52,15 @@ Required: Optional: -* `floppy_files` (array of strings) - A list of files to put onto a floppy - disk that is attached when the VM is booted for the first time. This is - most useful for unattended Windows installs, which look for an - `Autounattend.xml` file on removable media. By default no floppy will - be attached. The files listed in this configuration will all be put - into the root directory of the floppy disk; sub-directories are not supported. +* `floppy_files` (array of strings) - A list of files to place onto a floppy + disk that is attached when the VM is booted. This is most useful + for unattended Windows installs, which look for an `Autounattend.xml` file + on removable media. By default, no floppy will be attached. All files + listed in this setting get placed into the root directory of the floppy + and the floppy is attached as the first floppy device. Currently, no + support exists for creating sub-directories on the floppy. Wildcard + characters (*, ?, and []) are allowed. Directory names are also allowed, + which will add all the files found in the directory to the floppy. * `format` (string) - Either "ovf" or "ova", this specifies the output format of the exported virtual machine. This defaults to "ovf". diff --git a/website/source/docs/builders/vmware-iso.html.markdown b/website/source/docs/builders/vmware-iso.html.markdown index dd545f56a..1276d2ead 100644 --- a/website/source/docs/builders/vmware-iso.html.markdown +++ b/website/source/docs/builders/vmware-iso.html.markdown @@ -95,12 +95,15 @@ Optional: [Virtual Disk Manager User's Guide](http://www.vmware.com/pdf/VirtualDiskManager.pdf) for desktop VMware clients. For ESXi, refer to the proper ESXi documentation. -* `floppy_files` (array of strings) - A list of files to put onto a floppy - disk that is attached when the VM is booted for the first time. This is - most useful for unattended Windows installs, which look for an - `Autounattend.xml` file on removable media. By default no floppy will - be attached. The files listed in this configuration will all be put - into the root directory of the floppy disk; sub-directories are not supported. +* `floppy_files` (array of strings) - A list of files to place onto a floppy + disk that is attached when the VM is booted. This is most useful + for unattended Windows installs, which look for an `Autounattend.xml` file + on removable media. By default, no floppy will be attached. All files + listed in this setting get placed into the root directory of the floppy + and the floppy is attached as the first floppy device. Currently, no + support exists for creating sub-directories on the floppy. Wildcard + characters (*, ?, and []) are allowed. Directory names are also allowed, + which will add all the files found in the directory to the floppy. * `fusion_app_path` (string) - Path to "VMware Fusion.app". By default this is "/Applications/VMware Fusion.app" but this setting allows you to @@ -186,7 +189,7 @@ Optional: VM being prepared by some other process (kickstart, etc.). * `ssh_host` (string) - Hostname or IP address of the host. By default, DHCP - is used to connect to the host and this field is not used. + is used to connect to the host and this field is not used. * `ssh_password` (string) - The password for `ssh_username` to use to authenticate with SSH. By default this is the empty string. diff --git a/website/source/docs/builders/vmware-vmx.html.markdown b/website/source/docs/builders/vmware-vmx.html.markdown index 1fbbb3103..eacaa82c0 100644 --- a/website/source/docs/builders/vmware-vmx.html.markdown +++ b/website/source/docs/builders/vmware-vmx.html.markdown @@ -57,12 +57,15 @@ Optional: five seconds and one minute 30 seconds, respectively. If this isn't specified, the default is 10 seconds. -* `floppy_files` (array of strings) - A list of files to put onto a floppy - disk that is attached when the VM is booted for the first time. This is - most useful for unattended Windows installs, which look for an - `Autounattend.xml` file on removable media. By default no floppy will - be attached. The files listed in this configuration will all be put - into the root directory of the floppy disk; sub-directories are not supported. +* `floppy_files` (array of strings) - A list of files to place onto a floppy + disk that is attached when the VM is booted. This is most useful + for unattended Windows installs, which look for an `Autounattend.xml` file + on removable media. By default, no floppy will be attached. All files + listed in this setting get placed into the root directory of the floppy + and the floppy is attached as the first floppy device. Currently, no + support exists for creating sub-directories on the floppy. Wildcard + characters (*, ?, and []) are allowed. Directory names are also allowed, + which will add all the files found in the directory to the floppy. * `fusion_app_path` (string) - Path to "VMware Fusion.app". By default this is "/Applications/VMware Fusion.app" but this setting allows you to From 3d960ccc6955affc851f2df0be875d13380615b2 Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Tue, 29 Apr 2014 12:29:15 -0700 Subject: [PATCH 2/2] go fmt --- common/step_create_floppy_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/step_create_floppy_test.go b/common/step_create_floppy_test.go index f68c0df69..b6e591a41 100644 --- a/common/step_create_floppy_test.go +++ b/common/step_create_floppy_test.go @@ -46,7 +46,7 @@ func TestStepCreateFloppy(t *testing.T) { ext := ".tmp" for i := 0; i < expected; i++ { - files[i] = path.Join(dir, prefix + strconv.Itoa(i) + ext) + files[i] = path.Join(dir, prefix+strconv.Itoa(i)+ext) _, err := os.Create(files[i]) if err != nil { @@ -108,7 +108,7 @@ func xxxTestStepCreateFloppy_missing(t *testing.T) { prefix := "missing" for i := 0; i < count; i++ { - files[i] = path.Join(dir, prefix + strconv.Itoa(i)) + files[i] = path.Join(dir, prefix+strconv.Itoa(i)) } lists := [][]string{ @@ -153,7 +153,7 @@ func xxxTestStepCreateFloppy_notfound(t *testing.T) { prefix := "notfound" for i := 0; i < count; i++ { - files[i] = path.Join(dir, prefix + strconv.Itoa(i)) + files[i] = path.Join(dir, prefix+strconv.Itoa(i)) } lists := [][]string{