1.4.5.2
This commit is contained in:
319
EdgeDNS/internal/nodes/manager_domain.go
Normal file
319
EdgeDNS/internal/nodes/manager_domain.go
Normal file
@@ -0,0 +1,319 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/dbs"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/models"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeDNS/internal/rpc"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DomainManager 域名管理器
|
||||
type DomainManager struct {
|
||||
domainMap map[int64]*models.NSDomain // domainId => domain
|
||||
namesMap map[string]map[int64]*models.NSDomain // domain name => { domainId => domain }
|
||||
clusterId int64
|
||||
|
||||
db *dbs.DB
|
||||
version int64
|
||||
locker *sync.RWMutex
|
||||
|
||||
notifier chan bool
|
||||
}
|
||||
|
||||
// NewDomainManager 获取域名管理器对象
|
||||
func NewDomainManager(db *dbs.DB, clusterId int64) *DomainManager {
|
||||
return &DomainManager{
|
||||
db: db,
|
||||
domainMap: map[int64]*models.NSDomain{},
|
||||
namesMap: map[string]map[int64]*models.NSDomain{},
|
||||
clusterId: clusterId,
|
||||
notifier: make(chan bool, 8),
|
||||
locker: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// Start 启动自动任务
|
||||
func (this *DomainManager) Start() {
|
||||
remotelogs.Println("DOMAIN_MANAGER", "starting ...")
|
||||
|
||||
// 从本地数据库中加载数据
|
||||
err := this.Load()
|
||||
if err != nil {
|
||||
if rpc.IsConnError(err) {
|
||||
remotelogs.Debug("DOMAIN_MANAGER", "load failed: "+err.Error())
|
||||
} else {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "load failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化运行
|
||||
err = this.LoopAll()
|
||||
if err != nil {
|
||||
if rpc.IsConnError(err) {
|
||||
remotelogs.Debug("DOMAIN_MANAGER", "loop failed: "+err.Error())
|
||||
} else {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "loop failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// 更新
|
||||
var ticker = time.NewTicker(20 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-this.notifier:
|
||||
}
|
||||
|
||||
err = this.LoopAll()
|
||||
if err != nil {
|
||||
if rpc.IsConnError(err) {
|
||||
remotelogs.Debug("DOMAIN_MANAGER", "loop failed: "+err.Error())
|
||||
} else {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "loop failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DomainManager) LoopAll() error {
|
||||
for {
|
||||
hasNext, err := this.Loop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load 从数据库中加载数据
|
||||
func (this *DomainManager) Load() error {
|
||||
var offset = 0
|
||||
var size = 10000
|
||||
for {
|
||||
domains, err := this.db.ListDomains(this.clusterId, offset, size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(domains) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
this.locker.Lock()
|
||||
for _, domain := range domains {
|
||||
this.domainMap[domain.Id] = domain
|
||||
|
||||
nameMap, ok := this.namesMap[domain.Name]
|
||||
if ok {
|
||||
nameMap[domain.Id] = domain
|
||||
} else {
|
||||
this.namesMap[domain.Name] = map[int64]*models.NSDomain{
|
||||
domain.Id: domain,
|
||||
}
|
||||
}
|
||||
|
||||
if domain.Version > this.version {
|
||||
this.version = domain.Version
|
||||
}
|
||||
}
|
||||
this.locker.Unlock()
|
||||
|
||||
offset += size
|
||||
}
|
||||
|
||||
if this.version > 0 {
|
||||
this.version++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loop 单次循环任务
|
||||
func (this *DomainManager) Loop() (hasNext bool, err error) {
|
||||
client, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
resp, err := client.NSDomainRPC.ListNSDomainsAfterVersion(client.Context(), &pb.ListNSDomainsAfterVersionRequest{
|
||||
Version: this.version,
|
||||
Size: 20000,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var domains = resp.NsDomains
|
||||
if len(domains) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
for _, domain := range domains {
|
||||
this.processDomain(domain)
|
||||
if domain.Version > this.version {
|
||||
this.version = domain.Version
|
||||
}
|
||||
}
|
||||
this.version++
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// FindDomain 根据名称查找域名
|
||||
func (this *DomainManager) FindDomain(name string) (domain *models.NSDomain, ok bool) {
|
||||
this.locker.RLock()
|
||||
defer this.locker.RUnlock()
|
||||
|
||||
nameMap, ok := this.namesMap[name]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
for _, domain2 := range nameMap {
|
||||
return domain2, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindDomainWithId 根据域名ID查询域名
|
||||
func (this *DomainManager) FindDomainWithId(domainId int64) (domain *models.NSDomain) {
|
||||
this.locker.RLock()
|
||||
defer this.locker.RUnlock()
|
||||
|
||||
return this.domainMap[domainId]
|
||||
}
|
||||
|
||||
// NotifyUpdate 通知更新
|
||||
func (this *DomainManager) NotifyUpdate() {
|
||||
select {
|
||||
case this.notifier <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// SplitDomain 分解域名
|
||||
func (this *DomainManager) SplitDomain(fullDomainName string) (rootDomain *models.NSDomain, recordName string) {
|
||||
if len(fullDomainName) == 0 {
|
||||
return
|
||||
}
|
||||
fullDomainName = strings.TrimSuffix(fullDomainName, ".") // 去除尾部的点(.)
|
||||
fullDomainName = strings.ToLower(fullDomainName) // 转换为小写
|
||||
var domainName = fullDomainName
|
||||
var domain, ok = this.FindDomain(domainName)
|
||||
if !ok {
|
||||
for {
|
||||
var index = strings.Index(domainName, ".")
|
||||
if index < 0 {
|
||||
break
|
||||
}
|
||||
domainName = domainName[index+1:]
|
||||
domain, ok = this.FindDomain(domainName)
|
||||
if ok {
|
||||
recordName = fullDomainName[:len(fullDomainName)-len(domainName)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return domain, recordName
|
||||
}
|
||||
|
||||
// 处理域名
|
||||
func (this *DomainManager) processDomain(domain *pb.NSDomain) {
|
||||
if !domain.IsOn || domain.IsDeleted || domain.Status != dnsconfigs.NSDomainStatusVerified {
|
||||
this.locker.Lock()
|
||||
delete(this.domainMap, domain.Id)
|
||||
|
||||
nameMap, ok := this.namesMap[domain.Name]
|
||||
if ok {
|
||||
delete(nameMap, domain.Id)
|
||||
if len(nameMap) == 0 {
|
||||
delete(this.namesMap, domain.Name)
|
||||
}
|
||||
}
|
||||
|
||||
this.locker.Unlock()
|
||||
|
||||
// 从数据库中删除
|
||||
if this.db != nil {
|
||||
err := this.db.DeleteDomain(domain.Id)
|
||||
if err != nil {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "delete domain from db failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 存入数据库
|
||||
if this.db != nil {
|
||||
exists, err := this.db.ExistsDomain(domain.Id)
|
||||
if err != nil {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "query failed: "+err.Error())
|
||||
} else {
|
||||
if exists {
|
||||
err = this.db.UpdateDomain(domain.Id, domain.NsCluster.Id, domain.UserId, domain.Name, domain.TsigJSON, domain.Version)
|
||||
if err != nil {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "update failed: "+err.Error())
|
||||
}
|
||||
} else {
|
||||
err = this.db.InsertDomain(domain.Id, domain.NsCluster.Id, domain.UserId, domain.Name, domain.TsigJSON, domain.Version)
|
||||
if err != nil {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "insert failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 同集群的才需要加载
|
||||
if this.clusterId == domain.NsCluster.Id {
|
||||
this.locker.Lock()
|
||||
var tsigConfig = &dnsconfigs.NSTSIGConfig{}
|
||||
if len(domain.TsigJSON) > 0 {
|
||||
err := json.Unmarshal(domain.TsigJSON, tsigConfig)
|
||||
if err != nil {
|
||||
remotelogs.Error("DOMAIN_MANAGER", "decode TSIG json failed: "+err.Error()+", domain: "+domain.Name+", domainId: "+types.String(domain.Id)+", JSON: "+string(domain.TsigJSON))
|
||||
}
|
||||
}
|
||||
|
||||
var nsDomain = &models.NSDomain{
|
||||
Id: domain.Id,
|
||||
ClusterId: domain.NsCluster.Id,
|
||||
UserId: domain.UserId,
|
||||
Name: domain.Name,
|
||||
TSIG: tsigConfig,
|
||||
Version: domain.Version,
|
||||
}
|
||||
this.domainMap[domain.Id] = nsDomain
|
||||
|
||||
nameMap, ok := this.namesMap[domain.Name]
|
||||
if ok {
|
||||
nameMap[nsDomain.Id] = nsDomain
|
||||
} else {
|
||||
this.namesMap[domain.Name] = map[int64]*models.NSDomain{
|
||||
nsDomain.Id: nsDomain,
|
||||
}
|
||||
}
|
||||
this.locker.Unlock()
|
||||
} else {
|
||||
// 不同集群的删除域名
|
||||
this.locker.Lock()
|
||||
delete(this.domainMap, domain.Id)
|
||||
|
||||
nameMap, ok := this.namesMap[domain.Name]
|
||||
if ok {
|
||||
delete(nameMap, domain.Id)
|
||||
if len(nameMap) == 0 {
|
||||
delete(this.namesMap, domain.Name)
|
||||
}
|
||||
}
|
||||
|
||||
this.locker.Unlock()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user