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  }