plugins/wasm-go/extensions/frontend-gray/config/config.go (190 lines of code) (raw):
package config
import (
"errors"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/tidwall/gjson"
)
const (
XHigressTag = "x-higress-tag"
PreHigressVersion = "pre-higress-version"
IsHtmlRequest = "is-html-request"
IsIndexRequest = "is-index-request"
EnabledGray = "enabled-gray"
)
type GrayRule struct {
Name string
GrayKeyValue []string
GrayTagKey string
GrayTagValue []string
}
type Deployment struct {
Name string
Enabled bool
Version string
BackendVersion string
Weight int
VersionPredicates map[string]string
}
type Rewrite struct {
Host string
Index map[string]string
File map[string]string
}
type Injection struct {
GlobalConfig *GlobalConfig
Head []string
Body *BodyInjection
}
type GlobalConfig struct {
Key string
FeatureKey string
Value string
Enabled bool
}
type BodyInjection struct {
First []string
Last []string
}
type GrayConfig struct {
StoreMaxAge int
GrayKey string
LocalStorageGrayKey string
GraySubKey string
Rules []*GrayRule
Rewrite *Rewrite
Html string
BaseDeployment *Deployment
GrayDeployments []*Deployment
BackendGrayTag string
UniqueGrayTag string
Injection *Injection
SkippedPaths []string
SkippedByHeaders map[string]string
IndexPaths []string
GrayWeight int
}
func isValidName(s string) bool {
// 定义一个正则表达式,匹配字母、数字和下划线
re := regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
return re.MatchString(s)
}
func GetWithDefault(json gjson.Result, path, defaultValue string) string {
res := json.Get(path)
if res.Exists() {
return res.String()
}
return defaultValue
}
func convertToStringList(results []gjson.Result) []string {
interfaces := make([]string, len(results)) // 预分配切片容量
for i, result := range results {
interfaces[i] = result.String() // 使用 String() 方法直接获取字符串
}
return interfaces
}
func compatibleConvertToStringList(results []gjson.Result, compatibleResults []gjson.Result) []string {
// 优先使用兼容模式的数据
if len(compatibleResults) == 0 {
interfaces := make([]string, len(results)) // 预分配切片容量
for i, result := range results {
interfaces[i] = result.String() // 使用 String() 方法直接获取字符串
}
return interfaces
}
compatibleInterfaces := make([]string, len(compatibleResults)) // 预分配切片容量
for i, result := range compatibleResults {
compatibleInterfaces[i] = filepath.Join(result.String(), "**")
}
return compatibleInterfaces
}
func convertToStringMap(result gjson.Result) map[string]string {
m := make(map[string]string)
result.ForEach(func(key, value gjson.Result) bool {
m[key.String()] = value.String()
return true // keep iterating
})
return m
}
func JsonToGrayConfig(json gjson.Result, grayConfig *GrayConfig) error {
// 解析 GrayKey
grayConfig.LocalStorageGrayKey = json.Get("localStorageGrayKey").String()
grayConfig.GrayKey = json.Get("grayKey").String()
if grayConfig.LocalStorageGrayKey != "" {
grayConfig.GrayKey = grayConfig.LocalStorageGrayKey
}
grayConfig.GraySubKey = json.Get("graySubKey").String()
grayConfig.BackendGrayTag = GetWithDefault(json, "backendGrayTag", "x-mse-tag")
grayConfig.UniqueGrayTag = GetWithDefault(json, "uniqueGrayTag", "x-higress-uid")
grayConfig.StoreMaxAge = 60 * 60 * 24 * 365 // 默认一年
storeMaxAge, err := strconv.Atoi(GetWithDefault(json, "StoreMaxAge", strconv.Itoa(grayConfig.StoreMaxAge)))
if err != nil {
grayConfig.StoreMaxAge = storeMaxAge
}
grayConfig.Html = json.Get("html").String()
grayConfig.SkippedPaths = compatibleConvertToStringList(json.Get("skippedPaths").Array(), json.Get("skippedPathPrefixes").Array())
grayConfig.IndexPaths = compatibleConvertToStringList(json.Get("indexPaths").Array(), json.Get("includePathPrefixes").Array())
grayConfig.SkippedByHeaders = convertToStringMap(json.Get("skippedByHeaders"))
// 解析 Rules
rules := json.Get("rules").Array()
for _, rule := range rules {
grayRule := GrayRule{
Name: rule.Get("name").String(),
GrayKeyValue: convertToStringList(rule.Get("grayKeyValue").Array()),
GrayTagKey: rule.Get("grayTagKey").String(),
GrayTagValue: convertToStringList(rule.Get("grayTagValue").Array()),
}
grayConfig.Rules = append(grayConfig.Rules, &grayRule)
}
grayConfig.Rewrite = &Rewrite{
Host: json.Get("rewrite.host").String(),
Index: convertToStringMap(json.Get("rewrite.indexRouting")),
File: convertToStringMap(json.Get("rewrite.fileRouting")),
}
// 解析 deployment
baseDeployment := json.Get("baseDeployment")
grayDeployments := json.Get("grayDeployments").Array()
grayConfig.BaseDeployment = &Deployment{
Name: baseDeployment.Get("name").String(),
BackendVersion: baseDeployment.Get("backendVersion").String(),
Version: strings.Trim(baseDeployment.Get("version").String(), " "),
VersionPredicates: convertToStringMap(baseDeployment.Get("versionPredicates")),
}
for _, item := range grayDeployments {
if !item.Get("enabled").Bool() {
continue
}
weight := int(item.Get("weight").Int())
grayConfig.GrayDeployments = append(grayConfig.GrayDeployments, &Deployment{
Name: item.Get("name").String(),
Enabled: item.Get("enabled").Bool(),
Version: strings.Trim(item.Get("version").String(), " "),
BackendVersion: item.Get("backendVersion").String(),
Weight: weight,
VersionPredicates: convertToStringMap(item.Get("versionPredicates")),
})
if weight > 0 {
grayConfig.GrayWeight = weight
break
}
}
injectGlobalFeatureKey := GetWithDefault(json, "injection.globalConfig.featureKey", "FEATURE_STATUS")
injectGlobalKey := GetWithDefault(json, "injection.globalConfig.key", "HIGRESS_CONSOLE_CONFIG")
if !isValidName(injectGlobalFeatureKey) {
return errors.New("injection.globalConfig.featureKey is invalid")
}
if !isValidName(injectGlobalKey) {
return errors.New("injection.globalConfig.featureKey is invalid")
}
grayConfig.Injection = &Injection{
Head: convertToStringList(json.Get("injection.head").Array()),
Body: &BodyInjection{
First: convertToStringList(json.Get("injection.body.first").Array()),
Last: convertToStringList(json.Get("injection.body.last").Array()),
},
GlobalConfig: &GlobalConfig{
FeatureKey: injectGlobalFeatureKey,
Key: injectGlobalKey,
Value: json.Get("injection.globalConfig.value").String(),
Enabled: json.Get("injection.globalConfig.enabled").Bool(),
},
}
return nil
}