github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/image/cache/chain.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 cache
    16  
    17  import (
    18  	"sync"
    19  
    20  	"github.com/opencontainers/go-digest"
    21  	"github.com/pkg/errors"
    22  	"sigs.k8s.io/yaml"
    23  
    24  	"github.com/alibaba/sealer/common"
    25  	"github.com/alibaba/sealer/logger"
    26  	"github.com/alibaba/sealer/pkg/image/store"
    27  	v1 "github.com/alibaba/sealer/types/api/v1"
    28  )
    29  
    30  var imageChain *chainStore
    31  var once sync.Once
    32  
    33  //ChainID is caculated from a series of serialized cache layers. The layers cacheID
    34  // is "", but the COPY layer.
    35  // same ChainID indicates that same entire file system.
    36  type ChainID digest.Digest
    37  
    38  func (id ChainID) String() string {
    39  	return id.Digest().String()
    40  }
    41  
    42  // Digest converts ID into a digest
    43  func (id ChainID) Digest() digest.Digest {
    44  	return digest.Digest(id)
    45  }
    46  
    47  type ImageID digest.Digest
    48  
    49  func (id ImageID) String() string {
    50  	return id.Digest().String()
    51  }
    52  
    53  // Digest converts ID into a digest
    54  func (id ImageID) Digest() digest.Digest {
    55  	return digest.Digest(id)
    56  }
    57  
    58  // ChainStore is an interface for manipulating images
    59  type ChainStore interface {
    60  	Images() map[ImageID]*v1.Image
    61  	GetChainLayer(id ChainID) (v1.Layer, error)
    62  }
    63  
    64  type chainItem struct {
    65  	layer   v1.Layer
    66  	chainID ChainID
    67  }
    68  
    69  type chainStore struct {
    70  	sync.RWMutex
    71  	chains map[ChainID]*chainItem
    72  	fs     store.Backend
    73  	ls     store.LayerStore
    74  }
    75  
    76  func NewImageStore(fs store.Backend, ls store.LayerStore) (ChainStore, error) {
    77  	once.Do(func() {
    78  		imageChain = &chainStore{
    79  			chains: make(map[ChainID]*chainItem),
    80  			fs:     fs,
    81  			ls:     ls,
    82  		}
    83  
    84  		imageChain.restore()
    85  	})
    86  	return imageChain, nil
    87  }
    88  
    89  // restore reads all images saved in filesystem and calculate their chainID
    90  func (cs *chainStore) restore() {
    91  	cs.Lock()
    92  	defer cs.Unlock()
    93  
    94  	//read all image layers
    95  	images := cs.Images()
    96  	for _, image := range images {
    97  		layers := image.Spec.Layers
    98  		lastChainItem := &chainItem{}
    99  		for _, layer := range layers {
   100  			var (
   101  				chainID ChainID
   102  				err     error
   103  			)
   104  
   105  			cacheLayer, err := cs.newCacheLayer(layer)
   106  			if err != nil {
   107  				logger.Warn("failed to new a cache layer for %v, err: %s", layer, err)
   108  				continue
   109  			}
   110  
   111  			// first chainItem's parent chainID is empty
   112  			chainID, err = cacheLayer.ChainID(lastChainItem.chainID)
   113  			if err != nil {
   114  				logger.Error(err)
   115  				break
   116  			}
   117  			logger.Debug("current layer %+v, restore chain id: %s", cacheLayer, chainID)
   118  
   119  			_, ok := cs.chains[chainID]
   120  			if !ok {
   121  				cItem := &chainItem{
   122  					layer:   layer,
   123  					chainID: chainID,
   124  				}
   125  				cs.chains[chainID] = cItem
   126  			}
   127  			lastChainItem = &chainItem{
   128  				layer:   layer,
   129  				chainID: chainID,
   130  			}
   131  		}
   132  	}
   133  }
   134  
   135  func (cs *chainStore) GetChainLayer(id ChainID) (v1.Layer, error) {
   136  	cs.RLock()
   137  	defer cs.RUnlock()
   138  
   139  	if imagemeta, ok := cs.chains[id]; ok {
   140  		return imagemeta.layer, nil
   141  	}
   142  
   143  	return v1.Layer{}, errors.Errorf("no layer for chain id %s in file system", id)
   144  }
   145  
   146  func (cs *chainStore) Images() map[ImageID]*v1.Image {
   147  	var (
   148  		images  map[ImageID]*v1.Image
   149  		configs [][]byte
   150  		err     error
   151  	)
   152  
   153  	images = make(map[ImageID]*v1.Image)
   154  	configs, err = cs.fs.ListImages()
   155  	if err != nil {
   156  		logger.Error("failed to get images from file system, err: %v", err)
   157  		return nil
   158  	}
   159  	for _, config := range configs {
   160  		img := &v1.Image{}
   161  		err = yaml.Unmarshal(config, img)
   162  		if err != nil {
   163  			logger.Error("failed to unmarshal bytes into image")
   164  			continue
   165  		}
   166  		dgst := digest.FromBytes(config)
   167  		images[ImageID(dgst)] = img
   168  	}
   169  
   170  	return images
   171  }
   172  
   173  func (cs *chainStore) newCacheLayer(layer v1.Layer) (*Layer, error) {
   174  	var cacheLayer = Layer{Type: layer.Type, Value: layer.Value}
   175  	// only copy layer needs the cache id.
   176  	if layer.Type != common.COPYCOMMAND {
   177  		return &cacheLayer, nil
   178  	}
   179  
   180  	cacheIDBytes, err := cs.fs.GetMetadata(layer.ID, common.CacheID)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	// TODO maybe we should validate the cacheid over digest
   185  	cacheLayer.CacheID = string(cacheIDBytes)
   186  	return &cacheLayer, nil
   187  }
   188  
   189  func CalculateCacheID(cacheLayers []Layer) (ChainID, error) {
   190  	var parentID ChainID
   191  	var err error
   192  
   193  	for _, l := range cacheLayers {
   194  		parentID, err = l.ChainID(parentID)
   195  		if err != nil {
   196  			return "", err
   197  		}
   198  	}
   199  
   200  	return parentID, nil
   201  }