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