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 }