1.4.5.2
This commit is contained in:
455
EdgeDNS/internal/nodes/dns_node.go
Normal file
455
EdgeDNS/internal/nodes/dns_node.go
Normal file
@@ -0,0 +1,455 @@
|
||||
//go:build plus
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/agents"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeDNS/internal/const"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/dbs"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/events"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/firewalls"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/utils"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DaemonIsOn = false
|
||||
var DaemonPid = 0
|
||||
var nodeTaskNotify = make(chan bool, 8)
|
||||
|
||||
var sharedDomainManager *DomainManager
|
||||
var sharedRecordManager *RecordManager
|
||||
var sharedRouteManager *RouteManager
|
||||
var sharedKeyManager *KeyManager
|
||||
var sharedNodeConfig = &dnsconfigs.NSNodeConfig{}
|
||||
|
||||
func NewDNSNode() *DNSNode {
|
||||
return &DNSNode{
|
||||
sock: gosock.NewTmpSock(teaconst.ProcessName),
|
||||
}
|
||||
}
|
||||
|
||||
type DNSNode struct {
|
||||
sock *gosock.Sock
|
||||
|
||||
RPC *rpc.RPCClient
|
||||
}
|
||||
|
||||
func (this *DNSNode) Start() {
|
||||
// 设置netdns
|
||||
// 这个需要放在所有网络访问的最前面
|
||||
_ = os.Setenv("GODEBUG", "netdns=go")
|
||||
|
||||
// 判断是否在守护进程下
|
||||
_, ok := os.LookupEnv("EdgeDaemon")
|
||||
if ok {
|
||||
remotelogs.Println("DNS_NODE", "start from daemon")
|
||||
DaemonIsOn = true
|
||||
DaemonPid = os.Getppid()
|
||||
}
|
||||
|
||||
// 设置DNS解析库
|
||||
err := os.Setenv("GODEBUG", "netdns=go")
|
||||
if err != nil {
|
||||
remotelogs.Error("DNS_NODE", "[DNS_RESOLVER]set env failed: "+err.Error())
|
||||
}
|
||||
|
||||
// 处理异常
|
||||
this.handlePanic()
|
||||
|
||||
// 监听signal
|
||||
this.listenSignals()
|
||||
|
||||
// 本地Sock
|
||||
err = this.listenSock()
|
||||
if err != nil {
|
||||
logs.Println("[DNS_NODE]" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 启动IP库
|
||||
remotelogs.Println("DNS_NODE", "initializing ip library ...")
|
||||
err = iplibrary.InitPlus()
|
||||
if err != nil {
|
||||
remotelogs.Error("DNS_NODE", "initialize ip library failed: "+err.Error())
|
||||
}
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
|
||||
// 触发事件
|
||||
events.Notify(events.EventStart)
|
||||
|
||||
// 监控状态
|
||||
go NewNodeStatusExecutor().Listen()
|
||||
|
||||
// 连接API
|
||||
go NewAPIStream().Start()
|
||||
|
||||
// 启动
|
||||
go this.start()
|
||||
|
||||
// Hold住进程
|
||||
logs.Println("[DNS_NODE]started")
|
||||
select {}
|
||||
}
|
||||
|
||||
// Daemon 实现守护进程
|
||||
func (this *DNSNode) Daemon() {
|
||||
var isDebug = lists.ContainsString(os.Args, "debug")
|
||||
for {
|
||||
conn, err := this.sock.Dial()
|
||||
if err != nil {
|
||||
if isDebug {
|
||||
log.Println("[DAEMON]starting ...")
|
||||
}
|
||||
|
||||
// 尝试启动
|
||||
err = func() error {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 可以标记当前是从守护进程启动的
|
||||
_ = os.Setenv("EdgeDaemon", "on")
|
||||
_ = os.Setenv("EdgeBackground", "on")
|
||||
|
||||
var cmd = exec.Command(exe)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
if isDebug {
|
||||
log.Println("[DAEMON]", err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
} else {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
} else {
|
||||
_ = conn.Close()
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test 测试配置
|
||||
func (this *DNSNode) Test() error {
|
||||
// 检查是否能连接API
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return fmt.Errorf("test rpc failed: %w", err)
|
||||
}
|
||||
_, err = rpcClient.APINodeRPC.FindCurrentAPINodeVersion(rpcClient.Context(), &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("test rpc failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallSystemService 安装系统服务
|
||||
func (this *DNSNode) InstallSystemService() error {
|
||||
shortName := teaconst.SystemdServiceName
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manager := utils.NewServiceManager(shortName, teaconst.ProductName)
|
||||
err = manager.Install(exe, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 监听一些信号
|
||||
func (this *DNSNode) listenSignals() {
|
||||
var queue = make(chan os.Signal, 8)
|
||||
signal.Notify(queue, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT)
|
||||
goman.New(func() {
|
||||
for range queue {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
utils.Exit()
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 监听本地sock
|
||||
func (this *DNSNode) listenSock() error {
|
||||
// 检查是否在运行
|
||||
if this.sock.IsListening() {
|
||||
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
|
||||
if err == nil {
|
||||
return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid"))
|
||||
} else {
|
||||
return errors.New("error: the process is already running")
|
||||
}
|
||||
}
|
||||
|
||||
// 启动监听
|
||||
go func() {
|
||||
this.sock.OnCommand(func(cmd *gosock.Command) {
|
||||
switch cmd.Code {
|
||||
case "pid":
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Code: "pid",
|
||||
Params: map[string]interface{}{
|
||||
"pid": os.Getpid(),
|
||||
},
|
||||
})
|
||||
case "info":
|
||||
exePath, _ := os.Executable()
|
||||
_ = cmd.Reply(&gosock.Command{
|
||||
Code: "info",
|
||||
Params: map[string]interface{}{
|
||||
"pid": os.Getpid(),
|
||||
"version": teaconst.Version,
|
||||
"path": exePath,
|
||||
},
|
||||
})
|
||||
case "stop":
|
||||
_ = cmd.ReplyOk()
|
||||
|
||||
// 退出主进程
|
||||
events.Notify(events.EventQuit)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
case "gc":
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
_ = cmd.ReplyOk()
|
||||
}
|
||||
})
|
||||
|
||||
err := this.sock.Listen()
|
||||
if err != nil {
|
||||
logs.Println("NODE", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
events.On(events.EventQuit, func() {
|
||||
logs.Println("[DNS_NODE]", "quit unix sock")
|
||||
_ = this.sock.Close()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 启动
|
||||
func (this *DNSNode) start() {
|
||||
client, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
remotelogs.Error("DNS_NODE", err.Error())
|
||||
return
|
||||
}
|
||||
this.RPC = client
|
||||
tryTimes := 0
|
||||
var configJSON []byte
|
||||
for {
|
||||
resp, err := client.NSNodeRPC.FindCurrentNSNodeConfig(client.Context(), &pb.FindCurrentNSNodeConfigRequest{})
|
||||
if err != nil {
|
||||
tryTimes++
|
||||
if tryTimes%10 == 0 {
|
||||
remotelogs.Error("NODE", "read config from API failed: "+err.Error())
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// 不做长时间的无意义的重试
|
||||
if tryTimes > 1000 {
|
||||
remotelogs.Error("NODE", "load failed: unable to read config from API")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
configJSON = resp.NsNodeJSON
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(configJSON) == 0 {
|
||||
remotelogs.Error("NODE", "can not find node config")
|
||||
return
|
||||
}
|
||||
var config = &dnsconfigs.NSNodeConfig{}
|
||||
err = json.Unmarshal(configJSON, config)
|
||||
if err != nil {
|
||||
remotelogs.Error("NODE", "decode config failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
err = config.Init(context.TODO())
|
||||
if err != nil {
|
||||
remotelogs.Error("NODE", "init config failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
sharedNodeConfig = config
|
||||
|
||||
configs.SharedNodeConfig = config
|
||||
events.Notify(events.EventReload)
|
||||
|
||||
sharedNodeConfigManager.reload(config)
|
||||
|
||||
apiConfig, _ := configs.SharedAPIConfig()
|
||||
if apiConfig != nil {
|
||||
apiConfig.NumberId = config.Id
|
||||
}
|
||||
|
||||
var db = dbs.NewDB(Tea.Root + "/data/data-" + types.String(config.Id) + "-" + config.NodeId + "-v0.1.0.db")
|
||||
err = db.Init()
|
||||
if err != nil {
|
||||
remotelogs.Error("NODE", "init database failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
go sharedNodeConfigManager.Start()
|
||||
|
||||
sharedDomainManager = NewDomainManager(db, config.ClusterId)
|
||||
go sharedDomainManager.Start()
|
||||
|
||||
sharedRecordManager = NewRecordManager(db)
|
||||
go sharedRecordManager.Start()
|
||||
|
||||
sharedRouteManager = NewRouteManager(db)
|
||||
go sharedRouteManager.Start()
|
||||
|
||||
sharedKeyManager = NewKeyManager(db)
|
||||
go sharedKeyManager.Start()
|
||||
|
||||
agents.SharedManager = agents.NewManager(db)
|
||||
go agents.SharedManager.Start()
|
||||
|
||||
// 发送通知,这里发送通知需要在DomainManager、RecordeManager等加载完成之后
|
||||
time.Sleep(1 * time.Second)
|
||||
events.Notify(events.EventLoaded)
|
||||
|
||||
// 启动循环
|
||||
go this.loop()
|
||||
}
|
||||
|
||||
// 更新配置Loop
|
||||
func (this *DNSNode) loop() {
|
||||
var ticker = time.NewTicker(60 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-nodeTaskNotify:
|
||||
}
|
||||
|
||||
err := this.processTasks()
|
||||
if err != nil {
|
||||
if rpc.IsConnError(err) {
|
||||
remotelogs.Debug("DNS_NODE", "process tasks: "+err.Error())
|
||||
} else {
|
||||
remotelogs.Error("DNS_NODE", "process tasks: "+err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理任务
|
||||
func (this *DNSNode) processTasks() error {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 所有的任务
|
||||
tasksResp, err := rpcClient.NodeTaskRPC.FindNodeTasks(rpcClient.Context(), &pb.FindNodeTasksRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, task := range tasksResp.NodeTasks {
|
||||
switch task.Type {
|
||||
case "nsConfigChanged":
|
||||
sharedNodeConfigManager.NotifyChange()
|
||||
case "nsDomainChanged":
|
||||
sharedDomainManager.NotifyUpdate()
|
||||
case "nsRecordChanged":
|
||||
sharedRecordManager.NotifyUpdate()
|
||||
case "nsRouteChanged":
|
||||
sharedRouteManager.NotifyUpdate()
|
||||
case "nsKeyChanged":
|
||||
sharedKeyManager.NotifyUpdate()
|
||||
case "nsDDoSProtectionChanged":
|
||||
err := this.updateDDoS(rpcClient)
|
||||
if err != nil {
|
||||
remotelogs.Error("DNS_NODE", "apply DDoS config failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
_, err = rpcClient.NodeTaskRPC.ReportNodeTaskDone(rpcClient.Context(), &pb.ReportNodeTaskDoneRequest{
|
||||
NodeTaskId: task.Id,
|
||||
IsOk: true,
|
||||
Error: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *DNSNode) updateDDoS(rpcClient *rpc.RPCClient) error {
|
||||
resp, err := rpcClient.NSNodeRPC.FindNSNodeDDoSProtection(rpcClient.Context(), &pb.FindNSNodeDDoSProtectionRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.DdosProtectionJSON) == 0 {
|
||||
if sharedNodeConfig != nil {
|
||||
sharedNodeConfig.DDoSProtection = nil
|
||||
}
|
||||
} else {
|
||||
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
||||
err = json.Unmarshal(resp.DdosProtectionJSON, ddosProtectionConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode DDoS protection config failed: %w", err)
|
||||
}
|
||||
|
||||
if sharedNodeConfig != nil {
|
||||
sharedNodeConfig.DDoSProtection = ddosProtectionConfig
|
||||
}
|
||||
|
||||
err = firewalls.SharedDDoSProtectionManager.Apply(ddosProtectionConfig)
|
||||
if err != nil {
|
||||
// 不阻塞
|
||||
remotelogs.Error("NODE", "apply DDoS protection failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user