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 }