github.com/daaku/docker@v1.5.0/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, 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  func (graph *Graph) newTempFile() (*os.File, error) {
   227  	tmp, err := graph.Mktemp("")
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  	return ioutil.TempFile(tmp, "")
   232  }
   233  
   234  func bufferToFile(f *os.File, src io.Reader) (int64, error) {
   235  	n, err := io.Copy(f, src)
   236  	if err != nil {
   237  		return n, err
   238  	}
   239  	if err = f.Sync(); err != nil {
   240  		return n, err
   241  	}
   242  	if _, err := f.Seek(0, 0); err != nil {
   243  		return n, err
   244  	}
   245  	return n, nil
   246  }
   247  
   248  // setupInitLayer populates a directory with mountpoints suitable
   249  // for bind-mounting dockerinit into the container. The mountpoint is simply an
   250  // empty file at /.dockerinit
   251  //
   252  // This extra layer is used by all containers as the top-most ro layer. It protects
   253  // the container from unwanted side-effects on the rw layer.
   254  func SetupInitLayer(initLayer string) error {
   255  	for pth, typ := range map[string]string{
   256  		"/dev/pts":         "dir",
   257  		"/dev/shm":         "dir",
   258  		"/proc":            "dir",
   259  		"/sys":             "dir",
   260  		"/.dockerinit":     "file",
   261  		"/.dockerenv":      "file",
   262  		"/etc/resolv.conf": "file",
   263  		"/etc/hosts":       "file",
   264  		"/etc/hostname":    "file",
   265  		"/dev/console":     "file",
   266  		"/etc/mtab":        "/proc/mounts",
   267  	} {
   268  		parts := strings.Split(pth, "/")
   269  		prev := "/"
   270  		for _, p := range parts[1:] {
   271  			prev = path.Join(prev, p)
   272  			syscall.Unlink(path.Join(initLayer, prev))
   273  		}
   274  
   275  		if _, err := os.Stat(path.Join(initLayer, pth)); err != nil {
   276  			if os.IsNotExist(err) {
   277  				if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
   278  					return err
   279  				}
   280  				switch typ {
   281  				case "dir":
   282  					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
   283  						return err
   284  					}
   285  				case "file":
   286  					f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755)
   287  					if err != nil {
   288  						return err
   289  					}
   290  					f.Close()
   291  				default:
   292  					if err := os.Symlink(typ, path.Join(initLayer, pth)); err != nil {
   293  						return err
   294  					}
   295  				}
   296  			} else {
   297  				return err
   298  			}
   299  		}
   300  	}
   301  
   302  	// Layer is ready to use, if it wasn't before.
   303  	return nil
   304  }
   305  
   306  // Check if given error is "not empty".
   307  // Note: this is the way golang does it internally with os.IsNotExists.
   308  func isNotEmpty(err error) bool {
   309  	switch pe := err.(type) {
   310  	case nil:
   311  		return false
   312  	case *os.PathError:
   313  		err = pe.Err
   314  	case *os.LinkError:
   315  		err = pe.Err
   316  	}
   317  	return strings.Contains(err.Error(), " not empty")
   318  }
   319  
   320  // Delete atomically removes an image from the graph.
   321  func (graph *Graph) Delete(name string) error {
   322  	id, err := graph.idIndex.Get(name)
   323  	if err != nil {
   324  		return err
   325  	}
   326  	tmp, err := graph.Mktemp("")
   327  	graph.idIndex.Delete(id)
   328  	if err == nil {
   329  		err = os.Rename(graph.ImageRoot(id), tmp)
   330  		// On err make tmp point to old dir and cleanup unused tmp dir
   331  		if err != nil {
   332  			os.RemoveAll(tmp)
   333  			tmp = graph.ImageRoot(id)
   334  		}
   335  	} else {
   336  		// On err make tmp point to old dir for cleanup
   337  		tmp = graph.ImageRoot(id)
   338  	}
   339  	// Remove rootfs data from the driver
   340  	graph.driver.Remove(id)
   341  	// Remove the trashed image directory
   342  	return os.RemoveAll(tmp)
   343  }
   344  
   345  // Map returns a list of all images in the graph, addressable by ID.
   346  func (graph *Graph) Map() (map[string]*image.Image, error) {
   347  	images := make(map[string]*image.Image)
   348  	err := graph.walkAll(func(image *image.Image) {
   349  		images[image.ID] = image
   350  	})
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  	return images, nil
   355  }
   356  
   357  // walkAll iterates over each image in the graph, and passes it to a handler.
   358  // The walking order is undetermined.
   359  func (graph *Graph) walkAll(handler func(*image.Image)) error {
   360  	files, err := ioutil.ReadDir(graph.Root)
   361  	if err != nil {
   362  		return err
   363  	}
   364  	for _, st := range files {
   365  		if img, err := graph.Get(st.Name()); err != nil {
   366  			// Skip image
   367  			continue
   368  		} else if handler != nil {
   369  			handler(img)
   370  		}
   371  	}
   372  	return nil
   373  }
   374  
   375  // ByParent returns a lookup table of images by their parent.
   376  // If an image of id ID has 3 children images, then the value for key ID
   377  // will be a list of 3 images.
   378  // If an image has no children, it will not have an entry in the table.
   379  func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
   380  	byParent := make(map[string][]*image.Image)
   381  	err := graph.walkAll(func(img *image.Image) {
   382  		parent, err := graph.Get(img.Parent)
   383  		if err != nil {
   384  			return
   385  		}
   386  		if children, exists := byParent[parent.ID]; exists {
   387  			byParent[parent.ID] = append(children, img)
   388  		} else {
   389  			byParent[parent.ID] = []*image.Image{img}
   390  		}
   391  	})
   392  	return byParent, err
   393  }
   394  
   395  // Heads returns all heads in the graph, keyed by id.
   396  // A head is an image which is not the parent of another image in the graph.
   397  func (graph *Graph) Heads() (map[string]*image.Image, error) {
   398  	heads := make(map[string]*image.Image)
   399  	byParent, err := graph.ByParent()
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	err = graph.walkAll(func(image *image.Image) {
   404  		// If it's not in the byParent lookup table, then
   405  		// it's not a parent -> so it's a head!
   406  		if _, exists := byParent[image.ID]; !exists {
   407  			heads[image.ID] = image
   408  		}
   409  	})
   410  	return heads, err
   411  }
   412  
   413  func (graph *Graph) ImageRoot(id string) string {
   414  	return path.Join(graph.Root, id)
   415  }
   416  
   417  func (graph *Graph) Driver() graphdriver.Driver {
   418  	return graph.driver
   419  }