github.com/xuyutom/docker@v1.6.0/graph/graph.go (about)

     1  package graph
     2  
     3  import (
     4  	"compress/gzip"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"syscall"
    15  	"time"
    16  
    17  	log "github.com/Sirupsen/logrus"
    18  	"github.com/docker/distribution/digest"
    19  	"github.com/docker/docker/autogen/dockerversion"
    20  	"github.com/docker/docker/daemon/graphdriver"
    21  	"github.com/docker/docker/image"
    22  	"github.com/docker/docker/pkg/archive"
    23  	"github.com/docker/docker/pkg/common"
    24  	"github.com/docker/docker/pkg/progressreader"
    25  	"github.com/docker/docker/pkg/truncindex"
    26  	"github.com/docker/docker/runconfig"
    27  	"github.com/docker/docker/utils"
    28  )
    29  
    30  // A Graph is a store for versioned filesystem images and the relationship between them.
    31  type Graph struct {
    32  	Root    string
    33  	idIndex *truncindex.TruncIndex
    34  	driver  graphdriver.Driver
    35  }
    36  
    37  // NewGraph instantiates a new graph at the given root path in the filesystem.
    38  // `root` will be created if it doesn't exist.
    39  func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
    40  	abspath, err := filepath.Abs(root)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	// Create the root directory if it doesn't exists
    45  	if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
    46  		return nil, err
    47  	}
    48  
    49  	graph := &Graph{
    50  		Root:    abspath,
    51  		idIndex: truncindex.NewTruncIndex([]string{}),
    52  		driver:  driver,
    53  	}
    54  	if err := graph.restore(); err != nil {
    55  		return nil, err
    56  	}
    57  	return graph, nil
    58  }
    59  
    60  func (graph *Graph) restore() error {
    61  	dir, err := ioutil.ReadDir(graph.Root)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	var ids = []string{}
    66  	for _, v := range dir {
    67  		id := v.Name()
    68  		if graph.driver.Exists(id) {
    69  			ids = append(ids, id)
    70  		}
    71  	}
    72  	graph.idIndex = truncindex.NewTruncIndex(ids)
    73  	log.Debugf("Restored %d elements", len(dir))
    74  	return nil
    75  }
    76  
    77  // FIXME: Implement error subclass instead of looking at the error text
    78  // Note: This is the way golang implements os.IsNotExists on Plan9
    79  func (graph *Graph) IsNotExist(err error) bool {
    80  	return err != nil && (strings.Contains(strings.ToLower(err.Error()), "does not exist") || strings.Contains(strings.ToLower(err.Error()), "no such"))
    81  }
    82  
    83  // Exists returns true if an image is registered at the given id.
    84  // If the image doesn't exist or if an error is encountered, false is returned.
    85  func (graph *Graph) Exists(id string) bool {
    86  	if _, err := graph.Get(id); err != nil {
    87  		return false
    88  	}
    89  	return true
    90  }
    91  
    92  // Get returns the image with the given id, or an error if the image doesn't exist.
    93  func (graph *Graph) Get(name string) (*image.Image, error) {
    94  	id, err := graph.idIndex.Get(name)
    95  	if err != nil {
    96  		return nil, fmt.Errorf("could not find image: %v", err)
    97  	}
    98  	img, err := image.LoadImage(graph.ImageRoot(id))
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	if img.ID != id {
   103  		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
   104  	}
   105  	img.SetGraph(graph)
   106  
   107  	if img.Size < 0 {
   108  		size, err := graph.driver.DiffSize(img.ID, img.Parent)
   109  		if err != nil {
   110  			return nil, fmt.Errorf("unable to calculate size of image id %q: %s", img.ID, err)
   111  		}
   112  
   113  		img.Size = size
   114  		if err := img.SaveSize(graph.ImageRoot(id)); err != nil {
   115  			return nil, err
   116  		}
   117  	}
   118  	return img, nil
   119  }
   120  
   121  // Create creates a new image and registers it in the graph.
   122  func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
   123  	img := &image.Image{
   124  		ID:            common.GenerateRandomID(),
   125  		Comment:       comment,
   126  		Created:       time.Now().UTC(),
   127  		DockerVersion: dockerversion.VERSION,
   128  		Author:        author,
   129  		Config:        config,
   130  		Architecture:  runtime.GOARCH,
   131  		OS:            runtime.GOOS,
   132  	}
   133  
   134  	if containerID != "" {
   135  		img.Parent = containerImage
   136  		img.Container = containerID
   137  		img.ContainerConfig = *containerConfig
   138  	}
   139  
   140  	if err := graph.Register(img, layerData); err != nil {
   141  		return nil, err
   142  	}
   143  	return img, nil
   144  }
   145  
   146  // Register imports a pre-existing image into the graph.
   147  func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
   148  	defer func() {
   149  		// If any error occurs, remove the new dir from the driver.
   150  		// Don't check for errors since the dir might not have been created.
   151  		// FIXME: this leaves a possible race condition.
   152  		if err != nil {
   153  			graph.driver.Remove(img.ID)
   154  		}
   155  	}()
   156  	if err := utils.ValidateID(img.ID); err != nil {
   157  		return err
   158  	}
   159  	// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
   160  	if graph.Exists(img.ID) {
   161  		return fmt.Errorf("Image %s already exists", img.ID)
   162  	}
   163  
   164  	// Ensure that the image root does not exist on the filesystem
   165  	// when it is not registered in the graph.
   166  	// This is common when you switch from one graph driver to another
   167  	if err := os.RemoveAll(graph.ImageRoot(img.ID)); err != nil && !os.IsNotExist(err) {
   168  		return err
   169  	}
   170  
   171  	// If the driver has this ID but the graph doesn't, remove it from the driver to start fresh.
   172  	// (the graph is the source of truth).
   173  	// Ignore errors, since we don't know if the driver correctly returns ErrNotExist.
   174  	// (FIXME: make that mandatory for drivers).
   175  	graph.driver.Remove(img.ID)
   176  
   177  	tmp, err := graph.Mktemp("")
   178  	defer os.RemoveAll(tmp)
   179  	if err != nil {
   180  		return fmt.Errorf("Mktemp failed: %s", err)
   181  	}
   182  
   183  	// Create root filesystem in the driver
   184  	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
   185  		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
   186  	}
   187  	// Apply the diff/layer
   188  	img.SetGraph(graph)
   189  	if err := image.StoreImage(img, layerData, tmp); err != nil {
   190  		return err
   191  	}
   192  	// Commit
   193  	if err := os.Rename(tmp, graph.ImageRoot(img.ID)); err != nil {
   194  		return err
   195  	}
   196  	graph.idIndex.Add(img.ID)
   197  	return nil
   198  }
   199  
   200  // TempLayerArchive creates a temporary archive of the given image's filesystem layer.
   201  //   The archive is stored on disk and will be automatically deleted as soon as has been read.
   202  //   If output is not nil, a human-readable progress bar will be written to it.
   203  //   FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
   204  func (graph *Graph) TempLayerArchive(id string, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
   205  	image, err := graph.Get(id)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	tmp, err := graph.Mktemp("")
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	a, err := image.TarLayer()
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	progressReader := progressreader.New(progressreader.Config{
   218  		In:        a,
   219  		Out:       output,
   220  		Formatter: sf,
   221  		Size:      0,
   222  		NewLines:  false,
   223  		ID:        common.TruncateID(id),
   224  		Action:    "Buffering to disk",
   225  	})
   226  	defer progressReader.Close()
   227  	return archive.NewTempArchive(progressReader, tmp)
   228  }
   229  
   230  // Mktemp creates a temporary sub-directory inside the graph's filesystem.
   231  func (graph *Graph) Mktemp(id string) (string, error) {
   232  	dir := path.Join(graph.Root, "_tmp", common.GenerateRandomID())
   233  	if err := os.MkdirAll(dir, 0700); err != nil {
   234  		return "", err
   235  	}
   236  	return dir, nil
   237  }
   238  
   239  func (graph *Graph) newTempFile() (*os.File, error) {
   240  	tmp, err := graph.Mktemp("")
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	return ioutil.TempFile(tmp, "")
   245  }
   246  
   247  func bufferToFile(f *os.File, src io.Reader) (int64, digest.Digest, error) {
   248  	var (
   249  		h = sha256.New()
   250  		w = gzip.NewWriter(io.MultiWriter(f, h))
   251  	)
   252  	_, err := io.Copy(w, src)
   253  	w.Close()
   254  	if err != nil {
   255  		return 0, "", err
   256  	}
   257  	if err = f.Sync(); err != nil {
   258  		return 0, "", err
   259  	}
   260  	n, err := f.Seek(0, os.SEEK_CUR)
   261  	if err != nil {
   262  		return 0, "", err
   263  	}
   264  	if _, err := f.Seek(0, 0); err != nil {
   265  		return 0, "", err
   266  	}
   267  	return n, digest.NewDigest("sha256", h), nil
   268  }
   269  
   270  // setupInitLayer populates a directory with mountpoints suitable
   271  // for bind-mounting dockerinit into the container. The mountpoint is simply an
   272  // empty file at /.dockerinit
   273  //
   274  // This extra layer is used by all containers as the top-most ro layer. It protects
   275  // the container from unwanted side-effects on the rw layer.
   276  func SetupInitLayer(initLayer string) error {
   277  	for pth, typ := range map[string]string{
   278  		"/dev/pts":         "dir",
   279  		"/dev/shm":         "dir",
   280  		"/proc":            "dir",
   281  		"/sys":             "dir",
   282  		"/.dockerinit":     "file",
   283  		"/.dockerenv":      "file",
   284  		"/etc/resolv.conf": "file",
   285  		"/etc/hosts":       "file",
   286  		"/etc/hostname":    "file",
   287  		"/dev/console":     "file",
   288  		"/etc/mtab":        "/proc/mounts",
   289  	} {
   290  		parts := strings.Split(pth, "/")
   291  		prev := "/"
   292  		for _, p := range parts[1:] {
   293  			prev = path.Join(prev, p)
   294  			syscall.Unlink(path.Join(initLayer, prev))
   295  		}
   296  
   297  		if _, err := os.Stat(path.Join(initLayer, pth)); err != nil {
   298  			if os.IsNotExist(err) {
   299  				if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
   300  					return err
   301  				}
   302  				switch typ {
   303  				case "dir":
   304  					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
   305  						return err
   306  					}
   307  				case "file":
   308  					f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755)
   309  					if err != nil {
   310  						return err
   311  					}
   312  					f.Close()
   313  				default:
   314  					if err := os.Symlink(typ, path.Join(initLayer, pth)); err != nil {
   315  						return err
   316  					}
   317  				}
   318  			} else {
   319  				return err
   320  			}
   321  		}
   322  	}
   323  
   324  	// Layer is ready to use, if it wasn't before.
   325  	return nil
   326  }
   327  
   328  // Check if given error is "not empty".
   329  // Note: this is the way golang does it internally with os.IsNotExists.
   330  func isNotEmpty(err error) bool {
   331  	switch pe := err.(type) {
   332  	case nil:
   333  		return false
   334  	case *os.PathError:
   335  		err = pe.Err
   336  	case *os.LinkError:
   337  		err = pe.Err
   338  	}
   339  	return strings.Contains(err.Error(), " not empty")
   340  }
   341  
   342  // Delete atomically removes an image from the graph.
   343  func (graph *Graph) Delete(name string) error {
   344  	id, err := graph.idIndex.Get(name)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	tmp, err := graph.Mktemp("")
   349  	graph.idIndex.Delete(id)
   350  	if err == nil {
   351  		err = os.Rename(graph.ImageRoot(id), tmp)
   352  		// On err make tmp point to old dir and cleanup unused tmp dir
   353  		if err != nil {
   354  			os.RemoveAll(tmp)
   355  			tmp = graph.ImageRoot(id)
   356  		}
   357  	} else {
   358  		// On err make tmp point to old dir for cleanup
   359  		tmp = graph.ImageRoot(id)
   360  	}
   361  	// Remove rootfs data from the driver
   362  	graph.driver.Remove(id)
   363  	// Remove the trashed image directory
   364  	return os.RemoveAll(tmp)
   365  }
   366  
   367  // Map returns a list of all images in the graph, addressable by ID.
   368  func (graph *Graph) Map() (map[string]*image.Image, error) {
   369  	images := make(map[string]*image.Image)
   370  	err := graph.walkAll(func(image *image.Image) {
   371  		images[image.ID] = image
   372  	})
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  	return images, nil
   377  }
   378  
   379  // walkAll iterates over each image in the graph, and passes it to a handler.
   380  // The walking order is undetermined.
   381  func (graph *Graph) walkAll(handler func(*image.Image)) error {
   382  	files, err := ioutil.ReadDir(graph.Root)
   383  	if err != nil {
   384  		return err
   385  	}
   386  	for _, st := range files {
   387  		if img, err := graph.Get(st.Name()); err != nil {
   388  			// Skip image
   389  			continue
   390  		} else if handler != nil {
   391  			handler(img)
   392  		}
   393  	}
   394  	return nil
   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, error) {
   402  	byParent := make(map[string][]*image.Image)
   403  	err := 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, err
   415  }
   416  
   417  // Heads returns all heads in the graph, keyed by id.
   418  // A head is an image which is not the parent of another image in the graph.
   419  func (graph *Graph) Heads() (map[string]*image.Image, error) {
   420  	heads := make(map[string]*image.Image)
   421  	byParent, err := graph.ByParent()
   422  	if err != nil {
   423  		return nil, err
   424  	}
   425  	err = graph.walkAll(func(image *image.Image) {
   426  		// If it's not in the byParent lookup table, then
   427  		// it's not a parent -> so it's a head!
   428  		if _, exists := byParent[image.ID]; !exists {
   429  			heads[image.ID] = image
   430  		}
   431  	})
   432  	return heads, err
   433  }
   434  
   435  func (graph *Graph) ImageRoot(id string) string {
   436  	return path.Join(graph.Root, id)
   437  }
   438  
   439  func (graph *Graph) Driver() graphdriver.Driver {
   440  	return graph.driver
   441  }