AppRunner: Reports detailed status

Status includes: commit hash, running commands and logs

Change-Id: I5f28f69710b30508bb3296a22fc97b9ad7553844
diff --git a/apps/app-runner/Makefile b/apps/app-runner/Makefile
index 86ec333..187ca18 100644
--- a/apps/app-runner/Makefile
+++ b/apps/app-runner/Makefile
@@ -16,6 +16,11 @@
 clean:
 	rm -f app-runner
 
+build: export CGO_ENABLED=0
+build: export GO111MODULE=on
+build: clean
+	/usr/local/go/bin/go build -o app-runner *.go
+
 build_arm64: export CGO_ENABLED=0
 build_arm64: export GO111MODULE=on
 build_arm64: export GOOS=linux
diff --git a/apps/app-runner/main.go b/apps/app-runner/main.go
index 9c57225..b9acaa6 100644
--- a/apps/app-runner/main.go
+++ b/apps/app-runner/main.go
@@ -35,7 +35,7 @@
 	Env  []string `json:"env"`
 }
 
-func CloneRepositoryBranch(addr, branch, rootDir string, signer ssh.Signer, path string) error {
+func CloneRepositoryBranch(addr, branch, rootDir string, signer ssh.Signer, path string) (string, error) {
 	ref := fmt.Sprintf("refs/heads/%s", branch)
 	opts := &git.CloneOptions{
 		URL:             addr,
@@ -61,29 +61,13 @@
 	}
 	c, err := git.Clone(memory.NewStorage(), osfs.New(path, osfs.WithBoundOS()), opts)
 	if err != nil {
-		return err
+		return "", err
 	}
-	wt, err := c.Worktree()
+	head, err := c.Head()
 	if err != nil {
-		return err
+		return "", err
 	}
-	if wt == nil {
-		panic(wt)
-	}
-	// TODO(gio): This should probably be removed.
-	// sb, err := wt.Submodules()
-	// if err != nil {
-	// 	return err
-	// }
-	// if err := sb.Init(); err != nil {
-	// 	return err
-	// }
-	// if err := sb.Update(&git.SubmoduleUpdateOptions{
-	// 	Depth: 1,
-	// }); err != nil {
-	// 	return err
-	// }
-	return err
+	return head.Hash().String(), nil
 }
 
 func main() {
@@ -92,6 +76,10 @@
 	if !ok {
 		panic("no SELF_IP")
 	}
+	id, ok := os.LookupEnv("SELF_ID")
+	if !ok {
+		panic("no SELF_ID")
+	}
 	var signer ssh.Signer
 	// TODO(gio): revisit this logic
 	if *sshKey != "" && !(strings.HasPrefix(*repoAddr, "http://") || strings.HasPrefix(*repoAddr, "https://")) {
@@ -104,7 +92,7 @@
 			panic(err)
 		}
 	}
-	if err := CloneRepositoryBranch(*repoAddr, *branch, *rootDir, signer, *appDir); err != nil {
+	if err := os.Mkdir(*appDir, os.ModePerm); err != nil {
 		panic(err)
 	}
 	r, err := os.Open(*runCfg)
@@ -116,7 +104,7 @@
 	if err := json.NewDecoder(r).Decode(&cmds); err != nil {
 		panic(err)
 	}
-	s := NewServer(*port, *appId, *service, *repoAddr, *branch, *rootDir, signer, *appDir, cmds, self, *managerAddr)
+	s := NewServer(*port, *appId, *service, id, *repoAddr, *branch, *rootDir, signer, *appDir, cmds, self, *managerAddr)
 	if err := s.Start(); err != nil {
 		log.Fatal(err)
 	}
diff --git a/apps/app-runner/server.go b/apps/app-runner/server.go
index 83bcac4..1661731 100644
--- a/apps/app-runner/server.go
+++ b/apps/app-runner/server.go
@@ -17,11 +17,25 @@
 	"golang.org/x/crypto/ssh"
 )
 
+type CommandState string
+
+type CommandStatus struct {
+	Command string       `json:"command"`
+	State   CommandState `json:"state"`
+}
+
+type Status struct {
+	RepoOK   bool            `json:"repoOK"`
+	Commit   string          `json:"commit"`
+	Commands []CommandStatus `json:"commands"`
+}
+
 type Server struct {
 	l           sync.Locker
 	port        int
 	appId       string
 	service     string
+	id          string
 	ready       bool
 	cmd         *exec.Cmd
 	repoAddr    string
@@ -34,15 +48,17 @@
 	managerAddr string
 	logs        *Log
 	currDir     string
+	status      *Status
 }
 
-func NewServer(port int, appId, service, repoAddr, branch, rootDir string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
+func NewServer(port int, appId, service, id, repoAddr, branch, rootDir string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
 	return &Server{
 		l:           &sync.Mutex{},
 		port:        port,
 		ready:       false,
 		appId:       appId,
 		service:     service,
+		id:          id,
 		repoAddr:    repoAddr,
 		branch:      branch,
 		rootDir:     rootDir,
@@ -53,6 +69,7 @@
 		managerAddr: manager,
 		logs:        &Log{},
 		currDir:     "",
+		status:      nil,
 	}
 }
 
@@ -102,23 +119,45 @@
 	if err != nil {
 		return err
 	}
-	if err := CloneRepositoryBranch(s.repoAddr, s.branch, s.rootDir, s.signer, newDir); err != nil {
+	commit, err := CloneRepositoryBranch(s.repoAddr, s.branch, s.rootDir, s.signer, newDir)
+	if err != nil {
+		s.status = &Status{
+			RepoOK: false,
+		}
 		return err
 	}
-	logM := io.MultiWriter(os.Stdout, s.logs)
-	for i, c := range s.runCommands {
+	s.status = &Status{
+		RepoOK:   true,
+		Commit:   commit,
+		Commands: []CommandStatus{},
+	}
+	commands := []string{}
+	for _, c := range s.runCommands {
 		args := []string{c.Bin}
 		args = append(args, c.Args...)
+		cmd := strings.Join(args, " ")
+		commands = append(commands, cmd)
+		s.status.Commands = append(s.status.Commands, CommandStatus{
+			Command: cmd,
+			State:   "waiting",
+		})
+	}
+	logM := io.MultiWriter(os.Stdout, s.logs)
+	for i, c := range commands {
+		if i > 0 {
+			s.status.Commands[i-1].State = "success"
+		}
 		cmd := &exec.Cmd{
 			Dir:    filepath.Join(newDir, s.rootDir),
 			Path:   "/bin/sh",
-			Args:   []string{"/bin/sh", "-c", strings.Join(args, " ")},
-			Env:    append(os.Environ(), c.Env...),
+			Args:   []string{"/bin/sh", "-c", c},
+			Env:    append(os.Environ(), s.runCommands[i].Env...),
 			Stdout: logM,
 			Stderr: logM,
 		}
 		cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
-		fmt.Printf("Running: %s %s\n", c.Bin, c.Args)
+		fmt.Printf("Running: %s\n", c)
+		s.status.Commands[i].State = "running"
 		if i < len(s.runCommands)-1 {
 			if err := cmd.Run(); err != nil {
 				return err
@@ -129,8 +168,10 @@
 				if err := s.kill(); err != nil {
 					return err
 				}
-				if err := os.RemoveAll(s.currDir); err != nil {
-					return err
+				if s.currDir != "" {
+					if err := os.RemoveAll(s.currDir); err != nil {
+						return err
+					}
 				}
 			}
 			if err := cmd.Start(); err != nil {
@@ -144,9 +185,11 @@
 }
 
 type pingReq struct {
-	Service string `json:"service"`
-	Address string `json:"address"`
-	Logs    string `json:"logs"`
+	Id      string  `json:"id"`
+	Service string  `json:"service"`
+	Address string  `json:"address"`
+	Status  *Status `json:"status,omitempty"`
+	Logs    string  `json:"logs"`
 }
 
 func (s *Server) pingManager() {
@@ -157,8 +200,10 @@
 		}()
 	}()
 	buf, err := json.Marshal(pingReq{
+		Id:      s.id,
 		Service: s.service,
 		Address: fmt.Sprintf("http://%s:%d", s.self, s.port),
+		Status:  s.status,
 		Logs:    s.logs.Contents(),
 	})
 	if err != nil {
diff --git a/charts/app-runner/Chart.yaml b/charts/app-runner/Chart.yaml
index 0edd283..d72f597 100644
--- a/charts/app-runner/Chart.yaml
+++ b/charts/app-runner/Chart.yaml
@@ -2,5 +2,5 @@
 name: app-runner
 description: A Helm chart for PCloud App Runner
 type: application
-version: 0.0.4
-appVersion: "0.0.4"
+version: 0.0.5
+appVersion: "0.0.5"
diff --git a/charts/app-runner/templates/install.yaml b/charts/app-runner/templates/install.yaml
index 8a1a8c7..52b8495 100644
--- a/charts/app-runner/templates/install.yaml
+++ b/charts/app-runner/templates/install.yaml
@@ -98,6 +98,10 @@
           {{ toYaml .Values.appPorts | nindent 8 }}
         {{- end }}
         env:
+        - name: SELF_ID
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.name
         - name: SELF_IP
           valueFrom:
             fieldRef: