Dodo APP: infrastructure to deploy app by pusing to Git repo
Change-Id: I4034c6893255581b014ddb207c844261cb34202b
diff --git a/apps/app-runner/server.go b/apps/app-runner/server.go
new file mode 100644
index 0000000..665f493
--- /dev/null
+++ b/apps/app-runner/server.go
@@ -0,0 +1,135 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+ "os/exec"
+ "sync"
+ "time"
+
+ "golang.org/x/crypto/ssh"
+)
+
+type Server struct {
+ l sync.Locker
+ port int
+ ready bool
+ cmd *exec.Cmd
+ repoAddr string
+ signer ssh.Signer
+ appDir string
+ runCommands []Command
+ self string
+ manager string
+}
+
+func NewServer(port int, repoAddr string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
+ return &Server{
+ l: &sync.Mutex{},
+ port: port,
+ ready: false,
+ repoAddr: repoAddr,
+ signer: signer,
+ appDir: appDir,
+ runCommands: runCommands,
+ self: self,
+ manager: manager,
+ }
+}
+
+func (s *Server) Start() error {
+ http.HandleFunc("/update", s.handleUpdate)
+ http.HandleFunc("/ready", s.handleReady)
+ if err := s.run(); err != nil {
+ return err
+ }
+ go s.pingManager()
+ return http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil)
+}
+
+func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
+ s.l.Lock()
+ defer s.l.Unlock()
+ if s.ready {
+ fmt.Fprintln(w, "ok")
+ } else {
+ http.Error(w, "not ready", http.StatusInternalServerError)
+ }
+}
+
+func (s *Server) handleUpdate(w http.ResponseWriter, r *http.Request) {
+ fmt.Println("update")
+ s.l.Lock()
+ s.ready = false
+ s.l.Unlock()
+ if s.cmd != nil {
+ err := s.cmd.Process.Kill()
+ s.cmd.Wait()
+ s.cmd = nil
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+ if err := os.RemoveAll(s.appDir); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if err := s.run(); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ s.l.Lock()
+ s.ready = true
+ s.l.Unlock()
+}
+
+func (s *Server) run() error {
+ if err := CloneRepository(s.repoAddr, s.signer, s.appDir); err != nil {
+ return err
+ }
+ for i, c := range s.runCommands {
+ args := []string{c.Bin}
+ args = append(args, c.Args...)
+ cmd := &exec.Cmd{
+ Dir: *appDir,
+ Path: c.Bin,
+ Args: args,
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ }
+ fmt.Printf("Running: %s\n", c.Bin)
+ if i < len(s.runCommands)-1 {
+ if err := cmd.Run(); err != nil {
+ return err
+ }
+ } else {
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ s.cmd = cmd
+ }
+ }
+ return nil
+}
+
+type pingReq struct {
+ Address string `json:"address"`
+}
+
+func (s *Server) pingManager() {
+ defer func() {
+ go func() {
+ time.Sleep(5 * time.Second)
+ s.pingManager()
+ }()
+ }()
+ buf, err := json.Marshal(pingReq{s.self})
+ if err != nil {
+ return
+ }
+ http.Post(s.manager, "application/json", bytes.NewReader(buf))
+}