houston package 독립

This commit is contained in:
2023-05-21 23:37:54 +09:00
commit 3e8c79a48b
20 changed files with 3152 additions and 0 deletions

21
server/monitor.go Normal file
View File

@ -0,0 +1,21 @@
package server
import (
"context"
"houston/shared/protos"
)
type monitorServer struct {
protos.UnimplementedMonitorServer
}
func newMonitorServer() *monitorServer {
return &monitorServer{}
}
func (ms *monitorServer) Report(ctx context.Context, metrics *protos.Metrics) (*protos.Empty, error) {
select {
case <-ctx.Done():
default:
}
return &protos.Empty{}, nil
}

407
server/operation.go Normal file
View File

@ -0,0 +1,407 @@
package server
import (
"context"
"encoding/json"
"fmt"
"go-ayo/common/logger"
"houston/shared"
"houston/shared/protos"
"reflect"
"sync"
)
type opdef struct {
operation shared.Operation
args any
}
type ProcessSnapshot struct {
Name string
Args []string
Version string
State protos.ProcessState
Pid int32
StdoutSize int32
StderrSize int32
}
type hostWithChan struct {
Hostname string
Procs []*protos.ProcessDescription
Deploys map[string][]*protos.VersionAndArgs
opChan chan *opdef
}
func makeHostWithChan(desc *protos.OperationQueryRequest) *hostWithChan {
newdeploys := make(map[string][]*protos.VersionAndArgs)
for _, deploy := range desc.Deploys {
newdeploys[deploy.Name] = deploy.Versions
}
return &hostWithChan{
Hostname: desc.GetHostname(),
Procs: desc.Procs,
Deploys: newdeploys,
}
}
func (pc *hostWithChan) withOpChan(c chan *opdef) *hostWithChan {
pc.opChan = c
return pc
}
func (pc *hostWithChan) makeOpChan() *hostWithChan {
pc.opChan = make(chan *opdef, 1)
return pc
}
type hostPool struct {
sync.Mutex
hosts map[string]*hostWithChan
}
func (sp *hostPool) regist(desc *protos.OperationQueryRequest) (string, chan *opdef) {
sp.Lock()
defer sp.Unlock()
host := sp.hosts[desc.Hostname]
if host == nil {
host = makeHostWithChan(desc).makeOpChan()
} else {
host = makeHostWithChan(desc).withOpChan(host.opChan)
}
sp.hosts[desc.Hostname] = host
test, _ := json.Marshal(sp.hosts)
logger.Println(string(test))
return desc.Hostname, host.opChan
}
func (sp *hostPool) refresh(desc *protos.OperationQueryRequest) {
sp.Lock()
defer sp.Unlock()
host := sp.hosts[desc.Hostname]
if host != nil {
host = makeHostWithChan(desc).withOpChan(host.opChan)
sp.hosts[desc.Hostname] = host
}
test, _ := json.Marshal(sp.hosts)
logger.Println(string(test))
}
func (sp *hostPool) unregist(key string) {
sp.Lock()
defer sp.Unlock()
delete(sp.hosts, key)
}
type hostSnapshot struct {
Procs []ProcessSnapshot
Deploys map[string][]*protos.VersionAndArgs
}
func (sp *hostPool) allHosts() map[string]hostSnapshot {
sp.Lock()
defer sp.Unlock()
out := make(map[string]hostSnapshot)
for hn, v := range sp.hosts {
procs := make([]ProcessSnapshot, 0, len(v.Procs))
for _, p := range v.Procs {
procs = append(procs, ProcessSnapshot{
Name: p.Name,
Args: p.Args,
Version: p.Version,
State: p.State,
Pid: p.Pid,
StdoutSize: p.StdoutSize,
StderrSize: p.StderrSize,
})
}
out[hn] = hostSnapshot{
Procs: procs,
Deploys: v.Deploys,
}
}
return out
}
func (sp *hostPool) query(filter func(*hostWithChan) bool) []*hostWithChan {
sp.Lock()
defer sp.Unlock()
var targets []*hostWithChan
for _, v := range sp.hosts {
if filter(v) {
targets = append(targets, v)
}
}
return targets
}
type operationServer struct {
protos.UnimplementedOperationServer
hp hostPool
}
func marshal(argval reflect.Value, output map[string]string) map[string]string {
if argval.Kind() == reflect.Pointer {
argval = argval.Elem()
}
for i := 0; i < argval.Type().NumField(); i++ {
if !argval.Type().Field(i).IsExported() {
continue
}
if argval.Type().Field(i).Anonymous {
marshal(argval.Field(i), output)
} else if argval.Field(i).CanInt() {
output[argval.Type().Field(i).Name] = fmt.Sprintf("%d", argval.Field(i).Int())
} else {
output[argval.Type().Field(i).Name] = argval.Field(i).String()
}
}
return output
}
func (os *operationServer) Query(svr protos.Operation_QueryServer) error {
// 서버는 업데이트가 있는지 확인하고 있으면 stream에 응답을 보낸다.
// 업데이트가 없으면 대기
desc, err := svr.Recv()
if err != nil {
return err
}
key, opChan := os.hp.regist(desc)
defer os.hp.unregist(key)
Outer:
for {
select {
case <-svr.Context().Done():
break Outer
case opdef := <-opChan:
svr.Send(&protos.OperationQueryResponse{
Operation: string(opdef.operation),
Args: marshal(reflect.ValueOf(opdef.args), make(map[string]string)),
})
}
}
return nil
}
func (os *operationServer) Refresh(ctx context.Context, desc *protos.OperationQueryRequest) (*protos.Empty, error) {
os.hp.refresh(desc)
return &protos.Empty{}, nil
}
func (os *operationServer) Deploy(d DeployRequest) {
var targets []*hostWithChan
if len(d.hostnames) > 0 {
// hostname에 배포
conv := make(map[string]bool)
for _, hn := range d.hostnames {
conv[hn] = true
}
targets = os.hp.query(func(p *hostWithChan) bool {
_, ok := conv[p.Hostname]
return ok
})
} else {
// d.process에 모두 배포
targets = os.hp.query(func(p *hostWithChan) bool {
for _, p := range p.Procs {
if p.Name == d.Name {
return true
}
}
return false
})
}
for _, t := range targets {
t.opChan <- &opdef{
operation: shared.Deploy,
args: d,
}
}
}
func (os *operationServer) Withdraw(d WithdrawRequest) {
// 프로세스가 안돌고 있는 호스트에서도 회수해야 할 수 있다.
targets := os.hp.query(func(p *hostWithChan) bool {
return true
})
if len(d.hostnames) > 0 {
// 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 {
t.opChan <- &opdef{
operation: shared.Withdraw,
args: d,
}
}
}
func (os *operationServer) StartProcess(d StartProcessRequest) {
targets := os.hp.query(func(p *hostWithChan) bool {
// 디플로이만 되어있어도 해당
_, ok := p.Deploys[d.Name]
return ok
})
if len(d.hostnames) > 0 {
// 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 {
t.opChan <- &opdef{
operation: shared.Start,
args: d,
}
}
}
func (os *operationServer) StopProcess(d StopProcessRequest) {
// d.process 모두 정지
targets := os.hp.query(func(p *hostWithChan) bool {
for _, p := range p.Procs {
if p.Name == d.Name {
return true
}
}
return false
})
if len(d.hostnames) > 0 {
// 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 {
t.opChan <- &opdef{
operation: shared.Stop,
args: d,
}
}
}
func (os *operationServer) RestartProcess(d RestartProcessRequest) {
targets := os.hp.query(func(p *hostWithChan) bool {
for _, p := range p.Procs {
if p.Name == d.Name {
return true
}
}
return false
})
if len(d.hostnames) > 0 {
// 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 {
t.opChan <- &opdef{
operation: shared.Restart,
args: d,
}
}
}
func (os *operationServer) Upload(d UploadRequest) {
targets := os.hp.query(func(p *hostWithChan) bool {
// 실행 중이 아니라 디플로이만 되어있어도 해당
_, ok := p.Deploys[d.Name]
return ok
})
if len(d.hostnames) > 0 {
// 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 {
t.opChan <- &opdef{
operation: shared.Upload,
args: d,
}
}
}
func (os *operationServer) Hosts() map[string]hostSnapshot {
return os.hp.allHosts()
}
func newOperationServer() *operationServer {
return &operationServer{
hp: hostPool{
hosts: map[string]*hostWithChan{},
},
}
}

143
server/server.go Normal file
View File

@ -0,0 +1,143 @@
package server
import (
"fmt"
"houston/shared"
"houston/shared/protos"
"net"
"google.golang.org/grpc"
)
// protoc --go_out=. --go-grpc_out=. protos/*.proto
type HoustonServer interface {
Start(port int) error
Stop()
Operation() Operation
}
type DeployRequest struct {
shared.DeployRequest
hostnames []string
}
func MakeDeployRequest(req shared.DeployRequest, targets []string) DeployRequest {
return DeployRequest{
DeployRequest: req,
hostnames: targets,
}
}
type WithdrawRequest struct {
shared.WithdrawRequest
hostnames []string
}
func MakeWithdrawRequest(req shared.WithdrawRequest, targets []string) WithdrawRequest {
return WithdrawRequest{
WithdrawRequest: req,
hostnames: targets,
}
}
type StartProcessRequest struct {
shared.StartProcessRequest
hostnames []string
}
func MakeStartProcessRequest(req shared.StartProcessRequest, targets []string) StartProcessRequest {
return StartProcessRequest{
StartProcessRequest: req,
hostnames: targets,
}
}
type StopProcessRequest struct {
shared.StopProcessRequest
hostnames []string
}
func MakeStopRequest(req shared.StopProcessRequest, targets []string) StopProcessRequest {
return StopProcessRequest{
StopProcessRequest: req,
hostnames: targets,
}
}
type RestartProcessRequest struct {
shared.RestartProcessRequest
hostnames []string
}
func MakeRestartRequest(req shared.RestartProcessRequest, targets []string) RestartProcessRequest {
return RestartProcessRequest{
RestartProcessRequest: req,
hostnames: targets,
}
}
type UploadRequest struct {
shared.UploadRequest
hostnames []string
}
func MakeUploadRequest(req shared.UploadRequest, targets []string) UploadRequest {
return UploadRequest{
UploadRequest: req,
hostnames: targets,
}
}
type Operation interface {
Deploy(DeployRequest)
Withdraw(WithdrawRequest)
StartProcess(StartProcessRequest)
StopProcess(StopProcessRequest)
RestartProcess(RestartProcessRequest)
Upload(UploadRequest)
Hosts() map[string]hostSnapshot
}
func NewServer() HoustonServer {
var opts []grpc.ServerOption
grpcServer := grpc.NewServer(opts...)
os := newOperationServer()
ms := newMonitorServer()
protos.RegisterOperationServer(grpcServer, os)
protos.RegisterMonitorServer(grpcServer, ms)
return &houstonServer{
rpcServer: grpcServer,
os: os,
ms: ms,
}
}
type houstonServer struct {
rpcServer *grpc.Server
os *operationServer
ms *monitorServer
}
func (hs *houstonServer) Start(port int) error {
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
if err != nil {
return err
}
if err := hs.rpcServer.Serve(lis); err != nil {
return err
}
return nil
}
func (hs *houstonServer) Stop() {
hs.rpcServer.GracefulStop()
}
func (hs *houstonServer) Operation() Operation {
return hs.os
}