Skip to content

Commit 34ccd2e

Browse files
movebeanrockyang
and
rockyang
authored
[ISSUE polarismesh#1003] 查询接口支持模糊查询 (polarismesh#1004)
* query instance/namespaces/services * fix golang lint * query instance/namespaces/services * fix golang lint * merge main * query instance/namespaces/services * fix golang lint * merge main * fix testcase * modify error code description * fix testcase * fix testcase * fix testcase * fix testcase * modify * modify * fix lint * fix testcase * modify * modify * fix lint --------- Co-authored-by: rockyang <[email protected]>
1 parent 4a1b67d commit 34ccd2e

28 files changed

+580
-111
lines changed

cache/routing_config_query.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ func (rc *routingConfigCache) QueryRoutingConfigsV2(args *RoutingArgs) (uint32,
181181
}
182182

183183
if args.Name != "" {
184-
name, fuzzy := utils.ParseWildName(args.Name)
185-
if fuzzy {
184+
name, isWild := utils.ParseWildName(args.Name)
185+
if isWild {
186186
if !strings.Contains(routeRule.Name, name) {
187187
return
188188
}

cache/service_query.go

+50-18
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sync"
2424

2525
"github.com/polarismesh/polaris/common/model"
26+
"github.com/polarismesh/polaris/common/utils"
2627
"github.com/polarismesh/polaris/store"
2728
)
2829

@@ -34,10 +35,12 @@ type ServiceArgs struct {
3435
Metadata map[string]string
3536
// SvcIds 是否按照服务的ID进行等值查询
3637
SvcIds map[string]struct{}
37-
// FuzzyName 是否进行名字的模糊匹配
38-
FuzzyName bool
39-
// FuzzyBusiness 是否进行业务的模糊匹配
40-
FuzzyBusiness bool
38+
// WildName 是否进行名字的模糊匹配
39+
WildName bool
40+
// WildBusiness 是否进行业务的模糊匹配
41+
WildBusiness bool
42+
// WildNamespace 是否进行命名空间的模糊匹配
43+
WildNamespace bool
4144
// Namespace 条件中的命名空间
4245
Namespace string
4346
// Name 条件中的服务名
@@ -68,7 +71,7 @@ func (sc *serviceCache) GetServicesByFilter(serviceFilters *ServiceArgs,
6871
var services []*model.Service
6972

7073
// 如果具有名字条件,并且不是模糊查询,直接获取对应命名空间下面的服务,并检查是否匹配所有条件
71-
if serviceFilters.Name != "" && !serviceFilters.FuzzyName {
74+
if serviceFilters.Name != "" && !serviceFilters.WildName && !serviceFilters.WildNamespace {
7275
amount, services, err = sc.getServicesFromCacheByName(serviceFilters, instanceFilters, offset, limit)
7376
} else {
7477
amount, services, err = sc.getServicesByIteratingCache(serviceFilters, instanceFilters, offset, limit)
@@ -90,7 +93,8 @@ func (sc *serviceCache) GetServicesByFilter(serviceFilters *ServiceArgs,
9093
}
9194

9295
func hasInstanceFilter(instanceFilters *store.InstanceArgs) bool {
93-
if instanceFilters == nil || (len(instanceFilters.Hosts) == 0 && len(instanceFilters.Ports) == 0) {
96+
if instanceFilters == nil || (len(instanceFilters.Hosts) == 0 && len(instanceFilters.Ports) == 0 &&
97+
len(instanceFilters.Meta) == 0) {
9498
return false
9599
}
96100
return true
@@ -116,6 +120,27 @@ func (sc *serviceCache) matchInstances(instances []*model.Instance, instanceFilt
116120
matchedHost = true
117121
}
118122

123+
matchedMeta := false
124+
if len(instanceFilters.Meta) > 0 {
125+
for _, instance := range instances {
126+
instanceMetaMap := instance.Metadata()
127+
instanceMatched := true
128+
for key, metaPattern := range instanceFilters.Meta {
129+
if instanceMetaValue, ok := instanceMetaMap[key]; !ok ||
130+
!utils.IsWildMatch(instanceMetaValue, metaPattern) {
131+
instanceMatched = false
132+
break
133+
}
134+
}
135+
if instanceMatched {
136+
matchedMeta = true
137+
break
138+
}
139+
}
140+
} else {
141+
matchedMeta = true
142+
}
143+
119144
var matchedPort bool
120145
if len(instanceFilters.Ports) > 0 {
121146
var ports = make(map[uint32]bool, len(instanceFilters.Ports))
@@ -131,7 +156,7 @@ func (sc *serviceCache) matchInstances(instances []*model.Instance, instanceFilt
131156
} else {
132157
matchedPort = true
133158
}
134-
return matchedHost && matchedPort
159+
return matchedHost && matchedPort && matchedMeta
135160
}
136161

137162
// GetAllNamespaces 返回所有的命名空间
@@ -150,14 +175,14 @@ func (sc *serviceCache) getServicesFromCacheByName(svcArgs *ServiceArgs, instArg
150175
var res []*model.Service
151176
if svcArgs.Namespace != "" {
152177
svc := sc.GetServiceByName(svcArgs.Name, svcArgs.Namespace)
153-
if svc != nil && !svc.IsAlias() && matchService(svc, svcArgs.Filter, svcArgs.Metadata, false) &&
178+
if svc != nil && !svc.IsAlias() && matchService(svc, svcArgs.Filter, svcArgs.Metadata, false, false) &&
154179
sc.matchInstance(svc, instArgs) {
155180
res = append(res, svc)
156181
}
157182
} else {
158183
for _, namespace := range sc.GetAllNamespaces() {
159184
svc := sc.GetServiceByName(svcArgs.Name, namespace)
160-
if svc != nil && !svc.IsAlias() && matchService(svc, svcArgs.Filter, svcArgs.Metadata, false) &&
185+
if svc != nil && !svc.IsAlias() && matchService(svc, svcArgs.Filter, svcArgs.Metadata, false, false) &&
161186
sc.matchInstance(svc, instArgs) {
162187
res = append(res, svc)
163188
}
@@ -196,26 +221,33 @@ func sortBeforeTrim(services []*model.Service, offset, limit uint32) (uint32, []
196221
}
197222

198223
// matchService 根据查询条件比较一个服务是否符合条件
199-
func matchService(svc *model.Service, svcFilter map[string]string, metaFilter map[string]string, matchName bool) bool {
200-
if !matchServiceFilter(svc, svcFilter, matchName) {
224+
func matchService(svc *model.Service, svcFilter map[string]string, metaFilter map[string]string,
225+
isWildName, isWildNamespace bool) bool {
226+
if !matchServiceFilter(svc, svcFilter, isWildName, isWildNamespace) {
201227
return false
202228
}
203229
return matchMetadata(svc, metaFilter)
204230
}
205231

206232
// matchServiceFilter 查询一个服务是否满足服务相关字段的条件
207-
func matchServiceFilter(svc *model.Service, svcFilter map[string]string, matchName bool) bool {
233+
func matchServiceFilter(svc *model.Service, svcFilter map[string]string, isWildName, isWildNamespace bool) bool {
208234
var value string
209235
var exist bool
210-
if matchName {
211-
// 走到这一步,一定是模糊匹配
236+
if isWildName {
212237
if value, exist = svcFilter["name"]; exist {
213-
searchVal := value[0 : len(value)-1]
214-
if !strings.Contains(strings.ToLower(svc.Name), strings.ToLower(searchVal)) {
238+
if !utils.IsWildMatchIgnoreCase(svc.Name, value) {
239+
return false
240+
}
241+
}
242+
}
243+
if isWildNamespace {
244+
if value, exist = svcFilter["namespace"]; exist {
245+
if !utils.IsWildMatchIgnoreCase(svc.Namespace, value) {
215246
return false
216247
}
217248
}
218249
}
250+
219251
if value, exist = svcFilter["business"]; exist &&
220252
!strings.Contains(strings.ToLower(svc.Business), strings.ToLower(value)) {
221253
return false
@@ -272,7 +304,7 @@ func (sc *serviceCache) getServicesByIteratingCache(
272304
return
273305
}
274306
if !svcArgs.EmptyCondition {
275-
if !matchService(svc, svcArgs.Filter, svcArgs.Metadata, true) {
307+
if !matchService(svc, svcArgs.Filter, svcArgs.Metadata, svcArgs.WildName, svcArgs.WildNamespace) {
276308
return
277309
}
278310
}
@@ -281,7 +313,7 @@ func (sc *serviceCache) getServicesByIteratingCache(
281313
}
282314
res = append(res, svc)
283315
}
284-
if len(svcArgs.Namespace) > 0 {
316+
if len(svcArgs.Namespace) > 0 && !svcArgs.WildNamespace {
285317
// 从命名空间来找
286318
spaces, ok := sc.names.Load(svcArgs.Namespace)
287319
if !ok {

cache/service_query_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func Test_matchServiceFilter_ignoreServiceCI(t *testing.T) {
9393
}
9494
for _, tt := range tests {
9595
t.Run(tt.name, func(t *testing.T) {
96-
if got := matchServiceFilter(tt.args.svc, tt.args.svcFilter, tt.args.matchName); got != tt.want {
96+
if got := matchServiceFilter(tt.args.svc, tt.args.svcFilter, tt.args.matchName, false); got != tt.want {
9797
t.Errorf("matchServiceFilter() = %v, want %v", got, tt.want)
9898
}
9999
})
@@ -170,7 +170,7 @@ func Test_matchServiceFilter_business(t *testing.T) {
170170
}
171171
for _, tt := range tests {
172172
t.Run(tt.name, func(t *testing.T) {
173-
if got := matchServiceFilter(tt.args.svc, tt.args.svcFilter, tt.args.matchName); got != tt.want {
173+
if got := matchServiceFilter(tt.args.svc, tt.args.svcFilter, tt.args.matchName, false); got != tt.want {
174174
t.Errorf("matchServiceFilter() = %v, want %v, args %#v", got, tt.want, tt.args)
175175
}
176176
})

common/api/v1/codeinfo.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ var code2info = map[uint32]string{
215215
InvalidUserToken: "invalid user token",
216216
InvalidParameter: "invalid parameter",
217217
EmptyQueryParameter: "query instance parameter is empty",
218-
InvalidQueryInsParameter: "query instance, (service,namespace) or host is required",
218+
InvalidQueryInsParameter: "query instance, service or namespace or host is required",
219219
InvalidNamespaceName: "invalid namespace name",
220220
InvalidNamespaceOwners: "invalid namespace owners",
221221
InvalidNamespaceToken: "invalid namespace token",

common/api/v1/response.go

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ func AddNamespace(b *apiservice.BatchQueryResponse, namespace *apimodel.Namespac
6565
b.Namespaces = append(b.Namespaces, namespace)
6666
}
6767

68+
// AddNamespaceSummary 添加汇总信息
69+
func AddNamespaceSummary(b *apiservice.BatchQueryResponse, summary *apimodel.Summary) {
70+
b.Summary = summary
71+
}
72+
6873
// NewResponse 创建回复
6974
func NewResponse(code apimodel.Code) *apiservice.Response {
7075
return &apiservice.Response{

common/utils/funcs.go

+52-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package utils
1919

2020
import (
2121
"encoding/hex"
22+
"strings"
2223

2324
"github.com/google/uuid"
2425
)
@@ -48,12 +49,34 @@ func CollectMapKeys(filters map[string]string) []string {
4849
return fields
4950
}
5051

51-
// IsWildName 判断名字是否为通配名字,只支持前缀索引(名字最后为*)
52-
func IsWildName(name string) bool {
52+
// IsPrefixWildName 判断名字是否为通配名字,只支持前缀索引(名字最后为*)
53+
func IsPrefixWildName(name string) bool {
5354
length := len(name)
5455
return length >= 1 && name[length-1:length] == "*"
5556
}
5657

58+
// IsWildName 判断名字是否为通配名字,前缀或者后缀
59+
func IsWildName(name string) bool {
60+
return IsPrefixWildName(name) || IsSuffixWildName(name)
61+
}
62+
63+
// ParseWildNameForSql 如果 name 是通配字符串,将通配字符*替换为sql中的%
64+
func ParseWildNameForSql(name string) string {
65+
if IsPrefixWildName(name) {
66+
name = name[:len(name)-1] + "%"
67+
}
68+
if IsSuffixWildName(name) {
69+
name = "%" + name[1:]
70+
}
71+
return name
72+
}
73+
74+
// IsSuffixWildName 判断名字是否为通配名字,只支持后缀索引(名字第一个字符为*)
75+
func IsSuffixWildName(name string) bool {
76+
length := len(name)
77+
return length >= 1 && name[0:1] == "*"
78+
}
79+
5780
// ParseWildName 判断是否为格式化查询条件并且返回真正的查询信息
5881
func ParseWildName(name string) (string, bool) {
5982
length := len(name)
@@ -66,6 +89,33 @@ func ParseWildName(name string) (string, bool) {
6689
return name, false
6790
}
6891

92+
// IsWildMatchIgnoreCase 判断 name 是否匹配 pattern,pattern 可以是前缀或者后缀,忽略大小写
93+
func IsWildMatchIgnoreCase(name, pattern string) bool {
94+
return IsWildMatch(strings.ToLower(name), strings.ToLower(pattern))
95+
}
96+
97+
// IsWildMatch 判断 name 是否匹配 pattern,pattern 可以是前缀或者后缀
98+
func IsWildMatch(name, pattern string) bool {
99+
if IsPrefixWildName(pattern) {
100+
pattern = strings.TrimRight(pattern, "*")
101+
if strings.HasPrefix(name, pattern) {
102+
return true
103+
}
104+
if IsSuffixWildName(pattern) {
105+
pattern = strings.TrimLeft(pattern, "*")
106+
return strings.Contains(name, pattern)
107+
}
108+
return false
109+
} else if IsSuffixWildName(pattern) {
110+
pattern = strings.TrimLeft(pattern, "*")
111+
if strings.HasSuffix(name, pattern) {
112+
return true
113+
}
114+
return false
115+
}
116+
return pattern == name
117+
}
118+
69119
// NewUUID 返回一个随机的UUID
70120
func NewUUID() string {
71121
uuidBytes := uuid.New()

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ require (
8484

8585
require (
8686
github.com/DATA-DOG/go-sqlmock v1.5.0
87-
github.com/polarismesh/specification v1.2.0
87+
github.com/polarismesh/specification v1.2.1-alpha
8888
github.com/robfig/cron/v3 v3.0.1
8989
)
9090

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
321321
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
322322
github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219 h1:XnFyNUWnciM6zgXaz6tm+Egs35rhoD0KGMmKh4gCdi0=
323323
github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219/go.mod h1:4WhwBysTom9Eoy0hQ4W69I0FmO+T0EpjEW9/5sgHoUk=
324-
github.com/polarismesh/specification v1.2.0 h1:/g2sEY5d69MfwhxlJNRgB9G5UXzQxxrSaIh2IOLazcw=
325-
github.com/polarismesh/specification v1.2.0/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU=
324+
github.com/polarismesh/specification v1.2.1-alpha h1:RnEF5z5VZ3x7SjbjGSL7VeSlc6L9uGZDVGMhj+inFbs=
325+
github.com/polarismesh/specification v1.2.1-alpha/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU=
326326
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
327327
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
328328
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=

namespace/namespace.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ func (s *Server) GetNamespaces(ctx context.Context, query map[string][]string) *
351351
out := api.NewBatchQueryResponse(apimodel.Code_ExecuteSuccess)
352352
out.Amount = utils.NewUInt32Value(amount)
353353
out.Size = utils.NewUInt32Value(uint32(len(namespaces)))
354+
var totalServiceCount, totalInstanceCount, totalHealthInstanceCount uint32
354355
for _, namespace := range namespaces {
355356
nsCntInfo := s.caches.Service().GetNamespaceCntInfo(namespace.Name)
356357
api.AddNamespace(out, &apimodel.Namespace{
@@ -364,7 +365,15 @@ func (s *Server) GetNamespaces(ctx context.Context, query map[string][]string) *
364365
TotalInstanceCount: utils.NewUInt32Value(nsCntInfo.InstanceCnt.TotalInstanceCount),
365366
TotalHealthInstanceCount: utils.NewUInt32Value(nsCntInfo.InstanceCnt.HealthyInstanceCount),
366367
})
367-
}
368+
totalServiceCount += nsCntInfo.ServiceCount
369+
totalInstanceCount += nsCntInfo.InstanceCnt.TotalInstanceCount
370+
totalHealthInstanceCount += nsCntInfo.InstanceCnt.HealthyInstanceCount
371+
}
372+
api.AddNamespaceSummary(out, &apimodel.Summary{
373+
TotalServiceCount: totalServiceCount,
374+
TotalInstanceCount: totalInstanceCount,
375+
TotalHealthInstanceCount: totalHealthInstanceCount,
376+
})
368377
return out
369378
}
370379

release/conf/i18n/en.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
400102 = "invalid user token" #InvalidUserToken
2727
400103 = "invalid parameter" #InvalidParameter
2828
400104 = "query instance parameter is empty" #EmptyQueryParameter
29-
400105 = "query instance, (service,namespace) or host is required" #InvalidQueryInsParameter
29+
400105 = "query instance, service or namespace or host is required" #InvalidQueryInsParameter
3030
400110 = "invalid namespace name" #InvalidNamespaceName
3131
400111 = "invalid namespace owners" #InvalidNamespaceOwners
3232
400112 = "invalid namespace token" #InvalidNamespaceToken

release/conf/i18n/zh.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
400102 = "用户token非法" #InvalidUserToken
2727
400103 = "参数非法" #InvalidParameter
2828
400104 = "查询实例参数为空" #EmptyQueryParameter
29-
400105 = "查询实例, 需要(服务,命名空间)或主机作为参数" #InvalidQueryInsParameter
29+
400105 = "查询实例, 需要服务或命名空间或主机作为参数" #InvalidQueryInsParameter
3030
400110 = "命名空间名称非法" #InvalidNamespaceName
3131
400111 = "命名空间拥有人非法" #InvalidNamespaceOwners
3232
400112 = "命名空间token非法" #InvalidNamespaceToken

service/instance.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1171,8 +1171,8 @@ func preGetInstances(query map[string]string) (map[string]string, map[string]str
11711171
_, serviceIsAvail := query["service"]
11721172
_, namespaceIsAvail := query["namespace"]
11731173
_, hostIsAvail := query["host"]
1174-
// 要么(servicenamespace)存在,要么host存在,不然视为参数不完整
1175-
if !((serviceIsAvail && namespaceIsAvail) || hostIsAvail) {
1174+
// service namespace host 三个必须最少传一个
1175+
if !(serviceIsAvail || namespaceIsAvail || hostIsAvail) {
11761176
return nil, nil, api.NewBatchQueryResponse(apimodel.Code_InvalidQueryInsParameter)
11771177
}
11781178

0 commit comments

Comments
 (0)