github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/lxc/communicator.go (about) 1 package lxc 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "log" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 "syscall" 13 14 "github.com/hashicorp/packer/packer" 15 ) 16 17 type LxcAttachCommunicator struct { 18 RootFs string 19 ContainerName string 20 AttachOptions []string 21 CmdWrapper CommandWrapper 22 } 23 24 func (c *LxcAttachCommunicator) Start(cmd *packer.RemoteCmd) error { 25 localCmd, err := c.Execute(cmd.Command) 26 27 if err != nil { 28 return err 29 } 30 31 localCmd.Stdin = cmd.Stdin 32 localCmd.Stdout = cmd.Stdout 33 localCmd.Stderr = cmd.Stderr 34 if err := localCmd.Start(); err != nil { 35 return err 36 } 37 38 go func() { 39 exitStatus := 0 40 if err := localCmd.Wait(); err != nil { 41 if exitErr, ok := err.(*exec.ExitError); ok { 42 exitStatus = 1 43 44 // There is no process-independent way to get the REAL 45 // exit status so we just try to go deeper. 46 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 47 exitStatus = status.ExitStatus() 48 } 49 } 50 } 51 52 log.Printf( 53 "lxc-attach execution exited with '%d': '%s'", 54 exitStatus, cmd.Command) 55 cmd.SetExited(exitStatus) 56 }() 57 58 return nil 59 } 60 61 func (c *LxcAttachCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { 62 dst = filepath.Join(c.RootFs, dst) 63 log.Printf("Uploading to rootfs: %s", dst) 64 tf, err := ioutil.TempFile("", "packer-lxc-attach") 65 if err != nil { 66 return fmt.Errorf("Error uploading file to rootfs: %s", err) 67 } 68 defer os.Remove(tf.Name()) 69 io.Copy(tf, r) 70 71 cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", tf.Name(), dst)) 72 if err != nil { 73 return err 74 } 75 76 if fi != nil { 77 tfDir := filepath.Dir(tf.Name()) 78 // rename tempfile to match original file name. This makes sure that if file is being 79 // moved into a directory, the filename is preserved instead of a temp name. 80 adjustedTempName := filepath.Join(tfDir, (*fi).Name()) 81 mvCmd, err := c.CmdWrapper(fmt.Sprintf("sudo mv %s %s", tf.Name(), adjustedTempName)) 82 if err != nil { 83 return err 84 } 85 defer os.Remove(adjustedTempName) 86 ShellCommand(mvCmd).Run() 87 // change cpCmd to use new file name as source 88 cpCmd, err = c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", adjustedTempName, dst)) 89 if err != nil { 90 return err 91 } 92 } 93 94 log.Printf("Running copy command: %s", dst) 95 96 return ShellCommand(cpCmd).Run() 97 } 98 99 func (c *LxcAttachCommunicator) UploadDir(dst string, src string, exclude []string) error { 100 // TODO: remove any file copied if it appears in `exclude` 101 dest := filepath.Join(c.RootFs, dst) 102 log.Printf("Uploading directory '%s' to rootfs '%s'", src, dest) 103 cpCmd, err := c.CmdWrapper(fmt.Sprintf("sudo cp -R %s/. %s", src, dest)) 104 if err != nil { 105 return err 106 } 107 108 return ShellCommand(cpCmd).Run() 109 } 110 111 func (c *LxcAttachCommunicator) Download(src string, w io.Writer) error { 112 src = filepath.Join(c.RootFs, src) 113 log.Printf("Downloading from rootfs dir: %s", src) 114 f, err := os.Open(src) 115 if err != nil { 116 return err 117 } 118 defer f.Close() 119 120 if _, err := io.Copy(w, f); err != nil { 121 return err 122 } 123 124 return nil 125 } 126 127 func (c *LxcAttachCommunicator) DownloadDir(src string, dst string, exclude []string) error { 128 return fmt.Errorf("DownloadDir is not implemented for lxc") 129 } 130 131 func (c *LxcAttachCommunicator) Execute(commandString string) (*exec.Cmd, error) { 132 log.Printf("Executing with lxc-attach in container: %s %s %s", c.ContainerName, c.RootFs, commandString) 133 134 attachCommand := []string{"sudo", "lxc-attach"} 135 attachCommand = append(attachCommand, c.AttachOptions...) 136 attachCommand = append(attachCommand, []string{"--name", "%s", "--", "/bin/sh -c \"%s\""}...) 137 138 command, err := c.CmdWrapper( 139 fmt.Sprintf(strings.Join(attachCommand, " "), c.ContainerName, commandString)) 140 if err != nil { 141 return nil, err 142 } 143 144 localCmd := ShellCommand(command) 145 log.Printf("Executing lxc-attach: %s %#v", localCmd.Path, localCmd.Args) 146 147 return localCmd, nil 148 } 149 150 func (c *LxcAttachCommunicator) CheckInit() (string, error) { 151 log.Printf("Debug runlevel exec") 152 localCmd, err := c.Execute("/sbin/runlevel") 153 154 if err != nil { 155 return "", err 156 } 157 158 pr, _ := localCmd.StdoutPipe() 159 if err = localCmd.Start(); err != nil { 160 return "", err 161 } 162 163 output, err := ioutil.ReadAll(pr) 164 165 if err != nil { 166 return "", err 167 } 168 169 err = localCmd.Wait() 170 171 if err != nil { 172 return "", err 173 } 174 175 return strings.TrimSpace(string(output)), nil 176 }