// rootCmd represents the base command when called without any subcommandsvarrootCmd=&cobra.Command{Use:"sealos",Short:"sealos is a Kubernetes distribution, a unified OS to manage cloud native applications.",}
作为 sealos 成就 Kubernetes 集群最主要的命令,我们来尝试一下 sealos run
run 简简单单的一个命名是高度抽象的:我们 run 的时候 cmd 将 applier, err := apply.NewApplierFromArgs(images, runArgs) 传递给 NewApplierFromArgs
funcNewApplierFromArgs(imageName[]string,args*RunArgs)(applydrivers.Interface,error){clusterPath:=constants.Clusterfile(args.ClusterName)cf:=clusterfile.NewClusterFile(clusterPath,clusterfile.WithCustomConfigFiles(args.CustomConfigFiles),clusterfile.WithCustomEnvs(args.CustomEnv),)err:=cf.Process()iferr!=nil&&err!=clusterfile.ErrClusterFileNotExists{returnnil,err}iferr=cf.SetSingleMode(args.Single);err!=nil{returnnil,err}cluster:=cf.GetCluster()ifcluster==nil{logger.Debug("creating new cluster")cluster=initCluster(args.ClusterName)}else{cluster=cluster.DeepCopy()}c:=&ClusterArgs{clusterName:cluster.Name,cluster:cluster,}iferr=c.runArgs(imageName,args);err!=nil{returnnil,err}returnapplydrivers.NewDefaultApplier(c.cluster,cf,imageName)}
funcNewDefaultApplier(cluster*v2.Cluster,cfclusterfile.Interface,images[]string)(Interface,error){ifcluster.Name==""{returnnil,fmt.Errorf("cluster name cannot be empty")}ifcf==nil{cf=clusterfile.NewClusterFile(constants.Clusterfile(cluster.Name))}err:=cf.Process()if!cluster.CreationTimestamp.IsZero()&&err!=nil{returnnil,err}return&Applier{ClusterDesired:cluster,ClusterFile:cf,ClusterCurrent:cf.GetCluster(),RunNewImages:images,},nil}
func(c*Applier)Apply()error{clusterPath:=constants.Clusterfile(c.ClusterDesired.Name)// clusterErr and appErr should not appear in the same timevarclusterErr,appErrerror// save cluster to file after applydeferfunc(){switchclusterErr.(type){case*processor.CheckError,*processor.PreProcessError:return}logger.Debug("write cluster file to local storage: %s",clusterPath)saveErr:=yaml.MarshalYamlToFile(clusterPath,c.getWriteBackObjects()...)ifsaveErr!=nil{logger.Error("write cluster file to local storage: %s error, %s",clusterPath,saveErr)logger.Debug("complete write back file: \n %v",c.getWriteBackObjects())}}()c.initStatus()ifc.ClusterDesired.CreationTimestamp.IsZero()&&(c.ClusterCurrent==nil||c.ClusterCurrent.CreationTimestamp.IsZero()){clusterErr=c.initCluster()c.ClusterDesired.CreationTimestamp=metav1.Now()}else{clusterErr,appErr=c.reconcileCluster()c.ClusterDesired.CreationTimestamp=c.ClusterCurrent.CreationTimestamp}c.updateStatus(clusterErr,appErr)// return app error if not nilifappErr!=nil&&!errors.Is(appErr,processor.ErrCancelled){returnappErr}returnclusterErr}
func(c*Dumper)Dump()error{iflen(c.Configs)==0{logger.Debug("clusterfile config is empty!")returnnil}iferr:=c.WriteFiles();err!=nil{returnfmt.Errorf("failed to write config files %v",err)}returnnil}func(c*Dumper)WriteFiles()(errerror){for_,config:=rangec.Configs{ifconfig.Spec.Match!=""&&config.Spec.Match!=c.name{continue}configData:=[]byte(config.Spec.Data)configPath:=filepath.Join(c.RootPath,config.Spec.Path)//only the YAML format is supportedswitchconfig.Spec.Strategy{casev1beta1.Merge:configData,err=getMergeConfigData(configPath,configData)iferr!=nil{returnerr}casev1beta1.Insert:configData,err=getAppendOrInsertConfigData(configPath,configData,true)iferr!=nil{returnerr}casev1beta1.Append:configData,err=getAppendOrInsertConfigData(configPath,configData,false)iferr!=nil{returnerr}casev1beta1.Override:}err=file.WriteFile(configPath,configData)iferr!=nil{returnfmt.Errorf("write config file failed %v",err)}}returnnil}
func(f*defaultRootfs)mountRootfs(cluster*v2.Cluster,ipList[]string)error{target:=constants.NewData(f.getClusterName(cluster)).RootFSPath()ctx:=context.Background()eg,_:=errgroup.WithContext(ctx)envProcessor:=env.NewEnvProcessor(cluster,f.mounts)for_,mount:=rangef.mounts{src:=mounteg.Go(func()error{if!file.IsExist(src.MountPoint){logger.Debug("Image %s not exist, render env continue",src.ImageName)returnnil}// TODO: if we are planing to support rendering templates for each host,// then move this rendering process before ssh.CopyDir and do it one by one.err:=renderTemplatesWithEnv(src.MountPoint,ipList,envProcessor)iferr!=nil{returnfmt.Errorf("failed to render env: %w",err)}dirs,err:=file.StatDir(src.MountPoint,true)iferr!=nil{returnfmt.Errorf("failed to stat files: %w",err)}iflen(dirs)!=0{_,err=exec.RunBashCmd(fmt.Sprintf(constants.DefaultChmodBash,src.MountPoint))iferr!=nil{returnfmt.Errorf("run chmod to rootfs failed: %w",err)}}returnnil})}iferr:=eg.Wait();err!=nil{returnerr}sshClient:=f.getSSH(cluster)notRegistryDirFilter:=func(entryfs.DirEntry)bool{return!constants.IsRegistryDir(entry)}foridx:=rangeipList{ip:=ipList[idx]eg.Go(func()error{egg,_:=errgroup.WithContext(ctx)foridj:=rangef.mounts{mount:=f.mounts[idj]egg.Go(func()error{switchmount.Type{casev2.RootfsImage,v2.PatchImage:logger.Debug("send mount image, ip: %s, image name: %s, image type: %s",ip,mount.ImageName,mount.Type)err:=ssh.CopyDir(sshClient,ip,mount.MountPoint,target,notRegistryDirFilter)iferr!=nil{returnfmt.Errorf("failed to copy %s %s: %v",mount.Type,mount.Name,err)}}returnnil})}returnegg.Wait()})}err:=eg.Wait()iferr!=nil{returnerr}endEg,_:=errgroup.WithContext(ctx)master0:=cluster.GetMaster0IPAndPort()foridx:=rangef.mounts{mountInfo:=f.mounts[idx]endEg.Go(func()error{ifmountInfo.Type==v2.AppImage{logger.Debug("send app mount images, ip: %s, image name: %s, image type: %s",master0,mountInfo.ImageName,mountInfo.Type)err=ssh.CopyDir(sshClient,master0,mountInfo.MountPoint,constants.GetAppWorkDir(cluster.Name,mountInfo.Name),notRegistryDirFilter)iferr!=nil{returnfmt.Errorf("failed to copy %s %s: %v",mountInfo.Type,mountInfo.Name,err)}}returnnil})}returnendEg.Wait()}
func(c*CreateProcessor)Bootstrap(cluster*v2.Cluster)error{logger.Info("Executing pipeline Bootstrap in CreateProcessor")hosts:=append(cluster.GetMasterIPAndPortList(),cluster.GetNodeIPAndPortList()...)bs:=bootstrap.New(cluster)returnbs.Apply(hosts...)}
func(bs*realBootstrap)Apply(hosts...string)error{appliers:=make([]Applier,0)appliers=append(appliers,bs.preflights...)appliers=append(appliers,bs.initializers...)appliers=append(appliers,bs.postflights...)logger.Debug("apply %+v on hosts %+v",appliers,hosts)fori:=rangeappliers{applier:=appliers[i]iferr:=runParallel(hosts,func(hoststring)error{if!applier.Filter(bs.ctx,host){returnnil}logger.Debug("apply %s on host %s",applier,host)returnapplier.Apply(bs.ctx,host)});err!=nil{returnerr}}returnnil}
读者回响