playground: Remove compilerd sudo requirement.

Invoking docker doesn't require sudo if the user is in the docker
group. Removed sudo calls and added group requirement to readme.
Also removed shutdown command from compilerd. External watchdog can
be used to shutdown machine when compilerd exits.
Made shutdown and run timeout parameters more flexible.
Reverted to using netrc instead of gitcookies.

Change-Id: I236152f2820796e2951a5827aeb01a5ea2b17d5f
diff --git a/client/test.sh b/client/test.sh
index b0d2924..5ebb503 100755
--- a/client/test.sh
+++ b/client/test.sh
@@ -27,7 +27,7 @@
   for e in $EXAMPLES; do
     local d="${PG_BUNDLES_DIR}/${e}"
     echo -e "\n\n>>>>> Test ${d}\n\n"
-    test_pg_example "${d}" "-v=true --runTimeout=5000" || shell_test::fail "${d}: failed to run"
+    test_pg_example "${d}" "-v=true --runTimeout=5s" || shell_test::fail "${d}: failed to run"
     # TODO(sadovsky): Make this "clean exit" check more robust.
     grep -q "\"Exited cleanly.\"" builder.out || shell_test::fail "${d}: did not exit cleanly"
     rm -f builder.out
diff --git a/go/src/playground/.gitignore b/go/src/playground/.gitignore
index f9838f9..682e93a 100644
--- a/go/src/playground/.gitignore
+++ b/go/src/playground/.gitignore
@@ -1,2 +1,2 @@
-builder/gitcookies
-builder/hgrc
+deploy/netrc
+deploy/hgrc
diff --git a/go/src/playground/Dockerfile b/go/src/playground/Dockerfile
index 36436a0..16df41e 100644
--- a/go/src/playground/Dockerfile
+++ b/go/src/playground/Dockerfile
@@ -19,11 +19,10 @@
 # Note: This will be cached! If you want to re-build the docker image using
 # fresh Vanadium code, you must pass "--no-cache" to the docker build command.
 # See README.md.
-ADD builder/gitcookies /root/.gitcookies
-RUN git config --global http.cookiefile ~/.gitcookies
+ADD deploy/netrc /root/.netrc
 RUN curl -u vanadium:D6HT]P,LrJ7e https://dev.v.io/noproxy/vanadium-setup.sh | bash
-RUN rm /root/.gitcookies
-ADD builder/hgrc /root/.hgrc
+RUN rm /root/.netrc
+ADD deploy/hgrc /root/.hgrc
 RUN $VANADIUM_ROOT/bin/v23 profile setup web
 RUN rm /root/.hgrc
 
@@ -36,7 +35,7 @@
 RUN $VANADIUM_ROOT/environment/cout/node/bin/npm install --production
 RUN $VANADIUM_ROOT/environment/cout/node/bin/npm link
 WORKDIR /home/playground
-RUN $VANADIUM_ROOT/environment/cout/node/bin/npm link vanadium 
+RUN $VANADIUM_ROOT/environment/cout/node/bin/npm link vanadium
 
 # Install Vanadium Go dependencies.
 WORKDIR /usr/local/vanadium/release
@@ -58,7 +57,7 @@
 # lines to install a version of proxyd (used by the builder tool) using your
 # local version of the code. This is useful when developing and testing local
 # changes.
-#RUN rm $VANADIUM_ROOT/release/go/bin/proxyd
+#RUN rm -f $VANADIUM_ROOT/release/go/bin/proxyd
 #ADD proxyd_main.go $VANADIUM_ROOT/release/go/src/v.io/x/ref/services/proxy/proxyd/main.go
 #RUN v23 go install v.io/x/ref/services/proxy/proxyd
 
diff --git a/go/src/playground/README.md b/go/src/playground/README.md
index ba5e2f5..6fd7155 100644
--- a/go/src/playground/README.md
+++ b/go/src/playground/README.md
@@ -13,19 +13,30 @@
 
     DOCKER_OPTS="${DOCKER_OPTS} -g /usr/local/google/docker"
 
+Add your user to the docker group:
+
+    $ sudo usermod -a -G docker $(whoami)
+
 Start (or restart) the Docker daemon:
 
     $ sudo service docker restart
 
 Build the playground Docker image (this will take a while...):
 
