mirror of
https://github.com/kubesphere/kubekey.git
synced 2025-12-25 17:12:50 +00:00
feat: --set contains multi string "=". (#2351)
* fix: --set contains multi string "=". Signed-off-by: joyceliu <joyceliu@yunify.com> * fix: --set contains multi string "=". Signed-off-by: joyceliu <joyceliu@yunify.com> --------- Signed-off-by: joyceliu <joyceliu@yunify.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
parent
4a060a91fe
commit
1aa519d295
|
|
@ -84,7 +84,7 @@ func (o *CommonOptions) Flags() cliflag.NamedFlagSets {
|
|||
gfs.StringVar(&o.WorkDir, "work-dir", o.WorkDir, "the base Dir for kubekey. Default current dir. ")
|
||||
gfs.StringVarP(&o.Artifact, "artifact", "a", "", "Path to a KubeKey artifact")
|
||||
gfs.StringVarP(&o.ConfigFile, "config", "c", o.ConfigFile, "the config file path. support *.yaml ")
|
||||
gfs.StringArrayVar(&o.Set, "set", o.Set, "set value in config. format --set key=val")
|
||||
gfs.StringArrayVar(&o.Set, "set", o.Set, "set value in config. format --set key=val or --set k1=v1,k2=v2")
|
||||
gfs.StringVarP(&o.InventoryFile, "inventory", "i", o.InventoryFile, "the host list file path. support *.ini")
|
||||
gfs.BoolVarP(&o.Debug, "debug", "d", o.Debug, "Debug mode, after a successful execution of Pipeline, will retain runtime data, which includes task execution status and parameters.")
|
||||
gfs.StringVarP(&o.Namespace, "namespace", "n", o.Namespace, "the namespace which pipeline will be executed, all reference resources(pipeline, config, inventory, task) should in the same namespace")
|
||||
|
|
@ -100,34 +100,10 @@ func (o *CommonOptions) completeRef(pipeline *kubekeyv1.Pipeline) (*kubekeyv1.Co
|
|||
o.WorkDir = filepath.Join(wd, o.WorkDir)
|
||||
}
|
||||
|
||||
config, err := genConfig(o.ConfigFile)
|
||||
config, err := o.genConfig()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("generate config error: %w", err)
|
||||
}
|
||||
config.Namespace = o.Namespace
|
||||
if wd, err := config.GetValue("work_dir"); err == nil && wd != nil {
|
||||
// if work_dir is defined in config, use it. otherwise use current dir.
|
||||
o.WorkDir = wd.(string)
|
||||
} else if err := config.SetValue("work_dir", o.WorkDir); err != nil {
|
||||
return nil, nil, fmt.Errorf("work_dir to config error: %w", err)
|
||||
}
|
||||
if o.Artifact != "" {
|
||||
// override artifact_file in config
|
||||
if err := config.SetValue("artifact_file", o.Artifact); err != nil {
|
||||
return nil, nil, fmt.Errorf("artifact file to config error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range o.Set {
|
||||
s = unescapeString(s)
|
||||
ss := strings.Split(s, "=")
|
||||
if len(ss) != 2 {
|
||||
return nil, nil, fmt.Errorf("--set value should be k=v")
|
||||
}
|
||||
if err := setValue(config, ss[0], ss[1]); err != nil {
|
||||
return nil, nil, fmt.Errorf("--set value to config error: %w", err)
|
||||
}
|
||||
}
|
||||
pipeline.Spec.ConfigRef = &corev1.ObjectReference{
|
||||
Kind: config.Kind,
|
||||
Namespace: config.Namespace,
|
||||
|
|
@ -137,11 +113,10 @@ func (o *CommonOptions) completeRef(pipeline *kubekeyv1.Pipeline) (*kubekeyv1.Co
|
|||
ResourceVersion: config.ResourceVersion,
|
||||
}
|
||||
|
||||
inventory, err := genInventory(o.InventoryFile)
|
||||
inventory, err := o.genInventory()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("generate inventory error: %w", err)
|
||||
}
|
||||
inventory.Namespace = o.Namespace
|
||||
pipeline.Spec.InventoryRef = &corev1.ObjectReference{
|
||||
Kind: inventory.Kind,
|
||||
Namespace: inventory.Namespace,
|
||||
|
|
@ -154,40 +129,77 @@ func (o *CommonOptions) completeRef(pipeline *kubekeyv1.Pipeline) (*kubekeyv1.Co
|
|||
return config, inventory, nil
|
||||
}
|
||||
|
||||
func genConfig(configFile string) (*kubekeyv1.Config, error) {
|
||||
if configFile != "" {
|
||||
cdata, err := os.ReadFile(configFile)
|
||||
// genConfig generate config by ConfigFile and set value by command args.
|
||||
func (o *CommonOptions) genConfig() (*kubekeyv1.Config, error) {
|
||||
config := defaultConfig.DeepCopy()
|
||||
if o.ConfigFile != "" {
|
||||
cdata, err := os.ReadFile(o.ConfigFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read config file error: %w", err)
|
||||
}
|
||||
config := &kubekeyv1.Config{}
|
||||
config = &kubekeyv1.Config{}
|
||||
if err := yaml.Unmarshal(cdata, config); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal config file error: %w", err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
// set by command args
|
||||
if o.Namespace != "" {
|
||||
config.Namespace = o.Namespace
|
||||
}
|
||||
if wd, err := config.GetValue("work_dir"); err == nil && wd != nil {
|
||||
// if work_dir is defined in config, use it. otherwise use current dir.
|
||||
o.WorkDir = wd.(string)
|
||||
} else if err := config.SetValue("work_dir", o.WorkDir); err != nil {
|
||||
return nil, fmt.Errorf("work_dir to config error: %w", err)
|
||||
}
|
||||
if o.Artifact != "" {
|
||||
// override artifact_file in config
|
||||
if err := config.SetValue("artifact_file", o.Artifact); err != nil {
|
||||
return nil, fmt.Errorf("artifact file to config error: %w", err)
|
||||
}
|
||||
}
|
||||
for _, s := range o.Set {
|
||||
for _, setVal := range strings.Split(unescapeString(s), ",") {
|
||||
i := strings.Index(setVal, "=")
|
||||
if i == 0 || i == -1 {
|
||||
return nil, fmt.Errorf("--set value should be k=v")
|
||||
}
|
||||
if err := setValue(config, setVal[:i], setVal[i+1:]); err != nil {
|
||||
return nil, fmt.Errorf("--set value to config error: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultConfig, nil
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func genInventory(inventoryFile string) (*kubekeyv1.Inventory, error) {
|
||||
if inventoryFile != "" {
|
||||
cdata, err := os.ReadFile(inventoryFile)
|
||||
// genConfig generate config by ConfigFile and set value by command args.
|
||||
func (o *CommonOptions) genInventory() (*kubekeyv1.Inventory, error) {
|
||||
inventory := defaultInventory.DeepCopy()
|
||||
if o.InventoryFile != "" {
|
||||
cdata, err := os.ReadFile(o.InventoryFile)
|
||||
if err != nil {
|
||||
klog.V(4).ErrorS(err, "read config file error")
|
||||
return nil, err
|
||||
}
|
||||
inventory := &kubekeyv1.Inventory{}
|
||||
inventory = &kubekeyv1.Inventory{}
|
||||
if err := yaml.Unmarshal(cdata, inventory); err != nil {
|
||||
klog.V(4).ErrorS(err, "unmarshal config file error")
|
||||
return nil, err
|
||||
}
|
||||
return inventory, nil
|
||||
}
|
||||
// set by command args
|
||||
if o.Namespace != "" {
|
||||
inventory.Namespace = o.Namespace
|
||||
}
|
||||
|
||||
return defaultInventory, nil
|
||||
return inventory, nil
|
||||
}
|
||||
|
||||
// setValue set key: val in config.
|
||||
// if val is json string. convert to map or slice
|
||||
// if val is TRUE,YES,Y. convert to bool type true.
|
||||
// if val is FALSE,NO,N. convert to bool type false.
|
||||
func setValue(config *kubekeyv1.Config, key, val string) error {
|
||||
switch {
|
||||
case strings.HasPrefix(val, "{") && strings.HasSuffix(val, "{"):
|
||||
|
|
@ -227,8 +239,8 @@ func unescapeString(s string) string {
|
|||
}
|
||||
|
||||
// Iterate over the replacements map and replace escape sequences in the string
|
||||
for old, new := range replacements {
|
||||
s = strings.ReplaceAll(s, old, new)
|
||||
for o, n := range replacements {
|
||||
s = strings.ReplaceAll(s, o, n)
|
||||
}
|
||||
|
||||
return s
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ func init() {
|
|||
SchemeBuilder.Register(&Config{}, &ConfigList{})
|
||||
}
|
||||
|
||||
// SetValue to config
|
||||
// if key contains "." (a.b), will convert map and set value (a:b:value)
|
||||
func (c *Config) SetValue(key string, value any) error {
|
||||
configMap := make(map[string]any)
|
||||
if c.Spec.Raw != nil {
|
||||
|
|
@ -77,11 +79,25 @@ func (c *Config) SetValue(key string, value any) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetValue by key
|
||||
// if key contains "." (a.b), find by the key path (if a:b:value in config.and get value)
|
||||
func (c *Config) GetValue(key string) (any, error) {
|
||||
configMap := make(map[string]any)
|
||||
if err := json.Unmarshal(c.Spec.Raw, &configMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if key == "" {
|
||||
return configMap, nil
|
||||
}
|
||||
// get value
|
||||
return configMap[key], nil
|
||||
var result any = configMap
|
||||
for _, k := range strings.Split(key, ".") {
|
||||
r, ok := result.(map[string]any)
|
||||
if !ok {
|
||||
// cannot find value
|
||||
return nil, nil
|
||||
}
|
||||
result = r[k]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,3 +59,53 @@ func TestSetValue(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetValue(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
key string
|
||||
config Config
|
||||
except any
|
||||
}{
|
||||
{
|
||||
name: "all value",
|
||||
key: "",
|
||||
config: Config{Spec: runtime.RawExtension{Raw: []byte(`{"a":1}`)}},
|
||||
except: map[string]interface{}{
|
||||
"a": int64(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "none value",
|
||||
key: "b",
|
||||
config: Config{Spec: runtime.RawExtension{Raw: []byte(`{"a":1}`)}},
|
||||
except: nil,
|
||||
},
|
||||
{
|
||||
name: "none multi value",
|
||||
key: "b.c",
|
||||
config: Config{Spec: runtime.RawExtension{Raw: []byte(`{"a":1}`)}},
|
||||
except: nil,
|
||||
},
|
||||
{
|
||||
name: "find one value",
|
||||
key: "a",
|
||||
config: Config{Spec: runtime.RawExtension{Raw: []byte(`{"a":1}`)}},
|
||||
except: int64(1),
|
||||
},
|
||||
{
|
||||
name: "find mulit value",
|
||||
key: "a.b",
|
||||
config: Config{Spec: runtime.RawExtension{Raw: []byte(`{"a":{"b":1}}`)}},
|
||||
except: int64(1),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
value, err := tc.config.GetValue(tc.key)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.except, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue