github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/apply/applydriver/local.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 applydriver
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/alibaba/sealer/utils/ssh"
    21  
    22  	v1 "github.com/alibaba/sealer/types/api/v1"
    23  
    24  	"github.com/alibaba/sealer/utils/platform"
    25  
    26  	"github.com/alibaba/sealer/apply/processor"
    27  	"github.com/alibaba/sealer/common"
    28  	"github.com/alibaba/sealer/logger"
    29  	"github.com/alibaba/sealer/pkg/clusterfile"
    30  	"github.com/alibaba/sealer/pkg/filesystem/cloudimage"
    31  	"github.com/alibaba/sealer/pkg/image/store"
    32  	"github.com/alibaba/sealer/pkg/runtime"
    33  	v2 "github.com/alibaba/sealer/types/api/v2"
    34  
    35  	"github.com/pkg/errors"
    36  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    37  	"k8s.io/apimachinery/pkg/version"
    38  
    39  	"github.com/alibaba/sealer/pkg/client/k8s"
    40  	"github.com/alibaba/sealer/pkg/image"
    41  	"github.com/alibaba/sealer/utils"
    42  )
    43  
    44  // Applier cloud builder using cloud provider to build a cluster image
    45  type Applier struct {
    46  	ClusterDesired     *v2.Cluster
    47  	ClusterCurrent     *v2.Cluster
    48  	ClusterFile        clusterfile.Interface
    49  	ImageManager       image.Service
    50  	CloudImageMounter  cloudimage.Interface
    51  	Client             *k8s.Client
    52  	ImageStore         store.ImageStore
    53  	CurrentClusterInfo *version.Info
    54  }
    55  
    56  func (c *Applier) Delete() (err error) {
    57  	t := metav1.Now()
    58  	c.ClusterDesired.DeletionTimestamp = &t
    59  	return c.deleteCluster()
    60  }
    61  
    62  // Apply different actions between ClusterDesired and ClusterCurrent.
    63  func (c *Applier) Apply() (err error) {
    64  	// first time to init cluster
    65  	if c.ClusterFile == nil {
    66  		c.ClusterFile, err = clusterfile.NewClusterFile(c.ClusterDesired.GetAnnotationsByKey(common.ClusterfileName))
    67  		if err != nil {
    68  			return err
    69  		}
    70  	}
    71  	if !utils.IsFileExist(common.DefaultKubeConfigFile()) {
    72  		if err = c.initCluster(); err != nil {
    73  			return err
    74  		}
    75  	} else {
    76  		if err = c.reconcileCluster(); err != nil {
    77  			return err
    78  		}
    79  	}
    80  
    81  	return utils.SaveClusterInfoToFile(c.ClusterDesired, c.ClusterDesired.Name)
    82  }
    83  
    84  func (c *Applier) fillClusterCurrent() error {
    85  	currentCluster, err := GetCurrentCluster(c.Client)
    86  	if err != nil {
    87  		return errors.Wrap(err, "get current cluster failed")
    88  	}
    89  	if currentCluster != nil {
    90  		c.ClusterCurrent = c.ClusterDesired.DeepCopy()
    91  		c.ClusterCurrent.Spec.Hosts = currentCluster.Spec.Hosts
    92  	}
    93  	return nil
    94  }
    95  
    96  func (c *Applier) mountClusterImage() error {
    97  	imageName := c.ClusterDesired.Spec.Image
    98  	platsMap, err := ssh.GetClusterPlatform(c.ClusterDesired)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	plats := []*v1.Platform{platform.GetDefaultPlatform()}
   103  	for _, v := range platsMap {
   104  		plat := v
   105  		plats = append(plats, &plat)
   106  	}
   107  	err = c.ImageManager.PullIfNotExist(imageName, plats)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	err = c.CloudImageMounter.MountImage(c.ClusterDesired)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	return nil
   116  }
   117  
   118  func (c *Applier) unMountClusterImage() error {
   119  	return c.CloudImageMounter.UnMountImage(c.ClusterDesired)
   120  }
   121  
   122  func (c *Applier) reconcileCluster() error {
   123  	client, err := k8s.Newk8sClient()
   124  	if err != nil {
   125  		return err
   126  	}
   127  	c.Client = client
   128  	info, err := c.Client.GetClusterVersion()
   129  	if err != nil {
   130  		return err
   131  	}
   132  	c.CurrentClusterInfo = info
   133  
   134  	if err := c.fillClusterCurrent(); err != nil {
   135  		return err
   136  	}
   137  
   138  	if err := c.mountClusterImage(); err != nil {
   139  		return err
   140  	}
   141  	defer func() {
   142  		if err := c.unMountClusterImage(); err != nil {
   143  			logger.Warn("failed to umount image %s, %v", c.ClusterDesired.ClusterName, err)
   144  		}
   145  	}()
   146  
   147  	baseImage, err := c.ImageStore.GetByName(c.ClusterDesired.Spec.Image, platform.GetDefaultPlatform())
   148  	if err != nil {
   149  		return fmt.Errorf("failed to get base image err: %s", err)
   150  	}
   151  	// if no rootfs ,try to install applications.
   152  	if baseImage.Spec.ImageConfig.ImageType == common.AppImage {
   153  		return c.installApp()
   154  	}
   155  
   156  	mj, md := utils.GetDiffHosts(c.ClusterCurrent.GetMasterIPList(), c.ClusterDesired.GetMasterIPList())
   157  	nj, nd := utils.GetDiffHosts(c.ClusterCurrent.GetNodeIPList(), c.ClusterDesired.GetNodeIPList())
   158  	if len(mj) == 0 && len(md) == 0 && len(nj) == 0 && len(nd) == 0 {
   159  		return c.upgrade()
   160  	}
   161  	return c.scaleCluster(mj, md, nj, nd)
   162  }
   163  
   164  func (c *Applier) scaleCluster(mj, md, nj, nd []string) error {
   165  	logger.Info("Start to scale this cluster")
   166  	logger.Debug("current cluster: master %s, worker %s", c.ClusterCurrent.GetMasterIPList(), c.ClusterCurrent.GetNodeIPList())
   167  
   168  	scaleProcessor, err := processor.NewScaleProcessor(c.ClusterFile.GetKubeadmConfig(), c.ClusterFile, mj, md, nj, nd)
   169  	if err != nil {
   170  		return err
   171  	}
   172  	var cluster *v2.Cluster
   173  	if !scaleProcessor.(*processor.ScaleProcessor).IsScaleUp {
   174  		c, err := runtime.DecodeCRDFromFile(common.GetClusterWorkClusterfile(c.ClusterDesired.Name), common.Cluster)
   175  		if err != nil {
   176  			return err
   177  		} else if c != nil {
   178  			cluster = c.(*v2.Cluster)
   179  		}
   180  	} else {
   181  		cluster = c.ClusterDesired
   182  	}
   183  	err = processor.NewExecutor(scaleProcessor).Execute(cluster)
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	logger.Info("Succeeded in scaling this cluster")
   189  
   190  	return nil
   191  }
   192  
   193  func (c *Applier) Upgrade(upgradeImgName string) error {
   194  	if err := c.initClusterfile(); err != nil {
   195  		return err
   196  	}
   197  	if err := c.initK8sClient(); err != nil {
   198  		return err
   199  	}
   200  
   201  	c.ClusterDesired.Spec.Image = upgradeImgName
   202  	if err := c.mountClusterImage(); err != nil {
   203  		return err
   204  	}
   205  	defer func() {
   206  		if err := c.unMountClusterImage(); err != nil {
   207  			logger.Warn("failed to umount image %s, %v", c.ClusterDesired.ClusterName, err)
   208  		}
   209  	}()
   210  	return c.upgrade()
   211  }
   212  
   213  func (c *Applier) upgrade() error {
   214  	runtimeInterface, err := runtime.NewDefaultRuntime(c.ClusterDesired, c.ClusterFile.GetKubeadmConfig())
   215  	if err != nil {
   216  		return fmt.Errorf("failed to init runtime, %v", err)
   217  	}
   218  	upgradeImgMeta, err := runtimeInterface.GetClusterMetadata()
   219  	if err != nil {
   220  		return fmt.Errorf("failed to get cluster metadata: %v", err)
   221  	}
   222  
   223  	if c.CurrentClusterInfo.GitVersion == upgradeImgMeta.Version {
   224  		logger.Info("No upgrade required, image version and cluster version are both %s.", c.CurrentClusterInfo.GitVersion)
   225  		return nil
   226  	}
   227  	logger.Info("Start to upgrade this cluster from version(%s) to version(%s)", c.CurrentClusterInfo.GitVersion, upgradeImgMeta.Version)
   228  
   229  	upgradeProcessor, err := processor.NewUpgradeProcessor(platform.DefaultMountCloudImageDir(c.ClusterDesired.Name), runtimeInterface)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	err = upgradeProcessor.Execute(c.ClusterDesired)
   234  	if err != nil {
   235  		return err
   236  	}
   237  	logger.Info("Succeeded in upgrading current cluster from version(%s) to version(%s)", c.CurrentClusterInfo.GitVersion, upgradeImgMeta.Version)
   238  	return utils.SaveClusterInfoToFile(c.ClusterDesired, c.ClusterDesired.Name)
   239  }
   240  
   241  func (c *Applier) initClusterfile() (err error) {
   242  	if c.ClusterFile != nil {
   243  		return nil
   244  	}
   245  	c.ClusterFile, err = clusterfile.NewClusterFile(c.ClusterDesired.GetAnnotationsByKey(common.ClusterfileName))
   246  	return err
   247  }
   248  
   249  func (c *Applier) initK8sClient() error {
   250  	client, err := k8s.Newk8sClient()
   251  	c.Client = client
   252  	if err != nil {
   253  		return err
   254  	}
   255  	info, err := client.GetClusterVersion()
   256  	c.CurrentClusterInfo = info
   257  	return err
   258  }
   259  
   260  func (c *Applier) installApp() error {
   261  	rootfs := platform.DefaultMountCloudImageDir(c.ClusterDesired.Name)
   262  	// use k8sClient to fetch current cluster version.
   263  	info := c.CurrentClusterInfo
   264  
   265  	clusterMetadata, err := runtime.LoadMetadata(rootfs)
   266  	if err != nil {
   267  		return err
   268  	}
   269  	if clusterMetadata != nil {
   270  		if !VersionCompatible(info.GitVersion, clusterMetadata.KubeVersion) {
   271  			return fmt.Errorf("incompatible application version, need: %s", clusterMetadata.KubeVersion)
   272  		}
   273  	}
   274  
   275  	installProcessor, err := processor.NewInstallProcessor(c.ClusterFile)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	err = processor.NewExecutor(installProcessor).Execute(c.ClusterDesired)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	return nil
   285  }
   286  
   287  func (c *Applier) initCluster() error {
   288  	logger.Info("Start to create a new cluster: master %s, worker %s", c.ClusterDesired.GetMasterIPList(), c.ClusterDesired.GetNodeIPList())
   289  	createProcessor, err := processor.NewCreateProcessor(c.ClusterFile)
   290  	if err != nil {
   291  		return err
   292  	}
   293  
   294  	if err := processor.NewExecutor(createProcessor).Execute(c.ClusterDesired); err != nil {
   295  		return err
   296  	}
   297  
   298  	logger.Info("Succeeded in creating a new cluster, enjoy it!")
   299  
   300  	return nil
   301  }
   302  
   303  func (c *Applier) deleteCluster() error {
   304  	deleteProcessor, err := processor.NewDeleteProcessor(c.ClusterFile)
   305  	if err != nil {
   306  		return err
   307  	}
   308  	if err := c.mountClusterImage(); err != nil {
   309  		return err
   310  	}
   311  	//deleteProcessor to unmount image
   312  	if err := processor.NewExecutor(deleteProcessor).Execute(c.ClusterDesired); err != nil {
   313  		return err
   314  	}
   315  
   316  	logger.Info("Succeeded in deleting current cluster")
   317  
   318  	return nil
   319  }