github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/imagedistributor/scp_distributor.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 imagedistributor
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net"
    21  	"os"
    22  	"path/filepath"
    23  
    24  	"github.com/sealerio/sealer/pkg/config"
    25  	"github.com/sealerio/sealer/pkg/env"
    26  	"github.com/sealerio/sealer/pkg/infradriver"
    27  	v1 "github.com/sealerio/sealer/types/api/v1"
    28  	osi "github.com/sealerio/sealer/utils/os"
    29  	"github.com/sirupsen/logrus"
    30  	"golang.org/x/sync/errgroup"
    31  )
    32  
    33  const (
    34  	RegistryDirName      = "registry"
    35  	RootfsCacheDirName   = "image"
    36  	RegistryCacheDirName = "registry"
    37  )
    38  
    39  type scpDistributor struct {
    40  	configs          []v1.Config
    41  	infraDriver      infradriver.InfraDriver
    42  	imageMountInfo   []ClusterImageMountInfo
    43  	registryCacheDir string
    44  	rootfsCacheDir   string
    45  	options          DistributeOption
    46  }
    47  
    48  func (s *scpDistributor) DistributeRegistry(deployHosts []net.IP, dataDir string) error {
    49  	for _, info := range s.imageMountInfo {
    50  		if !osi.IsFileExist(filepath.Join(info.MountDir, RegistryDirName)) {
    51  			continue
    52  		}
    53  
    54  		localCacheFile := filepath.Join(info.MountDir, info.ImageID)
    55  		remoteCacheFile := filepath.Join(s.registryCacheDir, info.ImageID)
    56  		eg, _ := errgroup.WithContext(context.Background())
    57  
    58  		for _, deployHost := range deployHosts {
    59  			tmpDeployHost := deployHost
    60  			eg.Go(func() error {
    61  				if !s.options.IgnoreCache {
    62  					// detect if remote cache file is exist.
    63  					existed, err := s.infraDriver.IsFileExist(tmpDeployHost, remoteCacheFile)
    64  					if err != nil {
    65  						return fmt.Errorf("failed to detect registry cache %s on host %s: %v",
    66  							remoteCacheFile, tmpDeployHost.String(), err)
    67  					}
    68  
    69  					if existed {
    70  						logrus.Debugf("cache %s hits on: %s, skip to do distribution", info.ImageID, tmpDeployHost.String())
    71  						return nil
    72  					}
    73  				}
    74  
    75  				// copy registry data
    76  				err := s.infraDriver.Copy(tmpDeployHost, filepath.Join(info.MountDir, RegistryDirName), dataDir)
    77  				if err != nil {
    78  					return fmt.Errorf("failed to copy registry data %s: %v", info.MountDir, err)
    79  				}
    80  
    81  				// write cache flag
    82  				err = s.writeCacheFlag(localCacheFile, remoteCacheFile, tmpDeployHost)
    83  				if err != nil {
    84  					return fmt.Errorf("failed to write registry cache %s on host %s: %v",
    85  						remoteCacheFile, tmpDeployHost.String(), err)
    86  				}
    87  
    88  				return nil
    89  			})
    90  		}
    91  		if err := eg.Wait(); err != nil {
    92  			return err
    93  		}
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func (s *scpDistributor) Distribute(hosts []net.IP, dest string) error {
   100  	for _, info := range s.imageMountInfo {
   101  		if err := s.dumpConfigToRootfs(info.MountDir); err != nil {
   102  			return err
   103  		}
   104  
   105  		if err := s.renderRootfs(info.MountDir); err != nil {
   106  			return err
   107  		}
   108  
   109  		eg, _ := errgroup.WithContext(context.Background())
   110  		localCacheFile := filepath.Join(info.MountDir, info.ImageID)
   111  		remoteCacheFile := filepath.Join(s.rootfsCacheDir, info.ImageID)
   112  
   113  		for _, ip := range info.Hosts {
   114  			host := ip
   115  			eg.Go(func() error {
   116  				if !s.options.IgnoreCache {
   117  					// detect if remote cache file is exist.
   118  					existed, err := s.infraDriver.IsFileExist(host, remoteCacheFile)
   119  					if err != nil {
   120  						return fmt.Errorf("failed to detect rootfs cache %s on host %s: %v",
   121  							remoteCacheFile, host.String(), err)
   122  					}
   123  
   124  					if existed {
   125  						logrus.Debugf("cache %s hits on: %s, skip to do distribution", info.ImageID, host.String())
   126  						return nil
   127  					}
   128  				}
   129  
   130  				// copy rootfs data
   131  				err := s.filterCopy(info.MountDir, dest, host)
   132  				if err != nil {
   133  					return fmt.Errorf("failed to copy rootfs files: %v", err)
   134  				}
   135  
   136  				// write cache flag
   137  				err = s.writeCacheFlag(localCacheFile, remoteCacheFile, host)
   138  				if err != nil {
   139  					return fmt.Errorf("failed to write rootfs cache %s on host %s: %v",
   140  						remoteCacheFile, host.String(), err)
   141  				}
   142  
   143  				return nil
   144  			})
   145  		}
   146  
   147  		if err := eg.Wait(); err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  func (s *scpDistributor) filterCopy(mountDir, dest string, host net.IP) error {
   156  	files, err := os.ReadDir(mountDir)
   157  	if err != nil {
   158  		return fmt.Errorf("failed to read dir %s: %s", mountDir, err)
   159  	}
   160  
   161  	for _, f := range files {
   162  		//skip registry directory
   163  		if f.IsDir() && f.Name() == RegistryDirName {
   164  			continue
   165  		}
   166  
   167  		// copy rootfs data
   168  		err = s.infraDriver.Copy(host, filepath.Join(mountDir, f.Name()), filepath.Join(dest, f.Name()))
   169  		if err != nil {
   170  			return fmt.Errorf("failed to copy rootfs files: %v", err)
   171  		}
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func (s *scpDistributor) dumpConfigToRootfs(mountDir string) error {
   178  	return config.NewConfiguration(mountDir).Dump(s.configs)
   179  }
   180  
   181  // using cluster render data to render Rootfs files
   182  func (s *scpDistributor) renderRootfs(mountDir string) error {
   183  	var (
   184  		renderEtc       = filepath.Join(mountDir, "etc")
   185  		renderChart     = filepath.Join(mountDir, "charts")
   186  		renderManifests = filepath.Join(mountDir, "manifests")
   187  		renderData      = s.infraDriver.GetClusterEnv()
   188  	)
   189  
   190  	for _, dir := range []string{renderEtc, renderChart, renderManifests} {
   191  		if osi.IsFileExist(dir) {
   192  			err := env.RenderTemplate(dir, renderData)
   193  			if err != nil {
   194  				return err
   195  			}
   196  		}
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  // writeCacheFlag : write image sha256ID to remote host.
   203  // remoteCacheFile looks like: /var/lib/sealer/data/my-cluster/rootfs/cache/registry/9eb6f8a1ca09559189dd1fed5e587b14
   204  func (s *scpDistributor) writeCacheFlag(localCacheFile, remoteCacheFile string, host net.IP) error {
   205  	if !osi.IsFileExist(localCacheFile) {
   206  		err := osi.NewCommonWriter(localCacheFile).WriteFile([]byte(""))
   207  		if err != nil {
   208  			return fmt.Errorf("failed to write local cache file %s: %v", localCacheFile, err)
   209  		}
   210  	}
   211  
   212  	err := s.infraDriver.Copy(host, localCacheFile, remoteCacheFile)
   213  	if err != nil {
   214  		return fmt.Errorf("failed to copy rootfs cache file: %v", err)
   215  	}
   216  
   217  	logrus.Debugf("successfully write cache file %s on: %s", remoteCacheFile, host.String())
   218  	return nil
   219  }
   220  
   221  func (s *scpDistributor) Restore(targetDir string, hosts []net.IP) error {
   222  	if !s.options.Prune {
   223  		return nil
   224  	}
   225  
   226  	rmRootfsCMD := fmt.Sprintf("rm -rf %s", targetDir)
   227  
   228  	eg, _ := errgroup.WithContext(context.Background())
   229  	for _, ip := range hosts {
   230  		host := ip
   231  		eg.Go(func() error {
   232  			err := s.infraDriver.CmdAsync(host, nil, rmRootfsCMD)
   233  			if err != nil {
   234  				return fmt.Errorf("faild to delete rootfs on host [%s]: %v", host.String(), err)
   235  			}
   236  			return nil
   237  		})
   238  	}
   239  
   240  	if err := eg.Wait(); err != nil {
   241  		return err
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func NewScpDistributor(imageMountInfo []ClusterImageMountInfo, driver infradriver.InfraDriver, configs []v1.Config, options DistributeOption) (Distributor, error) {
   248  	return &scpDistributor{
   249  		configs:          configs,
   250  		imageMountInfo:   imageMountInfo,
   251  		infraDriver:      driver,
   252  		registryCacheDir: filepath.Join(driver.GetClusterRootfsPath(), "cache", RegistryCacheDirName),
   253  		rootfsCacheDir:   filepath.Join(driver.GetClusterRootfsPath(), "cache", RootfsCacheDirName),
   254  		options:          options,
   255  	}, nil
   256  }