github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/config/config.go (about) 1 // Copyright © 2021 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package config 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "path/filepath" 22 "strings" 23 24 k8sv1 "k8s.io/api/core/v1" 25 26 "github.com/alibaba/sealer/common" 27 28 v2 "github.com/alibaba/sealer/types/api/v2" 29 30 "gopkg.in/yaml.v3" 31 k8sYaml "sigs.k8s.io/yaml" 32 33 "github.com/alibaba/sealer/logger" 34 v1 "github.com/alibaba/sealer/types/api/v1" 35 "github.com/alibaba/sealer/utils" 36 ) 37 38 /* 39 config in Clusterfile: 40 41 apiVersion: sealer.aliyun.com/v1alpha1 42 kind: Config 43 metadata: 44 name: redis-config 45 spec: 46 path: etc/redis-config.yaml 47 data: | 48 redis-user: root 49 redis-passwd: xxx 50 51 Dump will dump the config to etc/redis-config.yaml file 52 */ 53 54 const ( 55 Merge = "merge" 56 Overwrite = "overwrite" 57 ) 58 59 type Interface interface { 60 // Dump Config in Clusterfile to the cluster rootfs disk 61 Dump(configs []v1.Config) error 62 } 63 64 type Dumper struct { 65 Configs []v1.Config 66 Cluster *v2.Cluster 67 } 68 69 func NewConfiguration(cluster *v2.Cluster) Interface { 70 return &Dumper{ 71 Cluster: cluster, 72 } 73 } 74 75 func (c *Dumper) Dump(configs []v1.Config) error { 76 if configs == nil { 77 logger.Debug("clusterfile config is empty!") 78 return nil 79 } 80 /* configs, err := utils.DecodeConfigs(clusterfile) 81 if err != nil { 82 return fmt.Errorf("failed to dump config %v", err) 83 }*/ 84 c.Configs = configs 85 if err := c.WriteFiles(); err != nil { 86 return fmt.Errorf("failed to write config files %v", err) 87 } 88 return nil 89 } 90 91 func (c *Dumper) WriteFiles() (err error) { 92 if c.Configs == nil { 93 logger.Debug("empty config found") 94 return nil 95 } 96 for _, config := range c.Configs { 97 configData := []byte(config.Spec.Data) 98 mountRoot := filepath.Join(common.DefaultClusterRootfsDir, c.Cluster.Name, "mount") 99 mountDirs, err := ioutil.ReadDir(mountRoot) 100 if err != nil { 101 return err 102 } 103 convertSecret := strings.Contains(config.Spec.Process, toSecretProcessorName) 104 for _, f := range mountDirs { 105 if !f.IsDir() { 106 continue 107 } 108 configPath := filepath.Join(mountRoot, f.Name(), config.Spec.Path) 109 if convertSecret { 110 if configData, err = convertSecretYaml(config, configPath); err != nil { 111 return fmt.Errorf("faild to convert to secret file: %v", err) 112 } 113 } 114 //only the YAML format is supported 115 if utils.IsExist(configPath) && !convertSecret && config.Spec.Strategy == Merge { 116 if configData, err = getMergeConfigData(configPath, configData); err != nil { 117 return err 118 } 119 } 120 121 err = utils.WriteFile(configPath, configData) 122 if err != nil { 123 return fmt.Errorf("write config file failed %v", err) 124 } 125 } 126 } 127 128 return nil 129 } 130 131 //merge the contents of data into the path file 132 func getMergeConfigData(path string, data []byte) ([]byte, error) { 133 var configs [][]byte 134 context, err := ioutil.ReadFile(filepath.Clean(path)) 135 if err != nil { 136 return nil, err 137 } 138 mergeConfigMap := make(map[string]interface{}) 139 err = yaml.Unmarshal(data, &mergeConfigMap) 140 if err != nil { 141 return nil, fmt.Errorf("failed to unmarshal merge map: %v", err) 142 } 143 for _, rawCfgData := range bytes.Split(context, []byte("---\n")) { 144 configMap := make(map[string]interface{}) 145 err = yaml.Unmarshal(rawCfgData, &configMap) 146 if err != nil { 147 return nil, fmt.Errorf("failed to unmarshal config: %v", err) 148 } 149 if len(configMap) == 0 { 150 continue 151 } 152 deepMerge(&configMap, &mergeConfigMap) 153 cfg, err := k8sYaml.Marshal(&configMap) 154 if err != nil { 155 return nil, err 156 } 157 configs = append(configs, cfg) 158 } 159 return bytes.Join(configs, []byte("\n---\n")), nil 160 } 161 162 func deepMerge(dst, src *map[string]interface{}) { 163 for srcK, srcV := range *src { 164 dstV, ok := (*dst)[srcK] 165 if !ok { 166 continue 167 } 168 dV, ok := dstV.(map[string]interface{}) 169 // dstV is string type 170 if !ok { 171 (*dst)[srcK] = srcV 172 continue 173 } 174 sV, ok := srcV.(map[string]interface{}) 175 if !ok { 176 continue 177 } 178 deepMerge(&dV, &sV) 179 (*dst)[srcK] = dV 180 } 181 } 182 183 func convertSecretYaml(config v1.Config, configPath string) ([]byte, error) { 184 secret := k8sv1.Secret{} 185 dataMap := make(map[string]string) 186 if err := k8sYaml.Unmarshal([]byte(config.Spec.Data), &dataMap); err != nil { 187 return nil, err 188 } 189 if utils.IsExist(configPath) { 190 rawData, err := ioutil.ReadFile(filepath.Clean(configPath)) 191 if err != nil { 192 return nil, err 193 } 194 if err = k8sYaml.Unmarshal(rawData, &secret); err != nil { 195 return nil, err 196 } 197 } 198 if secret.Data == nil { 199 secret.Data = make(map[string][]byte) 200 } 201 //set secret data 202 for k, v := range dataMap { 203 v := []byte(v) 204 secret.Data[k] = v 205 } 206 return k8sYaml.Marshal(secret) 207 }