blob: fb8dad735328b7b604b826c796d4c348155902ed [file] [log] [blame]
package integration
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"time"
"veyron.io/veyron/veyron/lib/expect"
"veyron.io/veyron/veyron/lib/modules"
"veyron.io/veyron/veyron/lib/modules/core"
)
// BuildPkgs returns a path to a directory that contains the built
// binaries for the given set of packages and a function that should
// be invoked to clean up the build artifacts. Note that the clients
// of this function should not modify the contents of this directory
// directly and instead defer to the cleanup function.
func BuildPkgs(pkgs []string) (string, func(), error) {
// The VEYRON_INTEGRATION_BIN_DIR environment variable can be
// used to identify a directory that multiple integration
// tests can use to share binaries. Whoever sets this
// environment variable is responsible for cleaning up the
// directory it points to.
binDir, cleanupFn := os.Getenv("VEYRON_INTEGRATION_BIN_DIR"), func() {}
if binDir == "" {
// If the aforementioned environment variable is not
// set, the given packages are built in a temporary
// directory, which the cleanup function removes.
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
return "", nil, fmt.Errorf("TempDir() failed: %v", err)
}
binDir, cleanupFn = tmpDir, func() { os.RemoveAll(tmpDir) }
}
for _, pkg := range pkgs {
binFile := filepath.Join(binDir, path.Base(pkg))
if _, err := os.Stat(binFile); err != nil {
if !os.IsNotExist(err) {
return "", nil, err
}
cmd := exec.Command("veyron", "go", "build", "-o", filepath.Join(binDir, path.Base(pkg)), pkg)
if err := cmd.Run(); err != nil {
return "", nil, err
}
}
}
return binDir, cleanupFn, nil
}
// StartRootMT uses the given shell to start a root mount table and
// returns a handle for the started command along with the object name
// of the mount table.
func StartRootMT(shell *modules.Shell) (modules.Handle, string, error) {
handle, err := shell.Start(core.RootMTCommand, nil, "--", "--veyron.tcp.address=127.0.0.1:0")
if err != nil {
return nil, "", err
}
s := expect.NewSession(nil, handle.Stdout(), time.Second)
name := s.ExpectVar("MT_NAME")
if err := s.Error(); err != nil {
return nil, "", err
}
s.ExpectVar("MT_ADDR")
if err := s.Error(); err != nil {
return nil, "", err
}
s.ExpectVar("PID")
if err := s.Error(); err != nil {
return nil, "", err
}
return handle, name, nil
}
// StartServer starts a veyron server using the given binary and
// arguments, waiting for the server to successfully mount itself in
// the mount table.
//
// TODO(jsimsa,sadovsky): Use an instance of modules.Shell to start
// and manage the server process to prevent leaking processes when
// its parent terminates unexpectedly.
func StartServer(bin string, args []string) (*os.Process, error) {
args = append(args, "-logtostderr", "-vmodule=publisher=2")
cmd := exec.Command(bin, args...)
outPipe, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("%q failed: %v", strings.Join(cmd.Args, " "), err)
}
// Wait for the server to mount both its tcp and ws endpoint.
ready := make(chan struct{}, 1)
go func() {
defer outPipe.Close()
scanner := bufio.NewScanner(outPipe)
nmounts := 0
for scanner.Scan() {
line := scanner.Text()
if strings.Index(line, "ipc pub: mount") != -1 {
nmounts++
if nmounts == 2 {
close(ready)
}
}
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Scan() failOAed: %v\n", err)
}
}()
select {
case <-ready:
return cmd.Process, nil
case <-time.After(time.Second):
cmd.Process.Kill()
return nil, fmt.Errorf("timed out waiting for %q to mount itself", bin)
}
}