blob: f3ee563dfb63a3ce708e5ce94c27f7fe44a10794 [file] [log] [blame]
gio0eaf2712024-04-14 13:08:46 +04001package main
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "os"
9 "os/exec"
10 "sync"
11 "time"
12
13 "golang.org/x/crypto/ssh"
14)
15
16type Server struct {
17 l sync.Locker
18 port int
gio266c04f2024-07-03 14:18:45 +040019 appId string
gio0eaf2712024-04-14 13:08:46 +040020 ready bool
21 cmd *exec.Cmd
22 repoAddr string
23 signer ssh.Signer
24 appDir string
25 runCommands []Command
26 self string
gioa60f0de2024-07-08 10:49:48 +040027 managerAddr string
gio0eaf2712024-04-14 13:08:46 +040028}
29
gio266c04f2024-07-03 14:18:45 +040030func NewServer(port int, appId string, repoAddr string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
gio0eaf2712024-04-14 13:08:46 +040031 return &Server{
32 l: &sync.Mutex{},
33 port: port,
34 ready: false,
gio266c04f2024-07-03 14:18:45 +040035 appId: appId,
gio0eaf2712024-04-14 13:08:46 +040036 repoAddr: repoAddr,
37 signer: signer,
38 appDir: appDir,
39 runCommands: runCommands,
40 self: self,
gioa60f0de2024-07-08 10:49:48 +040041 managerAddr: manager,
gio0eaf2712024-04-14 13:08:46 +040042 }
43}
44
45func (s *Server) Start() error {
46 http.HandleFunc("/update", s.handleUpdate)
47 http.HandleFunc("/ready", s.handleReady)
48 if err := s.run(); err != nil {
49 return err
50 }
51 go s.pingManager()
52 return http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil)
53}
54
55func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
56 s.l.Lock()
57 defer s.l.Unlock()
58 if s.ready {
59 fmt.Fprintln(w, "ok")
60 } else {
61 http.Error(w, "not ready", http.StatusInternalServerError)
62 }
63}
64
65func (s *Server) handleUpdate(w http.ResponseWriter, r *http.Request) {
66 fmt.Println("update")
67 s.l.Lock()
68 s.ready = false
69 s.l.Unlock()
70 if s.cmd != nil {
71 err := s.cmd.Process.Kill()
72 s.cmd.Wait()
73 s.cmd = nil
74 if err != nil {
75 http.Error(w, err.Error(), http.StatusInternalServerError)
76 return
77 }
78 }
79 if err := os.RemoveAll(s.appDir); err != nil {
80 http.Error(w, err.Error(), http.StatusInternalServerError)
81 return
82 }
83 if err := s.run(); err != nil {
84 http.Error(w, err.Error(), http.StatusInternalServerError)
85 return
86 }
87 s.l.Lock()
88 s.ready = true
89 s.l.Unlock()
90}
91
92func (s *Server) run() error {
93 if err := CloneRepository(s.repoAddr, s.signer, s.appDir); err != nil {
94 return err
95 }
96 for i, c := range s.runCommands {
97 args := []string{c.Bin}
98 args = append(args, c.Args...)
99 cmd := &exec.Cmd{
100 Dir: *appDir,
101 Path: c.Bin,
102 Args: args,
gio1364e432024-06-29 11:39:18 +0400103 Env: c.Env,
gio0eaf2712024-04-14 13:08:46 +0400104 Stdout: os.Stdout,
105 Stderr: os.Stderr,
106 }
107 fmt.Printf("Running: %s\n", c.Bin)
108 if i < len(s.runCommands)-1 {
109 if err := cmd.Run(); err != nil {
110 return err
111 }
112 } else {
113 if err := cmd.Start(); err != nil {
114 return err
115 }
116 s.cmd = cmd
117 }
118 }
119 return nil
120}
121
122type pingReq struct {
123 Address string `json:"address"`
124}
125
126func (s *Server) pingManager() {
127 defer func() {
128 go func() {
129 time.Sleep(5 * time.Second)
130 s.pingManager()
131 }()
132 }()
gioa60f0de2024-07-08 10:49:48 +0400133 buf, err := json.Marshal(pingReq{
134 Address: fmt.Sprintf("%s:%d", s.self, s.port),
135 })
gio0eaf2712024-04-14 13:08:46 +0400136 if err != nil {
137 return
138 }
gioa60f0de2024-07-08 10:49:48 +0400139 registerWorkerAddr := fmt.Sprintf("%s/api/apps/%s/workers", s.managerAddr, s.appId)
140 http.Post(registerWorkerAddr, "application/json", bytes.NewReader(buf))
gio0eaf2712024-04-14 13:08:46 +0400141}