Compare commits

..

84 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
3ab055008c 인증 일단 막음 2024-08-13 17:30:33 +09:00
71e80d2908 모듈 업데이트 2024-08-05 10:23:42 +09:00
12a0f9d2b1 argument 수식 처리 2024-08-05 10:20:41 +09:00
016f459252 houston server에 재접속시 autorun이 다시 불리는 문제 수정 2024-07-30 11:19:25 +09:00
030fa658f5 모듈 업데이트 2024-07-29 17:50:33 +09:00
3c96921703 autorun 포맷 오류시 대응 2024-07-25 16:10:09 +09:00
6f444e0187 config file service 방식 변경 2024-07-24 13:13:46 +09:00
3eaad85453 Merge branch 'master' of https://repositories.action2quare.com/ayo/houston 2024-07-10 14:55:49 +09:00
401cfa8b84 noauth대신 authtype 파라미터 사용 2024-07-10 14:55:47 +09:00
5e4799ff55 모듈 업데이트 2024-07-08 15:12:54 +09:00
86e14fbd23 logger.UseLogFile을 houston의 자식 프로세스에도 적용 2024-07-08 15:10:32 +09:00
fb3886e2e4 하위 프로세스에 환경변수 전달 2024-06-12 14:32:32 +09:00
95d6741389 config 로딩을 gocommon사용하도록 변경 2024-06-06 13:18:50 +09:00
fdb534c5e0 프로세스 정지시 로그 자동 업로드 2024-06-05 17:26:28 +09:00
dfcb78b70c 로그파일에 호스트이름 추가 2024-06-05 17:26:17 +09:00
4e928f3426 autorun 타이밍 변경. Query를 먼저 보내고 해야 함 2024-06-05 15:33:09 +09:00
d546f2340d config handler 추가 2024-06-05 13:41:43 +09:00
bad563dce7 autorun 추가 2024-06-05 13:40:10 +09:00
62494fb052 주소 잘못 보냄 2024-02-15 17:32:24 +09:00
eb86c0a073 houston을 prometheus target 파일로 출력 2024-02-15 17:04:28 +09:00
9d4718592d houston에 constlabel 설정 가능 2024-02-15 12:17:22 +09:00
dcc94d2609 homepath 설정 2024-02-13 15:12:18 +09:00
f45558483e 버전 비교 로직 제거 2024-02-07 17:03:19 +09:00
01940222b6 모듈 업데이트 2024-01-22 17:17:25 +09:00
d03f02a44f 윈도우에서 로그 폴더 위치 비정상 수정 2023-12-08 18:30:14 +09:00
27dd9226e9 윈도우에서 동작 안하는 문제 수정 2023-12-08 16:47:14 +09:00
00fa08a739 자식 프로세스 강종 후 나도 바로 종료 2023-12-05 18:56:14 +09:00
ccfa9e4be3 houston 종료시 kill signal 보냄 2023-12-05 18:54:42 +09:00
68a5876fd8 index와 pid 출력 2023-12-02 22:04:27 +09:00
33181f1717 모듈 업데이트 2023-12-02 21:31:27 +09:00
6e0bd71c80 중지할 프로세스를 동시에 처리 2023-12-01 19:34:35 +09:00
75992472f3 로그 추가 2023-12-01 10:38:02 +09:00
ee1459d760 metric 등록 오류 수정 2023-12-01 02:34:24 +09:00
3913c6cdcf 모듈 업데이트 2023-11-30 14:48:54 +09:00
2744a0a990 metrix exporter를 프로세스 단위로 내림 2023-11-30 14:47:11 +09:00
1d3266bbaf 동일 실행파일을 여러개 실행시켰을 때 구분해서 affinity 설정 2023-11-30 14:17:27 +09:00
023a2a5194 환경변수를 expand할 수 있음 2023-11-29 18:46:10 +09:00
6eaa856688 모듈 업데이트 2023-11-29 17:35:58 +09:00
afa270c9a0 메트릭 사용방법 변경 2023-11-28 22:31:42 +09:00
ad3382db89 모듈 업데이트 2023-11-28 00:57:43 +09:00
e1e2f3c087 endian 수정 2023-11-27 23:27:53 +09:00
cf4b458a4b metric namespace 설정 추가 2023-11-26 18:02:02 +09:00
72b88b194f 로그 변경 2023-11-25 22:16:12 +09:00
cfd6e23384 실행인자 cr 제거 2023-11-25 22:07:24 +09:00
6416c27230 메트릭 로그 정리 2023-11-25 21:06:52 +09:00
2ae42d0b08 setcap 다시 제거 -> iptables로 정리 2023-11-24 17:19:26 +09:00
d4cd792950 setcap 부활 2023-11-24 16:02:31 +09:00
84543f9a31 심볼링크 로그 파일은 빼고 삭제 2023-11-24 01:52:52 +09:00
1a404e5361 프로세스 종료시 알림 2023-11-24 00:19:17 +09:00
d18fe3e3a1 메트릭 중복 등록시 크래시 막음 2023-11-23 14:55:37 +09:00
b2ff0e8ffc set_permission 리눅스&클라이언트만 2023-11-23 12:51:33 +09:00
3208eba280 소켓 바인딩 권한 부여 2023-11-23 12:36:57 +09:00
882d35d604 모듈 업데이트 2023-11-16 19:57:36 +09:00
ba72262d50 custom metric 추가 2023-11-16 19:47:30 +09:00
8d764c8d18 최신 로그파일에 심볼릭 링크 생성 2023-11-14 11:22:23 +09:00
299a0a2bd3 빌드 태그는 main만 2023-11-14 10:29:33 +09:00
5c765fe32f 최신 로그 파일의 심볼릭 링크 생성 2023-11-13 17:29:22 +09:00
394466e216 로그 저장 로직 단순화 2023-11-13 16:43:56 +09:00
61d2fbf709 server 태그는 떼고 client 태그로만 빌드 구분 2023-11-13 12:04:27 +09:00
d43b83e761 client와 server 빌드를 분리하고 client에는 node_exporter 통합 2023-11-13 11:35:52 +09:00
432cc68024 절대 경로로 child process 실행 2023-11-08 09:21:09 +09:00
22148f8ae7 마지막 배포 상태 항상 유지 + timestamp 2023-10-26 13:53:36 +09:00
282ef95ab0 로그 다시 제거 2023-10-26 10:42:48 +09:00
5b0b977a39 로그 추가 2023-10-26 10:40:33 +09:00
2985e0fdaf GetDeployingProgress 추가 2023-10-24 21:18:28 +09:00
b8c1e97ab8 chunksize 계산 2023-10-24 20:57:41 +09:00
c9fecf55de 로그 추가 2023-10-24 20:39:12 +09:00
15ab1c9e7c 로그 추가 2023-10-24 20:18:49 +09:00
b6b8aa0794 deployprogress 2023-10-24 20:08:48 +09:00
64ca01c3a7 모듈 업데이트 2023-09-19 18:52:24 +09:00
5798e77a6c 모듈 업데이트 2023-09-11 12:49:05 +09:00
c5e2bc203a 모듈 업데이트 2023-09-08 15:28:13 +09:00
22 changed files with 1869 additions and 740 deletions

1
.gitignore vendored
View File

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

View File

