2013-03-21 20:47:23 -04:00
package docker
2013-03-18 03:15:35 -04:00
import (
"fmt"
2013-10-31 19:57:45 -04:00
"github.com/dotcloud/docker/archive"
2013-05-14 18:37:35 -04:00
"github.com/dotcloud/docker/utils"
2013-05-06 14:06:44 -04:00
"io"
2013-03-18 03:15:35 -04:00
"io/ioutil"
"os"
"path"
"path/filepath"
2013-03-26 08:28:17 -04:00
"strings"
2013-03-18 03:15:35 -04:00
"time"
)
2013-03-30 03:22:24 -04:00
// A Graph is a store for versioned filesystem images and the relationship between them.
2013-03-18 03:15:35 -04:00
type Graph struct {
2013-07-17 15:13:22 -04:00
Root string
idIndex * utils . TruncIndex
2013-03-18 03:15:35 -04:00
}
2013-03-30 03:22:24 -04:00
// NewGraph instantiates a new graph at the given root path in the filesystem.
2013-03-30 00:13:59 -04:00
// `root` will be created if it doesn't exist.
2013-03-21 20:47:23 -04:00
func NewGraph ( root string ) ( * Graph , error ) {
2013-03-18 03:15:35 -04:00
abspath , err := filepath . Abs ( root )
if err != nil {
return nil , err
}
// Create the root directory if it doesn't exists
2013-05-08 19:22:12 -04:00
if err := os . MkdirAll ( root , 0700 ) ; err != nil && ! os . IsExist ( err ) {
2013-03-18 03:15:35 -04:00
return nil , err
}
2013-04-01 01:11:55 -04:00
graph := & Graph {
2013-07-17 15:13:22 -04:00
Root : abspath ,
idIndex : utils . NewTruncIndex ( ) ,
2013-04-01 01:11:55 -04:00
}
if err := graph . restore ( ) ; err != nil {
return nil , err
}
return graph , nil
}
func ( graph * Graph ) restore ( ) error {
dir , err := ioutil . ReadDir ( graph . Root )
if err != nil {
return err
}
for _ , v := range dir {
id := v . Name ( )
graph . idIndex . Add ( id )
}
return nil
2013-03-18 03:15:35 -04:00
}
2013-03-26 08:28:17 -04:00
// FIXME: Implement error subclass instead of looking at the error text
// Note: This is the way golang implements os.IsNotExists on Plan9
func ( graph * Graph ) IsNotExist ( err error ) bool {
2013-05-09 12:57:47 -04:00
return err != nil && ( strings . Contains ( err . Error ( ) , "does not exist" ) || strings . Contains ( err . Error ( ) , "No such" ) )
2013-03-26 08:28:17 -04:00
}
2013-03-30 00:13:59 -04:00
// Exists returns true if an image is registered at the given id.
// If the image doesn't exist or if an error is encountered, false is returned.
2013-03-18 03:15:35 -04:00
func ( graph * Graph ) Exists ( id string ) bool {
if _ , err := graph . Get ( id ) ; err != nil {
return false
}
return true
}
2013-03-30 00:13:59 -04:00
// Get returns the image with the given id, or an error if the image doesn't exist.
2013-04-01 01:11:55 -04:00
func ( graph * Graph ) Get ( name string ) ( * Image , error ) {
id , err := graph . idIndex . Get ( name )
if err != nil {
return nil , err
}
2013-03-21 22:01:55 -04:00
// FIXME: return nil when the image doesn't exist, instead of an error
2013-03-18 03:15:35 -04:00
img , err := LoadImage ( graph . imageRoot ( id ) )
if err != nil {
return nil , err
}
2013-06-04 14:00:22 -04:00
if img . ID != id {
return nil , fmt . Errorf ( "Image stored at '%s' has wrong id '%s'" , id , img . ID )
2013-03-18 03:15:35 -04:00
}
2013-05-24 09:03:09 -04:00
img . graph = graph
2013-05-13 09:10:26 -04:00
if img . Size == 0 {
root , err := img . root ( )
if err != nil {
return nil , err
}
if err := StoreSize ( img , root ) ; err != nil {
return nil , err
}
}
2013-03-18 03:15:35 -04:00
return img , nil
}
2013-03-30 00:13:59 -04:00
// Create creates a new image and registers it in the graph.
2013-10-31 19:57:45 -04:00
func ( graph * Graph ) Create ( layerData archive . Archive , container * Container , comment , author string , config * Config ) ( * Image , error ) {
2013-03-18 03:15:35 -04:00
img := & Image {
2013-06-04 14:00:22 -04:00
ID : GenerateID ( ) ,
2013-04-04 21:38:43 -04:00
Comment : comment ,
Created : time . Now ( ) ,
DockerVersion : VERSION ,
2013-04-17 22:58:17 -04:00
Author : author ,
2013-04-25 19:48:31 -04:00
Config : config ,
2013-05-24 14:42:09 -04:00
Architecture : "x86_64" ,
2013-03-18 03:15:35 -04:00
}
2013-03-22 00:13:27 -04:00
if container != nil {
img . Parent = container . Image
2013-06-04 14:00:22 -04:00
img . Container = container . ID
2013-03-23 17:48:16 -04:00
img . ContainerConfig = * container . Config
2013-03-22 00:13:27 -04:00
}
2013-07-22 18:44:55 -04:00
if err := graph . Register ( nil , layerData , img ) ; err != nil {
2013-03-18 03:15:35 -04:00
return nil , err
}
return img , nil
}
2013-03-30 00:13:59 -04:00
// Register imports a pre-existing image into the graph.
// FIXME: pass img as first argument
2013-10-31 19:57:45 -04:00
func ( graph * Graph ) Register ( jsonData [ ] byte , layerData archive . Archive , img * Image ) error {
2013-06-04 14:00:22 -04:00
if err := ValidateID ( img . ID ) ; err != nil {
2013-03-18 03:15:35 -04:00
return err
}
// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
2013-06-04 14:00:22 -04:00
if graph . Exists ( img . ID ) {
return fmt . Errorf ( "Image %s already exists" , img . ID )
2013-03-18 03:15:35 -04:00
}
2013-04-17 19:35:22 -04:00
tmp , err := graph . Mktemp ( "" )
2013-03-18 03:15:35 -04:00
defer os . RemoveAll ( tmp )
if err != nil {
return fmt . Errorf ( "Mktemp failed: %s" , err )
}
2013-07-22 18:44:55 -04:00
if err := StoreImage ( img , jsonData , layerData , tmp ) ; err != nil {
2013-03-18 03:15:35 -04:00
return err
}
// Commit
2013-06-04 14:00:22 -04:00
if err := os . Rename ( tmp , graph . imageRoot ( img . ID ) ) ; err != nil {
2013-03-18 03:15:35 -04:00
return err
}
img . graph = graph
2013-06-04 14:00:22 -04:00
graph . idIndex . Add ( img . ID )
2013-03-18 03:15:35 -04:00
return nil
}
2013-04-21 17:23:55 -04:00
// TempLayerArchive creates a temporary archive of the given image's filesystem layer.
// The archive is stored on disk and will be automatically deleted as soon as has been read.
2013-04-21 18:29:26 -04:00
// If output is not nil, a human-readable progress bar will be written to it.
// FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
2013-10-31 19:57:45 -04:00
func ( graph * Graph ) TempLayerArchive ( id string , compression archive . Compression , sf * utils . StreamFormatter , output io . Writer ) ( * archive . TempArchive , error ) {
2013-04-21 17:23:55 -04:00
image , err := graph . Get ( id )
if err != nil {
return nil , err
}
tmp , err := graph . tmp ( )
if err != nil {
return nil , err
}
2013-10-31 19:57:45 -04:00
a , err := image . TarLayer ( compression )
2013-04-21 17:23:55 -04:00
if err != nil {
return nil , err
}
2013-10-31 19:57:45 -04:00
return archive . NewTempArchive ( utils . ProgressReader ( ioutil . NopCloser ( a ) , 0 , output , sf . FormatProgress ( "" , "Buffering to disk" , "%v/%v (%v)" ) , sf , true ) , tmp . Root )
2013-04-21 17:23:55 -04:00
}
2013-03-30 00:13:59 -04:00
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
2013-03-18 03:15:35 -04:00
func ( graph * Graph ) Mktemp ( id string ) ( string , error ) {
2013-04-04 01:14:28 -04:00
if id == "" {
2013-06-04 14:00:22 -04:00
id = GenerateID ( )
2013-04-04 01:14:28 -04:00
}
2013-04-21 17:23:55 -04:00
tmp , err := graph . tmp ( )
2013-03-18 03:15:35 -04:00
if err != nil {
return "" , fmt . Errorf ( "Couldn't create temp: %s" , err )
}
if tmp . Exists ( id ) {
2013-06-27 00:33:55 -04:00
return "" , fmt . Errorf ( "Image %s already exists" , id )
2013-03-18 03:15:35 -04:00
}
return tmp . imageRoot ( id ) , nil
}
2013-06-14 19:56:08 -04:00
// getDockerInitLayer returns the path of a layer containing a mountpoint suitable
// for bind-mounting dockerinit into the container. The mountpoint is simply an
// empty file at /.dockerinit
//
// This extra layer is used by all containers as the top-most ro layer. It protects
// the container from unwanted side-effects on the rw layer.
func ( graph * Graph ) getDockerInitLayer ( ) ( string , error ) {
tmp , err := graph . tmp ( )
if err != nil {
return "" , err
}
initLayer := tmp . imageRoot ( "_dockerinit" )
2013-07-22 17:55:07 -04:00
if err := os . Mkdir ( initLayer , 0755 ) ; err != nil && ! os . IsExist ( err ) {
2013-06-14 19:56:08 -04:00
// If directory already existed, keep going.
// For all other errors, abort.
return "" , err
}
2013-08-08 14:25:02 -04:00
for pth , typ := range map [ string ] string {
"/dev/pts" : "dir" ,
"/dev/shm" : "dir" ,
"/proc" : "dir" ,
"/sys" : "dir" ,
"/.dockerinit" : "file" ,
2013-08-13 18:40:23 -04:00
"/.dockerenv" : "file" ,
2013-08-08 14:25:02 -04:00
"/etc/resolv.conf" : "file" ,
2013-09-09 15:40:25 -04:00
"/etc/hosts" : "file" ,
"/etc/hostname" : "file" ,
2013-08-08 14:25:02 -04:00
// "var/run": "dir",
// "var/lock": "dir",
} {
if _ , err := os . Stat ( path . Join ( initLayer , pth ) ) ; err != nil {
if os . IsNotExist ( err ) {
switch typ {
case "dir" :
if err := os . MkdirAll ( path . Join ( initLayer , pth ) , 0755 ) ; err != nil {
return "" , err
}
case "file" :
if err := os . MkdirAll ( path . Join ( initLayer , path . Dir ( pth ) ) , 0755 ) ; err != nil {
return "" , err
}
if f , err := os . OpenFile ( path . Join ( initLayer , pth ) , os . O_CREATE , 0755 ) ; err != nil {
return "" , err
} else {
f . Close ( )
}
}
} else {
return "" , err
}
}
2013-06-14 19:56:08 -04:00
}
2013-08-08 14:25:02 -04:00
2013-06-14 19:56:08 -04:00
// Layer is ready to use, if it wasn't before.
return initLayer , nil
}
2013-04-21 17:23:55 -04:00
func ( graph * Graph ) tmp ( ) ( * Graph , error ) {
2013-06-14 19:56:08 -04:00
// Changed to _tmp from :tmp:, because it messed with ":" separators in aufs branch syntax...
return NewGraph ( path . Join ( graph . Root , "_tmp" ) )
2013-04-21 17:23:55 -04:00
}
2013-03-30 03:22:24 -04:00
// Check if given error is "not empty".
// Note: this is the way golang does it internally with os.IsNotExists.
2013-03-26 06:33:47 -04:00
func isNotEmpty ( err error ) bool {
switch pe := err . ( type ) {
case nil :
return false
case * os . PathError :
err = pe . Err
case * os . LinkError :
err = pe . Err
}
return strings . Contains ( err . Error ( ) , " not empty" )
}
2013-03-30 00:13:59 -04:00
// Delete atomically removes an image from the graph.
2013-04-01 19:04:44 -04:00
func ( graph * Graph ) Delete ( name string ) error {
id , err := graph . idIndex . Get ( name )
if err != nil {
return err
}
2013-04-04 01:14:28 -04:00
tmp , err := graph . Mktemp ( "" )
2013-03-18 03:15:35 -04:00
if err != nil {
return err
}
2013-04-01 01:11:55 -04:00
graph . idIndex . Delete ( id )
2013-04-04 01:14:28 -04:00
err = os . Rename ( graph . imageRoot ( id ) , tmp )
2013-03-18 03:15:35 -04:00
if err != nil {
return err
}
2013-04-04 01:14:28 -04:00
return os . RemoveAll ( tmp )
2013-03-18 03:15:35 -04:00
}
2013-03-30 03:22:24 -04:00
// Map returns a list of all images in the graph, addressable by ID.
2013-03-21 20:35:49 -04:00
func ( graph * Graph ) Map ( ) ( map [ string ] * Image , error ) {
2013-08-31 23:31:21 -04:00
images := make ( map [ string ] * Image )
2013-08-31 23:34:51 -04:00
err := graph . walkAll ( func ( image * Image ) {
2013-08-31 23:31:21 -04:00
images [ image . ID ] = image
} )
2013-03-21 20:35:49 -04:00
if err != nil {
return nil , err
}
return images , nil
}
2013-08-31 23:34:51 -04:00
// walkAll iterates over each image in the graph, and passes it to a handler.
2013-03-30 00:13:59 -04:00
// The walking order is undetermined.
2013-08-31 23:34:51 -04:00
func ( graph * Graph ) walkAll ( handler func ( * Image ) ) error {
2013-03-18 03:15:35 -04:00
files , err := ioutil . ReadDir ( graph . Root )
if err != nil {
2013-03-23 20:03:30 -04:00
return err
2013-03-18 03:15:35 -04:00
}
for _ , st := range files {
if img , err := graph . Get ( st . Name ( ) ) ; err != nil {
// Skip image
continue
2013-03-23 20:03:30 -04:00
} else if handler != nil {
handler ( img )
}
}
return nil
}
2013-03-30 00:13:59 -04:00
// ByParent returns a lookup table of images by their parent.
// If an image of id ID has 3 children images, then the value for key ID
// will be a list of 3 images.
// If an image has no children, it will not have an entry in the table.
2013-03-23 20:03:30 -04:00
func ( graph * Graph ) ByParent ( ) ( map [ string ] [ ] * Image , error ) {
byParent := make ( map [ string ] [ ] * Image )
2013-08-31 23:34:51 -04:00
err := graph . walkAll ( func ( image * Image ) {
2013-05-07 00:31:59 -04:00
parent , err := graph . Get ( image . Parent )
2013-03-23 20:03:30 -04:00
if err != nil {
return
}
2013-06-04 14:00:22 -04:00
if children , exists := byParent [ parent . ID ] ; exists {
byParent [ parent . ID ] = append ( children , image )
2013-08-11 03:37:16 -04:00
} else {
byParent [ parent . ID ] = [ ] * Image { image }
2013-03-18 03:15:35 -04:00
}
2013-03-23 20:03:30 -04:00
} )
return byParent , err
}
2013-03-30 00:13:59 -04:00
// Heads returns all heads in the graph, keyed by id.
// A head is an image which is not the parent of another image in the graph.
2013-03-23 20:03:30 -04:00
func ( graph * Graph ) Heads ( ) ( map [ string ] * Image , error ) {
heads := make ( map [ string ] * Image )
byParent , err := graph . ByParent ( )
if err != nil {
return nil , err
2013-03-18 03:15:35 -04:00
}
2013-08-31 23:34:51 -04:00
err = graph . walkAll ( func ( image * Image ) {
2013-03-23 20:03:30 -04:00
// If it's not in the byParent lookup table, then
// it's not a parent -> so it's a head!
2013-06-04 14:00:22 -04:00
if _ , exists := byParent [ image . ID ] ; ! exists {
heads [ image . ID ] = image
2013-03-23 20:03:30 -04:00
}
} )
return heads , err
2013-03-18 03:15:35 -04:00
}
func ( graph * Graph ) imageRoot ( id string ) string {
return path . Join ( graph . Root , id )
}