blob: 5969160443a468425246fe74d67a2aed394144ad [file] [log] [blame]
gio0eaf2712024-04-14 13:08:46 +04001package main
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
gio183e8342024-08-20 06:01:24 +04007 "io"
gio0eaf2712024-04-14 13:08:46 +04008 "net/http"
9 "os"
10 "os/exec"
11 "sync"
12 "time"
13
14 "golang.org/x/crypto/ssh"
15)
16
17type Server struct {
18 l sync.Locker
19 port int
gio266c04f2024-07-03 14:18:45 +040020 appId string
gio0eaf2712024-04-14 13:08:46 +040021 ready bool
22 cmd *exec.Cmd
23 repoAddr string
24 signer ssh.Signer
25 appDir string
26 runCommands []Command
27 self string
gioa60f0de2024-07-08 10:49:48 +040028 managerAddr string
gio183e8342024-08-20 06:01:24 +040029 logs *Log
gio0eaf2712024-04-14 13:08:46 +040030}
31
gio266c04f2024-07-03 14:18:45 +040032func 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 +040033 return &Server{
34 l: &sync.Mutex{},
35 port: port,
36 ready: false,
gio266c04f2024-07-03 14:18:45 +040037 appId: appId,
gio0eaf2712024-04-14 13:08:46 +040038 repoAddr: repoAddr,
39 signer: signer,
40 appDir: appDir,
41 runCommands: runCommands,
42 self: self,
gioa60f0de2024-07-08 10:49:48 +040043 managerAddr: manager,
gio183e8342024-08-20 06:01:24 +040044 logs: &Log{},
gio0eaf2712024-04-14 13:08:46 +040045 }
46}
47
48func (s *Server) Start() error {
49 http.HandleFunc("/update", s.handleUpdate)
50 http.HandleFunc("/ready", s.handleReady)
gio183e8342024-08-20 06:01:24 +040051 http.HandleFunc("/logs", s.handleLogs)
gio0eaf2712024-04-14 13:08:46 +040052 if err := s.run(); err != nil {
53 return err
54 }
55 go s.pingManager()
56 return http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil)
57}
58
gio183e8342024-08-20 06:01:24 +040059func (s *Server) handleLogs(w http.ResponseWriter, r *http.Request) {
60 fmt.Fprint(w, s.logs.Contents())
61}
62
gio0eaf2712024-04-14 13:08:46 +040063func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
64 s.l.Lock()
65 defer s.l.Unlock()
66 if s.ready {
67 fmt.Fprintln(w, "ok")
68 } else {
69 http.Error(w, "not ready", http.StatusInternalServerError)
70 }
71}
72
73func (s *Server) handleUpdate(w http.ResponseWriter, r *http.Request) {
74 fmt.Println("update")
75 s.l.Lock()
76 s.ready = false
77 s.l.Unlock()
78 if s.cmd != nil {
79 err := s.cmd.Process.Kill()
80 s.cmd.Wait()
81 s.cmd = nil
82 if err != nil {
83 http.Error(w, err.Error(), http.StatusInternalServerError)
84 return
85 }
86 }
87 if err := os.RemoveAll(s.appDir); err != nil {
88 http.Error(w, err.Error(), http.StatusInternalServerError)
89 return
90 }
91 if err := s.run(); err != nil {
92 http.Error(w, err.Error(), http.StatusInternalServerError)
93 return
94 }
95 s.l.Lock()
96 s.ready = true
97 s.l.Unlock()
98}
99
100func (s *Server) run() error {
101 if err := CloneRepository(s.repoAddr, s.signer, s.appDir); err != nil {
102 return err
103 }
gio183e8342024-08-20 06:01:24 +0400104 logM := io.MultiWriter(os.Stdout, s.logs)
gio0eaf2712024-04-14 13:08:46 +0400105 for i, c := range s.runCommands {
106 args := []string{c.Bin}
107 args = append(args, c.Args...)
108 cmd := &exec.Cmd{
109 Dir: *appDir,
110 Path: c.Bin,
111 Args: args,
gio1364e432024-06-29 11:39:18 +0400112 Env: c.Env,
gio183e8342024-08-20 06:01:24 +0400113 Stdout: logM,
114 Stderr: logM,
gio0eaf2712024-04-14 13:08:46 +0400115 }
116 fmt.Printf("Running: %s\n", c.Bin)
117 if i < len(s.runCommands)-1 {
118 if err := cmd.Run(); err != nil {
119 return err
120 }
121 } else {
122 if err := cmd.Start(); err != nil {
123 return err
124 }
125 s.cmd = cmd
126 }
127 }
128 return nil
129}
130
131type pingReq struct {
132 Address string `json:"address"`
gio183e8342024-08-20 06:01:24 +0400133 Logs string `json:"logs"`
gio0eaf2712024-04-14 13:08:46 +0400134}
135
136func (s *Server) pingManager() {
137 defer func() {
138 go func() {
139 time.Sleep(5 * time.Second)
140 s.pingManager()
141 }()
142 }()
gioa60f0de2024-07-08 10:49:48 +0400143 buf, err := json.Marshal(pingReq{
144 Address: fmt.Sprintf("%s:%d", s.self, s.port),
gio183e8342024-08-20 06:01:24 +0400145 Logs: s.logs.Contents(),
gioa60f0de2024-07-08 10:49:48 +0400146 })
gio0eaf2712024-04-14 13:08:46 +0400147 if err != nil {
148 return
149 }
gioa60f0de2024-07-08 10:49:48 +0400150 registerWorkerAddr := fmt.Sprintf("%s/api/apps/%s/workers", s.managerAddr, s.appId)
151 http.Post(registerWorkerAddr, "application/json", bytes.NewReader(buf))
gio0eaf2712024-04-14 13:08:46 +0400152}