-    $ cp ~/.gitcookies $VANADIUM_ROOT/release/projects/playground/go/src/playground/builder/gitcookies
-    $ cp ~/.hgrc $VANADIUM_ROOT/release/projects/playground/go/src/playground/builder/hgrc
-    $ sudo docker build -t playground $VANADIUM_ROOT/release/projects/playground/go/src/playground/.
+    $ cp ~/.netrc $VANADIUM_ROOT/release/projects/playground/go/src/playground/deploy/netrc
+    $ cp ~/.hgrc $VANADIUM_ROOT/release/projects/playground/go/src/playground/deploy/hgrc
+    $ docker build -t playground $VANADIUM_ROOT/release/projects/playground/go/src/playground/.
 
 Note: If you want to ensure an up-to-date version of Vanadium is installed in
 the Docker image, run the above command with the "--no-cache" flag.
 
+Note: If you have only a .gitcookies googlesource.com entry and not a .netrc
+one, you can convert it to a .netrc entry using:
+
+    $ cat ~/.gitcookies | grep vanadium.googlesource.com | tail -n 1 | sed -E 's/(\S+)\s+(\S+\s+){5}([^=]+)=(\S+)/machine \1 login \3 password \4/' >> ~/.netrc
+
+(see http://www.chromium.org/chromium-os/developer-guide/gerrit-guide for details)
+
 The 'docker build' command above will compile builder from the main Vanadium
 repository. If you want to use local code instead, open Dockerfile and
 uncomment marked lines before running the command.
@@ -33,7 +44,7 @@
 Test your image (without running compilerd):
 
     $ cd $VANADIUM_ROOT/release/projects/playground/client && make src/example_bundles
-    $ sudo docker run -i playground < $VANADIUM_ROOT/release/projects/playground/client/bundles/fortune/ex0_go/bundle.json
+    $ docker run -i playground < $VANADIUM_ROOT/release/projects/playground/client/bundles/fortune/ex0_go/bundle.json
 
 ## Running the playground server (compilerd)
 
@@ -41,14 +52,14 @@
 
     $ GOPATH=$VANADIUM_ROOT/release/projects/playground/go v23 go install playground/...
 
-Run the compiler binary as root:
+Run the compiler binary:
 
-    $ sudo $VANADIUM_ROOT/release/projects/playground/go/bin/compilerd --shutdown=false --address=localhost:8181
+    $ $VANADIUM_ROOT/release/projects/playground/go/bin/compilerd --listenTimeout=0 --address=localhost:8181
 
 Or, run it without Docker (for faster iterations during development):
 
     $ cd $(mktemp -d "/tmp/XXXXXXXX")
-    $ PATH=$VANADIUM_ROOT/release/go/bin:$VANADIUM_ROOT/release/projects/playground/go/bin:$PATH compilerd --shutdown=false --address=localhost:8181 --use-docker=false
+    $ PATH=$VANADIUM_ROOT/release/go/bin:$VANADIUM_ROOT/release/projects/playground/go/bin:$PATH compilerd --listenTimeout=0 --address=localhost:8181 --use-docker=false
 
 The server should now be running at http://localhost:8181 and responding to
 compile requests at http://localhost:8181/compile.
diff --git a/go/src/playground/builder/main.go b/go/src/playground/builder/main.go
index 5f3f177..8f50d59 100644
--- a/go/src/playground/builder/main.go
+++ b/go/src/playground/builder/main.go
@@ -47,9 +47,7 @@
 	includeV23Env = flag.Bool("includeV23Env", false, "Whether to log the output of \"v23 env\" before compilation.")
 
 	// TODO(ivanpi): Separate out mounttable, proxy, wspr timeouts. Add compile timeout. Revise default.
-	// TODO(ivanpi): you can make this a time.Duration, implement the flag.Value
-	// interface as per veyron2/config settings.
-	runTimeout = flag.Int64("runTimeout", 3000, "Time limit for running user code, in milliseconds.")
+	runTimeout = flag.Duration("runTimeout", 3*time.Second, "Time limit for running user code.")
 
 	// Sink for writing events (debug and run output) to stdout as JSON, one event per line.
 	out event.Sink
@@ -60,10 +58,6 @@
 	mu sync.Mutex
 )
 
-func getRunTimeout() time.Duration {
-	return time.Duration(*runTimeout) * time.Millisecond
-}
-
 // Type of data sent to the builder on stdin.  Input should contain Files.  We
 // look for a file whose Name ends with .id, and parse that into credentials.
 //
@@ -259,7 +253,7 @@
 		}
 	}
 
