From 1befba24edfaf0635fd4c37c1423b4b0a5c9cdc3 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 17 Sep 2013 05:58:16 -0700 Subject: [PATCH 01/21] Allow user to override chroot command. --- builder/amazon/chroot/builder.go | 6 ++++++ builder/amazon/chroot/communicator.go | 7 ++----- builder/amazon/chroot/step_chroot_provision.go | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index c940a7bfb..6647b769e 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -30,6 +30,7 @@ type Config struct { CopyFiles []string `mapstructure:"copy_files"` DevicePath string `mapstructure:"device_path"` MountCommand string `mapstructure:"mount_command"` + ChrootCommand string `mapstructure:"chroot_command"` MountPath string `mapstructure:"mount_path"` SourceAmi string `mapstructure:"source_ami"` UnmountCommand string `mapstructure:"unmount_command"` @@ -82,6 +83,10 @@ func (b *Builder) Prepare(raws ...interface{}) error { b.config.MountCommand = "mount" } + if b.config.ChrootCommand == "" { + b.config.ChrootCommand = "chroot" + } + if b.config.MountPath == "" { b.config.MountPath = "packer-amazon-chroot-volumes/{{.Device}}" } @@ -129,6 +134,7 @@ func (b *Builder) Prepare(raws ...interface{}) error { templates := map[string]*string{ "device_path": &b.config.DevicePath, "mount_command": &b.config.MountCommand, + "chroot_command": &b.config.ChrootCommand, "source_ami": &b.config.SourceAmi, "unmount_command": &b.config.UnmountCommand, } diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 111adb42e..3e1e8deda 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -14,15 +14,12 @@ import ( // commands locally but within a chroot. type Communicator struct { Chroot string + ChrootCommand string } func (c *Communicator) Start(cmd *packer.RemoteCmd) error { - chrootCmdPath, err := exec.LookPath("chroot") - if err != nil { - return err - } - localCmd := exec.Command(chrootCmdPath, c.Chroot, "/bin/sh", "-c", cmd.Command) + localCmd := exec.Command(c.ChrootCommand, c.Chroot, "/bin/sh", "-c", cmd.Command) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index 5ad87836d..b1a156216 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -14,11 +14,13 @@ type StepChrootProvision struct { func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { hook := state.Get("hook").(packer.Hook) mountPath := state.Get("mount_path").(string) + chrootCommand := state.Get("chroot_command").(string) ui := state.Get("ui").(packer.Ui) // Create our communicator comm := &Communicator{ Chroot: mountPath, + ChrootCommand: chrootCommand, } // Provision From 4f6fc963971785d7a36d4f1ecb46a237ae2b7988 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 17 Sep 2013 06:01:23 -0700 Subject: [PATCH 02/21] some documentation. --- website/source/docs/builders/amazon-chroot.html.markdown | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/source/docs/builders/amazon-chroot.html.markdown b/website/source/docs/builders/amazon-chroot.html.markdown index 593ac97fe..5d0f23b6e 100644 --- a/website/source/docs/builders/amazon-chroot.html.markdown +++ b/website/source/docs/builders/amazon-chroot.html.markdown @@ -126,6 +126,10 @@ Optional: * `unmount_command` (string) - Just like `mount_command`, except this is the command to unmount devices. +* `chroot_command` (string) - The command to use to create the chroot. + This defaults to "chroot", but like `mount_command`, it may be useful + to use `sudo` or variables. + ## Basic Example Here is a basic example. It is completely valid except for the access keys: From 5086ff1a4eb30073147067d8aca12f76180d8c1b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 25 Sep 2013 03:20:22 -0700 Subject: [PATCH 03/21] pass chroot command to /bin/sh this allows us to specify an arbitrary chroot command, i.e. for sudo --- builder/amazon/chroot/communicator.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 3e1e8deda..d434da8f1 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -13,13 +13,14 @@ import ( // Communicator is a special communicator that works by executing // commands locally but within a chroot. type Communicator struct { - Chroot string - ChrootCommand string + Chroot string + ChrootCommand string } func (c *Communicator) Start(cmd *packer.RemoteCmd) error { - localCmd := exec.Command(c.ChrootCommand, c.Chroot, "/bin/sh", "-c", cmd.Command) + chrootCommand := fmt.Sprintf("%s %s %s", c.ChrootCommand, c.Chroot, cmd.Command) + localcmd := exec.Command("/bin/sh", "-c", chrootCommand) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr From dd356d33d8e912eedaa724c6ff2b443bc1bcace9 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 00:31:07 -0700 Subject: [PATCH 04/21] notes/reorg. --- builder/amazon/chroot/communicator.go | 32 ++++++++++++------------ builder/amazon/chroot/step_copy_files.go | 6 +++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index d434da8f1..b81300563 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -52,22 +52,6 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { return nil } -func (c *Communicator) Upload(dst string, r io.Reader) error { - dst = filepath.Join(c.Chroot, dst) - log.Printf("Uploading to chroot dir: %s", dst) - f, err := os.Create(dst) - if err != nil { - return err - } - defer f.Close() - - if _, err := io.Copy(f, r); err != nil { - return err - } - - return nil -} - func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { walkFn := func(fullPath string, info os.FileInfo, err error) error { if err != nil { @@ -100,6 +84,22 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error return filepath.Walk(src, walkFn) } +func (c *Communicator) Upload(dst string, r io.Reader) error { + dst = filepath.Join(c.Chroot, dst) + log.Printf("Uploading to chroot dir: %s", dst) + f, err := os.Create(dst) + if err != nil { + return err + } + defer f.Close() + + if _, err := io.Copy(f, r); err != nil { + return err + } + + return nil +} + func (c *Communicator) Download(src string, w io.Writer) error { src = filepath.Join(c.Chroot, src) log.Printf("Downloading from chroot dir: %s", src) diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index d729ee234..d383103a4 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -68,6 +68,12 @@ func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error { return nil } +/* TODO: move to util file. + * change prototype to + func copySingle(dst string, src string, copyCommand string) error + * I think we should switch to cp for copying files, then allow specifying a copy_files_command or something. +Maybe we should just do a execute_wrapper that allows you to wrap every command... +*/ func (s *StepCopyFiles) copySingle(dst, src string) error { // Stat the src file so we can copy the mode later srcInfo, err := os.Stat(src) From 7fa238503b6499e4bc1b4f43f109e2eb0ed710b1 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 00:58:25 -0700 Subject: [PATCH 05/21] wip --- builder/amazon/chroot/builder.go | 6 ++++++ builder/amazon/chroot/communicator.go | 12 +++++------ builder/amazon/chroot/copy_files.go | 21 +++++++++++++++++++ .../amazon/chroot/step_chroot_provision.go | 6 ++++-- builder/amazon/chroot/step_copy_files.go | 3 ++- 5 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 builder/amazon/chroot/copy_files.go diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 6647b769e..93685de72 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -31,6 +31,7 @@ type Config struct { DevicePath string `mapstructure:"device_path"` MountCommand string `mapstructure:"mount_command"` ChrootCommand string `mapstructure:"chroot_command"` + CopyCommand string `mapstructure:"copy_command"` MountPath string `mapstructure:"mount_path"` SourceAmi string `mapstructure:"source_ami"` UnmountCommand string `mapstructure:"unmount_command"` @@ -87,6 +88,10 @@ func (b *Builder) Prepare(raws ...interface{}) error { b.config.ChrootCommand = "chroot" } + if b.config.CopyCommand == "" { + b.config.CopyCommand = "cp" + } + if b.config.MountPath == "" { b.config.MountPath = "packer-amazon-chroot-volumes/{{.Device}}" } @@ -135,6 +140,7 @@ func (b *Builder) Prepare(raws ...interface{}) error { "device_path": &b.config.DevicePath, "mount_command": &b.config.MountCommand, "chroot_command": &b.config.ChrootCommand, + "copy_command": &b.config.CopyCommand, "source_ami": &b.config.SourceAmi, "unmount_command": &b.config.UnmountCommand, } diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index b81300563..2af07833c 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -15,6 +15,7 @@ import ( type Communicator struct { Chroot string ChrootCommand string + CopyCommand string } func (c *Communicator) Start(cmd *packer.RemoteCmd) error { @@ -71,13 +72,10 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error } dstPath := filepath.Join(dst, path) - f, err := os.Open(fullPath) - if err != nil { - return err - } - defer f.Close() - - return c.Upload(dstPath, f) + dst = filepath.Join(c.Chroot, dst) + log.Printf("Uploading to chroot dir: %s", dst) + return copySingle(dst, "", c.CopyCommand) + //return c.Upload(dstPath, f) } log.Printf("Uploading directory '%s' to '%s'", src, dst) diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go new file mode 100644 index 000000000..8d75cb65d --- /dev/null +++ b/builder/amazon/chroot/copy_files.go @@ -0,0 +1,21 @@ +package chroot + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "syscall" +) + +func copySingle(dst string, src string, copyCommand string) error { + cpCommand := fmt.Sprintf("sudo cp -fn %s %s", src, dest) + localcmd := exec.Command("/bin/sh", "-c", cpCommand) + log.Println(localcmd.Args) + out, err := localcmd.CombinedOutput() + if err != nil { + log.Println(err) + } + log.Printf("output: %s", out) + return nil +} diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index b1a156216..bb53e0603 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -15,12 +15,14 @@ func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction hook := state.Get("hook").(packer.Hook) mountPath := state.Get("mount_path").(string) chrootCommand := state.Get("chroot_command").(string) + copyCommand := state.Get("copy_command").(string) ui := state.Get("ui").(packer.Ui) // Create our communicator comm := &Communicator{ - Chroot: mountPath, - ChrootCommand: chrootCommand, + Chroot: mountPath, + ChrootCommand: chrootCommand, + CopyCommand: copyCommand, } // Provision diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index d383103a4..efe411354 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -22,6 +22,7 @@ type StepCopyFiles struct { func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) mountPath := state.Get("mount_path").(string) + copyCmd := state.Get("copy_command").(string) ui := state.Get("ui").(packer.Ui) s.files = make([]string, 0, len(config.CopyFiles)) @@ -32,7 +33,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { chrootPath := filepath.Join(mountPath, path) log.Printf("Copying '%s' to '%s'", path, chrootPath) - if err := s.copySingle(chrootPath, path); err != nil { + if err := copySingle(chrootPath, path, copyCmd); err != nil { err := fmt.Errorf("Error copying file: %s", err) state.Put("error", err) ui.Error(err.Error()) From ce3725efec2019d412a427e313ddc6e2d0276f3e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 01:07:01 -0700 Subject: [PATCH 06/21] wip --- builder/amazon/chroot/communicator.go | 5 ++- builder/amazon/chroot/copy_files.go | 3 -- builder/amazon/chroot/step_copy_files.go | 45 ------------------------ 3 files changed, 2 insertions(+), 51 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 2af07833c..ba71d8605 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -72,10 +72,9 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error } dstPath := filepath.Join(dst, path) - dst = filepath.Join(c.Chroot, dst) + dst := filepath.Join(c.Chroot, dst) log.Printf("Uploading to chroot dir: %s", dst) - return copySingle(dst, "", c.CopyCommand) - //return c.Upload(dstPath, f) + return copySingle(dst, fullPath, c.CopyCommand) } log.Printf("Uploading directory '%s' to '%s'", src, dst) diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index 8d75cb65d..0c4ef2dbc 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -2,10 +2,7 @@ package chroot import ( "log" - "os" "os/exec" - "path/filepath" - "syscall" ) func copySingle(dst string, src string, copyCommand string) error { diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index efe411354..5e779efa8 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "io" "log" "os" "path/filepath" @@ -68,47 +67,3 @@ func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error { s.files = nil return nil } - -/* TODO: move to util file. - * change prototype to - func copySingle(dst string, src string, copyCommand string) error - * I think we should switch to cp for copying files, then allow specifying a copy_files_command or something. -Maybe we should just do a execute_wrapper that allows you to wrap every command... -*/ -func (s *StepCopyFiles) copySingle(dst, src string) error { - // Stat the src file so we can copy the mode later - srcInfo, err := os.Stat(src) - if err != nil { - return err - } - - // Remove any existing destination file - if err := os.Remove(dst); err != nil { - return err - } - - // Copy the files - srcF, err := os.Open(src) - if err != nil { - return err - } - defer srcF.Close() - - dstF, err := os.Create(dst) - if err != nil { - return err - } - defer dstF.Close() - - if _, err := io.Copy(dstF, srcF); err != nil { - return err - } - dstF.Close() - - // Match the mode - if err := os.Chmod(dst, srcInfo.Mode()); err != nil { - return err - } - - return nil -} From 5e9ff92ff3e0115603dbb6956937f1b26eb68f16 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 01:16:51 -0700 Subject: [PATCH 07/21] Revert "notes/reorg." This reverts commit 73c5aec24d0a504ecfbae038b4b7effb54adb929. Conflicts: builder/amazon/chroot/step_copy_files.go --- builder/amazon/chroot/communicator.go | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index ba71d8605..741de2d83 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -53,6 +53,22 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { return nil } +func (c *Communicator) Upload(dst string, r io.Reader) error { + dst = filepath.Join(c.Chroot, dst) + log.Printf("Uploading to chroot dir: %s", dst) + f, err := os.Create(dst) + if err != nil { + return err + } + defer f.Close() + + if _, err := io.Copy(f, r); err != nil { + return err + } + + return nil +} + func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { walkFn := func(fullPath string, info os.FileInfo, err error) error { if err != nil { @@ -81,22 +97,6 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error return filepath.Walk(src, walkFn) } -func (c *Communicator) Upload(dst string, r io.Reader) error { - dst = filepath.Join(c.Chroot, dst) - log.Printf("Uploading to chroot dir: %s", dst) - f, err := os.Create(dst) - if err != nil { - return err - } - defer f.Close() - - if _, err := io.Copy(f, r); err != nil { - return err - } - - return nil -} - func (c *Communicator) Download(src string, w io.Writer) error { src = filepath.Join(c.Chroot, src) log.Printf("Downloading from chroot dir: %s", src) From 7740c8fed527860e72ab4ea70bb2533cebc6df46 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 01:32:53 -0700 Subject: [PATCH 08/21] using blocking cp method. --- builder/amazon/chroot/communicator.go | 3 ++- builder/amazon/chroot/copy_files.go | 13 +++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 741de2d83..9d4e4ce09 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -1,6 +1,7 @@ package chroot import ( + "fmt" "github.com/mitchellh/packer/packer" "io" "log" @@ -21,7 +22,7 @@ type Communicator struct { func (c *Communicator) Start(cmd *packer.RemoteCmd) error { chrootCommand := fmt.Sprintf("%s %s %s", c.ChrootCommand, c.Chroot, cmd.Command) - localcmd := exec.Command("/bin/sh", "-c", chrootCommand) + localCmd := exec.Command("/bin/sh", "-c", chrootCommand) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index 0c4ef2dbc..ef5cd70b5 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -1,18 +1,15 @@ package chroot import ( - "log" + "fmt" "os/exec" ) -func copySingle(dst string, src string, copyCommand string) error { +func copySingle(dest string, src string, copyCommand string) error { cpCommand := fmt.Sprintf("sudo cp -fn %s %s", src, dest) - localcmd := exec.Command("/bin/sh", "-c", cpCommand) - log.Println(localcmd.Args) - out, err := localcmd.CombinedOutput() - if err != nil { - log.Println(err) + localCmd := exec.Command("/bin/sh", "-c", cpCommand) + if err := localCmd.Run(); err != nil { + return err } - log.Printf("output: %s", out) return nil } From 23a331fc009dbed7d38133a94cb3941db2852a65 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 01:35:29 -0700 Subject: [PATCH 09/21] bugfix. --- builder/amazon/chroot/communicator.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 9d4e4ce09..2b2e1cada 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -88,10 +88,9 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error } } - dstPath := filepath.Join(dst, path) - dst := filepath.Join(c.Chroot, dst) + chrootDest := filepath.Join(c.Chroot, dst, path) log.Printf("Uploading to chroot dir: %s", dst) - return copySingle(dst, fullPath, c.CopyCommand) + return copySingle(chrootDest, fullPath, c.CopyCommand) } log.Printf("Uploading directory '%s' to '%s'", src, dst) From e1dadfc57a8e326f54ed138bf9f5f1d5275096fc Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 02:25:57 -0700 Subject: [PATCH 10/21] Unit tests. --- builder/amazon/chroot/copy_files.go | 2 +- builder/amazon/chroot/copy_files_test.go | 42 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 builder/amazon/chroot/copy_files_test.go diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index ef5cd70b5..81be8a9c8 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -6,7 +6,7 @@ import ( ) func copySingle(dest string, src string, copyCommand string) error { - cpCommand := fmt.Sprintf("sudo cp -fn %s %s", src, dest) + cpCommand := fmt.Sprintf("%s %s %s", copyCommand, src, dest) localCmd := exec.Command("/bin/sh", "-c", cpCommand) if err := localCmd.Run(); err != nil { return err diff --git a/builder/amazon/chroot/copy_files_test.go b/builder/amazon/chroot/copy_files_test.go new file mode 100644 index 000000000..9a0bb5a4a --- /dev/null +++ b/builder/amazon/chroot/copy_files_test.go @@ -0,0 +1,42 @@ +package chroot + +import ( + "io/ioutil" + "log" + "os" + "testing" +) + +func TestCopyFile(t *testing.T) { + first, err := ioutil.TempFile("", "copy_files_test") + if err != nil { + t.Fatalf("couldn't create temp file.") + } + defer os.Remove(first.Name()) + newName := first.Name() + "-new" + + payload := "copy_files_test.go payload" + if _, err = first.WriteString(payload); err != nil { + t.Fatalf("Couldn't write payload to first file.") + } + + if err := copySingle(newName, first.Name(), "cp"); err != nil { + t.Fatalf("Couldn't copy file") + } + defer os.Remove(newName) + + second, err := os.Open(newName) + if err != nil { + t.Fatalf("Couldn't open copied file.") + } + defer second.Close() + + var copiedPayload = make([]byte, len(payload)) + if _, err := second.Read(copiedPayload); err != nil { + t.Fatalf("Couldn't open copied file for reading.") + } + + if string(copiedPayload) != payload { + t.Fatalf("payload not copied.") + } +} From ee60ed319b08f2dc21964df9fec8ce4983a588a3 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 02:31:10 -0700 Subject: [PATCH 11/21] We're not using this log package. --- builder/amazon/chroot/copy_files_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/builder/amazon/chroot/copy_files_test.go b/builder/amazon/chroot/copy_files_test.go index 9a0bb5a4a..a9da8d742 100644 --- a/builder/amazon/chroot/copy_files_test.go +++ b/builder/amazon/chroot/copy_files_test.go @@ -2,7 +2,6 @@ package chroot import ( "io/ioutil" - "log" "os" "testing" ) From 1104ad3e17ed90875af3279643c62273ca5a855e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 16:39:37 -0700 Subject: [PATCH 12/21] get chroot command from proper place. --- builder/amazon/chroot/step_copy_files.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index 5e779efa8..4b8298928 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -21,7 +21,6 @@ type StepCopyFiles struct { func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) mountPath := state.Get("mount_path").(string) - copyCmd := state.Get("copy_command").(string) ui := state.Get("ui").(packer.Ui) s.files = make([]string, 0, len(config.CopyFiles)) @@ -32,7 +31,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { chrootPath := filepath.Join(mountPath, path) log.Printf("Copying '%s' to '%s'", path, chrootPath) - if err := copySingle(chrootPath, path, copyCmd); err != nil { + if err := copySingle(chrootPath, path, config.ChrootCommand); err != nil { err := fmt.Errorf("Error copying file: %s", err) state.Put("error", err) ui.Error(err.Error()) From c15bb284913b76d393951c48c2a6d65240b8a66e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 27 Sep 2013 01:28:06 +0000 Subject: [PATCH 13/21] bugfixes, wip --- builder/amazon/chroot/copy_files.go | 2 ++ builder/amazon/chroot/step_chroot_provision.go | 7 +++---- builder/amazon/chroot/step_copy_files.go | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index 81be8a9c8..f44c7da96 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -3,11 +3,13 @@ package chroot import ( "fmt" "os/exec" + "log" ) func copySingle(dest string, src string, copyCommand string) error { cpCommand := fmt.Sprintf("%s %s %s", copyCommand, src, dest) localCmd := exec.Command("/bin/sh", "-c", cpCommand) + log.Println(localCmd.Args) if err := localCmd.Run(); err != nil { return err } diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index bb53e0603..6b76d9541 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -14,15 +14,14 @@ type StepChrootProvision struct { func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { hook := state.Get("hook").(packer.Hook) mountPath := state.Get("mount_path").(string) - chrootCommand := state.Get("chroot_command").(string) - copyCommand := state.Get("copy_command").(string) + config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) // Create our communicator comm := &Communicator{ Chroot: mountPath, - ChrootCommand: chrootCommand, - CopyCommand: copyCommand, + ChrootCommand: config.ChrootCommand, + CopyCommand: config.CopyCommand, } // Provision diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index 4b8298928..102967423 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -5,7 +5,6 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "log" - "os" "path/filepath" ) @@ -31,7 +30,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { chrootPath := filepath.Join(mountPath, path) log.Printf("Copying '%s' to '%s'", path, chrootPath) - if err := copySingle(chrootPath, path, config.ChrootCommand); err != nil { + if err := copySingle(chrootPath, path, config.CopyCommand); err != nil { err := fmt.Errorf("Error copying file: %s", err) state.Put("error", err) ui.Error(err.Error()) @@ -57,7 +56,7 @@ func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error { if s.files != nil { for _, file := range s.files { log.Printf("Removing: %s", file) - if err := os.Remove(file); err != nil { + if err := copySingle(file, "", "rm"); err != nil { return err } } From de83755c00000e5a848ec05ebc74ed220e3e1efd Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 26 Sep 2013 18:34:01 -0700 Subject: [PATCH 14/21] wip --- builder/amazon/chroot/step_copy_files.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index 102967423..7ca6ef2a0 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -5,6 +5,7 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "log" + "os/exec" "path/filepath" ) @@ -56,7 +57,9 @@ func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error { if s.files != nil { for _, file := range s.files { log.Printf("Removing: %s", file) - if err := copySingle(file, "", "rm"); err != nil { + chrootCommand := fmt.Sprintf("rm %s", file) + localCmd := exec.Command("/bin/sh", "-c", chrootCommand) + if err := localCmd.Run(); err != nil { return err } } From cebbcc51a64e2b201a54a232187c54cd9558ba9d Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 27 Sep 2013 02:11:28 +0000 Subject: [PATCH 15/21] fix chroot Upload command. --- builder/amazon/chroot/communicator.go | 15 ++++++--------- builder/amazon/chroot/copy_files.go | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 2b2e1cada..879d5e5a5 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/mitchellh/packer/packer" "io" + "io/ioutil" "log" "os" "os/exec" @@ -57,17 +58,13 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Upload(dst string, r io.Reader) error { dst = filepath.Join(c.Chroot, dst) log.Printf("Uploading to chroot dir: %s", dst) - f, err := os.Create(dst) + tf, err := ioutil.TempFile("", "packer-amazon-chroot") if err != nil { - return err + return fmt.Errorf("Error preparing shell script: %s", err) } - defer f.Close() - - if _, err := io.Copy(f, r); err != nil { - return err - } - - return nil + defer os.Remove(tf.Name()) + io.Copy(tf, r) + return copySingle(dst, tf.Name(), c.CopyCommand) } func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index f44c7da96..16d0a82f6 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -9,7 +9,7 @@ import ( func copySingle(dest string, src string, copyCommand string) error { cpCommand := fmt.Sprintf("%s %s %s", copyCommand, src, dest) localCmd := exec.Command("/bin/sh", "-c", cpCommand) - log.Println(localCmd.Args) + log.Printf("Executing copy: %s %#v", localCmd.Path, localCmd.Args) if err := localCmd.Run(); err != nil { return err } From ac496a63dcc75b4e65988506931cecfdbeec6ed4 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 27 Sep 2013 10:54:53 +0000 Subject: [PATCH 16/21] replace command config with wrapper command. --- builder/amazon/chroot/builder.go | 29 ++++--------------- builder/amazon/chroot/communicator.go | 20 ++++++++----- builder/amazon/chroot/copy_files.go | 19 ++++++------ .../amazon/chroot/step_chroot_provision.go | 26 +++++++++++++++-- builder/amazon/chroot/step_copy_files.go | 12 ++++---- builder/amazon/chroot/step_mount_device.go | 12 ++++---- builder/amazon/chroot/step_mount_extra.go | 13 ++++----- .../docs/builders/amazon-chroot.html.markdown | 15 ++++------ 8 files changed, 74 insertions(+), 72 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 93685de72..165e29078 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -29,12 +29,9 @@ type Config struct { ChrootMounts [][]string `mapstructure:"chroot_mounts"` CopyFiles []string `mapstructure:"copy_files"` DevicePath string `mapstructure:"device_path"` - MountCommand string `mapstructure:"mount_command"` - ChrootCommand string `mapstructure:"chroot_command"` - CopyCommand string `mapstructure:"copy_command"` + CommandWrapper string `mapstructure:"command_wrapper"` MountPath string `mapstructure:"mount_path"` SourceAmi string `mapstructure:"source_ami"` - UnmountCommand string `mapstructure:"unmount_command"` tpl *packer.ConfigTemplate } @@ -80,26 +77,14 @@ func (b *Builder) Prepare(raws ...interface{}) error { b.config.CopyFiles = []string{"/etc/resolv.conf"} } - if b.config.MountCommand == "" { - b.config.MountCommand = "mount" - } - - if b.config.ChrootCommand == "" { - b.config.ChrootCommand = "chroot" - } - - if b.config.CopyCommand == "" { - b.config.CopyCommand = "cp" + if b.config.CommandWrapper == "" { + b.config.CommandWrapper = "{{.Command}}" } if b.config.MountPath == "" { b.config.MountPath = "packer-amazon-chroot-volumes/{{.Device}}" } - if b.config.UnmountCommand == "" { - b.config.UnmountCommand = "umount" - } - // Accumulate any errors errs := common.CheckUnusedConfig(md) errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...) @@ -137,12 +122,8 @@ func (b *Builder) Prepare(raws ...interface{}) error { } templates := map[string]*string{ - "device_path": &b.config.DevicePath, - "mount_command": &b.config.MountCommand, - "chroot_command": &b.config.ChrootCommand, - "copy_command": &b.config.CopyCommand, - "source_ami": &b.config.SourceAmi, - "unmount_command": &b.config.UnmountCommand, + "device_path": &b.config.DevicePath, + "source_ami": &b.config.SourceAmi, } for n, ptr := range templates { diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 879d5e5a5..2902e5708 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -1,5 +1,7 @@ package chroot +// pf := func () { somefunc("a str", 1) } + import ( "fmt" "github.com/mitchellh/packer/packer" @@ -12,18 +14,18 @@ import ( "syscall" ) +type Command func(string) *exec.Cmd + // Communicator is a special communicator that works by executing // commands locally but within a chroot. type Communicator struct { - Chroot string - ChrootCommand string - CopyCommand string + Chroot string + ChrootCmd Command + wrappedCommand Command } func (c *Communicator) Start(cmd *packer.RemoteCmd) error { - - chrootCommand := fmt.Sprintf("%s %s %s", c.ChrootCommand, c.Chroot, cmd.Command) - localCmd := exec.Command("/bin/sh", "-c", chrootCommand) + localCmd := c.ChrootCmd(cmd.Command) localCmd.Stdin = cmd.Stdin localCmd.Stdout = cmd.Stdout localCmd.Stderr = cmd.Stderr @@ -64,7 +66,8 @@ func (c *Communicator) Upload(dst string, r io.Reader) error { } defer os.Remove(tf.Name()) io.Copy(tf, r) - return copySingle(dst, tf.Name(), c.CopyCommand) + cpCmd := fmt.Sprintf("cp %s %s", dst, tf.Name()) + return (*c.ChrootCmd(cpCmd)).Run() } func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { @@ -87,7 +90,8 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error chrootDest := filepath.Join(c.Chroot, dst, path) log.Printf("Uploading to chroot dir: %s", dst) - return copySingle(chrootDest, fullPath, c.CopyCommand) + cpCmd := fmt.Sprintf("cp %s %s", chrootDest, fullPath) + return c.ChrootCmd(cpCmd).Run() } log.Printf("Uploading directory '%s' to '%s'", src, dst) diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index 16d0a82f6..055a7d403 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -2,16 +2,17 @@ package chroot import ( "fmt" - "os/exec" "log" + "os/exec" ) -func copySingle(dest string, src string, copyCommand string) error { - cpCommand := fmt.Sprintf("%s %s %s", copyCommand, src, dest) - localCmd := exec.Command("/bin/sh", "-c", cpCommand) - log.Printf("Executing copy: %s %#v", localCmd.Path, localCmd.Args) - if err := localCmd.Run(); err != nil { - return err - } - return nil +func ChrootCommand(chroot string, command string) *exec.Cmd { + chrootCommand := fmt.Sprintf("chroot %s %s", chroot, command) + return ShellCommand(chrootCommand) +} + +func ShellCommand(command string) *exec.Cmd { + cmd := exec.Command("/bin/sh", "-c", command) + log.Printf("WrappedCommand(%s) -> #%v", command, cmd.Args) + return cmd } diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index 6b76d9541..7375ecad2 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -4,6 +4,7 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "log" + "os/exec" ) // StepChrootProvision provisions the instance within a chroot. @@ -11,17 +12,36 @@ type StepChrootProvision struct { mounts []string } +type WrappedCommandTemplate struct { + command string +} + func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { hook := state.Get("hook").(packer.Hook) mountPath := state.Get("mount_path").(string) config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) + chrootCmd := func(command string) *exec.Cmd { + return ChrootCommand(mountPath, command) + } + wrappedCommand := func(command string) *exec.Cmd { + wrapped, err := config.tpl.Process(config.CommandWrapper, &WrappedCommandTemplate{ + command: command, + }) + if err != nil { + ui.Error(err.Error()) + } + return ShellCommand(wrapped) + } + + state.Put("chrootCmd", chrootCmd) + state.Put("wrappedCommand", wrappedCommand) // Create our communicator comm := &Communicator{ - Chroot: mountPath, - ChrootCommand: config.ChrootCommand, - CopyCommand: config.CopyCommand, + Chroot: mountPath, + ChrootCmd: chrootCmd, + wrappedCommand: wrappedCommand, } // Provision diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index 7ca6ef2a0..80a7b135e 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -5,7 +5,6 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "log" - "os/exec" "path/filepath" ) @@ -22,6 +21,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) mountPath := state.Get("mount_path").(string) ui := state.Get("ui").(packer.Ui) + wrappedCommand := state.Get("wrappedCommand").(Command) s.files = make([]string, 0, len(config.CopyFiles)) if len(config.CopyFiles) > 0 { @@ -31,7 +31,8 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { chrootPath := filepath.Join(mountPath, path) log.Printf("Copying '%s' to '%s'", path, chrootPath) - if err := copySingle(chrootPath, path, config.CopyCommand); err != nil { + cmd := fmt.Sprintf("cp %s %s", chrootPath, path) + if err := wrappedCommand(cmd); err != nil { err := fmt.Errorf("Error copying file: %s", err) state.Put("error", err) ui.Error(err.Error()) @@ -53,12 +54,13 @@ func (s *StepCopyFiles) Cleanup(state multistep.StateBag) { } } -func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error { +func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error { + wrappedCommand := state.Get("wrappedCommand").(Command) if s.files != nil { for _, file := range s.files { log.Printf("Removing: %s", file) - chrootCommand := fmt.Sprintf("rm %s", file) - localCmd := exec.Command("/bin/sh", "-c", chrootCommand) + localCmd := wrappedCommand(fmt.Sprintf("rm -f %s", file)) + log.Println(localCmd.Args) if err := localCmd.Run(); err != nil { return err } diff --git a/builder/amazon/chroot/step_mount_device.go b/builder/amazon/chroot/step_mount_device.go index 6079a0293..f9a823623 100644 --- a/builder/amazon/chroot/step_mount_device.go +++ b/builder/amazon/chroot/step_mount_device.go @@ -7,7 +7,6 @@ import ( "github.com/mitchellh/packer/packer" "log" "os" - "os/exec" "path/filepath" ) @@ -59,8 +58,9 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { ui.Say("Mounting the root device...") stderr := new(bytes.Buffer) - mountCommand := fmt.Sprintf("%s %s %s", config.MountCommand, device, mountPath) - cmd := exec.Command("/bin/sh", "-c", mountCommand) + mountCommand := fmt.Sprintf("mount %s %s", device, mountPath) + wrappedCommand := state.Get("wrappedCommand").(Command) + cmd := wrappedCommand(mountCommand) cmd.Stderr = stderr if err := cmd.Run(); err != nil { err := fmt.Errorf( @@ -90,12 +90,12 @@ func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error { return nil } - config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) ui.Say("Unmounting the root device...") - unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, s.mountPath) - cmd := exec.Command("/bin/sh", "-c", unmountCommand) + unmountCommand := fmt.Sprintf("umount %s", s.mountPath) + wrappedCommand := state.Get("wrappedCommand").(Command) + cmd := wrappedCommand(unmountCommand) if err := cmd.Run(); err != nil { return fmt.Errorf("Error unmounting root device: %s", err) } diff --git a/builder/amazon/chroot/step_mount_extra.go b/builder/amazon/chroot/step_mount_extra.go index 3fc35f4ac..bbe958ec8 100644 --- a/builder/amazon/chroot/step_mount_extra.go +++ b/builder/amazon/chroot/step_mount_extra.go @@ -6,7 +6,6 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "os" - "os/exec" ) // StepMountExtra mounts the attached device. @@ -21,6 +20,7 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) mountPath := state.Get("mount_path").(string) ui := state.Get("ui").(packer.Ui) + wrappedCommand := state.Get("wrappedCommand").(Command) s.mounts = make([]string, 0, len(config.ChrootMounts)) @@ -43,12 +43,11 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction { ui.Message(fmt.Sprintf("Mounting: %s", mountInfo[2])) stderr := new(bytes.Buffer) mountCommand := fmt.Sprintf( - "%s %s %s %s", - config.MountCommand, + "mount %s %s %s", flags, mountInfo[1], innerPath) - cmd := exec.Command("/bin/sh", "-c", mountCommand) + cmd := wrappedCommand(mountCommand) cmd.Stderr = stderr if err := cmd.Run(); err != nil { err := fmt.Errorf( @@ -79,15 +78,15 @@ func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error { return nil } - config := state.Get("config").(*Config) + wrappedCommand := state.Get("wrappedCommand").(Command) for len(s.mounts) > 0 { var path string lastIndex := len(s.mounts) - 1 path, s.mounts = s.mounts[lastIndex], s.mounts[:lastIndex] - unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, path) + unmountCommand := fmt.Sprintf("unmount %s", path) stderr := new(bytes.Buffer) - cmd := exec.Command("/bin/sh", "-c", unmountCommand) + cmd := wrappedCommand(unmountCommand) cmd.Stderr = stderr if err := cmd.Run(); err != nil { return fmt.Errorf( diff --git a/website/source/docs/builders/amazon-chroot.html.markdown b/website/source/docs/builders/amazon-chroot.html.markdown index 8b593c3e5..2ea9e875d 100644 --- a/website/source/docs/builders/amazon-chroot.html.markdown +++ b/website/source/docs/builders/amazon-chroot.html.markdown @@ -111,9 +111,11 @@ Optional: of the source AMI will be attached. This defaults to "" (empty string), which forces Packer to find an open device automatically. -* `mount_command` (string) - The command to use to mount devices. This - defaults to "mount". This may be useful to set if you want to set - environmental variables or perhaps run it with `sudo` or so on. +* `command_wrapper` (string) - How to run shell commands. This + defaults to "{{.Command}}". This may be useful to set if you want to set + environmental variables or perhaps run it with `sudo` or so on. This is a + configuration template where the `.Command` variable is replaced with the + command to be run.. * `mount_path` (string) - The path where the volume will be mounted. This is where the chroot environment will be. This defaults to @@ -123,13 +125,6 @@ Optional: * `tags` (object of key/value strings) - Tags applied to the AMI. -* `unmount_command` (string) - Just like `mount_command`, except this is - the command to unmount devices. - -* `chroot_command` (string) - The command to use to create the chroot. - This defaults to "chroot", but like `mount_command`, it may be useful - to use `sudo` or variables. - ## Basic Example Here is a basic example. It is completely valid except for the access keys: From d2f9ba0d118886b2e7ae728ace434f392c4211bb Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 27 Sep 2013 11:55:19 +0000 Subject: [PATCH 17/21] fixing up types. --- builder/amazon/chroot/builder_test.go | 11 +++++++++++ builder/amazon/chroot/copy_files_test.go | 4 +++- builder/amazon/chroot/step_chroot_provision.go | 4 ++-- builder/amazon/chroot/step_copy_files.go | 1 - builder/amazon/chroot/step_mount_device.go | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/builder/amazon/chroot/builder_test.go b/builder/amazon/chroot/builder_test.go index 2f13bae54..6bdf7dc28 100644 --- a/builder/amazon/chroot/builder_test.go +++ b/builder/amazon/chroot/builder_test.go @@ -82,3 +82,14 @@ func TestBuilderPrepare_SourceAmi(t *testing.T) { t.Errorf("err: %s", err) } } + +func TestBuilderPrepare_CommandWrapper(t *testing.T) { + b := &Builder{} + config := testConfig() + + config["command_wrapper"] = "echo hi; {{.Command}}" + err := b.Prepare(config) + if err != nil { + t.Errorf("err: %s", err) + } +} diff --git a/builder/amazon/chroot/copy_files_test.go b/builder/amazon/chroot/copy_files_test.go index a9da8d742..2dccbf0bc 100644 --- a/builder/amazon/chroot/copy_files_test.go +++ b/builder/amazon/chroot/copy_files_test.go @@ -18,8 +18,10 @@ func TestCopyFile(t *testing.T) { if _, err = first.WriteString(payload); err != nil { t.Fatalf("Couldn't write payload to first file.") } + first.Sync() - if err := copySingle(newName, first.Name(), "cp"); err != nil { + cmd := ShellCommand(fmt.Sprintf("cp %s %s", first.Name(), newName)) + if err := cmd.Run(); err != nil { t.Fatalf("Couldn't copy file") } defer os.Remove(newName) diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index 7375ecad2..59a95f45c 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -13,7 +13,7 @@ type StepChrootProvision struct { } type WrappedCommandTemplate struct { - command string + Command string } func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { @@ -26,7 +26,7 @@ func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction } wrappedCommand := func(command string) *exec.Cmd { wrapped, err := config.tpl.Process(config.CommandWrapper, &WrappedCommandTemplate{ - command: command, + Command: command, }) if err != nil { ui.Error(err.Error()) diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index 80a7b135e..e22301369 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -60,7 +60,6 @@ func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error { for _, file := range s.files { log.Printf("Removing: %s", file) localCmd := wrappedCommand(fmt.Sprintf("rm -f %s", file)) - log.Println(localCmd.Args) if err := localCmd.Run(); err != nil { return err } diff --git a/builder/amazon/chroot/step_mount_device.go b/builder/amazon/chroot/step_mount_device.go index f9a823623..4cce60f10 100644 --- a/builder/amazon/chroot/step_mount_device.go +++ b/builder/amazon/chroot/step_mount_device.go @@ -59,7 +59,7 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { ui.Say("Mounting the root device...") stderr := new(bytes.Buffer) mountCommand := fmt.Sprintf("mount %s %s", device, mountPath) - wrappedCommand := state.Get("wrappedCommand").(Command) + wrappedCommand := state.Get("wrappedCommand").(*Command) cmd := wrappedCommand(mountCommand) cmd.Stderr = stderr if err := cmd.Run(); err != nil { From 39c3051a95c44a4ec6205040ec4995b0388e21fd Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 27 Sep 2013 20:47:44 +0000 Subject: [PATCH 18/21] building but there's an exec error. --- builder/amazon/chroot/builder.go | 12 +++++++++++ builder/amazon/chroot/communicator.go | 2 +- builder/amazon/chroot/copy_files.go | 2 +- builder/amazon/chroot/copy_files_test.go | 1 + .../amazon/chroot/step_chroot_provision.go | 21 +++---------------- builder/amazon/chroot/step_mount_device.go | 8 ++++++- builder/amazon/chroot/step_mount_extra.go | 2 +- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 165e29078..a7f46a5f4 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -13,6 +13,7 @@ import ( "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "log" + "os/exec" "runtime" ) @@ -160,12 +161,23 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ec2conn := ec2.New(auth, region) + wrappedCommand := func(command string) *exec.Cmd { + wrapped, err := b.config.tpl.Process(b.config.CommandWrapper, &WrappedCommandTemplate{ + Command: command, + }) + if err != nil { + ui.Error(err.Error()) + } + return ShellCommand(wrapped) + } + // Setup the state bag and initial state for the steps state := new(multistep.BasicStateBag) state.Put("config", &b.config) state.Put("ec2", ec2conn) state.Put("hook", hook) state.Put("ui", ui) + state.Put("wrappedCommand", Command(wrappedCommand)) // Build the steps steps := []multistep.Step{ diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 2902e5708..7f52d9fdb 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -21,7 +21,7 @@ type Command func(string) *exec.Cmd type Communicator struct { Chroot string ChrootCmd Command - wrappedCommand Command + WrappedCommand Command } func (c *Communicator) Start(cmd *packer.RemoteCmd) error { diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index 055a7d403..5af58baa5 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -13,6 +13,6 @@ func ChrootCommand(chroot string, command string) *exec.Cmd { func ShellCommand(command string) *exec.Cmd { cmd := exec.Command("/bin/sh", "-c", command) - log.Printf("WrappedCommand(%s) -> #%v", command, cmd.Args) + log.Printf("ShellCommand(%s) -> #%v", command, cmd.Args) return cmd } diff --git a/builder/amazon/chroot/copy_files_test.go b/builder/amazon/chroot/copy_files_test.go index 2dccbf0bc..c7e00018e 100644 --- a/builder/amazon/chroot/copy_files_test.go +++ b/builder/amazon/chroot/copy_files_test.go @@ -1,6 +1,7 @@ package chroot import ( + "fmt" "io/ioutil" "os" "testing" diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index 59a95f45c..481f58671 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -4,7 +4,6 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "log" - "os/exec" ) // StepChrootProvision provisions the instance within a chroot. @@ -19,29 +18,15 @@ type WrappedCommandTemplate struct { func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { hook := state.Get("hook").(packer.Hook) mountPath := state.Get("mount_path").(string) - config := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) - chrootCmd := func(command string) *exec.Cmd { - return ChrootCommand(mountPath, command) - } - wrappedCommand := func(command string) *exec.Cmd { - wrapped, err := config.tpl.Process(config.CommandWrapper, &WrappedCommandTemplate{ - Command: command, - }) - if err != nil { - ui.Error(err.Error()) - } - return ShellCommand(wrapped) - } - - state.Put("chrootCmd", chrootCmd) - state.Put("wrappedCommand", wrappedCommand) + wrappedCommand := state.Get("wrappedCommand").(Command) + chrootCmd := state.Get("chrootCmd").(Command) // Create our communicator comm := &Communicator{ Chroot: mountPath, ChrootCmd: chrootCmd, - wrappedCommand: wrappedCommand, + WrappedCommand: wrappedCommand, } // Provision diff --git a/builder/amazon/chroot/step_mount_device.go b/builder/amazon/chroot/step_mount_device.go index 4cce60f10..e8a987d1d 100644 --- a/builder/amazon/chroot/step_mount_device.go +++ b/builder/amazon/chroot/step_mount_device.go @@ -7,6 +7,7 @@ import ( "github.com/mitchellh/packer/packer" "log" "os" + "os/exec" "path/filepath" ) @@ -56,10 +57,15 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + chrootCmd := func(command string) *exec.Cmd { + return ChrootCommand(mountPath, command) + } + state.Put("chrootCmd", Command(chrootCmd)) + ui.Say("Mounting the root device...") stderr := new(bytes.Buffer) mountCommand := fmt.Sprintf("mount %s %s", device, mountPath) - wrappedCommand := state.Get("wrappedCommand").(*Command) + wrappedCommand := state.Get("wrappedCommand").(Command) cmd := wrappedCommand(mountCommand) cmd.Stderr = stderr if err := cmd.Run(); err != nil { diff --git a/builder/amazon/chroot/step_mount_extra.go b/builder/amazon/chroot/step_mount_extra.go index bbe958ec8..a5216e055 100644 --- a/builder/amazon/chroot/step_mount_extra.go +++ b/builder/amazon/chroot/step_mount_extra.go @@ -83,7 +83,7 @@ func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error { var path string lastIndex := len(s.mounts) - 1 path, s.mounts = s.mounts[lastIndex], s.mounts[:lastIndex] - unmountCommand := fmt.Sprintf("unmount %s", path) + unmountCommand := fmt.Sprintf("umount %s", path) stderr := new(bytes.Buffer) cmd := wrappedCommand(unmountCommand) From 831d5caa50653afb6db985dac50bdc37f326b430 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 27 Sep 2013 22:08:15 +0000 Subject: [PATCH 19/21] move wrapper definitions around. --- builder/amazon/chroot/builder.go | 6 +++++- builder/amazon/chroot/communicator.go | 4 ++-- builder/amazon/chroot/step_chroot_provision.go | 9 ++++----- builder/amazon/chroot/step_copy_files.go | 2 +- builder/amazon/chroot/step_mount_device.go | 6 ------ 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index a7f46a5f4..642f4925d 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -37,6 +37,10 @@ type Config struct { tpl *packer.ConfigTemplate } +type wrappedCommandTemplate struct { + Command string +} + type Builder struct { config Config runner multistep.Runner @@ -162,7 +166,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ec2conn := ec2.New(auth, region) wrappedCommand := func(command string) *exec.Cmd { - wrapped, err := b.config.tpl.Process(b.config.CommandWrapper, &WrappedCommandTemplate{ + wrapped, err := b.config.tpl.Process(b.config.CommandWrapper, &wrappedCommandTemplate{ Command: command, }) if err != nil { diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 7f52d9fdb..92ed0d25a 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -66,7 +66,7 @@ func (c *Communicator) Upload(dst string, r io.Reader) error { } defer os.Remove(tf.Name()) io.Copy(tf, r) - cpCmd := fmt.Sprintf("cp %s %s", dst, tf.Name()) + cpCmd := fmt.Sprintf("cp %s %s", tf.Name(), dst) return (*c.ChrootCmd(cpCmd)).Run() } @@ -90,7 +90,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error chrootDest := filepath.Join(c.Chroot, dst, path) log.Printf("Uploading to chroot dir: %s", dst) - cpCmd := fmt.Sprintf("cp %s %s", chrootDest, fullPath) + cpCmd := fmt.Sprintf("cp %s %s", fullPath, chrootDest) return c.ChrootCmd(cpCmd).Run() } diff --git a/builder/amazon/chroot/step_chroot_provision.go b/builder/amazon/chroot/step_chroot_provision.go index 481f58671..f4612c807 100644 --- a/builder/amazon/chroot/step_chroot_provision.go +++ b/builder/amazon/chroot/step_chroot_provision.go @@ -4,6 +4,7 @@ import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" "log" + "os/exec" ) // StepChrootProvision provisions the instance within a chroot. @@ -11,16 +12,14 @@ type StepChrootProvision struct { mounts []string } -type WrappedCommandTemplate struct { - Command string -} - func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { hook := state.Get("hook").(packer.Hook) mountPath := state.Get("mount_path").(string) ui := state.Get("ui").(packer.Ui) wrappedCommand := state.Get("wrappedCommand").(Command) - chrootCmd := state.Get("chrootCmd").(Command) + chrootCmd := func(command string) *exec.Cmd { + return ChrootCommand(mountPath, command) + } // Create our communicator comm := &Communicator{ diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index e22301369..c7be3bb66 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -31,7 +31,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { chrootPath := filepath.Join(mountPath, path) log.Printf("Copying '%s' to '%s'", path, chrootPath) - cmd := fmt.Sprintf("cp %s %s", chrootPath, path) + cmd := fmt.Sprintf("cp %s %s", path, chrootPath) if err := wrappedCommand(cmd); err != nil { err := fmt.Errorf("Error copying file: %s", err) state.Put("error", err) diff --git a/builder/amazon/chroot/step_mount_device.go b/builder/amazon/chroot/step_mount_device.go index e8a987d1d..f9a823623 100644 --- a/builder/amazon/chroot/step_mount_device.go +++ b/builder/amazon/chroot/step_mount_device.go @@ -7,7 +7,6 @@ import ( "github.com/mitchellh/packer/packer" "log" "os" - "os/exec" "path/filepath" ) @@ -57,11 +56,6 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - chrootCmd := func(command string) *exec.Cmd { - return ChrootCommand(mountPath, command) - } - state.Put("chrootCmd", Command(chrootCmd)) - ui.Say("Mounting the root device...") stderr := new(bytes.Buffer) mountCommand := fmt.Sprintf("mount %s %s", device, mountPath) From a15f629f4fe62e228de7381689bc48773b733136 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Sat, 28 Sep 2013 01:10:33 +0000 Subject: [PATCH 20/21] WIP copying files. --- builder/amazon/chroot/communicator.go | 48 +++++++++++++----------- builder/amazon/chroot/copy_files.go | 11 +++--- builder/amazon/chroot/step_copy_files.go | 11 ++++-- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 92ed0d25a..37a9bbdf8 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -49,7 +49,7 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { } log.Printf( - "Chroot executation ended with '%d': '%s'", + "Chroot executation exited with '%d': '%s'", exitStatus, cmd.Command) cmd.SetExited(exitStatus) }() @@ -67,35 +67,39 @@ func (c *Communicator) Upload(dst string, r io.Reader) error { defer os.Remove(tf.Name()) io.Copy(tf, r) cpCmd := fmt.Sprintf("cp %s %s", tf.Name(), dst) - return (*c.ChrootCmd(cpCmd)).Run() + return (c.WrappedCommand(cpCmd)).Run() } func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { - walkFn := func(fullPath string, info os.FileInfo, err error) error { - if err != nil { - return err - } + /* + walkFn := func(fullPath string, info os.FileInfo, err error) error { + if err != nil { + return err + } - path, err := filepath.Rel(src, fullPath) - if err != nil { - return err - } + path, err := filepath.Rel(src, fullPath) + if err != nil { + return err + } - for _, e := range exclude { - if e == path { - log.Printf("Skipping excluded file: %s", path) - return nil + for _, e := range exclude { + if e == path { + log.Printf("Skipping excluded file: %s", path) + return nil + } } - } - chrootDest := filepath.Join(c.Chroot, dst, path) - log.Printf("Uploading to chroot dir: %s", dst) - cpCmd := fmt.Sprintf("cp %s %s", fullPath, chrootDest) - return c.ChrootCmd(cpCmd).Run() - } + chrootDest := filepath.Join(c.Chroot, dst, path) + log.Printf("Uploading dir %s to chroot dir: %s", src, dst) + cpCmd := fmt.Sprintf("cp %s %s", fullPath, chrootDest) + return c.WrappedCommand(cpCmd).Run() + } + */ - log.Printf("Uploading directory '%s' to '%s'", src, dst) - return filepath.Walk(src, walkFn) + chrootDest := filepath.Join(c.Chroot, dst) + log.Printf("Uploading directory '%s' to '%s'", src, chrootDest) + cpCmd := fmt.Sprintf("cp -R %s* %s", src, chrootDest) + return c.WrappedCommand(cpCmd).Run() } func (c *Communicator) Download(src string, w io.Writer) error { diff --git a/builder/amazon/chroot/copy_files.go b/builder/amazon/chroot/copy_files.go index 5af58baa5..b8807d095 100644 --- a/builder/amazon/chroot/copy_files.go +++ b/builder/amazon/chroot/copy_files.go @@ -7,12 +7,13 @@ import ( ) func ChrootCommand(chroot string, command string) *exec.Cmd { - chrootCommand := fmt.Sprintf("chroot %s %s", chroot, command) - return ShellCommand(chrootCommand) + cmd := fmt.Sprintf("sudo chroot %s", chroot) + return ShellCommand(cmd, command) } -func ShellCommand(command string) *exec.Cmd { - cmd := exec.Command("/bin/sh", "-c", command) - log.Printf("ShellCommand(%s) -> #%v", command, cmd.Args) +func ShellCommand(commands ...string) *exec.Cmd { + cmds := append([]string{"-c"}, commands...) + cmd := exec.Command("/bin/sh", cmds...) + log.Printf("ShellCommand: %s %v", cmd.Path, cmd.Args[1:]) return cmd } diff --git a/builder/amazon/chroot/step_copy_files.go b/builder/amazon/chroot/step_copy_files.go index c7be3bb66..3cb096d10 100644 --- a/builder/amazon/chroot/step_copy_files.go +++ b/builder/amazon/chroot/step_copy_files.go @@ -1,6 +1,7 @@ package chroot import ( + "bytes" "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" @@ -22,6 +23,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { mountPath := state.Get("mount_path").(string) ui := state.Get("ui").(packer.Ui) wrappedCommand := state.Get("wrappedCommand").(Command) + stderr := new(bytes.Buffer) s.files = make([]string, 0, len(config.CopyFiles)) if len(config.CopyFiles) > 0 { @@ -31,9 +33,12 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { chrootPath := filepath.Join(mountPath, path) log.Printf("Copying '%s' to '%s'", path, chrootPath) - cmd := fmt.Sprintf("cp %s %s", path, chrootPath) - if err := wrappedCommand(cmd); err != nil { - err := fmt.Errorf("Error copying file: %s", err) + cmd := wrappedCommand(fmt.Sprintf("cp %s %s", path, chrootPath)) + stderr.Reset() + cmd.Stderr = stderr + if err := cmd.Run(); err != nil { + err := fmt.Errorf( + "Error copying file: %s\nnStderr: %s", err, stderr.String()) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt From 5f19648268d80760a8fac6afde962a839f9c43e4 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Sun, 29 Sep 2013 08:04:57 +0000 Subject: [PATCH 21/21] comment, formatting. --- builder/amazon/chroot/builder.go | 7 ++++--- builder/amazon/chroot/communicator.go | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 642f4925d..de3a840b1 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -166,9 +166,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ec2conn := ec2.New(auth, region) wrappedCommand := func(command string) *exec.Cmd { - wrapped, err := b.config.tpl.Process(b.config.CommandWrapper, &wrappedCommandTemplate{ - Command: command, - }) + wrapped, err := b.config.tpl.Process( + b.config.CommandWrapper, &wrappedCommandTemplate{ + Command: command, + }) if err != nil { ui.Error(err.Error()) } diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 37a9bbdf8..8e6bc90bf 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -96,6 +96,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error } */ + // TODO: remove any file copied if it appears in `exclude` chrootDest := filepath.Join(c.Chroot, dst) log.Printf("Uploading directory '%s' to '%s'", src, chrootDest) cpCmd := fmt.Sprintf("cp -R %s* %s", src, chrootDest)