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  }