blob: 05ab2e2649ecb461d8139e37d2f666f050a6fec9 [file] [log] [blame]
Jiri Simsae63e07d2014-12-04 11:12:08 -08001package integration
2
3import (
4 "bufio"
5 "fmt"
6 "io/ioutil"
7 "os"
8 "os/exec"
9 "path"
10 "path/filepath"
11 "strings"
12 "time"
13
14 "veyron.io/veyron/veyron/lib/expect"
15 "veyron.io/veyron/veyron/lib/modules"
16 "veyron.io/veyron/veyron/lib/modules/core"
17)
18
19// BuildPkgs returns a path to a directory that contains the built
20// binaries for the given set of packages and a function that should
21// be invoked to clean up the build artifacts. Note that the clients
22// of this function should not modify the contents of this directory
23// directly and instead defer to the cleanup function.
24func BuildPkgs(pkgs []string) (string, func(), error) {
25 // The VEYRON_INTEGRATION_BIN_DIR environment variable can be
26 // used to identify a directory that multiple integration
27 // tests can use to share binaries. Whoever sets this
28 // environment variable is responsible for cleaning up the
29 // directory it points to.
30 binDir, cleanupFn := os.Getenv("VEYRON_INTEGRATION_BIN_DIR"), func() {}
31 if binDir == "" {
32 // If the aforementioned environment variable is not
33 // set, the given packages are built in a temporary
34 // directory, which the cleanup function removes.
35 tmpDir, err := ioutil.TempDir("", "")
36 if err != nil {
37 return "", nil, fmt.Errorf("TempDir() failed: %v", err)
38 }
39 binDir, cleanupFn = tmpDir, func() { os.RemoveAll(tmpDir) }
40 }
41 for _, pkg := range pkgs {
42 binFile := filepath.Join(binDir, path.Base(pkg))
43 if _, err := os.Stat(binFile); err != nil {
44 if !os.IsNotExist(err) {
45 return "", nil, err
46 }
47 cmd := exec.Command("veyron", "go", "build", "-o", filepath.Join(binDir, path.Base(pkg)), pkg)
48 if err := cmd.Run(); err != nil {
49 return "", nil, err
50 }
51 }
52 }
53 return binDir, cleanupFn, nil
54}
55
56// StartRootMT uses the given shell to start a root mount table and
57// returns a handle for the started command along with the object name
58// of the mount table.
59func StartRootMT(shell *modules.Shell) (modules.Handle, string, error) {
60 handle, err := shell.Start(core.RootMTCommand, nil, "--", "--veyron.tcp.address=127.0.0.1:0")
61 if err != nil {
62 return nil, "", err
63 }
64 s := expect.NewSession(nil, handle.Stdout(), time.Second)
65 name := s.ExpectVar("MT_NAME")
66 if err := s.Error(); err != nil {
67 return nil, "", err
68 }
69 s.ExpectVar("MT_ADDR")
70 if err := s.Error(); err != nil {
71 return nil, "", err
72 }
73 s.ExpectVar("PID")
74 if err := s.Error(); err != nil {
75 return nil, "", err
76 }
77 return handle, name, nil
78}
79
80// StartServer starts a veyron server using the given binary and
81// arguments, waiting for the server to successfully mount itself in
82// the mount table.
83//
84// TODO(jsimsa,sadovsky): Use an instance of modules.Shell to start
85// and manage the server process to prevent leaking processes when
86// its parent terminates unexpectedly.
87func StartServer(bin string, args []string) (*os.Process, error) {
88 args = append(args, "-logtostderr", "-vmodule=publisher=2")
89 cmd := exec.Command(bin, args...)
90 outPipe, err := cmd.StderrPipe()
91 if err != nil {
92 return nil, err
93 }
Jiri Simsa75dda812014-12-08 15:47:19 -080094 // TODO(jsimsa): Consider using the veyron exec library to
95 // facilitate coordination and communication between the
96 // parent and the child process.
Jiri Simsae63e07d2014-12-04 11:12:08 -080097 if err := cmd.Start(); err != nil {
98 return nil, fmt.Errorf("%q failed: %v", strings.Join(cmd.Args, " "), err)
99 }
Jiri Simsa432cc2e2014-12-08 15:53:38 -0800100 // Wait for the server to mount both its tcp and ws endpoint.
Jiri Simsae63e07d2014-12-04 11:12:08 -0800101 ready := make(chan struct{}, 1)
102 go func() {
103 defer outPipe.Close()
104 scanner := bufio.NewScanner(outPipe)
Cosmos Nicolaouae8dd212014-12-13 23:43:08 -0800105 mounts := 0
Jiri Simsae63e07d2014-12-04 11:12:08 -0800106 for scanner.Scan() {
107 line := scanner.Text()
Cosmos Nicolaouae8dd212014-12-13 23:43:08 -0800108 // TODO(cnicolaou): find a better way of synchronizing with
109 // the child process, this is way too fragile.
Jiri Simsae63e07d2014-12-04 11:12:08 -0800110 if strings.Index(line, "ipc pub: mount") != -1 {
Cosmos Nicolaouae8dd212014-12-13 23:43:08 -0800111 mounts++
112 if mounts == 1 {
Jiri Simsa75dda812014-12-08 15:47:19 -0800113 close(ready)
114 }
Jiri Simsae63e07d2014-12-04 11:12:08 -0800115 }
116 }
117 if err := scanner.Err(); err != nil {
Jiri Simsa75dda812014-12-08 15:47:19 -0800118 fmt.Fprintf(os.Stderr, "Scan() failed: %v\n", err)
Jiri Simsae63e07d2014-12-04 11:12:08 -0800119 }
120 }()
121 select {
122 case <-ready:
123 return cmd.Process, nil
Cosmos Nicolaou80d22e92014-12-10 08:08:17 -0800124 case <-time.After(time.Minute):
Jiri Simsae63e07d2014-12-04 11:12:08 -0800125 cmd.Process.Kill()
Jiri Simsa75dda812014-12-08 15:47:19 -0800126 return nil, fmt.Errorf("timed out waiting for %q to mount itself", strings.Join(cmd.Args, " "))
Jiri Simsae63e07d2014-12-04 11:12:08 -0800127 }
128}