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