github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/image/store/filesystem.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 store 16 17 import ( 18 "compress/gzip" 19 "encoding/json" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "strconv" 26 "strings" 27 "sync" 28 "time" 29 30 "github.com/docker/docker/pkg/ioutils" 31 "github.com/opencontainers/go-digest" 32 "github.com/pkg/errors" 33 "github.com/vbatts/tar-split/tar/asm" 34 "github.com/vbatts/tar-split/tar/storage" 35 "sigs.k8s.io/yaml" 36 37 "github.com/alibaba/sealer/common" 38 "github.com/alibaba/sealer/logger" 39 "github.com/alibaba/sealer/pkg/image/types" 40 v1 "github.com/alibaba/sealer/types/api/v1" 41 pkgutils "github.com/alibaba/sealer/utils" 42 platUtils "github.com/alibaba/sealer/utils/platform" 43 ) 44 45 // Backend is a service for image/layer read and write. 46 // is majorly used by layer store. 47 // Avoid invoking backend by others as possible as we can. 48 type Backend interface { 49 Get(id digest.Digest) ([]byte, error) 50 Set(data []byte) (digest.Digest, error) 51 Delete(id digest.Digest) error 52 ListImages() ([][]byte, error) 53 SetMetadata(id digest.Digest, key string, data []byte) error 54 GetMetadata(id digest.Digest, key string) ([]byte, error) 55 DeleteMetadata(id digest.Digest, key string) error 56 LayerDBDir(digest digest.Digest) string 57 LayerDataDir(digest digest.Digest) string 58 assembleTar(id LayerID, writer io.Writer) error 59 storeROLayer(layer Layer) error 60 loadAllROLayers() ([]*ROLayer, error) 61 addDistributionMetadata(layerID LayerID, newMetadatas map[string]digest.Digest) error 62 getImageByName(name string, platform *v1.Platform) (*v1.Image, error) 63 getImageByID(id string) (*v1.Image, error) 64 deleteImage(name string, platform *v1.Platform) error 65 deleteImageByID(id string) error 66 saveImage(image v1.Image) error 67 setImageMetadata(name string, metadata *types.ManifestDescriptor) error 68 getImageMetadataItem(name string, platform *v1.Platform) (*types.ManifestDescriptor, error) 69 getImageMetadataMap() (ImageMetadataMap, error) 70 } 71 72 type filesystem struct { 73 sync.RWMutex 74 layerDataRoot string 75 layerDBRoot string 76 imageDBRoot string 77 imageMetadataFilePath string 78 } 79 80 type ImageMetadataMap map[string]*types.ManifestList 81 82 func NewFSStoreBackend() (Backend, error) { 83 return &filesystem{ 84 layerDataRoot: layerDataRoot, 85 layerDBRoot: layerDBRoot, 86 imageDBRoot: imageDBRoot, 87 imageMetadataFilePath: imageMetadataFilePath, 88 }, nil 89 } 90 91 func metadataDir(v interface{}) string { 92 switch val := v.(type) { 93 case digest.Digest: 94 return filepath.Join(imageDBRoot, val.Hex()+common.YamlSuffix) 95 case string: 96 if strings.Contains(val, common.YamlSuffix) { 97 return filepath.Join(imageDBRoot, val) 98 } 99 return filepath.Join(imageDBRoot, val+common.YamlSuffix) 100 } 101 102 return "" 103 } 104 105 func (fs *filesystem) Get(id digest.Digest) ([]byte, error) { 106 var ( 107 metadata []byte 108 err error 109 ) 110 fs.RLock() 111 defer fs.RUnlock() 112 113 //we do not use the functions in pkgutils because the validation steps 114 //in its function is redundant in this situation 115 metadata, err = ioutil.ReadFile(metadataDir(id)) 116 if err != nil { 117 return nil, errors.Errorf("failed to read image %s's metadata, err: %v", id, err) 118 } 119 120 if digest.FromBytes(metadata) != id { 121 return nil, errors.Errorf("failed to verify image %s's hash value", id) 122 } 123 124 return metadata, nil 125 } 126 127 func (fs *filesystem) Set(data []byte) (digest.Digest, error) { 128 var ( 129 dgst digest.Digest 130 err error 131 ) 132 fs.Lock() 133 defer fs.Unlock() 134 135 if len(data) == 0 { 136 return "", errors.Errorf("invalid empty data") 137 } 138 139 dgst = digest.FromBytes(data) 140 if err = ioutil.WriteFile(metadataDir(dgst), data, common.FileMode0644); err != nil { 141 return "", errors.Errorf("failed to write image %s's metadata, err: %v", dgst, err) 142 } 143 144 return dgst, nil 145 } 146 147 func (fs *filesystem) Delete(dgst digest.Digest) error { 148 var ( 149 err error 150 ) 151 fs.Lock() 152 defer fs.Unlock() 153 154 if err = os.RemoveAll(metadataDir(dgst)); err != nil { 155 return errors.Errorf("failed to delete image metadata, err: %v", err) 156 } 157 158 return nil 159 } 160 161 func (fs *filesystem) assembleTar(id LayerID, writer io.Writer) error { 162 var ( 163 tarDataPath = filepath.Join(fs.LayerDBDir(id.ToDigest()), tarDataGZ) 164 layerDataPath = fs.LayerDataDir(id.ToDigest()) 165 ) 166 167 mf, err := os.Open(filepath.Clean(tarDataPath)) 168 if err != nil { 169 return fmt.Errorf("failed to open %s for layer %s, err: %s", tarDataGZ, id, err) 170 } 171 172 mfz, err := gzip.NewReader(mf) 173 if err != nil { 174 err = mf.Close() 175 if err != nil { 176 return err 177 } 178 return err 179 } 180 181 gzipReader := ioutils.NewReadCloserWrapper(mfz, func() error { 182 err := mfz.Close() 183 if err != nil { 184 return err 185 } 186 return mf.Close() 187 }) 188 189 defer gzipReader.Close() 190 metaUnpacker := storage.NewJSONUnpacker(gzipReader) 191 fileGetter := storage.NewPathFileGetter(layerDataPath) 192 return asm.WriteOutputTarStream(fileGetter, metaUnpacker, writer) 193 } 194 195 func (fs *filesystem) ListImages() ([][]byte, error) { 196 var ( 197 configs [][]byte 198 err error 199 fileInfos []os.FileInfo 200 ) 201 fileInfos, err = ioutil.ReadDir(fs.imageDBRoot) 202 if err != nil { 203 return nil, errors.Errorf("failed to open metadata directory %s, err: %v", 204 fs.imageDBRoot, err) 205 } 206 207 for _, fileInfo := range fileInfos { 208 if fileInfo.IsDir() { 209 continue 210 } 211 212 if strings.Contains(fileInfo.Name(), common.YamlSuffix) { 213 config, err := ioutil.ReadFile(metadataDir(fileInfo.Name())) 214 if err != nil { 215 logger.Error("failed to read file %v, err: %v", fileInfo.Name(), err) 216 } 217 configs = append(configs, config) 218 } 219 } 220 221 return configs, nil 222 } 223 224 func (fs *filesystem) SetMetadata(id digest.Digest, key string, data []byte) error { 225 fs.Lock() 226 defer fs.Unlock() 227 228 baseDir := fs.LayerDBDir(id) 229 if err := os.MkdirAll(baseDir, common.FileMode0755); err != nil { 230 return err 231 } 232 233 return ioutil.WriteFile(filepath.Join(baseDir, key), data, common.FileMode0644) 234 } 235 236 func (fs *filesystem) GetMetadata(id digest.Digest, key string) ([]byte, error) { 237 fs.Lock() 238 defer fs.Unlock() 239 240 bytes, err := ioutil.ReadFile(filepath.Clean(filepath.Join(fs.LayerDBDir(id), key))) 241 if err != nil { 242 return nil, errors.Errorf("failed to read metadata, err: %v", err) 243 } 244 245 return bytes, nil 246 } 247 248 func (fs *filesystem) DeleteMetadata(id digest.Digest, key string) error { 249 fs.Lock() 250 defer fs.Unlock() 251 252 return os.RemoveAll(filepath.Join(fs.LayerDBDir(id), key)) 253 } 254 255 func (fs *filesystem) LayerDBDir(digest digest.Digest) string { 256 return filepath.Join(fs.layerDBRoot, digest.Algorithm().String(), digest.Hex()) 257 } 258 259 func (fs *filesystem) LayerDataDir(digest digest.Digest) string { 260 return filepath.Join(fs.layerDataRoot, digest.Hex()) 261 } 262 263 func (fs *filesystem) storeROLayer(layer Layer) error { 264 dig := layer.ID().ToDigest() 265 dbDir := fs.LayerDBDir(dig) 266 err := pkgutils.WriteFile(filepath.Join(dbDir, "size"), []byte(fmt.Sprintf("%d", layer.Size()))) 267 if err != nil { 268 return fmt.Errorf("failed to write size for %s, err: %s", layer.ID(), err) 269 } 270 271 err = fs.addDistributionMetadata(layer.ID(), layer.DistributionMetadata()) 272 if err != nil { 273 return fmt.Errorf("failed to write distribution metadata for %s, err: %s", layer.ID(), err) 274 } 275 276 err = pkgutils.WriteFile(filepath.Join(dbDir, "id"), []byte(layer.ID())) 277 logger.Debug("writing id %s to %s", layer.ID(), filepath.Join(dbDir, "id")) 278 if err != nil { 279 return fmt.Errorf("failed to write id for %s, err: %s", layer.ID(), err) 280 } 281 282 return nil 283 } 284 285 func (fs *filesystem) loadLayerID(layerDBPath string) (LayerID, error) { 286 fs.RLock() 287 defer fs.RUnlock() 288 289 idBytes, err := ioutil.ReadFile(filepath.Clean(filepath.Join(layerDBPath, "id"))) 290 if err != nil { 291 return "", err 292 } 293 dig, err := digest.Parse(string(idBytes)) 294 if err != nil { 295 return "", err 296 } 297 return LayerID(dig), nil 298 } 299 300 func (fs *filesystem) loadLayerSize(layerDBPath string) (int64, error) { 301 fs.RLock() 302 defer fs.RUnlock() 303 304 sizeBytes, err := ioutil.ReadFile(filepath.Clean(filepath.Join(layerDBPath, "size"))) 305 if err != nil { 306 return 0, err 307 } 308 309 size, err := strconv.ParseInt(string(sizeBytes), 10, 64) 310 if err != nil { 311 return 0, err 312 } 313 return size, nil 314 } 315 316 func (fs *filesystem) loadROLayer(layerDBPath string) (*ROLayer, error) { 317 layerID, err := fs.loadLayerID(layerDBPath) 318 if err != nil { 319 return nil, fmt.Errorf("failed to get layer metadata %s, whose id file lost, err: %s", filepath.Base(layerDBPath), err) 320 } 321 322 layerSize, err := fs.loadLayerSize(layerDBPath) 323 if err != nil { 324 return nil, fmt.Errorf("failed to read size of layer %s, err: %s", filepath.Base(layerDBPath), err) 325 } 326 327 metadataMap, err := fs.LoadDistributionMetadata(layerID) 328 if err != nil { 329 // we could tolerate the miss of DistributionMetadata. 330 // the consequence is that we push the layer repeatedly 331 logger.Warn("failed to get layer distribution digest, err: %s", filepath.Base(layerDBPath), err) 332 } 333 334 return NewROLayer( 335 layerID.ToDigest(), 336 layerSize, 337 metadataMap, 338 ) 339 } 340 341 func (fs *filesystem) loadAllROLayers() ([]*ROLayer, error) { 342 layerDirs, err := traverseLayerDB(fs.layerDBRoot) 343 if err != nil { 344 return nil, err 345 } 346 347 var layers []*ROLayer 348 for _, layerDBDir := range layerDirs { 349 rolayer, err := fs.loadROLayer(layerDBDir) 350 if err != nil { 351 logger.Warn(err) 352 continue 353 } 354 layers = append(layers, rolayer) 355 } 356 return layers, nil 357 } 358 359 func (fs *filesystem) getImageMetadataMap() (ImageMetadataMap, error) { 360 imagesMap := make(ImageMetadataMap) 361 // create file if not exists 362 if !pkgutils.IsFileExist(fs.imageMetadataFilePath) { 363 if err := pkgutils.WriteFile(fs.imageMetadataFilePath, []byte("{}")); err != nil { 364 return nil, err 365 } 366 return imagesMap, nil 367 } 368 369 data, err := ioutil.ReadFile(fs.imageMetadataFilePath) 370 if err != nil { 371 return nil, fmt.Errorf("failed to read ImageMetadataMap, err: %s", err) 372 } 373 374 err = json.Unmarshal(data, &imagesMap) 375 if err != nil { 376 return nil, fmt.Errorf("failed to parsing ImageMetadataMap, err: %s", err) 377 } 378 return imagesMap, err 379 } 380 381 func (fs *filesystem) getImageByName(name string, platform *v1.Platform) (*v1.Image, error) { 382 imagesMap, err := fs.getImageMetadataMap() 383 if err != nil { 384 return nil, err 385 } 386 //get an imageId based on the name of ClusterImage 387 image, ok := imagesMap[name] 388 if !ok { 389 return nil, fmt.Errorf("failed to find image by name: %s", name) 390 } 391 392 for _, m := range image.Manifests { 393 if platUtils.Matched(m.Platform, *platform) { 394 if m.ID == "" { 395 return nil, fmt.Errorf("failed to find corresponding image id, id is empty") 396 } 397 ima, err := fs.getImageByID(m.ID) 398 if err != nil { 399 return nil, err 400 } 401 return ima, nil 402 } 403 } 404 405 return nil, fmt.Errorf("platform not matched: %s %s", platform.Architecture, platform.Variant) 406 } 407 408 func (fs *filesystem) getImageByID(id string) (*v1.Image, error) { 409 var ( 410 image v1.Image 411 filename = filepath.Join(fs.imageDBRoot, id+".yaml") 412 ) 413 414 err := pkgutils.UnmarshalYamlFile(filename, &image) 415 if err != nil { 416 return nil, fmt.Errorf("no such image id:%s", id) 417 } 418 419 return &image, nil 420 } 421 422 func (fs *filesystem) deleteImage(name string, platform *v1.Platform) error { 423 imagesMap, err := fs.getImageMetadataMap() 424 if err != nil { 425 return err 426 } 427 428 manifestList, ok := imagesMap[name] 429 if !ok { 430 return nil 431 } 432 433 if platform == nil { 434 delete(imagesMap, name) 435 } else { 436 for index, m := range manifestList.Manifests { 437 if !platUtils.Matched(m.Platform, *platform) { 438 continue 439 } 440 manifestList.Manifests = remove(manifestList.Manifests, index) 441 if len(manifestList.Manifests) == 0 { 442 delete(imagesMap, name) 443 } 444 } 445 } 446 447 data, err := json.MarshalIndent(imagesMap, "", DefaultJSONIndent) 448 if err != nil { 449 return err 450 } 451 452 if err = pkgutils.AtomicWriteFile(fs.imageMetadataFilePath, data, common.FileMode0644); err != nil { 453 return errors.Wrap(err, "failed to write DefaultImageMetadataFile") 454 } 455 return nil 456 } 457 458 func (fs *filesystem) deleteImageByID(id string) error { 459 imagesMap, err := fs.getImageMetadataMap() 460 if err != nil { 461 return err 462 } 463 464 for name, manifestList := range imagesMap { 465 mm := manifestList.Manifests 466 for index, m := range manifestList.Manifests { 467 if m.ID == id { 468 manifestList.Manifests = remove(mm, index) 469 if len(manifestList.Manifests) == 0 { 470 delete(imagesMap, name) 471 } 472 break 473 } 474 } 475 } 476 477 data, err := json.MarshalIndent(imagesMap, "", DefaultJSONIndent) 478 if err != nil { 479 return err 480 } 481 482 if err = pkgutils.AtomicWriteFile(fs.imageMetadataFilePath, data, common.FileMode0644); err != nil { 483 return errors.Wrap(err, "failed to write DefaultImageMetadataFile") 484 } 485 return nil 486 } 487 488 func (fs *filesystem) getImageMetadataItem(name string, platform *v1.Platform) (*types.ManifestDescriptor, error) { 489 imageMetadataMap, err := fs.getImageMetadataMap() 490 if err != nil { 491 return nil, err 492 } 493 494 manifestList, ok := imageMetadataMap[name] 495 if !ok { 496 return nil, fmt.Errorf("image %s not found", name) 497 } 498 499 for _, m := range manifestList.Manifests { 500 if platUtils.Matched(m.Platform, *platform) { 501 return m, nil 502 } 503 } 504 505 return nil, &types.ImageNameOrIDNotFoundError{Name: name} 506 } 507 508 func (fs *filesystem) setImageMetadata(name string, metadata *types.ManifestDescriptor) error { 509 metadata.CREATED = time.Now() 510 imagesMap, err := fs.getImageMetadataMap() 511 if err != nil { 512 return err 513 } 514 var changed bool 515 manifestList, ok := imagesMap[name] 516 // first save 517 if !ok { 518 var ml []*types.ManifestDescriptor 519 ml = append(ml, metadata) 520 manifestList = &types.ManifestList{Manifests: ml} 521 } else { 522 // modify the existed image 523 for _, m := range manifestList.Manifests { 524 if platUtils.Matched(m.Platform, metadata.Platform) { 525 m.ID = metadata.ID 526 m.CREATED = metadata.CREATED 527 m.SIZE = metadata.SIZE 528 changed = true 529 } 530 } 531 if !changed { 532 manifestList.Manifests = append(manifestList.Manifests, metadata) 533 } 534 } 535 imagesMap[name] = manifestList 536 data, err := json.MarshalIndent(imagesMap, "", DefaultJSONIndent) 537 if err != nil { 538 return err 539 } 540 541 if err = pkgutils.AtomicWriteFile(fs.imageMetadataFilePath, data, common.FileMode0644); err != nil { 542 return errors.Wrap(err, "failed to write DefaultImageMetadataFile") 543 } 544 return nil 545 } 546 547 func (fs *filesystem) saveImage(image v1.Image) error { 548 err := saveImageYaml(image, fs.imageDBRoot) 549 if err != nil { 550 return err 551 } 552 var res []string 553 for _, layer := range image.Spec.Layers { 554 if layer.ID != "" { 555 res = append(res, filepath.Join(common.DefaultLayerDir, layer.ID.Hex())) 556 } 557 } 558 size, err := pkgutils.GetFilesSize(res) 559 if err != nil { 560 return fmt.Errorf("failed to get image %s size, %v", image.Name, err) 561 } 562 return fs.setImageMetadata(image.Name, &types.ManifestDescriptor{ID: image.Spec.ID, SIZE: size, Platform: image.Spec.Platform}) 563 } 564 565 func saveImageYaml(image v1.Image, dir string) error { 566 imageYaml, err := yaml.Marshal(image) 567 if err != nil { 568 return err 569 } 570 571 err = os.MkdirAll(dir, common.FileMode0755) 572 if err != nil { 573 return err 574 } 575 576 return pkgutils.AtomicWriteFile(filepath.Join(dir, image.Spec.ID+common.YamlSuffix), imageYaml, common.FileMode0644) 577 } 578 579 func remove(slice []*types.ManifestDescriptor, s int) []*types.ManifestDescriptor { 580 return append(slice[:s], slice[s+1:]...) 581 }