github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/runtime/utils.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 runtime
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io"
    22  	"io/ioutil"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
    29  
    30  	"github.com/alibaba/sealer/pkg/runtime/kubeadm_types/v1beta2"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	k8sruntime "k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/util/yaml"
    34  	"k8s.io/kube-proxy/config/v1alpha1"
    35  	"k8s.io/kubelet/config/v1beta1"
    36  
    37  	v2 "github.com/alibaba/sealer/types/api/v2"
    38  
    39  	"github.com/pkg/errors"
    40  
    41  	"github.com/alibaba/sealer/common"
    42  	"github.com/alibaba/sealer/logger"
    43  	"github.com/alibaba/sealer/utils"
    44  	"github.com/alibaba/sealer/utils/ssh"
    45  )
    46  
    47  // VersionCompare :if v1 >= v2 return true, else return false
    48  func VersionCompare(v1, v2 string) bool {
    49  	v1 = strings.Replace(v1, "v", "", -1)
    50  	v2 = strings.Replace(v2, "v", "", -1)
    51  	v1 = strings.Split(v1, "-")[0]
    52  	v2 = strings.Split(v2, "-")[0]
    53  	v1List := strings.Split(v1, ".")
    54  	v2List := strings.Split(v2, ".")
    55  
    56  	if len(v1List) != 3 || len(v2List) != 3 {
    57  		logger.Error("error version format %s %s", v1, v2)
    58  		return false
    59  	}
    60  	if v1List[0] > v2List[0] {
    61  		return true
    62  	} else if v1List[0] < v2List[0] {
    63  		return false
    64  	}
    65  	if v1List[1] > v2List[1] {
    66  		return true
    67  	} else if v1List[1] < v2List[1] {
    68  		return false
    69  	}
    70  	if v1List[2] > v2List[2] {
    71  		return true
    72  	}
    73  	return true
    74  }
    75  
    76  func PreInitMaster0(sshClient ssh.Interface, remoteHostIP string) error {
    77  	err := ssh.WaitSSHReady(sshClient, 6, remoteHostIP)
    78  	if err != nil {
    79  		return fmt.Errorf("apply cloud cluster failed: %s", err)
    80  	}
    81  	// send sealer and cluster file to remote host
    82  	sealerPath := utils.ExecutableFilePath()
    83  	err = sshClient.Copy(remoteHostIP, sealerPath, common.RemoteSealerPath)
    84  	if err != nil {
    85  		return fmt.Errorf("send sealer to remote host %s failed:%v", remoteHostIP, err)
    86  	}
    87  	err = sshClient.CmdAsync(remoteHostIP, fmt.Sprintf(common.ChmodCmd, common.RemoteSealerPath))
    88  	if err != nil {
    89  		return fmt.Errorf("chmod +x sealer on remote host %s failed:%v", remoteHostIP, err)
    90  	}
    91  	logger.Info("send sealer cmd to %s success !", remoteHostIP)
    92  
    93  	// send tmp cluster file
    94  	err = sshClient.Copy(remoteHostIP, common.TmpClusterfile, common.TmpClusterfile)
    95  	if err != nil {
    96  		return fmt.Errorf("send cluster file to remote host %s failed:%v", remoteHostIP, err)
    97  	}
    98  	logger.Info("send cluster file to %s success !", remoteHostIP)
    99  
   100  	// send register login info
   101  	authFile := common.DefaultRegistryAuthConfigDir()
   102  	if utils.IsFileExist(authFile) {
   103  		err = sshClient.Copy(remoteHostIP, authFile, common.DefaultRegistryAuthDir)
   104  		if err != nil {
   105  			return fmt.Errorf("failed to send register config %s to remote host %s err: %v", authFile, remoteHostIP, err)
   106  		}
   107  		logger.Info("send register info to %s success !", remoteHostIP)
   108  	} else {
   109  		logger.Warn("failed to find %s, if image registry is private, please login first", authFile)
   110  	}
   111  	return nil
   112  }
   113  
   114  func GetKubectlAndKubeconfig(ssh ssh.Interface, host, rootfs string) error {
   115  	// fetch the cluster kubeconfig, and add /etc/hosts "EIP apiserver.cluster.local" so we can get the current cluster status later
   116  	err := ssh.Fetch(host, path.Join(common.DefaultKubeConfigDir(), "config"), common.KubeAdminConf)
   117  	if err != nil {
   118  		return errors.Wrap(err, "failed to copy kubeconfig")
   119  	}
   120  	_, err = utils.RunSimpleCmd(fmt.Sprintf("cat /etc/hosts |grep '%s %s' || echo '%s %s' >> /etc/hosts",
   121  		host, common.APIServerDomain, host, common.APIServerDomain))
   122  	if err != nil {
   123  		return errors.Wrap(err, "failed to add master IP to etc hosts")
   124  	}
   125  	if !utils.IsFileExist(common.KubectlPath) {
   126  		_, err = utils.CopySingleFile(filepath.Join(rootfs, "bin/kubectl"), common.KubectlPath)
   127  		if err != nil {
   128  			return err
   129  		}
   130  		err = utils.Cmd("chmod", "+x", common.KubectlPath)
   131  		if err != nil {
   132  			return errors.Wrap(err, "chmod a+x kubectl failed")
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  // LoadMetadata :read metadata via cluster image name.
   139  func LoadMetadata(rootfs string) (*Metadata, error) {
   140  	metadataPath := filepath.Join(rootfs, common.DefaultMetadataName)
   141  	var metadataFile []byte
   142  	var err error
   143  	var md Metadata
   144  	if !utils.IsFileExist(metadataPath) {
   145  		return nil, nil
   146  	}
   147  
   148  	metadataFile, err = ioutil.ReadFile(filepath.Clean(metadataPath))
   149  	if err != nil {
   150  		return nil, fmt.Errorf("failed to read CloudImage metadata %v", err)
   151  	}
   152  	err = json.Unmarshal(metadataFile, &md)
   153  	if err != nil {
   154  		return nil, fmt.Errorf("failed to load CloudImage metadata %v", err)
   155  	}
   156  	return &md, nil
   157  }
   158  
   159  func GetCloudImagePlatform(rootfs string) (cp ocispecs.Platform) {
   160  	// current we only support build on linux
   161  	cp = ocispecs.Platform{
   162  		Architecture: "amd64",
   163  		OS:           "linux",
   164  		Variant:      "",
   165  		OSVersion:    "",
   166  	}
   167  	meta, err := LoadMetadata(rootfs)
   168  	if err != nil {
   169  		return
   170  	}
   171  	if meta == nil {
   172  		return
   173  	}
   174  	if meta.Arch != "" {
   175  		cp.Architecture = meta.Arch
   176  	}
   177  	if meta.Variant != "" {
   178  		cp.Variant = meta.Variant
   179  	}
   180  	return
   181  }
   182  
   183  func ReadChanError(errors chan error) (err error) {
   184  	for {
   185  		if len(errors) == 0 {
   186  			break
   187  		}
   188  		err = fmt.Errorf("%v,%v", err, <-errors)
   189  	}
   190  
   191  	return
   192  }
   193  
   194  func GetMasterIPList(cluster *v2.Cluster) (masters []string) {
   195  	if cluster == nil {
   196  		return
   197  	}
   198  	return getHostsIPByRole(cluster, common.MASTER)
   199  }
   200  
   201  func GetMaster0Ip(cluster *v2.Cluster) string {
   202  	//cluster master ips > 0
   203  	return cluster.Spec.Hosts[0].IPS[0]
   204  }
   205  
   206  func GetNodeIPList(cluster *v2.Cluster) (masters []string) {
   207  	if cluster == nil {
   208  		return
   209  	}
   210  	return getHostsIPByRole(cluster, common.NODE)
   211  }
   212  
   213  func getHostsIPByRole(cluster *v2.Cluster, role string) (nodes []string) {
   214  	for _, host := range cluster.Spec.Hosts {
   215  		if utils.InList(role, host.Roles) {
   216  			nodes = append(nodes, host.IPS...)
   217  		}
   218  	}
   219  	return
   220  }
   221  
   222  func DecodeCRDFromFile(filePath string, kind string) (interface{}, error) {
   223  	file, err := os.Open(filepath.Clean(filePath))
   224  	if err != nil {
   225  		return nil, fmt.Errorf("failed to dump config %v", err)
   226  	}
   227  	defer func() {
   228  		if err := file.Close(); err != nil {
   229  			logger.Warn("failed to dump config close clusterfile failed %v", err)
   230  		}
   231  	}()
   232  	return DecodeCRDFromReader(file, kind)
   233  }
   234  
   235  func DecodeCRDFromReader(r io.Reader, kind string) (interface{}, error) {
   236  	d := yaml.NewYAMLOrJSONDecoder(r, 4096)
   237  
   238  	for {
   239  		ext := k8sruntime.RawExtension{}
   240  		if err := d.Decode(&ext); err != nil {
   241  			if err == io.EOF {
   242  				break
   243  			}
   244  			return nil, err
   245  		}
   246  		// TODO: This needs to be able to handle object in other encodings and schemas.
   247  		ext.Raw = bytes.TrimSpace(ext.Raw)
   248  		if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) {
   249  			continue
   250  		}
   251  		metaType := metav1.TypeMeta{}
   252  		err := yaml.Unmarshal(ext.Raw, &metaType)
   253  		if err != nil {
   254  			return nil, fmt.Errorf("decode cluster failed %v", err)
   255  		}
   256  		// ext.Raw
   257  		if metaType.Kind == kind {
   258  			return TypeConversion(ext.Raw, kind)
   259  		}
   260  	}
   261  	return nil, nil
   262  }
   263  
   264  func DecodeCRDFromString(config string, kind string) (interface{}, error) {
   265  	return DecodeCRDFromReader(strings.NewReader(config), kind)
   266  }
   267  
   268  func TypeConversion(raw []byte, kind string) (i interface{}, err error) {
   269  	i = typeConversion(kind)
   270  	if i == nil {
   271  		return nil, fmt.Errorf("not found type %s from %s", kind, string(raw))
   272  	}
   273  	return i, yaml.Unmarshal(raw, i)
   274  }
   275  
   276  func typeConversion(kind string) interface{} {
   277  	switch kind {
   278  	case Cluster:
   279  		return &v2.Cluster{}
   280  	case InitConfiguration:
   281  		return &v1beta2.InitConfiguration{}
   282  	case JoinConfiguration:
   283  		return &v1beta2.JoinConfiguration{}
   284  	case ClusterConfiguration:
   285  		return &v1beta2.ClusterConfiguration{}
   286  	case KubeletConfiguration:
   287  		return &v1beta1.KubeletConfiguration{}
   288  	case KubeProxyConfiguration:
   289  		return &v1alpha1.KubeProxyConfiguration{}
   290  	}
   291  	return nil
   292  }