github.com/polarismesh/polaris@v1.17.8/store/boltdb/config_file.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 boltdb 19 20 import ( 21 "errors" 22 "fmt" 23 "sort" 24 "strings" 25 "time" 26 27 bolt "go.etcd.io/bbolt" 28 "go.uber.org/zap" 29 30 "github.com/polarismesh/polaris/common/model" 31 "github.com/polarismesh/polaris/common/utils" 32 "github.com/polarismesh/polaris/store" 33 ) 34 35 var _ store.ConfigFileStore = (*configFileStore)(nil) 36 37 const ( 38 tblConfigFile string = "ConfigFile" 39 tblConfigFileID string = "ConfigFileID" 40 41 FileFieldId string = "Id" 42 FileFieldName string = "Name" 43 FileFieldNamespace string = "Namespace" 44 FileFieldGroup string = "Group" 45 FileFieldContent string = "Content" 46 FileFieldComment string = "Comment" 47 FileFieldFormat string = "Format" 48 FileFieldFlag string = "Flag" 49 FileFieldCreateTime string = "CreateTime" 50 FileFieldCreateBy string = "CreateBy" 51 FileFieldModifyTime string = "ModifyTime" 52 FileFieldModifyBy string = "ModifyBy" 53 FileFieldValid string = "Valid" 54 FileFieldMetadata string = "Metadata" 55 FileFieldEncrypt string = "Encrypt" 56 FileFieldEncryptAlgo string = "EncryptAlgo" 57 ) 58 59 var ( 60 ErrMultipleConfigFileFound = errors.New("multiple config_file found") 61 ) 62 63 type configFileStore struct { 64 handler BoltHandler 65 } 66 67 func newConfigFileStore(handler BoltHandler) *configFileStore { 68 s := &configFileStore{handler: handler} 69 return s 70 } 71 72 func (cf *configFileStore) LockConfigFile(tx store.Tx, file *model.ConfigFileKey) (*model.ConfigFile, error) { 73 return cf.GetConfigFileTx(tx, file.Namespace, file.Group, file.Name) 74 } 75 76 // CreateConfigFile 创建配置文件 77 func (cf *configFileStore) CreateConfigFileTx(proxyTx store.Tx, file *model.ConfigFile) error { 78 dbTx := proxyTx.GetDelegateTx().(*bolt.Tx) 79 table, err := dbTx.CreateBucketIfNotExists([]byte(tblConfigFile)) 80 if err != nil { 81 return store.Error(err) 82 } 83 nextId, err := table.NextSequence() 84 if err != nil { 85 return store.Error(err) 86 } 87 88 file.Id = nextId 89 file.Valid = true 90 file.CreateTime = time.Now() 91 file.ModifyTime = file.CreateTime 92 93 key := fmt.Sprintf("%s@%s@%s", file.Namespace, file.Group, file.Name) 94 if err := saveValue(dbTx, tblConfigFile, key, file); err != nil { 95 log.Error("[ConfigFile] save config_file", zap.String("key", key), zap.Error(err)) 96 return err 97 } 98 return nil 99 } 100 101 func (cf *configFileStore) GetConfigFile(namespace, group, name string) (*model.ConfigFile, error) { 102 tx, err := cf.handler.StartTx() 103 if err != nil { 104 return nil, store.Error(err) 105 } 106 defer func() { 107 _ = tx.Rollback() 108 }() 109 return cf.GetConfigFileTx(tx, namespace, group, name) 110 } 111 112 // GetConfigFileTx 获取配置文件 113 func (cf *configFileStore) GetConfigFileTx(tx store.Tx, namespace, group, name string) (*model.ConfigFile, error) { 114 dbTx := tx.GetDelegateTx().(*bolt.Tx) 115 data, err := cf.getConfigFile(dbTx, namespace, group, name) 116 if err != nil { 117 return nil, err 118 } 119 return data, nil 120 } 121 122 // GetConfigFile 获取配置文件 123 func (cf *configFileStore) getConfigFile(tx *bolt.Tx, namespace, group, name string) (*model.ConfigFile, error) { 124 key := fmt.Sprintf("%s@%s@%s", namespace, group, name) 125 126 values := make(map[string]interface{}) 127 if err := loadValues(tx, tblConfigFile, []string{key}, &model.ConfigFile{}, values); err != nil { 128 return nil, err 129 } 130 131 if len(values) == 0 { 132 return nil, nil 133 } 134 135 if len(values) > 1 { 136 return nil, ErrMultipleConfigFileFound 137 } 138 139 data := values[key].(*model.ConfigFile) 140 if data.Valid { 141 return data, nil 142 } 143 144 return nil, nil 145 } 146 147 // QueryConfigFiles 翻页查询配置文件,group、name可为模糊匹配 148 func (cf *configFileStore) QueryConfigFiles(filter map[string]string, 149 offset, limit uint32) (uint32, []*model.ConfigFile, error) { 150 fields := []string{FileFieldNamespace, FileFieldGroup, FileFieldName, FileFieldValid} 151 152 namespace := filter["namespace"] 153 group := filter["group"] 154 name := filter["name"] 155 156 hasNs := len(namespace) != 0 157 hasGroup := len(group) != 0 158 hasName := len(name) != 0 159 160 ret, err := cf.handler.LoadValuesByFilter(tblConfigFile, fields, &model.ConfigFile{}, 161 func(m map[string]interface{}) bool { 162 valid, _ := m[FileFieldValid].(bool) 163 if !valid { 164 return false 165 } 166 167 saveNs, _ := m[FileFieldNamespace].(string) 168 saveGroup, _ := m[FileFieldGroup].(string) 169 saveFileName, _ := m[FileFieldName].(string) 170 171 if hasNs && utils.IsWildNotMatch(saveNs, namespace) { 172 return false 173 } 174 if hasGroup && utils.IsWildNotMatch(saveGroup, group) { 175 return false 176 } 177 if hasName && utils.IsWildNotMatch(saveFileName, name) { 178 return false 179 } 180 181 return true 182 }) 183 184 if err != nil { 185 return 0, nil, err 186 } 187 188 return uint32(len(ret)), doConfigFilePage(ret, offset, limit), nil 189 } 190 191 // UpdateConfigFile 更新配置文件 192 func (cf *configFileStore) UpdateConfigFileTx(tx store.Tx, file *model.ConfigFile) error { 193 dbTx := tx.GetDelegateTx().(*bolt.Tx) 194 key := fmt.Sprintf("%s@%s@%s", file.Namespace, file.Group, file.Name) 195 properties := make(map[string]interface{}) 196 properties[FileFieldContent] = file.Content 197 properties[FileFieldComment] = file.Comment 198 properties[FileFieldFormat] = file.Format 199 properties[FileFieldMetadata] = file.Metadata 200 properties[FileFieldEncrypt] = file.Encrypt 201 properties[FileFieldEncryptAlgo] = file.EncryptAlgo 202 properties[FileFieldModifyTime] = time.Now() 203 properties[FileFieldModifyBy] = file.ModifyBy 204 if err := updateValue(dbTx, tblConfigFile, key, properties); err != nil { 205 return err 206 } 207 return nil 208 } 209 210 // DeleteConfigFile 删除配置文件 211 func (cf *configFileStore) DeleteConfigFileTx(proxyTx store.Tx, namespace, group, name string) error { 212 _, err := DoTransactionIfNeed(proxyTx, cf.handler, func(tx *bolt.Tx) ([]interface{}, error) { 213 key := fmt.Sprintf("%s@%s@%s", namespace, group, name) 214 215 properties := make(map[string]interface{}) 216 properties[FileFieldValid] = false 217 properties[FileFieldModifyTime] = time.Now() 218 219 err := updateValue(tx, tblConfigFile, key, properties) 220 return nil, err 221 }) 222 return err 223 } 224 225 // CountConfigFiles 统计配置文件组下的配置文件数量 226 func (cf *configFileStore) CountConfigFiles(namespace, group string) (uint64, error) { 227 hasNs := len(namespace) != 0 228 hasGroup := len(group) != 0 229 230 fields := []string{FileFieldNamespace, FileFieldGroup, FileFieldValid} 231 232 ret, err := cf.handler.LoadValuesByFilter(tblConfigFile, fields, &model.ConfigFile{}, 233 func(m map[string]interface{}) bool { 234 valid, _ := m[FileFieldValid].(bool) 235 if !valid { 236 return false 237 } 238 239 saveNs, _ := m[FileFieldNamespace].(string) 240 saveGroup, _ := m[FileFieldGroup].(string) 241 242 if hasNs && strings.Compare(saveNs, namespace) != 0 { 243 return false 244 } 245 if hasGroup && strings.Compare(saveGroup, group) != 0 { 246 return false 247 } 248 249 return true 250 }) 251 252 if err != nil { 253 return 0, err 254 } 255 256 return uint64(len(ret)), nil 257 } 258 259 func (cf *configFileStore) CountConfigFileEachGroup() (map[string]map[string]int64, error) { 260 values, err := cf.handler.LoadValuesAll(tblConfigFile, &model.ConfigFile{}) 261 if err != nil { 262 return nil, err 263 } 264 265 ret := make(map[string]map[string]int64) 266 for i := range values { 267 file := values[i].(*model.ConfigFile) 268 if !file.Valid { 269 continue 270 } 271 if _, ok := ret[file.Namespace]; !ok { 272 ret[file.Namespace] = map[string]int64{} 273 } 274 if _, ok := ret[file.Namespace][file.Group]; !ok { 275 ret[file.Namespace][file.Group] = 0 276 } 277 ret[file.Namespace][file.Group] = ret[file.Namespace][file.Group] + 1 278 } 279 280 return ret, nil 281 } 282 283 // doConfigFilePage 进行分页 284 func doConfigFilePage(ret map[string]interface{}, offset, limit uint32) []*model.ConfigFile { 285 files := make([]*model.ConfigFile, 0, len(ret)) 286 beginIndex := offset 287 endIndex := beginIndex + limit 288 totalCount := uint32(len(ret)) 289 290 if totalCount == 0 { 291 return files 292 } 293 if beginIndex >= endIndex { 294 return files 295 } 296 if beginIndex >= totalCount { 297 return files 298 } 299 if endIndex > totalCount { 300 endIndex = totalCount 301 } 302 for k := range ret { 303 files = append(files, ret[k].(*model.ConfigFile)) 304 } 305 306 sort.Slice(files, func(i, j int) bool { 307 return files[i].Id > files[j].Id 308 }) 309 310 return files[beginIndex:endIndex] 311 }