github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/runtime/init.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  	"context"
    19  	"fmt"
    20  	"path/filepath"
    21  	"strings"
    22  
    23  	"github.com/alibaba/sealer/common"
    24  	"github.com/alibaba/sealer/logger"
    25  	"github.com/alibaba/sealer/pkg/cert"
    26  	v2 "github.com/alibaba/sealer/types/api/v2"
    27  	"github.com/alibaba/sealer/utils"
    28  	"golang.org/x/sync/errgroup"
    29  )
    30  
    31  const (
    32  	RemoteCmdCopyStatic            = "mkdir -p %s && cp -f %s %s"
    33  	RemoteApplyYaml                = `echo '%s' | kubectl apply -f -`
    34  	RemoteCmdGetNetworkInterface   = "ls /sys/class/net"
    35  	RemoteCmdExistNetworkInterface = "ip addr show %s | egrep \"%s\" || true"
    36  	WriteKubeadmConfigCmd          = `cd %s && echo '%s' > etc/kubeadm.yml`
    37  	DefaultVIP                     = "10.103.97.2"
    38  	DefaultAPIserverDomain         = "apiserver.cluster.local"
    39  	DefaultRegistryPort            = 5000
    40  	DockerCertDir                  = "/etc/docker/certs.d"
    41  )
    42  
    43  func (k *KubeadmRuntime) ConfigKubeadmOnMaster0() error {
    44  	if err := k.LoadFromClusterfile(k.Config.ClusterFileKubeConfig); err != nil {
    45  		return fmt.Errorf("failed to load kubeadm config from clusterfile: %v", err)
    46  	}
    47  	// TODO handle the kubeadm config, like kubeproxy config
    48  	k.handleKubeadmConfig()
    49  	if err := k.KubeadmConfig.Merge(k.getDefaultKubeadmConfig()); err != nil {
    50  		return err
    51  	}
    52  	bs, err := k.generateConfigs()
    53  	if err != nil {
    54  		return err
    55  	}
    56  	cmd := fmt.Sprintf(WriteKubeadmConfigCmd, k.getRootfs(), string(bs))
    57  	sshClient, err := k.getHostSSHClient(k.GetMaster0IP())
    58  	if err != nil {
    59  		return err
    60  	}
    61  	return sshClient.CmdAsync(k.GetMaster0IP(), cmd)
    62  }
    63  
    64  func (k *KubeadmRuntime) generateConfigs() ([]byte, error) {
    65  	//getCgroupDriverFromShell need get CRISocket, so after merge
    66  	cGroupDriver, err := k.getCgroupDriverFromShell(k.GetMaster0IP())
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	k.setCgroupDriver(cGroupDriver)
    71  	k.setKubeadmAPIVersion()
    72  	return utils.MarshalYamlConfigs(&k.InitConfiguration,
    73  		&k.ClusterConfiguration,
    74  		&k.KubeletConfiguration,
    75  		&k.KubeProxyConfiguration)
    76  }
    77  
    78  func (k *KubeadmRuntime) handleKubeadmConfig() {
    79  	//The configuration set here does not require merge
    80  	k.setInitAdvertiseAddress(k.GetMaster0IP())
    81  	k.setControlPlaneEndpoint(fmt.Sprintf("%s:6443", k.getAPIServerDomain()))
    82  	if k.APIServer.ExtraArgs == nil {
    83  		k.APIServer.ExtraArgs = make(map[string]string)
    84  	}
    85  	k.APIServer.ExtraArgs[EtcdServers] = getEtcdEndpointsWithHTTPSPrefix(k.GetMasterIPList())
    86  	k.IPVS.ExcludeCIDRs = append(k.KubeProxyConfiguration.IPVS.ExcludeCIDRs, fmt.Sprintf("%s/32", k.getVIP()))
    87  }
    88  
    89  //CmdToString is in host exec cmd and replace to spilt str
    90  func (k *KubeadmRuntime) CmdToString(host, cmd, split string) (string, error) {
    91  	ssh, err := k.getHostSSHClient(host)
    92  	if err != nil {
    93  		return "", fmt.Errorf("failed to get host ssh client, %s %v", cmd, err)
    94  	}
    95  	data, err := ssh.Cmd(host, cmd)
    96  	if err != nil {
    97  		return "", fmt.Errorf("exec remote cmd failed, %s %v", cmd, err)
    98  	}
    99  	if data != nil {
   100  		str := string(data)
   101  		str = strings.ReplaceAll(str, "\r\n", split)
   102  		str = strings.ReplaceAll(str, "\n", split)
   103  		return str, nil
   104  	}
   105  	return "", nil
   106  }
   107  
   108  func (k *KubeadmRuntime) getRemoteHostName(hostIP string) (string, error) {
   109  	hostName, err := k.CmdToString(hostIP, "hostname", "")
   110  	if err != nil {
   111  		return "", err
   112  	}
   113  	if hostName == "" {
   114  		return "", fmt.Errorf("get remote hostname failed %s", hostIP)
   115  	}
   116  	return strings.ToLower(hostName), nil
   117  }
   118  
   119  func (k *KubeadmRuntime) GenerateCert() error {
   120  	hostName, err := k.getRemoteHostName(k.GetMaster0IP())
   121  	if err != nil {
   122  		return err
   123  	}
   124  	err = cert.GenerateCert(
   125  		k.getPKIPath(),
   126  		k.getEtcdCertPath(),
   127  		k.getCertSANS(),
   128  		k.GetMaster0IP(),
   129  		hostName,
   130  		k.getSvcCIDR(),
   131  		k.getDNSDomain(),
   132  	)
   133  	if err != nil {
   134  		return fmt.Errorf("generate certs failed %v", err)
   135  	}
   136  	err = k.sendNewCertAndKey(k.GetMasterIPList()[:1])
   137  	if err != nil {
   138  		return err
   139  	}
   140  	return k.GenerateRegistryCert()
   141  }
   142  
   143  func (k *KubeadmRuntime) GenerateRegistryCert() error {
   144  	err := GenerateRegistryCert(k.getCertsDir(), k.RegConfig.Domain)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	err = k.sendRegistryCertAndKey()
   149  	if err != nil {
   150  		return err
   151  	}
   152  	return k.sendRegistryCert(k.GetMasterIPList()[:1])
   153  }
   154  
   155  func (k *KubeadmRuntime) CreateKubeConfig() error {
   156  	hostname, err := k.getRemoteHostName(k.GetMaster0IP())
   157  	if err != nil {
   158  		return err
   159  	}
   160  	certConfig := cert.Config{
   161  		Path:     k.getPKIPath(),
   162  		BaseName: "ca",
   163  	}
   164  
   165  	controlPlaneEndpoint := fmt.Sprintf("https://%s:6443", k.getAPIServerDomain())
   166  	err = cert.CreateJoinControlPlaneKubeConfigFiles(k.getBasePath(),
   167  		certConfig, hostname, controlPlaneEndpoint, "kubernetes")
   168  	if err != nil {
   169  		return fmt.Errorf("generator kubeconfig failed %s", err)
   170  	}
   171  	return nil
   172  }
   173  
   174  func (k *KubeadmRuntime) CopyStaticFiles(nodes []string) error {
   175  	for _, file := range MasterStaticFiles {
   176  		staticFilePath := filepath.Join(k.getStaticFileDir(), file.Name)
   177  		cmdLinkStatic := fmt.Sprintf(RemoteCmdCopyStatic, file.DestinationDir, staticFilePath, filepath.Join(file.DestinationDir, file.Name))
   178  		eg, _ := errgroup.WithContext(context.Background())
   179  		for _, host := range nodes {
   180  			host := host
   181  			eg.Go(func() error {
   182  				ssh, err := k.getHostSSHClient(host)
   183  				if err != nil {
   184  					return fmt.Errorf("new ssh client failed %v", err)
   185  				}
   186  				err = ssh.CmdAsync(host, cmdLinkStatic)
   187  				if err != nil {
   188  					return fmt.Errorf("[%s] link static file failed, error:%s", host, err.Error())
   189  				}
   190  				return err
   191  			})
   192  		}
   193  		if err := eg.Wait(); err != nil {
   194  			return err
   195  		}
   196  	}
   197  	return nil
   198  }
   199  
   200  //decode output to join token hash and key
   201  func (k *KubeadmRuntime) decodeMaster0Output(output []byte) {
   202  	s0 := string(output)
   203  	logger.Debug("decodeOutput: %s", s0)
   204  	slice := strings.Split(s0, "kubeadm join")
   205  	slice1 := strings.Split(slice[1], "Please note")
   206  	logger.Info("join command is: kubeadm join %s", slice1[0])
   207  	k.decodeJoinCmd(slice1[0])
   208  }
   209  
   210  //  192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --experimental-control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
   211  func (k *KubeadmRuntime) decodeJoinCmd(cmd string) {
   212  	logger.Debug("[globals]decodeJoinCmd: %s", cmd)
   213  	stringSlice := strings.Split(cmd, " ")
   214  
   215  	for i, r := range stringSlice {
   216  		// upstream error, delete \t, \\, \n, space.
   217  		r = strings.ReplaceAll(r, "\t", "")
   218  		r = strings.ReplaceAll(r, "\n", "")
   219  		r = strings.ReplaceAll(r, "\\", "")
   220  		r = strings.TrimSpace(r)
   221  		if strings.Contains(r, "--token") {
   222  			k.setJoinToken(stringSlice[i+1])
   223  		}
   224  		if strings.Contains(r, "--discovery-token-ca-cert-hash") {
   225  			k.setTokenCaCertHash([]string{stringSlice[i+1]})
   226  		}
   227  		if strings.Contains(r, "--certificate-key") {
   228  			k.setInitCertificateKey(stringSlice[i+1][:64])
   229  		}
   230  	}
   231  	logger.Debug("joinToken: %v\nTokenCaCertHash: %v\nCertificateKey: %v", k.getJoinToken(), k.getTokenCaCertHash(), k.getCertificateKey())
   232  }
   233  
   234  //InitMaster0 is
   235  func (k *KubeadmRuntime) InitMaster0() error {
   236  	ssh, err := k.getHostSSHClient(k.GetMaster0IP())
   237  	if err != nil {
   238  		return fmt.Errorf("failed to get master0 ssh client, %v", err)
   239  	}
   240  
   241  	if err := k.SendJoinMasterKubeConfigs([]string{k.GetMaster0IP()}, AdminConf, ControllerConf, SchedulerConf, KubeletConf); err != nil {
   242  		return err
   243  	}
   244  	apiServerHost := getAPIServerHost(k.GetMaster0IP(), k.getAPIServerDomain())
   245  	cmdAddEtcHost := fmt.Sprintf(RemoteAddEtcHosts, apiServerHost, apiServerHost)
   246  	err = ssh.CmdAsync(k.GetMaster0IP(), cmdAddEtcHost)
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	logger.Info("start to init master0...")
   252  	cmdInit := k.Command(k.getKubeVersion(), InitMaster)
   253  
   254  	// TODO skip docker version error check for test
   255  	output, err := ssh.Cmd(k.GetMaster0IP(), cmdInit)
   256  	if err != nil {
   257  		_, wErr := common.StdOut.WriteString(string(output))
   258  		if wErr != nil {
   259  			return err
   260  		}
   261  		return fmt.Errorf("init master0 failed, error: %s. Please clean and reinstall", err.Error())
   262  	}
   263  	k.decodeMaster0Output(output)
   264  	err = ssh.CmdAsync(k.GetMaster0IP(), RemoteCopyKubeConfig)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	return nil
   270  }
   271  
   272  func (k *KubeadmRuntime) GetKubectlAndKubeconfig() error {
   273  	if utils.IsFileExist(common.DefaultKubeConfigFile()) {
   274  		return nil
   275  	}
   276  	ssh, err := k.getHostSSHClient(k.GetMaster0IP())
   277  	if err != nil {
   278  		return fmt.Errorf("failed to get master0 ssh client when get kubbectl and kubeconfig %v", err)
   279  	}
   280  
   281  	return GetKubectlAndKubeconfig(ssh, k.GetMaster0IP(), k.getImageMountDir())
   282  }
   283  
   284  func (k *KubeadmRuntime) CopyStaticFilesTomasters() error {
   285  	return k.CopyStaticFiles(k.GetMasterIPList())
   286  }
   287  
   288  func (k *KubeadmRuntime) init(cluster *v2.Cluster) error {
   289  	pipeline := []func() error{
   290  		k.ConfigKubeadmOnMaster0,
   291  		k.GenerateCert,
   292  		k.CreateKubeConfig,
   293  		k.CopyStaticFilesTomasters,
   294  		k.ApplyRegistry,
   295  		k.InitMaster0,
   296  		k.GetKubectlAndKubeconfig,
   297  	}
   298  
   299  	for _, f := range pipeline {
   300  		if err := f(); err != nil {
   301  			return fmt.Errorf("failed to init master0 %v", err)
   302  		}
   303  	}
   304  
   305  	return nil
   306  }