github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/build/buildimage/differ.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 buildimage 16 17 import ( 18 "context" 19 "fmt" 20 "io/fs" 21 "os" 22 "path/filepath" 23 "strings" 24 25 reference2 "github.com/docker/distribution/reference" 26 "github.com/pkg/errors" 27 "golang.org/x/sync/errgroup" 28 29 "github.com/sealerio/sealer/build/layerutils/charts" 30 manifest "github.com/sealerio/sealer/build/layerutils/manifests" 31 "github.com/sealerio/sealer/common" 32 "github.com/sealerio/sealer/pkg/define/application" 33 v12 "github.com/sealerio/sealer/pkg/define/image/v1" 34 "github.com/sealerio/sealer/pkg/image/save" 35 "github.com/sealerio/sealer/pkg/rootfs" 36 v1 "github.com/sealerio/sealer/types/api/v1" 37 osi "github.com/sealerio/sealer/utils/os" 38 ) 39 40 // TODO: update the variable name 41 var ( 42 copyToManifests = "manifests" 43 copyToChart = "charts" 44 copyToImageList = "imageList" 45 copyToApplication = "application" 46 ) 47 48 type parseContainerImageStringSliceFunc func(srcPath string) ([]string, error) 49 type parseContainerImageListFunc func(srcPath string) ([]*v12.ContainerImage, error) 50 51 var parseContainerImageStringSliceFuncMap = map[string]func(srcPath string) ([]string, error){ 52 copyToManifests: parseYamlImages, 53 copyToChart: parseChartImages, 54 copyToImageList: parseRawImageList, 55 copyToApplication: WrapParseContainerImageList2StringSlice(parseApplicationImages), 56 } 57 58 var parseContainerImageListFuncMap = map[string]func(srcPath string) ([]*v12.ContainerImage, error){ 59 copyToManifests: WrapParseStringSlice2ContainerImageList(parseYamlImages), 60 copyToChart: WrapParseStringSlice2ContainerImageList(parseChartImages), 61 copyToImageList: WrapParseStringSlice2ContainerImageList(parseRawImageList), 62 copyToApplication: parseApplicationImages, 63 } 64 65 type Registry struct { 66 platform v1.Platform 67 puller save.ImageSave 68 } 69 70 func NewRegistry(platform v1.Platform) *Registry { 71 ctx := context.Background() 72 return &Registry{ 73 platform: platform, 74 puller: save.NewImageSaver(ctx), 75 } 76 } 77 78 func (r *Registry) SaveImages(rootfs string, containerImages []string) error { 79 return r.puller.SaveImages(containerImages, filepath.Join(rootfs, common.RegistryDirName), r.platform) 80 } 81 82 func WrapParseStringSlice2ContainerImageList(parseFunc parseContainerImageStringSliceFunc) func(srcPath string) ([]*v12.ContainerImage, error) { 83 return func(srcPath string) ([]*v12.ContainerImage, error) { 84 images, err := parseFunc(srcPath) 85 if err != nil { 86 return nil, err 87 } 88 var containerImageList []*v12.ContainerImage 89 for _, image := range images { 90 containerImageList = append(containerImageList, &v12.ContainerImage{ 91 Image: image, 92 AppName: "", 93 }) 94 } 95 return containerImageList, nil 96 } 97 } 98 99 func WrapParseContainerImageList2StringSlice(parseFunc parseContainerImageListFunc) func(srcPath string) ([]string, error) { 100 return func(srcPath string) ([]string, error) { 101 containerImageList, err := parseFunc(srcPath) 102 if err != nil { 103 return nil, err 104 } 105 return v12.GetImageSliceFromContainerImageList(containerImageList), nil 106 } 107 } 108 109 func ParseContainerImageList(srcPath string) ([]*v12.ContainerImage, error) { 110 eg, _ := errgroup.WithContext(context.Background()) 111 112 var containerImageList []*v12.ContainerImage 113 for t, p := range parseContainerImageListFuncMap { 114 dispatchType := t 115 parse := p 116 eg.Go(func() error { 117 parsedImageList, err := parse(srcPath) 118 if err != nil { 119 return fmt.Errorf("failed to parse images from %s: %v", dispatchType, err) 120 } 121 for _, image := range parsedImageList { 122 img, err := reference2.ParseNormalizedNamed(image.Image) 123 if err != nil { 124 continue 125 } 126 containerImageList = append(containerImageList, &v12.ContainerImage{ 127 Image: img.String(), 128 AppName: image.AppName, 129 Platform: image.Platform, 130 }) 131 } 132 return nil 133 }) 134 } 135 if err := eg.Wait(); err != nil { 136 return nil, err 137 } 138 return containerImageList, nil 139 } 140 141 func ParseContainerImageSlice(srcPath string) ([]string, error) { 142 eg, _ := errgroup.WithContext(context.Background()) 143 144 var images []string 145 for t, p := range parseContainerImageStringSliceFuncMap { 146 dispatchType := t 147 parse := p 148 eg.Go(func() error { 149 ima, err := parse(srcPath) 150 if err != nil { 151 return fmt.Errorf("failed to parse images from %s: %v", dispatchType, err) 152 } 153 images = append(images, ima...) 154 return nil 155 }) 156 } 157 if err := eg.Wait(); err != nil { 158 return nil, err 159 } 160 return images, nil 161 } 162 163 func parseApplicationImages(srcPath string) ([]*v12.ContainerImage, error) { 164 applicationPath := filepath.Clean(filepath.Join(srcPath, rootfs.GlobalManager.App().Root())) 165 166 if !osi.IsFileExist(applicationPath) { 167 return nil, nil 168 } 169 170 var ( 171 containerImageList []*v12.ContainerImage 172 err error 173 ) 174 175 entries, err := os.ReadDir(applicationPath) 176 if err != nil { 177 return nil, errors.Wrap(err, "error in readdir in parseApplicationImages") 178 } 179 for _, entry := range entries { 180 name := entry.Name() 181 appPath := filepath.Join(applicationPath, name) 182 if entry.IsDir() { 183 if !isChartArtifactEnough(appPath) { 184 imagesTmp, err := parseApplicationKubeImages(appPath) 185 if err != nil { 186 return nil, errors.Wrapf(err, "failed to parse container image list for app(%s) with type(%s)", 187 name, application.KubeApp) 188 } 189 for _, image := range imagesTmp { 190 containerImageList = append(containerImageList, &v12.ContainerImage{ 191 Image: image, 192 AppName: name, 193 }) 194 } 195 continue 196 } 197 198 imagesTmp, err := parseApplicationHelmImages(appPath) 199 if err != nil { 200 return nil, errors.Wrapf(err, "failed to parse container image list for app(%s) with type(%s)", 201 name, application.HelmApp) 202 } 203 for _, image := range imagesTmp { 204 containerImageList = append(containerImageList, &v12.ContainerImage{ 205 Image: image, 206 AppName: name, 207 }) 208 } 209 } 210 } 211 212 return containerImageList, nil 213 } 214 215 func parseApplicationHelmImages(helmPath string) ([]string, error) { 216 if !osi.IsFileExist(helmPath) { 217 return nil, nil 218 } 219 220 var images []string 221 222 imageSearcher, err := charts.NewCharts() 223 if err != nil { 224 return nil, err 225 } 226 227 err = filepath.Walk(helmPath, func(path string, f fs.FileInfo, err error) error { 228 if err != nil { 229 return err 230 } 231 232 if !f.IsDir() { 233 return nil 234 } 235 236 if isChartArtifactEnough(path) { 237 imgs, err := imageSearcher.ListImages(path) 238 if err != nil { 239 return err 240 } 241 242 images = append(images, imgs...) 243 return filepath.SkipDir 244 } 245 return nil 246 }) 247 if err != nil { 248 return nil, err 249 } 250 return FormatImages(images), nil 251 } 252 253 func parseApplicationKubeImages(kubePath string) ([]string, error) { 254 if !osi.IsFileExist(kubePath) { 255 return nil, nil 256 } 257 var images []string 258 imageSearcher, err := manifest.NewManifests() 259 if err != nil { 260 return nil, err 261 } 262 263 err = filepath.Walk(kubePath, func(path string, f fs.FileInfo, err error) error { 264 if err != nil { 265 return err 266 } 267 if f.IsDir() { 268 return nil 269 } 270 271 ext := strings.ToLower(filepath.Ext(f.Name())) 272 if ext != ".yaml" && ext != ".yml" && ext != ".tmpl" { 273 return nil 274 } 275 276 ima, err := imageSearcher.ListImages(path) 277 278 if err != nil { 279 return err 280 } 281 images = append(images, ima...) 282 return nil 283 }) 284 285 if err != nil { 286 return nil, err 287 } 288 return FormatImages(images), nil 289 } 290 291 func parseChartImages(srcPath string) ([]string, error) { 292 chartsPath := filepath.Join(srcPath, copyToChart) 293 if !osi.IsFileExist(chartsPath) { 294 return nil, nil 295 } 296 297 var images []string 298 imageSearcher, err := charts.NewCharts() 299 if err != nil { 300 return nil, err 301 } 302 303 err = filepath.Walk(chartsPath, func(path string, f fs.FileInfo, err error) error { 304 if err != nil { 305 return err 306 } 307 308 if !f.IsDir() { 309 return nil 310 } 311 312 if isChartArtifactEnough(path) { 313 ima, err := imageSearcher.ListImages(path) 314 if err != nil { 315 return err 316 } 317 images = append(images, ima...) 318 return filepath.SkipDir 319 } 320 return nil 321 }) 322 if err != nil { 323 return nil, err 324 } 325 return FormatImages(images), nil 326 } 327 328 func parseYamlImages(srcPath string) ([]string, error) { 329 manifestsPath := filepath.Join(srcPath, copyToManifests) 330 if !osi.IsFileExist(manifestsPath) { 331 return nil, nil 332 } 333 var images []string 334 335 imageSearcher, err := manifest.NewManifests() 336 if err != nil { 337 return nil, err 338 } 339 340 err = filepath.Walk(manifestsPath, func(path string, f fs.FileInfo, err error) error { 341 if err != nil { 342 return err 343 } 344 if f.IsDir() { 345 return nil 346 } 347 348 ext := strings.ToLower(filepath.Ext(f.Name())) 349 if ext != ".yaml" && ext != ".yml" && ext != ".tmpl" { 350 return nil 351 } 352 353 ima, err := imageSearcher.ListImages(path) 354 355 if err != nil { 356 return err 357 } 358 images = append(images, ima...) 359 return nil 360 }) 361 362 if err != nil { 363 return nil, err 364 } 365 return FormatImages(images), nil 366 } 367 368 func parseRawImageList(srcPath string) ([]string, error) { 369 imageListFilePath := filepath.Join(srcPath, copyToManifests, copyToImageList) 370 if !osi.IsFileExist(imageListFilePath) { 371 return nil, nil 372 } 373 374 images, err := osi.NewFileReader(imageListFilePath).ReadLines() 375 if err != nil { 376 return nil, fmt.Errorf("failed to read file content %s:%v", imageListFilePath, err) 377 } 378 return FormatImages(images), nil 379 } 380 381 var isChartArtifactEnough = func(path string) bool { 382 return osi.IsFileExist(filepath.Join(path, "Chart.yaml")) && 383 osi.IsFileExist(filepath.Join(path, "values.yaml")) && 384 osi.IsFileExist(filepath.Join(path, "templates")) 385 }