github.com/nf/docker@v1.8.1/graph/graph.go (about) 1 package graph 2 3 import ( 4 "compress/gzip" 5 "crypto/sha256" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strconv" 15 "strings" 16 "sync" 17 "time" 18 19 "github.com/Sirupsen/logrus" 20 "github.com/docker/distribution/digest" 21 "github.com/docker/docker/autogen/dockerversion" 22 "github.com/docker/docker/daemon/graphdriver" 23 "github.com/docker/docker/image" 24 "github.com/docker/docker/pkg/archive" 25 "github.com/docker/docker/pkg/progressreader" 26 "github.com/docker/docker/pkg/streamformatter" 27 "github.com/docker/docker/pkg/stringid" 28 "github.com/docker/docker/pkg/system" 29 "github.com/docker/docker/pkg/truncindex" 30 "github.com/docker/docker/runconfig" 31 "github.com/vbatts/tar-split/tar/asm" 32 "github.com/vbatts/tar-split/tar/storage" 33 ) 34 35 // The type is used to protect pulling or building related image 36 // layers from deleteing when filtered by dangling=true 37 // The key of layers is the images ID which is pulling or building 38 // The value of layers is a slice which hold layer IDs referenced to 39 // pulling or building images 40 type retainedLayers struct { 41 layerHolders map[string]map[string]struct{} // map[layerID]map[sessionID] 42 sync.Mutex 43 } 44 45 func (r *retainedLayers) Add(sessionID string, layerIDs []string) { 46 r.Lock() 47 defer r.Unlock() 48 for _, layerID := range layerIDs { 49 if r.layerHolders[layerID] == nil { 50 r.layerHolders[layerID] = map[string]struct{}{} 51 } 52 r.layerHolders[layerID][sessionID] = struct{}{} 53 } 54 } 55 56 func (r *retainedLayers) Delete(sessionID string, layerIDs []string) { 57 r.Lock() 58 defer r.Unlock() 59 for _, layerID := range layerIDs { 60 holders, ok := r.layerHolders[layerID] 61 if !ok { 62 continue 63 } 64 delete(holders, sessionID) 65 if len(holders) == 0 { 66 delete(r.layerHolders, layerID) // Delete any empty reference set. 67 } 68 } 69 } 70 71 func (r *retainedLayers) Exists(layerID string) bool { 72 r.Lock() 73 _, exists := r.layerHolders[layerID] 74 r.Unlock() 75 return exists 76 } 77 78 // A Graph is a store for versioned filesystem images and the relationship between them. 79 type Graph struct { 80 root string 81 idIndex *truncindex.TruncIndex 82 driver graphdriver.Driver 83 imageMutex imageMutex // protect images in driver. 84 retained *retainedLayers 85 } 86 87 // file names for ./graph/<ID>/ 88 const ( 89 jsonFileName = "json" 90 layersizeFileName = "layersize" 91 digestFileName = "checksum" 92 tarDataFileName = "tar-data.json.gz" 93 ) 94 95 var ( 96 // ErrDigestNotSet is used when request the digest for a layer 97 // but the layer has no digest value or content to compute the 98 // the digest. 99 ErrDigestNotSet = errors.New("digest is not set for layer") 100 ) 101 102 // NewGraph instantiates a new graph at the given root path in the filesystem. 103 // `root` will be created if it doesn't exist. 104 func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) { 105 abspath, err := filepath.Abs(root) 106 if err != nil { 107 return nil, err 108 } 109 // Create the root directory if it doesn't exists 110 if err := system.MkdirAll(root, 0700); err != nil && !os.IsExist(err) { 111 return nil, err 112 } 113 114 graph := &Graph{ 115 root: abspath, 116 idIndex: truncindex.NewTruncIndex([]string{}), 117 driver: driver, 118 retained: &retainedLayers{layerHolders: make(map[string]map[string]struct{})}, 119 } 120 if err := graph.restore(); err != nil { 121 return nil, err 122 } 123 return graph, nil 124 } 125 126 // IsHeld returns whether the given layerID is being used by an ongoing pull or build. 127 func (graph *Graph) IsHeld(layerID string) bool { 128 return graph.retained.Exists(layerID) 129 } 130 131 func (graph *Graph) restore() error { 132 dir, err := ioutil.ReadDir(graph.root) 133 if err != nil { 134 return err 135 } 136 var ids = []string{} 137 for _, v := range dir { 138 id := v.Name() 139 if graph.driver.Exists(id) { 140 ids = append(ids, id) 141 } 142 } 143 144 baseIds, err := graph.restoreBaseImages() 145 if err != nil { 146 return err 147 } 148 ids = append(ids, baseIds...) 149 150 graph.idIndex = truncindex.NewTruncIndex(ids) 151 logrus.Debugf("Restored %d elements", len(ids)) 152 return nil 153 } 154 155 // FIXME: Implement error subclass instead of looking at the error text 156 // Note: This is the way golang implements os.IsNotExists on Plan9 157 func (graph *Graph) IsNotExist(err error, id string) bool { 158 return err != nil && (strings.Contains(strings.ToLower(err.Error()), "does not exist") || strings.Contains(strings.ToLower(err.Error()), "no such")) && strings.Contains(err.Error(), id) 159 } 160 161 // Exists returns true if an image is registered at the given id. 162 // If the image doesn't exist or if an error is encountered, false is returned. 163 func (graph *Graph) Exists(id string) bool { 164 if _, err := graph.Get(id); err != nil { 165 return false 166 } 167 return true 168 } 169 170 // Get returns the image with the given id, or an error if the image doesn't exist. 171 func (graph *Graph) Get(name string) (*image.Image, error) { 172 id, err := graph.idIndex.Get(name) 173 if err != nil { 174 return nil, fmt.Errorf("could not find image: %v", err) 175 } 176 img, err := graph.loadImage(id) 177 if err != nil { 178 return nil, err 179 } 180 if img.ID != id { 181 return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID) 182 } 183 184 if img.Size < 0 { 185 size, err := graph.driver.DiffSize(img.ID, img.Parent) 186 if err != nil { 187 return nil, fmt.Errorf("unable to calculate size of image id %q: %s", img.ID, err) 188 } 189 190 img.Size = size 191 if err := graph.saveSize(graph.imageRoot(id), int(img.Size)); err != nil { 192 return nil, err 193 } 194 } 195 return img, nil 196 } 197 198 // Create creates a new image and registers it in the graph. 199 func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) { 200 img := &image.Image{ 201 ID: stringid.GenerateRandomID(), 202 Comment: comment, 203 Created: time.Now().UTC(), 204 DockerVersion: dockerversion.VERSION, 205 Author: author, 206 Config: config, 207 Architecture: runtime.GOARCH, 208 OS: runtime.GOOS, 209 } 210 211 if containerID != "" { 212 img.Parent = containerImage 213 img.Container = containerID 214 img.ContainerConfig = *containerConfig 215 } 216 217 if err := graph.Register(img, layerData); err != nil { 218 return nil, err 219 } 220 return img, nil 221 } 222 223 // Register imports a pre-existing image into the graph. 224 func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) { 225 226 if err := image.ValidateID(img.ID); err != nil { 227 return err 228 } 229 230 // We need this entire operation to be atomic within the engine. Note that 231 // this doesn't mean Register is fully safe yet. 232 graph.imageMutex.Lock(img.ID) 233 defer graph.imageMutex.Unlock(img.ID) 234 235 // The returned `error` must be named in this function's signature so that 236 // `err` is not shadowed in this deferred cleanup. 237 defer func() { 238 // If any error occurs, remove the new dir from the driver. 239 // Don't check for errors since the dir might not have been created. 240 if err != nil { 241 graph.driver.Remove(img.ID) 242 } 243 }() 244 245 // (This is a convenience to save time. Race conditions are taken care of by os.Rename) 246 if graph.Exists(img.ID) { 247 return fmt.Errorf("Image %s already exists", img.ID) 248 } 249 250 // Ensure that the image root does not exist on the filesystem 251 // when it is not registered in the graph. 252 // This is common when you switch from one graph driver to another 253 if err := os.RemoveAll(graph.imageRoot(img.ID)); err != nil && !os.IsNotExist(err) { 254 return err 255 } 256 257 // If the driver has this ID but the graph doesn't, remove it from the driver to start fresh. 258 // (the graph is the source of truth). 259 // Ignore errors, since we don't know if the driver correctly returns ErrNotExist. 260 // (FIXME: make that mandatory for drivers). 261 graph.driver.Remove(img.ID) 262 263 tmp, err := graph.mktemp("") 264 defer os.RemoveAll(tmp) 265 if err != nil { 266 return fmt.Errorf("mktemp failed: %s", err) 267 } 268 269 // Create root filesystem in the driver 270 if err := createRootFilesystemInDriver(graph, img, layerData); err != nil { 271 return err 272 } 273 274 // Apply the diff/layer 275 if err := graph.storeImage(img, layerData, tmp); err != nil { 276 return err 277 } 278 // Commit 279 if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil { 280 return err 281 } 282 graph.idIndex.Add(img.ID) 283 return nil 284 } 285 286 // TempLayerArchive creates a temporary archive of the given image's filesystem layer. 287 // The archive is stored on disk and will be automatically deleted as soon as has been read. 288 // If output is not nil, a human-readable progress bar will be written to it. 289 func (graph *Graph) TempLayerArchive(id string, sf *streamformatter.StreamFormatter, output io.Writer) (*archive.TempArchive, error) { 290 image, err := graph.Get(id) 291 if err != nil { 292 return nil, err 293 } 294 tmp, err := graph.mktemp("") 295 if err != nil { 296 return nil, err 297 } 298 a, err := graph.TarLayer(image) 299 if err != nil { 300 return nil, err 301 } 302 progressReader := progressreader.New(progressreader.Config{ 303 In: a, 304 Out: output, 305 Formatter: sf, 306 Size: 0, 307 NewLines: false, 308 ID: stringid.TruncateID(id), 309 Action: "Buffering to disk", 310 }) 311 defer progressReader.Close() 312 return archive.NewTempArchive(progressReader, tmp) 313 } 314 315 // mktemp creates a temporary sub-directory inside the graph's filesystem. 316 func (graph *Graph) mktemp(id string) (string, error) { 317 dir := filepath.Join(graph.root, "_tmp", stringid.GenerateRandomID()) 318 if err := system.MkdirAll(dir, 0700); err != nil { 319 return "", err 320 } 321 return dir, nil 322 } 323 324 func (graph *Graph) newTempFile() (*os.File, error) { 325 tmp, err := graph.mktemp("") 326 if err != nil { 327 return nil, err 328 } 329 return ioutil.TempFile(tmp, "") 330 } 331 332 func bufferToFile(f *os.File, src io.Reader) (int64, digest.Digest, error) { 333 var ( 334 h = sha256.New() 335 w = gzip.NewWriter(io.MultiWriter(f, h)) 336 ) 337 _, err := io.Copy(w, src) 338 w.Close() 339 if err != nil { 340 return 0, "", err 341 } 342 n, err := f.Seek(0, os.SEEK_CUR) 343 if err != nil { 344 return 0, "", err 345 } 346 if _, err := f.Seek(0, 0); err != nil { 347 return 0, "", err 348 } 349 return n, digest.NewDigest("sha256", h), nil 350 } 351 352 // Delete atomically removes an image from the graph. 353 func (graph *Graph) Delete(name string) error { 354 id, err := graph.idIndex.Get(name) 355 if err != nil { 356 return err 357 } 358 tmp, err := graph.mktemp("") 359 graph.idIndex.Delete(id) 360 if err == nil { 361 if err := os.Rename(graph.imageRoot(id), tmp); err != nil { 362 // On err make tmp point to old dir and cleanup unused tmp dir 363 os.RemoveAll(tmp) 364 tmp = graph.imageRoot(id) 365 } 366 } else { 367 // On err make tmp point to old dir for cleanup 368 tmp = graph.imageRoot(id) 369 } 370 // Remove rootfs data from the driver 371 graph.driver.Remove(id) 372 // Remove the trashed image directory 373 return os.RemoveAll(tmp) 374 } 375 376 // Map returns a list of all images in the graph, addressable by ID. 377 func (graph *Graph) Map() map[string]*image.Image { 378 images := make(map[string]*image.Image) 379 graph.walkAll(func(image *image.Image) { 380 images[image.ID] = image 381 }) 382 return images 383 } 384 385 // walkAll iterates over each image in the graph, and passes it to a handler. 386 // The walking order is undetermined. 387 func (graph *Graph) walkAll(handler func(*image.Image)) { 388 graph.idIndex.Iterate(func(id string) { 389 if img, err := graph.Get(id); err != nil { 390 return 391 } else if handler != nil { 392 handler(img) 393 } 394 }) 395 } 396 397 // ByParent returns a lookup table of images by their parent. 398 // If an image of id ID has 3 children images, then the value for key ID 399 // will be a list of 3 images. 400 // If an image has no children, it will not have an entry in the table. 401 func (graph *Graph) ByParent() map[string][]*image.Image { 402 byParent := make(map[string][]*image.Image) 403 graph.walkAll(func(img *image.Image) { 404 parent, err := graph.Get(img.Parent) 405 if err != nil { 406 return 407 } 408 if children, exists := byParent[parent.ID]; exists { 409 byParent[parent.ID] = append(children, img) 410 } else { 411 byParent[parent.ID] = []*image.Image{img} 412 } 413 }) 414 return byParent 415 } 416 417 // If the images and layers are in pulling chain, retain them. 418 // If not, they may be deleted by rmi with dangling condition. 419 func (graph *Graph) Retain(sessionID string, layerIDs ...string) { 420 graph.retained.Add(sessionID, layerIDs) 421 } 422 423 // Release removes the referenced image id from the provided set of layers. 424 func (graph *Graph) Release(sessionID string, layerIDs ...string) { 425 graph.retained.Delete(sessionID, layerIDs) 426 } 427 428 // Heads returns all heads in the graph, keyed by id. 429 // A head is an image which is not the parent of another image in the graph. 430 func (graph *Graph) Heads() map[string]*image.Image { 431 heads := make(map[string]*image.Image) 432 byParent := graph.ByParent() 433 graph.walkAll(func(image *image.Image) { 434 // If it's not in the byParent lookup table, then 435 // it's not a parent -> so it's a head! 436 if _, exists := byParent[image.ID]; !exists { 437 heads[image.ID] = image 438 } 439 }) 440 return heads 441 } 442 443 func (graph *Graph) imageRoot(id string) string { 444 return filepath.Join(graph.root, id) 445 } 446 447 // loadImage fetches the image with the given id from the graph. 448 func (graph *Graph) loadImage(id string) (*image.Image, error) { 449 root := graph.imageRoot(id) 450 451 // Open the JSON file to decode by streaming 452 jsonSource, err := os.Open(jsonPath(root)) 453 if err != nil { 454 return nil, err 455 } 456 defer jsonSource.Close() 457 458 img := &image.Image{} 459 dec := json.NewDecoder(jsonSource) 460 461 // Decode the JSON data 462 if err := dec.Decode(img); err != nil { 463 return nil, err 464 } 465 if err := image.ValidateID(img.ID); err != nil { 466 return nil, err 467 } 468 469 if buf, err := ioutil.ReadFile(filepath.Join(root, layersizeFileName)); err != nil { 470 if !os.IsNotExist(err) { 471 return nil, err 472 } 473 // If the layersize file does not exist then set the size to a negative number 474 // because a layer size of 0 (zero) is valid 475 img.Size = -1 476 } else { 477 // Using Atoi here instead would temporarily convert the size to a machine 478 // dependent integer type, which causes images larger than 2^31 bytes to 479 // display negative sizes on 32-bit machines: 480 size, err := strconv.ParseInt(string(buf), 10, 64) 481 if err != nil { 482 return nil, err 483 } 484 img.Size = int64(size) 485 } 486 487 return img, nil 488 } 489 490 // saveSize stores the `size` in the provided graph `img` directory `root`. 491 func (graph *Graph) saveSize(root string, size int) error { 492 if err := ioutil.WriteFile(filepath.Join(root, layersizeFileName), []byte(strconv.Itoa(size)), 0600); err != nil { 493 return fmt.Errorf("Error storing image size in %s/%s: %s", root, layersizeFileName, err) 494 } 495 return nil 496 } 497 498 // SetDigest sets the digest for the image layer to the provided value. 499 func (graph *Graph) SetDigest(id string, dgst digest.Digest) error { 500 root := graph.imageRoot(id) 501 if err := ioutil.WriteFile(filepath.Join(root, digestFileName), []byte(dgst.String()), 0600); err != nil { 502 return fmt.Errorf("Error storing digest in %s/%s: %s", root, digestFileName, err) 503 } 504 return nil 505 } 506 507 // GetDigest gets the digest for the provide image layer id. 508 func (graph *Graph) GetDigest(id string) (digest.Digest, error) { 509 root := graph.imageRoot(id) 510 cs, err := ioutil.ReadFile(filepath.Join(root, digestFileName)) 511 if err != nil { 512 if os.IsNotExist(err) { 513 return "", ErrDigestNotSet 514 } 515 return "", err 516 } 517 return digest.ParseDigest(string(cs)) 518 } 519 520 // RawJSON returns the JSON representation for an image as a byte array. 521 func (graph *Graph) RawJSON(id string) ([]byte, error) { 522 root := graph.imageRoot(id) 523 524 buf, err := ioutil.ReadFile(jsonPath(root)) 525 if err != nil { 526 return nil, fmt.Errorf("Failed to read json for image %s: %s", id, err) 527 } 528 529 return buf, nil 530 } 531 532 func jsonPath(root string) string { 533 return filepath.Join(root, jsonFileName) 534 } 535 536 func (graph *Graph) disassembleAndApplyTarLayer(img *image.Image, layerData archive.ArchiveReader, root string) error { 537 // this is saving the tar-split metadata 538 mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) 539 if err != nil { 540 return err 541 } 542 mfz := gzip.NewWriter(mf) 543 metaPacker := storage.NewJSONPacker(mfz) 544 defer mf.Close() 545 defer mfz.Close() 546 547 inflatedLayerData, err := archive.DecompressStream(layerData) 548 if err != nil { 549 return err 550 } 551 552 // we're passing nil here for the file putter, because the ApplyDiff will 553 // handle the extraction of the archive 554 rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil) 555 if err != nil { 556 return err 557 } 558 559 if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, archive.ArchiveReader(rdr)); err != nil { 560 return err 561 } 562 563 return nil 564 } 565 566 func (graph *Graph) assembleTarLayer(img *image.Image) (archive.Archive, error) { 567 root := graph.imageRoot(img.ID) 568 mFileName := filepath.Join(root, tarDataFileName) 569 mf, err := os.Open(mFileName) 570 if err != nil { 571 if !os.IsNotExist(err) { 572 logrus.Errorf("failed to open %q: %s", mFileName, err) 573 } 574 return nil, err 575 } 576 pR, pW := io.Pipe() 577 // this will need to be in a goroutine, as we are returning the stream of a 578 // tar archive, but can not close the metadata reader early (when this 579 // function returns)... 580 go func() { 581 defer mf.Close() 582 // let's reassemble! 583 logrus.Debugf("[graph] TarLayer with reassembly: %s", img.ID) 584 mfz, err := gzip.NewReader(mf) 585 if err != nil { 586 pW.CloseWithError(fmt.Errorf("[graph] error with %s: %s", mFileName, err)) 587 return 588 } 589 defer mfz.Close() 590 591 // get our relative path to the container 592 fsLayer, err := graph.driver.Get(img.ID, "") 593 if err != nil { 594 pW.CloseWithError(err) 595 return 596 } 597 defer graph.driver.Put(img.ID) 598 599 metaUnpacker := storage.NewJSONUnpacker(mfz) 600 fileGetter := storage.NewPathFileGetter(fsLayer) 601 logrus.Debugf("[graph] %s is at %q", img.ID, fsLayer) 602 ots := asm.NewOutputTarStream(fileGetter, metaUnpacker) 603 defer ots.Close() 604 if _, err := io.Copy(pW, ots); err != nil { 605 pW.CloseWithError(err) 606 return 607 } 608 pW.Close() 609 }() 610 return pR, nil 611 }