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  }