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  }