-	timeout := time.After(getRunTimeout())
+	timeout := time.After(*runTimeout)
 
 	for running > 0 {
 		select {
@@ -320,7 +314,7 @@
 }
 
 func (f *codeFile) startJs() error {
-	wsprProc, wsprPort, err := startWspr(f.Name, f.credentials, getRunTimeout())
+	wsprProc, wsprPort, err := startWspr(f.Name, f.credentials, *runTimeout)
 	if err != nil {
 		return fmt.Errorf("Error starting wspr: %v", err)
 	}
@@ -424,11 +418,11 @@
 
 	panicOnError(createCredentials(r.Credentials))
 
-	mt, err := startMount(getRunTimeout())
+	mt, err := startMount(*runTimeout)
 	panicOnError(err)
 	defer mt.Kill()
 
-	proxy, err := startProxy(getRunTimeout())
+	proxy, err := startProxy(*runTimeout)
 	panicOnError(err)
 	defer proxy.Kill()
 
diff --git a/go/src/playground/compilerd/compile.go b/go/src/playground/compilerd/compile.go
index 32f29bb..602133f 100644
--- a/go/src/playground/compilerd/compile.go
+++ b/go/src/playground/compilerd/compile.go
@@ -110,16 +110,13 @@
 	}
 	cmdKill := lib.DoOnce(func() {
 		event.Debug(res, "Killing program")
+		// The docker client can get in a state where stopping/killing/rm-ing
+		// the container will not kill the client. The opposite should work
+		// correctly (killing the docker client stops the container).
+		// If not, the docker rm call below will.
+		// Note, this wouldn't be sufficient if docker was called through sudo
+		// since sudo doesn't pass sigkill to child processes.
 		cmd.Process.Kill()
-		if *useDocker {
-			// Sudo doesn't pass sigkill to child processes, so we need to find and
-			// kill the docker process directly.
-			// The docker client can get in a state where stopping/killing/rm-ing
-			// the container will not kill the client. The opposite should work
-			// correctly (killing the docker client stops the container).
-			// If not, the docker rm call below will.
-			exec.Command("sudo", "pkill", "-SIGKILL", "-f", id).Run()
-		}
 	})
 
 	cmd.Stdin = bytes.NewReader(requestBody)
@@ -319,9 +316,7 @@
 // Miscellaneous helper functions
 
 func docker(args ...string) *exec.Cmd {
-	fullArgs := []string{"docker"}
-	fullArgs = append(fullArgs, args...)
-	return exec.Command("sudo", fullArgs...)
+	return exec.Command("docker", args...)
 }
 
 // A channel which returns unique ids for the containers.
diff --git a/go/src/playground/compilerd/main.go b/go/src/playground/compilerd/main.go
index ad1c432..07d1610 100644
--- a/go/src/playground/compilerd/main.go
+++ b/go/src/playground/compilerd/main.go
@@ -14,21 +14,21 @@
 	"math/rand"
 	"net/http"
 	"os"
-	"os/exec"
 	"os/signal"
 	"syscall"
 	"time"
 )
 
 var (
-	// This channel is closed when the server begins shutting down.
+	// This channel is closed when clean exit is triggered.
 	// No values are ever sent to it.
 	lameduck chan bool = make(chan bool)
 
 	address = flag.String("address", ":8181", "Address to listen on.")
 
-	// Note, shutdown triggers on SIGTERM or when the time limit is hit.
-	enableShutdown = flag.Bool("shutdown", true, "Whether to ever shutdown the machine.")
+	// compilerd exits cleanly on SIGTERM or after a random amount of time,
+	// between listenTimeout/2 and listenTimeout.
+	listenTimeout = flag.Duration("listenTimeout", 60*time.Minute, "Maximum amount of time to listen for before exiting. A value of 0 disables the timeout.")
 
 	// Maximum request and output size. Same limit as imposed by Go tour.
 	// Note: The response includes error and status messages as well as output,
@@ -37,6 +37,10 @@
 	// maxSize should be large enough to fit all error and status messages
 	// written by compilerd to prevent reaching the hard limit.
 	maxSize = 1 << 16
+
+	// Time to finish serving currently running requests before exiting cleanly.
+	// No new requests are accepted during this time.
+	exitDelay = 30 * time.Second
 )
 
 // Seeds the non-secure random number generator.
@@ -69,50 +73,46 @@
 		panic(err)
 	}
 
