Files
houston/client/client.go

767 lines
18 KiB
Go
Raw Normal View History

2023-05-21 23:37:54 +09:00
package client
import (
"context"
2024-08-16 15:05:05 +09:00
"encoding/json"
2023-06-09 11:53:31 +09:00
"errors"
"fmt"
2023-05-21 23:37:54 +09:00
"io"
2023-06-14 14:16:47 +09:00
"io/fs"
"net/http"
2023-05-21 23:37:54 +09:00
"os"
"os/exec"
2023-05-25 10:59:04 +09:00
"os/signal"
2023-05-21 23:37:54 +09:00
"path"
"path/filepath"
2023-05-21 23:37:54 +09:00
"reflect"
"sort"
"strconv"
2023-06-27 10:53:14 +09:00
"strings"
"sync"
2023-11-24 00:19:17 +09:00
"sync/atomic"
2023-05-25 10:59:04 +09:00
"syscall"
"time"
2023-05-21 23:37:54 +09:00
"unsafe"
"github.com/djherbis/times"
"repositories.action2quare.com/ayo/gocommon"
2024-06-05 13:40:10 +09:00
"repositories.action2quare.com/ayo/gocommon/flagx"
2023-06-14 00:13:51 +09:00
"repositories.action2quare.com/ayo/gocommon/logger"
2023-05-22 02:13:03 +09:00
"repositories.action2quare.com/ayo/houston/shared"
"repositories.action2quare.com/ayo/houston/shared/protos"
2023-05-21 23:37:54 +09:00
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
2024-11-12 12:25:52 +09:00
"google.golang.org/grpc/status"
2023-05-21 23:37:54 +09:00
)
2024-06-05 13:40:10 +09:00
type runcommand struct {
Exec string `json:"exec"`
Args []string `json:"args"`
Version string `json:"version"`
AutoRestart bool `json:"auto_restart"`
OutputLogFile string `json:"logfile"`
2024-08-16 15:05:05 +09:00
}
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
2024-06-05 13:40:10 +09:00
}
2023-06-09 11:53:31 +09:00
type clientConfig struct {
2024-06-05 13:40:10 +09:00
GrpcAddress string `json:"grpc_server_address"`
HttpAddress string `json:"http_server_address"`
StorageRoot string `json:"storage_path"`
MetricNamespace string `json:"metric_namespace"`
ConstLabels map[string]string `json:"metric_const_labels"`
Autorun map[string]runcommand `json:"autorun"`
2023-06-09 11:53:31 +09:00
}
2024-06-05 13:40:10 +09:00
var autorun = flagx.String("autorun", "", "")
type outerconfig struct {
Houston *struct {
Client clientConfig `json:"client"`
} `json:"houston"`
}
2023-06-13 16:26:00 +09:00
func loadClientConfig() (clientConfig, error) {
var oc outerconfig
err := gocommon.LoadConfig[outerconfig](&oc)
2023-06-13 16:26:00 +09:00
if err != nil {
logger.Println(err)
2023-06-13 16:26:00 +09:00
return clientConfig{}, err
}
return oc.Houston.Client, nil
2023-06-09 11:53:31 +09:00
}
2023-05-21 23:37:54 +09:00
type HoustonClient interface {
Shutdown()
2023-05-25 10:59:04 +09:00
Start()
2023-05-21 23:37:54 +09:00
}
2023-11-24 00:19:17 +09:00
var seq = int32(1)
2023-05-21 23:37:54 +09:00
type procmeta struct {
id int32
cmd *exec.Cmd
name string
args []string
version string
verpath string
recover bool
state int32
stdin io.WriteCloser
logfile string
keepLatest bool
2023-05-21 23:37:54 +09:00
}
2023-11-24 00:19:17 +09:00
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 uploadRequest struct {
logFile string
name string
version string
}
2023-05-21 23:37:54 +09:00
type houstonClient struct {
childProcs []*procmeta
extraMetrics unsafe.Pointer // map[string]float32
deploys map[string][]*protos.VersionAndArgs
shutdownFunc context.CancelFunc
ctx context.Context
operationChan chan *protos.OperationQueryResponse
exitChan chan *exec.Cmd
clientChan chan *grpc.ClientConn
uploadChan chan uploadRequest
timestamp string
wg sync.WaitGroup
config clientConfig
version string
standalone bool
siblingProcIndex map[string]uint64
2023-05-21 23:37:54 +09:00
}
func unmarshal[T any](val *T, src map[string]string) {
2024-09-23 21:15:15 +09:00
defer func() {
r := recover()
if r != nil {
logger.Error(r)
}
}()
2023-05-21 23:37:54 +09:00
argval := reflect.ValueOf(val)
for i := 0; i < argval.Elem().Type().NumField(); i++ {
if !argval.Elem().Type().Field(i).IsExported() {
continue
}
arg := src[argval.Elem().Type().Field(i).Name]
if argval.Elem().Field(i).CanInt() {
num, _ := strconv.ParseInt(arg, 10, 0)
argval.Elem().Field(i).SetInt(num)
2023-06-27 10:53:14 +09:00
} else if argval.Elem().Field(i).Kind() == reflect.Array || argval.Elem().Field(i).Kind() == reflect.Slice {
conv := strings.Split(arg, "\n")
argval.Elem().Field(i).Set(reflect.ValueOf(conv))
2024-09-23 21:15:15 +09:00
} else if argval.Elem().Field(i).Kind() == reflect.Bool {
bv, _ := strconv.ParseBool(arg)
argval.Elem().Field(i).SetBool(bv)
2023-05-21 23:37:54 +09:00
} else {
argval.Elem().Field(i).SetString(arg)
}
}
2023-11-25 22:16:12 +09:00
logger.Println("operation receive :", argval.Elem().Type().Name(), *val)
2023-05-21 23:37:54 +09:00
}
2024-02-07 17:03:19 +09:00
type version_args_ts struct {
*protos.VersionAndArgs
modTime time.Time
}
func gatherDeployedPrograms(storageRoot, name string) (out []*protos.VersionAndArgs) {
var rawvers []version_args_ts
2023-06-14 14:16:47 +09:00
targetPath := path.Join(storageRoot, name)
if vers, err := os.ReadDir(targetPath); err == nil {
2023-05-21 23:37:54 +09:00
for _, ver := range vers {
if ver.IsDir() {
2024-02-07 17:03:19 +09:00
fi, _ := ver.Info()
2023-06-14 14:16:47 +09:00
args := lastExecutionArgs(path.Join(targetPath, ver.Name()))
2024-02-07 17:03:19 +09:00
rawvers = append(rawvers, version_args_ts{
VersionAndArgs: &protos.VersionAndArgs{
Version: ver.Name(),
Args: args,
},
modTime: fi.ModTime(),
2023-05-21 23:37:54 +09:00
})
}
}
}
sort.Slice(rawvers, func(i, j int) bool {
2024-02-07 17:03:19 +09:00
return rawvers[i].modTime.After(rawvers[j].modTime)
2023-05-21 23:37:54 +09:00
})
2024-02-15 12:17:22 +09:00
2024-02-07 17:03:19 +09:00
for _, v := range rawvers {
out = append(out, v.VersionAndArgs)
}
return
2023-05-21 23:37:54 +09:00
}
func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryRequest {
2023-06-29 12:05:11 +09:00
var procs []*protos.ProcessDescription
var deploys []*protos.DeployedVersions
var selfname string
var selfargs []string
2023-06-29 11:00:26 +09:00
if hc.standalone {
selfname = path.Base(filepath.ToSlash(os.Args[0]))
2023-06-29 12:05:11 +09:00
selfargs = os.Args[1:]
2023-06-29 11:00:26 +09:00
} else {
2023-06-29 12:05:11 +09:00
selfname = "houston"
selfargs = []string{}
2023-06-29 11:00:26 +09:00
}
if len(path.Ext(selfname)) > 0 {
selfname = selfname[:len(selfname)-len(path.Ext(selfname))]
}
2023-06-29 12:05:11 +09:00
procs = append(procs, &protos.ProcessDescription{
Name: selfname,
Args: selfargs,
Version: hc.version,
State: protos.ProcessState_Running,
Pid: int32(os.Getpid()),
})
deploys = append(deploys, &protos.DeployedVersions{
Name: selfname,
Versions: []*protos.VersionAndArgs{
{Version: hc.version, Args: selfargs},
2023-06-29 12:05:11 +09:00
},
})
2023-05-21 23:37:54 +09:00
for _, child := range hc.childProcs {
procs = append(procs, &protos.ProcessDescription{
2023-05-23 10:57:24 +09:00
Name: child.name,
2023-11-29 18:46:10 +09:00
Args: child.cmd.Args,
2023-05-23 10:57:24 +09:00
Version: child.version,
2023-11-24 00:19:17 +09:00
State: child.getState(),
2023-05-23 10:57:24 +09:00
Pid: int32(child.cmd.Process.Pid),
2023-05-21 23:37:54 +09:00
})
}
for name, prog := range hc.deploys {
deploys = append(deploys, &protos.DeployedVersions{
Name: name,
Versions: prog,
})
}
2023-06-29 12:05:11 +09:00
2023-06-29 11:00:26 +09:00
hn, _ := os.Hostname()
2023-05-21 23:37:54 +09:00
return &protos.OperationQueryRequest{
Hostname: hn,
PublicIp: os.Getenv("PUBIP"),
PrivateIp: os.Getenv("PRVIP"),
Procs: procs,
Deploys: deploys,
2023-05-21 23:37:54 +09:00
}
}
2023-06-29 11:00:26 +09:00
func NewClient(standalone bool) (HoustonClient, error) {
2023-06-13 16:26:00 +09:00
clientConfig, err := loadClientConfig()
if err != nil {
2023-06-09 11:53:31 +09:00
return nil, err
}
2023-06-13 16:26:00 +09:00
if len(clientConfig.GrpcAddress) == 0 {
2023-06-09 11:53:31 +09:00
return nil, errors.New("client.grpc_server_address is missing")
}
2023-06-13 16:26:00 +09:00
if len(clientConfig.HttpAddress) == 0 {
2023-06-09 11:53:31 +09:00
return nil, errors.New("client.http_server_address is missing")
}
2023-05-21 23:37:54 +09:00
exefile, err := os.Executable()
if err != nil {
return nil, err
}
exefi, err := os.Stat(exefile)
if err != nil {
return nil, err
}
2023-06-14 14:16:47 +09:00
sp, err := os.Stat(clientConfig.StorageRoot)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
err = os.MkdirAll(clientConfig.StorageRoot, 0775)
}
} else if !sp.IsDir() {
err = errors.New(clientConfig.StorageRoot + " is not directory")
}
if err != nil {
return nil, err
}
2023-05-21 23:37:54 +09:00
deploys := make(map[string][]*protos.VersionAndArgs)
2023-06-14 14:16:47 +09:00
if dirs, err := os.ReadDir(clientConfig.StorageRoot); err == nil {
2023-05-21 23:37:54 +09:00
for _, dir := range dirs {
if dir.IsDir() {
2023-06-14 14:16:47 +09:00
flagf := path.Join(clientConfig.StorageRoot, dir.Name(), "@houston")
2023-05-21 23:37:54 +09:00
if _, err := os.Stat(flagf); !os.IsNotExist(err) {
2023-06-14 14:16:47 +09:00
deploys[dir.Name()] = gatherDeployedPrograms(clientConfig.StorageRoot, dir.Name())
2023-05-21 23:37:54 +09:00
}
}
}
}
2023-06-29 11:00:26 +09:00
ver, _ := os.ReadFile("@version")
if len(ver) == 0 {
ver = []byte("0.0.0")
}
2023-05-21 23:37:54 +09:00
hc := &houstonClient{
config: clientConfig,
clientChan: make(chan *grpc.ClientConn),
extraMetrics: unsafe.Pointer(&map[string]float32{}),
deploys: deploys,
timestamp: exefi.ModTime().String(),
version: string(ver),
standalone: standalone,
uploadChan: make(chan uploadRequest, 100),
siblingProcIndex: make(map[string]uint64),
2023-05-21 23:37:54 +09:00
}
ctx, cancel := context.WithCancel(context.Background())
exitChan := make(chan *exec.Cmd, 10)
operationChan := make(chan *protos.OperationQueryResponse, 10)
hc.wg.Add(1)
2024-11-12 16:43:39 +09:00
ignoreRecover := int32(0)
2024-06-05 13:40:10 +09:00
// autorun 처리
2023-05-21 23:37:54 +09:00
go func() {
defer hc.wg.Done()
2023-05-21 23:37:54 +09:00
// 메인 operator
2023-06-14 01:50:40 +09:00
var op protos.OperationClient
myname, _ := os.Executable()
myname = path.Base(filepath.ToSlash(myname))
if len(path.Ext(myname)) > 0 {
myname = myname[:len(myname)-len(path.Ext(myname))]
}
if myname == "__debug_bin" {
myname = "houston"
}
2023-05-21 23:37:54 +09:00
for {
select {
case <-ctx.Done():
return
2023-06-14 01:50:40 +09:00
case newClient := <-hc.clientChan:
op = protos.NewOperationClient(newClient)
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
2023-06-14 01:50:40 +09:00
2023-05-21 23:37:54 +09:00
case exited := <-exitChan:
var newprocs []*procmeta
2023-05-21 23:37:54 +09:00
for _, proc := range hc.childProcs {
if proc.cmd == exited {
2023-11-24 00:19:17 +09:00
if proc.isState(protos.ProcessState_Running) || proc.isState(protos.ProcessState_Restart) {
2023-06-27 09:44:56 +09:00
go func(proc *procmeta) {
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
proc.cmd.Process.Signal(os.Kill)
}
2023-06-27 09:44:56 +09:00
proc.cmd.Wait()
proc.cmd.Process.Release()
2023-11-24 00:19:17 +09:00
if proc.isState(protos.ProcessState_Restart) {
if proc.keepLatest {
proc.version = "latest"
}
if err := hc.startChildProcess(&shared.StartProcessRequest{
2023-06-27 09:44:56 +09:00
Version: proc.version,
Name: proc.name,
2023-11-13 16:43:56 +09:00
Args: proc.args,
}); err != nil {
logger.ErrorWithCallStack(err)
} else {
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
}
2023-06-27 09:44:56 +09:00
}
}(proc)
}
} else {
newprocs = append(newprocs, proc)
2023-05-21 23:37:54 +09:00
}
}
hc.childProcs = newprocs
op.Refresh(ctx, hc.makeOperationQueryRequest())
2023-05-21 23:37:54 +09:00
case resp := <-operationChan:
2024-08-22 16:35:14 +09:00
logger.Println("houton query operation :", resp.Operation)
2023-05-21 23:37:54 +09:00
switch shared.Operation(resp.Operation) {
case shared.Deploy:
var dr shared.DeployRequest
unmarshal(&dr, resp.Args)
2024-08-22 16:35:14 +09:00
logger.Println("args :", dr)
2024-11-11 20:59:12 +09:00
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 {
if dr.Name == "houston" {
// houston_update_dir 다운로드가 완료되었으므로 종료
2024-11-11 20:59:12 +09:00
// 종료되고나면 스크립트가 알아서 재 실행
hc.Shutdown()
return
}
2024-11-11 20:59:12 +09:00
prog := gatherDeployedPrograms(hc.config.StorageRoot, dr.Name)
hc.deploys[dr.Name] = prog
op.Refresh(ctx, hc.makeOperationQueryRequest())
op.ReportDeployingProgress(ctx, &protos.DeployingProgress{
Hostname: hn,
Name: dr.Name,
Version: dr.Version,
State: "success",
Progress: 0,
Total: 0,
})
2023-05-21 23:37:54 +09:00
} else {
2024-11-11 20:59:12 +09:00
logger.Println(err)
op.ReportDeployingProgress(ctx, &protos.DeployingProgress{
Hostname: hn,
Name: dr.Name,
Version: dr.Version,
State: "fail:" + err.Error(),
Progress: 0,
Total: 0,
})
2023-05-21 23:37:54 +09:00
}
case shared.Withdraw:
var wr shared.WithdrawRequest
unmarshal(&wr, resp.Args)
2024-08-22 16:35:14 +09:00
logger.Println("args :", wr)
2023-05-21 23:37:54 +09:00
err := hc.withdraw(&wr)
if err == nil {
2023-06-14 14:16:47 +09:00
prog := gatherDeployedPrograms(hc.config.StorageRoot, wr.Name)
2023-06-09 11:53:31 +09:00
if len(prog) == 0 {
delete(hc.deploys, wr.Name)
} else {
hc.deploys[wr.Name] = prog
}
2023-05-21 23:37:54 +09:00
op.Refresh(ctx, hc.makeOperationQueryRequest())
} else {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-21 23:37:54 +09:00
}
case shared.Start:
var sr shared.StartProcessRequest
unmarshal(&sr, resp.Args)
2024-08-22 16:35:14 +09:00
logger.Println("args :", sr)
if err := hc.startChildProcess(&sr); err != nil {
logger.ErrorWithCallStack(err)
} else {
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
2023-05-21 23:37:54 +09:00
}
case shared.Stop:
var sr shared.StopProcessRequest
unmarshal(&sr, resp.Args)
2024-08-22 16:35:14 +09:00
logger.Println("args :", sr)
2023-06-14 01:50:40 +09:00
if err := hc.stopChildProcess(&sr, op); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-21 23:37:54 +09:00
}
case shared.Restart:
var rr shared.RestartProcessRequest
unmarshal(&rr, resp.Args)
2024-08-22 16:35:14 +09:00
logger.Println("args :", rr)
2023-06-14 01:50:40 +09:00
if err := hc.restartChildProcess(&rr, op); err != nil {
2023-06-14 00:13:51 +09:00
logger.Println(err)
2023-05-21 23:37:54 +09:00
}
2023-11-24 00:19:17 +09:00
case shared.Exception:
idstr := resp.Args["id"]
id64, _ := strconv.ParseInt(idstr, 10, 0)
id := int32(id64)
2024-08-16 15:05:05 +09:00
var found *procmeta
hc.childProcs = gocommon.ShrinkSlice(hc.childProcs, func(e *procmeta) bool {
if e.id == id {
2024-08-16 15:05:05 +09:00
found = e
return true
2023-11-24 00:19:17 +09:00
}
return false
})
2023-11-24 00:19:17 +09:00
2024-08-16 15:05:05 +09:00
if found != nil {
found.cmd.Wait()
found.cmd.Process.Release()
2024-11-12 16:43:39 +09:00
if found.recover && atomic.LoadInt32(&ignoreRecover) == 0 {
2024-08-16 15:05:05 +09:00
time.Sleep(time.Second)
sr := shared.StartProcessRequest{
Name: found.name,
Version: found.version,
Args: found.args,
AutoRestart: found.recover,
OutputLogFile: found.logfile,
2024-08-16 15:05:05 +09:00
}
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())
}
2023-05-21 23:37:54 +09:00
}
}
}
}()
2023-12-05 18:54:42 +09:00
hc.shutdownFunc = func() {
// child process 강제 종료
2024-11-12 16:43:39 +09:00
atomic.StoreInt32(&ignoreRecover, 1)
2023-12-05 18:54:42 +09:00
for _, procmeta := range hc.childProcs {
if procmeta.cmd != nil && procmeta.cmd.Process != nil {
procmeta.cmd.Process.Signal(os.Kill)
}
}
time.Sleep(time.Second)
2023-12-05 18:54:42 +09:00
cancel()
}
2023-05-25 10:59:04 +09:00
hc.exitChan = exitChan
hc.ctx = ctx
hc.operationChan = operationChan
2023-05-21 23:37:54 +09:00
2023-05-25 10:59:04 +09:00
return hc, nil
}
func uploadSafe(url, filePath, name, version string) error {
defer func() {
r := recover()
if r != nil {
logger.Error(r)
}
}()
t, err := times.Stat(filePath)
if err != nil {
return err
}
file, err := os.Open(filePath)
if err != nil {
return err
}
if file == nil {
return errors.New("upload file is missing :" + filePath)
}
defer file.Close()
// hc.config.HttpAddress+"/upload",
httpreq, err := http.NewRequest("POST", url, file)
if err != nil {
return err
}
hn, _ := os.Hostname()
// createTime := file.
httpreq.Header.Set("Houston-Service-Name", name)
httpreq.Header.Set("Houston-Service-Version", version)
httpreq.Header.Set("Houston-Service-Filename", t.BirthTime().UTC().Format(time.DateOnly)+"."+hn+path.Ext(filePath))
httpreq.Header.Set("Content-Type", "application/zip")
resp, err := http.DefaultClient.Do(httpreq)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("upload file failed. response code : %s, %d", filePath, resp.StatusCode)
}
if err := os.Remove(filePath); err != nil {
return err
}
return nil
}
2023-05-25 10:59:04 +09:00
func (hc *houstonClient) Start() {
// receive from stream
defer func() {
hc.wg.Wait()
2023-05-25 10:59:04 +09:00
for _, proc := range hc.childProcs {
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
proc.cmd.Process.Signal(os.Kill)
2023-11-24 00:19:17 +09:00
proc.setState(protos.ProcessState_Stopping)
2023-05-21 23:37:54 +09:00
}
}
2023-05-25 10:59:04 +09:00
for _, proc := range hc.childProcs {
proc.cmd.Wait()
proc.cmd.Process.Release()
2023-05-25 10:59:04 +09:00
}
close(hc.uploadChan)
}()
go func() {
// upload 고루틴
url := hc.config.HttpAddress + "/upload"
for req := range hc.uploadChan {
2024-11-12 16:43:39 +09:00
logger.Println("uploadSafe :", req)
err := uploadSafe(url, req.logFile, req.name, req.version)
if err != nil {
logger.Println("uploadSafe return err :", err)
}
}
2023-05-21 23:37:54 +09:00
}()
2023-05-25 10:59:04 +09:00
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
2023-05-21 23:37:54 +09:00
2023-05-25 10:59:04 +09:00
go func() {
c := <-interrupt
2023-06-14 00:13:51 +09:00
logger.Println("interrupt!!!!!!!! :", c.String())
2023-05-25 10:59:04 +09:00
hc.shutdownFunc()
}()
2023-06-14 01:50:40 +09:00
var client *grpc.ClientConn
reconnCount := 0
2023-06-29 11:00:26 +09:00
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)
}
}
}
}
2023-05-25 10:59:04 +09:00
for {
select {
case <-hc.ctx.Done():
return
default:
2023-06-14 01:50:40 +09:00
if client == nil {
if reconnCount == 0 {
logger.Println("grpc.DialContext :", hc.config.GrpcAddress)
}
reconnCount++
2023-10-24 20:08:48 +09:00
var err error
2023-06-14 01:50:40 +09:00
dialContext, cancelDial := context.WithTimeout(context.Background(), 15*time.Second)
2023-10-24 20:08:48 +09:00
client, err = grpc.DialContext(dialContext, hc.config.GrpcAddress, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()))
2023-06-14 01:50:40 +09:00
cancelDial()
2023-10-24 20:08:48 +09:00
if err != nil {
logger.Println("grpc.DialContext returns err :", err)
} else if client != nil {
2023-06-14 01:50:40 +09:00
reconnCount = 0
logger.Println("grpc.DialContext succeeded")
hc.clientChan <- client
}
}
if client != nil {
err := hc.checkOperation(client)
if err != nil {
2024-11-12 12:25:52 +09:00
if status.Convert(err).Message() != status.Convert(context.Canceled).Message() {
logger.Println("grpc.DialContext hc.checkOperation failed :", err)
}
2023-06-14 01:50:40 +09:00
client = nil
}
2023-05-25 10:59:04 +09:00
}
}
}
2023-05-21 23:37:54 +09:00
}
func (hc *houstonClient) Shutdown() {
hc.shutdownFunc()
}
2023-06-14 01:50:40 +09:00
func (hc *houstonClient) checkOperation(client *grpc.ClientConn) error {
2023-05-21 23:37:54 +09:00
defer func() {
r := recover()
if r != nil {
2023-06-14 00:13:51 +09:00
logger.Println(r)
2023-05-21 23:37:54 +09:00
}
}()
2023-06-14 01:50:40 +09:00
op := protos.NewOperationClient(client)
2023-05-25 10:59:04 +09:00
cl, err := op.Query(hc.ctx, grpc.WaitForReady(true))
2023-05-21 23:37:54 +09:00
if err != nil {
return err
}
err = cl.Send(hc.makeOperationQueryRequest())
if err != nil {
cl.CloseSend()
return err
}
for {
update, err := cl.Recv()
if err != nil {
cl.CloseSend()
return err
}
2023-05-25 10:59:04 +09:00
hc.operationChan <- update
2023-05-21 23:37:54 +09:00
}
}