github.com/rish1988/moby@v25.0.2+incompatible/image/store.go (about) 1 package image // import "github.com/docker/docker/image" 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/containerd/log" 10 "github.com/docker/docker/errdefs" 11 "github.com/docker/docker/layer" 12 "github.com/opencontainers/go-digest" 13 "github.com/opencontainers/go-digest/digestset" 14 "github.com/pkg/errors" 15 ) 16 17 // Store is an interface for creating and accessing images 18 type Store interface { 19 Create(config []byte) (ID, error) 20 Get(id ID) (*Image, error) 21 Delete(id ID) ([]layer.Metadata, error) 22 Search(partialID string) (ID, error) 23 SetParent(id ID, parent ID) error 24 GetParent(id ID) (ID, error) 25 SetLastUpdated(id ID) error 26 GetLastUpdated(id ID) (time.Time, error) 27 Children(id ID) []ID 28 Map() map[ID]*Image 29 Heads() map[ID]*Image 30 Len() int 31 } 32 33 // LayerGetReleaser is a minimal interface for getting and releasing images. 34 type LayerGetReleaser interface { 35 Get(layer.ChainID) (layer.Layer, error) 36 Release(layer.Layer) ([]layer.Metadata, error) 37 } 38 39 type imageMeta struct { 40 layer layer.Layer 41 children map[ID]struct{} 42 } 43 44 type store struct { 45 sync.RWMutex 46 lss LayerGetReleaser 47 images map[ID]*imageMeta 48 fs StoreBackend 49 digestSet *digestset.Set 50 } 51 52 // NewImageStore returns new store object for given set of layer stores 53 func NewImageStore(fs StoreBackend, lss LayerGetReleaser) (Store, error) { 54 is := &store{ 55 lss: lss, 56 images: make(map[ID]*imageMeta), 57 fs: fs, 58 digestSet: digestset.NewSet(), 59 } 60 61 // load all current images and retain layers 62 if err := is.restore(); err != nil { 63 return nil, err 64 } 65 66 return is, nil 67 } 68 69 func (is *store) restore() error { 70 // As the code below is run when restoring all images (which can be "many"), 71 // constructing the "log.G(ctx).WithFields" is deliberately not "DRY", as the 72 // logger is only used for error-cases, and we don't want to do allocations 73 // if we don't need it. The "f" type alias is here is just for convenience, 74 // and to make the code _slightly_ more DRY. See the discussion on GitHub; 75 // https://github.com/moby/moby/pull/44426#discussion_r1059519071 76 type f = log.Fields 77 err := is.fs.Walk(func(dgst digest.Digest) error { 78 img, err := is.Get(ID(dgst)) 79 if err != nil { 80 log.G(context.TODO()).WithFields(f{"digest": dgst, "err": err}).Error("invalid image") 81 return nil 82 } 83 var l layer.Layer 84 if chainID := img.RootFS.ChainID(); chainID != "" { 85 if err := CheckOS(img.OperatingSystem()); err != nil { 86 log.G(context.TODO()).WithFields(f{"chainID": chainID, "os": img.OperatingSystem()}).Error("not restoring image with unsupported operating system") 87 return nil 88 } 89 l, err = is.lss.Get(chainID) 90 if err != nil { 91 if errors.Is(err, layer.ErrLayerDoesNotExist) { 92 log.G(context.TODO()).WithFields(f{"chainID": chainID, "os": img.OperatingSystem(), "err": err}).Error("not restoring image") 93 return nil 94 } 95 return err 96 } 97 } 98 if err := is.digestSet.Add(dgst); err != nil { 99 return err 100 } 101 102 is.images[ID(dgst)] = &imageMeta{ 103 layer: l, 104 children: make(map[ID]struct{}), 105 } 106 107 return nil 108 }) 109 if err != nil { 110 return err 111 } 112 113 // Second pass to fill in children maps 114 for id := range is.images { 115 if parent, err := is.GetParent(id); err == nil { 116 if parentMeta := is.images[parent]; parentMeta != nil { 117 parentMeta.children[id] = struct{}{} 118 } 119 } 120 } 121 122 return nil 123 } 124 125 func (is *store) Create(config []byte) (ID, error) { 126 var img *Image 127 img, err := NewFromJSON(config) 128 if err != nil { 129 return "", err 130 } 131 132 // Must reject any config that references diffIDs from the history 133 // which aren't among the rootfs layers. 134 rootFSLayers := make(map[layer.DiffID]struct{}) 135 for _, diffID := range img.RootFS.DiffIDs { 136 rootFSLayers[diffID] = struct{}{} 137 } 138 139 layerCounter := 0 140 for _, h := range img.History { 141 if !h.EmptyLayer { 142 layerCounter++ 143 } 144 } 145 if layerCounter > len(img.RootFS.DiffIDs) { 146 return "", errdefs.InvalidParameter(errors.New("too many non-empty layers in History section")) 147 } 148 149 imageDigest, err := is.fs.Set(config) 150 if err != nil { 151 return "", errdefs.InvalidParameter(err) 152 } 153 154 is.Lock() 155 defer is.Unlock() 156 157 imageID := ID(imageDigest) 158 if _, exists := is.images[imageID]; exists { 159 return imageID, nil 160 } 161 162 layerID := img.RootFS.ChainID() 163 164 var l layer.Layer 165 if layerID != "" { 166 if err := CheckOS(img.OperatingSystem()); err != nil { 167 return "", err 168 } 169 l, err = is.lss.Get(layerID) 170 if err != nil { 171 return "", errdefs.InvalidParameter(errors.Wrapf(err, "failed to get layer %s", layerID)) 172 } 173 } 174 175 is.images[imageID] = &imageMeta{ 176 layer: l, 177 children: make(map[ID]struct{}), 178 } 179 180 if err = is.digestSet.Add(imageDigest); err != nil { 181 delete(is.images, imageID) 182 return "", errdefs.InvalidParameter(err) 183 } 184 185 return imageID, nil 186 } 187 188 type imageNotFoundError string 189 190 func (e imageNotFoundError) Error() string { 191 return "No such image: " + string(e) 192 } 193 194 func (imageNotFoundError) NotFound() {} 195 196 func (is *store) Search(term string) (ID, error) { 197 dgst, err := is.digestSet.Lookup(term) 198 if err != nil { 199 if err == digestset.ErrDigestNotFound { 200 err = imageNotFoundError(term) 201 } 202 return "", errors.WithStack(err) 203 } 204 return ID(dgst), nil 205 } 206 207 func (is *store) Get(id ID) (*Image, error) { 208 // todo: Check if image is in images 209 // todo: Detect manual insertions and start using them 210 config, err := is.fs.Get(id.Digest()) 211 if err != nil { 212 return nil, errdefs.NotFound(err) 213 } 214 215 img, err := NewFromJSON(config) 216 if err != nil { 217 return nil, errdefs.InvalidParameter(err) 218 } 219 img.computedID = id 220 221 img.Parent, err = is.GetParent(id) 222 if err != nil { 223 img.Parent = "" 224 } 225 226 return img, nil 227 } 228 229 func (is *store) Delete(id ID) ([]layer.Metadata, error) { 230 is.Lock() 231 defer is.Unlock() 232 233 imgMeta := is.images[id] 234 if imgMeta == nil { 235 return nil, errdefs.NotFound(fmt.Errorf("unrecognized image ID %s", id.String())) 236 } 237 _, err := is.Get(id) 238 if err != nil { 239 return nil, errdefs.NotFound(fmt.Errorf("unrecognized image %s, %v", id.String(), err)) 240 } 241 for cID := range imgMeta.children { 242 is.fs.DeleteMetadata(cID.Digest(), "parent") 243 } 244 if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil { 245 delete(is.images[parent].children, id) 246 } 247 248 if err := is.digestSet.Remove(id.Digest()); err != nil { 249 log.G(context.TODO()).Errorf("error removing %s from digest set: %q", id, err) 250 } 251 delete(is.images, id) 252 is.fs.Delete(id.Digest()) 253 254 if imgMeta.layer != nil { 255 return is.lss.Release(imgMeta.layer) 256 } 257 return nil, nil 258 } 259 260 func (is *store) SetParent(id, parentID ID) error { 261 is.Lock() 262 defer is.Unlock() 263 parentMeta := is.images[parentID] 264 if parentMeta == nil { 265 return errdefs.NotFound(fmt.Errorf("unknown parent image ID %s", parentID.String())) 266 } 267 if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil { 268 delete(is.images[parent].children, id) 269 } 270 parentMeta.children[id] = struct{}{} 271 return is.fs.SetMetadata(id.Digest(), "parent", []byte(parentID)) 272 } 273 274 func (is *store) GetParent(id ID) (ID, error) { 275 d, err := is.fs.GetMetadata(id.Digest(), "parent") 276 if err != nil { 277 return "", errdefs.NotFound(err) 278 } 279 return ID(d), nil // todo: validate? 280 } 281 282 // SetLastUpdated time for the image ID to the current time 283 func (is *store) SetLastUpdated(id ID) error { 284 lastUpdated := []byte(time.Now().Format(time.RFC3339Nano)) 285 return is.fs.SetMetadata(id.Digest(), "lastUpdated", lastUpdated) 286 } 287 288 // GetLastUpdated time for the image ID 289 func (is *store) GetLastUpdated(id ID) (time.Time, error) { 290 bytes, err := is.fs.GetMetadata(id.Digest(), "lastUpdated") 291 if err != nil || len(bytes) == 0 { 292 // No lastUpdated time 293 return time.Time{}, nil 294 } 295 return time.Parse(time.RFC3339Nano, string(bytes)) 296 } 297 298 func (is *store) Children(id ID) []ID { 299 is.RLock() 300 defer is.RUnlock() 301 302 return is.children(id) 303 } 304 305 func (is *store) children(id ID) []ID { 306 var ids []ID 307 if is.images[id] != nil { 308 for id := range is.images[id].children { 309 ids = append(ids, id) 310 } 311 } 312 return ids 313 } 314 315 func (is *store) Heads() map[ID]*Image { 316 return is.imagesMap(false) 317 } 318 319 func (is *store) Map() map[ID]*Image { 320 return is.imagesMap(true) 321 } 322 323 func (is *store) imagesMap(all bool) map[ID]*Image { 324 is.RLock() 325 defer is.RUnlock() 326 327 images := make(map[ID]*Image) 328 329 for id := range is.images { 330 if !all && len(is.children(id)) > 0 { 331 continue 332 } 333 img, err := is.Get(id) 334 if err != nil { 335 log.G(context.TODO()).Errorf("invalid image access: %q, error: %q", id, err) 336 continue 337 } 338 images[id] = img 339 } 340 return images 341 } 342 343 func (is *store) Len() int { 344 is.RLock() 345 defer is.RUnlock() 346 return len(is.images) 347 }