houston 자체 업데이트 기능 추가 - replacer
This commit is contained in:
@ -4,15 +4,18 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
@ -93,6 +96,7 @@ type houstonClient struct {
|
||||
exitChan chan *exec.Cmd
|
||||
httpAddr string
|
||||
timestamp string
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func bToMb(b uint64) uint32 {
|
||||
@ -218,7 +222,9 @@ func NewClient() (HoustonClient, error) {
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
hc.wg.Add(1)
|
||||
go func() {
|
||||
defer hc.wg.Done()
|
||||
// regularly send status
|
||||
sc := protos.NewMonitorClient(client)
|
||||
hn, _ := os.Hostname()
|
||||
@ -250,9 +256,21 @@ func NewClient() (HoustonClient, error) {
|
||||
|
||||
exitChan := make(chan *exec.Cmd, 10)
|
||||
operationChan := make(chan *protos.OperationQueryResponse, 10)
|
||||
hc.wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer hc.wg.Done()
|
||||
|
||||
// 메인 operator
|
||||
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 {
|
||||
select {
|
||||
@ -276,13 +294,33 @@ func NewClient() (HoustonClient, error) {
|
||||
case shared.Deploy:
|
||||
var dr shared.DeployRequest
|
||||
unmarshal(&dr, resp.Args)
|
||||
err := hc.deploy(&dr)
|
||||
if err == nil {
|
||||
prog := gatherDeployedPrograms(dr.Name)
|
||||
hc.deploys[dr.Name] = prog
|
||||
op.Refresh(ctx, hc.makeOperationQueryRequest())
|
||||
|
||||
if dr.Name == myname {
|
||||
if srcdir, _, err := hc.prepareUpdateSelf(&dr); err == nil {
|
||||
args := []string{
|
||||
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 {
|
||||
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:
|
||||
@ -344,6 +382,8 @@ func NewClient() (HoustonClient, error) {
|
||||
func (hc *houstonClient) Start() {
|
||||
// receive from stream
|
||||
defer func() {
|
||||
hc.wg.Wait()
|
||||
|
||||
for _, proc := range hc.childProcs {
|
||||
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||
proc.cmd.Process.Signal(os.Kill)
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
@ -54,7 +55,7 @@ func download(dir string, urlpath string, accessToken string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out.Name(), nil
|
||||
return filepath.ToSlash(out.Name()), nil
|
||||
}
|
||||
|
||||
func unzip(fname string) error {
|
||||
@ -63,7 +64,10 @@ func unzip(fname string) error {
|
||||
os.Remove(fname)
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
defer func() {
|
||||
archive.Close()
|
||||
os.Remove(fname)
|
||||
}()
|
||||
|
||||
verpath := path.Dir(fname)
|
||||
for _, f := range archive.File {
|
||||
@ -102,6 +106,7 @@ func unzip(fname string) error {
|
||||
dstFile.Close()
|
||||
fileInArchive.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -110,7 +115,10 @@ func untar(fname string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
defer func() {
|
||||
file.Close()
|
||||
os.Remove(fname)
|
||||
}()
|
||||
|
||||
verpath := path.Dir(fname)
|
||||
tarReader := tar.NewReader(file)
|
||||
@ -142,6 +150,7 @@ func untar(fname string) error {
|
||||
return errors.New("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -179,6 +188,83 @@ func (hc *houstonClient) prepareDeploy(name string, version string) (destPath st
|
||||
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 {
|
||||
logger.Println("start deploying")
|
||||
root, err := hc.prepareDeploy(req.Name, req.Version)
|
||||
@ -186,14 +272,9 @@ func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
|
||||
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)
|
||||
// verpath에 배포 시작
|
||||
fname, err := download(root, req.Url, req.AccessToken)
|
||||
fname, err := download(root, hc.makeDownloadUrl(req), req.AccessToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
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 {
|
||||
t.Error(err)
|
||||
return
|
||||
|
||||
2
main.go
2
main.go
@ -30,7 +30,7 @@ func main() {
|
||||
hc.Start()
|
||||
} else if *runAsServer {
|
||||
svr := server.NewServer()
|
||||
svr.Start(*port)
|
||||
svr.Start()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
89
replacer/main.go
Normal file
89
replacer/main.go
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user