@ -22,6 +22,8 @@ import (
"time" "time"
"unsafe" "unsafe"
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/shared" "repositories.action2quare.com/ayo/houston/shared"
"repositories.action2quare.com/ayo/houston/shared/protos" "repositories.action2quare.com/ayo/houston/shared/protos"
@ -30,100 +32,116 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
) )
type runcommand struct {
Exec string `json:"exec"`
Args []string `json:"args"`
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 {
GrpcAddress string `json:"grpc_server_address"` GrpcAddress string `json:"grpc_server_address"`
HttpAddress string `json:"http_server_address"` HttpAddress string `json:"http_server_address"`
StorageRoot string `json:"storage_path"` StorageRoot string `json:"storage_path"`
MetricNamespace string `json:"metric_namespace"`
ConstLabels map[string]string `json:"metric_const_labels"`
Autorun map[string]runcommand `json:"autorun"`
}
var autorun = flagx.String("autorun", "", "")
type outerconfig struct {
Houston *struct {
Client clientConfig `json:"client"`
} `json:"houston"`
} }
func loadClientConfig() (clientConfig, error) { func loadClientConfig() (clientConfig, error) {
configFile, err := os.Open("config.json") var oc outerconfig
if err != nil { err := gocommon.LoadConfig[outerconfig](&oc)
return clientConfig{}, err
}
defer configFile.Close()
var config struct {
Houston *struct {
Client clientConfig `json:"client"`
} `json:"houston"`
}
dec := json.NewDecoder(configFile)
err = dec.Decode(&config)
if err != nil { if err != nil {
logger.Println(err)
return clientConfig{}, err return clientConfig{}, err
} }
if config.Houston == nil { return oc.Houston.Client, nil
return clientConfig{}, errors.New(`"houston" object is missing in config.json`)
}
return config.Houston.Client, nil
} }
type HoustonClient interface { type HoustonClient interface {
SetReportMetrics(map[string]float32)
Shutdown() Shutdown()
Start() Start()
} }
type bufferStack struct { var seq = int32(1)
pool [5][]byte
cursor int32
}
func (bs *bufferStack) pop() []byte {
pos := atomic.LoadInt32(&bs.cursor)
for !atomic.CompareAndSwapInt32(&bs.cursor, pos, pos+1) {
pos = atomic.LoadInt32(&bs.cursor)
}
defer func() {
bs.pool[pos] = nil
}()
curbuf := bs.pool[pos]
if curbuf == nil {
curbuf = make([]byte, 1024)
}
return curbuf
}
func (bs *bufferStack) push(x []byte) {
pos := atomic.AddInt32(&bs.cursor, -1)
bs.pool[pos] = x
}
type procmeta struct { type procmeta struct {
cmd *exec.Cmd id int32
name string cmd *exec.Cmd
version string name string
state protos.ProcessState args []string
stdin io.WriteCloser version string
logUploadChan chan *shared.UploadRequest verpath string
buffers bufferStack recover bool
state int32
stdin io.WriteCloser
logfile string
keepLatest bool
}
func (pm *procmeta) isState(s protos.ProcessState) bool {
return atomic.LoadInt32(&pm.state) == int32(s)
}
func (pm *procmeta) getState() protos.ProcessState {
return protos.ProcessState(atomic.LoadInt32(&pm.state))
}
func (pm *procmeta) setState(s protos.ProcessState) {
atomic.StoreInt32(&pm.state, int32(s))
} }
type houstonClient struct { type houstonClient struct {
childProcs []*procmeta childProcs []*procmeta
extraMetrics unsafe.Pointer // map[string]float32 extraMetrics unsafe.Pointer // map[string]float32
deploys map[string][]*protos.VersionAndArgs deploys map[string][]*protos.VersionAndArgs
shutdownFunc context.CancelFunc shutdownFunc context.CancelFunc
ctx context.Context ctx context.Context
operationChan chan *protos.OperationQueryResponse operationChan chan *protos.OperationQueryResponse
exitChan chan *exec.Cmd exitChan chan *exec.Cmd
clientChan chan *grpc.ClientConn clientChan chan *grpc.ClientConn
timestamp string timestamp string
wg sync.WaitGroup wg sync.WaitGroup
config clientConfig config clientConfig
version string version string
standalone bool standalone bool
siblingProcIndex map[string]uint64
} }
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)
logger.Println("operation receive :", argval.Type().Name(), src)
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() {
continue continue
@ -135,32 +153,47 @@ 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)
} }
} }
logger.Println("operation receive :", argval.Elem().Type().Name(), *val)
} }
func gatherDeployedPrograms(storageRoot, name string) []*protos.VersionAndArgs { type version_args_ts struct {
var rawvers []*protos.VersionAndArgs *protos.VersionAndArgs
modTime time.Time
}
func gatherDeployedPrograms(storageRoot, name string) (out []*protos.VersionAndArgs) {
var rawvers []version_args_ts
targetPath := path.Join(storageRoot, name) targetPath := path.Join(storageRoot, name)
if vers, err := os.ReadDir(targetPath); err == nil { if vers, err := os.ReadDir(targetPath); err == nil {
for _, ver := range vers { for _, ver := range vers {
if ver.IsDir() { if ver.IsDir() {
fi, _ := ver.Info()
args := lastExecutionArgs(path.Join(targetPath, ver.Name())) args := lastExecutionArgs(path.Join(targetPath, ver.Name()))
rawvers = append(rawvers, &protos.VersionAndArgs{ rawvers = append(rawvers, version_args_ts{
Version: ver.Name(), VersionAndArgs: &protos.VersionAndArgs{
Args: args, Version: ver.Name(),
Args: args,
},
modTime: fi.ModTime(),
}) })
} }
} }
} }
sort.Slice(rawvers, func(i, j int) bool { sort.Slice(rawvers, func(i, j int) bool {
leftParsed := shared.ParseVersionString(rawvers[i].Version) return rawvers[i].modTime.After(rawvers[j].modTime)
rightParsed := shared.ParseVersionString(rawvers[j].Version)
return shared.CompareVersionString(leftParsed, rightParsed) < 0
}) })
return rawvers
for _, v := range rawvers {
out = append(out, v.VersionAndArgs)
}
return
} }
func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryRequest { func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryRequest {
@ -170,13 +203,17 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
var selfname string var selfname string
var selfargs []string var selfargs []string
if hc.standalone { if hc.standalone {
selfname = path.Base(os.Args[0]) selfname = path.Base(filepath.ToSlash(os.Args[0]))
selfargs = os.Args[1:] selfargs = os.Args[1:]
} else { } else {
selfname = "houston" selfname = "houston"
selfargs = []string{} selfargs = []string{}
} }
if len(path.Ext(selfname)) > 0 {
selfname = selfname[:len(selfname)-len(path.Ext(selfname))]
}
procs = append(procs, &protos.ProcessDescription{ procs = append(procs, &protos.ProcessDescription{
Name: selfname, Name: selfname,
Args: selfargs, Args: selfargs,
@ -196,7 +233,7 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
Name: child.name, Name: child.name,
Args: child.cmd.Args, Args: child.cmd.Args,
Version: child.version, Version: child.version,
State: child.state, State: child.getState(),
Pid: int32(child.cmd.Process.Pid), Pid: int32(child.cmd.Process.Pid),
}) })
} }
@ -210,9 +247,11 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
hn, _ := os.Hostname() hn, _ := os.Hostname()
return &protos.OperationQueryRequest{ return &protos.OperationQueryRequest{
Hostname: hn, Hostname: hn,
Procs: procs, PublicIp: os.Getenv("PUBIP"),
Deploys: deploys, PrivateIp: os.Getenv("PRVIP"),
Procs: procs,
Deploys: deploys,
} }
} }
@ -271,13 +310,14 @@ func NewClient(standalone bool) (HoustonClient, error) {
} }
hc := &houstonClient{ hc := &houstonClient{
config: clientConfig, config: clientConfig,
clientChan: make(chan *grpc.ClientConn), clientChan: make(chan *grpc.ClientConn),
extraMetrics: unsafe.Pointer(&map[string]float32{}), extraMetrics: unsafe.Pointer(&map[string]float32{}),
deploys: deploys, deploys: deploys,
timestamp: exefi.ModTime().String(), timestamp: exefi.ModTime().String(),
version: string(ver), version: string(ver),
standalone: standalone, standalone: standalone,
siblingProcIndex: make(map[string]uint64),
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -286,6 +326,7 @@ func NewClient(standalone bool) (HoustonClient, error) {
operationChan := make(chan *protos.OperationQueryResponse, 10) operationChan := make(chan *protos.OperationQueryResponse, 10)
hc.wg.Add(1) hc.wg.Add(1)
// autorun 처리
go func() { go func() {
defer hc.wg.Done() defer hc.wg.Done()
@ -307,12 +348,13 @@ func NewClient(standalone bool) (HoustonClient, error) {
case newClient := <-hc.clientChan: case newClient := <-hc.clientChan:
op = protos.NewOperationClient(newClient) op = protos.NewOperationClient(newClient)
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
case exited := <-exitChan: case exited := <-exitChan:
var newprocs []*procmeta var newprocs []*procmeta
for _, proc := range hc.childProcs { for _, proc := range hc.childProcs {
if proc.cmd == exited { if proc.cmd == exited {
if proc.state == protos.ProcessState_Running || proc.state == protos.ProcessState_Restart { if proc.isState(protos.ProcessState_Running) || proc.isState(protos.ProcessState_Restart) {
go func(proc *procmeta) { go func(proc *procmeta) {
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)
@ -320,12 +362,20 @@ func NewClient(standalone bool) (HoustonClient, error) {
proc.cmd.Wait() proc.cmd.Wait()
proc.cmd.Process.Release() proc.cmd.Process.Release()
if proc.state == protos.ProcessState_Restart { if proc.isState(protos.ProcessState_Restart) {
hc.startChildProcess(&shared.StartProcessRequest{ if proc.keepLatest {
proc.version = "latest"
}
if err := hc.startChildProcess(&shared.StartProcessRequest{
Version: proc.version, Version: proc.version,
Name: proc.name, Name: proc.name,
Args: proc.cmd.Args, Args: proc.args,
}, op) }); err != nil {
logger.ErrorWithCallStack(err)
} else {
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
}
} }
}(proc) }(proc)
} }
@ -337,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{
@ -359,18 +413,45 @@ func NewClient(standalone bool) (HoustonClient, error) {
logger.Println(err) logger.Println(err)
} }
} else { } else {
if err := hc.deploy(&dr); err == nil { hn, _ := os.Hostname()
if err := hc.deploy(&dr, func(dp *protos.DeployingProgress) {
dp.Hostname = hn
dp.Name = dr.Name
dp.Version = dr.Version
op.ReportDeployingProgress(ctx, dp)
}); err == nil {
prog := gatherDeployedPrograms(hc.config.StorageRoot, dr.Name) prog := gatherDeployedPrograms(hc.config.StorageRoot, dr.Name)
hc.deploys[dr.Name] = prog hc.deploys[dr.Name] = prog
op.Refresh(ctx, hc.makeOperationQueryRequest()) op.Refresh(ctx, hc.makeOperationQueryRequest())
op.ReportDeployingProgress(ctx, &protos.DeployingProgress{
Hostname: hn,
Name: dr.Name,
Version: dr.Version,
State: "success",
Progress: 0,
Total: 0,
})
} else { } else {
logger.Println(err) logger.Println(err)
op.ReportDeployingProgress(ctx, &protos.DeployingProgress{
Hostname: hn,
Name: dr.Name,
Version: dr.Version,
State: "fail:" + err.Error(),
Progress: 0,
Total: 0,
})
} }
} }
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)
@ -387,13 +468,19 @@ 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)
if err := hc.startChildProcess(&sr, op); err != nil { logger.Println("args :", sr)
logger.Println(err)
if err := hc.startChildProcess(&sr); err != nil {
logger.ErrorWithCallStack(err)
} else {
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
} }
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)
} }
@ -401,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)
} }
@ -408,15 +497,69 @@ 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)
} }
case shared.Exception:
idstr := resp.Args["id"]
id64, _ := strconv.ParseInt(idstr, 10, 0)
id := int32(id64)
var found *procmeta
hc.childProcs = gocommon.ShrinkSlice(hc.childProcs, func(e *procmeta) bool {
if e.id == id {
found = e
return true
}
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())
}
} }
} }
} }
}() }()
hc.shutdownFunc = cancel hc.shutdownFunc = func() {
// child process 강제 종료
for _, procmeta := range hc.childProcs {
if procmeta.cmd != nil && procmeta.cmd.Process != nil {
procmeta.cmd.Process.Signal(os.Kill)
}
}
time.Sleep(time.Second)
cancel()
}
hc.exitChan = exitChan hc.exitChan = exitChan
hc.ctx = ctx hc.ctx = ctx
hc.operationChan = operationChan hc.operationChan = operationChan
@ -432,7 +575,7 @@ func (hc *houstonClient) Start() {
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)
proc.state = protos.ProcessState_Stopping proc.setState(protos.ProcessState_Stopping)
} }
} }
@ -455,6 +598,40 @@ func (hc *houstonClient) Start() {
reconnCount := 0 reconnCount := 0
time.Sleep(time.Second) time.Sleep(time.Second)
if autorun != nil && len(*autorun) > 0 {
hascount := strings.Split(*autorun, "/")
var service string
count := 1
if len(hascount) > 1 {
service = hascount[0]
if len(hascount[1]) > 0 {
count, _ = strconv.Atoi(hascount[1])
}
} else {
service = *autorun
}
if cmd, ok := hc.config.Autorun[service]; ok {
// service 서비스
for i := 0; i < count; i++ {
sr := shared.StartProcessRequest{
Name: service,
Version: cmd.Version,
Args: append([]string{cmd.Exec}, cmd.Args...),
AutoRestart: cmd.AutoRestart,
OutputLogFile: cmd.OutputLogFile,
}
if err := hc.startChildProcess(&sr); err != nil {
logger.Println("startChildProcess failed by autorun :", err)
logger.ErrorWithCallStack(err)
} else {
logger.Println("autorun success :", sr)
}
}
}
}
for { for {
select { select {
case <-hc.ctx.Done(): case <-hc.ctx.Done():
@ -467,10 +644,15 @@ func (hc *houstonClient) Start() {
} }
reconnCount++ reconnCount++
var err error
dialContext, cancelDial := context.WithTimeout(context.Background(), 15*time.Second) dialContext, cancelDial := context.WithTimeout(context.Background(), 15*time.Second)
client, _ = grpc.DialContext(dialContext, hc.config.GrpcAddress, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())) client, err = grpc.DialContext(dialContext, hc.config.GrpcAddress, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()))
cancelDial() cancelDial()
if client != nil {
if err != nil {
logger.Println("grpc.DialContext returns err :", err)
} else if client != nil {
reconnCount = 0 reconnCount = 0
logger.Println("grpc.DialContext succeeded") logger.Println("grpc.DialContext succeeded")
hc.clientChan <- client hc.clientChan <- client
@ -480,8 +662,7 @@ func (hc *houstonClient) Start() {
if client != nil { if client != nil {
err := hc.checkOperation(client) err := hc.checkOperation(client)
if err != nil { if err != nil {
logger.Println("hc.checkUpdate failed :", err) logger.Println("grpc.DialContext hc.checkOperation failed :", err)
client = nil client = nil
} }
} }
@ -522,7 +703,3 @@ func (hc *houstonClient) checkOperation(client *grpc.ClientConn) error {
hc.operationChan <- update hc.operationChan <- update
} }
} }
func (hc *houstonClient) SetReportMetrics(extra map[string]float32) {
atomic.StorePointer(&hc.extraMetrics, unsafe.Pointer(&extra))
}

26
client/client_linux.go Normal file
View File

@ -0,0 +1,26 @@
//go:build linux
package client
import (
"golang.org/x/sys/unix"
"repositories.action2quare.com/ayo/gocommon/logger"
)
func set_affinity(pid int, cpu int) {
var cpuset unix.CPUSet
err := unix.SchedGetaffinity(pid, &cpuset)
if err != nil {
logger.Println("SchedGetaffinity failed :", err)
}
count := cpuset.Count()
cpuset.Zero()
cpuset.Set(cpu % count)
err = unix.SchedSetaffinity(pid, &cpuset)
if err != nil {
logger.Println("SchedSetaffinity failed :", err)
}
}

41
client/client_misc.go Normal file
View File

@ -0,0 +1,41 @@
//go:build !linux
package client
func set_affinity(pid int, cpu int) {
}
// package main
// import (
// "fmt"
// "syscall"
// "time"
// "unsafe"
// )
// func main() {
// var mask uintptr
// // Get the current CPU affinity of the process
// if _, _, err := syscall.RawSyscall(syscall.SYS_SCHED_GETAFFINITY, 0, uintptr(unsafe.Sizeof(mask)), uintptr(unsafe.Pointer(&mask))); err != 0 {
// fmt.Println("Failed to get CPU affinity:", err)
// return
// }
// fmt.Println("Current CPU affinity:", mask)
// // Set the new CPU affinity
// mask = 3
// if _, _, err := syscall.RawSyscall(syscall.SYS_SCHED_SETAFFINITY, 0, uintptr(unsafe.Sizeof(mask)), uintptr(unsafe.Pointer(&mask))); err != 0 {
// fmt.Println("Failed to set CPU affinity:", err)
// return
// }
// fmt.Println("New CPU affinity:", mask)
// // some code
// for {
// println("Hello, World!")
// time.Sleep(1 * time.Second)
// }
// }

View File

@ -3,6 +3,8 @@ package client
import ( import (
"archive/tar" "archive/tar"
"archive/zip" "archive/zip"
"crypto/md5"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -11,17 +13,37 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/shared" "repositories.action2quare.com/ayo/houston/shared"
"repositories.action2quare.com/ayo/houston/shared/protos"
"golang.org/x/text/encoding/korean" "golang.org/x/text/encoding/korean"
"golang.org/x/text/transform" "golang.org/x/text/transform"
) )
func download(dir string, urlpath string, accessToken string) (target string, err error) { func pof2(x int64, min int64) (out int64) {
out = 1
org := x
for (x >> 1) > 0 {
out = out << 1
x = x >> 1
}
if org > out {
out = out << 1
}
if out < min {
out = min
}
return
}
func download(dir string, urlpath string, accessToken string, cb func(int64, int64)) (target string, err error) {
logger.Println("start downloading", dir, urlpath) logger.Println("start downloading", dir, urlpath)
defer func() { defer func() {
if err != nil { if err != nil {
@ -38,6 +60,7 @@ func download(dir string, urlpath string, accessToken string) (target string, er
req, _ := http.NewRequest("GET", urlpath, nil) req, _ := http.NewRequest("GET", urlpath, nil)
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51") req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51")
req.Header.Add("As-X-UrlHash", accessToken)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return "", err return "", err
@ -48,15 +71,33 @@ func download(dir string, urlpath string, accessToken string) (target string, er
return "", fmt.Errorf("download failed : %d %s", resp.StatusCode, parsed.Path) return "", fmt.Errorf("download failed : %d %s", resp.StatusCode, parsed.Path)
} }
out, err := os.Create(path.Join(dir, path.Base(parsed.Path))) out, err := os.Create(path.Join(dir, path.Base(filepath.ToSlash(parsed.Path))))
if err != nil { if err != nil {
return "", err return "", err
} }
defer out.Close() defer out.Close()
_, err = io.Copy(out, resp.Body) cl := resp.Header.Get("Content-Length")
if err != nil { totalLength, _ := strconv.ParseInt(cl, 10, 0)
return "", err totalWritten := int64(0)
chunkSize := pof2(totalLength/100, 1024*1024)
if cb != nil {
cb(0, totalLength)
}
for {
written, err := io.CopyN(out, resp.Body, chunkSize)
totalWritten += written
if cb != nil {
cb(totalWritten, totalLength)
}
if err != nil {
if err == io.EOF {
break
}
return "", err
}
} }
return filepath.ToSlash(out.Name()), nil return filepath.ToSlash(out.Name()), nil
@ -204,7 +245,7 @@ func (hc *houstonClient) makeDownloadUrl(rel string) string {
return out return out
} }
func copy(src, dst string) error { func copyfile(src, dst string) error {
fi, err := os.Stat(src) fi, err := os.Stat(src)
if err != nil { if err != nil {
return err return err
@ -248,7 +289,7 @@ func (hc *houstonClient) prepareUpdateSelf(req *shared.DeployRequest) (srcdir st
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
fname, err := download(tempdir, hc.makeDownloadUrl(req.Url), req.AccessToken) fname, err := download(tempdir, hc.makeDownloadUrl(req.Url), req.AccessToken, nil)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -273,7 +314,7 @@ func (hc *houstonClient) prepareUpdateSelf(req *shared.DeployRequest) (srcdir st
selfname, _ := os.Executable() selfname, _ := os.Executable()
srcreplacer := path.Join(path.Dir(fname), "replacer") + path.Ext(selfname) srcreplacer := path.Join(path.Dir(fname), "replacer") + path.Ext(selfname)
replacer = "./" + filepath.ToSlash("replacer"+path.Ext(selfname)) replacer = "./" + filepath.ToSlash("replacer"+path.Ext(selfname))
err = copy(srcreplacer, replacer) err = copyfile(srcreplacer, replacer)
if err == nil { if err == nil {
err = os.Chmod(replacer, 0775) err = os.Chmod(replacer, 0775)
} }
@ -282,7 +323,7 @@ func (hc *houstonClient) prepareUpdateSelf(req *shared.DeployRequest) (srcdir st
return filepath.ToSlash(tempdir), replacer, err return filepath.ToSlash(tempdir), replacer, err
} }
func (hc *houstonClient) deploy(req *shared.DeployRequest) error { func (hc *houstonClient) deploy(req *shared.DeployRequest, cb func(*protos.DeployingProgress)) 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)
if err != nil { if err != nil {
@ -290,11 +331,28 @@ func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
} }
// verpath에 배포 시작 // verpath에 배포 시작
fname, err := download(root, hc.makeDownloadUrl(req.Url), req.AccessToken) h := md5.New()
h.Write([]byte(strings.Trim(req.Url, "/")))
at := hex.EncodeToString(h.Sum(nil))
fname, err := download(root, hc.makeDownloadUrl(req.Url), at, func(written int64, total int64) {
prog := protos.DeployingProgress{
State: "download",
Progress: written,
Total: total,
}
cb(&prog)
})
if err != nil { if err != nil {
return err return err
} }
cb(&protos.DeployingProgress{
State: "unpack",
Progress: 0,
Total: 0,
})
switch path.Ext(fname) { switch path.Ext(fname) {
case ".zip": case ".zip":
err = unzip(fname) err = unzip(fname)
@ -304,7 +362,11 @@ func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
if err == nil && len(req.Config) > 0 { if err == nil && len(req.Config) > 0 {
// config.json도 다운로드 // config.json도 다운로드
_, err = download(root, hc.makeDownloadUrl(req.Config), req.AccessToken) h := md5.New()
h.Write([]byte(strings.Trim(req.Config, "/")))
at = hex.EncodeToString(h.Sum(nil))
_, err = download(root, hc.makeDownloadUrl(req.Config), at, nil)
} }
return err return err

9
client/deploy_test.go Normal file
View File

@ -0,0 +1,9 @@
package client
import (
"testing"
)
func TestDownload(t *testing.T) {
}

133
client/node_exporter.go Normal file
View File

@ -0,0 +1,133 @@
package client
import (
"fmt"
stdlog "log"
"net/http"
_ "net/http/pprof"
"sort"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
promcollectors "github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/version"
"github.com/prometheus/node_exporter/collector"
)
// handler wraps an unfiltered http.Handler but uses a filtered handler,
// created on the fly, if filtering is requested. Create instances with
// newHandler.
type handler struct {
unfilteredHandler http.Handler
// exporterMetricsRegistry is a separate registry for the metrics about
// the exporter itself.
exporterMetricsRegistry *prometheus.Registry
includeExporterMetrics bool
maxRequests int
logger log.Logger
}
func NewHandlerForNodeExporter(includeExporterMetrics bool, maxRequests int, logger log.Logger) *handler {
h := &handler{
exporterMetricsRegistry: prometheus.NewRegistry(),
includeExporterMetrics: includeExporterMetrics,
maxRequests: maxRequests,
logger: logger,
}
if h.includeExporterMetrics {
h.exporterMetricsRegistry.MustRegister(
promcollectors.NewProcessCollector(promcollectors.ProcessCollectorOpts{}),
promcollectors.NewGoCollector(),
)
}
if innerHandler, err := h.innerHandler(); err != nil {
panic(fmt.Sprintf("Couldn't create metrics handler: %s", err))
} else {
h.unfilteredHandler = innerHandler
}
return h
}
// ServeHTTP implements http.Handler.
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
filters := r.URL.Query()["collect[]"]
level.Debug(h.logger).Log("msg", "collect query:", "filters", filters)
if len(filters) == 0 {
// No filters, use the prepared unfiltered handler.
h.unfilteredHandler.ServeHTTP(w, r)
return
}
// To serve filtered metrics, we create a filtering handler on the fly.
filteredHandler, err := h.innerHandler(filters...)
if err != nil {
level.Warn(h.logger).Log("msg", "Couldn't create filtered metrics handler:", "err", err)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err)))
return
}
filteredHandler.ServeHTTP(w, r)
}
// innerHandler is used to create both the one unfiltered http.Handler to be
// wrapped by the outer handler and also the filtered handlers created on the
// fly. The former is accomplished by calling innerHandler without any arguments
// (in which case it will log all the collectors enabled via command-line
// flags).
func (h *handler) innerHandler(filters ...string) (http.Handler, error) {
nc, err := collector.NewNodeCollector(h.logger, filters...)
if err != nil {
return nil, fmt.Errorf("couldn't create collector: %s", err)
}
// Only log the creation of an unfiltered handler, which should happen
// only once upon startup.
if len(filters) == 0 {
level.Info(h.logger).Log("msg", "Enabled collectors")
collectors := []string{}
for n := range nc.Collectors {
collectors = append(collectors, n)
}
sort.Strings(collectors)
for _, c := range collectors {
level.Info(h.logger).Log("collector", c)
}
}
r := prometheus.NewRegistry()
r.MustRegister(version.NewCollector("node_exporter"))
if err := r.Register(nc); err != nil {
return nil, fmt.Errorf("couldn't register node collector: %s", err)
}
var handler http.Handler
if h.includeExporterMetrics {
handler = promhttp.HandlerFor(
prometheus.Gatherers{h.exporterMetricsRegistry, r},
promhttp.HandlerOpts{
ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(h.logger)), "", 0),
ErrorHandling: promhttp.ContinueOnError,
MaxRequestsInFlight: h.maxRequests,
Registry: h.exporterMetricsRegistry,
},
)
// Note that we have to use h.exporterMetricsRegistry here to
// use the same promhttp metrics for all expositions.
handler = promhttp.InstrumentMetricHandler(
h.exporterMetricsRegistry, handler,
)
} else {
handler = promhttp.HandlerFor(
r,
promhttp.HandlerOpts{
ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(h.logger)), "", 0),
ErrorHandling: promhttp.ContinueOnError,
MaxRequestsInFlight: h.maxRequests,
},
)
}
return handler, nil
}

View File

@ -2,22 +2,27 @@ package client
import ( import (
"archive/zip" "archive/zip"
"bufio"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"runtime/debug" "regexp"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"github.com/Knetic/govaluate"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/metric"
"repositories.action2quare.com/ayo/houston/shared" "repositories.action2quare.com/ayo/houston/shared"
"repositories.action2quare.com/ayo/houston/shared/protos" "repositories.action2quare.com/ayo/houston/shared/protos"
) )
@ -58,7 +63,7 @@ func (hc *houstonClient) uploadZipLogFile(zipFile string, name string, version s
} }
req.Header.Set("Houston-Service-Name", name) req.Header.Set("Houston-Service-Name", name)
req.Header.Set("Houston-Service-Version", version) req.Header.Set("Houston-Service-Version", version)
req.Header.Set("Houston-Service-Filename", path.Base(zipFile)) req.Header.Set("Houston-Service-Filename", path.Base(filepath.ToSlash(zipFile)))
req.Header.Set("Content-Type", "application/zip") req.Header.Set("Content-Type", "application/zip")
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
@ -73,28 +78,34 @@ func (hc *houstonClient) uploadZipLogFile(zipFile string, name string, version s
return nil return nil
} }
func zipLogFiles(storageRoot string, req *shared.UploadRequest, start, except string) (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
}
out = append(out, file)
} }
return root, out
}
root = path.Join(root, path.Dir(req.Filter)) func zipCompressFiles(root string, matches []string) (string, error) {
zipFileName := path.Join(os.TempDir(), path.Base(matches[0])) + ".zip" f, err := os.CreateTemp(os.TempDir(), "*.zip")
f, err := os.OpenFile(zipFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
defer f.Close() defer f.Close()
@ -103,93 +114,169 @@ func zipLogFiles(storageRoot string, req *shared.UploadRequest, start, except st
oldestFile := "" oldestFile := ""
for i, file := range matches { for i, file := range matches {
if file == root { if fi, err := os.Lstat(file); err == nil {
continue if (fi.Mode() & os.ModeSymlink) == os.ModeSymlink {
} matches[i] = ""
if len(except) > 0 && file >= except { continue
matches = matches[:i] }
break
}
if len(start) > 0 && file < start {
continue
} }
if len(oldestFile) == 0 { if len(oldestFile) == 0 {
oldestFile = path.Base(file) oldestFile = path.Base(filepath.ToSlash(file))
} }
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 {
logger.Println(err) return "", err
return "", nil, err
} }
src, err := os.Open(file) src, err := os.Open(file)
if err != nil { if err != nil {
logger.Println(err) return "", err
return "", nil, err
} }
defer src.Close() defer src.Close()
if _, err = io.Copy(fw, src); err != nil { if _, err = io.Copy(fw, src); err != nil {
logger.Println(err) return "", err
return "", nil, err
} }
} }
return f.Name(), matches, nil return f.Name(), nil
// defer func() {
// tempname := f.Name()
// f.Close()
// resp, _ := http.Post(req.Url, "application/zip", f)
// if resp != nil && resp.Body != nil {
// resp.Body.Close()
// }
// os.Remove(tempname)
// if del, err := strconv.ParseBool(req.DeleteAfterUploaded); del && err == nil {
// for _, file := range matches {
// if strings.HasSuffix(file, except) {
// continue
// }
// os.Remove(file)
// }
// }
// }()
// Create a new zip archive.
//}(f)
//return nil
} }
func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) *procmeta { func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) (*procmeta, error) {
if len(req.Args) == 0 { if len(req.Args) == 0 {
return nil return nil, errors.New("args is empty")
} }
verpath := path.Join(storageRoot, req.Name, req.Version)
foundVersion := req.Version
if req.Version == "latest" {
entries, err := os.ReadDir(path.Join(storageRoot, req.Name))
if err != nil {
return nil, err
}
var latestTimestamp time.Time
var latestVersion string
for _, entry := range entries {
if !entry.IsDir() {
continue
}
fi, err := entry.Info()
if err != nil {
return nil, err
}
createTime := fi.ModTime()
if latestTimestamp.Before(createTime) {
latestTimestamp = fi.ModTime()
latestVersion = fi.Name()
}
}
if len(latestVersion) > 0 {
foundVersion = latestVersion
}
}
verpath := path.Join(storageRoot, req.Name, foundVersion)
fi, err := os.Stat(verpath) fi, err := os.Stat(verpath)
if err != nil {
return nil, err
}
if err == nil && fi.IsDir() { if fi.IsDir() {
req.Args[0] = "./" + path.Clean(strings.TrimPrefix(req.Args[0], "/")) exefile := "./" + path.Clean(strings.TrimPrefix(req.Args[0], "/"))
os.Chmod(path.Join(verpath, req.Args[0]), 0777) os.Chmod(path.Join(verpath, exefile), 0777)
exef, _ := os.Executable()
expanded := make([]string, len(req.Args))
for i, arg := range req.Args {
expanded[i] = os.ExpandEnv(arg)
}
exename := path.Join(path.Dir(strings.ReplaceAll(exef, "\\", "/")), verpath, exefile)
logger.Println("exefile :", exefile)
logger.Println("verpath :", verpath)
logger.Println("exef :", exef)
logger.Println("path.Dir :", path.Dir(exef))
logger.Println("exename :", exename)
cmd := exec.Command(os.ExpandEnv(exename), expanded[1:]...)
cmd := exec.Command(req.Args[0], req.Args[1:]...)
cmd.Dir = verpath cmd.Dir = verpath
stdin, _ := cmd.StdinPipe() stdin, _ := cmd.StdinPipe()
seq++
return &procmeta{ return &procmeta{
cmd: cmd, id: seq,
name: req.Name, cmd: cmd,
version: req.Version, name: req.Name,
state: protos.ProcessState_Stopped, args: req.Args,
stdin: stdin, version: foundVersion,
logUploadChan: make(chan *shared.UploadRequest), recover: req.AutoRestart,
buffers: bufferStack{cursor: 0}, verpath: verpath,
state: int32(protos.ProcessState_Stopped),
stdin: stdin,
logfile: req.OutputLogFile,
keepLatest: req.Version == "latest",
}, nil
}
return nil, errors.New("not found")
}
func evaluateArgs(args []string, params map[string]any) ([]string, error) {
re := regexp.MustCompile(`\$\(\((.*?)\)\)`)
for i, input := range args {
matches := re.FindAllStringSubmatch(input, -1)
if len(matches) == 0 {
continue
}
for _, match := range matches {
if len(match) > 1 {
expression := strings.TrimSpace(match[1])
expr, err := govaluate.NewEvaluableExpression(expression)
if err != nil {
return nil, err
}
result, err := expr.Evaluate(params)
if err != nil {
return nil, err
}
// 원래 표현식을 결과로 대체
input = strings.Replace(input, match[0], fmt.Sprintf("%v", result), -1)
}
}
args[i] = input
}
return args, nil
}
func parseEnv(input []string) map[string]any {
output := make(map[string]any, len(input))
for _, envkv := range input {
kv := strings.SplitN(envkv, "=", 2)
parsed, err := strconv.ParseInt(kv[1], 10, 0)
if err == nil {
output[kv[0]] = parsed
} else {
parsed, err := strconv.ParseFloat(kv[1], 32)
if err == nil {
output[kv[0]] = parsed
} else {
output[kv[0]] = kv[1]
}
} }
} }
return nil return output
} }
func (hc *houstonClient) launch(meta *procmeta) error { func (hc *houstonClient) launch(meta *procmeta) error {
@ -202,141 +289,258 @@ func (hc *houstonClient) launch(meta *procmeta) error {
return err return err
} }
err = os.MkdirAll(path.Join(meta.cmd.Dir, "logs"), 0775) logfolder := path.Join(meta.verpath, "logs")
err = os.MkdirAll(logfolder, 0775)
if err != nil { if err != nil {
return err return err
} }
relayChan := make(chan struct { stdReader := func(jobName string, r io.ReadCloser, index int, logfilePath string) {
size int
buf []byte
})
go func() {
defer func() { defer func() {
r := recover() logger.Println("stdReader is terminated :", meta.name)
if r != nil { if meta.isState(protos.ProcessState_Running) {
logger.Println(r) hc.operationChan <- &protos.OperationQueryResponse{
debug.PrintStack() Operation: string(shared.Exception),
Args: map[string]string{
"id": fmt.Sprintf("%d", meta.id),
},
}
} }
close(relayChan)
hc.exitChan <- meta.cmd
}() }()
now := time.Now().UTC()
ext := path.Ext(meta.cmd.Args[0])
nameonly := path.Base(meta.cmd.Args[0])
if len(ext) > 0 {
nameonly = nameonly[:len(nameonly)-len(ext)]
}
ts := now.Format("2006-01-02T15-04-05")
stdPrefix := path.Join(meta.cmd.Dir, "logs", fmt.Sprintf("%s_%s", nameonly, ts))
logfile, _ := os.Create(stdPrefix + "_0.log")
defer logfile.Close()
logfileIdx := 0
for {
thisFileSize := 0
switchToNextFile := func() string {
logfileIdx++
nextFile := fmt.Sprintf("%s_%d.log", stdPrefix, logfileIdx)
if nextLogfile, err := os.Create(nextFile); err == nil {
logfile.Close()
logfile = nextLogfile
}
thisFileSize = 0
return nextFile
}
uploadStartFile := ""
select {
case req := <-meta.logUploadChan:
nextFile := switchToNextFile()
startFile := uploadStartFile
uploadStartFile = nextFile
go func(startFile, nextFile string) {
zipFile, srcFiles, err := zipLogFiles(hc.config.StorageRoot, req, startFile, nextFile)
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 {
if err = hc.uploadZipLogFile(zipFile, meta.name, meta.version); err == nil {
for _, oldf := range srcFiles {
os.Remove(oldf)
}
} else {
logger.Println("uploadZipLogFile failed :", err)
}
} else if err != nil {
logger.Println("zipLogFiles failed :", err)
}
}(startFile, nextFile)
case bt := <-relayChan:
if bt.buf == nil {
return
}
logfile.Write(bt.buf[:bt.size])
logfile.Sync()
meta.buffers.push(bt.buf)
thisFileSize += bt.size
if thisFileSize > 10*1024*1024 {
switchToNextFile()
}
}
}
}()
stdReader := func(r io.Reader) {
defer func() { defer func() {
recover() overflow := index / 64
stdout.Close() offset := index % 64
key := fmt.Sprintf("%s-%d", meta.args[0], overflow)
runningFlags := hc.siblingProcIndex[key]
mask := uint64(1 << offset)
runningFlags = runningFlags ^ mask
hc.siblingProcIndex[key] = runningFlags
}() }()
defer func() {
reco := recover()
if reco != nil {
logger.Println(reco)
}
r.Close()
}()
metricExporter := metric.NewPrometheusExport(hc.config.MetricNamespace)
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 {
buff := meta.buffers.pop() if targetFile == nil {
size, err := r.Read(buff) 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')
if err != nil { if err != nil {
relayChan <- struct { logger.Println("ReadBytes at stdReader return err :", err, meta.name)
size int
buf []byte
}{buf: nil}
break break
} }
if size > 0 {
relayChan <- struct { if readingMetric {
size int metricBuffer = append(metricBuffer, buff...)
buf []byte } else if buff[0] == metric.METRIC_HEAD_INLINE {
}{size: size, buf: buff} readingMetric = true
metricBuffer = append(metricBuffer, buff[1:]...)
}
if readingMetric {
if metricBuffer[len(metricBuffer)-2] == metric.METRIC_TAIL_INLINE {
readingMetric = false
metricBuffer = metricBuffer[:len(metricBuffer)-2]
if metricBuffer[0] == '{' {
var desc metric.MetricDescription
if err := json.Unmarshal(metricBuffer, &desc); err != nil {
logger.Println("unmarshal metric failed :", err, string(metricBuffer))
continue
}
if desc.ConstLabels == nil {
desc.ConstLabels = make(map[string]string)
}
for k, v := range hc.config.ConstLabels {
desc.ConstLabels[k] = v
}
desc.ConstLabels["job"] = jobName
metricExporter.RegisterMetric(&desc)
} else {
key, val := metric.ReadMetricValue(metricBuffer)
metricExporter.UpdateMetric(key, val)
}
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)
if total > 1024*1024 {
total = 0
targetFile.Close()
targetFile = nil
hc.uploadProcFiles(meta, "logs/*"+ext, true)
}
} }
} }
} }
go stdReader(stderr) errReader := func(r io.ReadCloser, logfilePath string) {
go stdReader(stdout) 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)
}
}
}
}
index := 0
for overflow := 0; ; overflow++ {
key := fmt.Sprintf("%s-%d", meta.args[0], overflow)
runningFlags := hc.siblingProcIndex[key]
if runningFlags == math.MaxUint64 {
index += 64
} else {
for si := 0; si < 64; si++ {
mask := uint64(1 << si)
if runningFlags&mask == 0 {
index += si
runningFlags |= mask
break
}
}
hc.siblingProcIndex[key] = runningFlags
break
}
}
// 자체 환경 변수
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 = 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))
if err != nil {
logger.Println("evaluateArgs failed :", 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)
err = meta.cmd.Start() err = meta.cmd.Start()
if err == nil { if err == nil {
meta.state = protos.ProcessState_Running logger.Println("process index, pid =", index, meta.cmd.Process.Pid)
set_affinity(meta.cmd.Process.Pid, index)
meta.setState(protos.ProcessState_Running)
} }
return err return err
} }
var errPrepareprocessLaunchFailed = errors.New("prepareProcessLaunch failed") func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest) error {
meta, err := prepareProcessLaunch(hc.config.StorageRoot, req)
func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest, op protos.OperationClient) error { if err != nil {
logger.Println("startChildProcess :", *req) return err
if req.Version == "latest" {
// 최신 버전을 찾음
latest, err := shared.FindLastestVersion(hc.config.StorageRoot, req.Name)
if err != nil {
return err
}
req.Version = latest
} }
meta := prepareProcessLaunch(hc.config.StorageRoot, req)
if meta == nil {
return errPrepareprocessLaunchFailed
}
if err := hc.launch(meta); err != nil { if err := hc.launch(meta); err != nil {
return err return err
} }
@ -345,94 +549,67 @@ func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest, op p
vers := hc.deploys[req.Name] vers := hc.deploys[req.Name]
for _, ver := range vers { for _, ver := range vers {
if ver.Version == req.Version { if ver.Version == req.Version {
ver.Args = meta.cmd.Args ver.Args = meta.args
} }
} }
if argfile, err := os.Create(path.Join(hc.config.StorageRoot, req.Name, "@args")); err == nil { if argfile, err := os.Create(path.Join(hc.config.StorageRoot, req.Name, "@args")); err == nil {
enc := json.NewEncoder(argfile) enc := json.NewEncoder(argfile)
enc.Encode(meta.cmd.Args) enc.Encode(req.Args)
argfile.Close() argfile.Close()
} }
if argfile, err := os.Create(path.Join(hc.config.StorageRoot, req.Name, req.Version, "@args")); err == nil { if argfile, err := os.Create(path.Join(hc.config.StorageRoot, req.Name, req.Version, "@args")); err == nil {
enc := json.NewEncoder(argfile) enc := json.NewEncoder(argfile)
enc.Encode(meta.cmd.Args) enc.Encode(req.Args)
argfile.Close() argfile.Close()
} }
hc.childProcs = append(hc.childProcs, meta) hc.childProcs = append(hc.childProcs, meta)
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
return nil return nil
} }
var errNoRunningProcess = errors.New("no running processed")
func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op protos.OperationClient) error { func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op protos.OperationClient) error {
if req.Version == "latest" { killer := func(proc *procmeta) {
// 최신 버전을 찾음 proc.setState(protos.ProcessState_Stopping)
latest, err := shared.FindLastestVersion(hc.config.StorageRoot, req.Name) if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
if err != nil { proc.cmd.Process.Signal(os.Kill)
return err
} }
req.Version = latest go func() {
proc.cmd.Wait()
hc.operationChan <- &protos.OperationQueryResponse{
Operation: string(shared.Exception),
Args: map[string]string{
"id": fmt.Sprintf("%d", proc.id),
},
}
}()
} }
var remains []*procmeta
var killing []*procmeta
for _, proc := range hc.childProcs { for _, proc := range hc.childProcs {
if proc.state != protos.ProcessState_Running { if !proc.isState(protos.ProcessState_Running) {
continue continue
} }
if req.Pid != 0 { if req.Pid != 0 {
if req.Pid == int32(proc.cmd.Process.Pid) { if req.Pid == int32(proc.cmd.Process.Pid) {
// 해당 pid만 제거 // 해당 pid만 제거
killing = append(killing, proc) killer(proc)
} else {
remains = append(remains, proc)
} }
} else if proc.name == req.Name { } else if proc.name == req.Name {
if len(req.Version) == 0 { if len(req.Version) == 0 {
// program 다 정지 // program 다 정지
killing = append(killing, proc) killer(proc)
} else if req.Version == proc.version { } else if req.Version == proc.version {
// program의 특정 버전만 정지 // program의 특정 버전만 정지
killing = append(killing, proc) killer(proc)
} else {
// 해당 사항 없음
remains = append(remains, proc)
} }
} else {
// 해당 사항 없음
remains = append(remains, proc)
} }
} }
if len(killing) > 0 { op.Refresh(context.Background(), hc.makeOperationQueryRequest())
for _, proc := range killing {
proc.state = protos.ProcessState_Stopping
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
proc.cmd.Process.Signal(os.Kill)
}
}
op.Refresh(context.Background(), hc.makeOperationQueryRequest()) return nil
for _, proc := range killing {
proc.cmd.Wait()
proc.cmd.Process.Release()
}
hc.childProcs = remains
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
return nil
}
return errNoRunningProcess
} }
func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest, op protos.OperationClient) error { func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest, op protos.OperationClient) error {
@ -440,13 +617,13 @@ func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest,
if proc.cmd.Process.Pid == int(req.Pid) { if proc.cmd.Process.Pid == int(req.Pid) {
if len(req.Config) > 0 { if len(req.Config) > 0 {
// config.json를 먼저 다운로드 시도 // config.json를 먼저 다운로드 시도
root := proc.cmd.Dir root := proc.verpath
if _, err := download(root, hc.makeDownloadUrl(req.Config), ""); err != nil { if _, err := download(root, hc.makeDownloadUrl(req.Config), "", nil); err != nil {
return err return err
} }
} }
proc.state = protos.ProcessState_Restart proc.setState(protos.ProcessState_Restart)
op.Refresh(context.Background(), hc.makeOperationQueryRequest()) op.Refresh(context.Background(), hc.makeOperationQueryRequest())
hc.exitChan <- proc.cmd hc.exitChan <- proc.cmd
@ -457,31 +634,53 @@ 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) {
if req.Version == "latest" { logger.Println("uploadFiles found :", child.version, child.name)
// 최신 버전을 찾음 root, matches := findMatchFiles(hc.config.StorageRoot, child.name, child.version, filter)
latest, err := shared.FindLastestVersion(hc.config.StorageRoot, req.Name)
if err != nil { go func(deleteAfterUpload bool, root string, matches []string) {
return err zipFile, err := zipCompressFiles(root, matches)
if err == nil && len(zipFile) > 0 {
if err = hc.uploadZipLogFile(zipFile, child.name, child.version); err == nil {
if deleteAfterUpload {
for _, fn := range matches {
if len(fn) > 0 {
os.Remove(fn)
}
}
}
} else {
logger.Println("uploadZipLogFile failed :", err)
}
os.Remove(zipFile)
} else if err != nil {
logger.Println("zipLogFiles failed :", err)
} }
}(deleteAfterUpload, root, matches)
}
req.Version = latest func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error {
}
logger.Println("uploadFiles req :", *req) logger.Println("uploadFiles req :", *req)
for _, child := range hc.childProcs { for _, child := range hc.childProcs {
if child.version == req.Version && child.name == req.Name { if child.version == req.Version && child.name == req.Name {
logger.Println("uploadFiles found :", child.version, child.name) hc.uploadProcFiles(child, req.Filter, false)
child.logUploadChan <- req
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 = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err != nil { if err == nil && len(zipFile) > 0 && len(zipFile) > 0 {
if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err == nil {
for _, fn := range matches {
if len(fn) > 0 {
os.Remove(fn)
}
}
os.Remove(zipFile)
} else {
logger.Println("uploadZipLogFile failed :", err) logger.Println("uploadZipLogFile failed :", err)
} }
} else if err != nil { } else if err != nil {

70
go.mod
View File

@ -1,17 +1,71 @@
module repositories.action2quare.com/ayo/houston module repositories.action2quare.com/ayo/houston
go 1.18 go 1.19
require ( require (
golang.org/x/text v0.10.0 github.com/Knetic/govaluate v3.0.0+incompatible
google.golang.org/grpc v1.56.0 github.com/go-kit/log v0.2.1
google.golang.org/protobuf v1.30.0 github.com/prometheus/client_golang v1.17.0
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22 github.com/prometheus/common v0.44.0
github.com/prometheus/node_exporter v1.6.1
golang.org/x/sys v0.16.0
golang.org/x/text v0.14.0
google.golang.org/grpc v1.60.1
google.golang.org/protobuf v1.32.0
repositories.action2quare.com/ayo/gocommon v0.0.0-20240729084947-8e3d6c28f024
) )
require ( require (
github.com/alecthomas/kingpin/v2 v2.3.2 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/beevik/ntp v0.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/dennwc/btrfs v0.0.0-20230312211831-a1f570bd01a1 // indirect
github.com/dennwc/ioctl v1.0.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/ema/qdisc v0.0.0-20230120214811-5b708f463de3 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.11.0 // indirect github.com/golang/snappy v0.0.1 // indirect
golang.org/x/sys v0.9.0 // indirect github.com/google/go-cmp v0.5.9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect github.com/hashicorp/go-envparse v0.1.0 // indirect
github.com/hodgesds/perf-utils v0.7.0 // indirect
github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/jsimonetti/rtnetlink v1.3.2 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/lufia/iostat v1.2.1 // indirect
github.com/mattn/go-xmlrpc v0.0.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mdlayher/ethtool v0.0.0-20221212131811-ba3b4bc2e02c // indirect
github.com/mdlayher/genetlink v1.3.1 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mdlayher/wifi v0.0.0-20220330172155-a44c70b6d3c8 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus-community/go-runit v0.1.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.mongodb.org/mongo-driver v1.11.6 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.6.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
howett.net/plist v1.0.0 // indirect
) )

185
go.sum
View File

@ -1,22 +1,179 @@
github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU=
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw=
github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dennwc/btrfs v0.0.0-20230312211831-a1f570bd01a1 h1:ue4Es4Xzz255hWQ7NAWzZxuXG+YOV7URzzusLLSe0zU=
github.com/dennwc/btrfs v0.0.0-20230312211831-a1f570bd01a1/go.mod h1:MYsOV9Dgsec3FFSOjywi0QK5r6TeBbdWxdrMGtiYXHA=
github.com/dennwc/ioctl v1.0.0 h1:DsWAAjIxRqNcLn9x6mwfuf2pet3iB7aK90K4tF16rLg=
github.com/dennwc/ioctl v1.0.0/go.mod h1:ellh2YB5ldny99SBU/VX7Nq0xiZbHphf1DrtHxxjMk0=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/ema/qdisc v0.0.0-20230120214811-5b708f463de3 h1:Jrl8sD8wO34+EE1dV2vhOXrqFAZa/FILDnZRaV28+cw=
github.com/ema/qdisc v0.0.0-20230120214811-5b708f463de3/go.mod h1:FhIc0fLYi7f+lK5maMsesDqwYojIOh3VfRs8EVd5YJQ=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= github.com/hodgesds/perf-utils v0.7.0 h1:7KlHGMuig4FRH5fNw68PV6xLmgTe7jKs9hgAcEAbioU=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= github.com/hodgesds/perf-utils v0.7.0/go.mod h1:LAklqfDadNKpkxoAJNHpD5tkY0rkZEVdnCEWN5k4QJY=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973 h1:hk4LPqXIY/c9XzRbe7dA6qQxaT6Axcbny0L/G5a4owQ=
github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973/go.mod h1:PoK3ejP3LJkGTzKqRlpvCIFas3ncU02v8zzWDW+g0FY=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v1.3.2 h1:dcn0uWkfxycEEyNy0IGfx3GrhQ38LH7odjxAghimsVI=
github.com/jsimonetti/rtnetlink v1.3.2/go.mod h1:BBu4jZCpTjP6Gk0/wfrO8qcqymnN3g0hoFqObRmUo6U=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lufia/iostat v1.2.1 h1:tnCdZBIglgxD47RyD55kfWQcJMGzO+1QBziSQfesf2k=
github.com/lufia/iostat v1.2.1/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg=
github.com/mattn/go-xmlrpc v0.0.3 h1:Y6WEMLEsqs3RviBrAa1/7qmbGB7DVD3brZIbqMbQdGY=
github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mdlayher/ethtool v0.0.0-20221212131811-ba3b4bc2e02c h1:Y7LoKqIgD7vmqJ7+6ZVnADuwUO+m3tGXbf2lK0OvjIw=
github.com/mdlayher/ethtool v0.0.0-20221212131811-ba3b4bc2e02c/go.mod h1:i0nPbE+sL2G3OtdIb9SXxW/T4UiAwh6rxPW7zcuX+KQ=
github.com/mdlayher/genetlink v1.3.1 h1:roBiPnual+eqtRkKX2Jb8UQN5ZPWnhDCGj/wR6Jlz2w=
github.com/mdlayher/genetlink v1.3.1/go.mod h1:uaIPxkWmGk753VVIzDtROxQ8+T+dkHqOI0vB1NA9S/Q=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/wifi v0.0.0-20220330172155-a44c70b6d3c8 h1:/HCRFfpoICSWHvNrJ356VO4opd9dg/LaU7m8Tzdf39c=
github.com/mdlayher/wifi v0.0.0-20220330172155-a44c70b6d3c8/go.mod h1:IqdtNfemiXr50M8tnxLWSFdZKZ9vcI1Mgt0oTrCIS7A=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus-community/go-runit v0.1.0 h1:uTWEj/Fn2RoLdfg/etSqwzgYNOYPrARx1BHUN052tGA=
github.com/prometheus-community/go-runit v0.1.0/go.mod h1:AvJ9Jo3gAFu2lbM4+qfjdpq30FfiLDJZKbQ015u08IQ=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/node_exporter v1.6.1 h1:Srqr6UAOUDnKhurvYGIYa7GZXuMCwJpzT9KP8uTJ4vw=
github.com/prometheus/node_exporter v1.6.1/go.mod h1:+zK+m9vwxu19JHl/kVVmixdCT6fWWHlmcOUHDFpkt0Y=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/siebenmann/go-kstat v0.0.0-20210513183136-173c9b0a9973 h1:GfSdC6wKfTGcgCS7BtzF5694Amne1pGCSTY252WhlEY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o=
go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
google.golang.org/grpc v1.56.0 h1:+y7Bs8rtMd07LeXmL3NxcTLn7mUkbKZqEpPhMNkwJEE= google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.56.0/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22 h1:DImSGNxZrc+Q4WlS1OKMsLAScEfDYLX4XMJdjAaVnXc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
repositories.action2quare.com/ayo/gocommon v0.0.0-20240729084947-8e3d6c28f024 h1:WdvW4BJHoBwXqNsfEgOAZai7L9iHqRCZ7PZL0cwOULE=
repositories.action2quare.com/ayo/gocommon v0.0.0-20240729084947-8e3d6c28f024/go.mod h1:XA8+hQtUNh956T+kAbJKkUtMl5HUWj83knvdBvvPS5s=

108
main.go
View File

@ -1,108 +0,0 @@
package main
import (
"repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/client"
"repositories.action2quare.com/ayo/houston/server"
)
var runAsClient = flagx.Bool("client", false, "")
var runAsServer = flagx.Bool("server", false, "")
func main() {
flagx.Parse()
if !*runAsClient && !*runAsServer {
logger.Fatal("client or server flag is needed")
return
}
if *runAsClient {
hc, err := client.NewClient(true)
if err != nil {
logger.Fatal(err)
return
}
hc.Start()
} else if *runAsServer {
svr := server.NewServer()
svr.Start()
}
}
// func TestOperationServer(t *testing.T) {
// hc, err := client.NewClient("192.168.9.32:8080", "http://192.168.9.32/commandcenter")
// if err != nil {
// t.Error(err)
// return
// }
// for i := 0; ; i++ {
// hc.SetReportMetrics(map[string]float32{
// "count": float32(i),
// })
// time.Sleep(1300 * time.Millisecond)
// }
// // token, _ := getMicrosoftAuthoizationToken("30330e18-f407-4e35-a6d6-b734b9fe9ee9", "VTr8Q~VBAUAOSmFiHM~bjgszYXBm9nuGBQCk8cLq")
// //go func() {
// //time.Sleep(2 * time.Second)
// // testver := fmt.Sprintf("%d.%d.%d", time.Now().Hour(), time.Now().Minute(), time.Now().Second())
// // svr.Operation().Deploy(server.MakeDeployRequest(
// // common.DeployRequest{
// // Name: "warehouse",
// // Version: testver,
// // Url: "https://actionsquare.s3.ap-northeast-2.amazonaws.com/warehouse.zip?response-content-disposition=inline&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEK7%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDmFwLW5vcnRoZWFzdC0yIkcwRQIgeYQKZXvVQsYEZNoWzxSRVjsKHzhq5VhIHVIaLpsUpssCIQCeZn8tfVM9jIjiKp62RPwEnb9oGR8T7apbsnqnntNlJCqGAwiH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAIaDDU0OTY2MjkyMDczOCIMeHddxdoH6Xfz68ZqKtoCwVyCYH45tC7aDBpkl%2FsGRPYlhUVy84h%2FVQx4Bu8hvgu3Y3fYSceAFgFWv%2FE3HpvrHD8AY42UsaHPBCd7tmlyydqnPoOr%2F5rjUCAmHXziGV7oAcO3HIbobbjO1rf3W2tQf7FSGbfPyxFdRhoObRz3sQi%2FcmYLKZWPS9UZRuWOSh2J3HHOoEdAIDq38eYxtVl1OEKxPIjfeJHTzmOOmvoOFBOzrY9HJyABcYxvmtOUvR6469Qf5r%2FTe%2BvuL1NQsYyBKwukcSxHcGbg7t%2BNeDTE%2FUS9lL7VYMEZlhfA1WSADbvAcYEu7cv7MENJ44XmAEHnC6zWIvDNqwK9FCfJrpALIJhbXqv%2FU%2Ft%2B5udZT1TXDDqp1se%2FBRLg8NyplcN4E8z6Qt%2F9pNSm1flhORHJsaPzk2ZfGeqvFvZGv1oBigwA6eJ3WCNl2hHhLkiSBg%2BvFwXA1KxxH9U8Nkl7EjDp7JmhBjqzAqPqVamph2PzNkEszr52GH69m90pjYkNTLM4nwMuGdo1f5%2BOm%2FVloBjBCh6OpTSK3XH67zEMZE0tFQ7qmqu2d69EY8Frt749G3RSNPeKptuIKxhBYF692an9nYUXiVH8OJkey0LDMbwWDaVfSZyOiYr%2FmeiVK0eRdK3C0JGwP%2BT6vUHBL1Agi5MH0dKvmlHwzvl%2BuqArgw7ZdOx%2BJsFHRD%2FqA87B5qPuvxPXkAO5qgwZfUW9MAxdh5hxcc9kNfmryYuVWD1DM%2BvRsRF2TsUqeffucajpQ7lhvN6rspDPMltD3VHFX82Hv12nqU7pHwtNLSO0D43W4JCmOJA8TFqhCkY4zCFDok0lx3x6b8w%2F4GptjvCo1c4HG9LAurTNK8HOb3XkYdmPwKOHaqMNajMsKZoohb0%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230331T060558Z&X-Amz-SignedHeaders=host&X-Amz-Expires=43199&X-Amz-Credential=ASIAX76TWSAROTUEDRGM%2F20230331%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Signature=aa6cc8aac808a066ea0c25e57b3a220cb6b2eb6118f6fb28974cb6e3c34e59d0",
// // // AccessToken: token,
// // },
// // []string{"mountain"},
// // ))
// // time.Sleep(2 * time.Second)
// // svr.Operation().Start(server.MakeStartRequest(
// // common.StartRequest{
// // Name: "warehouse",
// // Version: "latest",
// // Args: "biglocal.exe -port=8090 -dev",
// // },
// // []string{"mountain"},
// // ))
// // time.Sleep(25 * time.Second)
// // svr.Operation().Restart(server.MakeRestartRequest(
// // common.RestartRequest{
// // Name: "warehouse",
// // Version: "latest",
// // },
// // []string{"mountain"},
// // ))
// // time.Sleep(5 * time.Second)
// // svr.Operation().Stop(server.MakeStopRequest(
// // common.StopRequest{
// // Name: "warehouse",
// // Version: "latest",
// // Pid: 0,
// // },
// // []string{"mountain"},
// // ))
// // svr.Operation().Upload(server.MakeUploadRequest(
// // common.UploadRequest{
// // Name: "warehouse",
// // Version: "latest",
// // Url: "http://localhost",
// // Filter: "logs/*.log",
// // },
// // []string{"mountain"},
// // ))
// // time.Sleep(5 * time.Second)
// // svr.Operation().Withdraw(server.MakeWithdrawRequest(
// // common.WithdrawRequest{
// // Name: "warehouse",
// // Version: testver,
// // },
// // nil,
// // ))
// //}()
// }

41
main_client.go Normal file
View File

@ -0,0 +1,41 @@
//go:build client
package main
import (
"context"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
"repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/client"
"net/http"
)
func main() {
flagx.Parse()
hc, err := client.NewClient(true)
if err != nil {
panic(err)
}
http.Handle("/metrics", promhttp.Handler())
server := &http.Server{Addr: ":9100", Handler: nil}
go func() {
logger.Println("listen /metrics")
err := server.ListenAndServe()
if err != nil {
logger.Error(err)
}
}()
hc.Start()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
server.Shutdown(ctx)
cancel()
}

15
main_server.go Normal file
View File

@ -0,0 +1,15 @@
//go:build !client
package main
import (
"repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/houston/server"
)
func main() {
flagx.Parse()
svr := server.NewServer()
svr.Start()
}

View File

@ -4,7 +4,11 @@ del houston.zip
$Env:GOOS="linux" $Env:GOOS="linux"
$Env:GOARCH="amd64" $Env:GOARCH="amd64"
go build -ldflags="-s -w" .
go get repositories.action2quare.com/ayo/gocommon
go mod tidy
go build -ldflags="-s -w" -tags=client .
cp houston .\replacer\houston cp houston .\replacer\houston
cp config.json .\replacer\config.json cp config.json .\replacer\config.json

View File

@ -5,6 +5,7 @@ import "protos/empty.proto";
service Operation { service Operation {
rpc Query(stream OperationQueryRequest) returns (stream OperationQueryResponse) {} rpc Query(stream OperationQueryRequest) returns (stream OperationQueryResponse) {}
rpc Refresh(OperationQueryRequest) returns (Empty) {} rpc Refresh(OperationQueryRequest) returns (Empty) {}
rpc ReportDeployingProgress(DeployingProgress) returns (Empty) {}
} }
message VersionAndArgs { message VersionAndArgs {
@ -19,6 +20,9 @@ message DeployedVersions {
message OperationQueryRequest { message OperationQueryRequest {
string hostname = 1; string hostname = 1;
string public_ip = 4;
string private_ip =5;
repeated ProcessDescription procs = 2; repeated ProcessDescription procs = 2;
repeated DeployedVersions deploys = 3; repeated DeployedVersions deploys = 3;
} }
@ -43,3 +47,12 @@ message OperationQueryResponse {
string operation = 1; string operation = 1;
map<string, string> args = 2; map<string, string> args = 2;
} }
message DeployingProgress {
string hostname = 1;
string name = 2;
string version = 3;
string state = 4;
int64 progress = 5;
int64 total = 6;
}

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 {
@ -346,6 +362,7 @@ func (h *houstonHandler) StartProcess(w http.ResponseWriter, r *http.Request) {
argsTemp := re.FindAllString(argsline, -1) argsTemp := re.FindAllString(argsline, -1)
var args []string var args []string
for _, arg := range argsTemp { for _, arg := range argsTemp {
arg = strings.Trim(arg, "\n ")
if strings.HasPrefix(arg, `"`) && len(args) > 0 { if strings.HasPrefix(arg, `"`) && len(args) > 0 {
lastarg := args[len(args)-1] lastarg := args[len(args)-1]
if strings.HasSuffix(lastarg, "=") { if strings.HasSuffix(lastarg, "=") {
@ -379,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
@ -404,6 +423,13 @@ func (h *houstonHandler) StopProcess(w http.ResponseWriter, r *http.Request) {
Version: version, Version: version,
Pid: int32(pid), Pid: int32(pid),
}, targets)) }, targets))
h.Operation().Upload(MakeUploadRequest(shared.UploadRequest{
Name: name,
Version: version,
Url: "upload",
DeleteAfterUploaded: "true",
}, targets))
} }
func (h *houstonHandler) RestartProcess(w http.ResponseWriter, r *http.Request) { func (h *houstonHandler) RestartProcess(w http.ResponseWriter, r *http.Request) {
@ -416,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
@ -460,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
@ -493,15 +523,13 @@ 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
} }
if version == "latest" {
version, _ = shared.FindLastestVersion(h.downloadPath, name)
}
root := path.Join(h.downloadPath, name, version) root := path.Join(h.downloadPath, name, version)
logfiles, err := os.ReadDir(root) logfiles, err := os.ReadDir(root)
if err != nil { if err != nil {
@ -517,3 +545,7 @@ func (h *houstonHandler) GetLogFileLinks(w http.ResponseWriter, r *http.Request)
enc := json.NewEncoder(w) enc := json.NewEncoder(w)
enc.Encode(out) enc.Encode(out)
} }
func (h *houstonHandler) GetDeployingProgress(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(h.Operation().DeplyingProgress())
}

View File

@ -1,30 +1,35 @@
package server package server
import ( import (
"encoding/json" "archive/zip"
"crypto/md5"
"encoding/hex"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"os" "os"
"path" "path"
"reflect" "reflect"
"runtime/debug" "runtime/debug"
"strings" "strings"
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/flagx" "repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
) )
type HoustonServerWithHandler interface { type HoustonServerWithHandler interface {
HoustonServer HoustonServer
RegisterHandlers(serveMux *http.ServeMux, prefix string) error RegisterHandlers(serveMux gocommon.ServerMuxInterface, prefix string) error
} }
type houstonHandler struct { type houstonHandler struct {
HoustonServer HoustonServer
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 {
@ -37,12 +42,13 @@ func NewHoustonHandler() HoustonServerWithHandler {
methods[strings.ToLower(method.Name)] = method methods[strings.ToLower(method.Name)] = method
} }
return &houstonHandler{ return &houstonHandler{
HoustonServer: NewServer(), HoustonServer: NewServer(),
methods: methods, methods: methods,
maingateApiToken: loadServerConfig().MaingateApiToken,
} }
} }
func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string) error { func (h *houstonHandler) RegisterHandlers(serveMux gocommon.ServerMuxInterface, prefix string) error {
config := loadServerConfig() config := loadServerConfig()
storagePath := config.StorageRoot storagePath := config.StorageRoot
h.deployPath = path.Join(storagePath, sub_folder_name_deploys) h.deployPath = path.Join(storagePath, sub_folder_name_deploys)
@ -56,15 +62,65 @@ func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string
return err return err
} }
logger.Printf("houstonHandler registed. deployPath : %s, downloadPath : %s", h.deployPath, h.downloadPath)
if len(prefix) > 0 { if len(prefix) > 0 {
prefix = "/" + prefix prefix = "/" + prefix
} }
serveMux.Handle(prefix, h) serveMux.Handle(prefix, h)
fsx := http.FileServer(http.Dir(h.deployPath)) fsx := http.FileServer(http.Dir(h.deployPath))
serveMux.Handle(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_deploys), http.StripPrefix(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_deploys), fsx)) deployPrefix := fmt.Sprintf("%s/%s/", prefix, sub_folder_name_deploys)
logger.Printf("houstonHandler registed. deployPath : %s -> %s", fmt.Sprintf("%s/%s/", prefix, sub_folder_name_deploys), h.deployPath)
serveMux.HandleFunc(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_deploys), func(w http.ResponseWriter, r *http.Request) {
p := strings.TrimPrefix(r.URL.Path, deployPrefix)
rp := strings.TrimPrefix(r.URL.RawPath, deployPrefix)
h := md5.New()
src := strings.TrimLeft(r.URL.Path, fmt.Sprintf("/%s/", prefix))
h.Write([]byte(src))
at := hex.EncodeToString(h.Sum(nil))
if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) && at == r.Header.Get("As-X-UrlHash") {
r2 := new(http.Request)
*r2 = *r
r2.URL = new(url.URL)
*r2.URL = *r.URL
r2.URL.Path = p
r2.URL.RawPath = rp
fsx.ServeHTTP(w, r2)
} else {
http.NotFound(w, r)
}
})
// config는 접근하기 편하게 단축 경로 제공
serveMux.HandleFunc("/config/", func(w http.ResponseWriter, r *http.Request) {
logger.Println("config url.path :", r.URL.Path)
testhash := md5.New()
testhash.Write([]byte(r.URL.Path))
at := hex.EncodeToString(testhash.Sum(nil))
hash := r.Header.Get("As-X-UrlHash")
logger.Println("config at = hash :", at, hash)
if at == hash {
urlpath := strings.TrimPrefix(r.URL.Path, "/config/")
dir := path.Dir(urlpath)
file := path.Base(urlpath)
sourceFile := path.Join(h.deployPath, dir, "config", file)
logger.Println("config dest :", sourceFile)
bt, err := os.ReadFile(sourceFile)
if err != nil && !os.IsExist(err) {
logger.Println("config file is missing :", sourceFile)
w.WriteHeader(http.StatusNotFound)
} else {
if _, err = w.Write(bt); err != nil {
logger.Println("config write failed :", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
} else {
http.NotFound(w, r)
}
})
ufsx := http.FileServer(http.Dir(h.downloadPath)) ufsx := http.FileServer(http.Dir(h.downloadPath))
serveMux.Handle(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_downloads), http.StripPrefix(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_downloads), ufsx)) serveMux.Handle(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_downloads), http.StripPrefix(fmt.Sprintf("%s/%s/", prefix, sub_folder_name_downloads), ufsx))
@ -73,8 +129,7 @@ func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string
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()
@ -85,11 +140,26 @@ func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string
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)
@ -103,6 +173,7 @@ func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string
} }
var noauth = flagx.Bool("noauth", false, "") var noauth = flagx.Bool("noauth", false, "")
var authtype = flagx.String("auth", "on", "on|off|both")
func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
@ -118,37 +189,38 @@ func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.Body.Close() r.Body.Close()
}() }()
var userinfo map[string]any // TODO : 구글 인증까지 붙인 후에 주석 제거
if !*noauth { // var userinfo map[string]any
authheader := r.Header.Get("Authorization") // if !*noauth && (*authtype == "on" || *authtype == "both") {
if len(authheader) == 0 { // authheader := r.Header.Get("Authorization")
logger.Println("Authorization header is not valid :", authheader) // if len(authheader) == 0 {
w.WriteHeader(http.StatusBadRequest) // logger.Println("Authorization header is not valid :", authheader)
return // w.WriteHeader(http.StatusBadRequest)
} // return
// }
req, _ := http.NewRequest("GET", "https://graph.microsoft.com/oidc/userinfo", nil) // req, _ := http.NewRequest("GET", "https://graph.microsoft.com/oidc/userinfo", nil)
req.Header.Add("Authorization", authheader) // req.Header.Add("Authorization", authheader)
client := &http.Client{} // client := &http.Client{}
resp, err := client.Do(req) // resp, err := client.Do(req)
if err != nil { // if err != nil {
logger.Println("graph microsoft api call failed :", err) // logger.Println("graph microsoft api call failed :", err)
w.WriteHeader(http.StatusBadRequest) // w.WriteHeader(http.StatusBadRequest)
return // return
} // }
defer resp.Body.Close() // defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body) // raw, _ := io.ReadAll(resp.Body)
if err = json.Unmarshal(raw, &userinfo); err != nil { // if err = json.Unmarshal(raw, &userinfo); err != nil {
return // return
} // }
if _, expired := userinfo["error"]; expired { // if _, expired := userinfo["error"]; expired {
w.WriteHeader(http.StatusUnauthorized) // w.WriteHeader(http.StatusUnauthorized)
return // return
} // }
} // }
var operation string var operation string
if r.Method == "POST" { if r.Method == "POST" {

View File

@ -2,11 +2,16 @@ package server
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"net"
"os"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"time"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/shared" "repositories.action2quare.com/ayo/houston/shared"
"repositories.action2quare.com/ayo/houston/shared/protos" "repositories.action2quare.com/ayo/houston/shared/protos"
) )
@ -25,10 +30,12 @@ type ProcessSnapshot struct {
} }
type hostWithChan struct { type hostWithChan struct {
Hostname string Hostname string
Procs []*protos.ProcessDescription `json:"procs"` PrivateIp string
Deploys map[string][]*protos.VersionAndArgs `json:"deploys"` PublicIp string
opChan chan *opdef Procs []*protos.ProcessDescription `json:"procs"`
Deploys map[string][]*protos.VersionAndArgs `json:"deploys"`
opChan chan *opdef
} }
func makeHostWithChan(desc *protos.OperationQueryRequest) *hostWithChan { func makeHostWithChan(desc *protos.OperationQueryRequest) *hostWithChan {
@ -38,9 +45,11 @@ func makeHostWithChan(desc *protos.OperationQueryRequest) *hostWithChan {
} }
return &hostWithChan{ return &hostWithChan{
Hostname: desc.GetHostname(), PrivateIp: desc.PrivateIp,
Procs: desc.Procs, PublicIp: desc.PublicIp,
Deploys: newdeploys, Hostname: desc.GetHostname(),
Procs: desc.Procs,
Deploys: newdeploys,
} }
} }
@ -56,7 +65,27 @@ func (pc *hostWithChan) makeOpChan() *hostWithChan {
type hostPool struct { type hostPool struct {
sync.Mutex sync.Mutex
hosts map[string]*hostWithChan hosts map[string]*hostWithChan
exportChan chan string
}
type deployingProgress struct {
*protos.DeployingProgress
Timestamp int64
}
type deployingBoard struct {
sync.Mutex
progs []deployingProgress
}
func (db *deployingBoard) clone() (out []deployingProgress) {
db.Lock()
defer db.Unlock()
out = make([]deployingProgress, len(db.progs))
copy(out, db.progs)
return
} }
func (sp *hostPool) regist(desc *protos.OperationQueryRequest) (string, chan *opdef) { func (sp *hostPool) regist(desc *protos.OperationQueryRequest) (string, chan *opdef) {
@ -68,6 +97,28 @@ func (sp *hostPool) regist(desc *protos.OperationQueryRequest) (string, chan *op
} else { } else {
host = makeHostWithChan(desc).withOpChan(host.opChan) host = makeHostWithChan(desc).withOpChan(host.opChan)
} }
logger.Println("houston agent registered :", desc.Hostname, desc.PrivateIp, desc.PublicIp)
go func(prvip string, pubip string) {
if len(prvip) > 0 {
address := net.JoinHostPort(prvip, "9100")
if conn, _ := net.DialTimeout("tcp", address, 3*time.Second); conn != nil {
conn.Close()
sp.exportChan <- "+" + address
return
}
}
if len(pubip) > 0 {
address := net.JoinHostPort(pubip, "9100")
if conn, _ := net.DialTimeout("tcp", address, 3*time.Second); conn != nil {
conn.Close()
sp.exportChan <- "+" + address
return
}
}
}(desc.PrivateIp, desc.PublicIp)
sp.hosts[desc.Hostname] = host sp.hosts[desc.Hostname] = host
return desc.Hostname, host.opChan return desc.Hostname, host.opChan
} }
@ -87,6 +138,12 @@ func (sp *hostPool) unregist(key string) {
sp.Lock() sp.Lock()
defer sp.Unlock() defer sp.Unlock()
host := sp.hosts[key]
if host != nil {
sp.exportChan <- "-" + host.PublicIp
sp.exportChan <- "-" + host.PrivateIp
}
delete(sp.hosts, key) delete(sp.hosts, key)
} }
@ -135,6 +192,7 @@ func (sp *hostPool) query(filter func(*hostWithChan) bool) []*hostWithChan {
type operationServer struct { type operationServer struct {
protos.UnimplementedOperationServer protos.UnimplementedOperationServer
hp hostPool hp hostPool
db deployingBoard
} }
func marshal(argval reflect.Value, output map[string]string) map[string]string { func marshal(argval reflect.Value, output map[string]string) map[string]string {
@ -170,8 +228,14 @@ func (os *operationServer) Query(svr protos.Operation_QueryServer) error {
return err return err
} }
hostname := desc.Hostname
key, opChan := os.hp.regist(desc) key, opChan := os.hp.regist(desc)
defer os.hp.unregist(key) defer func() {
logger.Println("operationServer.Query : houston client unregistered ", hostname)
os.hp.unregist(key)
}()
logger.Println("operationServer.Query : houston client registered ", hostname)
Outer: Outer:
for { for {
@ -190,6 +254,25 @@ Outer:
return nil return nil
} }
func (os *operationServer) ReportDeployingProgress(ctx context.Context, dp *protos.DeployingProgress) (*protos.Empty, error) {
os.db.Lock()
defer os.db.Unlock()
for i, p := range os.db.progs {
if p.Hostname == dp.Hostname && p.Name == dp.Name && p.Version == dp.Version {
os.db.progs[i].DeployingProgress = dp
os.db.progs[i].Timestamp = time.Now().UTC().Unix()
return &protos.Empty{}, nil
}
}
os.db.progs = append(os.db.progs, deployingProgress{
DeployingProgress: dp,
Timestamp: time.Now().UTC().Unix(),
})
return &protos.Empty{}, nil
}
func (os *operationServer) Refresh(ctx context.Context, desc *protos.OperationQueryRequest) (*protos.Empty, error) { func (os *operationServer) Refresh(ctx context.Context, desc *protos.OperationQueryRequest) (*protos.Empty, error) {
os.hp.refresh(desc) os.hp.refresh(desc)
return &protos.Empty{}, nil return &protos.Empty{}, nil
@ -219,12 +302,29 @@ func (os *operationServer) Deploy(d DeployRequest) {
}) })
} }
for _, t := range targets { dps := make([]deployingProgress, len(targets))
now := time.Now().UTC().Unix()
for i, t := range targets {
dps[i] = deployingProgress{
DeployingProgress: &protos.DeployingProgress{
Hostname: t.Hostname,
Name: d.Name,
Version: d.Version,
State: "prepare",
},
Timestamp: now,
}
t.opChan <- &opdef{ t.opChan <- &opdef{
operation: shared.Deploy, operation: shared.Deploy,
args: d, args: d,
} }
} }
os.db.Lock()
defer os.db.Unlock()
os.db.progs = dps
} }
func (os *operationServer) Withdraw(d WithdrawRequest) { func (os *operationServer) Withdraw(d WithdrawRequest) {
@ -384,10 +484,43 @@ func (os *operationServer) Hosts() map[string]hostSnapshot {
return os.hp.allHosts() return os.hp.allHosts()
} }
func (os *operationServer) DeplyingProgress() []deployingProgress {
return os.db.clone()
}
func targetExportLoop(in chan string) {
all := make(map[string]bool)
for addr := range in {
logger.Println("targetExportLoop :", addr)
if addr[0] == '+' {
all[addr[1:]] = true
} else if addr[0] == '-' {
delete(all, addr[1:])
}
list := make([]string, 0, len(all))
for k := range all {
list = append(list, k)
}
output := []map[string]any{{"targets": list}}
if file, err := os.Create("prometheus_targets.json"); err == nil {
enc := json.NewEncoder(file)
enc.Encode(output)
file.Close()
}
}
}
func newOperationServer() *operationServer { func newOperationServer() *operationServer {
exportChan := make(chan string)
go targetExportLoop(exportChan)
return &operationServer{ return &operationServer{
hp: hostPool{ hp: hostPool{
hosts: map[string]*hostWithChan{}, hosts: map[string]*hostWithChan{},
exportChan: exportChan,
}, },
} }
} }

View File

@ -1,14 +1,11 @@
package server package server
import ( import (
"encoding/json"
"fmt" "fmt"
"net" "net"
"os"
"sync/atomic"
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/houston/client"
"repositories.action2quare.com/ayo/houston/shared" "repositories.action2quare.com/ayo/houston/shared"
"repositories.action2quare.com/ayo/houston/shared/protos" "repositories.action2quare.com/ayo/houston/shared/protos"
@ -23,9 +20,9 @@ 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"`
RunAsClient bool `json:"run_as_client"` MaingateApiToken string `json:"maingate_api_token"`
} }
type DeployRequest struct { type DeployRequest struct {
@ -108,26 +105,18 @@ type Operation interface {
RestartProcess(RestartProcessRequest) RestartProcess(RestartProcessRequest)
Upload(UploadRequest) Upload(UploadRequest)
Hosts() map[string]hostSnapshot Hosts() map[string]hostSnapshot
DeplyingProgress() []deployingProgress
}
type outerconfig struct {
Houston *struct {
Server serverConfig `json:"server"`
} `json:"houston"`
} }
func loadServerConfig() serverConfig { func loadServerConfig() serverConfig {
configFile, err := os.Open("config.json") var oc outerconfig
if err != nil { err := gocommon.LoadConfig[outerconfig](&oc)
logger.Println(err)
return serverConfig{
GrpcPort: 8080,
}
}
defer configFile.Close()
var config struct {
Houston *struct {
Server serverConfig `json:"server"`
} `json:"houston"`
}
dec := json.NewDecoder(configFile)
err = dec.Decode(&config)
if err != nil { if err != nil {
logger.Println(err) logger.Println(err)
return serverConfig{ return serverConfig{
@ -135,14 +124,7 @@ func loadServerConfig() serverConfig {
} }
} }
if config.Houston == nil { return oc.Houston.Server
logger.Println(`"houston" object is missing in config.json`)
return serverConfig{
GrpcPort: 8080,
}
}
return config.Houston.Server
} }
func NewServer() HoustonServer { func NewServer() HoustonServer {
@ -176,30 +158,7 @@ func (hs *houstonServer) Start() error {
return err return err
} }
closeCount := int32(0) return hs.rpcServer.Serve(lis)
var hc client.HoustonClient
if loadServerConfig().RunAsClient {
hc, err = client.NewClient(false)
if err != nil {
return err
}
go func() {
hc.Start()
if atomic.AddInt32(&closeCount, 1) == 1 {
hs.Stop()
}
}()
}
err = hs.rpcServer.Serve(lis)
if atomic.AddInt32(&closeCount, 1) == 1 {
if hc != nil {
hc.Shutdown()
}
}
return err
} }
func (hs *houstonServer) Stop() { func (hs *houstonServer) Stop() {

View File

@ -1,22 +1,16 @@
package shared package shared
import (
"io/fs"
"os"
"path"
"strings"
)
type Operation string type Operation string
const ( const (
Deploy = Operation("deploy") Deploy = Operation("deploy")
Withdraw = Operation("withdraw") Withdraw = Operation("withdraw")
Upgrade = Operation("upgrade") Upgrade = Operation("upgrade")
Start = Operation("start") Start = Operation("start")
Restart = Operation("restart") Restart = Operation("restart")
Stop = Operation("stop") Stop = Operation("stop")
Upload = Operation("upload") Upload = Operation("upload")
Exception = Operation("exception")
) )
type DeployRequest struct { type DeployRequest struct {
@ -33,9 +27,11 @@ type WithdrawRequest struct {
} }
type StartProcessRequest struct { 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 {
@ -57,62 +53,3 @@ type UploadRequest struct {
Filter string Filter string
DeleteAfterUploaded string // true, false DeleteAfterUploaded string // true, false
} }
type ParsedVersionString = []string
func ParseVersionString(ver string) ParsedVersionString {
return strings.Split(ver, ".")
}
func CompareVersionString(lhs, rhs ParsedVersionString) int {
minlen := len(lhs)
if minlen > len(rhs) {
minlen = len(rhs)
}
for i := 0; i < minlen; i++ {
if len(lhs[i]) < len(rhs[i]) {
return -1
}
if len(lhs[i]) > len(rhs[i]) {
return 1
}
if lhs[i] < rhs[i] {
return -1
}
if lhs[i] > rhs[i] {
return 1
}
}
return len(lhs) - len(rhs)
}
func FindLastestVersion(storageRoot, name string) (string, error) {
// 최신 버전을 찾음
targetPath := path.Join(storageRoot, name)
entries, err := os.ReadDir(targetPath)
if err != nil {
return "", err
}
if len(entries) == 0 {
return "", nil
}
var dironly []fs.DirEntry
for _, ent := range entries {
if ent.IsDir() {
dironly = append(dironly, ent)
}
}
latest := ParseVersionString(dironly[0].Name())
for i := 1; i < len(dironly); i++ {
next := ParseVersionString(dironly[i].Name())
if CompareVersionString(latest, next) < 0 {
latest = next
}
}
return strings.Join(latest, "."), nil
}

View File

@ -190,9 +190,11 @@ type OperationQueryRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
Procs []*ProcessDescription `protobuf:"bytes,2,rep,name=procs,proto3" json:"procs,omitempty"` PublicIp string `protobuf:"bytes,4,opt,name=public_ip,json=publicIp,proto3" json:"public_ip,omitempty"`
Deploys []*DeployedVersions `protobuf:"bytes,3,rep,name=deploys,proto3" json:"deploys,omitempty"` PrivateIp string `protobuf:"bytes,5,opt,name=private_ip,json=privateIp,proto3" json:"private_ip,omitempty"`
Procs []*ProcessDescription `protobuf:"bytes,2,rep,name=procs,proto3" json:"procs,omitempty"`
Deploys []*DeployedVersions `protobuf:"bytes,3,rep,name=deploys,proto3" json:"deploys,omitempty"`
} }
func (x *OperationQueryRequest) Reset() { func (x *OperationQueryRequest) Reset() {
@ -234,6 +236,20 @@ func (x *OperationQueryRequest) GetHostname() string {
return "" return ""
} }
func (x *OperationQueryRequest) GetPublicIp() string {
if x != nil {
return x.PublicIp
}
return ""
}
func (x *OperationQueryRequest) GetPrivateIp() string {
if x != nil {
return x.PrivateIp
}
return ""
}
func (x *OperationQueryRequest) GetProcs() []*ProcessDescription { func (x *OperationQueryRequest) GetProcs() []*ProcessDescription {
if x != nil { if x != nil {
return x.Procs return x.Procs
@ -382,6 +398,93 @@ func (x *OperationQueryResponse) GetArgs() map[string]string {
return nil return nil
} }
type DeployingProgress struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"`
Progress int64 `protobuf:"varint,5,opt,name=progress,proto3" json:"progress,omitempty"`
Total int64 `protobuf:"varint,6,opt,name=total,proto3" json:"total,omitempty"`
}
func (x *DeployingProgress) Reset() {
*x = DeployingProgress{}
if protoimpl.UnsafeEnabled {
mi := &file_protos_operation_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeployingProgress) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeployingProgress) ProtoMessage() {}
func (x *DeployingProgress) ProtoReflect() protoreflect.Message {
mi := &file_protos_operation_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeployingProgress.ProtoReflect.Descriptor instead.
func (*DeployingProgress) Descriptor() ([]byte, []int) {
return file_protos_operation_proto_rawDescGZIP(), []int{5}
}
func (x *DeployingProgress) GetHostname() string {
if x != nil {
return x.Hostname
}
return ""
}
func (x *DeployingProgress) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *DeployingProgress) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *DeployingProgress) GetState() string {
if x != nil {
return x.State
}
return ""
}
func (x *DeployingProgress) GetProgress() int64 {
if x != nil {
return x.Progress
}
return 0
}
func (x *DeployingProgress) GetTotal() int64 {
if x != nil {
return x.Total
}
return 0
}
var File_protos_operation_proto protoreflect.FileDescriptor var File_protos_operation_proto protoreflect.FileDescriptor
var file_protos_operation_proto_rawDesc = []byte{ var file_protos_operation_proto_rawDesc = []byte{
@ -397,49 +500,67 @@ var file_protos_operation_proto_rawDesc = []byte{
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x41, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x73, 0x22, 0x8b, 0x01, 0x0a, 0x15, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x73, 0x22, 0xc7, 0x01, 0x0a, 0x15, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51,
0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68,
0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68,
0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x63, 0x73, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x63, 0x5f, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c,
0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x69, 0x63, 0x49, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f,
0x63, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x18, 0x03, 0x20, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65, 0x65, 0x49, 0x70, 0x12, 0x29, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x22, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x73, 0x63,
0x8d, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x73, 0x63, 0x72, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x63, 0x73, 0x12, 0x2b,
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x11, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18, 0x6e, 0x73, 0x52, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x12,
0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02,
0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65,
0x03, 0x70, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72,
0xa6, 0x01, 0x0a, 0x16, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20,
0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61,
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64,
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0xa6, 0x01, 0x0a, 0x16,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65,
0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x1a, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,
0x37, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x72, 0x67, 0x73,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4e, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x41,
0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x70, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x69, 0x6e, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x67, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x12, 0x0b, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa5, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x69,
0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x04, 0x32, 0x78, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x16, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f,
0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65,
0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72,
0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20,
0x12, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72,
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72,
0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18,
0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x2a, 0x4e, 0x0a, 0x0c,
0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07,
0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x74, 0x6f,
0x70, 0x70, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x6e, 0x69,
0x6e, 0x67, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x10,
0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x04, 0x32, 0xb1, 0x01, 0x0a,
0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x05, 0x51, 0x75,
0x65, 0x72, 0x79, 0x12, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51,
0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x4f, 0x70,
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65,
0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x17, 0x52, 0x65, 0x70, 0x6f, 0x72,
0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65,
0x73, 0x73, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x72,
0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
0x42, 0x0f, 0x5a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -455,7 +576,7 @@ func file_protos_operation_proto_rawDescGZIP() []byte {
} }
var file_protos_operation_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_protos_operation_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_protos_operation_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_protos_operation_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_protos_operation_proto_goTypes = []interface{}{ var file_protos_operation_proto_goTypes = []interface{}{
(ProcessState)(0), // 0: ProcessState (ProcessState)(0), // 0: ProcessState
(*VersionAndArgs)(nil), // 1: VersionAndArgs (*VersionAndArgs)(nil), // 1: VersionAndArgs
@ -463,21 +584,24 @@ var file_protos_operation_proto_goTypes = []interface{}{
(*OperationQueryRequest)(nil), // 3: OperationQueryRequest (*OperationQueryRequest)(nil), // 3: OperationQueryRequest
(*ProcessDescription)(nil), // 4: ProcessDescription (*ProcessDescription)(nil), // 4: ProcessDescription
(*OperationQueryResponse)(nil), // 5: OperationQueryResponse (*OperationQueryResponse)(nil), // 5: OperationQueryResponse
nil, // 6: OperationQueryResponse.ArgsEntry (*DeployingProgress)(nil), // 6: DeployingProgress
(*Empty)(nil), // 7: Empty nil, // 7: OperationQueryResponse.ArgsEntry
(*Empty)(nil), // 8: Empty
} }
var file_protos_operation_proto_depIdxs = []int32{ var file_protos_operation_proto_depIdxs = []int32{
1, // 0: DeployedVersions.versions:type_name -> VersionAndArgs 1, // 0: DeployedVersions.versions:type_name -> VersionAndArgs
4, // 1: OperationQueryRequest.procs:type_name -> ProcessDescription 4, // 1: OperationQueryRequest.procs:type_name -> ProcessDescription
2, // 2: OperationQueryRequest.deploys:type_name -> DeployedVersions 2, // 2: OperationQueryRequest.deploys:type_name -> DeployedVersions
0, // 3: ProcessDescription.state:type_name -> ProcessState 0, // 3: ProcessDescription.state:type_name -> ProcessState
6, // 4: OperationQueryResponse.args:type_name -> OperationQueryResponse.ArgsEntry 7, // 4: OperationQueryResponse.args:type_name -> OperationQueryResponse.ArgsEntry
3, // 5: Operation.Query:input_type -> OperationQueryRequest 3, // 5: Operation.Query:input_type -> OperationQueryRequest
3, // 6: Operation.Refresh:input_type -> OperationQueryRequest 3, // 6: Operation.Refresh:input_type -> OperationQueryRequest
5, // 7: Operation.Query:output_type -> OperationQueryResponse 6, // 7: Operation.ReportDeployingProgress:input_type -> DeployingProgress
7, // 8: Operation.Refresh:output_type -> Empty 5, // 8: Operation.Query:output_type -> OperationQueryResponse
7, // [7:9] is the sub-list for method output_type 8, // 9: Operation.Refresh:output_type -> Empty
5, // [5:7] is the sub-list for method input_type 8, // 10: Operation.ReportDeployingProgress:output_type -> Empty
8, // [8:11] is the sub-list for method output_type
5, // [5:8] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee 5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name 0, // [0:5] is the sub-list for field type_name
@ -550,6 +674,18 @@ func file_protos_operation_proto_init() {
return nil return nil
} }
} }
file_protos_operation_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeployingProgress); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
@ -557,7 +693,7 @@ func file_protos_operation_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_protos_operation_proto_rawDesc, RawDescriptor: file_protos_operation_proto_rawDesc,
NumEnums: 1, NumEnums: 1,
NumMessages: 6, NumMessages: 7,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -24,6 +24,7 @@ const _ = grpc.SupportPackageIsVersion7
type OperationClient interface { type OperationClient interface {
Query(ctx context.Context, opts ...grpc.CallOption) (Operation_QueryClient, error) Query(ctx context.Context, opts ...grpc.CallOption) (Operation_QueryClient, error)
Refresh(ctx context.Context, in *OperationQueryRequest, opts ...grpc.CallOption) (*Empty, error) Refresh(ctx context.Context, in *OperationQueryRequest, opts ...grpc.CallOption) (*Empty, error)
ReportDeployingProgress(ctx context.Context, in *DeployingProgress, opts ...grpc.CallOption) (*Empty, error)
} }
type operationClient struct { type operationClient struct {
@ -74,12 +75,22 @@ func (c *operationClient) Refresh(ctx context.Context, in *OperationQueryRequest
return out, nil return out, nil
} }
func (c *operationClient) ReportDeployingProgress(ctx context.Context, in *DeployingProgress, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/Operation/ReportDeployingProgress", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// OperationServer is the server API for Operation service. // OperationServer is the server API for Operation service.
// All implementations must embed UnimplementedOperationServer // All implementations must embed UnimplementedOperationServer
// for forward compatibility // for forward compatibility
type OperationServer interface { type OperationServer interface {
Query(Operation_QueryServer) error Query(Operation_QueryServer) error
Refresh(context.Context, *OperationQueryRequest) (*Empty, error) Refresh(context.Context, *OperationQueryRequest) (*Empty, error)
ReportDeployingProgress(context.Context, *DeployingProgress) (*Empty, error)
mustEmbedUnimplementedOperationServer() mustEmbedUnimplementedOperationServer()
} }
@ -93,6 +104,9 @@ func (UnimplementedOperationServer) Query(Operation_QueryServer) error {
func (UnimplementedOperationServer) Refresh(context.Context, *OperationQueryRequest) (*Empty, error) { func (UnimplementedOperationServer) Refresh(context.Context, *OperationQueryRequest) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented") return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented")
} }
func (UnimplementedOperationServer) ReportDeployingProgress(context.Context, *DeployingProgress) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReportDeployingProgress not implemented")
}
func (UnimplementedOperationServer) mustEmbedUnimplementedOperationServer() {} func (UnimplementedOperationServer) mustEmbedUnimplementedOperationServer() {}
// UnsafeOperationServer may be embedded to opt out of forward compatibility for this service. // UnsafeOperationServer may be embedded to opt out of forward compatibility for this service.
@ -150,6 +164,24 @@ func _Operation_Refresh_Handler(srv interface{}, ctx context.Context, dec func(i
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Operation_ReportDeployingProgress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeployingProgress)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(OperationServer).ReportDeployingProgress(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Operation/ReportDeployingProgress",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(OperationServer).ReportDeployingProgress(ctx, req.(*DeployingProgress))
}
return interceptor(ctx, in, info, handler)
}
// Operation_ServiceDesc is the grpc.ServiceDesc for Operation service. // Operation_ServiceDesc is the grpc.ServiceDesc for Operation service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -161,6 +193,10 @@ var Operation_ServiceDesc = grpc.ServiceDesc{
MethodName: "Refresh", MethodName: "Refresh",
Handler: _Operation_Refresh_Handler, Handler: _Operation_Refresh_Handler,
}, },
{
MethodName: "ReportDeployingProgress",
Handler: _Operation_ReportDeployingProgress_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {