github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/clusterfile/decoder.go (about) 1 // Copyright © 2022 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 clusterfile 16 17 import ( 18 "bufio" 19 "bytes" 20 "fmt" 21 "io" 22 "net" 23 "strconv" 24 "strings" 25 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/util/yaml" 29 "k8s.io/kube-proxy/config/v1alpha1" 30 "k8s.io/kubelet/config/v1beta1" 31 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" 32 kubeadmConstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" 33 34 "github.com/sealerio/sealer/common" 35 "github.com/sealerio/sealer/types/api/constants" 36 v1 "github.com/sealerio/sealer/types/api/v1" 37 v2 "github.com/sealerio/sealer/types/api/v2" 38 utilsnet "github.com/sealerio/sealer/utils/net" 39 strUtil "github.com/sealerio/sealer/utils/strings" 40 ) 41 42 func DecodeClusterfile(reader io.Reader) (*ClusterFile, error) { 43 clusterFile := new(ClusterFile) 44 // use user specified Clusterfile 45 if err := decodeClusterFile(reader, clusterFile); err != nil { 46 return nil, fmt.Errorf("failed to load clusterfile: %v", err) 47 } 48 return clusterFile, nil 49 } 50 51 func decodeClusterFile(reader io.Reader, clusterfile *ClusterFile) error { 52 decoder := yaml.NewYAMLToJSONDecoder(bufio.NewReaderSize(reader, 4096)) 53 54 for { 55 ext := runtime.RawExtension{} 56 if err := decoder.Decode(&ext); err != nil { 57 if err == io.EOF { 58 return nil 59 } 60 return err 61 } 62 63 ext.Raw = bytes.TrimSpace(ext.Raw) 64 if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) { 65 continue 66 } 67 metaType := metav1.TypeMeta{} 68 if err := yaml.Unmarshal(ext.Raw, &metaType); err != nil { 69 return fmt.Errorf("failed to decode TypeMeta: %v", err) 70 } 71 72 switch metaType.Kind { 73 case constants.ClusterKind: 74 var cluster v2.Cluster 75 76 if err := yaml.Unmarshal(ext.Raw, &cluster); err != nil { 77 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 78 } 79 if err := checkAndFillCluster(&cluster); err != nil { 80 return fmt.Errorf("failed to check and complete cluster: %v", err) 81 } 82 83 clusterfile.cluster = &cluster 84 case constants.ConfigKind: 85 var cfg v1.Config 86 87 if err := yaml.Unmarshal(ext.Raw, &cfg); err != nil { 88 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 89 } 90 91 if cfg.Spec.Path == "" { 92 return fmt.Errorf("failed to decode config %s, config path is empty", cfg.Name) 93 } 94 95 if cfg.Spec.Data == "" { 96 return fmt.Errorf("failed to decode config %s, config data is empty", cfg.Name) 97 } 98 99 clusterfile.configs = append(clusterfile.configs, cfg) 100 case constants.PluginKind: 101 var plu v1.Plugin 102 103 if err := yaml.Unmarshal(ext.Raw, &plu); err != nil { 104 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 105 } 106 107 clusterfile.plugins = append(clusterfile.plugins, plu) 108 case constants.ApplicationKind: 109 var app v2.Application 110 111 if err := yaml.Unmarshal(ext.Raw, &app); err != nil { 112 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 113 } 114 115 for _, config := range app.Spec.Configs { 116 if config.Name == "" { 117 return fmt.Errorf("application configs name coule not be nil") 118 } 119 120 if config.Launch != nil { 121 launchCmds := parseLaunchCmds(config.Launch) 122 if launchCmds == nil { 123 return fmt.Errorf("failed to get launchCmds from application configs") 124 } 125 } 126 127 for _, appFile := range config.Files { 128 if appFile.Data == "" { 129 return fmt.Errorf("failed to decode application config %s. data is empty", config.Name) 130 } 131 132 if appFile.Path == "" { 133 return fmt.Errorf("failed to decode application config %s. path is empty", config.Name) 134 } 135 } 136 } 137 138 clusterfile.app = &app 139 case kubeadmConstants.InitConfigurationKind: 140 var in v1beta3.InitConfiguration 141 142 if err := yaml.Unmarshal(ext.Raw, &in); err != nil { 143 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 144 } 145 146 clusterfile.kubeadmConfig.InitConfiguration = in 147 case kubeadmConstants.JoinConfigurationKind: 148 var in v1beta3.JoinConfiguration 149 150 if err := yaml.Unmarshal(ext.Raw, &in); err != nil { 151 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 152 } 153 154 clusterfile.kubeadmConfig.JoinConfiguration = in 155 case kubeadmConstants.ClusterConfigurationKind: 156 var in v1beta3.ClusterConfiguration 157 158 if err := yaml.Unmarshal(ext.Raw, &in); err != nil { 159 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 160 } 161 162 clusterfile.kubeadmConfig.ClusterConfiguration = in 163 case common.KubeletConfiguration: 164 var in v1beta1.KubeletConfiguration 165 166 if err := yaml.Unmarshal(ext.Raw, &in); err != nil { 167 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 168 } 169 170 clusterfile.kubeadmConfig.KubeletConfiguration = in 171 case common.KubeProxyConfiguration: 172 var in v1alpha1.KubeProxyConfiguration 173 174 if err := yaml.Unmarshal(ext.Raw, &in); err != nil { 175 return fmt.Errorf("failed to decode %s[%s]: %v", metaType.Kind, metaType.APIVersion, err) 176 } 177 178 clusterfile.kubeadmConfig.KubeProxyConfiguration = in 179 } 180 } 181 } 182 183 func checkAndFillCluster(cluster *v2.Cluster) error { 184 defaultInsecure := false 185 defaultHA := true 186 187 if cluster.Spec.Registry.LocalRegistry == nil && cluster.Spec.Registry.ExternalRegistry == nil { 188 cluster.Spec.Registry.LocalRegistry = &v2.LocalRegistry{} 189 } 190 191 if cluster.Spec.Registry.LocalRegistry != nil { 192 if cluster.Spec.Registry.LocalRegistry.Domain == "" { 193 cluster.Spec.Registry.LocalRegistry.Domain = common.DefaultRegistryDomain 194 } 195 if cluster.Spec.Registry.LocalRegistry.Port == 0 { 196 cluster.Spec.Registry.LocalRegistry.Port = common.DefaultRegistryPort 197 } 198 if cluster.Spec.Registry.LocalRegistry.Insecure == nil { 199 cluster.Spec.Registry.LocalRegistry.Insecure = &defaultInsecure 200 } 201 if cluster.Spec.Registry.LocalRegistry.HA == nil { 202 cluster.Spec.Registry.LocalRegistry.HA = &defaultHA 203 } 204 } 205 206 if cluster.Spec.Registry.ExternalRegistry != nil { 207 if cluster.Spec.Registry.ExternalRegistry.Domain == "" { 208 return fmt.Errorf("external registry domain can not be empty") 209 } 210 } 211 212 var newEnv []string 213 for _, env := range cluster.Spec.Env { 214 if strings.HasPrefix(env, common.EnvLocalRegistryDomain) || 215 strings.HasPrefix(env, common.EnvLocalRegistryPort) || 216 strings.HasPrefix(env, common.EnvLocalRegistryURL) || 217 strings.HasPrefix(env, common.EnvExternalRegistryDomain) || 218 strings.HasPrefix(env, common.EnvExternalRegistryPort) || 219 strings.HasPrefix(env, common.EnvExternalRegistryURL) || 220 strings.HasPrefix(env, common.EnvRegistryDomain) || 221 strings.HasPrefix(env, common.EnvRegistryPort) || 222 strings.HasPrefix(env, common.EnvRegistryURL) || 223 strings.HasPrefix(env, common.EnvContainerRuntime) || 224 strings.HasPrefix(env, common.EnvDNSSvcIP) || 225 strings.HasPrefix(env, common.EnvKubeSvcIP) { 226 continue 227 } 228 newEnv = append(newEnv, env) 229 } 230 cluster.Spec.Env = newEnv 231 232 clusterEnvMap := strUtil.ConvertStringSliceToMap(cluster.Spec.Env) 233 if svcCIDR, ok := clusterEnvMap[common.EnvSvcCIDR]; ok && svcCIDR != "" { 234 cidrs := strings.Split(svcCIDR, ",") 235 _, cidr, err := net.ParseCIDR(cidrs[0]) 236 if err != nil { 237 return fmt.Errorf("failed to parse svc CIDR: %v", err) 238 } 239 kubeIP, err := utilsnet.GetIndexIP(cidr, 1) 240 if err != nil { 241 return fmt.Errorf("failed to get 1th ip from svc CIDR: %v", err) 242 } 243 dnsIP, err := utilsnet.GetIndexIP(cidr, 10) 244 if err != nil { 245 return fmt.Errorf("failed to get 10th ip from svc CIDR: %v", err) 246 } 247 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvKubeSvcIP, kubeIP)) 248 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvDNSSvcIP, dnsIP)) 249 } 250 251 regConfig := v2.RegistryConfig{} 252 if cluster.Spec.Registry.LocalRegistry != nil { 253 regConfig = cluster.Spec.Registry.LocalRegistry.RegistryConfig 254 255 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvLocalRegistryDomain, regConfig.Domain)) 256 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%d", common.EnvLocalRegistryPort, regConfig.Port)) 257 registryURL := net.JoinHostPort(regConfig.Domain, strconv.Itoa(regConfig.Port)) 258 if regConfig.Port == 0 { 259 registryURL = regConfig.Domain 260 } 261 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvLocalRegistryURL, registryURL)) 262 } 263 if cluster.Spec.Registry.ExternalRegistry != nil { 264 regConfig = cluster.Spec.Registry.ExternalRegistry.RegistryConfig 265 266 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvExternalRegistryDomain, regConfig.Domain)) 267 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%d", common.EnvExternalRegistryPort, regConfig.Port)) 268 registryURL := net.JoinHostPort(regConfig.Domain, strconv.Itoa(regConfig.Port)) 269 if regConfig.Port == 0 { 270 registryURL = regConfig.Domain 271 } 272 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvExternalRegistryURL, registryURL)) 273 } 274 275 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvRegistryDomain, regConfig.Domain)) 276 portStr := fmt.Sprintf("%d", regConfig.Port) 277 if regConfig.Port == 0 { 278 portStr = "" 279 } 280 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvRegistryPort, portStr)) 281 registryURL := net.JoinHostPort(regConfig.Domain, strconv.Itoa(regConfig.Port)) 282 if regConfig.Port == 0 { 283 registryURL = regConfig.Domain 284 } 285 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvRegistryURL, registryURL)) 286 287 if cluster.Spec.ContainerRuntime.Type != "" { 288 cluster.Spec.Env = append(cluster.Spec.Env, fmt.Sprintf("%s=%s", common.EnvContainerRuntime, cluster.Spec.ContainerRuntime.Type)) 289 } 290 291 if cluster.Spec.DataRoot == "" { 292 cluster.Spec.DataRoot = common.DefaultSealerDataDir 293 } 294 295 return nil 296 } 297 298 // parseLaunchCmds parse shell, kube,helm type launch cmds 299 // kubectl apply -n sealer-io -f ns.yaml -f app.yaml 300 // helm install my-nginx bitnami/nginx 301 // key1=value1 key2=value2 && bash install1.sh && bash install2.sh 302 func parseLaunchCmds(launch *v2.Launch) []string { 303 if launch.Cmds != nil { 304 return launch.Cmds 305 } 306 // TODO add shell,helm,kube type cmds. 307 return nil 308 }