Compare commits

...

52 Commits

Author SHA1 Message Date
31d77efe84 Merge branch 'master' into kd-live 2023-09-05 17:16:43 +09:00
3ace1be27a stdout을 dev/null로 redirect 2023-09-05 17:16:05 +09:00
3fccae1ef5 Merge branch 'master' into kd-live 2023-07-03 14:32:09 +09:00
3dc121bc71 버전에 태그가 일찍 붙는 문제 수정 2023-07-03 14:31:55 +09:00
706339a386 Merge branch 'master' into kd-live 2023-07-03 14:18:20 +09:00
849973c449 서비스 자체 태그도 넘겨줌 2023-07-03 14:18:11 +09:00
759dcaf00b Merge branch 'master' into kd-live 2023-07-03 14:12:35 +09:00
d1a192159b 서비스 태그를 하위 버전에도 붙여줌 2023-07-03 14:12:21 +09:00
db4769687c Merge branch 'master' into kd-live 2023-07-03 14:08:46 +09:00
7a5696961f 버전에 태그를 붙임 2023-07-03 14:08:28 +09:00
7418d847b7 Merge branch 'master' into kd-live 2023-07-03 11:44:00 +09:00
4d9a25dd6b 클라이언트에서는 태그를 없애고 서버에서만 관리 2023-07-03 11:34:21 +09:00
b057730ad5 Merge branch 'master' into kd-live 2023-07-03 10:54:27 +09:00
9bd09509c8 reaonly 대신 tag로 교체 2023-07-03 10:54:13 +09:00
d526a923ca Merge branch 'master' into kd-live 2023-06-30 16:48:51 +09:00
1b57e9f87e 업로드 된 로그 파일은 지우되, 최신도 같이 지워지던 문제 수정 2023-06-30 16:48:13 +09:00
30412bce70 Merge branch 'master' into kd-live 2023-06-30 16:38:13 +09:00
4667d351a8 업로드한 로그 제거 안함 2023-06-30 16:38:01 +09:00
20dc204cb1 Merge branch 'master' into kd-live 2023-06-30 15:51:47 +09:00
571f0d76df 업로드한 로그도 제거하지 말자 2023-06-30 15:51:03 +09:00
025e583f12 Merge branch 'master' into kd-live 2023-06-29 14:13:17 +09:00
15dd1e544c 하위 폴더 복사 누락 수정 2023-06-29 14:13:04 +09:00
b3ed139431 Merge branch 'master' into kd-live 2023-06-29 12:24:22 +09:00
45ab15d345 gracefulstop 말고 바로 stop 2023-06-29 12:24:13 +09:00
c0a0f88220 Merge branch 'master' into kd-live 2023-06-29 12:05:22 +09:00
02db65e06f houston 버전 관리 2023-06-29 12:05:11 +09:00
ed4ca6cfed Merge branch 'master' into kd-live 2023-06-29 11:11:35 +09:00
a8821b694b 버전 파일 경로 오류 수정 2023-06-29 11:11:23 +09:00
7c54fda9a2 Merge branch 'master' into kd-live 2023-06-29 11:00:49 +09:00
5429d3d90f houstonClient도 버전 관리 2023-06-29 11:00:26 +09:00
77174ccef4 Merge branch 'master' into kd-live 2023-06-29 10:20:09 +09:00
cf46888b6a houstonClient 종료시 houstonServer도 종료 2023-06-29 10:19:57 +09:00
642d53fbba Merge branch 'master' into kd-live 2023-06-28 18:29:20 +09:00
6a35d7b1fe 프로세스 구동중인지 검출하는 로직 변경 2023-06-28 18:29:12 +09:00
361ff4bb3a Merge branch 'master' into kd-live 2023-06-28 18:23:08 +09:00
d04dd5b05d 로그 더 추가 2023-06-28 18:22:50 +09:00
942875071f Merge branch 'master' into kd-live 2023-06-28 18:15:25 +09:00
d3442be5dc 로그 추가 2023-06-28 18:15:13 +09:00
bff9548dab Merge branch 'master' into kd-live 2023-06-28 18:01:53 +09:00
06bc095ea4 프로세스 종료 판별 로직 수정 2023-06-28 18:00:51 +09:00
e5b7a7b02b Merge branch 'master' into kd-live 2023-06-27 20:05:32 +09:00
da47d7c587 httpwriter tracker 일단 다시 제거 2023-06-27 20:05:23 +09:00
3f2c82251b Merge branch 'master' into kd-live 2023-06-27 11:55:39 +09:00
d464812cf8 config 다운로드 경로 수정 2023-06-27 11:55:24 +09:00
fdb0a7baa5 Merge branch 'master' into kd-live 2023-06-27 11:17:28 +09:00
fa8b78efed restart 로직 변경 2023-06-27 11:17:16 +09:00
cf033b6107 Merge branch 'master' into kd-live 2023-06-27 11:02:28 +09:00
9b0aa4d640 인덱스 버그 수정 2023-06-27 11:02:19 +09:00
96a0b43e3f Merge branch 'master' into kd-live 2023-06-27 10:53:27 +09:00
cacbc1008a operation argument에 []string 지원 2023-06-27 10:53:14 +09:00
6185d055ef Merge branch 'master' into kd-live 2023-06-27 10:06:42 +09:00
6d319f2fa1 logapicall 플래그 추가 2023-06-27 10:06:26 +09:00
10 changed files with 146 additions and 80 deletions

View File

@ -15,6 +15,7 @@ import (
"reflect"
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"syscall"
@ -116,6 +117,8 @@ type houstonClient struct {
timestamp string
wg sync.WaitGroup
config clientConfig
version string
standalone bool
}
func unmarshal[T any](val *T, src map[string]string) {
@ -129,6 +132,9 @@ func unmarshal[T any](val *T, src map[string]string) {
if argval.Elem().Field(i).CanInt() {
num, _ := strconv.ParseInt(arg, 10, 0)
argval.Elem().Field(i).SetInt(num)
} else if argval.Elem().Field(i).Kind() == reflect.Array || argval.Elem().Field(i).Kind() == reflect.Slice {
conv := strings.Split(arg, "\n")
argval.Elem().Field(i).Set(reflect.ValueOf(conv))
} else {
argval.Elem().Field(i).SetString(arg)
}
@ -158,8 +164,33 @@ func gatherDeployedPrograms(storageRoot, name string) []*protos.VersionAndArgs {
}
func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryRequest {
hn, _ := os.Hostname()
procs := make([]*protos.ProcessDescription, 0, len(hc.childProcs))
var procs []*protos.ProcessDescription
var deploys []*protos.DeployedVersions
var selfname string
var selfargs []string
if hc.standalone {
selfname = path.Base(os.Args[0])
selfargs = os.Args[1:]
} else {
selfname = "houston"
selfargs = []string{}
}
procs = append(procs, &protos.ProcessDescription{
Name: selfname,
Args: selfargs,
Version: hc.version,
State: protos.ProcessState_Running,
Pid: int32(os.Getpid()),
})
deploys = append(deploys, &protos.DeployedVersions{
Name: selfname,
Versions: []*protos.VersionAndArgs{
{Version: hc.version, Args: selfargs},
},
})
for _, child := range hc.childProcs {
procs = append(procs, &protos.ProcessDescription{
Name: child.name,
@ -170,7 +201,6 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
})
}
var deploys []*protos.DeployedVersions
for name, prog := range hc.deploys {
deploys = append(deploys, &protos.DeployedVersions{
Name: name,
@ -178,6 +208,7 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
})
}
hn, _ := os.Hostname()
return &protos.OperationQueryRequest{
Hostname: hn,
Procs: procs,
@ -185,7 +216,7 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
}
}
func NewClient() (HoustonClient, error) {
func NewClient(standalone bool) (HoustonClient, error) {
clientConfig, err := loadClientConfig()
if err != nil {
return nil, err
@ -234,12 +265,19 @@ func NewClient() (HoustonClient, error) {
}
}
ver, _ := os.ReadFile("@version")
if len(ver) == 0 {
ver = []byte("0.0.0")
}
hc := &houstonClient{
config: clientConfig,
clientChan: make(chan *grpc.ClientConn),
extraMetrics: unsafe.Pointer(&map[string]float32{}),
deploys: deploys,
timestamp: exefi.ModTime().String(),
version: string(ver),
standalone: standalone,
}
ctx, cancel := context.WithCancel(context.Background())
@ -415,6 +453,8 @@ func (hc *houstonClient) Start() {
var client *grpc.ClientConn
reconnCount := 0
time.Sleep(time.Second)
for {
select {
case <-hc.ctx.Done():

View File

@ -264,6 +264,12 @@ func (hc *houstonClient) prepareUpdateSelf(req *shared.DeployRequest) (srcdir st
return "", "", err
}
// houston version 파일
err = os.WriteFile(path.Join(path.Dir(fname), "@version"), []byte(req.Version), 0644)
if err != nil {
return "", "", err
}
selfname, _ := os.Executable()
srcreplacer := path.Join(path.Dir(fname), "replacer") + path.Ext(selfname)
replacer = "./" + filepath.ToSlash("replacer"+path.Ext(selfname))

View File

@ -102,11 +102,12 @@ func zipLogFiles(storageRoot string, req *shared.UploadRequest, start, except st
defer w.Close()
oldestFile := ""
for _, file := range matches {
for i, file := range matches {
if file == root {
continue
}
if len(except) > 0 && file >= except {
matches = matches[:i]
break
}
if len(start) > 0 && file < start {
@ -439,7 +440,7 @@ func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest,
if proc.cmd.Process.Pid == int(req.Pid) {
if len(req.Config) > 0 {
// config.json를 먼저 다운로드 시도
root := proc.cmd.Path
root := proc.cmd.Dir
if _, err := download(root, hc.makeDownloadUrl(req.Config), ""); err != nil {
return err
}
@ -447,10 +448,7 @@ func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest,
proc.state = protos.ProcessState_Restart
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
proc.cmd.Process.Signal(os.Kill)
}
hc.exitChan <- proc.cmd
break
}
@ -483,11 +481,7 @@ func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error {
// 전체 파일을 대상으로
zipFile, srcFiles, err := zipLogFiles(hc.config.StorageRoot, req, "", "")
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 {
if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err == nil {
for _, oldf := range srcFiles {
os.Remove(oldf)
}
} else {
if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err != nil {
logger.Println("uploadZipLogFile failed :", err)
}
} else if err != nil {

View File

@ -1,4 +1,4 @@
#!/bin/sh
nohup /home/opdev/houston -client -logfile &
nohup /home/opdev/houston -client -logfile > /dev/null &

View File

@ -18,7 +18,7 @@ func main() {
}
if *runAsClient {
hc, err := client.NewClient()
hc, err := client.NewClient(true)
if err != nil {
logger.Fatal(err)
return

View File

@ -8,15 +8,24 @@ import (
"os"
"os/exec"
"path"
"strconv"
"time"
)
func copy(src, dst string) error {
func copy(src, dst string, stdlog *log.Logger) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
if fi.IsDir() {
entries, _ := os.ReadDir(src)
for _, ent := range entries {
if err := copy(path.Join(src, ent.Name()), path.Join(dst, ent.Name()), stdlog); err != nil {
return err
}
}
return nil
}
inmode := fi.Mode()
in, err := os.Open(src)
@ -46,6 +55,7 @@ func copy(src, dst string) error {
return err
}
stdlog.Println("file copied :", src, dst)
return nil
}
@ -60,54 +70,34 @@ func main() {
// args[3:] : 다시 시작할 때 넘겨줄 arguments(프로세스 이름 포함)
stdlog.Println(args)
pid, err := strconv.Atoi(args[1])
if err != nil {
stdlog.Fatal(err)
}
for {
stdlog.Println("wait for terminating of", args[3])
proc, err := os.FindProcess(pid)
if err != nil {
stdlog.Fatal(err)
}
state, _ := proc.Wait()
if state == nil {
cmd := exec.Command("ps", "-p", args[1])
if err := cmd.Run(); err != nil {
break
}
time.Sleep(time.Second)
}
selfext, _ := os.Executable()
selfext = path.Base(selfext)
nextArgs := args[4:]
entries, _ := os.ReadDir(args[2])
for _, ent := range entries {
if ent.Name() == selfext {
continue
}
stdlog.Println("target is terminated")
if ent.IsDir() {
if err := os.MkdirAll(ent.Name(), 0775); err != nil {
stdlog.Fatal(err)
}
} else {
if ent.Name() == "@args" {
var tempArgs []string
argfile, _ := os.Open(path.Join(args[2], ent.Name()))
dec := json.NewDecoder(argfile)
if dec.Decode(&tempArgs) == nil {
nextArgs = tempArgs
}
} else if err := copy(path.Join(args[2], ent.Name()), ent.Name()); err != nil {
stdlog.Println("copy failed :", path.Join(args[2], ent.Name()), ent.Name())
stdlog.Fatal(err)
}
}
// replacer 제거. 내가 돌고 있으므로 복사는 안된다.
// 내가 실행되기 전에 이미 복사가 되서 나는 최신 버전임
os.Remove(path.Join(args[2], os.Args[0]))
if err := copy(args[2], "", stdlog); err != nil {
stdlog.Fatal(err)
}
err = os.RemoveAll(args[2])
nextArgs := args[4:]
if bt, _ := os.ReadFile("@args"); len(bt) > 0 {
var tempArgs []string
if json.Unmarshal(bt, &tempArgs) == nil {
nextArgs = tempArgs
}
}
os.Remove("@args")
err := os.RemoveAll(args[2])
if err != nil {
stdlog.Println("os.RemoveAll failed :", args[2], err)
}

View File

@ -34,6 +34,16 @@ func (h *houstonHandler) GetAgents(w http.ResponseWriter, r *http.Request) {
enc.Encode(allHosts)
}
func readTagsFromFile(paths ...string) string {
raw, _ := os.ReadFile(path.Join(paths...))
if len(raw) > 0 {
tag := string(raw)
return strings.Trim(tag, "\n")
}
return ""
}
func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request) {
files, err := os.ReadDir(h.deployPath)
if err != nil {
@ -43,19 +53,31 @@ func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request
}
getVersions := func(name string) []string {
var out []string
vers, _ := os.ReadDir(path.Join(h.deployPath, name))
mytags := readTagsFromFile(h.deployPath, name, "@tags")
out := []string{
mytags,
}
for _, fd := range vers {
if fd.IsDir() {
ver := fd.Name()
files, _ := os.ReadDir(path.Join(h.deployPath, name, ver))
vertags := readTagsFromFile(h.deployPath, name, ver, "@tags")
if len(files) > 0 {
downloadpath := path.Join(sub_folder_name_deploys, name, ver, files[0].Name())
ver = fmt.Sprintf("%s:%s", ver, downloadpath)
for _, file := range files {
if strings.HasPrefix(file.Name(), "@") {
continue
}
downloadpath := path.Join(sub_folder_name_deploys, name, ver, file.Name())
ver = fmt.Sprintf("%s:%s", ver+mytags+vertags, downloadpath)
break
}
}
out = append(out, ver)
}
}
return out
}
@ -100,8 +122,12 @@ func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Reque
if version == "config" {
filename = path.Join(h.deployPath, name, version, "config"+ext)
tags := readTagsFromFile(h.deployPath, name, version, "@tags")
if !strings.Contains(tags, "#hidden") {
tags = tags + "#hidden"
os.WriteFile(path.Join(h.deployPath, name, version, "@tags"), []byte(tags), 0644)
}
} else {
// deploys 폴더는 파일시스템 서비스이므로 다운로드 가능
filename = path.Join(h.deployPath, name, version, name+ext)
}
@ -223,6 +249,10 @@ func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
continue
}
if strings.HasPrefix(fd.Name(), "@") {
continue
}
fi, _ := fd.Info()
if fi.ModTime().After(latestTime) {
latestFilename = fi.Name()

View File

@ -15,10 +15,6 @@ import (
"repositories.action2quare.com/ayo/gocommon/logger"
)
const (
defaultMaxMemory = 32 << 10 // 32 KB
)
type HoustonServerWithHandler interface {
HoustonServer
RegisterHandlers(serveMux *http.ServeMux, prefix string) error
@ -157,10 +153,8 @@ func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var operation string
if r.Method == "POST" {
operation = r.FormValue("operation")
logger.Println("api called :", userinfo["email"], r.Form)
} else {
operation = r.URL.Query().Get("operation")
logger.Println("api called :", userinfo["email"], r.URL.Query())
}
if len(operation) == 0 {

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"reflect"
"strings"
"sync"
"repositories.action2quare.com/ayo/houston/shared"
@ -150,6 +151,12 @@ func marshal(argval reflect.Value, output map[string]string) map[string]string {
marshal(argval.Field(i), output)
} else if argval.Field(i).CanInt() {
output[argval.Type().Field(i).Name] = fmt.Sprintf("%d", argval.Field(i).Int())
} else if argval.Field(i).Kind() == reflect.Array || argval.Field(i).Kind() == reflect.Slice {
var conv []string
for j := 0; j < argval.Field(i).Len(); j++ {
conv = append(conv, argval.Field(i).Index(j).String())
}
output[argval.Type().Field(i).Name] = strings.Join(conv, "\n")
} else {
output[argval.Type().Field(i).Name] = argval.Field(i).String()
}
@ -158,8 +165,6 @@ func marshal(argval reflect.Value, output map[string]string) map[string]string {
}
func (os *operationServer) Query(svr protos.Operation_QueryServer) error {
// 서버는 업데이트가 있는지 확인하고 있으면 stream에 응답을 보낸다.
// 업데이트가 없으면 대기
desc, err := svr.Recv()
if err != nil {
return err

View File

@ -5,7 +5,7 @@ import (
"fmt"
"net"
"os"
"time"
"sync/atomic"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/client"
@ -176,27 +176,34 @@ func (hs *houstonServer) Start() error {
return err
}
closeCount := int32(0)
var hc client.HoustonClient
if loadServerConfig().RunAsClient {
hc, err = client.NewClient(false)
if err != nil {
return err
}
go func() {
time.Sleep(time.Second)
hc, err := client.NewClient()
if err != nil {
logger.Fatal(err)
return
}
hc.Start()
if atomic.AddInt32(&closeCount, 1) == 1 {
hs.Stop()
}
}()
}
if err := hs.rpcServer.Serve(lis); err != nil {
return err
err = hs.rpcServer.Serve(lis)
if atomic.AddInt32(&closeCount, 1) == 1 {
if hc != nil {
hc.Shutdown()
}
}
return nil
return err
}
func (hs *houstonServer) Stop() {
hs.rpcServer.GracefulStop()
hs.rpcServer.Stop()
}
func (hs *houstonServer) Operation() Operation {