blob: 665f4936ecd0ed4848c5db9426e1a14c759d3c2f [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
19 ready bool
20 cmd *exec.Cmd
21 repoAddr string
22 signer ssh.Signer
23 appDir string
24 runCommands []Command
25 self string
26 manager string
27}
28
29func NewServer(port int, repoAddr string, signer ssh.Signer, appDir string, runCommands []Command, self string, manager string) *Server {
30 return &Server{
31 l: &sync.Mutex{},
32 port: port,
33 ready: false,
34 repoAddr: repoAddr,
35 signer: signer,
36 appDir: appDir,
37 runCommands: runCommands,
38 self: self,
39 manager: manager,
40 }
41}
42
43func (s *Server) Start() error {
44 http.HandleFunc("/update", s.handleUpdate)
45 http.HandleFunc("/ready", s.handleReady)
46 if err := s.run(); err != nil {
47 return err
48 }
49 go s.pingManager()
50 return http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil)
51}
52
53func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
54 s.l.Lock()
55 defer s.l.Unlock()
56 if s.ready {
57 fmt.Fprintln(w, "ok")
58 } else {
59 http.Error(w, "not ready", http.StatusInternalServerError)
60 }
61}
62
63func (s *Server) handleUpdate(w http.ResponseWriter, r *http.Request) {
64 fmt.Println("update")
65 s.l.Lock()
66 s.ready = false
67 s.l.Unlock()
68 if s.cmd != nil {
69 err := s.cmd.Process.Kill()
70 s.cmd.Wait()
71 s.cmd = nil
72 if err != nil {
73 http.Error(w, err.Error(), http.StatusInternalServerError)
74 return
75 }
76 }
77 if err := os.RemoveAll(s.appDir); err != nil {
78 http.Error(w, err.Error(), http.StatusInternalServerError)
79 return
80 }
81 if err := s.run(); err != nil {
82 http.Error(w, err.Error(), http.StatusInternalServerError)
83 return
84 }
85 s.l.Lock()
86 s.ready = true
87 s.l.Unlock()
88}
89
90func (s *Server) run() error {
91 if err := CloneRepository(s.repoAddr, s.signer, s.appDir); err != nil {
92 return err
93 }
94 for i, c := range s.runCommands {
95 args := []string{c.Bin}
96 args = append(args, c.Args...)
97 cmd := &exec.Cmd{
98 Dir: *appDir,
99 Path: c.Bin,
100 Args: args,
101 Stdout: os.Stdout,
102 Stderr: os.Stderr,
103 }
104 fmt.Printf("Running: %s\n", c.Bin)
105 if i < len(s.runCommands)-1 {
106 if err := cmd.Run(); err != nil {
107 return err
108 }
109 } else {
110 if err := cmd.Start(); err != nil {
111 return err
112 }
113 s.cmd = cmd
114 }
115 }
116 return nil
117}
118
119type pingReq struct {
120 Address string `json:"address"`
121}
122
123func (s *Server) pingManager() {
124 defer func() {
125 go func() {
126 time.Sleep(5 * time.Second)
127 s.pingManager()
128 }()
129 }()
130 buf, err := json.Marshal(pingReq{s.self})
131 if err != nil {
132 return
133 }
134 http.Post(s.manager, "application/json", bytes.NewReader(buf))
135}