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  }