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 }