houston 자체 업데이트 기능 추가 - replacer

This commit is contained in:
2023-06-09 16:16:26 +09:00
parent ae783aabaf
commit 5326e26a8c
5 changed files with 227 additions and 17 deletions

View File

@ -4,15 +4,18 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"io/fs" "io/fs"
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"path" "path"
"path/filepath"
"reflect" "reflect"
"sort" "sort"
"strconv" "strconv"
"sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
"unsafe" "unsafe"
@ -93,6 +96,7 @@ type houstonClient struct {
exitChan chan *exec.Cmd exitChan chan *exec.Cmd
httpAddr string httpAddr string
timestamp string timestamp string
wg sync.WaitGroup
} }
func bToMb(b uint64) uint32 { func bToMb(b uint64) uint32 {
@ -218,7 +222,9 @@ func NewClient() (HoustonClient, error) {
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
hc.wg.Add(1)
go func() { go func() {
defer hc.wg.Done()
// regularly send status // regularly send status
sc := protos.NewMonitorClient(client) sc := protos.NewMonitorClient(client)
hn, _ := os.Hostname() hn, _ := os.Hostname()
@ -250,9 +256,21 @@ func NewClient() (HoustonClient, error) {
exitChan := make(chan *exec.Cmd, 10) exitChan := make(chan *exec.Cmd, 10)
operationChan := make(chan *protos.OperationQueryResponse, 10) operationChan := make(chan *protos.OperationQueryResponse, 10)
hc.wg.Add(1)
go func() { go func() {
defer hc.wg.Done()
// 메인 operator // 메인 operator
op := protos.NewOperationClient(hc.client) op := protos.NewOperationClient(hc.client)
myname, _ := os.Executable()
myname = path.Base(filepath.ToSlash(myname))
if len(path.Ext(myname)) > 0 {
myname = myname[:len(myname)-len(path.Ext(myname))]
}
if myname == "__debug_bin" {
myname = "houston"
}
for { for {
select { select {
@ -276,13 +294,33 @@ func NewClient() (HoustonClient, error) {
case shared.Deploy: case shared.Deploy:
var dr shared.DeployRequest var dr shared.DeployRequest
unmarshal(&dr, resp.Args) unmarshal(&dr, resp.Args)
err := hc.deploy(&dr)
if err == nil { if dr.Name == myname {
prog := gatherDeployedPrograms(dr.Name) if srcdir, _, err := hc.prepareUpdateSelf(&dr); err == nil {
hc.deploys[dr.Name] = prog args := []string{
op.Refresh(ctx, hc.makeOperationQueryRequest()) fmt.Sprintf("%d", os.Getpid()),
srcdir,
filepath.ToSlash(os.Args[0]),
}
args = append(args, os.Args[1:]...)
logger.Println(args)
// cmd := exec.Command(replacer, args...)
// if err := cmd.Start(); err != nil {
// logger.Println(err)
// } else {
// hc.shutdownFunc()
// }
} else {
logger.Println(err)
}
} else { } else {
logger.Println(err) if err := hc.deploy(&dr); err == nil {
prog := gatherDeployedPrograms(dr.Name)
hc.deploys[dr.Name] = prog
op.Refresh(ctx, hc.makeOperationQueryRequest())
} else {
logger.Println(err)
}
} }
case shared.Withdraw: case shared.Withdraw:
@ -344,6 +382,8 @@ func NewClient() (HoustonClient, error) {
func (hc *houstonClient) Start() { func (hc *houstonClient) Start() {
// receive from stream // receive from stream
defer func() { defer func() {
hc.wg.Wait()
for _, proc := range hc.childProcs { for _, proc := range hc.childProcs {
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil { if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
proc.cmd.Process.Signal(os.Kill) proc.cmd.Process.Signal(os.Kill)

View File

@ -11,6 +11,7 @@ import (
"net/url" "net/url"
"os" "os"
"path" "path"
"path/filepath"
"strings" "strings"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
@ -54,7 +55,7 @@ func download(dir string, urlpath string, accessToken string) (string, error) {
return "", err return "", err
} }
return out.Name(), nil return filepath.ToSlash(out.Name()), nil
} }
func unzip(fname string) error { func unzip(fname string) error {
@ -63,7 +64,10 @@ func unzip(fname string) error {
os.Remove(fname) os.Remove(fname)
return err return err
} }
defer archive.Close() defer func() {
archive.Close()
os.Remove(fname)
}()
verpath := path.Dir(fname) verpath := path.Dir(fname)
for _, f := range archive.File { for _, f := range archive.File {
@ -102,6 +106,7 @@ func unzip(fname string) error {
dstFile.Close() dstFile.Close()
fileInArchive.Close() fileInArchive.Close()
} }
return nil return nil
} }
@ -110,7 +115,10 @@ func untar(fname string) error {
if err != nil { if err != nil {
return err return err
} }
defer file.Close() defer func() {
file.Close()
os.Remove(fname)
}()
verpath := path.Dir(fname) verpath := path.Dir(fname)
tarReader := tar.NewReader(file) tarReader := tar.NewReader(file)
@ -142,6 +150,7 @@ func untar(fname string) error {
return errors.New("unknown type") return errors.New("unknown type")
} }
} }
return nil return nil
} }
@ -179,6 +188,83 @@ func (hc *houstonClient) prepareDeploy(name string, version string) (destPath st
return verpath, nil return verpath, nil
} }
func (hc *houstonClient) makeDownloadUrl(req *shared.DeployRequest) string {
out := req.Url
if !strings.HasPrefix(out, "http") {
tks := strings.SplitN(hc.httpAddr, "://", 2)
out = fmt.Sprintf("%s://%s", tks[0], path.Join(tks[1], req.Url))
}
return out
}
func copy(src, dst string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
inmode := fi.Mode()
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
copied, err := io.Copy(out, in)
if err != nil {
return err
}
if copied < fi.Size() {
return errors.New("copy not completed")
}
if err := out.Sync(); err != nil {
return err
}
if err := out.Chmod(inmode); err != nil {
return err
}
return nil
}
func (hc *houstonClient) prepareUpdateSelf(req *shared.DeployRequest) (srcdir string, replacer string, err error) {
// 내가 스스로 업데이트
// 다운로드 받고 압축 푼 다음에 교체용 프로세스 시작
tempdir, err := os.MkdirTemp(os.TempDir(), "*")
if err != nil {
return "", "", err
}
fname, err := download(tempdir, hc.makeDownloadUrl(req), req.AccessToken)
if err != nil {
return "", "", err
}
switch path.Ext(fname) {
case ".zip":
err = unzip(fname)
case ".tar":
err = untar(fname)
}
if err != nil {
return "", "", err
}
selfname, _ := os.Executable()
srcreplacer := path.Join(path.Dir(fname), "replacer") + path.Ext(selfname)
replacer = "replacer" + path.Ext(selfname)
// replacer먼저 가져옴
return filepath.ToSlash(tempdir), filepath.ToSlash(replacer), copy(srcreplacer, replacer)
}
func (hc *houstonClient) deploy(req *shared.DeployRequest) error { func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
logger.Println("start deploying") logger.Println("start deploying")
root, err := hc.prepareDeploy(req.Name, req.Version) root, err := hc.prepareDeploy(req.Name, req.Version)
@ -186,14 +272,9 @@ func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
return err return err
} }
if !strings.HasPrefix(req.Url, "http") {
tks := strings.SplitN(hc.httpAddr, "://", 2)
req.Url = fmt.Sprintf("%s://%s", tks[0], path.Join(tks[1], req.Url))
}
logger.Println("start downloading", req.Url) logger.Println("start downloading", req.Url)
// verpath에 배포 시작 // verpath에 배포 시작
fname, err := download(root, req.Url, req.AccessToken) fname, err := download(root, hc.makeDownloadUrl(req), req.AccessToken)
if err != nil { if err != nil {
return err return err
} }

View File

@ -8,7 +8,7 @@ import (
) )
func TestOperationServer(t *testing.T) { func TestOperationServer(t *testing.T) {
hc, err := client.NewClient("192.168.9.32:8080", "http://192.168.9.32/commandcenter") hc, err := client.NewClient()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -30,7 +30,7 @@ func main() {
hc.Start() hc.Start()
} else if *runAsServer { } else if *runAsServer {
svr := server.NewServer() svr := server.NewServer()
svr.Start(*port) svr.Start()
} }
} }

89
replacer/main.go Normal file
View File

@ -0,0 +1,89 @@
package main
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"path"
"strconv"
)
func copy(src, dst string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
inmode := fi.Mode()
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
copied, err := io.Copy(out, in)
if err != nil {
return err
}
if copied < fi.Size() {
return errors.New("copy not completed")
}
if err := out.Sync(); err != nil {
return err
}
if err := out.Chmod(inmode); err != nil {
return err
}
return nil
}
func main() {
args := os.Args
// args[1] : 나를 시작한 pid. pid가 종료될 때 까지 기다림
// args[2] : target 폴더
// args[3:] : 다시 시작할 때 넘겨줄 arguments(프로세스 이름 포함)
fmt.Println(args)
pid, err := strconv.Atoi(args[1])
if err != nil {
panic(err)
}
proc, err := os.FindProcess(pid)
if err != nil {
panic(err)
}
proc.Wait()
selfext, _ := os.Executable()
entries, _ := os.ReadDir(args[2])
for _, ent := range entries {
if ent.Name() == selfext {
continue
}
if ent.IsDir() {
if err := os.MkdirAll(ent.Name(), os.ModePerm); err != nil {
panic(err)
}
} else {
if err := copy(path.Join(args[2], ent.Name()), ent.Name()); err != nil {
panic(err)
}
}
}
os.RemoveAll(args[2])
cmd := exec.Command(args[3], args[4:]...)
cmd.Start()
}