veyron/tools/playground: Fix jenkins tests (with luck).
Issues included:
* veyron.js was not linked, js files could not require it.
* addresses were given in IPv6 format, which GCE can't handle.
* port collisions when starting processes.
Change-Id: I51b84135365e9f91709cb69b695b992b282feba8
diff --git a/tools/playground/builder/netrc b/tools/playground/builder/netrc
deleted file mode 100644
index 2cb442c..0000000
--- a/tools/playground/builder/netrc
+++ /dev/null
@@ -1,2 +0,0 @@
-machine veyron.googlesource.com login git-nlacasse.google.com password 1/jK0LNfz-nCtEoI2zixqeuGJww3CIlfN_vY31eVDRMck
-machine veyron-review.googlesource.com login git-nlacasse.google.com password 1/jK0LNfz-nCtEoI2zixqeuGJww3CIlfN_vY31eVDRMck
diff --git a/tools/playground/builder/services.go b/tools/playground/builder/services.go
index ded83f9..3bee6a8 100644
--- a/tools/playground/builder/services.go
+++ b/tools/playground/builder/services.go
@@ -6,19 +6,42 @@
"fmt"
"io"
"log"
+ "math/rand"
"os"
"path"
"regexp"
"strconv"
+ "syscall"
"time"
)
var (
- proxyPort = 1234
- proxyName = "proxy"
- wsprBasePort = 1235
+ proxyName = "proxy"
)
+// Note: This was copied from veyron/go/src/veyron/tools/findunusedport.
+// I would like to be able to import that package directly, but it defines a
+// main(), so can't be imported. An alternative solution would be to call the
+// 'findunusedport' binary, but that would require starting another process and
+// parsing the output. It seemed simpler to just copy the function here.
+func findUnusedPort() (int, error) {
+ rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+ for i := 0; i < 1000; i++ {
+ port := 1024 + rnd.Int31n(64512)
+ fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ if err != nil {
+ continue
+ }
+ sa := &syscall.SockaddrInet4{Port: int(port)}
+ if err := syscall.Bind(fd, sa); err != nil {
+ continue
+ }
+ syscall.Close(fd)
+ return int(port), nil
+ }
+ return 0, fmt.Errorf("Can't find unused port.")
+}
+
// startMount starts a mounttabled process, and sets the NAMESPACE_ROOT env
// variable to the mounttable's location. We run one mounttabled process for
// the entire environment.
@@ -60,7 +83,11 @@
// startProxy starts a proxyd process. We run one proxyd process for the
// entire environment.
func startProxy() (proc *os.Process, err error) {
- cmd := makeCmdJsonEvent("", "proxyd", "-name="+proxyName, "-address=:"+strconv.Itoa(proxyPort))
+ port, err := findUnusedPort()
+ if err != nil {
+ return nil, err
+ }
+ cmd := makeCmdJsonEvent("", "proxyd", "-name="+proxyName, "-address=localhost:"+strconv.Itoa(port))
err = cmd.Start()
if err != nil {
return nil, err
@@ -69,10 +96,12 @@
}
// startWspr starts a wsprd process. We run one wsprd process for each
-// javascript file being run. The 'index' argument is used to pick a distinct
-// port for each wsprd process.
+// javascript file being run.
func startWspr(f *codeFile) (proc *os.Process, port int, err error) {
- port = wsprBasePort + f.index
+ port, err = findUnusedPort()
+ if err != nil {
+ return nil, port, err
+ }
cmd := makeCmdJsonEvent(f.Name,
"wsprd",
"-v=-1",
diff --git a/tools/playground/builder/vbuild.go b/tools/playground/builder/vbuild.go
index cc9188e..df63c38 100644
--- a/tools/playground/builder/vbuild.go
+++ b/tools/playground/builder/vbuild.go
@@ -25,7 +25,7 @@
"veyron/tools/playground/event"
)
-const RUN_TIMEOUT = time.Second
+const runTimeout = 3 * time.Second
var (
verbose = flag.Bool("v", false, "Verbose mode")
@@ -153,7 +153,7 @@
log.Fatal(err)
}
- mt, err := startMount(RUN_TIMEOUT)
+ mt, err := startMount(runTimeout)
if err != nil {
log.Fatal(err)
}
@@ -222,12 +222,13 @@
}
}
- timeout := time.After(RUN_TIMEOUT)
+ timeout := time.After(runTimeout)
for running > 0 {
select {
case <-timeout:
writeEvent("", "Playground exceeded deadline.", "stderr")
+ stopAll(files)
case status := <-exit:
if status.err == nil {
writeEvent(status.name, "Exited.", "stdout")
@@ -376,6 +377,17 @@
cmd := exec.Command(prog, args...)
cmd.Env = os.Environ()
+ // TODO(nlacasse): There is a bug in this code which results in
+ // "read |0: bad file descriptor".
+ // The error seems to be caused by our use of cmd.StdoutPipe/StderrPipe
+ // and cmd.Wait. In particular, we seem to be calling Wait after the
+ // pipes have been closed.
+ // See: http://stackoverflow.com/questions/20134095/why-do-i-get-bad-file-descriptor-in-this-go-program-using-stderr-and-ioutil-re
+ // and https://code.google.com/p/go/issues/detail?id=2266
+ //
+ // One solution is to wrap cmd.Start/Run, so that wait is never called
+ // before the pipes are closed.
+
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
diff --git a/tools/playground/test.sh b/tools/playground/test.sh
index 214570d..90d4e8f 100755
--- a/tools/playground/test.sh
+++ b/tools/playground/test.sh
@@ -4,6 +4,18 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+install_veyron_js() {
+ # This installs the veyron.js library, and makes it accessable to javascript
+ # files in the veyron playground test folder under the module name 'veyron'.
+ #
+ # TODO(nlacasse): Once veyron.js is installed in a public npm registry, this
+ # should be replaced with just "npm install veyron".
+ pushd "${VEYRON_ROOT}/veyron.js"
+ "${VEYRON_ROOT}/environment/cout/node/bin/npm" link
+ popd
+ "${VEYRON_ROOT}/environment/cout/node/bin/npm" link veyron
+}
+
build() {
local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/tools/identity || shell_test::fail "line ${LINENO}: failed to build 'identity'"
@@ -35,6 +47,7 @@
main() {
cd $(shell::tmp_dir)
build
+ install_veyron_js
local -r DIR="${VEYRON_ROOT}/veyron/go/src/veyron/tools/playground/testdata"