From 1a404e536141424826a73fa179395fcd532e7e78 Mon Sep 17 00:00:00 2001 From: mountain Date: Fri, 24 Nov 2023 00:19:17 +0900 Subject: [PATCH] =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=EC=A2=85=EB=A3=8C=EC=8B=9C=20=EC=95=8C=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/client.go | 50 ++++++++++++++++++++++++++++---- client/operation.go | 37 ++++++++++++++++------- client/platform_linux.go | 47 ++++++++++++++++++++++++++++++ client/platform_windows.go | 42 +++++++++++++++++++++++++++ client/set_permission_default.go | 7 ----- client/set_permission_linux.go | 21 -------------- go.mod | 2 +- go.sum | 4 +-- shared/operator.go | 15 +++++----- 9 files changed, 171 insertions(+), 54 deletions(-) create mode 100644 client/platform_linux.go create mode 100644 client/platform_windows.go delete mode 100644 client/set_permission_default.go delete mode 100644 client/set_permission_linux.go diff --git a/client/client.go b/client/client.go index 6602b74..c9eddbf 100644 --- a/client/client.go +++ b/client/client.go @@ -17,6 +17,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "syscall" "time" "unsafe" @@ -67,15 +68,30 @@ type HoustonClient interface { Start() } +var seq = int32(1) + type procmeta struct { + id int32 cmd *exec.Cmd name string args []string version string - state protos.ProcessState + state int32 stdin io.WriteCloser } +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 { childProcs []*procmeta extraMetrics unsafe.Pointer // map[string]float32 @@ -167,7 +183,7 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque Name: child.name, Args: child.args, Version: child.version, - State: child.state, + State: child.getState(), Pid: int32(child.cmd.Process.Pid), }) } @@ -283,7 +299,7 @@ func NewClient(standalone bool) (HoustonClient, error) { var newprocs []*procmeta for _, proc := range hc.childProcs { 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) { if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil { proc.cmd.Process.Signal(os.Kill) @@ -291,7 +307,7 @@ func NewClient(standalone bool) (HoustonClient, error) { proc.cmd.Wait() proc.cmd.Process.Release() - if proc.state == protos.ProcessState_Restart { + if proc.isState(protos.ProcessState_Restart) { hc.startChildProcess(&shared.StartProcessRequest{ Version: proc.version, Name: proc.name, @@ -407,6 +423,30 @@ func NewClient(standalone bool) (HoustonClient, error) { if err := hc.uploadFiles(&ur); err != nil { logger.Println(err) } + + case shared.Exception: + idstr := resp.Args["id"] + id64, _ := strconv.ParseInt(idstr, 10, 0) + id := int32(id64) + + var killing *procmeta + var remains []*procmeta + for _, meta := range hc.childProcs { + if meta.id == id { + killing = meta + } else { + remains = append(remains, meta) + } + } + + if killing != nil { + killing.cmd.Wait() + killing.cmd.Process.Release() + } + + hc.childProcs = remains + + op.Refresh(context.Background(), hc.makeOperationQueryRequest()) } } } @@ -428,7 +468,7 @@ func (hc *houstonClient) Start() { for _, proc := range hc.childProcs { if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil { proc.cmd.Process.Signal(os.Kill) - proc.state = protos.ProcessState_Stopping + proc.setState(protos.ProcessState_Stopping) } } diff --git a/client/operation.go b/client/operation.go index 228b6e1..e0150e1 100644 --- a/client/operation.go +++ b/client/operation.go @@ -153,23 +153,27 @@ func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) * if err == nil && fi.IsDir() { exefile := "./" + path.Clean(strings.TrimPrefix(req.Args[0], "/")) - err = set_permission(path.Join(verpath, exefile)) - if err != nil { - logger.Println("set_permission failed :", err) - return nil - } + os.Chmod(path.Join(verpath, exefile), 0777) exef, _ := os.Executable() cmd := exec.Command(path.Join(path.Dir(exef), verpath, exefile), req.Args[1:]...) + + if err := run_prelaunch_script(cmd.Args[0]); err != nil { + logger.Println("run_prelaunch_script failed :", cmd.Args[0], err) + return nil + } + cmd.Dir = verpath stdin, _ := cmd.StdinPipe() + seq++ return &procmeta{ + id: seq, cmd: cmd, name: req.Name, args: req.Args, version: req.Version, - state: protos.ProcessState_Stopped, + state: int32(protos.ProcessState_Stopped), stdin: stdin, } } @@ -236,6 +240,17 @@ func (hc *houstonClient) launch(meta *procmeta) error { var metricBuffer []byte metricValues := make(map[string]metricValueAccessor) + defer func() { + if meta.isState(protos.ProcessState_Running) { + hc.operationChan <- &protos.OperationQueryResponse{ + Operation: string(shared.Exception), + Args: map[string]string{ + "id": fmt.Sprintf("%d", meta.id), + }, + } + } + }() + for { buff, err := reader.ReadBytes('\n') if err != nil { @@ -319,11 +334,11 @@ func (hc *houstonClient) launch(meta *procmeta) error { go stdReader(meta.name, stdout) logger.Println("startChildProcess :", meta.cmd.Args) - err = meta.cmd.Start() if err == nil { - meta.state = protos.ProcessState_Running + meta.setState(protos.ProcessState_Running) } + return err } @@ -389,7 +404,7 @@ func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op pro var remains []*procmeta var killing []*procmeta for _, proc := range hc.childProcs { - if proc.state != protos.ProcessState_Running { + if !proc.isState(protos.ProcessState_Running) { continue } @@ -419,7 +434,7 @@ func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op pro if len(killing) > 0 { for _, proc := range killing { - proc.state = protos.ProcessState_Stopping + proc.setState(protos.ProcessState_Stopping) if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil { proc.cmd.Process.Signal(os.Kill) } @@ -453,7 +468,7 @@ func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest, } } - proc.state = protos.ProcessState_Restart + proc.setState(protos.ProcessState_Restart) op.Refresh(context.Background(), hc.makeOperationQueryRequest()) hc.exitChan <- proc.cmd diff --git a/client/platform_linux.go b/client/platform_linux.go new file mode 100644 index 0000000..4fc23fc --- /dev/null +++ b/client/platform_linux.go @@ -0,0 +1,47 @@ +//go:build client && linux + +package client + +import ( + "errors" + "io/fs" + "os" + "os/exec" + "path" + + "repositories.action2quare.com/ayo/gocommon/logger" +) + +func run_prelaunch_script(exepath string) error { + scriptPath := path.Join(path.Dir(exepath), "prelaunch.sh") + fi, err := os.Stat(scriptPath) + if errors.Is(err, fs.ErrNotExist) { + logger.Println("prelaunch.sh not exists :", scriptPath, err) + return nil + } + + if fi == nil { + logger.Println("prelaunch.sh fi is nil :", scriptPath) + return nil + } + + os.Chmod(scriptPath, 0777) + + logger.Println("run_prelaunch_script :", "/bin/bash", scriptPath, exepath) + return exec.Command("/bin/bash", scriptPath, exepath).Run() +} + +func run_postlaunch_script(exepath string) error { + scriptPath := path.Join(path.Dir(exepath), "postlaunch.sh") + fi, err := os.Stat(scriptPath) + if errors.Is(err, fs.ErrNotExist) { + return nil + } + + if fi == nil { + return nil + } + + os.Chmod(scriptPath, 0777) + return exec.Command("/bin/bash", scriptPath, exepath).Run() +} diff --git a/client/platform_windows.go b/client/platform_windows.go new file mode 100644 index 0000000..89aa4f2 --- /dev/null +++ b/client/platform_windows.go @@ -0,0 +1,42 @@ +//go:build !(client && linux) + +package client + +import ( + "errors" + "io/fs" + "os" + "os/exec" + "path" +) + +func run_prelaunch_script(exepath string) error { + scriptPath := path.Join(path.Dir(exepath), "prelaunch.bat") + fi, err := os.Stat(scriptPath) + if errors.Is(err, fs.ErrNotExist) { + return nil + } + + if fi == nil { + return nil + } + + os.Chmod(scriptPath, 0777) + + return exec.Command("cmd", scriptPath).Run() +} + +func run_postlaunch_script(exepath string) error { + scriptPath := path.Join(path.Dir(exepath), "postlaunch.bat") + fi, err := os.Stat(scriptPath) + if errors.Is(err, fs.ErrNotExist) { + return nil + } + + if fi == nil { + return nil + } + + os.Chmod(scriptPath, 0777) + return exec.Command("cmd", scriptPath).Run() +} diff --git a/client/set_permission_default.go b/client/set_permission_default.go deleted file mode 100644 index f13ff7c..0000000 --- a/client/set_permission_default.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !(client && linux) - -package client - -func set_permission(path string) error { - return nil -} diff --git a/client/set_permission_linux.go b/client/set_permission_linux.go deleted file mode 100644 index 689b862..0000000 --- a/client/set_permission_linux.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build client && linux - -package client - -import ( - "os" - "os/exec" -) - -func init() { - file, _ := os.OpenFile("setcap.sh", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0777) - file.Write([]byte("sudo setcap 'cap_net_bind_service=+ep' $1")) - file.Sync() - file.Close() -} - -func set_permission(path string) error { - os.Chmod(path, 0777) - cmd := exec.Command("/bin/bash", "./setcap.sh", path) - return cmd.Run() -} diff --git a/go.mod b/go.mod index 4226b3a..cae96a7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( golang.org/x/text v0.10.0 google.golang.org/grpc v1.56.0 google.golang.org/protobuf v1.31.0 - repositories.action2quare.com/ayo/gocommon v0.0.0-20231123013925-8adef2adb814 + repositories.action2quare.com/ayo/gocommon v0.0.0-20231123110857-d672b5dd90ec ) require ( diff --git a/go.sum b/go.sum index 49f2dad..461b9f3 100644 --- a/go.sum +++ b/go.sum @@ -120,5 +120,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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-20231123013925-8adef2adb814 h1:3K7YBlNbm7AJJLt8LJ2iDHxgJG5vL7hE1VvdG7VwKCM= -repositories.action2quare.com/ayo/gocommon v0.0.0-20231123013925-8adef2adb814/go.mod h1:XvklTTSvQX5uviivGBcZo8eIL+mV94W2e4uBBXcT5JY= +repositories.action2quare.com/ayo/gocommon v0.0.0-20231123110857-d672b5dd90ec h1:hB0bjNdS/5xClLR/vhPcACLRbpuxaLiCRwH6PV4a6EU= +repositories.action2quare.com/ayo/gocommon v0.0.0-20231123110857-d672b5dd90ec/go.mod h1:XvklTTSvQX5uviivGBcZo8eIL+mV94W2e4uBBXcT5JY= diff --git a/shared/operator.go b/shared/operator.go index 6f20ff0..10b147d 100644 --- a/shared/operator.go +++ b/shared/operator.go @@ -10,13 +10,14 @@ import ( type Operation string const ( - Deploy = Operation("deploy") - Withdraw = Operation("withdraw") - Upgrade = Operation("upgrade") - Start = Operation("start") - Restart = Operation("restart") - Stop = Operation("stop") - Upload = Operation("upload") + Deploy = Operation("deploy") + Withdraw = Operation("withdraw") + Upgrade = Operation("upgrade") + Start = Operation("start") + Restart = Operation("restart") + Stop = Operation("stop") + Upload = Operation("upload") + Exception = Operation("exception") ) type DeployRequest struct {