Compare commits

...

12 Commits

Author SHA1 Message Date
7fe5090efa latest 버전 옵션을 프로세스 재시작시에도 적용 2024-09-27 15:26:51 +09:00
e9370513c2 zip 파일 unzip후 정리 추가 2024-09-26 13:18:32 +09:00
387d4f3ea8 로그파일 전송 오류 수정 2024-09-26 12:01:53 +09:00
e5984b3342 에러 로그 추가 2024-09-23 22:23:59 +09:00
f174a165fe 지원 타입 및 로그 추가 2024-09-23 21:15:15 +09:00
a112f20cb8 로그 추가 2024-09-23 18:10:14 +09:00
97fc64be81 maingateApiToken추가 2024-09-23 17:46:39 +09:00
7ae391b599 로그 추가 2024-08-22 16:35:14 +09:00
f5e491325f logwirter수정 - 파일과 stdout 동시에 2024-08-22 15:53:53 +09:00
2fa02374fd 로그 폴더 생성 추가 2024-08-22 11:56:50 +09:00
380586fb73 로그파일 설정을 houston으로 이동 2024-08-22 11:28:58 +09:00
da37ed11cd 자동실행 추가 2024-08-16 15:05:05 +09:00
8 changed files with 372 additions and 215 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ houston
houston.zip houston.zip
config.json config.json
.vscode/ .vscode/
/data

View File