-	if *enableShutdown {
-		limit_min := 60
-		delay_min := limit_min/2 + rand.Intn(limit_min/2)
+	listenForNs := listenTimeout.Nanoseconds()
+	if listenForNs > 0 {
+		delayNs := listenForNs/2 + rand.Int63n(listenForNs/2)
 
 		// VMs will be periodically killed to prevent any owned VMs from causing
-		// damage. We want to shutdown cleanly before then so we don't cause
-		// requests to fail.
-		go waitForShutdown(time.Minute * time.Duration(delay_min))
+		// damage. We want to exit cleanly before then so we don't cause requests
+		// to fail. When compilerd exits, a watchdog will shut the machine down
+		// after a short delay.
+		go waitForExit(time.Nanosecond * time.Duration(delayNs))
 	}
 
 	if err := initDBHandles(); err != nil {
 		log.Fatal(err)
 	}
 
-	http.HandleFunc("/healthz", healthz)
 	http.HandleFunc("/compile", handlerCompile)
 	http.HandleFunc("/load", handlerLoad)
 	http.HandleFunc("/save", handlerSave)
+	http.HandleFunc("/healthz", healthz)
 
 	log.Printf("Serving %s\n", *address)
 	http.ListenAndServe(*address, nil)
 }
 
-func waitForShutdown(limit time.Duration) {
-	var beforeExit func() error
-
-	// Shutdown if we get a SIGTERM.
+func waitForExit(limit time.Duration) {
+	// Exit if we get a SIGTERM.
 	term := make(chan os.Signal, 1)
 	signal.Notify(term, syscall.SIGTERM)
 
 	// Or if the time limit expires.
 	deadline := time.After(limit)
-	log.Println("Shutting down at", time.Now().Add(limit))
+	log.Println("Exiting at", time.Now().Add(limit))
 Loop:
 	for {
 		select {
 		case <-deadline:
-			// Shutdown the VM.
-			log.Println("Deadline expired, shutting down.")
-			beforeExit = exec.Command("sudo", "halt").Run
+			log.Println("Deadline expired, exiting in", exitDelay)
 			break Loop
 		case <-term:
-			log.Println("Got SIGTERM, shutting down.")
-			// VM is probably already shutting down, so just exit.
+			log.Println("Got SIGTERM, exiting in", exitDelay)
 			break Loop
 		}
 	}
@@ -121,15 +121,8 @@
 	close(lameduck)
 
 	// Give running requests time to finish.
-	time.Sleep(30 * time.Second)
+	time.Sleep(exitDelay)
 
-	// Go ahead and shutdown.
-	if beforeExit != nil {
-		err := beforeExit()
-		if err != nil {
-			panic(err)
-		}
-	}
 	os.Exit(0)
 }
 
diff --git a/go/src/playground/monitor.py b/go/src/playground/deploy/monitor.py
similarity index 100%
rename from go/src/playground/monitor.py
rename to go/src/playground/deploy/monitor.py
diff --git a/go/src/playground/compilerd/pool_template.json b/go/src/playground/deploy/pool_template.json
similarity index 100%
rename from go/src/playground/compilerd/pool_template.json
rename to go/src/playground/deploy/pool_template.json
diff --git a/go/src/playground/compilerd/update.sh b/go/src/playground/deploy/update.sh
similarity index 100%
rename from go/src/playground/compilerd/update.sh
rename to go/src/playground/deploy/update.sh
diff --git a/go/src/playground/playground_v23_test.go b/go/src/playground/playground_v23_test.go
index c55b1f4..f38e439 100644
--- a/go/src/playground/playground_v23_test.go
+++ b/go/src/playground/playground_v23_test.go
@@ -74,7 +74,7 @@
 		}
 		i.Run("/bin/cp", filepath.Join(testdataDir, f), fdir)
 	}
-	return runPGExample(i, pgBundleDir, "-v=true", "--includeV23Env=true", "--runTimeout=5000")
+	return runPGExample(i, pgBundleDir, "-v=true", "--includeV23Env=true", "--runTimeout=5s")
 }
 
 func V23TestPlayground(i *v23tests.T) {
diff --git a/go/src/playground/test.sh b/go/src/playground/test.sh
index 32d04d2..043efb6 100755
--- a/go/src/playground/test.sh
+++ b/go/src/playground/test.sh
@@ -18,7 +18,7 @@
     cp "${TESTDATA_DIR}/${f}" "${fdir}/"
   done
 
-  test_pg_example "${PGBUNDLE_DIR}" "-v=true --includeV23Env=true --runTimeout=5000"
+  test_pg_example "${PGBUNDLE_DIR}" "-v=true --includeV23Env=true --runTimeout=5s"
 }
 
 main() {