github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/unstructured/yaml_config.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package unstructured
    21  
    22  import (
    23  	"strings"
    24  
    25  	mxjv2 "github.com/clbanning/mxj/v2"
    26  	"github.com/spf13/cast"
    27  	"gopkg.in/yaml.v2"
    28  
    29  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    30  )
    31  
    32  type yamlConfig struct {
    33  	name   string
    34  	config map[string]any
    35  }
    36  
    37  func init() {
    38  	CfgObjectRegistry().RegisterConfigCreator(appsv1alpha1.YAML, func(name string) ConfigObject {
    39  		return &yamlConfig{name: name}
    40  	})
    41  }
    42  
    43  func (y *yamlConfig) Update(key string, value any) error {
    44  	path := strings.Split(key, ".")
    45  	lastKey := path[len(path)-1]
    46  	deepestMap := checkAndCreateNestedPrefixMap(y.config, path[0:len(path)-1])
    47  	deepestMap[lastKey] = value
    48  	return nil
    49  }
    50  
    51  func (y *yamlConfig) RemoveKey(key string) error {
    52  	var m mxjv2.Map = y.config
    53  	_ = m.Remove(key)
    54  	return nil
    55  }
    56  
    57  func (y *yamlConfig) Get(key string) any {
    58  	keys := strings.Split(key, ".")
    59  	return searchMap(y.config, keys)
    60  }
    61  
    62  func (y *yamlConfig) GetString(key string) (string, error) {
    63  	v := y.Get(key)
    64  	if v != nil {
    65  		return cast.ToStringE(v)
    66  	}
    67  	return "", nil
    68  }
    69  
    70  func (y *yamlConfig) GetAllParameters() map[string]any {
    71  	return y.config
    72  }
    73  
    74  func (y *yamlConfig) SubConfig(key string) ConfigObject {
    75  	v := y.Get(key)
    76  	if m, ok := v.(map[string]any); ok {
    77  		return &yamlConfig{
    78  			name:   y.name,
    79  			config: m,
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  func (y *yamlConfig) Marshal() (string, error) {
    86  	b, err := yaml.Marshal(y.config)
    87  	return string(b), err
    88  }
    89  
    90  func (y *yamlConfig) Unmarshal(str string) error {
    91  	config := make(map[any]any)
    92  	err := yaml.Unmarshal([]byte(str), config)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	y.config = transKeyStringMap(config)
    97  	return nil
    98  }
    99  
   100  func checkAndCreateNestedPrefixMap(m map[string]any, path []string) map[string]any {
   101  	for _, k := range path {
   102  		m2, ok := m[k]
   103  		// if the key is not existed, create a new map
   104  		if !ok {
   105  			m3 := make(map[string]any)
   106  			m[k] = m3
   107  			m = m3
   108  			continue
   109  		}
   110  		m3, ok := m2.(map[string]any)
   111  		// if the type is not map, replace it with a new map
   112  		if !ok {
   113  			m3 = make(map[string]any)
   114  			m[k] = m3
   115  		}
   116  		m = m3
   117  	}
   118  	return m
   119  }
   120  
   121  func searchMap(m map[string]any, path []string) any {
   122  	if len(path) == 0 {
   123  		return m
   124  	}
   125  
   126  	next, ok := m[path[0]]
   127  	if !ok {
   128  		return nil
   129  	}
   130  	if len(path) == 1 {
   131  		return next
   132  	}
   133  	switch t := next.(type) {
   134  	default:
   135  		return nil
   136  	case map[any]any:
   137  		return searchMap(cast.ToStringMap(t), path[1:])
   138  	case map[string]any:
   139  		return searchMap(t, path[1:])
   140  	}
   141  }
   142  
   143  func transKeyStringMap(m map[any]any) map[string]any {
   144  	m2 := make(map[string]any, len(m))
   145  	for k, v := range m {
   146  		switch k := k.(type) {
   147  		case string:
   148  			m2[k] = convert(v)
   149  		default:
   150  			m2[cast.ToString(k)] = convert(v)
   151  		}
   152  	}
   153  	return m2
   154  }
   155  
   156  func convert(v any) any {
   157  	switch t := v.(type) {
   158  	case map[any]any:
   159  		return transKeyStringMap(t)
   160  	case []any:
   161  		for i, vv := range t {
   162  			t[i] = convert(vv)
   163  		}
   164  	}
   165  	return v
   166  }