github.com/polarismesh/polaris@v1.17.8/config/utils.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  	"archive/zip"
    22  	"bytes"
    23  	"crypto/md5"
    24  	"encoding/hex"
    25  	"encoding/json"
    26  	"errors"
    27  	"fmt"
    28  	"path"
    29  	"regexp"
    30  	"strconv"
    31  	"strings"
    32  	"unicode/utf8"
    33  
    34  	"github.com/golang/protobuf/ptypes/wrappers"
    35  	apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage"
    36  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    37  
    38  	api "github.com/polarismesh/polaris/common/api/v1"
    39  	"github.com/polarismesh/polaris/common/model"
    40  	"github.com/polarismesh/polaris/common/utils"
    41  )
    42  
    43  var (
    44  	regSourceName = regexp.MustCompile(`^[\dA-Za-z-.:_]+$`)
    45  	regFileName   = regexp.MustCompile(`^[\dA-Za-z-./:_]+$`)
    46  )
    47  
    48  // CheckResourceName 检查资源名称
    49  func CheckResourceName(name *wrappers.StringValue) error {
    50  	if name == nil {
    51  		return errors.New(utils.NilErrString)
    52  	}
    53  
    54  	if name.GetValue() == "" {
    55  		return errors.New(utils.EmptyErrString)
    56  	}
    57  
    58  	if ok := regSourceName.MatchString(name.GetValue()); !ok {
    59  		return errors.New("name contains invalid character")
    60  	}
    61  
    62  	return nil
    63  }
    64  
    65  // CheckFileName 校验文件名
    66  func CheckFileName(name *wrappers.StringValue) error {
    67  	if name == nil {
    68  		return errors.New(utils.NilErrString)
    69  	}
    70  
    71  	if name.GetValue() == "" {
    72  		return errors.New(utils.EmptyErrString)
    73  	}
    74  
    75  	if ok := regFileName.MatchString(name.GetValue()); !ok {
    76  		return errors.New("name contains invalid character")
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  // CalMd5 计算md5值
    83  func CalMd5(content string) string {
    84  	h := md5.New()
    85  	h.Write([]byte(content))
    86  	return hex.EncodeToString(h.Sum(nil))
    87  }
    88  
    89  // CheckContentLength 校验文件内容长度
    90  func CheckContentLength(content string, max int) error {
    91  	if utf8.RuneCountInString(content) > max {
    92  		return fmt.Errorf("content length too long. max length =%d", max)
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  // GenConfigFileResponse 为客户端生成响应对象
    99  func GenConfigFileResponse(namespace, group, fileName, content, md5str string,
   100  	version uint64) *apiconfig.ConfigClientResponse {
   101  	configFile := &apiconfig.ClientConfigFileInfo{
   102  		Namespace: utils.NewStringValue(namespace),
   103  		Group:     utils.NewStringValue(group),
   104  		FileName:  utils.NewStringValue(fileName),
   105  		Content:   utils.NewStringValue(content),
   106  		Version:   utils.NewUInt64Value(version),
   107  		Md5:       utils.NewStringValue(md5str),
   108  	}
   109  	return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, configFile)
   110  }
   111  
   112  type kv struct {
   113  	Key   string
   114  	Value string
   115  }
   116  
   117  // ToTagJsonStr 把 Tags 转化成 Json 字符串
   118  func ToTagJsonStr(tags []*apiconfig.ConfigFileTag) string {
   119  	if len(tags) == 0 {
   120  		return "[]"
   121  	}
   122  	kvs := make([]kv, 0, len(tags))
   123  	for _, tag := range tags {
   124  		kvs = append(kvs, kv{
   125  			Key:   tag.Key.GetValue(),
   126  			Value: tag.Value.GetValue(),
   127  		})
   128  	}
   129  
   130  	ret, err := json.Marshal(kvs)
   131  	if err != nil {
   132  		return "[]"
   133  	}
   134  	return string(ret)
   135  }
   136  
   137  // FromTagJson 从 Tags Json 字符串里反序列化出 Tags
   138  func FromTagJson(tagStr string) []*apiconfig.ConfigFileTag {
   139  	if tagStr == "" {
   140  		return nil
   141  	}
   142  
   143  	kvs := make([]kv, 0, 10)
   144  	err := json.Unmarshal([]byte(tagStr), &kvs)
   145  	if err != nil {
   146  		return nil
   147  	}
   148  
   149  	tags := make([]*apiconfig.ConfigFileTag, 0, len(kvs))
   150  	for _, val := range kvs {
   151  		tags = append(tags, &apiconfig.ConfigFileTag{
   152  			Key:   utils.NewStringValue(val.Key),
   153  			Value: utils.NewStringValue(val.Value),
   154  		})
   155  	}
   156  
   157  	return tags
   158  }
   159  
   160  // GenReleaseName 生成发布名称,规则是 filename-${三位自增长序列}
   161  func GenReleaseName(oldReleaseName, fileName string) string {
   162  	if oldReleaseName == "" {
   163  		return fileName + "-001"
   164  	}
   165  
   166  	nameInfo := strings.Split(oldReleaseName, "-")
   167  	if len(nameInfo) != 2 {
   168  		return oldReleaseName
   169  	}
   170  
   171  	if fileName != nameInfo[0] {
   172  		return oldReleaseName
   173  	}
   174  
   175  	num, err := strconv.ParseInt(nameInfo[1], 10, 64)
   176  	if err != nil {
   177  		return oldReleaseName
   178  	}
   179  
   180  	return fileName + "-" + strings.ReplaceAll(fmt.Sprintf("%3d", num+1), " ", "0")
   181  }
   182  
   183  func CompressConfigFiles(files []*model.ConfigFile,
   184  	fileID2Tags map[uint64][]*model.ConfigFileTag, isExportGroup bool) (*bytes.Buffer, error) {
   185  	var buf bytes.Buffer
   186  	w := zip.NewWriter(&buf)
   187  	defer w.Close()
   188  
   189  	var configFileMetas = make(map[string]*utils.ConfigFileMeta)
   190  	for _, file := range files {
   191  		fileName := file.Name
   192  		if isExportGroup {
   193  			fileName = path.Join(file.Group, file.Name)
   194  		}
   195  
   196  		configFileMetas[fileName] = &utils.ConfigFileMeta{
   197  			Tags:    make(map[string]string),
   198  			Comment: file.Comment,
   199  		}
   200  		for _, tag := range fileID2Tags[file.Id] {
   201  			configFileMetas[fileName].Tags[tag.Key] = tag.Value
   202  		}
   203  		f, err := w.Create(fileName)
   204  		if err != nil {
   205  			return nil, err
   206  		}
   207  		if _, err := f.Write([]byte(file.Content)); err != nil {
   208  			return nil, err
   209  		}
   210  	}
   211  	// 生成配置元文件
   212  	f, err := w.Create(utils.ConfigFileMetaFileName)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	data, err := json.MarshalIndent(configFileMetas, "", "\t")
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	if _, err := f.Write(data); err != nil {
   221  		return nil, err
   222  	}
   223  	return &buf, nil
   224  }