mirror of
https://github.com/alibaba/higress.git
synced 2026-03-13 13:10:49 +08:00
120 lines
4.0 KiB
Go
120 lines
4.0 KiB
Go
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
. "github.com/alibaba/higress/pkg/ingress/log"
|
|
"google.golang.org/protobuf/proto"
|
|
"istio.io/istio/pkg/config"
|
|
)
|
|
|
|
// TemplateProcessor handles template substitution in configs
|
|
type TemplateProcessor struct {
|
|
// getValue is a function that retrieves values by type, namespace, name and key
|
|
getValue func(valueType, namespace, name, key string) (string, error)
|
|
namespace string
|
|
secretConfigMgr *SecretConfigMgr
|
|
}
|
|
|
|
// NewTemplateProcessor creates a new TemplateProcessor with the given value getter function
|
|
func NewTemplateProcessor(getValue func(valueType, namespace, name, key string) (string, error), namespace string, secretConfigMgr *SecretConfigMgr) *TemplateProcessor {
|
|
return &TemplateProcessor{
|
|
getValue: getValue,
|
|
namespace: namespace,
|
|
secretConfigMgr: secretConfigMgr,
|
|
}
|
|
}
|
|
|
|
// ProcessConfig processes a config and substitutes any template variables
|
|
func (p *TemplateProcessor) ProcessConfig(cfg *config.Config) error {
|
|
// Convert spec to JSON string to process substitutions
|
|
jsonBytes, err := json.Marshal(cfg.Spec)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal config spec: %v", err)
|
|
}
|
|
|
|
configStr := string(jsonBytes)
|
|
// Find all value references in format:
|
|
// ${type.name.key} or ${type.namespace/name.key}
|
|
valueRegex := regexp.MustCompile(`\$\{([^.}]+)\.(?:([^/]+)/)?([^.}]+)\.([^}]+)\}`)
|
|
matches := valueRegex.FindAllStringSubmatch(configStr, -1)
|
|
// If there are no value references, return immediately
|
|
if len(matches) == 0 {
|
|
if p.secretConfigMgr != nil {
|
|
if err := p.secretConfigMgr.DeleteConfig(cfg); err != nil {
|
|
IngressLog.Errorf("failed to delete secret dependency: %v", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
foundSecretSource := false
|
|
IngressLog.Infof("start to apply config %s/%s with %d variables", cfg.Namespace, cfg.Name, len(matches))
|
|
for _, match := range matches {
|
|
valueType := match[1]
|
|
var namespace, name, key string
|
|
if match[2] != "" {
|
|
// Format: ${type.namespace/name.key}
|
|
namespace = match[2]
|
|
} else {
|
|
// Format: ${type.name.key} - use default namespace
|
|
namespace = p.namespace
|
|
}
|
|
name = match[3]
|
|
key = match[4]
|
|
|
|
// Get value using the provided getter function
|
|
value, err := p.getValue(valueType, namespace, name, key)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get %s value for %s/%s.%s: %v", valueType, namespace, name, key, err)
|
|
}
|
|
|
|
// Add secret dependency if this is a secret reference
|
|
if valueType == "secret" && p.secretConfigMgr != nil {
|
|
foundSecretSource = true
|
|
secretKey := fmt.Sprintf("%s/%s", namespace, name)
|
|
if err := p.secretConfigMgr.AddConfig(secretKey, cfg); err != nil {
|
|
IngressLog.Errorf("failed to add secret dependency: %v", err)
|
|
}
|
|
}
|
|
// Replace placeholder with actual value
|
|
configStr = strings.Replace(configStr, match[0], value, 1)
|
|
}
|
|
|
|
// Create a new instance of the same type as cfg.Spec
|
|
newSpec := proto.Clone(cfg.Spec.(proto.Message))
|
|
if err := json.Unmarshal([]byte(configStr), newSpec); err != nil {
|
|
return fmt.Errorf("failed to unmarshal substituted config: %v", err)
|
|
}
|
|
cfg.Spec = newSpec
|
|
|
|
// Delete secret dependency if no secret reference is found
|
|
if !foundSecretSource {
|
|
if p.secretConfigMgr != nil {
|
|
if err := p.secretConfigMgr.DeleteConfig(cfg); err != nil {
|
|
IngressLog.Errorf("failed to delete secret dependency: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
IngressLog.Infof("end to process config %s/%s", cfg.Namespace, cfg.Name)
|
|
return nil
|
|
}
|