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  }