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 }