yt-function-sdk-go/ytfunction.go

549 lines
15 KiB
Go
Raw Normal View History

2022-10-23 20:12:23 +03:00
package ytfunction
import (
"crypto/rand"
"fmt"
2022-10-25 12:59:22 +03:00
"strings"
2022-10-28 11:19:03 +03:00
"sync"
2022-10-25 12:59:22 +03:00
"time"
2022-10-23 20:12:23 +03:00
2022-12-01 15:23:42 +03:00
"git.ymnuktech.ru/ymnuk/yt-function-sdk-go/network"
ytlogger "git.ymnuktech.ru/ymnuk/yt-logger-go"
"git.ymnuktech.ru/ymnuk/yt-logger-go/log"
2022-10-23 20:12:23 +03:00
"github.com/nats-io/nats.go"
2022-10-25 12:59:22 +03:00
uuid "github.com/satori/go.uuid"
"google.golang.org/protobuf/proto"
2022-10-23 20:12:23 +03:00
)
2022-10-28 21:22:30 +03:00
type ChanRes struct {
2022-11-04 17:10:59 +03:00
res []byte
header *network.Header
err error
2022-10-28 21:22:30 +03:00
}
2022-10-23 20:12:23 +03:00
type funcDesc struct {
2022-10-25 14:47:26 +03:00
Name string
F func(header *network.Header, payload []byte) (result []byte, err error)
inputChan chan *nats.Msg
subj *nats.Subscription
conn *nats.Conn
timeout time.Duration
acceptFunc func(access string) bool
2022-12-01 15:23:42 +03:00
logger *ytlogger.YTLogger
2022-10-23 20:12:23 +03:00
}
2022-10-25 12:59:22 +03:00
func (fDesc *funcDesc) worker() {
2022-10-23 20:12:23 +03:00
for {
2022-10-25 12:59:22 +03:00
data, ok := <-fDesc.inputChan
2022-10-23 20:12:23 +03:00
if !ok {
break
}
2022-10-25 12:59:22 +03:00
var err error
2022-11-04 16:36:03 +03:00
pkg := &network.Function{}
2022-10-25 12:59:22 +03:00
2022-11-04 16:36:03 +03:00
err = proto.Unmarshal(data.Data, pkg)
2022-10-23 20:12:23 +03:00
if err != nil {
2022-10-25 12:59:22 +03:00
panic(err)
}
2022-12-01 15:23:42 +03:00
timeStart := time.Now()
2022-10-25 14:47:26 +03:00
// Проверка может ли вызывающая сторона вызвать функцию
if !fDesc.acceptFunc(pkg.Metadata.CallFrom) {
if pkg.Metadata.QueueCallback != "" {
pkg.ErrNo = 403
pkg.Error = "forbidden"
pkg.Metadata.CallResponseID = uuid.NewV4().String()
2022-11-04 16:36:03 +03:00
buff, err := proto.Marshal(pkg)
2022-10-25 14:47:26 +03:00
if err != nil {
panic(err)
}
2022-12-01 15:23:42 +03:00
if fDesc.logger != nil {
fDesc.logger.Err(&pkg.Metadata.FuncName, &pkg.Metadata.CallID, &pkg.Header.CallPrevID, &pkg.Metadata.CallFrom, &pkg.Metadata.CallResponseID, &timeStart, &[]time.Time{time.Now()}[0], nil, &pkg.ErrNo, &pkg.Error, &pkg.ErrNo, nil, nil)
}
2022-11-22 09:19:07 +03:00
if err = fDesc.conn.Publish(pkg.Metadata.QueueCallback, buff); err != nil {
continue
}
2022-12-01 15:23:42 +03:00
} else {
if fDesc.logger != nil {
fDesc.logger.Err(&pkg.Metadata.FuncName, &pkg.Metadata.CallID, &pkg.Header.CallPrevID, &pkg.Metadata.CallFrom, &pkg.Metadata.CallResponseID, &timeStart, &[]time.Time{time.Now()}[0], nil, &pkg.ErrNo, &pkg.Error, &pkg.ErrNo, nil, nil)
}
2022-10-25 14:47:26 +03:00
}
continue
}
// Вызов функции
2022-10-28 21:22:30 +03:00
chanRes := make(chan *ChanRes)
2022-10-25 12:59:22 +03:00
go func() {
2022-11-04 17:10:59 +03:00
if pkg.Header.ResponseHeaders == nil {
pkg.Header.ResponseHeaders = make(map[string]string)
}
2022-10-28 21:22:30 +03:00
result, err := fDesc.F(pkg.Header, pkg.Payload)
chanRes <- &ChanRes{
res: result,
err: err,
2022-10-25 12:59:22 +03:00
}
}()
select {
2022-10-28 21:22:30 +03:00
case res := <-chanRes:
if res.err != nil {
if pkg.Metadata.QueueCallback != "" {
pkg.ErrNo = 500
2022-11-23 12:09:34 +03:00
//if err != nil {
pkg.Error = res.err.Error()
/*} else {
2022-11-23 09:48:42 +03:00
pkg.Error = ""
2022-11-23 12:09:34 +03:00
}*/
2022-10-28 21:22:30 +03:00
pkg.Metadata.CallResponseID = uuid.NewV4().String()
2022-12-01 15:23:42 +03:00
if fDesc.logger != nil {
fDesc.logger.Err(&pkg.Metadata.FuncName, &pkg.Metadata.CallID, &pkg.Header.CallPrevID, &pkg.Metadata.CallFrom, &pkg.Metadata.CallResponseID, &timeStart, &[]time.Time{time.Now()}[0], nil, &pkg.ErrNo, &pkg.Error, &pkg.ErrNo, nil, nil)
}
2022-11-23 12:09:34 +03:00
buff, err := proto.Marshal(pkg)
if err != nil {
//panic(err)
continue
}
//fDesc.conn.Publish(pkg.Metadata.QueueCallback, []byte(pkg.Error))
fDesc.conn.Publish(pkg.Metadata.QueueCallback, buff)
2022-10-28 21:22:30 +03:00
}
continue
}
2022-10-25 14:47:26 +03:00
if pkg.Metadata.QueueCallback != "" {
2022-10-28 21:22:30 +03:00
pkg.Payload = res.res
2022-10-25 14:47:26 +03:00
pkg.Metadata.CallResponseID = uuid.NewV4().String()
2022-12-01 15:23:42 +03:00
if fDesc.logger != nil {
fDesc.logger.Info(&pkg.Metadata.FuncName, &pkg.Metadata.CallID, &pkg.Header.CallPrevID, &pkg.Metadata.CallFrom, &pkg.Metadata.CallResponseID, &timeStart, &[]time.Time{time.Now()}[0], nil, &pkg.ErrNo, &pkg.Error, &pkg.ErrNo, &[]int64{int64(len(pkg.Payload))}[0], nil)
}
2022-11-04 16:36:03 +03:00
buff, err := proto.Marshal(pkg)
2022-10-25 14:47:26 +03:00
if err != nil {
2022-11-22 09:19:07 +03:00
//panic(err)
continue
}
if err = fDesc.conn.Publish(pkg.Metadata.QueueCallback, buff); err != nil {
continue
2022-10-25 14:47:26 +03:00
}
2022-10-25 12:59:22 +03:00
}
2022-10-23 20:12:23 +03:00
continue
2022-10-25 12:59:22 +03:00
case <-time.After(fDesc.timeout):
2022-11-23 09:48:42 +03:00
//if err != nil {
if pkg.Metadata.QueueCallback != "" {
pkg.ErrNo = 408
//pkg.Error = err.Error()
pkg.Error = "function call timeout"
pkg.Metadata.CallResponseID = uuid.NewV4().String()
2022-12-01 15:23:42 +03:00
if fDesc.logger != nil {
fDesc.logger.Err(&pkg.Metadata.FuncName, &pkg.Metadata.CallID, &pkg.Header.CallPrevID, &pkg.Metadata.CallFrom, &pkg.Metadata.CallResponseID, &timeStart, &[]time.Time{time.Now()}[0], nil, &pkg.ErrNo, &pkg.Error, &pkg.ErrNo, nil, nil)
}
2022-11-23 12:09:34 +03:00
buff, err := proto.Marshal(pkg)
if err != nil {
continue
}
//fDesc.conn.Publish(pkg.Metadata.QueueCallback, []byte("function call timeout"))
if err = fDesc.conn.Publish(pkg.Metadata.QueueCallback, buff); err != nil {
continue
}
2022-10-25 12:59:22 +03:00
}
2022-11-23 09:48:42 +03:00
continue
//}
2022-10-23 20:12:23 +03:00
}
}
}
2022-11-05 12:41:03 +03:00
type CallbackFunc struct {
F func(err error, header *network.Header, result []byte)
TimeCall time.Time
2022-11-30 14:24:24 +03:00
//Metadata *network.Metadata
2022-11-05 12:41:03 +03:00
}
2022-10-28 11:19:03 +03:00
// Сервер инстанса
2022-10-23 20:12:23 +03:00
type Serve struct {
2022-11-11 19:07:23 +03:00
//NatsHost string
//NatsPort string
NatsAddr string
2022-11-05 12:41:03 +03:00
nc *nats.Conn
hasNats bool
projectName string
moduleName string
funcs map[string]funcDesc
timeoutCallback time.Duration
queueCallback string
chanQueueCallback chan *nats.Msg
subj *nats.Subscription
//callbackFuncs map[string]func(err error, header *network.Header, result []byte)
callbackFuncs map[string]*CallbackFunc
2022-10-28 11:19:03 +03:00
callbackFuncsMutex sync.Mutex
accept map[string]map[string]bool
2022-12-01 15:23:42 +03:00
logger *ytlogger.YTLogger
queueNameLog *string
2022-10-23 20:12:23 +03:00
}
2022-10-28 11:19:03 +03:00
// Создание нового инстанса сервера
2022-12-01 15:23:42 +03:00
func NewServe(addr string, projectName string, moduleName string, natsServ *nats.Conn, priority *ytlogger.Priority, queueNameLog *string) *Serve {
2022-10-25 12:59:22 +03:00
if strings.Trim(moduleName, " ") == "" {
moduleName = "default"
} else {
moduleName = strings.Trim(moduleName, " ")
}
if strings.Trim(projectName, " ") == "" {
projectName = "default"
} else {
projectName = strings.Trim(projectName, " ")
2022-10-23 20:12:23 +03:00
}
serve := &Serve{}
if natsServ != nil {
serve.nc = natsServ
} else {
2022-11-11 19:07:23 +03:00
/*serve.NatsHost = host
serve.NatsPort = port*/
serve.NatsAddr = addr
}
2022-10-25 12:59:22 +03:00
serve.projectName = projectName
2022-10-23 20:12:23 +03:00
serve.moduleName = moduleName
serve.funcs = make(map[string]funcDesc)
2022-10-25 14:47:26 +03:00
serve.accept = make(map[string]map[string]bool)
serve.accept["system"] = make(map[string]bool)
serve.accept["system"]["gateway"] = true
serve.accept["system"]["security"] = true
serve.accept["system"]["settings"] = true
serve.accept["system"]["telegram"] = true
//serve.accept[projectName][moduleName] = true
2022-11-05 12:41:03 +03:00
serve.timeoutCallback = time.Second * 30
2022-12-01 15:23:42 +03:00
if queueNameLog != nil {
if priority == nil {
priority = &[]ytlogger.Priority{ytlogger.LOG_INFO}[0]
}
serve.logger = ytlogger.NewYTLogger(*priority, 100000, serve.sendLog)
serve.queueNameLog = queueNameLog
}
2022-10-23 20:12:23 +03:00
return serve
}
2022-12-01 15:23:42 +03:00
// Функция отправки логов на удаленный сервер
func (serve *Serve) sendLog(pkg *log.Pkg) {
var b []byte
var err error
b, err = proto.Marshal(pkg)
if err != nil {
return
}
serve.nc.Publish(*serve.queueNameLog, b)
}
2022-10-28 11:19:03 +03:00
// Установка разрешающих правил выполнения функций
2022-10-25 14:47:26 +03:00
func (serve *Serve) AddAccept(ruleName string) bool {
return serve.AddAcceptBool(ruleName, true)
}
// Установка разрешающих правил выполнения функций
func (serve *Serve) AddAcceptBool(ruleName string, allow bool) bool {
ruleName = strings.Trim(ruleName, " ")
if ruleName == "" {
return false
}
if ruleName == "*" {
serve.accept = make(map[string]map[string]bool)
serve.accept["*"] = make(map[string]bool)
serve.accept["*"]["*"] = allow
} else {
rules := strings.Split(ruleName, ".")
for i := range rules {
rules[i] = strings.Trim(rules[i], " ")
}
switch len(rules) {
case 0:
return false
case 1:
serve.accept[rules[0]] = make(map[string]bool)
serve.accept[rules[0]]["*"] = allow
case 2:
if rules[0] == "*" || rules[0] == "" {
return false
}
if rules[1] == "" {
return false
}
if _, ok := serve.accept[rules[0]]; !ok {
serve.accept[rules[0]] = make(map[string]bool)
}
if rules[1] == "*" {
serve.accept[rules[0]]["*"] = allow
} else {
serve.accept[rules[0]][rules[1]] = allow
}
}
}
return true
}
2022-10-28 13:46:43 +03:00
func (serve *Serve) natsErrHandler(nc *nats.Conn, sub *nats.Subscription, natsErr error) {
fmt.Printf("error: %v\n", natsErr)
if natsErr == nats.ErrSlowConsumer {
pendingMsgs, _, err := sub.Pending()
if err != nil {
fmt.Printf("couldn't get pending messages: %v", err)
return
}
fmt.Printf("Falling behind with %d pending messages on subject %q.\n",
pendingMsgs, sub.Subject)
// Log error, notify operations...
}
// check for other errors
}
2022-10-28 11:19:03 +03:00
// Запуск текущего инстанса сервера
2022-10-23 20:12:23 +03:00
func (serve *Serve) Run() (err error) {
if serve.nc == nil {
2022-11-11 19:07:23 +03:00
serve.nc, err = nats.Connect(serve.NatsAddr, nats.ErrorHandler(serve.natsErrHandler))
if err != nil {
return
}
2022-10-28 15:06:24 +03:00
} else {
serve.hasNats = true
2022-10-23 20:12:23 +03:00
}
2022-10-25 12:59:22 +03:00
rnd := make([]byte, 16)
_, err = rand.Read(rnd)
if err != nil {
return
}
2022-11-05 12:41:03 +03:00
//serve.callbackFuncs = make(map[string]func(err error, header *network.Header, result []byte))
serve.callbackFuncs = make(map[string]*CallbackFunc)
2022-10-25 12:59:22 +03:00
serve.queueCallback = fmt.Sprintf("%s.%s.%x", serve.projectName, serve.moduleName, rnd)
serve.chanQueueCallback = make(chan *nats.Msg)
serve.subj, err = serve.nc.ChanQueueSubscribe(serve.queueCallback, serve.projectName+"."+serve.moduleName, serve.chanQueueCallback)
2022-10-25 12:59:22 +03:00
go serve.worker()
2022-10-23 20:12:23 +03:00
return
}
2022-11-05 12:41:03 +03:00
func (serve *Serve) cleanTimeout() {
serve.callbackFuncsMutex.Lock()
defer serve.callbackFuncsMutex.Unlock()
for k, v := range serve.callbackFuncs {
if time.Since(v.TimeCall) > serve.timeoutCallback {
delete(serve.callbackFuncs, k)
err := fmt.Errorf("call function is timed out")
go v.F(err, nil, nil)
}
}
}
2022-10-25 12:59:22 +03:00
func (serve *Serve) worker() {
2022-11-05 12:41:03 +03:00
go func() {
for {
<-time.After(serve.timeoutCallback)
serve.cleanTimeout()
}
}()
2022-10-25 12:59:22 +03:00
for {
if msg, ok := <-serve.chanQueueCallback; !ok {
break
} else {
var err error
var payload network.Function
err = proto.Unmarshal(msg.Data, &payload)
if err != nil {
2022-11-22 09:19:07 +03:00
//panic(err)
continue
2022-10-25 12:59:22 +03:00
}
2022-11-23 10:44:09 +03:00
if payload.Metadata != nil {
if f, ok := serve.callbackFuncs[payload.Metadata.CallID]; ok {
serve.callbackFuncsMutex.Lock()
delete(serve.callbackFuncs, payload.Metadata.CallID)
serve.callbackFuncsMutex.Unlock()
if payload.ErrNo != 0 {
err = fmt.Errorf(payload.Error)
}
if f != nil {
//go f.F(err, payload.Header, payload.Payload)
f.F(err, payload.Header, payload.Payload)
}
//f(err, payload.Header, payload.Payload)
2022-11-05 12:41:03 +03:00
}
2022-10-25 12:59:22 +03:00
}
2022-10-28 11:19:03 +03:00
2022-10-25 12:59:22 +03:00
}
}
}
2022-10-28 11:19:03 +03:00
// Завершение текущего инстанса сервера
2022-10-23 20:12:23 +03:00
func (serve *Serve) Shutdown() {
for _, value := range serve.funcs {
value.subj.Unsubscribe()
close(value.inputChan)
}
2022-10-26 14:45:40 +03:00
err := serve.subj.Unsubscribe()
if err != nil {
panic(err)
}
2022-10-25 12:59:22 +03:00
close(serve.chanQueueCallback)
if !serve.hasNats {
serve.nc.Close()
}
2022-10-23 20:12:23 +03:00
}
2022-10-28 11:19:03 +03:00
// Регистрация функции в текущем инстансе
2022-10-25 12:59:22 +03:00
func (serve *Serve) RegisterFunction(funcName string, f func(header *network.Header, paylod []byte) (result []byte, err error)) (err error) {
2022-11-22 14:21:25 +03:00
return serve.RegisterFunctionWithTimeout(funcName, time.Second*30, f)
2022-10-25 12:59:22 +03:00
}
2022-10-28 11:19:03 +03:00
// Регистрация функции с установленным таймаутом в текущем инстансе
2022-10-25 12:59:22 +03:00
func (serve *Serve) RegisterFunctionWithTimeout(funcName string, timeout time.Duration, f func(header *network.Header, paylod []byte) (result []byte, err error)) (err error) {
2022-10-23 20:12:23 +03:00
if _, ok := serve.funcs[fmt.Sprintf("%s.%s", serve.moduleName, funcName)]; ok {
2022-10-27 13:43:26 +03:00
return fmt.Errorf(`restrict duplicate function name`)
2022-10-23 20:12:23 +03:00
}
id := make([]byte, 16)
_, err = rand.Read(id)
if err != nil {
return
}
fDesc := funcDesc{
2022-12-01 15:23:42 +03:00
Name: fmt.Sprintf("%s.%s.%s", serve.projectName, serve.moduleName, funcName),
F: f,
logger: serve.logger,
2022-10-23 20:12:23 +03:00
}
fDesc.inputChan = make(chan *nats.Msg)
fDesc.subj, err = serve.nc.ChanQueueSubscribe(fmt.Sprintf("%s.%s.%s", serve.projectName, serve.moduleName, funcName), serve.projectName+"."+serve.moduleName, fDesc.inputChan)
2022-10-25 12:59:22 +03:00
fDesc.conn = serve.nc
fDesc.timeout = timeout
2022-10-25 14:47:26 +03:00
fDesc.acceptFunc = serve.acceptVerify
2022-10-23 20:12:23 +03:00
go fDesc.worker()
2022-10-25 12:59:22 +03:00
serve.funcs[fmt.Sprintf("%s.%s.%s", serve.projectName, serve.moduleName, funcName)] = fDesc
2022-10-23 20:12:23 +03:00
return
}
2022-10-28 11:19:03 +03:00
// Проверка есть ли такая функция в текущем инстансе
2022-10-23 20:12:23 +03:00
func (serve *Serve) HasFunction(name string) bool {
2022-10-25 12:59:22 +03:00
_, ok := serve.funcs[fmt.Sprintf("%s.%s.%s", serve.projectName, serve.moduleName, name)]
2022-10-23 20:12:23 +03:00
return ok
}
2022-10-25 12:59:22 +03:00
2022-10-25 14:47:26 +03:00
// Проверка возможности выполнения функции на основе установленных вызывающих сторон
func (serve *Serve) acceptVerify(accept string) bool {
if _, ok := serve.accept["*"]; ok {
return true
} else {
accept = strings.Trim(accept, " ")
if accept == "" {
return false
}
rules := strings.Split(accept, ".")
for i := range rules {
rules[i] = strings.Trim(rules[i], " ")
}
if len(rules) == 0 {
return false
}
if val, ok := serve.accept[rules[0]]; !ok {
return false
} else {
if _, ok := val["*"]; ok {
return true
} else {
if len(rules) == 1 {
return true
}
if v, ok := val[rules[1]]; !ok {
return false
} else {
return v
}
}
}
}
}
2022-10-28 11:19:03 +03:00
// Асинхронный вызов функции
2022-11-04 17:10:59 +03:00
func (serve *Serve) CallAsync(name string, header *network.Header, payload []byte, f func(err error, header *network.Header, result []byte)) {
2022-10-25 12:59:22 +03:00
metadata := &network.Metadata{
2022-11-30 14:24:24 +03:00
//PackageType: network.Metadata_TYPE_REQUEST,
2022-10-25 12:59:22 +03:00
FuncName: name,
QueueCallback: serve.queueCallback,
CallID: uuid.NewV4().String(),
2022-10-25 14:47:26 +03:00
CallFrom: fmt.Sprintf("%s.%s", serve.projectName, serve.moduleName),
2022-10-25 12:59:22 +03:00
}
2022-10-27 15:19:47 +03:00
if header == nil {
header = &network.Header{}
}
2022-11-30 14:24:24 +03:00
header.CallPrevID = header.CallID
header.CallID = metadata.CallID
2022-10-27 15:19:47 +03:00
header.Project = serve.projectName
2022-10-25 12:59:22 +03:00
pkg := network.Function{
Header: header,
Payload: payload,
Metadata: metadata,
}
buf, err := proto.Marshal(&pkg)
if err != nil {
2022-11-28 10:13:52 +03:00
if f != nil {
f(err, header, payload)
}
return
2022-10-25 12:59:22 +03:00
}
2022-11-05 12:41:03 +03:00
callback := &CallbackFunc{
F: f,
TimeCall: time.Now(),
2022-11-30 14:24:24 +03:00
//Metadata: pkg.Metadata,
2022-11-05 12:41:03 +03:00
}
2022-10-28 14:59:43 +03:00
serve.callbackFuncsMutex.Lock()
defer serve.callbackFuncsMutex.Unlock()
2022-11-05 12:41:03 +03:00
serve.callbackFuncs[metadata.CallID] = callback
//serve.callbackFuncs[metadata.CallID] = f
2022-10-25 12:59:22 +03:00
serve.nc.Publish(name, buf)
}
2022-10-25 13:09:48 +03:00
2022-10-28 11:19:03 +03:00
// Синхронный вызов функции
2022-11-04 17:15:34 +03:00
func (serve *Serve) Call(name string, header *network.Header, payload []byte) (result []byte, head *network.Header, err error) {
2022-10-28 15:01:05 +03:00
chanRes := make(chan *ChanRes)
2022-11-04 17:10:59 +03:00
serve.CallAsync(name, header, payload, func(err error, header *network.Header, result []byte) {
2022-10-28 15:01:05 +03:00
chanRes <- &ChanRes{
2022-11-04 17:10:59 +03:00
res: result,
header: header,
err: err,
2022-10-28 13:46:43 +03:00
}
2022-10-25 13:09:48 +03:00
})
2022-10-28 13:46:43 +03:00
res := <-chanRes
err = res.err
2022-11-04 17:15:34 +03:00
head = res.header
2022-10-28 13:46:43 +03:00
result = res.res
2022-10-25 13:09:48 +03:00
return
}
2022-11-28 10:13:52 +03:00
// Вызов функции как поток без ожидания возврата результата
func (serve *Serve) CallStream(name string, header *network.Header, payload []byte) (err error) {
metadata := &network.Metadata{
2022-11-30 14:24:24 +03:00
//PackageType: network.Metadata_TYPE_STREAM,
2022-11-28 10:13:52 +03:00
FuncName: name,
QueueCallback: serve.queueCallback,
CallID: uuid.NewV4().String(),
CallFrom: fmt.Sprintf("%s.%s", serve.projectName, serve.moduleName),
}
if header == nil {
header = &network.Header{}
}
header.Project = serve.projectName
pkg := network.Function{
Header: header,
Payload: payload,
Metadata: metadata,
}
var buf []byte
buf, err = proto.Marshal(&pkg)
if err != nil {
return
}
serve.nc.Publish(name, buf)
return
}