프로세스 종료시 알림

This commit is contained in:
2023-11-24 00:19:17 +09:00
parent d18fe3e3a1
commit 1a404e5361
9 changed files with 171 additions and 54 deletions

View File

@ -17,6 +17,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
@ -67,15 +68,30 @@ type HoustonClient interface {
Start() Start()
} }
var seq = int32(1)
type procmeta struct { type procmeta struct {
id int32
cmd *exec.Cmd cmd *exec.Cmd
name string name string
args []string args []string
version string version string
state protos.ProcessState state int32
stdin io.WriteCloser 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 { type houstonClient struct {
childProcs []*procmeta childProcs []*procmeta
extraMetrics unsafe.Pointer // map[string]float32 extraMetrics unsafe.Pointer // map[string]float32
@ -167,7 +183,7 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
Name: child.name, Name: child.name,
Args: child.args, Args: child.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),
}) })
} }
@ -283,7 +299,7 @@ func NewClient(standalone bool) (HoustonClient, error) {
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)
@ -291,7 +307,7 @@ 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{ hc.startChildProcess(&shared.StartProcessRequest{
Version: proc.version, Version: proc.version,
Name: proc.name, Name: proc.name,
@ -407,6 +423,30 @@ func NewClient(standalone bool) (HoustonClient, error) {
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 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 { 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)
} }
} }

View File

@ -153,23 +153,27 @@ func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) *
if err == nil && fi.IsDir() { if err == nil && fi.IsDir() {
exefile := "./" + path.Clean(strings.TrimPrefix(req.Args[0], "/")) exefile := "./" + path.Clean(strings.TrimPrefix(req.Args[0], "/"))
err = set_permission(path.Join(verpath, exefile)) os.Chmod(path.Join(verpath, exefile), 0777)
if err != nil {
logger.Println("set_permission failed :", err)
return nil
}
exef, _ := os.Executable() exef, _ := os.Executable()
cmd := exec.Command(path.Join(path.Dir(exef), verpath, exefile), req.Args[1:]...) 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 cmd.Dir = verpath
stdin, _ := cmd.StdinPipe() stdin, _ := cmd.StdinPipe()
seq++
return &procmeta{ return &procmeta{
id: seq,
cmd: cmd, cmd: cmd,
name: req.Name, name: req.Name,
args: req.Args, args: req.Args,
version: req.Version, version: req.Version,
state: protos.ProcessState_Stopped, state: int32(protos.ProcessState_Stopped),
stdin: stdin, stdin: stdin,
} }
} }
@ -236,6 +240,17 @@ func (hc *houstonClient) launch(meta *procmeta) error {
var metricBuffer []byte var metricBuffer []byte
metricValues := make(map[string]metricValueAccessor) 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 { for {
buff, err := reader.ReadBytes('\n') buff, err := reader.ReadBytes('\n')
if err != nil { if err != nil {
@ -319,11 +334,11 @@ func (hc *houstonClient) launch(meta *procmeta) error {
go stdReader(meta.name, stdout) go stdReader(meta.name, stdout)
logger.Println("startChildProcess :", meta.cmd.Args) 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 meta.setState(protos.ProcessState_Running)
} }
return err return err
} }
@ -389,7 +404,7 @@ func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op pro
var remains []*procmeta var remains []*procmeta
var killing []*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
} }
@ -419,7 +434,7 @@ func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op pro
if len(killing) > 0 { if len(killing) > 0 {
for _, proc := range killing { for _, proc := range killing {
proc.state = protos.ProcessState_Stopping proc.setState(protos.ProcessState_Stopping)
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)
} }
@ -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()) op.Refresh(context.Background(), hc.makeOperationQueryRequest())
hc.exitChan <- proc.cmd hc.exitChan <- proc.cmd

47
client/platform_linux.go Normal file
View File

@ -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()
}

View File

@ -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()
}

View File

@ -1,7 +0,0 @@
//go:build !(client && linux)
package client
func set_permission(path string) error {
return nil
}

View File

@ -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()
}

2
go.mod
View File

@ -10,7 +10,7 @@ require (
golang.org/x/text v0.10.0 golang.org/x/text v0.10.0
google.golang.org/grpc v1.56.0 google.golang.org/grpc v1.56.0
google.golang.org/protobuf v1.31.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 ( require (

4
go.sum
View File

@ -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= 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 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= 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-20231123110857-d672b5dd90ec h1:hB0bjNdS/5xClLR/vhPcACLRbpuxaLiCRwH6PV4a6EU=
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/go.mod h1:XvklTTSvQX5uviivGBcZo8eIL+mV94W2e4uBBXcT5JY=

View File

@ -10,13 +10,14 @@ import (
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 {