2013-02-15 21:46:46 -05:00
package fs
import (
"database/sql"
2013-02-22 02:01:30 -05:00
"errors"
"fmt"
2013-03-12 14:59:27 -04:00
"github.com/dotcloud/docker/future"
2013-02-22 02:01:30 -05:00
_ "github.com/mattn/go-sqlite3"
2013-03-09 22:56:33 -05:00
"github.com/shykes/gorp" //Forked to implement CreateTablesOpts
2013-02-15 21:46:46 -05:00
"io"
2013-03-12 07:24:26 -04:00
"io/ioutil"
2013-02-22 02:01:30 -05:00
"os"
2013-02-15 21:46:46 -05:00
"path"
2013-03-11 08:42:36 -04:00
"path/filepath"
2013-02-22 02:01:30 -05:00
"syscall"
"time"
2013-02-15 21:46:46 -05:00
)
type Store struct {
2013-02-22 02:01:30 -05:00
Root string
db * sql . DB
orm * gorp . DbMap
layers * LayerStore
2013-02-15 21:46:46 -05:00
}
type Archive io . Reader
func New ( root string ) ( * Store , error ) {
2013-02-26 20:45:46 -05:00
isNewStore := true
2013-02-15 21:46:46 -05:00
if err := os . Mkdir ( root , 0700 ) ; err != nil && ! os . IsExist ( err ) {
return nil , err
}
db , err := sql . Open ( "sqlite3" , path . Join ( root , "db" ) )
if err != nil {
return nil , err
}
orm := & gorp . DbMap { Db : db , Dialect : gorp . SqliteDialect { } }
orm . AddTableWithName ( Image { } , "images" ) . SetKeys ( false , "Id" )
orm . AddTableWithName ( Path { } , "paths" ) . SetKeys ( false , "Path" , "Image" )
2013-02-16 00:51:36 -05:00
orm . AddTableWithName ( Mountpoint { } , "mountpoints" ) . SetKeys ( false , "Root" )
2013-02-19 00:10:19 -05:00
orm . AddTableWithName ( Tag { } , "tags" ) . SetKeys ( false , "TagName" )
2013-02-26 20:45:46 -05:00
if isNewStore {
2013-03-09 22:56:33 -05:00
if err := orm . CreateTablesOpts ( true ) ; err != nil {
2013-02-26 20:45:46 -05:00
return nil , err
}
2013-02-15 21:46:46 -05:00
}
2013-02-26 20:45:46 -05:00
2013-02-18 18:25:43 -05:00
layers , err := NewLayerStore ( path . Join ( root , "layers" ) )
if err != nil {
return nil , err
}
2013-02-15 21:46:46 -05:00
return & Store {
2013-02-22 02:01:30 -05:00
Root : root ,
db : db ,
orm : orm ,
2013-02-18 18:25:43 -05:00
layers : layers ,
2013-02-15 21:46:46 -05:00
} , nil
}
2013-02-22 02:01:30 -05:00
func ( store * Store ) imageList ( src [ ] interface { } ) [ ] * Image {
2013-02-15 21:46:46 -05:00
var images [ ] * Image
for _ , i := range src {
img := i . ( * Image )
img . store = store
images = append ( images , img )
}
return images
}
func ( store * Store ) Images ( ) ( [ ] * Image , error ) {
2013-02-22 02:01:30 -05:00
images , err := store . orm . Select ( Image { } , "select * from images" )
2013-02-15 21:46:46 -05:00
if err != nil {
return nil , err
}
return store . imageList ( images ) , nil
}
func ( store * Store ) Paths ( ) ( [ ] string , error ) {
var paths [ ] string
rows , err := store . db . Query ( "select distinct Path from paths order by Path" )
if err != nil {
return nil , err
}
for rows . Next ( ) {
var path string
if err := rows . Scan ( & path ) ; err != nil {
return nil , err
}
paths = append ( paths , path )
}
return paths , nil
}
2013-03-13 14:14:37 -04:00
func ( store * Store ) RemoveInPath ( pth string ) error {
images , err := store . List ( pth )
if err != nil {
return err
}
for _ , img := range images {
if err = store . Remove ( img ) ; err != nil {
return err
}
}
return nil
}
func ( store * Store ) Remove ( img * Image ) error {
_ , err := store . orm . Delete ( img )
return err
}
2013-02-15 21:46:46 -05:00
func ( store * Store ) List ( pth string ) ( [ ] * Image , error ) {
pth = path . Clean ( pth )
images , err := store . orm . Select ( Image { } , "select images.* from images, paths where Path=? and paths.Image=images.Id" , pth )
if err != nil {
return nil , err
}
return store . imageList ( images ) , nil
}
2013-03-12 08:57:19 -04:00
func ( store * Store ) Find ( pth string ) ( * Image , error ) {
pth = path . Clean ( pth )
img , err := store . Get ( pth )
if err != nil {
return nil , err
} else if img != nil {
return img , nil
}
images , err := store . orm . Select ( Image { } , "select images.* from images, paths where Path=? and paths.Image=images.Id order by images.Created desc limit 1" , pth )
if err != nil {
return nil , err
} else if len ( images ) < 1 {
return nil , nil
}
img = images [ 0 ] . ( * Image )
img . store = store
return img , nil
}
2013-02-15 21:46:46 -05:00
func ( store * Store ) Get ( id string ) ( * Image , error ) {
2013-02-16 00:48:04 -05:00
img , err := store . orm . Get ( Image { } , id )
2013-02-22 02:01:30 -05:00
if img == nil {
return nil , err
}
2013-02-26 20:45:46 -05:00
res := img . ( * Image )
res . store = store
return res , err
2013-02-15 21:46:46 -05:00
}
2013-02-18 18:25:43 -05:00
func ( store * Store ) Create ( layerData Archive , parent * Image , pth , comment string ) ( * Image , error ) {
2013-02-15 21:46:46 -05:00
// FIXME: actually do something with the layer...
img := & Image {
2013-02-22 02:01:30 -05:00
Id : future . RandomId ( ) ,
Comment : comment ,
2013-03-12 07:24:26 -04:00
Created : time . Now ( ) . Unix ( ) ,
2013-02-22 02:01:30 -05:00
store : store ,
2013-02-15 21:46:46 -05:00
}
2013-03-12 08:23:13 -04:00
if parent != nil {
img . Parent = parent . Id
}
2013-02-18 18:25:43 -05:00
// FIXME: we shouldn't have to pass os.Stderr to AddLayer()...
// FIXME: Archive should contain compression info. For now we only support uncompressed.
2013-03-11 08:42:36 -04:00
_ , err := store . layers . AddLayer ( img . Id , layerData )
2013-02-18 18:25:43 -05:00
if err != nil {
2013-02-19 00:10:19 -05:00
return nil , errors . New ( fmt . Sprintf ( "Could not add layer: %s" , err ) )
2013-02-18 18:25:43 -05:00
}
2013-02-15 21:46:46 -05:00
path := & Path {
2013-02-22 02:01:30 -05:00
Path : path . Clean ( pth ) ,
Image : img . Id ,
2013-02-15 21:46:46 -05:00
}
trans , err := store . orm . Begin ( )
if err != nil {
2013-02-19 00:10:19 -05:00
return nil , errors . New ( fmt . Sprintf ( "Could not begin transaction:" , err ) )
2013-02-15 21:46:46 -05:00
}
if err := trans . Insert ( img ) ; err != nil {
2013-02-19 00:10:19 -05:00
return nil , errors . New ( fmt . Sprintf ( "Could not insert image info: %s" , err ) )
2013-02-15 21:46:46 -05:00
}
if err := trans . Insert ( path ) ; err != nil {
2013-02-19 00:10:19 -05:00
return nil , errors . New ( fmt . Sprintf ( "Could not insert path info: %s" , err ) )
2013-02-15 21:46:46 -05:00
}
if err := trans . Commit ( ) ; err != nil {
2013-02-19 00:10:19 -05:00
return nil , errors . New ( fmt . Sprintf ( "Could not commit transaction: %s" , err ) )
2013-02-15 21:46:46 -05:00
}
return img , nil
}
func ( store * Store ) Register ( image * Image , pth string ) error {
2013-02-16 00:50:59 -05:00
image . store = store
2013-02-15 21:46:46 -05:00
// FIXME: import layer
trans , err := store . orm . Begin ( )
if err != nil {
return err
}
trans . Insert ( image )
trans . Insert ( & Path { Path : pth , Image : image . Id } )
return trans . Commit ( )
}
2013-03-08 13:48:22 -05:00
func ( store * Store ) Layers ( ) [ ] string {
return store . layers . List ( )
}
2013-02-15 21:46:46 -05:00
type Image struct {
2013-02-22 02:01:30 -05:00
Id string
Parent string
Comment string
2013-03-12 07:24:26 -04:00
Created int64
2013-02-22 02:01:30 -05:00
store * Store ` db:"-" `
2013-02-15 21:46:46 -05:00
}
func ( image * Image ) Copy ( pth string ) ( * Image , error ) {
if err := image . store . orm . Insert ( & Path { Path : pth , Image : image . Id } ) ; err != nil {
return nil , err
}
return image , nil
}
2013-02-16 00:51:36 -05:00
type Mountpoint struct {
2013-02-22 02:01:30 -05:00
Image string
Root string
Rw string
2013-02-26 20:45:46 -05:00
Store * Store ` db:"-" `
2013-02-16 00:51:36 -05:00
}
func ( image * Image ) Mountpoint ( root , rw string ) ( * Mountpoint , error ) {
2013-02-22 02:01:30 -05:00
mountpoint := & Mountpoint {
Root : path . Clean ( root ) ,
Rw : path . Clean ( rw ) ,
Image : image . Id ,
2013-02-26 20:45:46 -05:00
Store : image . store ,
2013-02-22 02:01:30 -05:00
}
2013-02-16 00:51:36 -05:00
if err := image . store . orm . Insert ( mountpoint ) ; err != nil {
return nil , err
}
return mountpoint , nil
}
2013-02-22 02:01:30 -05:00
func ( image * Image ) layers ( ) ( [ ] string , error ) {
var list [ ] string
2013-03-11 08:42:36 -04:00
var err error
2013-02-22 02:01:30 -05:00
currentImg := image
for currentImg != nil {
2013-03-12 08:23:13 -04:00
if layer := image . store . layers . Get ( currentImg . Id ) ; layer != "" {
2013-02-22 02:01:30 -05:00
list = append ( list , layer )
} else {
return list , fmt . Errorf ( "Layer not found for image %s" , image . Id )
}
currentImg , err = currentImg . store . Get ( currentImg . Parent )
if err != nil {
return list , fmt . Errorf ( "Error while getting parent image: %v" , err )
}
}
return list , nil
}
2013-02-16 00:51:36 -05:00
func ( image * Image ) Mountpoints ( ) ( [ ] * Mountpoint , error ) {
var mountpoints [ ] * Mountpoint
res , err := image . store . orm . Select ( Mountpoint { } , "select * from mountpoints where Image=?" , image . Id )
if err != nil {
return nil , err
}
for _ , mp := range res {
mountpoints = append ( mountpoints , mp . ( * Mountpoint ) )
}
return mountpoints , nil
}
2013-02-22 02:01:30 -05:00
func ( image * Image ) Mount ( root , rw string ) ( * Mountpoint , error ) {
var mountpoint * Mountpoint
2013-03-09 22:49:09 -05:00
if mp , err := image . store . FetchMountpoint ( root , rw ) ; err != nil {
2013-02-22 02:01:30 -05:00
return nil , err
} else if mp == nil {
mountpoint , err = image . Mountpoint ( root , rw )
if err != nil {
return nil , fmt . Errorf ( "Could not create mountpoint: %s" , err )
} else if mountpoint == nil {
return nil , errors . New ( "No mountpoint created" )
}
} else {
mountpoint = mp
}
if err := mountpoint . createFolders ( ) ; err != nil {
return nil , err
}
// FIXME: Now mount the layers
rwBranch := fmt . Sprintf ( "%v=rw" , mountpoint . Rw )
roBranches := ""
layers , err := image . layers ( )
if err != nil {
return nil , err
}
for _ , layer := range layers {
roBranches += fmt . Sprintf ( "%v=ro:" , layer )
}
branches := fmt . Sprintf ( "br:%v:%v" , rwBranch , roBranches )
if err := mount ( "none" , mountpoint . Root , "aufs" , 0 , branches ) ; err != nil {
return mountpoint , err
}
if ! mountpoint . Mounted ( ) {
return mountpoint , errors . New ( "Mount failed" )
}
return mountpoint , nil
}
2013-02-26 20:45:46 -05:00
func ( mp * Mountpoint ) EnsureMounted ( ) error {
if mp . Mounted ( ) {
return nil
}
img , err := mp . Store . Get ( mp . Image )
if err != nil {
return err
}
_ , err = img . Mount ( mp . Root , mp . Rw )
return err
}
2013-02-22 02:01:30 -05:00
func ( mp * Mountpoint ) createFolders ( ) error {
if err := os . Mkdir ( mp . Root , 0755 ) ; err != nil && ! os . IsExist ( err ) {
return err
}
if err := os . Mkdir ( mp . Rw , 0755 ) ; err != nil && ! os . IsExist ( err ) {
return err
}
return nil
}
func ( mp * Mountpoint ) Mounted ( ) bool {
root , err := os . Stat ( mp . Root )
if err != nil {
if os . IsNotExist ( err ) {
return false
}
panic ( err )
}
parent , err := os . Stat ( filepath . Join ( mp . Root , ".." ) )
if err != nil {
panic ( err )
}
rootSt := root . Sys ( ) . ( * syscall . Stat_t )
parentSt := parent . Sys ( ) . ( * syscall . Stat_t )
return rootSt . Dev != parentSt . Dev
}
func ( mp * Mountpoint ) Umount ( ) error {
if ! mp . Mounted ( ) {
return errors . New ( "Mountpoint doesn't seem to be mounted" )
}
if err := syscall . Unmount ( mp . Root , 0 ) ; err != nil {
return fmt . Errorf ( "Unmount syscall failed: %v" , err )
}
if mp . Mounted ( ) {
return fmt . Errorf ( "Umount: Filesystem still mounted after calling umount(%v)" , mp . Root )
}
// Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
// for some time. We'll just keep retrying until it succeeds.
for retries := 0 ; retries < 1000 ; retries ++ {
err := os . Remove ( mp . Root )
if err == nil {
// rm mntpoint succeeded
return nil
}
if os . IsNotExist ( err ) {
// mntpoint doesn't exist anymore. Success.
return nil
}
// fmt.Printf("(%v) Remove %v returned: %v\n", retries, mp.Root, err)
time . Sleep ( 10 * time . Millisecond )
}
return fmt . Errorf ( "Umount: Failed to umount %v" , mp . Root )
}
func ( mp * Mountpoint ) Deregister ( ) error {
if mp . Mounted ( ) {
return errors . New ( "Mountpoint is currently mounted, can't deregister" )
}
2013-02-26 20:45:46 -05:00
_ , err := mp . Store . orm . Delete ( mp )
return err
2013-02-22 02:01:30 -05:00
}
2013-03-09 22:49:09 -05:00
func ( store * Store ) FetchMountpoint ( root , rw string ) ( * Mountpoint , error ) {
res , err := store . orm . Select ( Mountpoint { } , "select * from mountpoints where Root=? and Rw=?" , root , rw )
2013-02-22 02:01:30 -05:00
if err != nil {
return nil , err
} else if len ( res ) < 1 || res [ 0 ] == nil {
return nil , nil
}
2013-02-26 20:45:46 -05:00
mp := res [ 0 ] . ( * Mountpoint )
2013-03-09 22:49:09 -05:00
mp . Store = store
2013-02-26 20:45:46 -05:00
return mp , nil
2013-02-22 02:01:30 -05:00
}
2013-03-12 07:24:26 -04:00
// OpenFile opens the named file for reading.
func ( mp * Mountpoint ) OpenFile ( path string , flag int , perm os . FileMode ) ( * os . File , error ) {
if err := mp . EnsureMounted ( ) ; err != nil {
return nil , err
}
return os . OpenFile ( filepath . Join ( mp . Root , path ) , flag , perm )
}
// ReadDir reads the directory named by dirname, relative to the Mountpoint's root,
// and returns a list of sorted directory entries
func ( mp * Mountpoint ) ReadDir ( dirname string ) ( [ ] os . FileInfo , error ) {
if err := mp . EnsureMounted ( ) ; err != nil {
return nil , err
}
return ioutil . ReadDir ( filepath . Join ( mp . Root , dirname ) )
}
2013-02-19 00:10:19 -05:00
func ( store * Store ) AddTag ( imageId , tagName string ) error {
if image , err := store . Get ( imageId ) ; err != nil {
return err
} else if image == nil {
return errors . New ( "No image with ID " + imageId )
}
err2 := store . orm . Insert ( & Tag {
2013-02-22 02:01:30 -05:00
TagName : tagName ,
Image : imageId ,
2013-02-19 00:10:19 -05:00
} )
return err2
}
func ( store * Store ) GetByTag ( tagName string ) ( * Image , error ) {
res , err := store . orm . Get ( Tag { } , tagName )
if err != nil {
return nil , err
} else if res == nil {
return nil , errors . New ( "No image associated to tag \"" + tagName + "\"" )
}
tag := res . ( * Tag )
img , err2 := store . Get ( tag . Image )
if err2 != nil {
return nil , err2
} else if img == nil {
return nil , errors . New ( "Tag was found but image seems to be inexistent." )
}
return img , nil
}
2013-02-15 21:46:46 -05:00
type Path struct {
2013-02-22 02:01:30 -05:00
Path string
Image string
2013-02-15 21:46:46 -05:00
}
2013-02-19 00:10:19 -05:00
type Tag struct {
2013-02-22 02:01:30 -05:00
TagName string
Image string
}