Skip to content

Commit

Permalink
feat(strategy): 增加域名和 HTTP 策略的测试用例并优化相关逻辑
Browse files Browse the repository at this point in the history
- 为域名和 HTTP 策略添加测试用例,提高代码覆盖率
- 优化域名策略中的证书和端口检测逻辑
- 修正 HTTP 策略中的状态码和响应时间判断条件- 更新数据源相关结构,增加额外信息字段
  • Loading branch information
aide-cloud committed Dec 27, 2024
1 parent fa3a1d5 commit 5bda63e
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 22 deletions.
2 changes: 2 additions & 0 deletions api/strategy.proto
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ message DomainStrategyItem {
expression: 'this.size() > 0'
message: "域名不允许为空"
}];
// 判断条件
Condition condition = 10;
// 策略名称
string alert = 20 [(buf.validate.field).cel = {
id: "Strategy_alert"
Expand Down
10 changes: 6 additions & 4 deletions cmd/server/houyi/internal/biz/bo/strategy_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type StrategyDomain struct {
Port uint32 `json:"port,omitempty"`
// 类型
StrategyType vobj.StrategyType `json:"strategyType,omitempty"`
// 判断条件
Condition vobj.Condition `json:"condition,omitempty"`
}

// String 策略转字符串
Expand Down Expand Up @@ -73,12 +75,12 @@ func (s *StrategyDomain) IsCompletelyMeet(values []*datasource.Value) (map[strin
}
for _, point := range values {
// 域名证书检测、小于等于阈值都是满足条件的
if s.StrategyType.IsDomainCertificate() && point.Value <= s.Threshold {
return nil, true
if s.StrategyType.IsDomainCertificate() && s.Condition.Judge(s.Threshold, point.Value) {
return point.Ext, true
}
// 端口检测、等于阈值才是满足条件的 1开启, 0关闭
if s.StrategyType.IsDomainPort() && point.Value == s.Threshold {
return nil, true
if s.StrategyType.IsDomainPort() && s.Condition.Judge(s.Threshold, point.Value) {
return point.Ext, true
}
}
return nil, false
Expand Down
78 changes: 78 additions & 0 deletions cmd/server/houyi/internal/biz/bo/strategy_domain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package bo_test

import (
"context"
"testing"

"github.com/aide-family/moon/cmd/server/houyi/internal/biz/bo"
"github.com/aide-family/moon/pkg/util/types"
"github.com/aide-family/moon/pkg/vobj"
)

func TestStrategyDomain_Eval(t *testing.T) {
domainStrategy := &bo.StrategyDomain{
ReceiverGroupIDs: nil,
LabelNotices: nil,
ID: 1,
LevelID: 1,
TeamID: 1,
Status: vobj.StatusEnable,
Alert: "百度域名证书监控",
Threshold: 1,
Labels: vobj.NewLabels(map[string]string{"domain": "baidu.com"}),
Annotations: vobj.NewAnnotations(map[string]string{
"summary": "百度域名证书监控",
"description": "百度域名证书监控 明细 {{ . }}",
}),
Domain: "baidu.com",
Port: 443,
StrategyType: vobj.StrategyTypeDomainCertificate,
Condition: vobj.ConditionGTE,
}

eval, err := domainStrategy.Eval(context.Background())
if err != nil {
t.Error(err)
return
}

for indexer, point := range eval {
bs, _ := types.Marshal(point.Values)
t.Logf("indexer: %s, point: %+v, meet: ", indexer, string(bs))
t.Log(domainStrategy.IsCompletelyMeet(point.Values))
}
}

func TestStrategyDomainPort_Eval(t *testing.T) {
domainStrategy := &bo.StrategyDomain{
ReceiverGroupIDs: nil,
LabelNotices: nil,
ID: 1,
LevelID: 1,
TeamID: 1,
Status: vobj.StatusEnable,
Alert: "百度域名端口监控",
Threshold: 1,
Labels: vobj.NewLabels(map[string]string{"domain": "baidu.com"}),
Annotations: vobj.NewAnnotations(map[string]string{
"summary": "百度域名端口监控",
"description": "百度域名端口监控 明细 {{ . }}",
}),
Domain: "baidu.com",
Port: 1443,
StrategyType: vobj.StrategyTypeDomainPort,
Condition: vobj.ConditionGTE,
}

eval, err := domainStrategy.Eval(context.Background())
if err != nil {
t.Error(err)
return
}

for indexer, point := range eval {
bs, _ := types.Marshal(point.Values)
t.Logf("indexer: %s, point: %+v, meet: ", indexer, string(bs))
t.Log(domainStrategy.IsCompletelyMeet(point.Values))
}
}
8 changes: 7 additions & 1 deletion cmd/server/houyi/internal/biz/bo/strategy_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,13 @@ func (e *StrategyHTTP) IsCompletelyMeet(values []*datasource.Value) (map[string]
}

codeMatch := types.MatchStatusCodes(e.StatusCode, int(code))
responseTimeMatch := e.ResponseTimeCondition.Judge(duration, e.ResponseTime)
responseTimeMatch := e.ResponseTimeCondition.Judge(e.ResponseTime, duration)

if e.StatusCodeCondition.IsEQ() && codeMatch {
codeMatch = true
}
if e.StatusCodeCondition.IsNE() && !codeMatch {
codeMatch = true
}
return extJSON, codeMatch && responseTimeMatch
}
47 changes: 47 additions & 0 deletions cmd/server/houyi/internal/biz/bo/strategy_http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bo_test

import (
"context"
"testing"

"github.com/aide-family/moon/cmd/server/houyi/internal/biz/bo"
"github.com/aide-family/moon/pkg/util/types"
"github.com/aide-family/moon/pkg/vobj"
)

func TestStrategyHTTP_Eval(t *testing.T) {
httpStrategy := &bo.StrategyHTTP{
StrategyType: vobj.StrategyTypeHTTP,
URL: "https://baidu.com",
StatusCode: "5xx",
StatusCodeCondition: vobj.ConditionGT,
Headers: map[string]string{"Content-Type": "application/json"},
Body: "",
Method: vobj.HTTPMethodGet,
ResponseTime: 1,
ResponseTimeCondition: vobj.ConditionGTE,
Labels: vobj.NewLabels(map[string]string{"http": "baidu.com"}),
Annotations: vobj.NewAnnotations(map[string]string{
"summary": "baidu.com http 探测",
"description": "baidu.com http 探测, 明细 {{ . }}",
}),
ReceiverGroupIDs: nil,
LabelNotices: nil,
TeamID: 1,
Status: vobj.StatusEnable,
Alert: "baidu http 探测",
LevelID: 1,
ID: 1,
}
eval, err := httpStrategy.Eval(context.Background())
if err != nil {
t.Error(err)
return
}

for indexer, point := range eval {
bs, _ := types.Marshal(point.Values)
t.Logf("indexer: %s, point: %+v, meet: ", indexer, string(bs))
t.Log(httpStrategy.IsCompletelyMeet(point.Values))
}
}
4 changes: 3 additions & 1 deletion cmd/server/palace/internal/service/builder/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,9 @@ func (d *doStrategyLevelsBuilder) ToDomainAPI(strategy *bizmodel.Strategy, level
Annotations: strategy.Annotations.Map(),
Threshold: level.Threshold,
Domain: strategy.Expr,
Condition: api.Condition(level.Condition),
Alert: strategy.Name,
Port: 0,
Port: 443,
StrategyType: api.StrategyType(strategy.StrategyType),
}
}
Expand Down Expand Up @@ -569,6 +570,7 @@ func (d *doStrategyLevelsBuilder) ToPortAPI(strategy *bizmodel.Strategy, level *
Annotations: strategy.Annotations.Map(),
Threshold: level.Threshold,
Domain: strategy.Expr,
Condition: api.Condition(vobj.ConditionEQ),
Alert: strategy.Name,
Port: level.Port,
StrategyType: api.StrategyType(strategy.StrategyType),
Expand Down
5 changes: 3 additions & 2 deletions pkg/houyi/datasource/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import (
type (
// Value 数据源查询值
Value struct {
Value float64 `json:"value"`
Timestamp int64 `json:"timestamp"`
Value float64 `json:"value"`
Timestamp int64 `json:"timestamp"`
Ext map[string]any `json:"ext"`
}

// Point 数据点
Expand Down
20 changes: 13 additions & 7 deletions pkg/houyi/datasource/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ import (
func DomainEval(_ context.Context, domain string, port uint32, timeout time.Duration) (map[watch.Indexer]*Point, error) {
now := time.Now()
points := make(map[watch.Indexer]*Point)

dsn := domain
if port != 0 {
dsn = domain + ":" + strconv.FormatUint(uint64(port), 10)
}
// 创建 TCP 连接
conn, err := net.DialTimeout("tcp", domain+":"+strconv.FormatUint(uint64(port), 10), timeout)
conn, err := net.DialTimeout("tcp", dsn, timeout)
if err != nil {
// 超时或者连接失败,返回空切片和错误信息
labels := vobj.NewLabels(map[string]string{vobj.Domain: domain, vobj.DomainPort: strconv.FormatUint(uint64(port), 10)})
Expand Down Expand Up @@ -47,26 +52,27 @@ func DomainEval(_ context.Context, domain string, port uint32, timeout time.Dura
defer tlsConn.Close()

// 创建一个 TLS 的握手
err = tlsConn.Handshake()
if err != nil {
if err = tlsConn.Handshake(); err != nil {
return nil, err
}

// 获取证书信息,返回的是一个切片
certs := tlsConn.ConnectionState().PeerCertificates
for _, cert := range certs {
labels := vobj.NewLabels(map[string]string{
vobj.Domain: domain,
vobj.DomainPort: strconv.FormatUint(uint64(port), 10),
vobj.DomainSubject: cert.Subject.CommonName,
vobj.DomainExpiresOn: cert.NotAfter.Format("2006-01-02 15:04:05"),
vobj.Domain: domain,
vobj.DomainPort: strconv.FormatUint(uint64(port), 10),
})
points[labels] = &Point{
Labels: labels.Map(),
Values: []*Value{
{
Value: float64(int(cert.NotAfter.Sub(now).Hours() / 24)),
Timestamp: now.Unix(),
Ext: map[string]any{
vobj.DomainSubject: cert.Subject.CommonName,
vobj.DomainExpiresOn: cert.NotAfter.Format("2006-01-02 15:04:05"),
},
},
},
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/houyi/datasource/endpoint_port.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func EndpointPortEval(_ context.Context, endpoint string, port uint32, timeout t
Labels: labels.Map(),
Values: []*Value{
{
Value: 1,
Value: 0,
Timestamp: now.Unix(),
},
},
Expand All @@ -37,7 +37,7 @@ func EndpointPortEval(_ context.Context, endpoint string, port uint32, timeout t
Labels: labels.Map(),
Values: []*Value{
{
Value: 0,
Value: 1,
Timestamp: now.Unix(),
},
},
Expand Down
10 changes: 5 additions & 5 deletions pkg/vobj/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ const (
)

// Judge 判断是否符合条件
func (c Condition) Judge(value, threshold float64) bool {
func (c Condition) Judge(threshold, value float64) bool {
switch c {
case ConditionEQ:
return threshold == value
case ConditionNE:
return threshold != value
case ConditionGT:
return threshold > value
return threshold < value
case ConditionGTE:
return threshold >= value
return threshold <= value
case ConditionLT:
return threshold < value
return threshold > value
case ConditionLTE:
return threshold <= value
return threshold >= value
default:
return false
}
Expand Down

0 comments on commit 5bda63e

Please sign in to comment.