github.com/polarismesh/polaris@v1.17.8/config/client.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 config 19 20 import ( 21 "context" 22 "encoding/base64" 23 24 apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage" 25 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 26 "go.uber.org/zap" 27 28 api "github.com/polarismesh/polaris/common/api/v1" 29 "github.com/polarismesh/polaris/common/model" 30 "github.com/polarismesh/polaris/common/rsa" 31 commontime "github.com/polarismesh/polaris/common/time" 32 "github.com/polarismesh/polaris/common/utils" 33 ) 34 35 type ( 36 compareFunction func(clientInfo *apiconfig.ClientConfigFileInfo, file *model.ConfigFileRelease) bool 37 ) 38 39 // GetConfigFileForClient 从缓存中获取配置文件,如果客户端的版本号大于服务端,则服务端重新加载缓存 40 func (s *Server) GetConfigFileForClient(ctx context.Context, 41 client *apiconfig.ClientConfigFileInfo) *apiconfig.ConfigClientResponse { 42 namespace := client.GetNamespace().GetValue() 43 group := client.GetGroup().GetValue() 44 fileName := client.GetFileName().GetValue() 45 clientVersion := client.GetVersion().GetValue() 46 47 if namespace == "" || group == "" || fileName == "" { 48 return api.NewConfigClientResponseWithInfo( 49 apimodel.Code_BadRequest, "namespace & group & fileName can not be empty") 50 } 51 // 从缓存中获取配置内容 52 release := s.fileCache.GetActiveRelease(namespace, group, fileName) 53 if release == nil { 54 return api.NewConfigClientResponse(apimodel.Code_NotFoundResource, nil) 55 } 56 57 // 客户端版本号大于服务端版本号,服务端不返回变更 58 if clientVersion > release.Version { 59 return api.NewConfigClientResponse(apimodel.Code_DataNoChange, nil) 60 } 61 configFile, err := toClientInfo(client, release) 62 if err != nil { 63 log.Error("[Config][Service] get config file to client info", utils.RequestID(ctx), zap.Error(err)) 64 return api.NewConfigClientResponseWithInfo(apimodel.Code_ExecuteException, err.Error()) 65 } 66 log.Info("[Config][Client] client get config file success.", utils.RequestID(ctx), 67 zap.String("client", utils.ParseClientAddress(ctx)), zap.String("file", fileName), 68 zap.Uint64("version", release.Version)) 69 return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, configFile) 70 } 71 72 // CreateConfigFileFromClient 调用config_file接口获取配置文件 73 func (s *Server) CreateConfigFileFromClient(ctx context.Context, 74 client *apiconfig.ConfigFile) *apiconfig.ConfigClientResponse { 75 configResponse := s.CreateConfigFile(ctx, client) 76 return api.NewConfigClientResponseFromConfigResponse(configResponse) 77 } 78 79 // UpdateConfigFileFromClient 调用config_file接口更新配置文件 80 func (s *Server) UpdateConfigFileFromClient(ctx context.Context, 81 client *apiconfig.ConfigFile) *apiconfig.ConfigClientResponse { 82 configResponse := s.UpdateConfigFile(ctx, client) 83 return api.NewConfigClientResponseFromConfigResponse(configResponse) 84 } 85 86 // PublishConfigFileFromClient 调用config_file_release接口发布配置文件 87 func (s *Server) PublishConfigFileFromClient(ctx context.Context, 88 client *apiconfig.ConfigFileRelease) *apiconfig.ConfigClientResponse { 89 configResponse := s.PublishConfigFile(ctx, client) 90 return api.NewConfigClientResponseFromConfigResponse(configResponse) 91 } 92 93 func (s *Server) WatchConfigFiles(ctx context.Context, 94 req *apiconfig.ClientWatchConfigFileRequest) (WatchCallback, error) { 95 96 watchFiles := req.GetWatchFiles() 97 // 2. 检查客户端是否有版本落后 98 resp, needWatch := s.checkClientConfigFile(ctx, watchFiles, compareByVersion) 99 if !needWatch { 100 return func() *apiconfig.ConfigClientResponse { 101 return resp 102 }, nil 103 } 104 105 // 3. 监听配置变更,hold 请求 30s,30s 内如果有配置发布,则响应请求 106 clientId := utils.ParseClientAddress(ctx) + "@" + utils.NewUUID()[0:8] 107 finishChan := s.ConnManager().AddConn(clientId, watchFiles) 108 return func() *apiconfig.ConfigClientResponse { 109 return <-finishChan 110 }, nil 111 } 112 113 // GetConfigFileNamesWithCache 114 func (s *Server) GetConfigFileNamesWithCache(ctx context.Context, 115 req *apiconfig.ConfigFileGroupRequest) *apiconfig.ConfigClientListResponse { 116 117 namespace := req.GetConfigFileGroup().GetNamespace().GetValue() 118 group := req.GetConfigFileGroup().GetName().GetValue() 119 120 out := api.NewConfigClientListResponse(apimodel.Code_ExecuteSuccess) 121 122 if namespace == "" || group == "" { 123 out.Code = utils.NewUInt32Value(uint32(apimodel.Code_BadRequest)) 124 out.Info = utils.NewStringValue("invalid namespace or group") 125 return out 126 } 127 128 releases, revision := s.fileCache.GetGroupActiveReleases(namespace, group) 129 if revision == req.GetRevision().GetValue() { 130 out.Code = utils.NewUInt32Value(uint32(apimodel.Code_DataNoChange)) 131 return out 132 } 133 ret := make([]*apiconfig.ClientConfigFileInfo, 0, len(releases)) 134 for i := range releases { 135 ret = append(ret, &apiconfig.ClientConfigFileInfo{ 136 Namespace: utils.NewStringValue(releases[i].Namespace), 137 Group: utils.NewStringValue(releases[i].Group), 138 FileName: utils.NewStringValue(releases[i].FileName), 139 Name: utils.NewStringValue(releases[i].Name), 140 Version: utils.NewUInt64Value(releases[i].Version), 141 ReleaseTime: utils.NewStringValue(commontime.Time2String(releases[i].ModifyTime)), 142 }) 143 } 144 145 return &apiconfig.ConfigClientListResponse{ 146 Code: utils.NewUInt32Value(uint32(apimodel.Code_ExecuteSuccess)), 147 Info: utils.NewStringValue(api.Code2Info(uint32(apimodel.Code_ExecuteSuccess))), 148 Revision: utils.NewStringValue(revision), 149 Namespace: namespace, 150 Group: group, 151 ConfigFileInfos: ret, 152 } 153 } 154 155 func compareByVersion(clientInfo *apiconfig.ClientConfigFileInfo, file *model.ConfigFileRelease) bool { 156 return clientInfo.GetVersion().GetValue() < file.Version 157 } 158 159 func compareByMD5(clientInfo *apiconfig.ClientConfigFileInfo, file *model.ConfigFileRelease) bool { 160 return clientInfo.Md5.GetValue() != file.Md5 161 } 162 163 func (s *Server) checkClientConfigFile(ctx context.Context, files []*apiconfig.ClientConfigFileInfo, 164 compartor compareFunction) (*apiconfig.ConfigClientResponse, bool) { 165 if len(files) == 0 { 166 return api.NewConfigClientResponse(apimodel.Code_InvalidWatchConfigFileFormat, nil), false 167 } 168 for _, configFile := range files { 169 namespace := configFile.GetNamespace().GetValue() 170 group := configFile.GetGroup().GetValue() 171 fileName := configFile.GetFileName().GetValue() 172 173 if namespace == "" || group == "" || fileName == "" { 174 return api.NewConfigClientResponseWithInfo(apimodel.Code_BadRequest, 175 "namespace & group & fileName can not be empty"), false 176 } 177 // 从缓存中获取最新的配置文件信息 178 release := s.fileCache.GetActiveRelease(namespace, group, fileName) 179 if release != nil && compartor(configFile, release) { 180 ret := &apiconfig.ClientConfigFileInfo{ 181 Namespace: utils.NewStringValue(namespace), 182 Group: utils.NewStringValue(group), 183 FileName: utils.NewStringValue(fileName), 184 Version: utils.NewUInt64Value(release.Version), 185 Md5: utils.NewStringValue(release.Md5), 186 } 187 return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, ret), false 188 } 189 } 190 return api.NewConfigClientResponse(apimodel.Code_DataNoChange, nil), true 191 } 192 193 func toClientInfo(client *apiconfig.ClientConfigFileInfo, 194 release *model.ConfigFileRelease) (*apiconfig.ClientConfigFileInfo, error) { 195 196 namespace := client.GetNamespace().GetValue() 197 group := client.GetGroup().GetValue() 198 fileName := client.GetFileName().GetValue() 199 publicKey := client.GetPublicKey().GetValue() 200 201 copyMetadata := func() map[string]string { 202 ret := map[string]string{} 203 for k, v := range release.Metadata { 204 ret[k] = v 205 } 206 delete(ret, utils.ConfigFileTagKeyDataKey) 207 return ret 208 }() 209 210 configFile := &apiconfig.ClientConfigFileInfo{ 211 Namespace: utils.NewStringValue(namespace), 212 Group: utils.NewStringValue(group), 213 FileName: utils.NewStringValue(fileName), 214 Content: utils.NewStringValue(release.Content), 215 Version: utils.NewUInt64Value(release.Version), 216 Md5: utils.NewStringValue(release.Md5), 217 Encrypted: utils.NewBoolValue(release.IsEncrypted()), 218 Tags: model.FromTagMap(copyMetadata), 219 } 220 221 dataKey := release.GetEncryptDataKey() 222 encryptAlgo := release.GetEncryptAlgo() 223 if dataKey != "" && encryptAlgo != "" { 224 dataKeyBytes, err := base64.StdEncoding.DecodeString(dataKey) 225 if err != nil { 226 log.Error("[Config][Service] decode data key error.", zap.String("dataKey", dataKey), zap.Error(err)) 227 return nil, err 228 } 229 if publicKey != "" { 230 cipherDataKey, err := rsa.EncryptToBase64(dataKeyBytes, publicKey) 231 if err != nil { 232 log.Error("[Config][Service] rsa encrypt data key error.", 233 zap.String("dataKey", dataKey), zap.Error(err)) 234 } else { 235 dataKey = cipherDataKey 236 } 237 } 238 configFile.Tags = append(configFile.Tags, 239 &apiconfig.ConfigFileTag{ 240 Key: utils.NewStringValue(utils.ConfigFileTagKeyDataKey), 241 Value: utils.NewStringValue(dataKey), 242 }, 243 ) 244 } 245 return configFile, nil 246 }