diff --git a/apiserver/xdsserverv3/generate.go b/apiserver/xdsserverv3/generate.go index 95b1e4f33..8b5da8d27 100644 --- a/apiserver/xdsserverv3/generate.go +++ b/apiserver/xdsserverv3/generate.go @@ -89,6 +89,7 @@ func (x *XdsResourceGenerator) Generate(versionLocal string, if runType == resource.RunTypeSidecar { for svcKey := range services { + opt.OpenOnDemand = false // 换成 INBOUND 构建 CDS、EDS、RDS opt.SelfService = svcKey opt.TrafficDirection = corev3.TrafficDirection_INBOUND diff --git a/apiserver/xdsserverv3/rds.go b/apiserver/xdsserverv3/rds.go index a54cf8b1e..67649a20c 100644 --- a/apiserver/xdsserverv3/rds.go +++ b/apiserver/xdsserverv3/rds.go @@ -174,8 +174,10 @@ func (rds *RDSBuilder) makeSidecarInBoundRoutes(selfService model.ServiceKey, limits, typedPerFilterConfig, err := resource.MakeSidecarLocalRateLimit(seacher, selfService) if err == nil { currentRoute.TypedPerFilterConfig = typedPerFilterConfig - currentRoute.TypedPerFilterConfig[resource.EnvoyHttpFilter_OnDemand] = - resource.BuildOnDemandRouteTypedPerFilterConfig() + if opt.OpenOnDemand { + currentRoute.TypedPerFilterConfig[resource.EnvoyHttpFilter_OnDemand] = + resource.BuildOnDemandRouteTypedPerFilterConfig() + } currentRoute.GetRoute().RateLimits = limits } return []*route.Route{ diff --git a/apiserver/xdsserverv3/resource/help.go b/apiserver/xdsserverv3/resource/help.go index 3d2e5b81a..ac862116e 100644 --- a/apiserver/xdsserverv3/resource/help.go +++ b/apiserver/xdsserverv3/resource/help.go @@ -296,6 +296,7 @@ func BuildRateLimitDescriptors(rule *traffic_manage.Rule) ([]*route.RateLimit_Ac actions = append(actions, &route.RateLimit_Action{ ActionSpecifier: &route.RateLimit_Action_HeaderValueMatch_{ HeaderValueMatch: BuildRateLimitActionHeaderValueMatch(":path", methodName, &apitraffic.MatchArgument{ + Key: ":path", Value: &apimodel.MatchString{ Type: methodMatchType, Value: wrapperspb.String(methodName), diff --git a/apiserver/xdsserverv3/resource/node.go b/apiserver/xdsserverv3/resource/node.go index 750370233..8283f8ee2 100644 --- a/apiserver/xdsserverv3/resource/node.go +++ b/apiserver/xdsserverv3/resource/node.go @@ -66,8 +66,6 @@ const ( SidecarTLSModeTag = "sidecar.polarismesh.cn/tlsMode" // SidecarOpenOnDemandFeature . SidecarOpenOnDemandFeature = "sidecar.polarismesh.cn/openOnDemand" - // SidecarConnectServerEndpoint report xds server the envoy xds on-demand cds server endpoint info - SidecarODCDSServerEndpoint = "sidecar.polarismesh.cn/odcdsServerEndpoint" ) type EnvoyNodeView struct { diff --git a/apiserver/xdsserverv3/server.go b/apiserver/xdsserverv3/server.go index 5f0e4a2f7..e4b13167e 100644 --- a/apiserver/xdsserverv3/server.go +++ b/apiserver/xdsserverv3/server.go @@ -273,6 +273,9 @@ func (x *XDSServer) startSynTask(ctx context.Context) { for _, info := range infos { cacheServiceInfos := registryInfo[ns] if _, ok := cacheServiceInfos[info.ServiceKey]; !ok { + if _, ok := needRemove[ns]; !ok { + needRemove[ns] = make(map[model.ServiceKey]*resource.ServiceInfo) + } needRemove[ns][info.ServiceKey] = info continue } diff --git a/config/client.go b/config/client.go index 62b37f06c..7f55dfe07 100644 --- a/config/client.go +++ b/config/client.go @@ -64,8 +64,8 @@ func (s *Server) GetConfigFileWithCache(ctx context.Context, return api.NewConfigClientResponse(apimodel.Code_NotFoundResource, nil) } } - // 客户端版本号大于服务端版本号,服务端不返回变更 todo: 结合灰度和全量版本 判断 - if client.GetVersion().GetValue() > release.Version { + // 客户端版本号大于等于服务端版本号,服务端不返回变更 + if client.GetVersion().GetValue() >= release.Version { return api.NewConfigClientResponse(apimodel.Code_DataNoChange, nil) } configFile, err := toClientInfo(client, release) @@ -147,6 +147,9 @@ func (s *Server) GetConfigFileNamesWithCache(ctx context.Context, } releases, revision := s.fileCache.GetGroupActiveReleases(namespace, group) + if revision == "" { + return api.NewConfigClientListResponse(apimodel.Code_ExecuteSuccess) + } if revision == req.GetRevision().GetValue() { return api.NewConfigClientListResponse(apimodel.Code_DataNoChange) } @@ -183,6 +186,11 @@ func (s *Server) GetConfigGroupsWithCache(ctx context.Context, req *apiconfig.Cl } groups, revision := s.groupCache.ListGroups(namespace) + if revision == "" { + out = api.NewConfigDiscoverResponse(apimodel.Code_ExecuteSuccess) + out.Type = apiconfig.ConfigDiscoverResponse_CONFIG_FILE_GROUPS + return out + } if revision == req.GetMd5().GetValue() { out = api.NewConfigDiscoverResponse(apimodel.Code_DataNoChange) out.Type = apiconfig.ConfigDiscoverResponse_CONFIG_FILE_GROUPS diff --git a/config/client_test.go b/config/client_test.go index f7b9aa51b..43c77792c 100644 --- a/config/client_test.go +++ b/config/client_test.go @@ -434,16 +434,7 @@ func TestWatchConfigFileAtFirstPublish(t *testing.T) { // Test10000ClientWatchConfigFile 测试 10000 个客户端同时监听配置变更,配置发布所有客户端都收到通知 func TestManyClientWatchConfigFile(t *testing.T) { - testSuit := &ConfigCenterTest{} - if err := testSuit.Initialize(); err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - if err := testSuit.clearTestData(); err != nil { - t.Fatal(err) - } - testSuit.Destroy() - }) + testSuit := newConfigCenterTestSuit(t) clientSize := 100 received := utils.NewSyncMap[string, bool]() @@ -505,61 +496,221 @@ func TestManyClientWatchConfigFile(t *testing.T) { // TestDeleteConfigFile 测试删除配置,删除配置会通知客户端,并且重新拉取配置会返回 NotFoundResourceConfigFile 状态码 func TestDeleteConfigFile(t *testing.T) { - testSuit := &ConfigCenterTest{} - if err := testSuit.Initialize(); err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - if err := testSuit.clearTestData(); err != nil { - t.Fatal(err) - } - testSuit.Destroy() - }) + testSuit := newConfigCenterTestSuit(t) + + newMockNs := "TestDeleteConfigFile" // 创建并发布一个配置文件 configFile := assembleConfigFile() + configFile.Namespace = wrapperspb.String(newMockNs) + rsp := testSuit.ConfigServer().CreateConfigFile(testSuit.DefaultCtx, configFile) assert.Equal(t, api.ExecuteSuccess, rsp.Code.GetValue()) rsp2 := testSuit.ConfigServer().PublishConfigFile(testSuit.DefaultCtx, assembleConfigFileRelease(configFile)) assert.Equal(t, api.ExecuteSuccess, rsp2.Code.GetValue()) + _ = testSuit.CacheMgr().TestUpdate() - time.Sleep(1200 * time.Millisecond) + activeRelease := testSuit.CacheMgr().ConfigFile().GetActiveRelease(configFile.Namespace.Value, + configFile.Group.Value, configFile.Name.Value) + assert.NotNil(t, activeRelease) // 客户端订阅 - clientId := randomStr() - watchConfigFiles := assembleDefaultClientConfigFile(0) + watchConfigFiles := assembleDefaultClientConfigFile(activeRelease.Version) + for i := range watchConfigFiles { + watchConfigFiles[i].Namespace = wrapperspb.String(newMockNs) + } t.Log("add config watcher") - watchCtx := testSuit.OriginConfigServer().WatchCenter().AddWatcher(clientId, watchConfigFiles, - config.BuildTimeoutWatchCtx(context.Background(), 30*time.Second)) - assert.NotNil(t, watchCtx) - // 删除配置文件 t.Log("remove config file") rsp3 := testSuit.ConfigServer().DeleteConfigFile(testSuit.DefaultCtx, &apiconfig.ConfigFile{ - Namespace: utils.NewStringValue(testNamespace), + Namespace: utils.NewStringValue(newMockNs), Group: utils.NewStringValue(testGroup), Name: utils.NewStringValue(testFile), }) assert.Equal(t, api.ExecuteSuccess, rsp3.Code.GetValue()) - - // 客户端收到推送通知 - t.Log("wait receive config change msg") - _, err := (watchCtx.(*config.LongPollWatchContext)).GetNotifieResultWithTime(10 * time.Second) - if err != nil { - t.Fatal(err) - } + _ = testSuit.CacheMgr().TestUpdate() fileInfo := &apiconfig.ClientConfigFileInfo{ - Namespace: &wrapperspb.StringValue{Value: testNamespace}, + Namespace: &wrapperspb.StringValue{Value: newMockNs}, Group: &wrapperspb.StringValue{Value: testGroup}, FileName: &wrapperspb.StringValue{Value: testFile}, - Version: &wrapperspb.UInt64Value{Value: 2}, } // 重新拉取配置,获取不到配置文件 rsp4 := testSuit.ConfigServer().GetConfigFileWithCache(testSuit.DefaultCtx, fileInfo) assert.Equal(t, uint32(api.NotFoundResource), rsp4.Code.GetValue()) } + +// TestServer_GetConfigFileNamesWithCache +func TestServer_GetConfigFileNamesWithCache(t *testing.T) { + testSuit := newConfigCenterTestSuit(t) + + mockFiles := make(map[string][]*apiconfig.ConfigFile) + groupTotal := 2 + fileTotal := 10 + for i := 0; i < groupTotal; i++ { + groupName := fmt.Sprintf("group-%d", i) + mockFiles[groupName] = make([]*apiconfig.ConfigFile, 0, fileTotal) + for j := 0; j < fileTotal; j++ { + item := &apiconfig.ConfigFile{ + Namespace: wrapperspb.String(testNamespace), + Group: wrapperspb.String(groupName), + Name: wrapperspb.String(fmt.Sprintf("file-%d", j)), + Content: wrapperspb.String(fmt.Sprintf("%d-%d", i, j)), + } + mockFiles[groupName] = append(mockFiles[groupName], item) + rsp := testSuit.ConfigServer().CreateConfigFile(testSuit.DefaultCtx, item) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code.GetValue()) + } + } + t.Cleanup(func() { + for k := range mockFiles { + items := mockFiles[k] + for _, item := range items { + testSuit.ConfigServer().DeleteConfigFile(testSuit.DefaultCtx, item) + } + } + }) + + t.Run("bad-request", func(t *testing.T) { + rsp := testSuit.ConfigServer().GetConfigFileNamesWithCache(testSuit.DefaultCtx, &apiconfig.ConfigFileGroupRequest{ + ConfigFileGroup: &apiconfig.ConfigFileGroup{ + Namespace: utils.NewStringValue(""), + Name: utils.NewStringValue("group-0"), + }, + }) + assert.Equal(t, uint32(apimodel.Code_BadRequest), rsp.Code.GetValue()) + + rsp = testSuit.ConfigServer().GetConfigFileNamesWithCache(testSuit.DefaultCtx, &apiconfig.ConfigFileGroupRequest{ + ConfigFileGroup: &apiconfig.ConfigFileGroup{ + Namespace: utils.NewStringValue(""), + Name: utils.NewStringValue(""), + }, + }) + assert.Equal(t, uint32(apimodel.Code_BadRequest), rsp.Code.GetValue()) + + rsp = testSuit.ConfigServer().GetConfigFileNamesWithCache(testSuit.DefaultCtx, &apiconfig.ConfigFileGroupRequest{ + ConfigFileGroup: &apiconfig.ConfigFileGroup{ + Namespace: utils.NewStringValue("mock-ns"), + Name: utils.NewStringValue(""), + }, + }) + assert.Equal(t, uint32(apimodel.Code_BadRequest), rsp.Code.GetValue()) + }) + + t.Run("no-publish-file", func(t *testing.T) { + rsp := testSuit.ConfigServer().GetConfigFileNamesWithCache(testSuit.DefaultCtx, &apiconfig.ConfigFileGroupRequest{ + ConfigFileGroup: &apiconfig.ConfigFileGroup{ + Namespace: utils.NewStringValue(testNamespace), + Name: utils.NewStringValue("group-0"), + }, + }) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code.GetValue(), rsp.Info.Value) + assert.True(t, len(rsp.ConfigFileInfos) == 0) + }) + + t.Run("publish-file", func(t *testing.T) { + for _, item := range mockFiles["group-0"] { + rsp := testSuit.ConfigServer().PublishConfigFile(testSuit.DefaultCtx, &apiconfig.ConfigFileRelease{ + Namespace: item.Namespace, + Group: item.Group, + FileName: item.Name, + }) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code.GetValue()) + } + + _ = testSuit.CacheMgr().TestUpdate() + + t.Run("revision-fetch", func(t *testing.T) { + rsp := testSuit.ConfigServer().GetConfigFileNamesWithCache(testSuit.DefaultCtx, &apiconfig.ConfigFileGroupRequest{ + ConfigFileGroup: &apiconfig.ConfigFileGroup{ + Namespace: utils.NewStringValue(testNamespace), + Name: utils.NewStringValue("group-0"), + }, + }) + + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code.GetValue(), rsp.String()) + assert.True(t, len(rsp.ConfigFileInfos) == fileTotal, rsp.String()) + + secondRsp := testSuit.ConfigServer().GetConfigFileNamesWithCache(testSuit.DefaultCtx, &apiconfig.ConfigFileGroupRequest{ + Revision: wrapperspb.String(rsp.GetRevision().GetValue()), + ConfigFileGroup: &apiconfig.ConfigFileGroup{ + Namespace: utils.NewStringValue(testNamespace), + Name: utils.NewStringValue("group-0"), + }, + }) + + assert.Equal(t, uint32(apimodel.Code_DataNoChange), secondRsp.Code.GetValue()) + assert.True(t, len(secondRsp.ConfigFileInfos) == 0) + }) + + }) +} + +// TestServer_GetConfigGroupsWithCache +func TestServer_GetConfigGroupsWithCache(t *testing.T) { + testSuit := newConfigCenterTestSuit(t) + + mockFiles := make(map[string][]*apiconfig.ConfigFileGroup) + nsTotal := 2 + groupTotal := 10 + for i := 0; i < nsTotal; i++ { + nsName := fmt.Sprintf("ns-%d", i) + mockFiles[nsName] = make([]*apiconfig.ConfigFileGroup, 0, groupTotal) + for j := 0; j < groupTotal; j++ { + item := &apiconfig.ConfigFileGroup{ + Namespace: wrapperspb.String(nsName), + Name: wrapperspb.String(fmt.Sprintf("group-%d", j)), + } + mockFiles[nsName] = append(mockFiles[nsName], item) + rsp := testSuit.ConfigServer().CreateConfigFileGroup(testSuit.DefaultCtx, item) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code.GetValue(), rsp.GetInfo().GetValue()) + } + } + t.Cleanup(func() { + for k := range mockFiles { + testSuit.NamespaceServer().DeleteNamespace(testSuit.DefaultCtx, &apimodel.Namespace{ + Name: wrapperspb.String(k), + }) + items := mockFiles[k] + for _, item := range items { + testSuit.ConfigServer().DeleteConfigFileGroup(testSuit.DefaultCtx, item.GetNamespace().Value, item.GetName().Value) + } + } + }) + + _ = testSuit.CacheMgr().TestUpdate() + + t.Run("case-1", func(t *testing.T) { + rsp := testSuit.ConfigServer().GetConfigGroupsWithCache(testSuit.DefaultCtx, &apiconfig.ClientConfigFileInfo{ + Namespace: wrapperspb.String("ns-0"), + }) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code, rsp.Info) + assert.True(t, len(rsp.ConfigFileGroups) == groupTotal) + + // 同一个 revision 查询 + rsp = testSuit.ConfigServer().GetConfigGroupsWithCache(testSuit.DefaultCtx, &apiconfig.ClientConfigFileInfo{ + Namespace: wrapperspb.String("ns-0"), + Md5: wrapperspb.String(rsp.GetRevision()), + }) + assert.Equal(t, uint32(apimodel.Code_DataNoChange), rsp.Code, rsp.Info) + + // 删除其中一个配置分组后查询 + groups := mockFiles["ns-0"] + for i := 0; i < 2; i++ { + delRsp := testSuit.ConfigServer().DeleteConfigFileGroup(testSuit.DefaultCtx, "ns-0", groups[i].Name.Value) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), delRsp.Code.Value, delRsp.Info.Value) + } + + _ = testSuit.CacheMgr().TestUpdate() + + rsp = testSuit.ConfigServer().GetConfigGroupsWithCache(testSuit.DefaultCtx, &apiconfig.ClientConfigFileInfo{ + Namespace: wrapperspb.String("ns-0"), + }) + assert.Equal(t, uint32(apimodel.Code_ExecuteSuccess), rsp.Code, rsp.Info) + assert.True(t, len(rsp.ConfigFileGroups) == groupTotal-2) + }) +} diff --git a/config/common.go b/config/common.go index 2acf7c8a9..4046e3a77 100644 --- a/config/common.go +++ b/config/common.go @@ -17,14 +17,6 @@ package config -import ( - "errors" - - apimodel "github.com/polarismesh/specification/source/go/api/v1/model" - - "github.com/polarismesh/polaris/common/model" -) - var ( availableSearch = map[string]map[string]string{ "config_file": { @@ -81,13 +73,3 @@ func (s *Server) checkNamespaceExisted(namespaceName string) bool { namespace, _ := s.storage.GetNamespace(namespaceName) return namespace != nil } - -func convertToErrCode(err error) apimodel.Code { - if errors.Is(err, model.ErrorTokenNotExist) { - return apimodel.Code_TokenNotExisted - } - if errors.Is(err, model.ErrorTokenDisabled) { - return apimodel.Code_TokenDisabled - } - return apimodel.Code_NotAllowedAccess -} diff --git a/config/config_file_release_test.go b/config/config_file_release_test.go index 451ac20e8..90d4d3987 100644 --- a/config/config_file_release_test.go +++ b/config/config_file_release_test.go @@ -271,6 +271,7 @@ func Test_PublishConfigFile(t *testing.T) { }) t.Run("client_get_configfile", func(t *testing.T) { + _ = testSuit.CacheMgr().TestUpdate() // 客户端获取符合预期, 这里强制触发一次缓存数据同步 clientResp := testSuit.ConfigServer().GetConfigFileWithCache(testSuit.DefaultCtx, &config_manage.ClientConfigFileInfo{ Namespace: utils.NewStringValue(mockNamespace), diff --git a/config/utils.go b/config/utils.go index 31a6c9d18..ac82e9592 100644 --- a/config/utils.go +++ b/config/utils.go @@ -27,8 +27,6 @@ import ( "fmt" "path" "regexp" - "strconv" - "strings" "unicode/utf8" "github.com/golang/protobuf/ptypes/wrappers" @@ -74,29 +72,6 @@ func CheckContentLength(content string, max int) error { return nil } -// GenReleaseName 生成发布名称,规则是 filename-${三位自增长序列} -func GenReleaseName(oldReleaseName, fileName string) string { - if oldReleaseName == "" { - return fileName + "-001" - } - - nameInfo := strings.Split(oldReleaseName, "-") - if len(nameInfo) != 2 { - return oldReleaseName - } - - if fileName != nameInfo[0] { - return oldReleaseName - } - - num, err := strconv.ParseInt(nameInfo[1], 10, 64) - if err != nil { - return oldReleaseName - } - - return fileName + "-" + strings.ReplaceAll(fmt.Sprintf("%3d", num+1), " ", "0") -} - func CompressConfigFiles(files []*model.ConfigFile, fileID2Tags map[uint64][]*model.ConfigFileTag, isExportGroup bool) (*bytes.Buffer, error) { var buf bytes.Buffer diff --git a/store/boltdb/config_file_release.go b/store/boltdb/config_file_release.go index d1a1a5272..ba49cc6e6 100644 --- a/store/boltdb/config_file_release.go +++ b/store/boltdb/config_file_release.go @@ -286,7 +286,9 @@ func (cfr *configFileReleaseStore) CleanConfigFileReleasesTx(tx store.Tx, namesp expect := saveNs == namespace && saveGroup == group && saveFileName == fileName return expect }, values) - + if err != nil { + return err + } properties := map[string]interface{}{ FileReleaseFieldFlag: 1, FileReleaseFieldValid: false, @@ -294,11 +296,10 @@ func (cfr *configFileReleaseStore) CleanConfigFileReleasesTx(tx store.Tx, namesp } for key := range values { if err := updateValue(dbTx, tblConfigFileRelease, key, properties); err != nil { - return nil + return err } } - - return err + return nil } // GetMoreReleaseFile Get the last update time more than a certain time point diff --git a/test/suit/test_suit.go b/test/suit/test_suit.go index e19126515..96d64c57c 100644 --- a/test/suit/test_suit.go +++ b/test/suit/test_suit.go @@ -409,6 +409,9 @@ func (d *DiscoverTestSuit) initialize(opts ...options) error { func (d *DiscoverTestSuit) Destroy() { d.cancel() + if svr, ok := d.configOriginSvr.(*config.Server); ok { + svr.WatchCenter().Close() + } d.healthCheckServer.Destroy() _ = d.cacheMgr.Close() _ = d.Storage.Destroy()