github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/lxc/communicator.go (about)

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