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