@ -2,6 +2,7 @@ package client
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -35,6 +36,23 @@ type runcommand struct {
Exec string `json:"exec"` Exec string `json:"exec"`
Args []string `json:"args"` Args []string `json:"args"`
Version string `json:"version"` Version string `json:"version"`
AutoRestart bool `json:"auto_restart"`
OutputLogFile string `json:"logfile"`
}
type easyruncommand runcommand
func (t *runcommand) UnmarshalJSON(b []byte) error {
easy := easyruncommand{
Version: "latest",
AutoRestart: true,
}
if err := json.Unmarshal(b, &easy); err != nil {
return err
}
*t = runcommand(easy)
return nil
} }
type clientConfig struct { type clientConfig struct {
@ -79,8 +97,11 @@ type procmeta struct {
args []string args []string
version string version string
verpath string verpath string
recover bool
state int32 state int32
stdin io.WriteCloser stdin io.WriteCloser
logfile string
keepLatest bool
} }
func (pm *procmeta) isState(s protos.ProcessState) bool { func (pm *procmeta) isState(s protos.ProcessState) bool {
@ -113,6 +134,13 @@ type houstonClient struct {
} }
func unmarshal[T any](val *T, src map[string]string) { func unmarshal[T any](val *T, src map[string]string) {
defer func() {
r := recover()
if r != nil {
logger.Error(r)
}
}()
argval := reflect.ValueOf(val) argval := reflect.ValueOf(val)
for i := 0; i < argval.Elem().Type().NumField(); i++ { for i := 0; i < argval.Elem().Type().NumField(); i++ {
if !argval.Elem().Type().Field(i).IsExported() { if !argval.Elem().Type().Field(i).IsExported() {
@ -125,6 +153,9 @@ func unmarshal[T any](val *T, src map[string]string) {
} else if argval.Elem().Field(i).Kind() == reflect.Array || argval.Elem().Field(i).Kind() == reflect.Slice { } else if argval.Elem().Field(i).Kind() == reflect.Array || argval.Elem().Field(i).Kind() == reflect.Slice {
conv := strings.Split(arg, "\n") conv := strings.Split(arg, "\n")
argval.Elem().Field(i).Set(reflect.ValueOf(conv)) argval.Elem().Field(i).Set(reflect.ValueOf(conv))
} else if argval.Elem().Field(i).Kind() == reflect.Bool {
bv, _ := strconv.ParseBool(arg)
argval.Elem().Field(i).SetBool(bv)
} else { } else {
argval.Elem().Field(i).SetString(arg) argval.Elem().Field(i).SetString(arg)
} }
@ -332,6 +363,10 @@ func NewClient(standalone bool) (HoustonClient, error) {
proc.cmd.Process.Release() proc.cmd.Process.Release()
if proc.isState(protos.ProcessState_Restart) { if proc.isState(protos.ProcessState_Restart) {
if proc.keepLatest {
proc.version = "latest"
}
if err := hc.startChildProcess(&shared.StartProcessRequest{ if err := hc.startChildProcess(&shared.StartProcessRequest{
Version: proc.version, Version: proc.version,
Name: proc.name, Name: proc.name,
@ -352,10 +387,14 @@ func NewClient(standalone bool) (HoustonClient, error) {
op.Refresh(ctx, hc.makeOperationQueryRequest()) op.Refresh(ctx, hc.makeOperationQueryRequest())
case resp := <-operationChan: case resp := <-operationChan:
logger.Println("houton query operation :", resp.Operation)
switch shared.Operation(resp.Operation) { switch shared.Operation(resp.Operation) {
case shared.Deploy: case shared.Deploy:
var dr shared.DeployRequest var dr shared.DeployRequest
unmarshal(&dr, resp.Args) unmarshal(&dr, resp.Args)
logger.Println("args :", dr)
if dr.Name == myname { if dr.Name == myname {
if srcdir, replacer, err := hc.prepareUpdateSelf(&dr); err == nil { if srcdir, replacer, err := hc.prepareUpdateSelf(&dr); err == nil {
args := []string{ args := []string{
@ -411,6 +450,8 @@ func NewClient(standalone bool) (HoustonClient, error) {
case shared.Withdraw: case shared.Withdraw:
var wr shared.WithdrawRequest var wr shared.WithdrawRequest
unmarshal(&wr, resp.Args) unmarshal(&wr, resp.Args)
logger.Println("args :", wr)
err := hc.withdraw(&wr) err := hc.withdraw(&wr)
if err == nil { if err == nil {
prog := gatherDeployedPrograms(hc.config.StorageRoot, wr.Name) prog := gatherDeployedPrograms(hc.config.StorageRoot, wr.Name)
@ -427,6 +468,8 @@ func NewClient(standalone bool) (HoustonClient, error) {
case shared.Start: case shared.Start:
var sr shared.StartProcessRequest var sr shared.StartProcessRequest
unmarshal(&sr, resp.Args) unmarshal(&sr, resp.Args)
logger.Println("args :", sr)
if err := hc.startChildProcess(&sr); err != nil { if err := hc.startChildProcess(&sr); err != nil {
logger.ErrorWithCallStack(err) logger.ErrorWithCallStack(err)
} else { } else {
@ -436,6 +479,8 @@ func NewClient(standalone bool) (HoustonClient, error) {
case shared.Stop: case shared.Stop:
var sr shared.StopProcessRequest var sr shared.StopProcessRequest
unmarshal(&sr, resp.Args) unmarshal(&sr, resp.Args)
logger.Println("args :", sr)
if err := hc.stopChildProcess(&sr, op); err != nil { if err := hc.stopChildProcess(&sr, op); err != nil {
logger.Println(err) logger.Println(err)
} }
@ -443,6 +488,8 @@ func NewClient(standalone bool) (HoustonClient, error) {
case shared.Restart: case shared.Restart:
var rr shared.RestartProcessRequest var rr shared.RestartProcessRequest
unmarshal(&rr, resp.Args) unmarshal(&rr, resp.Args)
logger.Println("args :", rr)
if err := hc.restartChildProcess(&rr, op); err != nil { if err := hc.restartChildProcess(&rr, op); err != nil {
logger.Println(err) logger.Println(err)
} }
@ -450,6 +497,8 @@ func NewClient(standalone bool) (HoustonClient, error) {
case shared.Upload: case shared.Upload:
var ur shared.UploadRequest var ur shared.UploadRequest
unmarshal(&ur, resp.Args) unmarshal(&ur, resp.Args)
logger.Println("args :", ur)
if err := hc.uploadFiles(&ur); err != nil { if err := hc.uploadFiles(&ur); err != nil {
logger.Println(err) logger.Println(err)
} }
@ -459,19 +508,44 @@ func NewClient(standalone bool) (HoustonClient, error) {
id64, _ := strconv.ParseInt(idstr, 10, 0) id64, _ := strconv.ParseInt(idstr, 10, 0)
id := int32(id64) id := int32(id64)
var found *procmeta
hc.childProcs = gocommon.ShrinkSlice(hc.childProcs, func(e *procmeta) bool { hc.childProcs = gocommon.ShrinkSlice(hc.childProcs, func(e *procmeta) bool {
if e.id == id { if e.id == id {
e.cmd.Wait() found = e
e.cmd.Process.Release()
return true return true
} }
return false return false
}) })
if found != nil {
found.cmd.Wait()
found.cmd.Process.Release()
if found.recover {
time.Sleep(time.Second)
sr := shared.StartProcessRequest{
Name: found.name,
Version: found.version,
Args: found.args,
AutoRestart: found.recover,
OutputLogFile: found.logfile,
}
if err := hc.startChildProcess(&sr); err != nil {
logger.Println("startChildProcess failed by autorun :", err)
logger.ErrorWithCallStack(err)
} else {
logger.Println("recover success :", sr)
}
}
}
if op != nil {
op.Refresh(context.Background(), hc.makeOperationQueryRequest()) op.Refresh(context.Background(), hc.makeOperationQueryRequest())
} }
} }
} }
}
}() }()
hc.shutdownFunc = func() { hc.shutdownFunc = func() {
@ -544,6 +618,8 @@ func (hc *houstonClient) Start() {
Name: service, Name: service,
Version: cmd.Version, Version: cmd.Version,
Args: append([]string{cmd.Exec}, cmd.Args...), Args: append([]string{cmd.Exec}, cmd.Args...),
AutoRestart: cmd.AutoRestart,
OutputLogFile: cmd.OutputLogFile,
} }
if err := hc.startChildProcess(&sr); err != nil { if err := hc.startChildProcess(&sr); err != nil {

View File

@ -15,7 +15,6 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@ -79,30 +78,34 @@ func (hc *houstonClient) uploadZipLogFile(zipFile string, name string, version s
return nil return nil
} }
func zipLogFiles(storageRoot string, req *shared.UploadRequest) (string, []string, error) { func findMatchFiles(storageRoot, name, version, filter string) (string, []string) {
root := path.Join(storageRoot, req.Name, req.Version) root := path.Join(storageRoot, name, version)
matches, err := filepath.Glob(path.Join(root, req.Filter)) matches, err := filepath.Glob(path.Join(root, filter))
if err != nil { if err != nil {
return "", nil, err return "", nil
} }
if len(matches) == 0 { if len(matches) == 0 {
return "", nil, nil return "", nil
} }
for i, file := range matches { root = path.Join(root, path.Dir(filter))
out := make([]string, 0, len(matches))
for _, file := range matches {
file = filepath.ToSlash(file) file = filepath.ToSlash(file)
matches[i] = file if file == root {
continue
} }
root = path.Join(root, path.Dir(req.Filter)) out = append(out, file)
hostname, _ := os.Hostname() }
zipFileName := path.Join(os.TempDir(), hostname+"_"+path.Base(filepath.ToSlash(matches[0]))) + ".zip" return root, out
os.Remove(zipFileName) }
f, err := os.OpenFile(zipFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
func zipCompressFiles(root string, matches []string) (string, error) {
f, err := os.CreateTemp(os.TempDir(), "*.zip")
if err != nil { if err != nil {
return "", nil, err return "", err
} }
defer f.Close() defer f.Close()
@ -111,10 +114,6 @@ func zipLogFiles(storageRoot string, req *shared.UploadRequest) (string, []strin
oldestFile := "" oldestFile := ""
for i, file := range matches { for i, file := range matches {
if file == root {
continue
}
if fi, err := os.Lstat(file); err == nil { if fi, err := os.Lstat(file); err == nil {
if (fi.Mode() & os.ModeSymlink) == os.ModeSymlink { if (fi.Mode() & os.ModeSymlink) == os.ModeSymlink {
matches[i] = "" matches[i] = ""
@ -129,21 +128,21 @@ func zipLogFiles(storageRoot string, req *shared.UploadRequest) (string, []strin
relative := file[len(root)+1:] relative := file[len(root)+1:]
fw, err := w.Create(relative) fw, err := w.Create(relative)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
src, err := os.Open(file) src, err := os.Open(file)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
defer src.Close() defer src.Close()
if _, err = io.Copy(fw, src); err != nil { if _, err = io.Copy(fw, src); err != nil {
return "", nil, err return "", err
} }
} }
return f.Name(), matches, nil return f.Name(), nil
} }
func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) (*procmeta, error) { func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) (*procmeta, error) {
@ -151,6 +150,7 @@ func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) (
return nil, errors.New("args is empty") return nil, errors.New("args is empty")
} }
foundVersion := req.Version
if req.Version == "latest" { if req.Version == "latest" {
entries, err := os.ReadDir(path.Join(storageRoot, req.Name)) entries, err := os.ReadDir(path.Join(storageRoot, req.Name))
if err != nil { if err != nil {
@ -176,11 +176,11 @@ func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) (
} }
if len(latestVersion) > 0 { if len(latestVersion) > 0 {
req.Version = latestVersion foundVersion = latestVersion
} }
} }
verpath := path.Join(storageRoot, req.Name, req.Version) verpath := path.Join(storageRoot, req.Name, foundVersion)
fi, err := os.Stat(verpath) fi, err := os.Stat(verpath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -215,41 +215,18 @@ func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) (
cmd: cmd, cmd: cmd,
name: req.Name, name: req.Name,
args: req.Args, args: req.Args,
version: req.Version, version: foundVersion,
recover: req.AutoRestart,
verpath: verpath, verpath: verpath,
state: int32(protos.ProcessState_Stopped), state: int32(protos.ProcessState_Stopped),
stdin: stdin, stdin: stdin,
logfile: req.OutputLogFile,
keepLatest: req.Version == "latest",
}, nil }, nil
} }
return nil, errors.New("not found") return nil, errors.New("not found")
} }
func makeLogFilePrefix(meta *procmeta, index int) string {
now := time.Now().UTC()
ext := path.Ext(meta.args[0])
nameonly := path.Base(filepath.ToSlash(meta.args[0]))
if len(ext) > 0 {
nameonly = nameonly[:len(nameonly)-len(ext)]
}
ts := now.Format("2006-01-02T15-04-05")
if index == 0 {
return path.Join(meta.verpath, "logs", fmt.Sprintf("%s_%s", nameonly, ts))
}
return path.Join(meta.verpath, "logs", fmt.Sprintf("%s_%d_%s", nameonly, index, ts))
}
func evaluateExpression(expression string, params map[string]any) (any, error) {
expression = strings.TrimSpace(expression)
expr, err := govaluate.NewEvaluableExpression(expression)
if err != nil {
return 0, err
}
return expr.Evaluate(params)
}
func evaluateArgs(args []string, params map[string]any) ([]string, error) { func evaluateArgs(args []string, params map[string]any) ([]string, error) {
re := regexp.MustCompile(`\$\(\((.*?)\)\)`) re := regexp.MustCompile(`\$\(\((.*?)\)\)`)
@ -307,17 +284,27 @@ func (hc *houstonClient) launch(meta *procmeta) error {
if err != nil { if err != nil {
return err return err
} }
stderr, err := meta.cmd.StderrPipe()
err = os.MkdirAll(path.Join(meta.verpath, "logs"), 0775)
if err != nil { if err != nil {
return err return err
} }
stdReader := func(jobName string, r io.ReadCloser, index int) { logfolder := path.Join(meta.verpath, "logs")
err = os.MkdirAll(logfolder, 0775)
if err != nil {
return err
}
stdReader := func(jobName string, r io.ReadCloser, index int, logfilePath string) {
defer func() { defer func() {
reco := recover() logger.Println("stdReader is terminated :", meta.name)
if reco != nil { if meta.isState(protos.ProcessState_Running) {
logger.Println(reco) hc.operationChan <- &protos.OperationQueryResponse{
Operation: string(shared.Exception),
Args: map[string]string{
"id": fmt.Sprintf("%d", meta.id),
},
}
} }
}() }()
@ -331,90 +318,35 @@ func (hc *houstonClient) launch(meta *procmeta) error {
hc.siblingProcIndex[key] = runningFlags hc.siblingProcIndex[key] = runningFlags
}() }()
defer r.Close()
reader := bufio.NewReader(r)
thisFileSize := 0
logFileIndex := 0
var logWriter func([]byte)
if *logger.UseLogFile {
logFileNamePrefix := makeLogFilePrefix(meta, index)
logFileName := fmt.Sprintf("%s_%d.log", logFileNamePrefix, logFileIndex)
targetFile, err := os.Create(logFileName)
if err != nil {
logger.Println("failed to create log file :", logFileName)
return
}
exef, _ := os.Executable()
var linkPath string
if index == 0 {
linkPath = path.Join(path.Dir(exef), path.Dir(logFileName), meta.name+".log")
} else {
linkPath = path.Join(path.Dir(exef), path.Dir(logFileName), fmt.Sprintf("%s_%d.log", meta.name, index))
}
os.Remove(linkPath)
os.Symlink(path.Base(filepath.ToSlash(targetFile.Name())), linkPath)
defer func() { defer func() {
if targetFile != nil { reco := recover()
targetFile.Close() if reco != nil {
} logger.Println(reco)
}()
logWriter = func(buff []byte) {
for written := 0; written < len(buff); {
n, err := targetFile.Write(buff)
if err != nil {
logger.Println("write log file failed :", logFileName, err)
break
} else {
written += n
thisFileSize += n
}
} }
if thisFileSize > 5*1024*1024 { r.Close()
logFileIndex++
logFileName = fmt.Sprintf("%s_%d.log", logFileNamePrefix, logFileIndex)
nextTargetFile, err := os.Create(logFileName)
if err != nil {
logger.Println("failed to create log file :", logFileName)
} else {
targetFile.Close()
targetFile = nextTargetFile
os.Remove(linkPath)
os.Symlink(path.Base(filepath.ToSlash(targetFile.Name())), linkPath)
thisFileSize = 0
}
}
}
} else {
logWriter = func(buff []byte) {
os.Stdout.Write(buff)
}
}
readingMetric := false
var metricBuffer []byte
defer func() {
logger.Println("stdReader is terminated :", meta.name)
if meta.isState(protos.ProcessState_Running) {
hc.operationChan <- &protos.OperationQueryResponse{
Operation: string(shared.Exception),
Args: map[string]string{
"id": fmt.Sprintf("%d", meta.id),
},
}
}
}() }()
metricExporter := metric.NewPrometheusExport(hc.config.MetricNamespace) metricExporter := metric.NewPrometheusExport(hc.config.MetricNamespace)
defer metricExporter.Shutdown() defer metricExporter.Shutdown()
total := 0
hn, _ := os.Hostname()
var targetFile *os.File
ext := path.Ext(logfilePath)
head := logfilePath[:len(logfilePath)-len(ext)]
reader := bufio.NewReader(r)
readingMetric := false
ext = "." + hn + ext
var metricBuffer []byte
for { for {
if targetFile == nil {
currentFile := head + time.Now().UTC().Format(".2006-01-02.150405") + ext
targetFile, _ = os.OpenFile(currentFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
}
buff, err := reader.ReadBytes('\n') buff, err := reader.ReadBytes('\n')
if err != nil { if err != nil {
logger.Println("ReadBytes at stdReader return err :", err, meta.name) logger.Println("ReadBytes at stdReader return err :", err, meta.name)
@ -458,11 +390,79 @@ func (hc *houstonClient) launch(meta *procmeta) error {
metricBuffer = metricBuffer[:0] metricBuffer = metricBuffer[:0]
} }
} else if targetFile != nil && len(buff) > 0 {
for written := 0; written < len(buff); {
n, err := targetFile.Write(buff[written:])
if err != nil {
logger.Println("write log file failed :", logfilePath, err)
break
} else {
written += n
}
}
total += len(buff)
continue if total > 1024*1024 {
total = 0
targetFile.Close()
targetFile = nil
hc.uploadProcFiles(meta, "logs/*"+ext, true)
}
}
}
} }
logWriter(buff) errReader := func(r io.ReadCloser, logfilePath string) {
defer func() {
reco := recover()
if reco != nil {
logger.Println(reco)
}
}()
defer r.Close()
total := 0
hn, _ := os.Hostname()
var targetFile *os.File
ext := path.Ext(logfilePath)
head := logfilePath[:len(logfilePath)-len(ext)]
reader := bufio.NewReader(r)
ext = "." + hn + ext
for {
if targetFile == nil {
currentFile := head + time.Now().UTC().Format(".2006-01-02.150405") + ext
targetFile, _ = os.OpenFile(currentFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
}
buff, errRead := reader.ReadBytes('\n')
if errRead != nil {
logger.Println("ReadBytes at stdReader return err :", err, meta.name)
break
}
if targetFile != nil && len(buff) > 0 {
for written := 0; written < len(buff); {
n, err := targetFile.Write(buff[written:])
if err != nil {
logger.Println("write log file failed :", logfilePath, err)
break
} else {
written += n
}
}
total += len(buff)
if total > 1024*1024 {
total = 0
targetFile.Close()
targetFile = nil
hc.uploadProcFiles(meta, "logs/*"+ext, true)
}
}
} }
} }
@ -486,14 +486,43 @@ func (hc *houstonClient) launch(meta *procmeta) error {
} }
} }
go stdReader(meta.name, stdout, index) // 자체 환경 변수
customEnv := map[string]string{
"HOUSTON_SIBLIING_INDEX": fmt.Sprintf("%d", index),
"HOUSTON_PROC_TIMESTAMP": time.Now().UTC().Format("2006-01-02T15-04-05"),
}
meta.cmd.Env = append(os.Environ(), fmt.Sprintf("HOUSTON_SIBLIING_INDEX=%d", index)) // 프로세스 환경 변수에 반영
meta.cmd.Env = os.Environ()
for k, v := range customEnv {
meta.cmd.Env = append(meta.cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
// argument 표현식 계산
meta.cmd.Args, err = evaluateArgs(meta.cmd.Args, parseEnv(meta.cmd.Env)) meta.cmd.Args, err = evaluateArgs(meta.cmd.Args, parseEnv(meta.cmd.Env))
if err != nil { if err != nil {
logger.Println("evaluateArgs failed :", err) logger.Println("evaluateArgs failed :", err)
return err return err
} }
// 로그파일에 환경변수 적용
evalfile := os.Expand(meta.logfile, func(n string) string {
v := os.Getenv(n)
if len(v) == 0 {
return customEnv[n]
}
return v
})
if len(evalfile) > 0 {
evalfile = path.Join(logfolder, evalfile)
} else {
evalfile = path.Join(logfolder, path.Base(meta.cmd.Args[0]))
}
go stdReader(meta.name, stdout, index, evalfile+".log")
go errReader(stderr, evalfile+".err")
logger.Println("startChildProcess :", meta.cmd.Args) logger.Println("startChildProcess :", meta.cmd.Args)
err = meta.cmd.Start() err = meta.cmd.Start()
@ -605,54 +634,52 @@ func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest,
return nil return nil
} }
func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error { func (hc *houstonClient) uploadProcFiles(child *procmeta, filter string, deleteAfterUpload bool) {
logger.Println("uploadFiles req :", *req)
for _, child := range hc.childProcs {
if child.version == req.Version && child.name == req.Name {
logger.Println("uploadFiles found :", child.version, child.name) logger.Println("uploadFiles found :", child.version, child.name)
root, matches := findMatchFiles(hc.config.StorageRoot, child.name, child.version, filter)
go func() { go func(deleteAfterUpload bool, root string, matches []string) {
zipFile, srcFiles, err := zipLogFiles(hc.config.StorageRoot, req) zipFile, err := zipCompressFiles(root, matches)
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 { if err == nil && len(zipFile) > 0 {
if err = hc.uploadZipLogFile(zipFile, child.name, child.version); err == nil { if err = hc.uploadZipLogFile(zipFile, child.name, child.version); err == nil {
// 마지막거 빼고 삭제 if deleteAfterUpload {
if req.DeleteAfterUploaded == "true" { for _, fn := range matches {
for i := 0; i < len(srcFiles)-1; i++ { if len(fn) > 0 {
os.Remove(srcFiles[i]) os.Remove(fn)
}
} else {
sort.StringSlice(srcFiles).Sort()
for i := 0; i < len(srcFiles)-1; i++ {
if len(srcFiles[i]) > 0 {
os.Remove(srcFiles[i])
} }
} }
} }
} else { } else {
logger.Println("uploadZipLogFile failed :", err) logger.Println("uploadZipLogFile failed :", err)
} }
os.Remove(zipFile)
} else if err != nil { } else if err != nil {
logger.Println("zipLogFiles failed :", err) logger.Println("zipLogFiles failed :", err)
} }
}() }(deleteAfterUpload, root, matches)
}
func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error {
logger.Println("uploadFiles req :", *req)
for _, child := range hc.childProcs {
if child.version == req.Version && child.name == req.Name {
hc.uploadProcFiles(child, req.Filter, false)
return nil return nil
} }
} }
// 실행 중이 아닌 폴더에서도 대상을 찾는다 // 실행 중이 아닌 폴더에서도 대상을 찾는다
// 전체 파일을 대상으로 // 전체 파일을 대상으로
zipFile, srcFiles, err := zipLogFiles(hc.config.StorageRoot, req) root, matches := findMatchFiles(hc.config.StorageRoot, req.Name, req.Version, req.Filter)
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 { zipFile, err := zipCompressFiles(root, matches)
if err == nil && len(zipFile) > 0 && len(zipFile) > 0 {
if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err == nil { if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err == nil {
// 마지막거 빼고 삭제 for _, fn := range matches {
sort.StringSlice(srcFiles).Sort() if len(fn) > 0 {
for i := 0; i < len(srcFiles)-1; i++ { os.Remove(fn)
if len(srcFiles[i]) > 0 {
os.Remove(srcFiles[i])
} }
} }
os.Remove(zipFile)
} else { } else {
logger.Println("uploadZipLogFile failed :", err) logger.Println("uploadZipLogFile failed :", err)
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"repositories.action2quare.com/ayo/gocommon/flagx" "repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/client" "repositories.action2quare.com/ayo/houston/client"
"net/http" "net/http"
@ -23,7 +24,14 @@ func main() {
http.Handle("/metrics", promhttp.Handler()) http.Handle("/metrics", promhttp.Handler())
server := &http.Server{Addr: ":9100", Handler: nil} server := &http.Server{Addr: ":9100", Handler: nil}
go server.ListenAndServe()
go func() {
logger.Println("listen /metrics")
err := server.ListenAndServe()
if err != nil {
logger.Error(err)
}
}()
hc.Start() hc.Start()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

View File

@ -29,6 +29,8 @@ const (
) )
func (h *houstonHandler) GetAgents(w http.ResponseWriter, r *http.Request) { func (h *houstonHandler) GetAgents(w http.ResponseWriter, r *http.Request) {
logger.Println("GetAgents")
enc := json.NewEncoder(w) enc := json.NewEncoder(w)
allHosts := h.Operation().Hosts() allHosts := h.Operation().Hosts()
enc.Encode(allHosts) enc.Encode(allHosts)
@ -45,6 +47,8 @@ func readTagsFromFile(paths ...string) string {
} }
func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request) { func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request) {
logger.Println("GetDeploySources")
files, err := os.ReadDir(h.deployPath) files, err := os.ReadDir(h.deployPath)
if err != nil { if err != nil {
logger.Println(err) logger.Println(err)
@ -100,6 +104,7 @@ func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Reque
// <input type="submit" value="업로드"> // <input type="submit" value="업로드">
// </form> // </form>
file, header, err := r.FormFile("file") file, header, err := r.FormFile("file")
if err != nil { if err != nil {
logger.Println(err) logger.Println(err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -118,6 +123,8 @@ func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Reque
name := r.FormValue("name") name := r.FormValue("name")
ext := path.Ext(header.Filename) ext := path.Ext(header.Filename)
logger.Println("UploadDeploySource :", name, version)
var filename string var filename string
if version == "config" { if version == "config" {
@ -154,6 +161,7 @@ func (h *houstonHandler) DeleteDeploySource(w http.ResponseWriter, r *http.Reque
version := r.FormValue("version") version := r.FormValue("version")
name := r.FormValue("name") name := r.FormValue("name")
logger.Println("DeleteDeploySource :", name, version)
if len(version) == 0 || len(name) == 0 { if len(version) == 0 || len(name) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -161,6 +169,8 @@ func (h *houstonHandler) DeleteDeploySource(w http.ResponseWriter, r *http.Reque
// deploys 폴더는 파일시스템 서비스이므로 다운로드 가능 // deploys 폴더는 파일시스템 서비스이므로 다운로드 가능
targetpath := path.Join(h.deployPath, name, version) targetpath := path.Join(h.deployPath, name, version)
logger.Println("DeleteDeploySource :", name, version, targetpath)
if err := os.RemoveAll(targetpath); err != nil { if err := os.RemoveAll(targetpath); err != nil {
logger.Println("deleteDeploySource failed :", err) logger.Println("deleteDeploySource failed :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -220,6 +230,8 @@ func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
version := r.FormValue("version") version := r.FormValue("version")
traws := r.FormValue("targets") traws := r.FormValue("targets")
logger.Println("Deploy :", name, version, traws)
var targets []string var targets []string
if len(traws) > 0 { if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil { if err := json.Unmarshal([]byte(traws), &targets); err != nil {
@ -292,6 +304,8 @@ func (h *houstonHandler) Undeploy(w http.ResponseWriter, r *http.Request) {
version := r.FormValue("version") version := r.FormValue("version")
traws := r.FormValue("targets") traws := r.FormValue("targets")
logger.Println("Undeploy :", name, version, traws)
var targets []string var targets []string
if len(traws) > 0 { if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil { if err := json.Unmarshal([]byte(traws), &targets); err != nil {
@ -328,6 +342,8 @@ func (h *houstonHandler) StartProcess(w http.ResponseWriter, r *http.Request) {
argsline := r.FormValue("args") argsline := r.FormValue("args")
traws := r.FormValue("targets") traws := r.FormValue("targets")
logger.Println("StartProcess :", name, version, argsline, traws)
var targets []string var targets []string
if len(traws) > 0 { if len(traws) > 0 {
if err := json.Unmarshal([]byte(traws), &targets); err != nil { if err := json.Unmarshal([]byte(traws), &targets); err != nil {
@ -380,6 +396,8 @@ func (h *houstonHandler) StopProcess(w http.ResponseWriter, r *http.Request) {
// <input type="submit" value="업로드"> // <input type="submit" value="업로드">
// </form> // </form>
name := r.FormValue("name") name := r.FormValue("name")
logger.Println("StopProcess :", name)
if len(name) == 0 { if len(name) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -424,6 +442,8 @@ func (h *houstonHandler) RestartProcess(w http.ResponseWriter, r *http.Request)
pidstr := r.FormValue("pid") pidstr := r.FormValue("pid")
target := r.FormValue("target") target := r.FormValue("target")
name := r.FormValue("name") name := r.FormValue("name")
logger.Println("RestartProcess :", name, pidstr, target)
if len(target) == 0 || len(pidstr) == 0 || len(name) == 0 { if len(target) == 0 || len(pidstr) == 0 || len(name) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -468,6 +488,8 @@ func (h *houstonHandler) UploadLogs(w http.ResponseWriter, r *http.Request) {
// <input type="submit" value="업로드"> // <input type="submit" value="업로드">
// </form> // </form>
name := r.FormValue("name") name := r.FormValue("name")
logger.Println("UploadLogs :", name)
if len(name) == 0 { if len(name) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -501,6 +523,8 @@ func (h *houstonHandler) GetLogFileLinks(w http.ResponseWriter, r *http.Request)
// </form> // </form>
name := r.FormValue("name") name := r.FormValue("name")
version := r.FormValue("version") version := r.FormValue("version")
logger.Println("GetLogFileLinks :", name, version)
if len(name) == 0 || len(version) == 0 { if len(name) == 0 || len(version) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return

View File

@ -1,6 +1,7 @@
package server package server
import ( import (
"archive/zip"
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -28,6 +29,7 @@ type houstonHandler struct {
methods map[string]reflect.Method methods map[string]reflect.Method
deployPath string deployPath string
downloadPath string downloadPath string
maingateApiToken string
} }
func NewHoustonHandler() HoustonServerWithHandler { func NewHoustonHandler() HoustonServerWithHandler {
@ -42,6 +44,7 @@ func NewHoustonHandler() HoustonServerWithHandler {
return &houstonHandler{ return &houstonHandler{
HoustonServer: NewServer(), HoustonServer: NewServer(),
methods: methods, methods: methods,
maingateApiToken: loadServerConfig().MaingateApiToken,
} }
} }
@ -126,8 +129,7 @@ func (h *houstonHandler) RegisterHandlers(serveMux gocommon.ServerMuxInterface,
defer func() { defer func() {
s := recover() s := recover()
if s != nil { if s != nil {
logger.Println(s) logger.Error(s)
debug.PrintStack()
} }
io.Copy(io.Discard, r.Body) io.Copy(io.Discard, r.Body)
r.Body.Close() r.Body.Close()
@ -138,11 +140,26 @@ func (h *houstonHandler) RegisterHandlers(serveMux gocommon.ServerMuxInterface,
filename := r.Header.Get("Houston-Service-Filename") filename := r.Header.Get("Houston-Service-Filename")
dir := path.Join(h.downloadPath, name, version) dir := path.Join(h.downloadPath, name, version)
if err := os.MkdirAll(dir, 0775); err == nil { if err := os.MkdirAll(dir, 0775); err == nil {
file, _ := os.Create(path.Join(dir, filename)) filepath := path.Join(dir, filename)
if file != nil { localfile, _ := os.Create(filepath)
defer file.Close() logger.Println("file uploaded :", localfile)
if _, err = io.Copy(file, r.Body); err != nil { if localfile != nil {
if _, err = io.Copy(localfile, r.Body); err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
} else {
localfile.Close()
localfile, _ = os.Open(filepath)
if strings.HasSuffix(filename, ".zip") {
stat, _ := localfile.Stat()
zipreader, _ := zip.NewReader(localfile, stat.Size())
for _, f := range zipreader.File {
file, _ := os.Create(path.Join(dir, f.Name))
comp, _ := f.Open()
io.Copy(file, comp)
file.Close()
}
os.Remove(filepath)
}
} }
} else { } else {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -172,6 +189,7 @@ func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.Body.Close() r.Body.Close()
}() }()
// TODO : 구글 인증까지 붙인 후에 주석 제거
// var userinfo map[string]any // var userinfo map[string]any
// if !*noauth && (*authtype == "on" || *authtype == "both") { // if !*noauth && (*authtype == "on" || *authtype == "both") {
// authheader := r.Header.Get("Authorization") // authheader := r.Header.Get("Authorization")

View File

@ -22,6 +22,7 @@ type HoustonServer interface {
type serverConfig struct { type serverConfig struct {
GrpcPort int `json:"grpc_port"` GrpcPort int `json:"grpc_port"`
StorageRoot string `json:"storage_path"` StorageRoot string `json:"storage_path"`
MaingateApiToken string `json:"maingate_api_token"`
} }
type DeployRequest struct { type DeployRequest struct {

View File

@ -30,6 +30,8 @@ type StartProcessRequest struct {
Name string Name string
Version string Version string
Args []string Args []string
AutoRestart bool
OutputLogFile string
} }
type StopProcessRequest struct { type StopProcessRequest struct {