github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/lxd/communicator.go (about) 1 package lxd 2 3 import ( 4 "fmt" 5 "github.com/hashicorp/packer/packer" 6 "io" 7 "log" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "syscall" 12 ) 13 14 type Communicator struct { 15 ContainerName string 16 CmdWrapper CommandWrapper 17 } 18 19 func (c *Communicator) Start(cmd *packer.RemoteCmd) error { 20 localCmd, err := c.Execute(cmd.Command) 21 22 if err != nil { 23 return err 24 } 25 26 localCmd.Stdin = cmd.Stdin 27 localCmd.Stdout = cmd.Stdout 28 localCmd.Stderr = cmd.Stderr 29 if err := localCmd.Start(); err != nil { 30 return err 31 } 32 33 go func() { 34 exitStatus := 0 35 if err := localCmd.Wait(); err != nil { 36 if exitErr, ok := err.(*exec.ExitError); ok { 37 exitStatus = 1 38 39 // There is no process-independent way to get the REAL 40 // exit status so we just try to go deeper. 41 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 42 exitStatus = status.ExitStatus() 43 } 44 } 45 } 46 47 log.Printf( 48 "lxc exec execution exited with '%d': '%s'", 49 exitStatus, cmd.Command) 50 cmd.SetExited(exitStatus) 51 }() 52 53 return nil 54 } 55 56 func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { 57 cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file push - %s", filepath.Join(c.ContainerName, dst))) 58 if err != nil { 59 return err 60 } 61 62 log.Printf("Running copy command: %s", cpCmd) 63 command := ShellCommand(cpCmd) 64 command.Stdin = r 65 66 return command.Run() 67 } 68 69 func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { 70 // NOTE:lxc file push doesn't yet support directory uploads. 71 // As a work around, we tar up the folder, upload it as a file, then extract it 72 73 // Don't use 'z' flag as compressing may take longer and the transfer is likely local. 74 // If this isn't the case, it is possible for the user to compress in another step then transfer. 75 // It wouldn't be possibe to disable compression, without exposing this option. 76 tar, err := c.CmdWrapper(fmt.Sprintf("tar -cf - -C %s .", src)) 77 if err != nil { 78 return err 79 } 80 81 cp, err := c.CmdWrapper(fmt.Sprintf("lxc exec %s -- tar -xf - -C %s", c.ContainerName, dst)) 82 if err != nil { 83 return err 84 } 85 86 tarCmd := ShellCommand(tar) 87 cpCmd := ShellCommand(cp) 88 89 cpCmd.Stdin, _ = tarCmd.StdoutPipe() 90 log.Printf("Starting tar command: %s", tar) 91 err = tarCmd.Start() 92 if err != nil { 93 return err 94 } 95 96 log.Printf("Running cp command: %s", cp) 97 err = cpCmd.Run() 98 if err != nil { 99 log.Printf("Error running cp command: %s", err) 100 return err 101 } 102 103 err = tarCmd.Wait() 104 if err != nil { 105 log.Printf("Error running tar command: %s", err) 106 return err 107 } 108 109 return nil 110 } 111 112 func (c *Communicator) Download(src string, w io.Writer) error { 113 cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file pull %s -", filepath.Join(c.ContainerName, src))) 114 if err != nil { 115 return err 116 } 117 118 log.Printf("Running copy command: %s", cpCmd) 119 command := ShellCommand(cpCmd) 120 command.Stdout = w 121 122 return command.Run() 123 } 124 125 func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { 126 // TODO This could probably be "lxc exec <container> -- cd <src> && tar -czf - | tar -xzf - -C <dst>" 127 return fmt.Errorf("DownloadDir is not implemented for lxc") 128 } 129 130 func (c *Communicator) Execute(commandString string) (*exec.Cmd, error) { 131 log.Printf("Executing with lxc exec in container: %s %s", c.ContainerName, commandString) 132 command, err := c.CmdWrapper( 133 fmt.Sprintf("lxc exec %s -- /bin/sh -c \"%s\"", c.ContainerName, commandString)) 134 if err != nil { 135 return nil, err 136 } 137 138 localCmd := ShellCommand(command) 139 log.Printf("Executing lxc exec: %s %#v", localCmd.Path, localCmd.Args) 140 141 return localCmd, nil 142 }