github.com/jogo/docker@v1.7.0-rc1/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/filepath"
    11  	"runtime"
    12  	"strings"
    13  	"syscall"
    14  	"time"
    15  
    16  	"github.com/Sirupsen/logrus"
    17  	"github.com/docker/distribution/digest"
    18  	"github.com/docker/docker/autogen/dockerversion"
    19  	"github.com/docker/docker/daemon/graphdriver"
    20  	"github.com/docker/docker/image"
    21  	"github.com/docker/docker/pkg/archive"
    22  	"github.com/docker/docker/pkg/progressreader"
    23  	"github.com/docker/docker/pkg/streamformatter"
    24  	"github.com/docker/docker/pkg/stringid"
    25  	"github.com/docker/docker/pkg/system"
    26  	"github.com/docker/docker/pkg/truncindex"
    27  	"github.com/docker/docker/runconfig"
    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 := system.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  	logrus.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, id string) bool {
    80  	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)
    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:            stringid.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 := image.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 *streamformatter.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:        stringid.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 := filepath.Join(graph.Root, "_tmp", stringid.GenerateRandomID())
   233  	if err := system.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  	n, err := f.Seek(0, os.SEEK_CUR)
   258  	if err != nil {
   259  		return 0, "", err
   260  	}
   261  	if _, err := f.Seek(0, 0); err != nil {
   262  		return 0, "", err
   263  	}
   264  	return n, digest.NewDigest("sha256", h), nil
   265  }
   266  
   267  // setupInitLayer populates a directory with mountpoints suitable
   268  // for bind-mounting dockerinit into the container. The mountpoint is simply an
   269  // empty file at /.dockerinit
   270  //
   271  // This extra layer is used by all containers as the top-most ro layer. It protects
   272  // the container from unwanted side-effects on the rw layer.
   273  func SetupInitLayer(initLayer string) error {
   274  	for pth, typ := range map[string]string{
   275  		"/dev/pts":         "dir",
   276  		"/dev/shm":         "dir",
   277  		"/proc":            "dir",
   278  		"/sys":             "dir",
   279  		"/.dockerinit":     "file",
   280  		"/.dockerenv":      "file",
   281  		"/etc/resolv.conf": "file",
   282  		"/etc/hosts":       "file",
   283  		"/etc/hostname":    "file",
   284  		"/dev/console":     "file",
   285  		"/etc/mtab":        "/proc/mounts",
   286  	} {
   287  		parts := strings.Split(pth, "/")
   288  		prev := "/"
   289  		for _, p := range parts[1:] {
   290  			prev = filepath.Join(prev, p)
   291  			syscall.Unlink(filepath.Join(initLayer, prev))
   292  		}
   293  
   294  		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
   295  			if os.IsNotExist(err) {
   296  				if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil {
   297  					return err
   298  				}
   299  				switch typ {
   300  				case "dir":
   301  					if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil {
   302  						return err
   303  					}
   304  				case "file":
   305  					f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755)
   306  					if err != nil {
   307  						return err
   308  					}
   309  					f.Close()
   310  				default:
   311  					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
   312  						return err
   313  					}
   314  				}
   315  			} else {
   316  				return err
   317  			}
   318  		}
   319  	}
   320  
   321  	// Layer is ready to use, if it wasn't before.
   322  	return nil
   323  }
   324  
   325  // Check if given error is "not empty".
   326  // Note: this is the way golang does it internally with os.IsNotExists.
   327  func isNotEmpty(err error) bool {
   328  	switch pe := err.(type) {
   329  	case nil:
   330  		return false
   331  	case *os.PathError:
   332  		err = pe.Err
   333  	case *os.LinkError:
   334  		err = pe.Err
   335  	}
   336  	return strings.Contains(err.Error(), " not empty")
   337  }
   338  
   339  // Delete atomically removes an image from the graph.
   340  func (graph *Graph) Delete(name string) error {
   341  	id, err := graph.idIndex.Get(name)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	tmp, err := graph.Mktemp("")
   346  	graph.idIndex.Delete(id)
   347  	if err == nil {
   348  		if err := os.Rename(graph.ImageRoot(id), tmp); err != nil {
   349  			// On err make tmp point to old dir and cleanup unused tmp dir
   350  			os.RemoveAll(tmp)
   351  			tmp = graph.ImageRoot(id)
   352  		}
   353  	} else {
   354  		// On err make tmp point to old dir for cleanup
   355  		tmp = graph.ImageRoot(id)
   356  	}
   357  	// Remove rootfs data from the driver
   358  	graph.driver.Remove(id)
   359  	// Remove the trashed image directory
   360  	return os.RemoveAll(tmp)
   361  }
   362  
   363  // Map returns a list of all images in the graph, addressable by ID.
   364  func (graph *Graph) Map() (map[string]*image.Image, error) {
   365  	images := make(map[string]*image.Image)
   366  	err := graph.walkAll(func(image *image.Image) {
   367  		images[image.ID] = image
   368  	})
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	return images, nil
   373  }
   374  
   375  // walkAll iterates over each image in the graph, and passes it to a handler.
   376  // The walking order is undetermined.
   377  func (graph *Graph) walkAll(handler func(*image.Image)) error {
   378  	files, err := ioutil.ReadDir(graph.Root)
   379  	if err != nil {
   380  		return err
   381  	}
   382  	for _, st := range files {
   383  		if img, err := graph.Get(st.Name()); err != nil {
   384  			// Skip image
   385  			continue
   386  		} else if handler != nil {
   387  			handler(img)
   388  		}
   389  	}
   390  	return nil
   391  }
   392  
   393  // ByParent returns a lookup table of images by their parent.
   394  // If an image of id ID has 3 children images, then the value for key ID
   395  // will be a list of 3 images.
   396  // If an image has no children, it will not have an entry in the table.
   397  func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
   398  	byParent := make(map[string][]*image.Image)
   399  	err := graph.walkAll(func(img *image.Image) {
   400  		parent, err := graph.Get(img.Parent)
   401  		if err != nil {
   402  			return
   403  		}
   404  		if children, exists := byParent[parent.ID]; exists {
   405  			byParent[parent.ID] = append(children, img)
   406  		} else {
   407  			byParent[parent.ID] = []*image.Image{img}
   408  		}
   409  	})
   410  	return byParent, err
   411  }
   412  
   413  // Heads returns all heads in the graph, keyed by id.
   414  // A head is an image which is not the parent of another image in the graph.
   415  func (graph *Graph) Heads() (map[string]*image.Image, error) {
   416  	heads := make(map[string]*image.Image)
   417  	byParent, err := graph.ByParent()
   418  	if err != nil {
   419  		return nil, err
   420  	}
   421  	err = graph.walkAll(func(image *image.Image) {
   422  		// If it's not in the byParent lookup table, then
   423  		// it's not a parent -> so it's a head!
   424  		if _, exists := byParent[image.ID]; !exists {
   425  			heads[image.ID] = image
   426  		}
   427  	})
   428  	return heads, err
   429  }
   430  
   431  func (graph *Graph) ImageRoot(id string) string {
   432  	return filepath.Join(graph.Root, id)
   433  }
   434  
   435  func (graph *Graph) Driver() graphdriver.Driver {
   436  	return graph.driver
   437  }