github.com/sirupsen/docker@v0.10.1-0.20150325003727-22dba32b4dab/graph/graph.go (about)

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