github.com/polarismesh/polaris@v1.17.8/apiserver/xdsserverv3/server_test.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package xdsserverv3 19 20 import ( 21 "bytes" 22 _ "embed" 23 "encoding/json" 24 "os" 25 "testing" 26 "time" 27 28 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 29 envoy_extensions_common_ratelimit_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3" 30 lrl "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3" 31 envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" 32 "github.com/golang/protobuf/jsonpb" 33 "github.com/golang/protobuf/proto" 34 "github.com/golang/protobuf/ptypes" 35 "github.com/golang/protobuf/ptypes/duration" 36 _struct "github.com/golang/protobuf/ptypes/struct" 37 "github.com/golang/protobuf/ptypes/wrappers" 38 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 39 apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 40 "google.golang.org/protobuf/types/known/anypb" 41 "google.golang.org/protobuf/types/known/structpb" 42 43 "github.com/polarismesh/polaris/apiserver/xdsserverv3/resource" 44 "github.com/polarismesh/polaris/common/model" 45 testdata "github.com/polarismesh/polaris/test/data" 46 ) 47 48 func generateRateLimitString(ruleType apitraffic.Rule_Type) (string, string, map[string]*anypb.Any) { 49 rule := &apitraffic.Rule{ 50 Namespace: &wrappers.StringValue{Value: "Test"}, 51 Service: &wrappers.StringValue{Value: "TestService1"}, 52 Resource: apitraffic.Rule_QPS, 53 Type: ruleType, 54 Method: &apimodel.MatchString{ 55 Type: 0, 56 Value: &wrappers.StringValue{Value: "/info"}, 57 }, 58 Labels: map[string]*apimodel.MatchString{ 59 "uin": { 60 Type: 0, 61 Value: &wrappers.StringValue{Value: "109870111"}, 62 }, 63 }, 64 AmountMode: apitraffic.Rule_GLOBAL_TOTAL, 65 Amounts: []*apitraffic.Amount{ 66 { 67 MaxAmount: &wrappers.UInt32Value{Value: 1000}, 68 ValidDuration: &duration.Duration{ 69 Seconds: 1, 70 }, 71 }, 72 }, 73 Action: &wrappers.StringValue{Value: "reject"}, 74 Failover: apitraffic.Rule_FAILOVER_LOCAL, 75 Disable: &wrappers.BoolValue{Value: false}, 76 } 77 // 期待的结果 78 expectRes := make(map[string]*anypb.Any) 79 expectStruct := lrl.LocalRateLimit{ 80 StatPrefix: "http_local_rate_limiter", 81 FilterEnabled: &core.RuntimeFractionalPercent{ 82 RuntimeKey: "local_rate_limit_enabled", 83 DefaultValue: &envoy_type_v3.FractionalPercent{ 84 Numerator: uint32(100), 85 Denominator: envoy_type_v3.FractionalPercent_HUNDRED, 86 }, 87 }, 88 FilterEnforced: &core.RuntimeFractionalPercent{ 89 RuntimeKey: "local_rate_limit_enforced", 90 DefaultValue: &envoy_type_v3.FractionalPercent{ 91 Numerator: uint32(100), 92 Denominator: envoy_type_v3.FractionalPercent_HUNDRED, 93 }, 94 }, 95 } 96 if rule.AmountMode == apitraffic.Rule_GLOBAL_TOTAL { 97 expectStruct.LocalRateLimitPerDownstreamConnection = true 98 } 99 expectStruct.Descriptors = []*envoy_extensions_common_ratelimit_v3.LocalRateLimitDescriptor{ 100 { 101 Entries: []*envoy_extensions_common_ratelimit_v3.RateLimitDescriptor_Entry{ 102 { 103 Key: "uin", 104 Value: "109870111", 105 }, 106 }, 107 TokenBucket: &envoy_type_v3.TokenBucket{ 108 MaxTokens: 1000, 109 FillInterval: &duration.Duration{Seconds: 1}, 110 }, 111 }, 112 } 113 pbst, err := ptypes.MarshalAny(&expectStruct) 114 if err != nil { 115 panic(err) 116 } 117 expectRes["envoy.filters.http.local_ratelimit"] = pbst 118 119 // 测试用限流字符串 120 labelStr, _ := json.Marshal(rule.Labels) 121 rule.Labels = nil 122 ruleStr, _ := json.Marshal(rule) 123 if ruleType == apitraffic.Rule_GLOBAL { 124 expectRes = nil 125 } 126 return string(ruleStr), string(labelStr), expectRes 127 } 128 129 func generateGlobalRateLimitRule() ([]*model.RateLimit, map[string]*anypb.Any) { 130 ruleStr, labelStr, expectRes := generateRateLimitString(apitraffic.Rule_GLOBAL) 131 var rateLimits []*model.RateLimit 132 rateLimits = append(rateLimits, &model.RateLimit{ 133 ID: "ratelimit-1", 134 ServiceID: "service-1", 135 Labels: labelStr, 136 Rule: ruleStr, 137 Revision: "revision-1", 138 Valid: false, 139 Disable: false, 140 CreateTime: time.Now(), 141 ModifyTime: time.Now(), 142 }) 143 return rateLimits, expectRes 144 } 145 146 func generateLocalRateLimitRule() ([]*model.RateLimit, map[string]*anypb.Any) { 147 ruleStr, labelStr, expectRes := generateRateLimitString(apitraffic.Rule_LOCAL) 148 var rateLimits []*model.RateLimit 149 rateLimits = append(rateLimits, &model.RateLimit{ 150 ID: "ratelimit-2", 151 ServiceID: "service-2", 152 Labels: labelStr, 153 Rule: ruleStr, 154 Revision: "revision-2", 155 Valid: false, 156 Disable: false, 157 CreateTime: time.Now(), 158 ModifyTime: time.Now(), 159 }) 160 return rateLimits, expectRes 161 } 162 163 func TestParseNodeID(t *testing.T) { 164 testTable := []struct { 165 NodeID string 166 167 Namespace string 168 UUID string 169 HostIP string 170 }{ 171 { 172 NodeID: "default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 173 Namespace: "default", 174 UUID: "9b9f5630-81a1-47cd-a558-036eb616dc71", 175 HostIP: "172.17.1.1", 176 }, 177 { 178 NodeID: "namespace/9b9f5630-81a1-47cd-a558-036eb616dc71~1.1.1.1", 179 Namespace: "namespace", 180 UUID: "9b9f5630-81a1-47cd-a558-036eb616dc71", 181 HostIP: "1.1.1.1", 182 }, 183 { 184 NodeID: "default/67c745dd-35b3-40fe-8a9d-64ea6ec19fb2~10.244.0.229", 185 Namespace: "default", 186 HostIP: "10.244.0.229", 187 UUID: "67c745dd-35b3-40fe-8a9d-64ea6ec19fb2", 188 }, 189 // bad case 190 { 191 NodeID: "namespace", 192 Namespace: "", 193 UUID: "", 194 HostIP: "", 195 }, 196 } 197 for _, item := range testTable { 198 _, ns, id, hostip := resource.ParseNodeID(item.NodeID) 199 if ns != item.Namespace || id != item.UUID || hostip != item.HostIP { 200 t.Fatalf("parse node id [%s] expected ['%s' '%s' '%s'] got : ['%s' '%s' '%s']", 201 item.NodeID, 202 item.Namespace, item.UUID, item.HostIP, 203 ns, id, hostip, 204 ) 205 } 206 } 207 } 208 209 func TestNodeHashID(t *testing.T) { 210 testTable := []struct { 211 Node *core.Node 212 TargetID string 213 }{ 214 { 215 Node: &core.Node{ 216 Id: "default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 217 Metadata: &_struct.Struct{ 218 Fields: map[string]*structpb.Value{ 219 resource.TLSModeTag: &_struct.Value{ 220 Kind: &_struct.Value_StringValue{ 221 StringValue: string(resource.TLSModeStrict), 222 }, 223 }, 224 }, 225 }, 226 }, 227 TargetID: "default/" + string(resource.TLSModeStrict), 228 }, 229 { 230 Node: &core.Node{ 231 Id: "polaris/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 232 Metadata: &_struct.Struct{ 233 Fields: map[string]*structpb.Value{ 234 resource.TLSModeTag: &_struct.Value{ 235 Kind: &_struct.Value_StringValue{ 236 StringValue: string(resource.TLSModePermissive), 237 }, 238 }, 239 }, 240 }, 241 }, 242 TargetID: "polaris/" + string(resource.TLSModePermissive), 243 }, 244 { 245 Node: &core.Node{ 246 Id: "default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 247 Metadata: &_struct.Struct{ 248 Fields: map[string]*structpb.Value{ 249 resource.TLSModeTag: &_struct.Value{ 250 Kind: &_struct.Value_StringValue{ 251 StringValue: string(resource.TLSModeNone), 252 }, 253 }, 254 }, 255 }, 256 }, 257 TargetID: "default", 258 }, 259 // bad case: wrong tls mode 260 { 261 Node: &core.Node{ 262 Id: "default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 263 Metadata: &_struct.Struct{ 264 Fields: map[string]*structpb.Value{ 265 resource.TLSModeTag: &_struct.Value{ 266 Kind: &_struct.Value_StringValue{ 267 StringValue: "abc", 268 }, 269 }, 270 }, 271 }, 272 }, 273 TargetID: "default", 274 }, 275 // no node metadata 276 { 277 Node: &core.Node{ 278 Id: "default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 279 }, 280 TargetID: "default", 281 }, 282 // metadata does not contain tls mode kv 283 { 284 Node: &core.Node{ 285 Id: "default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 286 Metadata: &_struct.Struct{ 287 Fields: map[string]*structpb.Value{ 288 "hello": &_struct.Value{ 289 Kind: &_struct.Value_StringValue{ 290 StringValue: "abc", 291 }, 292 }, 293 }, 294 }, 295 }, 296 TargetID: "default", 297 }, 298 { 299 Node: &core.Node{ 300 Id: "gateway~default/9b9f5630-81a1-47cd-a558-036eb616dc71~172.17.1.1", 301 Metadata: &_struct.Struct{ 302 Fields: map[string]*structpb.Value{ 303 "hello": &_struct.Value{ 304 Kind: &_struct.Value_StringValue{ 305 StringValue: "abc", 306 }, 307 }, 308 resource.GatewayNamespaceName: &_struct.Value{ 309 Kind: &structpb.Value_StringValue{ 310 StringValue: "default", 311 }, 312 }, 313 resource.GatewayServiceName: &_struct.Value{ 314 Kind: &structpb.Value_StringValue{ 315 StringValue: "service", 316 }, 317 }, 318 }, 319 }, 320 }, 321 TargetID: "gateway/default/service", 322 }, 323 } 324 for i, item := range testTable { 325 id := resource.PolarisNodeHash{}.ID(item.Node) 326 if id != item.TargetID { 327 t.Fatalf("test case [%d] failed: expect ID %s, got ID %s", 328 i, item.TargetID, id) 329 } 330 } 331 } 332 333 var ( 334 testServicesData []byte 335 noInboundDump []byte 336 permissiveDump []byte 337 strictDump []byte 338 gatewayDump []byte 339 ) 340 341 func init() { 342 var err error 343 testServicesData, err = os.ReadFile(testdata.Path("xds/data.json")) 344 if err != nil { 345 panic(err) 346 } 347 noInboundDump, err = os.ReadFile(testdata.Path("xds/dump.yaml")) 348 if err != nil { 349 panic(err) 350 } 351 permissiveDump, err = os.ReadFile(testdata.Path("xds/permissive.dump.yaml")) 352 if err != nil { 353 panic(err) 354 } 355 strictDump, err = os.ReadFile(testdata.Path("xds/strict.dump.yaml")) 356 if err != nil { 357 panic(err) 358 } 359 gatewayDump, err = os.ReadFile(testdata.Path("xds/gateway.dump.yaml")) 360 if err != nil { 361 panic(err) 362 } 363 } 364 365 // ParseArrayByText 通过字符串解析PB数组对象 366 func ParseArrayByText(createMessage func() proto.Message, text string) error { 367 jsonDecoder := json.NewDecoder(bytes.NewBuffer([]byte(text))) 368 return parseArray(createMessage, jsonDecoder) 369 } 370 371 func parseArray(createMessage func() proto.Message, jsonDecoder *json.Decoder) error { 372 // read open bracket 373 _, err := jsonDecoder.Token() 374 if err != nil { 375 return err 376 } 377 for jsonDecoder.More() { 378 protoMessage := createMessage() 379 err := jsonpb.UnmarshalNext(jsonDecoder, protoMessage) 380 if err != nil { 381 return err 382 } 383 } 384 return nil 385 }