github.phpd.cn/hashicorp/packer@v1.3.2/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 log.Printf("Uploading to rootfs: %s", dst) 63 tf, err := ioutil.TempFile("", "packer-lxc-attach") 64 if err != nil { 65 return fmt.Errorf("Error uploading file to rootfs: %s", err) 66 } 67 defer os.Remove(tf.Name()) 68 io.Copy(tf, r) 69 70 attachCommand := []string{"cat", "%s", " | ", "lxc-attach"} 71 attachCommand = append(attachCommand, c.AttachOptions...) 72 attachCommand = append(attachCommand, []string{"--name", "%s", "--", "/bin/sh -c \"/bin/cat > %s\""}...) 73 74 cpCmd, err := c.CmdWrapper(fmt.Sprintf(strings.Join(attachCommand, " "), tf.Name(), c.ContainerName, dst)) 75 if err != nil { 76 return err 77 } 78 79 if fi != nil { 80 tfDir := filepath.Dir(tf.Name()) 81 // rename tempfile to match original file name. This makes sure that if file is being 82 // moved into a directory, the filename is preserved instead of a temp name. 83 adjustedTempName := filepath.Join(tfDir, (*fi).Name()) 84 mvCmd, err := c.CmdWrapper(fmt.Sprintf("mv %s %s", tf.Name(), adjustedTempName)) 85 if err != nil { 86 return err 87 } 88 defer os.Remove(adjustedTempName) 89 ShellCommand(mvCmd).Run() 90 // change cpCmd to use new file name as source 91 cpCmd, err = c.CmdWrapper(fmt.Sprintf(strings.Join(attachCommand, " "), adjustedTempName, c.ContainerName, dst)) 92 if err != nil { 93 return err 94 } 95 } 96 97 log.Printf("Running copy command: %s", dst) 98 99 return ShellCommand(cpCmd).Run() 100 } 101 102 func (c *LxcAttachCommunicator) UploadDir(dst string, src string, exclude []string) error { 103 // TODO: remove any file copied if it appears in `exclude` 104 dest := filepath.Join(c.RootFs, dst) 105 log.Printf("Uploading directory '%s' to rootfs '%s'", src, dest) 106 cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp -R %s/. %s", src, dest)) 107 if err != nil { 108 return err 109 } 110 111 return ShellCommand(cpCmd).Run() 112 } 113 114 func (c *LxcAttachCommunicator) Download(src string, w io.Writer) error { 115 src = filepath.Join(c.RootFs, src) 116 log.Printf("Downloading from rootfs dir: %s", src) 117 f, err := os.Open(src) 118 if err != nil { 119 return err 120 } 121 defer f.Close() 122 123 if _, err := io.Copy(w, f); err != nil { 124 return err 125 } 126 127 return nil 128 } 129 130 func (c *LxcAttachCommunicator) DownloadDir(src string, dst string, exclude []string) error { 131 return fmt.Errorf("DownloadDir is not implemented for lxc") 132 } 133 134 func (c *LxcAttachCommunicator) Execute(commandString string) (*exec.Cmd, error) { 135 log.Printf("Executing with lxc-attach in container: %s %s %s", c.ContainerName, c.RootFs, commandString) 136 137 attachCommand := []string{"lxc-attach"} 138 attachCommand = append(attachCommand, c.AttachOptions...) 139 attachCommand = append(attachCommand, []string{"--name", "%s", "--", "/bin/sh -c \"%s\""}...) 140 141 command, err := c.CmdWrapper( 142 fmt.Sprintf(strings.Join(attachCommand, " "), c.ContainerName, commandString)) 143 if err != nil { 144 return nil, err 145 } 146 147 localCmd := ShellCommand(command) 148 log.Printf("Executing lxc-attach: %s %#v", localCmd.Path, localCmd.Args) 149 150 return localCmd, nil 151 } 152 153 func (c *LxcAttachCommunicator) CheckInit() (string, error) { 154 log.Printf("Debug runlevel exec") 155 localCmd, err := c.Execute("/sbin/runlevel") 156 157 if err != nil { 158 return "", err 159 } 160 161 pr, _ := localCmd.StdoutPipe() 162 if err = localCmd.Start(); err != nil { 163 return "", err 164 } 165 166 output, err := ioutil.ReadAll(pr) 167 168 if err != nil { 169 return "", err 170 } 171 172 err = localCmd.Wait() 173 174 if err != nil { 175 return "", err 176 } 177 178 return strings.TrimSpace(string(output)), nil 179 }