github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/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 stdos "os" 21 "path/filepath" 22 "strings" 23 24 "github.com/imdario/mergo" 25 "github.com/sirupsen/logrus" 26 "gopkg.in/yaml.v3" 27 k8sv1 "k8s.io/api/core/v1" 28 k8sYaml "sigs.k8s.io/yaml" 29 30 "github.com/sealerio/sealer/pkg/rootfs" 31 v1 "github.com/sealerio/sealer/types/api/v1" 32 "github.com/sealerio/sealer/utils/os" 33 ) 34 35 const ( 36 Merge = "merge" 37 ) 38 39 type Interface interface { 40 // Dump Configs from Clusterfile to the cluster rootfs 41 Dump(configs []v1.Config) error 42 } 43 44 type Dumper struct { 45 // rootPath typically is cluster image mounted base directory. 46 rootPath string 47 } 48 49 func NewConfiguration(rootPath string) Interface { 50 return &Dumper{ 51 rootPath: rootPath, 52 } 53 } 54 55 func (c *Dumper) Dump(configs []v1.Config) error { 56 if len(configs) == 0 { 57 logrus.Debug("no config is found") 58 return nil 59 } 60 61 if err := c.WriteFiles(configs); err != nil { 62 return fmt.Errorf("failed to dump config files %v", err) 63 } 64 return nil 65 } 66 67 func (c *Dumper) WriteFiles(configs []v1.Config) error { 68 for _, config := range configs { 69 //#nosec 70 if err := NewProcessorsAndRun(&config); err != nil { 71 return err 72 } 73 74 configData := []byte(config.Spec.Data) 75 path := config.Spec.Path 76 if config.Spec.APPName != "" { 77 path = filepath.Join(rootfs.GlobalManager.App().Root(), config.Spec.APPName, path) 78 } 79 configPath := filepath.Join(c.rootPath, path) 80 81 logrus.Debugf("dumping config:%+v\n on the target file", config) 82 if !os.IsFileExist(configPath) { 83 err := os.NewCommonWriter(configPath).WriteFile(configData) 84 if err != nil { 85 return fmt.Errorf("failed to overwrite config file %s: %v", configPath, err) 86 } 87 continue 88 } 89 90 contents, err := stdos.ReadFile(filepath.Clean(configPath)) 91 if err != nil { 92 return err 93 } 94 95 // todo: its strange to use config.Spec.Process to control config dump strategy. 96 if strings.Contains(config.Spec.Process, toSecretProcessorName) { 97 if configData, err = convertSecretYaml(contents, configData); err != nil { 98 return fmt.Errorf("faild to convert to secret file: %v", err) 99 } 100 } 101 //Only files in yaml format are supported. 102 //if Strategy is "Merge" will deeply merge each yaml file section. 103 //if not, overwrite the whole file content with config data. 104 if config.Spec.Strategy == Merge { 105 if configData, err = getMergeConfigData(contents, configData); err != nil { 106 return err 107 } 108 } 109 110 err = os.NewCommonWriter(configPath).WriteFile(configData) 111 if err != nil { 112 return fmt.Errorf("failed to write config file %s: %v", configPath, err) 113 } 114 } 115 return nil 116 } 117 118 // getMergeConfigData merge data to each section of given file with overriding. 119 // given file is must be yaml marshalled. 120 func getMergeConfigData(contents, data []byte) ([]byte, error) { 121 var ( 122 configs [][]byte 123 srcDataMap = make(map[string]interface{}) 124 ) 125 126 err := yaml.Unmarshal(data, &srcDataMap) 127 if err != nil { 128 return nil, fmt.Errorf("failed to load config data: %v", err) 129 } 130 131 for _, rawCfgData := range bytes.Split(contents, []byte("---\n")) { 132 destConfigMap := make(map[string]interface{}) 133 134 err = yaml.Unmarshal(rawCfgData, &destConfigMap) 135 if err != nil { 136 return nil, fmt.Errorf("failed to unmarshal config data: %v", err) 137 } 138 139 err = mergo.Merge(&destConfigMap, &srcDataMap, mergo.WithOverride) 140 if err != nil { 141 return nil, fmt.Errorf("failed to merge config: %v", err) 142 } 143 144 cfg, err := yaml.Marshal(destConfigMap) 145 if err != nil { 146 return nil, err 147 } 148 149 configs = append(configs, cfg) 150 } 151 return bytes.Join(configs, []byte("---\n")), nil 152 } 153 154 func convertSecretYaml(contents, data []byte) ([]byte, error) { 155 secret := k8sv1.Secret{} 156 dataMap := make(map[string]string) 157 if err := k8sYaml.Unmarshal(data, &dataMap); err != nil { 158 return nil, err 159 } 160 161 if err := k8sYaml.Unmarshal(contents, &secret); err != nil { 162 return nil, err 163 } 164 165 if secret.Data == nil { 166 secret.Data = make(map[string][]byte) 167 } 168 //set secret data 169 for k, v := range dataMap { 170 v := []byte(v) 171 secret.Data[k] = v 172 } 173 return k8sYaml.Marshal(secret) 174 }