DodoApp: Logs

Change-Id: Idb429c417b639b53352642d1d59a6b29622d499f
diff --git a/apps/app-runner/log.go b/apps/app-runner/log.go
new file mode 100644
index 0000000..2e5f524
--- /dev/null
+++ b/apps/app-runner/log.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+	"strings"
+	"sync"
+)
+
+type Log struct {
+	l sync.Mutex
+	d strings.Builder
+}
+
+func (l *Log) Write(p []byte) (n int, err error) {
+	l.l.Lock()
+	defer l.l.Unlock()
+	// TODO(gio): Reset s.logs periodically
+	return l.d.Write(p)
+}
+
+func (l *Log) Contents() string {
+	l.l.Lock()
+	defer l.l.Unlock()
+	return l.d.String()
+}
diff --git a/apps/app-runner/server.go b/apps/app-runner/server.go
index f3ee563..5969160 100644
--- a/apps/app-runner/server.go
+++ b/apps/app-runner/server.go
@@ -4,6 +4,7 @@
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"io"
 	"net/http"
 	"os"
 	"os/exec"
@@ -25,6 +26,7 @@
 	runCommands []Command
 	self        string
 	managerAddr string
+	logs        *Log
 }
 
 func NewServer(port int, appId string, repoAddr string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
@@ -39,12 +41,14 @@
 		runCommands: runCommands,
 		self:        self,
 		managerAddr: manager,
+		logs:        &Log{},
 	}
 }
 
 func (s *Server) Start() error {
 	http.HandleFunc("/update", s.handleUpdate)
 	http.HandleFunc("/ready", s.handleReady)
+	http.HandleFunc("/logs", s.handleLogs)
 	if err := s.run(); err != nil {
 		return err
 	}
@@ -52,6 +56,10 @@
 	return http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil)
 }
 
+func (s *Server) handleLogs(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprint(w, s.logs.Contents())
+}
+
 func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
 	s.l.Lock()
 	defer s.l.Unlock()
@@ -93,6 +101,7 @@
 	if err := CloneRepository(s.repoAddr, s.signer, s.appDir); err != nil {
 		return err
 	}
+	logM := io.MultiWriter(os.Stdout, s.logs)
 	for i, c := range s.runCommands {
 		args := []string{c.Bin}
 		args = append(args, c.Args...)
@@ -101,8 +110,8 @@
 			Path:   c.Bin,
 			Args:   args,
 			Env:    c.Env,
-			Stdout: os.Stdout,
-			Stderr: os.Stderr,
+			Stdout: logM,
+			Stderr: logM,
 		}
 		fmt.Printf("Running: %s\n", c.Bin)
 		if i < len(s.runCommands)-1 {
@@ -121,6 +130,7 @@
 
 type pingReq struct {
 	Address string `json:"address"`
+	Logs    string `json:"logs"`
 }
 
 func (s *Server) pingManager() {
@@ -132,6 +142,7 @@
 	}()
 	buf, err := json.Marshal(pingReq{
 		Address: fmt.Sprintf("%s:%d", s.self, s.port),
+		Logs:    s.logs.Contents(),
 	})
 	if err != nil {
 		return