Compare commits
113 Commits
49921d44ce
...
kd-live
| Author | SHA1 | Date | |
|---|---|---|---|
| 31d77efe84 | |||
| 3ace1be27a | |||
| 3fccae1ef5 | |||
| 3dc121bc71 | |||
| 706339a386 | |||
| 849973c449 | |||
| 759dcaf00b | |||
| d1a192159b | |||
| db4769687c | |||
| 7a5696961f | |||
| 7418d847b7 | |||
| 4d9a25dd6b | |||
| b057730ad5 | |||
| 9bd09509c8 | |||
| d526a923ca | |||
| 1b57e9f87e | |||
| 30412bce70 | |||
| 4667d351a8 | |||
| 20dc204cb1 | |||
| 571f0d76df | |||
| 025e583f12 | |||
| 15dd1e544c | |||
| b3ed139431 | |||
| 45ab15d345 | |||
| c0a0f88220 | |||
| 02db65e06f | |||
| ed4ca6cfed | |||
| a8821b694b | |||
| 7c54fda9a2 | |||
| 5429d3d90f | |||
| 77174ccef4 | |||
| cf46888b6a | |||
| 642d53fbba | |||
| 6a35d7b1fe | |||
| 361ff4bb3a | |||
| d04dd5b05d | |||
| 942875071f | |||
| d3442be5dc | |||
| bff9548dab | |||
| 06bc095ea4 | |||
| e5b7a7b02b | |||
| da47d7c587 | |||
| 3f2c82251b | |||
| d464812cf8 | |||
| fdb0a7baa5 | |||
| fa8b78efed | |||
| cf033b6107 | |||
| 9b0aa4d640 | |||
| 96a0b43e3f | |||
| cacbc1008a | |||
| 6185d055ef | |||
| 6d319f2fa1 | |||
| b2aae5a38e | |||
| 01b4782e78 | |||
| 52491aff17 | |||
| 72e94ccfc3 | |||
| 8a1bcdc840 | |||
| d17c53c79c | |||
| 8bb1e4d0a2 | |||
| 9590de2e00 | |||
| d48afc61af | |||
| 58897522a7 | |||
| 1b6c4edaff | |||
| 06b390815c | |||
| 95bc4d8db3 | |||
| 3d3020f827 | |||
| 6a316afd90 | |||
| 9947835ba1 | |||
| bbe340f559 | |||
| 30ff0a4b27 | |||
| c98023cc8b | |||
| 3278bca32f | |||
| bdc3e19718 | |||
| 61a98247d8 | |||
| 3f2ea5cee3 | |||
| a8548fffe2 | |||
| f58db1ec09 | |||
| 1c1f9f748a | |||
| 279c9f47f0 | |||
| 46aedbe767 | |||
| 96ee2a4627 | |||
| 2e4b7811db | |||
| 3add5d9355 | |||
| 20b2df1fc5 | |||
| 4cec01609a | |||
| 43da9424f0 | |||
| c4c0f86947 | |||
| 838b3a2194 | |||
| 93c13ba92f | |||
| ed5b4c06e9 | |||
| c1847ee3e1 | |||
| d490188bd2 | |||
| ac355d32b4 | |||
| 26b12fad72 | |||
| faefd8cfca | |||
| 5f1b23ed80 | |||
| 5c00ff73d7 | |||
| b14ad791df | |||
| 471d07a188 | |||
| 153a9e50ca | |||
| fa5a612173 | |||
| 6719d9b6c1 | |||
| 06c7fbd32d | |||
| 1d525eb7cf | |||
| 18e7ee0afa | |||
| 5326e26a8c | |||
| ae783aabaf | |||
| 1d87ef3501 | |||
| cff643310b | |||
| 9b9c3eaa4d | |||
| 2f30d4cbc0 | |||
| 3161afd091 | |||
| 18e7cbf75e |
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,6 @@
|
|||||||
*.log
|
*.log
|
||||||
*.exe
|
*.exe
|
||||||
|
houston
|
||||||
|
houston.zip
|
||||||
|
config.json
|
||||||
|
.vscode/
|
||||||
21
.vscode/launch.json
vendored
21
.vscode/launch.json
vendored
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "houston",
|
|
||||||
"type": "go",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "auto",
|
|
||||||
"program": "${workspaceFolder}",
|
|
||||||
"env": {
|
|
||||||
},
|
|
||||||
"args" : [
|
|
||||||
"-port=8080",
|
|
||||||
"-client"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
278
client/client.go
278
client/client.go
@ -2,31 +2,66 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"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"
|
||||||
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
sigar "github.com/cloudfoundry/gosigar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type clientConfig struct {
|
||||||
|
GrpcAddress string `json:"grpc_server_address"`
|
||||||
|
HttpAddress string `json:"http_server_address"`
|
||||||
|
StorageRoot string `json:"storage_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadClientConfig() (clientConfig, error) {
|
||||||
|
configFile, err := os.Open("config.json")
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
|
return clientConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Houston == 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)
|
SetReportMetrics(map[string]float32)
|
||||||
Shutdown()
|
Shutdown()
|
||||||
@ -71,7 +106,6 @@ type procmeta struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type houstonClient struct {
|
type houstonClient struct {
|
||||||
client *grpc.ClientConn
|
|
||||||
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
|
||||||
@ -79,16 +113,17 @@ type houstonClient struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
operationChan chan *protos.OperationQueryResponse
|
operationChan chan *protos.OperationQueryResponse
|
||||||
exitChan chan *exec.Cmd
|
exitChan chan *exec.Cmd
|
||||||
httpAddr string
|
clientChan chan *grpc.ClientConn
|
||||||
timestamp string
|
timestamp string
|
||||||
}
|
wg sync.WaitGroup
|
||||||
|
config clientConfig
|
||||||
func bToMb(b uint64) uint32 {
|
version string
|
||||||
return uint32(b / 1024 / 1024)
|
standalone bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshal[T any](val *T, src map[string]string) {
|
func unmarshal[T any](val *T, src map[string]string) {
|
||||||
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
|
||||||
@ -97,18 +132,22 @@ func unmarshal[T any](val *T, src map[string]string) {
|
|||||||
if argval.Elem().Field(i).CanInt() {
|
if argval.Elem().Field(i).CanInt() {
|
||||||
num, _ := strconv.ParseInt(arg, 10, 0)
|
num, _ := strconv.ParseInt(arg, 10, 0)
|
||||||
argval.Elem().Field(i).SetInt(num)
|
argval.Elem().Field(i).SetInt(num)
|
||||||
|
} 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))
|
||||||
} else {
|
} else {
|
||||||
argval.Elem().Field(i).SetString(arg)
|
argval.Elem().Field(i).SetString(arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatherDeployedPrograms(name string) []*protos.VersionAndArgs {
|
func gatherDeployedPrograms(storageRoot, name string) []*protos.VersionAndArgs {
|
||||||
var rawvers []*protos.VersionAndArgs
|
var rawvers []*protos.VersionAndArgs
|
||||||
if vers, err := os.ReadDir(path.Join("./", name)); err == nil {
|
targetPath := path.Join(storageRoot, name)
|
||||||
|
if vers, err := os.ReadDir(targetPath); err == nil {
|
||||||
for _, ver := range vers {
|
for _, ver := range vers {
|
||||||
if ver.IsDir() {
|
if ver.IsDir() {
|
||||||
args := lastExecutionArgs(path.Join(name, ver.Name()))
|
args := lastExecutionArgs(path.Join(targetPath, ver.Name()))
|
||||||
rawvers = append(rawvers, &protos.VersionAndArgs{
|
rawvers = append(rawvers, &protos.VersionAndArgs{
|
||||||
Version: ver.Name(),
|
Version: ver.Name(),
|
||||||
Args: args,
|
Args: args,
|
||||||
@ -125,8 +164,33 @@ func gatherDeployedPrograms(name string) []*protos.VersionAndArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryRequest {
|
func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryRequest {
|
||||||
hn, _ := os.Hostname()
|
var procs []*protos.ProcessDescription
|
||||||
procs := make([]*protos.ProcessDescription, 0, len(hc.childProcs))
|
var deploys []*protos.DeployedVersions
|
||||||
|
|
||||||
|
var selfname string
|
||||||
|
var selfargs []string
|
||||||
|
if hc.standalone {
|
||||||
|
selfname = path.Base(os.Args[0])
|
||||||
|
selfargs = os.Args[1:]
|
||||||
|
} else {
|
||||||
|
selfname = "houston"
|
||||||
|
selfargs = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
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},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
for _, child := range hc.childProcs {
|
for _, child := range hc.childProcs {
|
||||||
procs = append(procs, &protos.ProcessDescription{
|
procs = append(procs, &protos.ProcessDescription{
|
||||||
Name: child.name,
|
Name: child.name,
|
||||||
@ -137,7 +201,6 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var deploys []*protos.DeployedVersions
|
|
||||||
for name, prog := range hc.deploys {
|
for name, prog := range hc.deploys {
|
||||||
deploys = append(deploys, &protos.DeployedVersions{
|
deploys = append(deploys, &protos.DeployedVersions{
|
||||||
Name: name,
|
Name: name,
|
||||||
@ -145,6 +208,7 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hn, _ := os.Hostname()
|
||||||
return &protos.OperationQueryRequest{
|
return &protos.OperationQueryRequest{
|
||||||
Hostname: hn,
|
Hostname: hn,
|
||||||
Procs: procs,
|
Procs: procs,
|
||||||
@ -152,12 +216,20 @@ func (hc *houstonClient) makeOperationQueryRequest() *protos.OperationQueryReque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(grpcAddr string, httpAddr string) (HoustonClient, error) {
|
func NewClient(standalone bool) (HoustonClient, error) {
|
||||||
client, err := grpc.Dial(grpcAddr, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
clientConfig, err := loadClientConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(clientConfig.GrpcAddress) == 0 {
|
||||||
|
return nil, errors.New("client.grpc_server_address is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(clientConfig.HttpAddress) == 0 {
|
||||||
|
return nil, errors.New("client.http_server_address is missing")
|
||||||
|
}
|
||||||
|
|
||||||
exefile, err := os.Executable()
|
exefile, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -168,73 +240,95 @@ func NewClient(grpcAddr string, httpAddr string) (HoustonClient, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
deploys := make(map[string][]*protos.VersionAndArgs)
|
deploys := make(map[string][]*protos.VersionAndArgs)
|
||||||
if dirs, err := os.ReadDir("./"); err == nil {
|
if dirs, err := os.ReadDir(clientConfig.StorageRoot); err == nil {
|
||||||
for _, dir := range dirs {
|
for _, dir := range dirs {
|
||||||
if dir.IsDir() {
|
if dir.IsDir() {
|
||||||
flagf := path.Join(dir.Name(), "@houston")
|
flagf := path.Join(clientConfig.StorageRoot, dir.Name(), "@houston")
|
||||||
if _, err := os.Stat(flagf); !os.IsNotExist(err) {
|
if _, err := os.Stat(flagf); !os.IsNotExist(err) {
|
||||||
deploys[dir.Name()] = gatherDeployedPrograms(dir.Name())
|
deploys[dir.Name()] = gatherDeployedPrograms(clientConfig.StorageRoot, dir.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ver, _ := os.ReadFile("@version")
|
||||||
|
if len(ver) == 0 {
|
||||||
|
ver = []byte("0.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
hc := &houstonClient{
|
hc := &houstonClient{
|
||||||
client: client,
|
config: clientConfig,
|
||||||
|
clientChan: make(chan *grpc.ClientConn),
|
||||||
extraMetrics: unsafe.Pointer(&map[string]float32{}),
|
extraMetrics: unsafe.Pointer(&map[string]float32{}),
|
||||||
deploys: deploys,
|
deploys: deploys,
|
||||||
httpAddr: httpAddr,
|
|
||||||
timestamp: exefi.ModTime().String(),
|
timestamp: exefi.ModTime().String(),
|
||||||
|
version: string(ver),
|
||||||
|
standalone: standalone,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go func() {
|
|
||||||
// regularly send status
|
|
||||||
sc := protos.NewMonitorClient(client)
|
|
||||||
hn, _ := os.Hostname()
|
|
||||||
mem := sigar.Mem{}
|
|
||||||
mem.Get()
|
|
||||||
|
|
||||||
metrics := &protos.Metrics{
|
|
||||||
Hostname: hn,
|
|
||||||
Total: bToMb(mem.Total),
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
percent, _ := cpu.Percent(0, false)
|
|
||||||
|
|
||||||
metrics.Cpu = float32(percent[0])
|
|
||||||
metrics.Free = bToMb(mem.ActualFree)
|
|
||||||
metrics.Metrics = *(*map[string]float32)(atomic.LoadPointer(&hc.extraMetrics))
|
|
||||||
|
|
||||||
sc.Report(ctx, metrics, grpc.WaitForReady(true))
|
|
||||||
mem.Get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
exitChan := make(chan *exec.Cmd, 10)
|
exitChan := make(chan *exec.Cmd, 10)
|
||||||
operationChan := make(chan *protos.OperationQueryResponse, 10)
|
operationChan := make(chan *protos.OperationQueryResponse, 10)
|
||||||
|
hc.wg.Add(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer hc.wg.Done()
|
||||||
|
|
||||||
// 메인 operator
|
// 메인 operator
|
||||||
op := protos.NewOperationClient(hc.client)
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case newClient := <-hc.clientChan:
|
||||||
|
op = protos.NewOperationClient(newClient)
|
||||||
|
|
||||||
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 && proc.state != protos.ProcessState_Stopped {
|
if proc.cmd == exited {
|
||||||
proc.state = protos.ProcessState_Stopped
|
if proc.state == protos.ProcessState_Running || proc.state == protos.ProcessState_Restart {
|
||||||
|
go func(proc *procmeta) {
|
||||||
|
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||||
|
proc.cmd.Process.Signal(os.Kill)
|
||||||
|
}
|
||||||
|
proc.cmd.Wait()
|
||||||
|
proc.cmd.Process.Release()
|
||||||
|
|
||||||
|
if proc.state == protos.ProcessState_Restart {
|
||||||
|
hc.startChildProcess(&shared.StartProcessRequest{
|
||||||
|
Version: proc.version,
|
||||||
|
Name: proc.name,
|
||||||
|
Args: proc.cmd.Args,
|
||||||
|
}, op)
|
||||||
|
}
|
||||||
|
}(proc)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newprocs = append(newprocs, proc)
|
newprocs = append(newprocs, proc)
|
||||||
}
|
}
|
||||||
@ -247,22 +341,44 @@ func NewClient(grpcAddr string, httpAddr string) (HoustonClient, error) {
|
|||||||
case shared.Deploy:
|
case shared.Deploy:
|
||||||
var dr shared.DeployRequest
|
var dr shared.DeployRequest
|
||||||
unmarshal(&dr, resp.Args)
|
unmarshal(&dr, resp.Args)
|
||||||
err := hc.deploy(&dr)
|
if dr.Name == myname {
|
||||||
if err == nil {
|
if srcdir, replacer, err := hc.prepareUpdateSelf(&dr); err == nil {
|
||||||
prog := gatherDeployedPrograms(dr.Name)
|
args := []string{
|
||||||
|
fmt.Sprintf("%d", os.Getpid()),
|
||||||
|
srcdir,
|
||||||
|
filepath.ToSlash(os.Args[0]),
|
||||||
|
}
|
||||||
|
args = append(args, os.Args[1:]...)
|
||||||
|
cmd := exec.Command(replacer, args...)
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
} else {
|
||||||
|
hc.shutdownFunc()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := hc.deploy(&dr); err == nil {
|
||||||
|
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())
|
||||||
} else {
|
} else {
|
||||||
logger.Println(err)
|
logger.Println(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case shared.Withdraw:
|
case shared.Withdraw:
|
||||||
var wr shared.WithdrawRequest
|
var wr shared.WithdrawRequest
|
||||||
unmarshal(&wr, resp.Args)
|
unmarshal(&wr, resp.Args)
|
||||||
err := hc.withdraw(&wr)
|
err := hc.withdraw(&wr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
prog := gatherDeployedPrograms(wr.Name)
|
prog := gatherDeployedPrograms(hc.config.StorageRoot, wr.Name)
|
||||||
|
if len(prog) == 0 {
|
||||||
|
delete(hc.deploys, wr.Name)
|
||||||
|
} else {
|
||||||
hc.deploys[wr.Name] = prog
|
hc.deploys[wr.Name] = prog
|
||||||
|
}
|
||||||
op.Refresh(ctx, hc.makeOperationQueryRequest())
|
op.Refresh(ctx, hc.makeOperationQueryRequest())
|
||||||
} else {
|
} else {
|
||||||
logger.Println(err)
|
logger.Println(err)
|
||||||
@ -271,21 +387,21 @@ func NewClient(grpcAddr string, httpAddr string) (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); err != nil {
|
if err := hc.startChildProcess(&sr, op); err != nil {
|
||||||
logger.Println(err)
|
logger.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case shared.Stop:
|
case shared.Stop:
|
||||||
var sr shared.StopProcessRequest
|
var sr shared.StopProcessRequest
|
||||||
unmarshal(&sr, resp.Args)
|
unmarshal(&sr, resp.Args)
|
||||||
if err := hc.stopChildProcess(&sr); err != nil {
|
if err := hc.stopChildProcess(&sr, op); err != nil {
|
||||||
logger.Println(err)
|
logger.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case shared.Restart:
|
case shared.Restart:
|
||||||
var rr shared.RestartProcessRequest
|
var rr shared.RestartProcessRequest
|
||||||
unmarshal(&rr, resp.Args)
|
unmarshal(&rr, resp.Args)
|
||||||
if err := hc.restartChildProcess(&rr); err != nil {
|
if err := hc.restartChildProcess(&rr, op); err != nil {
|
||||||
logger.Println(err)
|
logger.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +427,8 @@ func NewClient(grpcAddr string, httpAddr string) (HoustonClient, error) {
|
|||||||
func (hc *houstonClient) Start() {
|
func (hc *houstonClient) Start() {
|
||||||
// receive from stream
|
// receive from stream
|
||||||
defer func() {
|
defer func() {
|
||||||
|
hc.wg.Wait()
|
||||||
|
|
||||||
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)
|
||||||
@ -320,6 +438,7 @@ func (hc *houstonClient) Start() {
|
|||||||
|
|
||||||
for _, proc := range hc.childProcs {
|
for _, proc := range hc.childProcs {
|
||||||
proc.cmd.Wait()
|
proc.cmd.Wait()
|
||||||
|
proc.cmd.Process.Release()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -332,15 +451,39 @@ func (hc *houstonClient) Start() {
|
|||||||
hc.shutdownFunc()
|
hc.shutdownFunc()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var client *grpc.ClientConn
|
||||||
|
reconnCount := 0
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-hc.ctx.Done():
|
case <-hc.ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err := hc.checkOperation()
|
if client == nil {
|
||||||
|
if reconnCount == 0 {
|
||||||
|
logger.Println("grpc.DialContext :", hc.config.GrpcAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnCount++
|
||||||
|
dialContext, cancelDial := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
|
client, _ = grpc.DialContext(dialContext, hc.config.GrpcAddress, grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
cancelDial()
|
||||||
|
if client != nil {
|
||||||
|
reconnCount = 0
|
||||||
|
logger.Println("grpc.DialContext succeeded")
|
||||||
|
hc.clientChan <- client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if client != nil {
|
||||||
|
err := hc.checkOperation(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Println("hc.checkUpdate failed :", err)
|
logger.Println("hc.checkUpdate failed :", err)
|
||||||
|
|
||||||
|
client = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,15 +493,15 @@ func (hc *houstonClient) Shutdown() {
|
|||||||
hc.shutdownFunc()
|
hc.shutdownFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *houstonClient) checkOperation() error {
|
func (hc *houstonClient) checkOperation(client *grpc.ClientConn) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
r := recover()
|
r := recover()
|
||||||
if r != nil {
|
if r != nil {
|
||||||
logger.Error(r)
|
logger.Println(r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
op := protos.NewOperationClient(hc.client)
|
op := protos.NewOperationClient(client)
|
||||||
cl, err := op.Query(hc.ctx, grpc.WaitForReady(true))
|
cl, err := op.Query(hc.ctx, grpc.WaitForReady(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -376,7 +519,6 @@ func (hc *houstonClient) checkOperation() error {
|
|||||||
cl.CloseSend()
|
cl.CloseSend()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Println(update)
|
|
||||||
hc.operationChan <- update
|
hc.operationChan <- update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
162
client/deploy.go
162
client/deploy.go
@ -6,33 +6,37 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"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) (string, error) {
|
func download(dir string, urlpath string, accessToken string) (target string, err error) {
|
||||||
|
logger.Println("start downloading", dir, urlpath)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("downloading failed :", err)
|
||||||
|
} else {
|
||||||
|
logger.Println("downloading succeeded")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
parsed, err := url.Parse(urlpath)
|
parsed, err := url.Parse(urlpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, _ := http.NewRequest("GET", urlpath, nil)
|
req, _ := http.NewRequest("GET", urlpath, nil)
|
||||||
if len(accessToken) > 0 {
|
|
||||||
req.Header.Add("Authorization", accessToken)
|
|
||||||
}
|
|
||||||
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")
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -55,7 +59,7 @@ func download(dir string, urlpath string, accessToken string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.Name(), nil
|
return filepath.ToSlash(out.Name()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unzip(fname string) error {
|
func unzip(fname string) error {
|
||||||
@ -64,7 +68,10 @@ func unzip(fname string) error {
|
|||||||
os.Remove(fname)
|
os.Remove(fname)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer archive.Close()
|
defer func() {
|
||||||
|
archive.Close()
|
||||||
|
os.Remove(fname)
|
||||||
|
}()
|
||||||
|
|
||||||
verpath := path.Dir(fname)
|
verpath := path.Dir(fname)
|
||||||
for _, f := range archive.File {
|
for _, f := range archive.File {
|
||||||
@ -75,14 +82,16 @@ func unzip(fname string) error {
|
|||||||
name = f.Name
|
name = f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = strings.ReplaceAll(name, `\`, "/")
|
||||||
filePath := path.Join(verpath, name)
|
filePath := path.Join(verpath, name)
|
||||||
|
if f.FileInfo().IsDir() || strings.HasSuffix(f.FileInfo().Name(), `\`) {
|
||||||
if f.FileInfo().IsDir() {
|
if err = os.MkdirAll(filePath, 0775); err != nil {
|
||||||
os.MkdirAll(filePath, os.ModePerm)
|
return err
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(path.Dir(filePath), os.ModePerm); err != nil {
|
if err := os.MkdirAll(path.Dir(filePath), 0775); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +112,7 @@ func unzip(fname string) error {
|
|||||||
dstFile.Close()
|
dstFile.Close()
|
||||||
fileInArchive.Close()
|
fileInArchive.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +121,10 @@ func untar(fname string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer func() {
|
||||||
|
file.Close()
|
||||||
|
os.Remove(fname)
|
||||||
|
}()
|
||||||
|
|
||||||
verpath := path.Dir(fname)
|
verpath := path.Dir(fname)
|
||||||
tarReader := tar.NewReader(file)
|
tarReader := tar.NewReader(file)
|
||||||
@ -126,7 +139,7 @@ func untar(fname string) error {
|
|||||||
|
|
||||||
switch header.Typeflag {
|
switch header.Typeflag {
|
||||||
case tar.TypeDir:
|
case tar.TypeDir:
|
||||||
if err := os.MkdirAll(path.Join(verpath, header.Name), 0755); err != nil {
|
if err := os.MkdirAll(path.Join(verpath, header.Name), 0775); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case tar.TypeReg:
|
case tar.TypeReg:
|
||||||
@ -143,6 +156,7 @@ func untar(fname string) error {
|
|||||||
return errors.New("unknown type")
|
return errors.New("unknown type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,8 +164,9 @@ func (hc *houstonClient) prepareDeploy(name string, version string) (destPath st
|
|||||||
// houston관리용임을 표시하기 위해 더미파일 생성
|
// houston관리용임을 표시하기 위해 더미파일 생성
|
||||||
defer func() {
|
defer func() {
|
||||||
var flagf *os.File
|
var flagf *os.File
|
||||||
if _, err := os.Stat(path.Join(name, "@houston")); os.IsNotExist(err) {
|
markerPath := path.Join(hc.config.StorageRoot, name, "@houston")
|
||||||
flagf, err = os.Create(path.Join(name, "@houston"))
|
if _, err := os.Stat(markerPath); os.IsNotExist(err) {
|
||||||
|
flagf, err = os.Create(markerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -160,10 +175,10 @@ func (hc *houstonClient) prepareDeploy(name string, version string) (destPath st
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
verpath := path.Join("./", name, version)
|
verpath := path.Join(hc.config.StorageRoot, name, version)
|
||||||
if _, err := os.Stat(verpath); os.IsNotExist(err) {
|
if _, err := os.Stat(verpath); os.IsNotExist(err) {
|
||||||
// 없네? 만들면 된다.
|
// 없네? 만들면 된다.
|
||||||
err = os.MkdirAll(verpath, fs.FileMode(os.O_WRONLY))
|
err = os.MkdirAll(verpath, 0775)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -180,6 +195,93 @@ func (hc *houstonClient) prepareDeploy(name string, version string) (destPath st
|
|||||||
return verpath, nil
|
return verpath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hc *houstonClient) makeDownloadUrl(rel string) string {
|
||||||
|
out := rel
|
||||||
|
if !strings.HasPrefix(out, "http") {
|
||||||
|
tks := strings.SplitN(hc.config.HttpAddress, "://", 2)
|
||||||
|
out = fmt.Sprintf("%s://%s", tks[0], path.Join(tks[1], rel))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func copy(src, dst string) error {
|
||||||
|
fi, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
inmode := fi.Mode()
|
||||||
|
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
copied, err := io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if copied < fi.Size() {
|
||||||
|
return errors.New("copy not completed")
|
||||||
|
}
|
||||||
|
if err := out.Sync(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := out.Chmod(inmode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hc *houstonClient) prepareUpdateSelf(req *shared.DeployRequest) (srcdir string, replacer string, err error) {
|
||||||
|
// 내가 스스로 업데이트
|
||||||
|
// 다운로드 받고 압축 푼 다음에 교체용 프로세스 시작
|
||||||
|
tempdir, err := os.MkdirTemp(os.TempDir(), "*")
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
fname, err := download(tempdir, hc.makeDownloadUrl(req.Url), req.AccessToken)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch path.Ext(fname) {
|
||||||
|
case ".zip":
|
||||||
|
err = unzip(fname)
|
||||||
|
case ".tar":
|
||||||
|
err = untar(fname)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// houston version 파일
|
||||||
|
err = os.WriteFile(path.Join(path.Dir(fname), "@version"), []byte(req.Version), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
selfname, _ := os.Executable()
|
||||||
|
srcreplacer := path.Join(path.Dir(fname), "replacer") + path.Ext(selfname)
|
||||||
|
replacer = "./" + filepath.ToSlash("replacer"+path.Ext(selfname))
|
||||||
|
err = copy(srcreplacer, replacer)
|
||||||
|
if err == nil {
|
||||||
|
err = os.Chmod(replacer, 0775)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacer먼저 가져옴
|
||||||
|
return filepath.ToSlash(tempdir), replacer, err
|
||||||
|
}
|
||||||
|
|
||||||
func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
|
func (hc *houstonClient) deploy(req *shared.DeployRequest) 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)
|
||||||
@ -187,14 +289,8 @@ func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(req.Url, "http") {
|
|
||||||
tks := strings.SplitN(hc.httpAddr, "://", 2)
|
|
||||||
req.Url = fmt.Sprintf("%s://%s", tks[0], path.Join(tks[1], req.Url))
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Println("start downloading", req.Url)
|
|
||||||
// verpath에 배포 시작
|
// verpath에 배포 시작
|
||||||
fname, err := download(root, req.Url, req.AccessToken)
|
fname, err := download(root, hc.makeDownloadUrl(req.Url), req.AccessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -206,23 +302,27 @@ func (hc *houstonClient) deploy(req *shared.DeployRequest) error {
|
|||||||
err = untar(fname)
|
err = untar(fname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err == nil && len(req.Config) > 0 {
|
||||||
|
// config.json도 다운로드
|
||||||
|
_, err = download(root, hc.makeDownloadUrl(req.Config), req.AccessToken)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *houstonClient) withdraw(req *shared.WithdrawRequest) error {
|
func (hc *houstonClient) withdraw(req *shared.WithdrawRequest) error {
|
||||||
fd, _ := os.Stat(path.Join("./", req.Name, req.Version))
|
targetPath := path.Join(hc.config.StorageRoot, req.Name, req.Version)
|
||||||
|
fd, _ := os.Stat(targetPath)
|
||||||
if fd != nil {
|
if fd != nil {
|
||||||
if fd.IsDir() {
|
if fd.IsDir() {
|
||||||
for _, running := range hc.childProcs {
|
for _, running := range hc.childProcs {
|
||||||
if running.name == req.Name && running.version == req.Version {
|
if running.name == req.Name && (len(req.Version) == 0 || running.version == req.Version) {
|
||||||
// 회수하려는 버전이 돌고 있다
|
// 회수하려는 버전이 돌고 있다
|
||||||
if running.state != protos.ProcessState_Stopped {
|
|
||||||
return fmt.Errorf("withdraw failed. %s@%s is still running", req.Name, req.Version)
|
return fmt.Errorf("withdraw failed. %s@%s is still running", req.Name, req.Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return os.RemoveAll(path.Join("./", req.Name, req.Version))
|
return os.RemoveAll(targetPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,13 +12,12 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@ -53,9 +52,9 @@ func (hc *houstonClient) uploadZipLogFile(zipFile string, name string, version s
|
|||||||
|
|
||||||
defer zf.Close()
|
defer zf.Close()
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", hc.httpAddr+"/upload", zf)
|
req, err := http.NewRequest("POST", hc.config.HttpAddress+"/upload", zf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
}
|
}
|
||||||
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)
|
||||||
@ -74,8 +73,8 @@ func (hc *houstonClient) uploadZipLogFile(zipFile string, name string, version s
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func zipLogFiles(req *shared.UploadRequest, start, except string) (string, []string, error) {
|
func zipLogFiles(storageRoot string, req *shared.UploadRequest, start, except string) (string, []string, error) {
|
||||||
root := path.Join(req.Name, req.Version)
|
root := path.Join(storageRoot, req.Name, req.Version)
|
||||||
matches, err := filepath.Glob(path.Join(root, req.Filter))
|
matches, err := filepath.Glob(path.Join(root, req.Filter))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
@ -85,8 +84,13 @@ func zipLogFiles(req *shared.UploadRequest, start, except string) (string, []str
|
|||||||
return "", nil, nil
|
return "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, file := range matches {
|
||||||
|
file = filepath.ToSlash(file)
|
||||||
|
matches[i] = file
|
||||||
|
}
|
||||||
|
|
||||||
root = path.Join(root, path.Dir(req.Filter))
|
root = path.Join(root, path.Dir(req.Filter))
|
||||||
zipFileName := path.Join(os.TempDir(), path.Base(matches[0]), ".zip")
|
zipFileName := path.Join(os.TempDir(), path.Base(matches[0])) + ".zip"
|
||||||
f, err := os.OpenFile(zipFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
f, err := os.OpenFile(zipFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -99,12 +103,11 @@ func zipLogFiles(req *shared.UploadRequest, start, except string) (string, []str
|
|||||||
|
|
||||||
oldestFile := ""
|
oldestFile := ""
|
||||||
for i, file := range matches {
|
for i, file := range matches {
|
||||||
file = filepath.ToSlash(file)
|
|
||||||
matches[i] = file
|
|
||||||
if file == root {
|
if file == root {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(except) > 0 && file >= except {
|
if len(except) > 0 && file >= except {
|
||||||
|
matches = matches[:i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(start) > 0 && file < start {
|
if len(start) > 0 && file < start {
|
||||||
@ -117,19 +120,19 @@ func zipLogFiles(req *shared.UploadRequest, start, except string) (string, []str
|
|||||||
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.Error(err)
|
logger.Println(err)
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := os.Open(file)
|
src, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
return "", nil, 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.Error(err)
|
logger.Println(err)
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,15 +164,18 @@ func zipLogFiles(req *shared.UploadRequest, start, except string) (string, []str
|
|||||||
//return nil
|
//return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareProcessLaunch(req *shared.StartProcessRequest) *procmeta {
|
func prepareProcessLaunch(storageRoot string, req *shared.StartProcessRequest) *procmeta {
|
||||||
re := regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)`)
|
if len(req.Args) == 0 {
|
||||||
args := re.FindAllString(req.Args, -1)
|
return nil
|
||||||
|
}
|
||||||
verpath := path.Join("./", req.Name, req.Version)
|
verpath := path.Join(storageRoot, req.Name, req.Version)
|
||||||
fi, err := os.Stat(verpath)
|
fi, err := os.Stat(verpath)
|
||||||
|
|
||||||
if err == nil && fi.IsDir() {
|
if err == nil && fi.IsDir() {
|
||||||
cmd := exec.Command(args[0], args[1:]...)
|
req.Args[0] = "./" + path.Clean(strings.TrimPrefix(req.Args[0], "/"))
|
||||||
|
os.Chmod(path.Join(verpath, req.Args[0]), 0777)
|
||||||
|
|
||||||
|
cmd := exec.Command(req.Args[0], req.Args[1:]...)
|
||||||
cmd.Dir = verpath
|
cmd.Dir = verpath
|
||||||
stdin, _ := cmd.StdinPipe()
|
stdin, _ := cmd.StdinPipe()
|
||||||
|
|
||||||
@ -196,7 +202,7 @@ func (hc *houstonClient) launch(meta *procmeta) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.MkdirAll(path.Join(meta.cmd.Dir, "logs"), os.ModePerm)
|
err = os.MkdirAll(path.Join(meta.cmd.Dir, "logs"), 0775)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -249,7 +255,7 @@ func (hc *houstonClient) launch(meta *procmeta) error {
|
|||||||
startFile := uploadStartFile
|
startFile := uploadStartFile
|
||||||
uploadStartFile = nextFile
|
uploadStartFile = nextFile
|
||||||
go func(startFile, nextFile string) {
|
go func(startFile, nextFile string) {
|
||||||
zipFile, srcFiles, err := zipLogFiles(req, startFile, nextFile)
|
zipFile, srcFiles, err := zipLogFiles(hc.config.StorageRoot, req, startFile, nextFile)
|
||||||
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 {
|
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 {
|
||||||
if err = hc.uploadZipLogFile(zipFile, meta.name, meta.version); err == nil {
|
if err = hc.uploadZipLogFile(zipFile, meta.name, meta.version); err == nil {
|
||||||
for _, oldf := range srcFiles {
|
for _, oldf := range srcFiles {
|
||||||
@ -268,9 +274,10 @@ func (hc *houstonClient) launch(meta *procmeta) error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logfile.Write(bt.buf[:bt.size])
|
logfile.Write(bt.buf[:bt.size])
|
||||||
|
logfile.Sync()
|
||||||
meta.buffers.push(bt.buf)
|
meta.buffers.push(bt.buf)
|
||||||
thisFileSize += bt.size
|
thisFileSize += bt.size
|
||||||
if thisFileSize > 1024*1024 {
|
if thisFileSize > 10*1024*1024 {
|
||||||
switchToNextFile()
|
switchToNextFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,11 +319,13 @@ func (hc *houstonClient) launch(meta *procmeta) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest) error {
|
var errPrepareprocessLaunchFailed = errors.New("prepareProcessLaunch failed")
|
||||||
|
|
||||||
|
func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest, op protos.OperationClient) error {
|
||||||
logger.Println("startChildProcess :", *req)
|
logger.Println("startChildProcess :", *req)
|
||||||
if req.Version == "latest" {
|
if req.Version == "latest" {
|
||||||
// 최신 버전을 찾음
|
// 최신 버전을 찾음
|
||||||
latest, err := shared.FindLastestVersion(path.Join("./", req.Name))
|
latest, err := shared.FindLastestVersion(hc.config.StorageRoot, req.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -324,26 +333,34 @@ func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest) erro
|
|||||||
req.Version = latest
|
req.Version = latest
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := prepareProcessLaunch(req)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch가 성공하면 args 저장. this and parent folder
|
// launch가 성공하면 args 저장. this and parent folder
|
||||||
if argfile, err := os.Create(path.Join(req.Name, "@args")); err == nil {
|
vers := hc.deploys[req.Name]
|
||||||
|
for _, ver := range vers {
|
||||||
|
if ver.Version == req.Version {
|
||||||
|
ver.Args = meta.cmd.Args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(meta.cmd.Args)
|
||||||
argfile.Close()
|
argfile.Close()
|
||||||
}
|
}
|
||||||
if argfile, err := os.Create(path.Join(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(meta.cmd.Args)
|
||||||
argfile.Close()
|
argfile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
hc.childProcs = append(hc.childProcs, meta)
|
hc.childProcs = append(hc.childProcs, meta)
|
||||||
|
|
||||||
op := protos.NewOperationClient(hc.client)
|
|
||||||
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -351,10 +368,10 @@ func (hc *houstonClient) startChildProcess(req *shared.StartProcessRequest) erro
|
|||||||
|
|
||||||
var errNoRunningProcess = errors.New("no running processed")
|
var errNoRunningProcess = errors.New("no running processed")
|
||||||
|
|
||||||
func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest) error {
|
func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest, op protos.OperationClient) error {
|
||||||
if req.Version == "latest" {
|
if req.Version == "latest" {
|
||||||
// 최신 버전을 찾음
|
// 최신 버전을 찾음
|
||||||
latest, err := shared.FindLastestVersion(path.Join("./", req.Name))
|
latest, err := shared.FindLastestVersion(hc.config.StorageRoot, req.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -395,17 +412,17 @@ func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest) error
|
|||||||
|
|
||||||
if len(killing) > 0 {
|
if len(killing) > 0 {
|
||||||
for _, proc := range killing {
|
for _, proc := range killing {
|
||||||
|
proc.state = 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)
|
||||||
proc.state = protos.ProcessState_Stopping
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
op := protos.NewOperationClient(hc.client)
|
|
||||||
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
||||||
|
|
||||||
for _, proc := range killing {
|
for _, proc := range killing {
|
||||||
proc.cmd.Wait()
|
proc.cmd.Wait()
|
||||||
|
proc.cmd.Process.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
hc.childProcs = remains
|
hc.childProcs = remains
|
||||||
@ -418,54 +435,24 @@ func (hc *houstonClient) stopChildProcess(req *shared.StopProcessRequest) error
|
|||||||
return errNoRunningProcess
|
return errNoRunningProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest) error {
|
func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest, op protos.OperationClient) error {
|
||||||
if req.Version == "latest" {
|
|
||||||
// 최신 버전을 찾음
|
|
||||||
latest, err := shared.FindLastestVersion(path.Join("./", req.Name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Version = latest
|
|
||||||
}
|
|
||||||
|
|
||||||
var restarts []*procmeta
|
|
||||||
for _, proc := range hc.childProcs {
|
for _, proc := range hc.childProcs {
|
||||||
if proc.name == req.Name {
|
if proc.cmd.Process.Pid == int(req.Pid) {
|
||||||
if len(req.Version) == 0 {
|
if len(req.Config) > 0 {
|
||||||
restarts = append(restarts, proc)
|
// config.json를 먼저 다운로드 시도
|
||||||
} else if req.Version == proc.version {
|
root := proc.cmd.Dir
|
||||||
restarts = append(restarts, proc)
|
if _, err := download(root, hc.makeDownloadUrl(req.Config), ""); err != nil {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(restarts) == 0 {
|
|
||||||
return errNoRunningProcess
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, proc := range restarts {
|
|
||||||
if err := proc.cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
|
||||||
proc.cmd.Process.Signal(os.Kill)
|
|
||||||
}
|
|
||||||
proc.state = protos.ProcessState_Stopping
|
|
||||||
}
|
|
||||||
|
|
||||||
op := protos.NewOperationClient(hc.client)
|
|
||||||
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
|
||||||
|
|
||||||
for _, proc := range restarts {
|
|
||||||
proc.cmd.Wait()
|
|
||||||
proc.state = protos.ProcessState_Stopped
|
|
||||||
}
|
|
||||||
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
|
||||||
|
|
||||||
for _, proc := range restarts {
|
|
||||||
if err := hc.launch(proc); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc.state = protos.ProcessState_Restart
|
||||||
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
op.Refresh(context.Background(), hc.makeOperationQueryRequest())
|
||||||
|
hc.exitChan <- proc.cmd
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -473,7 +460,7 @@ func (hc *houstonClient) restartChildProcess(req *shared.RestartProcessRequest)
|
|||||||
func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error {
|
func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error {
|
||||||
if req.Version == "latest" {
|
if req.Version == "latest" {
|
||||||
// 최신 버전을 찾음
|
// 최신 버전을 찾음
|
||||||
latest, err := shared.FindLastestVersion(path.Join("./", req.Name))
|
latest, err := shared.FindLastestVersion(hc.config.StorageRoot, req.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -492,13 +479,9 @@ func (hc *houstonClient) uploadFiles(req *shared.UploadRequest) error {
|
|||||||
|
|
||||||
// 실행 중이 아닌 폴더에서도 대상을 찾는다
|
// 실행 중이 아닌 폴더에서도 대상을 찾는다
|
||||||
// 전체 파일을 대상으로
|
// 전체 파일을 대상으로
|
||||||
zipFile, srcFiles, err := zipLogFiles(req, "", "")
|
zipFile, srcFiles, err := zipLogFiles(hc.config.StorageRoot, req, "", "")
|
||||||
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 {
|
if err == nil && len(zipFile) > 0 && len(srcFiles) > 0 {
|
||||||
if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err == nil {
|
if err = hc.uploadZipLogFile(zipFile, req.Name, req.Version); err != nil {
|
||||||
for _, oldf := range srcFiles {
|
|
||||||
os.Remove(oldf)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Println("uploadZipLogFile failed :", err)
|
logger.Println("uploadZipLogFile failed :", err)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
|||||||
@ -1 +1,12 @@
|
|||||||
{}
|
{
|
||||||
|
"houston" : {
|
||||||
|
"client" : {
|
||||||
|
"grpc_server_address" : "10.5.10.100:8080",
|
||||||
|
"http_server_address" : "https://kdcc.action2quare.com/houston"
|
||||||
|
},
|
||||||
|
"server" : {
|
||||||
|
"grpc_port" : 8080,
|
||||||
|
"storage_path" : "/data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
25
go.mod
25
go.mod
@ -1,28 +1,17 @@
|
|||||||
module repositories.action2quare.com/ayo/houston
|
module repositories.action2quare.com/ayo/houston
|
||||||
|
|
||||||
go 1.19
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cloudfoundry/gosigar v1.3.12
|
golang.org/x/text v0.10.0
|
||||||
github.com/shirou/gopsutil/v3 v3.23.4
|
google.golang.org/grpc v1.56.0
|
||||||
golang.org/x/text v0.9.0
|
|
||||||
google.golang.org/grpc v1.55.0
|
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.30.0
|
||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230524061015-e95efa06a6d4
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
golang.org/x/net v0.11.0 // indirect
|
||||||
github.com/onsi/gomega v1.18.1 // indirect
|
golang.org/x/sys v0.9.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
|
||||||
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
|
||||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
|
||||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
|
||||||
golang.org/x/net v0.10.0 // indirect
|
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
154
go.sum
154
go.sum
@ -1,152 +1,22 @@
|
|||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/cloudfoundry/gosigar v1.3.12 h1:kUvHg+O7DcYKpMSQEJFpNf9kJXLwATBJh5gBwcRxGqI=
|
|
||||||
github.com/cloudfoundry/gosigar v1.3.12/go.mod h1:OKmnKziBrg7zYdKJQ4fDW+B6Aqh/1Vy1StnjqiPEI3I=
|
|
||||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
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.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
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/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/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.6/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=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|
||||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
|
||||||
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
|
||||||
github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
|
|
||||||
github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
|
|
||||||
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
|
|
||||||
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
|
||||||
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
|
|
||||||
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
|
||||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
|
||||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
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=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
google.golang.org/grpc v1.56.0 h1:+y7Bs8rtMd07LeXmL3NxcTLn7mUkbKZqEpPhMNkwJEE=
|
||||||
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
google.golang.org/grpc v1.56.0/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22 h1:DImSGNxZrc+Q4WlS1OKMsLAScEfDYLX4XMJdjAaVnXc=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
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=
|
|
||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230524061015-e95efa06a6d4 h1:DFrkLvbPWqwVDU4X0QGJs2lhPduJYJU+JM/r1L2RMwo=
|
|
||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230524061015-e95efa06a6d4/go.mod h1:pw573a06qV7dP1lSyavbWmzyYAsmwtK6mdbFENbh3cs=
|
|
||||||
|
|||||||
4
houston.sh
Normal file
4
houston.sh
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
nohup /home/opdev/houston -client -logfile > /dev/null &
|
||||||
|
|
||||||
@ -1,24 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"repositories.action2quare.com/ayo/houston/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOperationServer(t *testing.T) {
|
func TestOperationServer(t *testing.T) {
|
||||||
hc, err := client.NewClient("192.168.9.32:8080", "http://192.168.9.32/commandcenter")
|
fmt.Println(path.Join("a", "b", "/"))
|
||||||
if err != nil {
|
fmt.Println(path.Join("a", "b/"))
|
||||||
t.Error(err)
|
fmt.Println(path.Join("a", "b/", "/"))
|
||||||
return
|
fmt.Println(path.Join("a", "b/", "/", "/"))
|
||||||
}
|
|
||||||
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")
|
// token, _ := getMicrosoftAuthoizationToken("30330e18-f407-4e35-a6d6-b734b9fe9ee9", "VTr8Q~VBAUAOSmFiHM~bjgszYXBm9nuGBQCk8cLq")
|
||||||
//go func() {
|
//go func() {
|
||||||
|
|||||||
17
main.go
17
main.go
@ -1,29 +1,24 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"repositories.action2quare.com/ayo/gocommon/flagx"
|
||||||
|
|
||||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
"repositories.action2quare.com/ayo/houston/client"
|
"repositories.action2quare.com/ayo/houston/client"
|
||||||
"repositories.action2quare.com/ayo/houston/server"
|
"repositories.action2quare.com/ayo/houston/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var runAsClient = flag.Bool("client", false, "")
|
var runAsClient = flagx.Bool("client", false, "")
|
||||||
var runAsServer = flag.Bool("server", false, "")
|
var runAsServer = flagx.Bool("server", false, "")
|
||||||
var port = flag.Int("port", 8080, "")
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if !flag.Parsed() {
|
flagx.Parse()
|
||||||
flag.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !*runAsClient && !*runAsServer {
|
if !*runAsClient && !*runAsServer {
|
||||||
logger.Fatal("client or server flag is needed")
|
logger.Fatal("client or server flag is needed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *runAsClient {
|
if *runAsClient {
|
||||||
hc, err := client.NewClient("192.168.9.32:8080", "http://192.168.9.32/commandcenter")
|
hc, err := client.NewClient(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err)
|
logger.Fatal(err)
|
||||||
return
|
return
|
||||||
@ -31,7 +26,7 @@ func main() {
|
|||||||
hc.Start()
|
hc.Start()
|
||||||
} else if *runAsServer {
|
} else if *runAsServer {
|
||||||
svr := server.NewServer()
|
svr := server.NewServer()
|
||||||
svr.Start(*port)
|
svr.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
24
make_houston_package.ps1
Normal file
24
make_houston_package.ps1
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# $ErrorActionPreference = 'SilentlyContinue'
|
||||||
|
|
||||||
|
del houston.zip
|
||||||
|
|
||||||
|
$Env:GOOS="linux"
|
||||||
|
$Env:GOARCH="amd64"
|
||||||
|
go build -ldflags="-s -w" .
|
||||||
|
|
||||||
|
cp houston .\replacer\houston
|
||||||
|
cp config.json .\replacer\config.json
|
||||||
|
|
||||||
|
cd replacer
|
||||||
|
go build -ldflags="-s -w" .
|
||||||
|
|
||||||
|
Compress-Archive -Path replacer -DestinationPath houston.zip -Force
|
||||||
|
Compress-Archive -Path config.json -Update -DestinationPath houston.zip
|
||||||
|
Compress-Archive -Path houston -Update -DestinationPath houston.zip
|
||||||
|
|
||||||
|
del houston
|
||||||
|
del config.json
|
||||||
|
del replacer
|
||||||
|
|
||||||
|
mv houston.zip ..\houston.zip
|
||||||
|
cd ..
|
||||||
@ -27,7 +27,8 @@ enum ProcessState {
|
|||||||
Stopped = 0;
|
Stopped = 0;
|
||||||
Stopping = 1;
|
Stopping = 1;
|
||||||
Running = 2;
|
Running = 2;
|
||||||
Error = 3;
|
Restart = 3;
|
||||||
|
Error = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProcessDescription {
|
message ProcessDescription {
|
||||||
@ -36,8 +37,6 @@ message ProcessDescription {
|
|||||||
string version = 3;
|
string version = 3;
|
||||||
ProcessState state = 4;
|
ProcessState state = 4;
|
||||||
int32 pid = 5;
|
int32 pid = 5;
|
||||||
int32 stdout_size = 6;
|
|
||||||
int32 stderr_size = 7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message OperationQueryResponse {
|
message OperationQueryResponse {
|
||||||
|
|||||||
113
replacer/main.go
Normal file
113
replacer/main.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copy(src, dst string, stdlog *log.Logger) error {
|
||||||
|
fi, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fi.IsDir() {
|
||||||
|
entries, _ := os.ReadDir(src)
|
||||||
|
for _, ent := range entries {
|
||||||
|
if err := copy(path.Join(src, ent.Name()), path.Join(dst, ent.Name()), stdlog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
inmode := fi.Mode()
|
||||||
|
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
copied, err := io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if copied < fi.Size() {
|
||||||
|
return errors.New("copy not completed")
|
||||||
|
}
|
||||||
|
if err := out.Sync(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := out.Chmod(inmode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdlog.Println("file copied :", src, dst)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logfile, _ := os.OpenFile("replacer.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||||||
|
defer logfile.Close()
|
||||||
|
stdlog := log.New(logfile, "", log.LstdFlags)
|
||||||
|
|
||||||
|
args := os.Args
|
||||||
|
// args[1] : 나를 시작한 pid. pid가 종료될 때 까지 기다림
|
||||||
|
// args[2] : target 폴더
|
||||||
|
// args[3:] : 다시 시작할 때 넘겨줄 arguments(프로세스 이름 포함)
|
||||||
|
stdlog.Println(args)
|
||||||
|
|
||||||
|
for {
|
||||||
|
stdlog.Println("wait for terminating of", args[3])
|
||||||
|
cmd := exec.Command("ps", "-p", args[1])
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdlog.Println("target is terminated")
|
||||||
|
|
||||||
|
// replacer 제거. 내가 돌고 있으므로 복사는 안된다.
|
||||||
|
// 내가 실행되기 전에 이미 복사가 되서 나는 최신 버전임
|
||||||
|
os.Remove(path.Join(args[2], os.Args[0]))
|
||||||
|
if err := copy(args[2], "", stdlog); err != nil {
|
||||||
|
stdlog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextArgs := args[4:]
|
||||||
|
if bt, _ := os.ReadFile("@args"); len(bt) > 0 {
|
||||||
|
var tempArgs []string
|
||||||
|
if json.Unmarshal(bt, &tempArgs) == nil {
|
||||||
|
nextArgs = tempArgs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Remove("@args")
|
||||||
|
|
||||||
|
err := os.RemoveAll(args[2])
|
||||||
|
if err != nil {
|
||||||
|
stdlog.Println("os.RemoveAll failed :", args[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Chmod(args[3], 0775)
|
||||||
|
if err != nil {
|
||||||
|
stdlog.Println("os.Chmod failed :", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdlog.Println("exec.Command :", args)
|
||||||
|
cmd := exec.Command(args[3], nextArgs...)
|
||||||
|
cmd.Start()
|
||||||
|
}
|
||||||
@ -2,11 +2,16 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
@ -18,27 +23,61 @@ import (
|
|||||||
현재 접속 중인 Agent 목록을 보여줍니다.
|
현재 접속 중인 Agent 목록을 보여줍니다.
|
||||||
- http method : GET
|
- http method : GET
|
||||||
*/
|
*/
|
||||||
|
const (
|
||||||
|
sub_folder_name_deploys = string("_deploys")
|
||||||
|
sub_folder_name_downloads = string("_downloads")
|
||||||
|
)
|
||||||
|
|
||||||
func (h *houstonHandler) GetAgents(w http.ResponseWriter, r *http.Request) {
|
func (h *houstonHandler) GetAgents(w http.ResponseWriter, r *http.Request) {
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
enc.Encode(h.Operation().Hosts())
|
allHosts := h.Operation().Hosts()
|
||||||
|
enc.Encode(allHosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTagsFromFile(paths ...string) string {
|
||||||
|
raw, _ := os.ReadFile(path.Join(paths...))
|
||||||
|
if len(raw) > 0 {
|
||||||
|
tag := string(raw)
|
||||||
|
return strings.Trim(tag, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request) {
|
func (h *houstonHandler) GetDeploySources(w http.ResponseWriter, r *http.Request) {
|
||||||
files, err := os.ReadDir("deploys")
|
files, err := os.ReadDir(h.deployPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
getVersions := func(name string) []string {
|
getVersions := func(name string) []string {
|
||||||
var out []string
|
vers, _ := os.ReadDir(path.Join(h.deployPath, name))
|
||||||
files, _ := os.ReadDir(path.Join("deploys", name))
|
mytags := readTagsFromFile(h.deployPath, name, "@tags")
|
||||||
for _, fd := range files {
|
out := []string{
|
||||||
|
mytags,
|
||||||
|
}
|
||||||
|
for _, fd := range vers {
|
||||||
if fd.IsDir() {
|
if fd.IsDir() {
|
||||||
out = append(out, fd.Name())
|
ver := fd.Name()
|
||||||
|
files, _ := os.ReadDir(path.Join(h.deployPath, name, ver))
|
||||||
|
vertags := readTagsFromFile(h.deployPath, name, ver, "@tags")
|
||||||
|
if len(files) > 0 {
|
||||||
|
for _, file := range files {
|
||||||
|
if strings.HasPrefix(file.Name(), "@") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadpath := path.Join(sub_folder_name_deploys, name, ver, file.Name())
|
||||||
|
ver = fmt.Sprintf("%s:%s", ver+mytags+vertags, downloadpath)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
out = append(out, ver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +101,7 @@ func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Reque
|
|||||||
// </form>
|
// </form>
|
||||||
file, header, err := r.FormFile("file")
|
file, header, err := r.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -70,7 +109,7 @@ func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Reque
|
|||||||
|
|
||||||
contents, err := io.ReadAll(file)
|
contents, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -79,10 +118,21 @@ 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)
|
||||||
|
|
||||||
// deploys 폴더는 파일시스템 서비스이므로 다운로드 가능
|
var filename string
|
||||||
filename := path.Join("deploys", name, version, name+ext)
|
|
||||||
if err = os.MkdirAll(path.Dir(filename), os.ModePerm); err != nil {
|
if version == "config" {
|
||||||
logger.Error(err)
|
filename = path.Join(h.deployPath, name, version, "config"+ext)
|
||||||
|
tags := readTagsFromFile(h.deployPath, name, version, "@tags")
|
||||||
|
if !strings.Contains(tags, "#hidden") {
|
||||||
|
tags = tags + "#hidden"
|
||||||
|
os.WriteFile(path.Join(h.deployPath, name, version, "@tags"), []byte(tags), 0644)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filename = path.Join(h.deployPath, name, version, name+ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll(path.Dir(filename), 0775); err != nil {
|
||||||
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -90,12 +140,75 @@ func (h *houstonHandler) UploadDeploySource(w http.ResponseWriter, r *http.Reque
|
|||||||
// 파일 저장
|
// 파일 저장
|
||||||
err = os.WriteFile(filename, contents, 0644)
|
err = os.WriteFile(filename, contents, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *houstonHandler) DeleteDeploySource(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// <form action="/houston" method="post" enctype="multipart/form-data">
|
||||||
|
// <input type="text" name="name">
|
||||||
|
// <input type="text" name="version">
|
||||||
|
// </form>
|
||||||
|
version := r.FormValue("version")
|
||||||
|
name := r.FormValue("name")
|
||||||
|
|
||||||
|
if len(version) == 0 || len(name) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// deploys 폴더는 파일시스템 서비스이므로 다운로드 가능
|
||||||
|
targetpath := path.Join(h.deployPath, name, version)
|
||||||
|
if err := os.RemoveAll(targetpath); err != nil {
|
||||||
|
logger.Println("deleteDeploySource failed :", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *houstonHandler) findLastestConfigFile(name string) (string, error) {
|
||||||
|
logger.Println("findLastestConfigFile :", name)
|
||||||
|
configFiles, err := os.ReadDir(path.Join(h.deployPath, name, "config"))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Println("findLastestConfigFile failed :", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cf fs.FileInfo
|
||||||
|
for _, file := range configFiles {
|
||||||
|
if file.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(file.Name(), "config.") {
|
||||||
|
test, err := file.Info()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cf == nil {
|
||||||
|
cf = test
|
||||||
|
} else if test.ModTime().After(cf.ModTime()) {
|
||||||
|
cf = test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cf != nil {
|
||||||
|
logger.Println("findLastestConfigFile cf found :", cf.Name())
|
||||||
|
return path.Join(sub_folder_name_deploys, name, "config", cf.Name()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Println("findLastestConfigFile cf NOT found")
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
|
func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
|
||||||
// <form action="/houston" method="post" enctype="multipart/form-data">
|
// <form action="/houston" method="post" enctype="multipart/form-data">
|
||||||
// <input type="text" name="name">
|
// <input type="text" name="name">
|
||||||
@ -110,7 +223,7 @@ func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
|
|||||||
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 {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -121,11 +234,11 @@ func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relPath := path.Join("deploys", name, version)
|
relPath := path.Join(h.deployPath, name, version)
|
||||||
files, err := os.ReadDir(relPath)
|
files, err := os.ReadDir(relPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +249,10 @@ func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(fd.Name(), "@") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
fi, _ := fd.Info()
|
fi, _ := fd.Info()
|
||||||
if fi.ModTime().After(latestTime) {
|
if fi.ModTime().After(latestTime) {
|
||||||
latestFilename = fi.Name()
|
latestFilename = fi.Name()
|
||||||
@ -147,11 +264,52 @@ func (h *houstonHandler) Deploy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configPath, err := h.findLastestConfigFile(name)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
h.Operation().Deploy(MakeDeployRequest(
|
h.Operation().Deploy(MakeDeployRequest(
|
||||||
shared.DeployRequest{
|
shared.DeployRequest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Version: version,
|
Version: version,
|
||||||
Url: path.Join(relPath, latestFilename),
|
Url: path.Join(sub_folder_name_deploys, name, version, latestFilename),
|
||||||
|
Config: configPath,
|
||||||
|
},
|
||||||
|
targets,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *houstonHandler) Undeploy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// <form action="/houston" method="post" enctype="multipart/form-data">
|
||||||
|
// <input type="text" name="name">
|
||||||
|
// <input type="text" name="version">
|
||||||
|
// <input type="text" name="targets">
|
||||||
|
// </form>
|
||||||
|
name := r.FormValue("name")
|
||||||
|
version := r.FormValue("version")
|
||||||
|
traws := r.FormValue("targets")
|
||||||
|
|
||||||
|
var targets []string
|
||||||
|
if len(traws) > 0 {
|
||||||
|
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) == 0 || len(version) == 0 || len(targets) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Operation().Withdraw(MakeWithdrawRequest(
|
||||||
|
shared.WithdrawRequest{
|
||||||
|
Name: name,
|
||||||
|
Version: version,
|
||||||
},
|
},
|
||||||
targets,
|
targets,
|
||||||
))
|
))
|
||||||
@ -167,13 +325,13 @@ func (h *houstonHandler) StartProcess(w http.ResponseWriter, r *http.Request) {
|
|||||||
// </form>
|
// </form>
|
||||||
name := r.FormValue("name")
|
name := r.FormValue("name")
|
||||||
version := r.FormValue("version")
|
version := r.FormValue("version")
|
||||||
args := r.FormValue("args")
|
argsline := r.FormValue("args")
|
||||||
traws := r.FormValue("targets")
|
traws := r.FormValue("targets")
|
||||||
|
|
||||||
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 {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -184,6 +342,27 @@ func (h *houstonHandler) StartProcess(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)`)
|
||||||
|
argsTemp := re.FindAllString(argsline, -1)
|
||||||
|
var args []string
|
||||||
|
for _, arg := range argsTemp {
|
||||||
|
if strings.HasPrefix(arg, `"`) && len(args) > 0 {
|
||||||
|
lastarg := args[len(args)-1]
|
||||||
|
if strings.HasSuffix(lastarg, "=") {
|
||||||
|
args[len(args)-1] = lastarg + arg
|
||||||
|
} else {
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
h.Operation().StartProcess(MakeStartProcessRequest(shared.StartProcessRequest{
|
h.Operation().StartProcess(MakeStartProcessRequest(shared.StartProcessRequest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Version: version,
|
Version: version,
|
||||||
@ -212,7 +391,7 @@ func (h *houstonHandler) StopProcess(w http.ResponseWriter, r *http.Request) {
|
|||||||
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 {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -227,6 +406,51 @@ func (h *houstonHandler) StopProcess(w http.ResponseWriter, r *http.Request) {
|
|||||||
}, targets))
|
}, targets))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *houstonHandler) RestartProcess(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// <form action="/houston" method="post" enctype="multipart/form-data">
|
||||||
|
// <input type="text" name="name">
|
||||||
|
// <input type="text" name="target">
|
||||||
|
// <input type="text" name="pid">
|
||||||
|
// <input type="text" name="config">
|
||||||
|
// </form>
|
||||||
|
pidstr := r.FormValue("pid")
|
||||||
|
target := r.FormValue("target")
|
||||||
|
name := r.FormValue("name")
|
||||||
|
if len(target) == 0 || len(pidstr) == 0 || len(name) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deployConfig := false
|
||||||
|
configstr := r.FormValue("config")
|
||||||
|
if len(configstr) > 0 {
|
||||||
|
deployConfig, _ = strconv.ParseBool(configstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pid, err := strconv.ParseInt(pidstr, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var configPath string
|
||||||
|
if deployConfig {
|
||||||
|
configPath, err = h.findLastestConfigFile(name)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Operation().RestartProcess(MakeRestartRequest(shared.RestartProcessRequest{
|
||||||
|
Name: name,
|
||||||
|
Pid: int32(pid),
|
||||||
|
Config: configPath,
|
||||||
|
}, []string{target}))
|
||||||
|
}
|
||||||
|
|
||||||
func (h *houstonHandler) UploadLogs(w http.ResponseWriter, r *http.Request) {
|
func (h *houstonHandler) UploadLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
// <form action="/houston" method="post" enctype="multipart/form-data">
|
// <form action="/houston" method="post" enctype="multipart/form-data">
|
||||||
// <input type="text" name="name">
|
// <input type="text" name="name">
|
||||||
@ -249,7 +473,7 @@ func (h *houstonHandler) UploadLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
var targets []string
|
var targets []string
|
||||||
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
|
if err := json.Unmarshal([]byte(traws), &targets); err != nil {
|
||||||
logger.Error(err)
|
logger.Println(err)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -275,19 +499,19 @@ func (h *houstonHandler) GetLogFileLinks(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if version == "latest" {
|
if version == "latest" {
|
||||||
version, _ = shared.FindLastestVersion(path.Join("downloads", name))
|
version, _ = shared.FindLastestVersion(h.downloadPath, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
root := path.Join("downloads", 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 {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var out []string
|
var out []string
|
||||||
for _, lf := range logfiles {
|
for _, lf := range logfiles {
|
||||||
out = append(out, path.Join(root, lf.Name()))
|
out = append(out, path.Join(sub_folder_name_downloads, name, version, lf.Name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -10,13 +11,10 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"repositories.action2quare.com/ayo/gocommon/flagx"
|
||||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultMaxMemory = 32 << 10 // 32 KB
|
|
||||||
)
|
|
||||||
|
|
||||||
type HoustonServerWithHandler interface {
|
type HoustonServerWithHandler interface {
|
||||||
HoustonServer
|
HoustonServer
|
||||||
RegisterHandlers(serveMux *http.ServeMux, prefix string) error
|
RegisterHandlers(serveMux *http.ServeMux, prefix string) error
|
||||||
@ -25,6 +23,8 @@ type HoustonServerWithHandler interface {
|
|||||||
type houstonHandler struct {
|
type houstonHandler struct {
|
||||||
HoustonServer
|
HoustonServer
|
||||||
methods map[string]reflect.Method
|
methods map[string]reflect.Method
|
||||||
|
deployPath string
|
||||||
|
downloadPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHoustonHandler() HoustonServerWithHandler {
|
func NewHoustonHandler() HoustonServerWithHandler {
|
||||||
@ -43,16 +43,33 @@ func NewHoustonHandler() HoustonServerWithHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string) error {
|
func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string) error {
|
||||||
logger.Println("houstonHandler registed")
|
config := loadServerConfig()
|
||||||
serveMux.Handle("/"+path.Join(prefix, "houston"), h)
|
storagePath := config.StorageRoot
|
||||||
|
h.deployPath = path.Join(storagePath, sub_folder_name_deploys)
|
||||||
|
h.downloadPath = path.Join(storagePath, sub_folder_name_downloads)
|
||||||
|
|
||||||
fsx := http.FileServer(http.Dir("./deploys"))
|
if err := os.MkdirAll(h.deployPath, 0775); err != nil {
|
||||||
serveMux.Handle(fmt.Sprintf("/%s/deploys/", prefix), http.StripPrefix(fmt.Sprintf("/%s/deploys/", prefix), fsx))
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
ufsx := http.FileServer(http.Dir("./downloads"))
|
if err := os.MkdirAll(h.downloadPath, 0775); err != nil {
|
||||||
serveMux.Handle(fmt.Sprintf("/%s/houston/downloads/", prefix), http.StripPrefix(fmt.Sprintf("/%s/houston/downloads/", prefix), ufsx))
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
serveMux.HandleFunc("/"+path.Join(prefix, "upload"), func(w http.ResponseWriter, r *http.Request) {
|
logger.Printf("houstonHandler registed. deployPath : %s, downloadPath : %s", h.deployPath, h.downloadPath)
|
||||||
|
|
||||||
|
if len(prefix) > 0 {
|
||||||
|
prefix = "/" + prefix
|
||||||
|
}
|
||||||
|
serveMux.Handle(prefix, h)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
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.HandleFunc(fmt.Sprintf("%s/upload", prefix), func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer func() {
|
defer func() {
|
||||||
s := recover()
|
s := recover()
|
||||||
if s != nil {
|
if s != nil {
|
||||||
@ -66,8 +83,8 @@ func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string
|
|||||||
name := r.Header.Get("Houston-Service-Name")
|
name := r.Header.Get("Houston-Service-Name")
|
||||||
version := r.Header.Get("Houston-Service-Version")
|
version := r.Header.Get("Houston-Service-Version")
|
||||||
filename := r.Header.Get("Houston-Service-Filename")
|
filename := r.Header.Get("Houston-Service-Filename")
|
||||||
dir := fmt.Sprintf("./downloads/%s/%s", name, version)
|
dir := path.Join(h.downloadPath, name, version)
|
||||||
if err := os.MkdirAll(dir, os.ModePerm); err == nil {
|
if err := os.MkdirAll(dir, 0775); err == nil {
|
||||||
file, _ := os.Create(path.Join(dir, filename))
|
file, _ := os.Create(path.Join(dir, filename))
|
||||||
if file != nil {
|
if file != nil {
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
@ -85,6 +102,8 @@ func (h *houstonHandler) RegisterHandlers(serveMux *http.ServeMux, prefix string
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var noauth = flagx.Bool("noauth", false, "")
|
||||||
|
|
||||||
func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
defer func() {
|
defer func() {
|
||||||
s := recover()
|
s := recover()
|
||||||
@ -99,6 +118,38 @@ func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var userinfo map[string]any
|
||||||
|
if !*noauth {
|
||||||
|
authheader := r.Header.Get("Authorization")
|
||||||
|
if len(authheader) == 0 {
|
||||||
|
logger.Println("Authorization header is not valid :", authheader)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", "https://graph.microsoft.com/oidc/userinfo", nil)
|
||||||
|
req.Header.Add("Authorization", authheader)
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("graph microsoft api call failed :", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
raw, _ := io.ReadAll(resp.Body)
|
||||||
|
if err = json.Unmarshal(raw, &userinfo); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, expired := userinfo["error"]; expired {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var operation string
|
var operation string
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
operation = r.FormValue("operation")
|
operation = r.FormValue("operation")
|
||||||
@ -119,15 +170,14 @@ func (h *houstonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.PostForm == nil {
|
|
||||||
r.ParseMultipartForm(defaultMaxMemory)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []reflect.Value{
|
args := []reflect.Value{
|
||||||
reflect.ValueOf(h),
|
reflect.ValueOf(h),
|
||||||
reflect.ValueOf(w),
|
reflect.ValueOf(w),
|
||||||
reflect.ValueOf(r),
|
reflect.ValueOf(r),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||||
|
|
||||||
method.Func.Call(args)
|
method.Func.Call(args)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,13 +2,11 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@ -19,19 +17,17 @@ type opdef struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProcessSnapshot struct {
|
type ProcessSnapshot struct {
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
Args []string
|
Args []string `json:"args"`
|
||||||
Version string
|
Version string `json:"version"`
|
||||||
State protos.ProcessState
|
State protos.ProcessState `json:"state"`
|
||||||
Pid int32
|
Pid int32 `json:"pid"`
|
||||||
StdoutSize int32
|
|
||||||
StderrSize int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type hostWithChan struct {
|
type hostWithChan struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
Procs []*protos.ProcessDescription
|
Procs []*protos.ProcessDescription `json:"procs"`
|
||||||
Deploys map[string][]*protos.VersionAndArgs
|
Deploys map[string][]*protos.VersionAndArgs `json:"deploys"`
|
||||||
opChan chan *opdef
|
opChan chan *opdef
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,9 +69,6 @@ func (sp *hostPool) regist(desc *protos.OperationQueryRequest) (string, chan *op
|
|||||||
host = makeHostWithChan(desc).withOpChan(host.opChan)
|
host = makeHostWithChan(desc).withOpChan(host.opChan)
|
||||||
}
|
}
|
||||||
sp.hosts[desc.Hostname] = host
|
sp.hosts[desc.Hostname] = host
|
||||||
|
|
||||||
test, _ := json.Marshal(sp.hosts)
|
|
||||||
logger.Println(string(test))
|
|
||||||
return desc.Hostname, host.opChan
|
return desc.Hostname, host.opChan
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,9 +81,6 @@ func (sp *hostPool) refresh(desc *protos.OperationQueryRequest) {
|
|||||||
host = makeHostWithChan(desc).withOpChan(host.opChan)
|
host = makeHostWithChan(desc).withOpChan(host.opChan)
|
||||||
sp.hosts[desc.Hostname] = host
|
sp.hosts[desc.Hostname] = host
|
||||||
}
|
}
|
||||||
|
|
||||||
test, _ := json.Marshal(sp.hosts)
|
|
||||||
logger.Println(string(test))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *hostPool) unregist(key string) {
|
func (sp *hostPool) unregist(key string) {
|
||||||
@ -101,8 +91,8 @@ func (sp *hostPool) unregist(key string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type hostSnapshot struct {
|
type hostSnapshot struct {
|
||||||
Procs []ProcessSnapshot
|
Procs []ProcessSnapshot `json:"procs"`
|
||||||
Deploys map[string][]*protos.VersionAndArgs
|
Deploys map[string][]*protos.VersionAndArgs `json:"deploys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *hostPool) allHosts() map[string]hostSnapshot {
|
func (sp *hostPool) allHosts() map[string]hostSnapshot {
|
||||||
@ -119,8 +109,6 @@ func (sp *hostPool) allHosts() map[string]hostSnapshot {
|
|||||||
Version: p.Version,
|
Version: p.Version,
|
||||||
State: p.State,
|
State: p.State,
|
||||||
Pid: p.Pid,
|
Pid: p.Pid,
|
||||||
StdoutSize: p.StdoutSize,
|
|
||||||
StderrSize: p.StderrSize,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
out[hn] = hostSnapshot{
|
out[hn] = hostSnapshot{
|
||||||
@ -163,6 +151,12 @@ func marshal(argval reflect.Value, output map[string]string) map[string]string {
|
|||||||
marshal(argval.Field(i), output)
|
marshal(argval.Field(i), output)
|
||||||
} else if argval.Field(i).CanInt() {
|
} else if argval.Field(i).CanInt() {
|
||||||
output[argval.Type().Field(i).Name] = fmt.Sprintf("%d", argval.Field(i).Int())
|
output[argval.Type().Field(i).Name] = fmt.Sprintf("%d", argval.Field(i).Int())
|
||||||
|
} else if argval.Field(i).Kind() == reflect.Array || argval.Field(i).Kind() == reflect.Slice {
|
||||||
|
var conv []string
|
||||||
|
for j := 0; j < argval.Field(i).Len(); j++ {
|
||||||
|
conv = append(conv, argval.Field(i).Index(j).String())
|
||||||
|
}
|
||||||
|
output[argval.Type().Field(i).Name] = strings.Join(conv, "\n")
|
||||||
} else {
|
} else {
|
||||||
output[argval.Type().Field(i).Name] = argval.Field(i).String()
|
output[argval.Type().Field(i).Name] = argval.Field(i).String()
|
||||||
}
|
}
|
||||||
@ -171,8 +165,6 @@ func marshal(argval reflect.Value, output map[string]string) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (os *operationServer) Query(svr protos.Operation_QueryServer) error {
|
func (os *operationServer) Query(svr protos.Operation_QueryServer) error {
|
||||||
// 서버는 업데이트가 있는지 확인하고 있으면 stream에 응답을 보낸다.
|
|
||||||
// 업데이트가 없으면 대기
|
|
||||||
desc, err := svr.Recv()
|
desc, err := svr.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -341,27 +333,19 @@ func (os *operationServer) RestartProcess(d RestartProcessRequest) {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(d.hostnames) > 0 {
|
if len(d.hostnames) != 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// hostname만 재시작
|
// hostname만 재시작
|
||||||
var final []*hostWithChan
|
|
||||||
conv := make(map[string]bool)
|
|
||||||
for _, hn := range d.hostnames {
|
|
||||||
conv[hn] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range targets {
|
|
||||||
if _, ok := conv[t.Hostname]; ok {
|
|
||||||
final = append(final, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targets = final
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range targets {
|
for _, t := range targets {
|
||||||
|
if t.Hostname == d.hostnames[0] {
|
||||||
t.opChan <- &opdef{
|
t.opChan <- &opdef{
|
||||||
operation: shared.Restart,
|
operation: shared.Restart,
|
||||||
args: d,
|
args: d,
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
|
||||||
@ -12,11 +17,17 @@ import (
|
|||||||
|
|
||||||
// protoc --go_out=. --go-grpc_out=. protos/*.proto
|
// protoc --go_out=. --go-grpc_out=. protos/*.proto
|
||||||
type HoustonServer interface {
|
type HoustonServer interface {
|
||||||
Start(port int) error
|
Start() error
|
||||||
Stop()
|
Stop()
|
||||||
Operation() Operation
|
Operation() Operation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverConfig struct {
|
||||||
|
GrpcPort int `json:"grpc_port"`
|
||||||
|
StorageRoot string `json:"storage_path"`
|
||||||
|
RunAsClient bool `json:"run_as_client"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeployRequest struct {
|
type DeployRequest struct {
|
||||||
shared.DeployRequest
|
shared.DeployRequest
|
||||||
hostnames []string
|
hostnames []string
|
||||||
@ -99,8 +110,42 @@ type Operation interface {
|
|||||||
Hosts() map[string]hostSnapshot
|
Hosts() map[string]hostSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() HoustonServer {
|
func loadServerConfig() serverConfig {
|
||||||
|
configFile, err := os.Open("config.json")
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
|
logger.Println(err)
|
||||||
|
return serverConfig{
|
||||||
|
GrpcPort: 8080,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Houston == nil {
|
||||||
|
logger.Println(`"houston" object is missing in config.json`)
|
||||||
|
return serverConfig{
|
||||||
|
GrpcPort: 8080,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.Houston.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer() HoustonServer {
|
||||||
var opts []grpc.ServerOption
|
var opts []grpc.ServerOption
|
||||||
grpcServer := grpc.NewServer(opts...)
|
grpcServer := grpc.NewServer(opts...)
|
||||||
|
|
||||||
@ -113,6 +158,7 @@ func NewServer() HoustonServer {
|
|||||||
rpcServer: grpcServer,
|
rpcServer: grpcServer,
|
||||||
os: os,
|
os: os,
|
||||||
ms: ms,
|
ms: ms,
|
||||||
|
port: loadServerConfig().GrpcPort,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,23 +166,44 @@ type houstonServer struct {
|
|||||||
rpcServer *grpc.Server
|
rpcServer *grpc.Server
|
||||||
os *operationServer
|
os *operationServer
|
||||||
ms *monitorServer
|
ms *monitorServer
|
||||||
|
port int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *houstonServer) Start(port int) error {
|
func (hs *houstonServer) Start() error {
|
||||||
lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
|
logger.Println("houston server is started at port", hs.port)
|
||||||
|
lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", hs.port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hs.rpcServer.Serve(lis); err != nil {
|
closeCount := int32(0)
|
||||||
|
var hc client.HoustonClient
|
||||||
|
if loadServerConfig().RunAsClient {
|
||||||
|
hc, err = client.NewClient(false)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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() {
|
||||||
hs.rpcServer.GracefulStop()
|
hs.rpcServer.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *houstonServer) Operation() Operation {
|
func (hs *houstonServer) Operation() Operation {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package shared
|
|||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ type DeployRequest struct {
|
|||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Url string
|
Url string
|
||||||
|
Config string
|
||||||
AccessToken string
|
AccessToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ type WithdrawRequest struct {
|
|||||||
type StartProcessRequest struct {
|
type StartProcessRequest struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Args string
|
Args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type StopProcessRequest struct {
|
type StopProcessRequest struct {
|
||||||
@ -44,7 +46,8 @@ type StopProcessRequest struct {
|
|||||||
|
|
||||||
type RestartProcessRequest struct {
|
type RestartProcessRequest struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Pid int32
|
||||||
|
Config string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UploadRequest struct {
|
type UploadRequest struct {
|
||||||
@ -88,9 +91,10 @@ func CompareVersionString(lhs, rhs ParsedVersionString) int {
|
|||||||
return len(lhs) - len(rhs)
|
return len(lhs) - len(rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindLastestVersion(root string) (string, error) {
|
func FindLastestVersion(storageRoot, name string) (string, error) {
|
||||||
// 최신 버전을 찾음
|
// 최신 버전을 찾음
|
||||||
entries, err := os.ReadDir(root)
|
targetPath := path.Join(storageRoot, name)
|
||||||
|
entries, err := os.ReadDir(targetPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,8 @@ const (
|
|||||||
ProcessState_Stopped ProcessState = 0
|
ProcessState_Stopped ProcessState = 0
|
||||||
ProcessState_Stopping ProcessState = 1
|
ProcessState_Stopping ProcessState = 1
|
||||||
ProcessState_Running ProcessState = 2
|
ProcessState_Running ProcessState = 2
|
||||||
ProcessState_Error ProcessState = 3
|
ProcessState_Restart ProcessState = 3
|
||||||
|
ProcessState_Error ProcessState = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for ProcessState.
|
// Enum value maps for ProcessState.
|
||||||
@ -35,13 +36,15 @@ var (
|
|||||||
0: "Stopped",
|
0: "Stopped",
|
||||||
1: "Stopping",
|
1: "Stopping",
|
||||||
2: "Running",
|
2: "Running",
|
||||||
3: "Error",
|
3: "Restart",
|
||||||
|
4: "Error",
|
||||||
}
|
}
|
||||||
ProcessState_value = map[string]int32{
|
ProcessState_value = map[string]int32{
|
||||||
"Stopped": 0,
|
"Stopped": 0,
|
||||||
"Stopping": 1,
|
"Stopping": 1,
|
||||||
"Running": 2,
|
"Running": 2,
|
||||||
"Error": 3,
|
"Restart": 3,
|
||||||
|
"Error": 4,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -255,8 +258,6 @@ type ProcessDescription struct {
|
|||||||
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
|
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
State ProcessState `protobuf:"varint,4,opt,name=state,proto3,enum=ProcessState" json:"state,omitempty"`
|
State ProcessState `protobuf:"varint,4,opt,name=state,proto3,enum=ProcessState" json:"state,omitempty"`
|
||||||
Pid int32 `protobuf:"varint,5,opt,name=pid,proto3" json:"pid,omitempty"`
|
Pid int32 `protobuf:"varint,5,opt,name=pid,proto3" json:"pid,omitempty"`
|
||||||
StdoutSize int32 `protobuf:"varint,6,opt,name=stdout_size,json=stdoutSize,proto3" json:"stdout_size,omitempty"`
|
|
||||||
StderrSize int32 `protobuf:"varint,7,opt,name=stderr_size,json=stderrSize,proto3" json:"stderr_size,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ProcessDescription) Reset() {
|
func (x *ProcessDescription) Reset() {
|
||||||
@ -326,20 +327,6 @@ func (x *ProcessDescription) GetPid() int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ProcessDescription) GetStdoutSize() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.StdoutSize
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ProcessDescription) GetStderrSize() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.StderrSize
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type OperationQueryResponse struct {
|
type OperationQueryResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@ -419,7 +406,7 @@ var file_protos_operation_proto_rawDesc = []byte{
|
|||||||
0x63, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x18, 0x03, 0x20,
|
0x63, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x18, 0x03, 0x20,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65,
|
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65,
|
||||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x22,
|
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x73, 0x22,
|
||||||
0xcf, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x73, 0x63, 0x72,
|
0x8d, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x73, 0x63, 0x72,
|
||||||
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72,
|
||||||
0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18,
|
0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x18,
|
||||||
@ -427,35 +414,32 @@ var file_protos_operation_proto_rawDesc = []byte{
|
|||||||
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74,
|
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74,
|
||||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73,
|
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73,
|
||||||
0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a,
|
0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a,
|
||||||
0x03, 0x70, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12,
|
0x03, 0x70, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22,
|
||||||
0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06,
|
0xa6, 0x01, 0x0a, 0x16, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65,
|
||||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x53, 0x69, 0x7a, 0x65,
|
0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70,
|
||||||
0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
|
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f,
|
||||||
0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x53, 0x69, 0x7a,
|
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73,
|
||||||
0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51,
|
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
|
||||||
0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
|
0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e,
|
||||||
0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x1a,
|
||||||
0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x72,
|
0x37, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||||
0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61,
|
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
|
||||||
0x65, 0x2e, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x61, 0x72, 0x67,
|
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4e, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x63,
|
||||||
0x73, 0x1a, 0x37, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70,
|
||||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
0x70, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x69, 0x6e,
|
||||||
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x67, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x02,
|
||||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x41, 0x0a, 0x0c, 0x50, 0x72,
|
0x12, 0x0b, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x10, 0x03, 0x12, 0x09, 0x0a,
|
||||||
0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x74,
|
0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x04, 0x32, 0x78, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72,
|
||||||
0x6f, 0x70, 0x70, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x74, 0x6f, 0x70, 0x70,
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x16,
|
||||||
0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67,
|
0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52,
|
||||||
0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x32, 0x78, 0x0a,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
|
||||||
0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x05, 0x51, 0x75,
|
0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||||
0x65, 0x72, 0x79, 0x12, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51,
|
0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
|
||||||
0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x4f, 0x70,
|
0x12, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72,
|
||||||
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70,
|
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65,
|
0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f,
|
||||||
0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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 (
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
["././././warehouse.exe","-dev","-port=8090"]
|
|
||||||
@ -1 +0,0 @@
|
|||||||
2023/05/26 16:45:31 warehouse.exe 2023-05-26 16:11:31.7258831 +0900 KST
|
|
||||||
Reference in New Issue
Block a user