github.com/rothwerx/packer@v0.9.0/builder/amazon/chroot/communicator.go (about) 1 package chroot 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "log" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 "syscall" 14 15 "github.com/mitchellh/packer/packer" 16 ) 17 18 // Communicator is a special communicator that works by executing 19 // commands locally but within a chroot. 20 type Communicator struct { 21 Chroot string 22 CmdWrapper CommandWrapper 23 } 24 25 func (c *Communicator) Start(cmd *packer.RemoteCmd) error { 26 command, err := c.CmdWrapper( 27 fmt.Sprintf("chroot %s /bin/sh -c \"%s\"", c.Chroot, cmd.Command)) 28 if err != nil { 29 return err 30 } 31 32 localCmd := ShellCommand(command) 33 localCmd.Stdin = cmd.Stdin 34 localCmd.Stdout = cmd.Stdout 35 localCmd.Stderr = cmd.Stderr 36 log.Printf("Executing: %s %#v", localCmd.Path, localCmd.Args) 37 if err := localCmd.Start(); err != nil { 38 return err 39 } 40 41 go func() { 42 exitStatus := 0 43 if err := localCmd.Wait(); err != nil { 44 if exitErr, ok := err.(*exec.ExitError); ok { 45 exitStatus = 1 46 47 // There is no process-independent way to get the REAL 48 // exit status so we just try to go deeper. 49 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 50 exitStatus = status.ExitStatus() 51 } 52 } 53 } 54 55 log.Printf( 56 "Chroot execution exited with '%d': '%s'", 57 exitStatus, cmd.Command) 58 cmd.SetExited(exitStatus) 59 }() 60 61 return nil 62 } 63 64 func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { 65 dst = filepath.Join(c.Chroot, dst) 66 log.Printf("Uploading to chroot dir: %s", dst) 67 tf, err := ioutil.TempFile("", "packer-amazon-chroot") 68 if err != nil { 69 return fmt.Errorf("Error preparing shell script: %s", err) 70 } 71 defer os.Remove(tf.Name()) 72 io.Copy(tf, r) 73 74 cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp %s %s", tf.Name(), dst)) 75 if err != nil { 76 return err 77 } 78 79 return ShellCommand(cpCmd).Run() 80 } 81 82 func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { 83 // If src ends with a trailing "/", copy from "src/." so that 84 // directory contents (including hidden files) are copied, but the 85 // directory "src" is omitted. BSD does this automatically when 86 // the source contains a trailing slash, but linux does not. 87 if src[len(src)-1] == '/' { 88 src = src + "." 89 } 90 91 // TODO: remove any file copied if it appears in `exclude` 92 chrootDest := filepath.Join(c.Chroot, dst) 93 94 log.Printf("Uploading directory '%s' to '%s'", src, chrootDest) 95 cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp -R '%s' %s", src, chrootDest)) 96 if err != nil { 97 return err 98 } 99 100 var stderr bytes.Buffer 101 cmd := ShellCommand(cpCmd) 102 cmd.Env = append(cmd.Env, "LANG=C") 103 cmd.Env = append(cmd.Env, os.Environ()...) 104 cmd.Stderr = &stderr 105 err = cmd.Run() 106 if err == nil { 107 return err 108 } 109 110 if strings.Contains(stderr.String(), "No such file") { 111 // This just means that the directory was empty. Just ignore it. 112 return nil 113 } 114 115 return err 116 } 117 118 func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { 119 return fmt.Errorf("DownloadDir is not implemented for amazon-chroot") 120 } 121 122 func (c *Communicator) Download(src string, w io.Writer) error { 123 src = filepath.Join(c.Chroot, src) 124 log.Printf("Downloading from chroot dir: %s", src) 125 f, err := os.Open(src) 126 if err != nil { 127 return err 128 } 129 defer f.Close() 130 131 if _, err := io.Copy(w, f); err != nil { 132 return err 133 } 134 135 return nil 136 }