blob: 04d94e2bc20ea316983c53e14b9130be746d57b7 [file] [log] [blame]
// Copyright Istio Authors
//
// 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 (
"flag"
"fmt"
"os"
"strings"
)
import (
"go.uber.org/atomic"
"gopkg.in/yaml.v3"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test/scopes"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/file"
)
const prefix = "istio.test"
var (
configFilePath string
parsed atomic.Bool
)
func init() {
flag.StringVar(&configFilePath, "istio.test.config", "", "Path to test framework config file")
}
type Value interface {
flag.Value
// SetConfig will receive either a Map or a []Map
SetConfig(interface{}) error
}
func Parsed() bool {
return flag.Parsed() && parsed.Load()
}
// Parse overrides any unset command line flags beginning with "istio.test" with values provided
// from a YAML file.
func Parse() {
defer func() {
parsed.Store(true)
}()
if !flag.Parsed() {
flag.Parse()
}
if configFilePath == "" {
return
}
cfg, err := readConfig()
if err != nil {
scopes.Framework.Error(err)
return
}
set := map[string]struct{}{}
flag.Visit(func(f *flag.Flag) {
set[f.Name] = struct{}{}
})
flag.VisitAll(func(f *flag.Flag) {
var err error
defer func() {
if err != nil {
scopes.Framework.Errorf("failed getting %s from config file: %v", f.Name, err)
}
}()
// exclude non-istio flags and flags that were set via command line
if !strings.HasPrefix(f.Name, prefix) {
return
}
if _, ok := set[f.Name]; ok {
return
}
// grab the map containing the last "." separated key
keys := strings.Split(f.Name, ".")
parentPath, key := keys[:len(keys)-1], keys[len(keys)-1]
parent := cfg
for _, k := range parentPath {
parent = parent.Map(k)
if parent == nil {
return
}
}
// if the registered flag implements config.Value, and is a non-string type, we can do fancy custom parsing
cfgValue, isCfgVal := f.Value.(Value)
if cfgMap := parent.Map(key); isCfgVal && len(cfgMap) > 0 {
err = cfgValue.SetConfig(cfgMap)
} else if cfgSlice := parent.Slice(key); isCfgVal && len(cfgSlice) > 0 {
err = cfgValue.SetConfig(cfgSlice)
} else if v := parent.String(key); v != "" {
// otherwise parse via string (if-set)
err = f.Value.Set(v)
}
})
}
func readConfig() (Map, error) {
path, err := file.NormalizePath(configFilePath)
if err != nil {
return nil, fmt.Errorf("failed normalizing config file path %q: %v", configFilePath, err)
}
bytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed reading %s: %v", path, err)
}
cfg := Map{}
if err := yaml.Unmarshal(bytes, cfg); err != nil {
return nil, fmt.Errorf("failed unmarshalling %s: %v", path, err)
}
return cfg, nil
}