github.com/docker/docker-ce@v17.12.1-ce-rc2+incompatible/components/engine/image/store.go (about) 1 package image 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 "sync" 8 "time" 9 10 "github.com/docker/distribution/digestset" 11 "github.com/docker/docker/layer" 12 "github.com/docker/docker/pkg/system" 13 "github.com/opencontainers/go-digest" 14 "github.com/pkg/errors" 15 "github.com/sirupsen/logrus" 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 Children(id ID) []ID 29 Map() map[ID]*Image 30 Heads() map[ID]*Image 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 ls LayerGetReleaser 47 images map[ID]*imageMeta 48 fs StoreBackend 49 digestSet *digestset.Set 50 os string 51 } 52 53 // NewImageStore returns new store object for given layer store 54 func NewImageStore(fs StoreBackend, os string, ls LayerGetReleaser) (Store, error) { 55 is := &store{ 56 ls: ls, 57 images: make(map[ID]*imageMeta), 58 fs: fs, 59 digestSet: digestset.NewSet(), 60 os: os, 61 } 62 63 // load all current images and retain layers 64 if err := is.restore(); err != nil { 65 return nil, err 66 } 67 68 return is, nil 69 } 70 71 func (is *store) restore() error { 72 err := is.fs.Walk(func(dgst digest.Digest) error { 73 img, err := is.Get(IDFromDigest(dgst)) 74 if err != nil { 75 logrus.Errorf("invalid image %v, %v", dgst, err) 76 return nil 77 } 78 var l layer.Layer 79 if chainID := img.RootFS.ChainID(); chainID != "" { 80 l, err = is.ls.Get(chainID) 81 if err != nil { 82 return err 83 } 84 } 85 if err := is.digestSet.Add(dgst); err != nil { 86 return err 87 } 88 89 imageMeta := &imageMeta{ 90 layer: l, 91 children: make(map[ID]struct{}), 92 } 93 94 is.images[IDFromDigest(dgst)] = imageMeta 95 96 return nil 97 }) 98 if err != nil { 99 return err 100 } 101 102 // Second pass to fill in children maps 103 for id := range is.images { 104 if parent, err := is.GetParent(id); err == nil { 105 if parentMeta := is.images[parent]; parentMeta != nil { 106 parentMeta.children[id] = struct{}{} 107 } 108 } 109 } 110 111 return nil 112 } 113 114 func (is *store) Create(config []byte) (ID, error) { 115 var img Image 116 err := json.Unmarshal(config, &img) 117 if err != nil { 118 return "", err 119 } 120 121 // TODO @jhowardmsft - LCOW Support. This will need revisiting when coalescing the image stores. 122 // Integrity check - ensure we are creating something for the correct platform 123 if system.LCOWSupported() { 124 if strings.ToLower(img.OperatingSystem()) != strings.ToLower(is.os) { 125 return "", fmt.Errorf("cannot create entry for operating system %q in image store for operating system %q", img.OperatingSystem(), is.os) 126 } 127 } 128 129 // Must reject any config that references diffIDs from the history 130 // which aren't among the rootfs layers. 131 rootFSLayers := make(map[layer.DiffID]struct{}) 132 for _, diffID := range img.RootFS.DiffIDs { 133 rootFSLayers[diffID] = struct{}{} 134 } 135 136 layerCounter := 0 137 for _, h := range img.History { 138 if !h.EmptyLayer { 139 layerCounter++ 140 } 141 } 142 if layerCounter > len(img.RootFS.DiffIDs) { 143 return "", errors.New("too many non-empty layers in History section") 144 } 145 146 dgst, err := is.fs.Set(config) 147 if err != nil { 148 return "", err 149 } 150 imageID := IDFromDigest(dgst) 151 152 is.Lock() 153 defer is.Unlock() 154 155 if _, exists := is.images[imageID]; exists { 156 return imageID, nil 157 } 158 159 layerID := img.RootFS.ChainID() 160 161 var l layer.Layer 162 if layerID != "" { 163 l, err = is.ls.Get(layerID) 164 if err != nil { 165 return "", errors.Wrapf(err, "failed to get layer %s", layerID) 166 } 167 } 168 169 imageMeta := &imageMeta{ 170 layer: l, 171 children: make(map[ID]struct{}), 172 } 173 174 is.images[imageID] = imageMeta 175 if err := is.digestSet.Add(imageID.Digest()); err != nil { 176 delete(is.images, imageID) 177 return "", err 178 } 179 180 return imageID, nil 181 } 182 183 type imageNotFoundError string 184 185 func (e imageNotFoundError) Error() string { 186 return "No such image: " + string(e) 187 } 188 189 func (imageNotFoundError) NotFound() {} 190 191 func (is *store) Search(term string) (ID, error) { 192 dgst, err := is.digestSet.Lookup(term) 193 if err != nil { 194 if err == digestset.ErrDigestNotFound { 195 err = imageNotFoundError(term) 196 } 197 return "", errors.WithStack(err) 198 } 199 return IDFromDigest(dgst), nil 200 } 201 202 func (is *store) Get(id ID) (*Image, error) { 203 // todo: Check if image is in images 204 // todo: Detect manual insertions and start using them 205 config, err := is.fs.Get(id.Digest()) 206 if err != nil { 207 return nil, err 208 } 209 210 img, err := NewFromJSON(config) 211 if err != nil { 212 return nil, err 213 } 214 img.computedID = id 215 216 img.Parent, err = is.GetParent(id) 217 if err != nil { 218 img.Parent = "" 219 } 220 221 return img, nil 222 } 223 224 func (is *store) Delete(id ID) ([]layer.Metadata, error) { 225 is.Lock() 226 defer is.Unlock() 227 228 imageMeta := is.images[id] 229 if imageMeta == nil { 230 return nil, fmt.Errorf("unrecognized image ID %s", id.String()) 231 } 232 for id := range imageMeta.children { 233 is.fs.DeleteMetadata(id.Digest(), "parent") 234 } 235 if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil { 236 delete(is.images[parent].children, id) 237 } 238 239 if err := is.digestSet.Remove(id.Digest()); err != nil { 240 logrus.Errorf("error removing %s from digest set: %q", id, err) 241 } 242 delete(is.images, id) 243 is.fs.Delete(id.Digest()) 244 245 if imageMeta.layer != nil { 246 return is.ls.Release(imageMeta.layer) 247 } 248 return nil, nil 249 } 250 251 func (is *store) SetParent(id, parent ID) error { 252 is.Lock() 253 defer is.Unlock() 254 parentMeta := is.images[parent] 255 if parentMeta == nil { 256 return fmt.Errorf("unknown parent image ID %s", parent.String()) 257 } 258 if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil { 259 delete(is.images[parent].children, id) 260 } 261 parentMeta.children[id] = struct{}{} 262 return is.fs.SetMetadata(id.Digest(), "parent", []byte(parent)) 263 } 264 265 func (is *store) GetParent(id ID) (ID, error) { 266 d, err := is.fs.GetMetadata(id.Digest(), "parent") 267 if err != nil { 268 return "", err 269 } 270 return ID(d), nil // todo: validate? 271 } 272 273 // SetLastUpdated time for the image ID to the current time 274 func (is *store) SetLastUpdated(id ID) error { 275 lastUpdated := []byte(time.Now().Format(time.RFC3339Nano)) 276 return is.fs.SetMetadata(id.Digest(), "lastUpdated", lastUpdated) 277 } 278 279 // GetLastUpdated time for the image ID 280 func (is *store) GetLastUpdated(id ID) (time.Time, error) { 281 bytes, err := is.fs.GetMetadata(id.Digest(), "lastUpdated") 282 if err != nil || len(bytes) == 0 { 283 // No lastUpdated time 284 return time.Time{}, nil 285 } 286 return time.Parse(time.RFC3339Nano, string(bytes)) 287 } 288 289 func (is *store) Children(id ID) []ID { 290 is.RLock() 291 defer is.RUnlock() 292 293 return is.children(id) 294 } 295 296 func (is *store) children(id ID) []ID { 297 var ids []ID 298 if is.images[id] != nil { 299 for id := range is.images[id].children { 300 ids = append(ids, id) 301 } 302 } 303 return ids 304 } 305 306 func (is *store) Heads() map[ID]*Image { 307 return is.imagesMap(false) 308 } 309 310 func (is *store) Map() map[ID]*Image { 311 return is.imagesMap(true) 312 } 313 314 func (is *store) imagesMap(all bool) map[ID]*Image { 315 is.RLock() 316 defer is.RUnlock() 317 318 images := make(map[ID]*Image) 319 320 for id := range is.images { 321 if !all && len(is.children(id)) > 0 { 322 continue 323 } 324 img, err := is.Get(id) 325 if err != nil { 326 logrus.Errorf("invalid image access: %q, error: %q", id, err) 327 continue 328 } 329 images[id] = img 330 } 331 return images 332 }