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  }