diff --git a/container.go b/container.go index 4a398a1c3e..21f4d54920 100644 --- a/container.go +++ b/container.go @@ -2,6 +2,7 @@ package docker import ( "./fs" + "bytes" "encoding/json" "errors" "github.com/kr/pty" @@ -118,17 +119,25 @@ func createContainer(id string, root string, command string, args []string, imag return container, nil } -func loadContainer(containerPath string, netManager *NetworkManager) (*Container, error) { +func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) { data, err := ioutil.ReadFile(path.Join(containerPath, "config.json")) if err != nil { return nil, err } + mountpoint, err := store.FetchMountpoint( + path.Join(containerPath, "rootfs"), + path.Join(containerPath, "rw"), + ) + if err != nil { + return nil, err + } container := &Container{ stdout: newWriteBroadcaster(), stderr: newWriteBroadcaster(), lxcConfigPath: path.Join(containerPath, "config.lxc"), networkManager: netManager, NetworkSettings: &NetworkSettings{}, + Mountpoint: mountpoint, } // Load container settings if err := json.Unmarshal(data, container); err != nil { diff --git a/docker.go b/docker.go index b3b52edb2d..49c6d83248 100644 --- a/docker.go +++ b/docker.go @@ -94,12 +94,11 @@ func (docker *Docker) restore() error { return err } for _, v := range dir { - container, err := loadContainer(path.Join(docker.repository, v.Name()), docker.networkManager) + container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager) if err != nil { log.Printf("Failed to load container %v: %v", v.Name(), err) continue } - container.Mountpoint.Store = docker.Store docker.containers.PushBack(container) } return nil diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go index 7fff1eb7dd..22ae8df8e0 100644 --- a/dockerd/dockerd.go +++ b/dockerd/dockerd.go @@ -1,9 +1,9 @@ package main import ( - "flag" ".." "../server" + "flag" "log" ) diff --git a/fake/fake.go b/fake/fake.go index 8edbd88e6b..598ff6a813 100644 --- a/fake/fake.go +++ b/fake/fake.go @@ -13,7 +13,7 @@ func FakeTar() (io.Reader, error) { content := []byte("Hello world!\n") buf := new(bytes.Buffer) tw := tar.NewWriter(buf) - for _, name := range []string{"hello", "etc/postgres/postgres.conf", "etc/passwd", "var/log/postgres/postgres.conf"} { + for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} { hdr := new(tar.Header) hdr.Size = int64(len(content)) hdr.Name = name diff --git a/fs/changes.go b/fs/changes.go index 5c3523f3f3..9bd315ad82 100644 --- a/fs/changes.go +++ b/fs/changes.go @@ -1,129 +1,129 @@ package fs import ( - "fmt" - "path/filepath" - "os" - "strings" + "fmt" + "os" + "path/filepath" + "strings" ) type ChangeType int const ( - ChangeModify = iota - ChangeAdd - ChangeDelete + ChangeModify = iota + ChangeAdd + ChangeDelete ) type Change struct { - Path string - Kind ChangeType + Path string + Kind ChangeType } func (change *Change) String() string { - var kind string - switch change.Kind { - case ChangeModify: - kind = "C" - case ChangeAdd: - kind = "A" - case ChangeDelete: - kind = "D" - } - return fmt.Sprintf("%s %s", kind, change.Path) + var kind string + switch change.Kind { + case ChangeModify: + kind = "C" + case ChangeAdd: + kind = "A" + case ChangeDelete: + kind = "D" + } + return fmt.Sprintf("%s %s", kind, change.Path) } func (store *Store) Changes(mp *Mountpoint) ([]Change, error) { - var changes []Change - image, err := store.Get(mp.Image) - if err != nil { - return nil, err - } - layers, err := image.layers() - if err != nil { - return nil, err - } + var changes []Change + image, err := store.Get(mp.Image) + if err != nil { + return nil, err + } + layers, err := image.layers() + if err != nil { + return nil, err + } - err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } + err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } - // Rebase path - path, err = filepath.Rel(mp.Rw, path) - if err != nil { - return err - } - path = filepath.Join("/", path) + // Rebase path + path, err = filepath.Rel(mp.Rw, path) + if err != nil { + return err + } + path = filepath.Join("/", path) - // Skip root - if path == "/" { - return nil - } + // Skip root + if path == "/" { + return nil + } - // Skip AUFS metadata - if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched { - return err - } + // Skip AUFS metadata + if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched { + return err + } - change := Change{ - Path: path, - } + change := Change{ + Path: path, + } - // Find out what kind of modification happened - file := filepath.Base(path) - // If there is a whiteout, then the file was removed - if strings.HasPrefix(file, ".wh.") { - originalFile := strings.TrimLeft(file, ".wh.") - change.Path = filepath.Join(filepath.Dir(path), originalFile) - change.Kind = ChangeDelete - } else { - // Otherwise, the file was added - change.Kind = ChangeAdd + // Find out what kind of modification happened + file := filepath.Base(path) + // If there is a whiteout, then the file was removed + if strings.HasPrefix(file, ".wh.") { + originalFile := strings.TrimLeft(file, ".wh.") + change.Path = filepath.Join(filepath.Dir(path), originalFile) + change.Kind = ChangeDelete + } else { + // Otherwise, the file was added + change.Kind = ChangeAdd - // ...Unless it already existed in a top layer, in which case, it's a modification - for _, layer := range layers { - stat, err := os.Stat(filepath.Join(layer, path)) - if err != nil && !os.IsNotExist(err) { - return err - } - if err == nil { - // The file existed in the top layer, so that's a modification + // ...Unless it already existed in a top layer, in which case, it's a modification + for _, layer := range layers { + stat, err := os.Stat(filepath.Join(layer, path)) + if err != nil && !os.IsNotExist(err) { + return err + } + if err == nil { + // The file existed in the top layer, so that's a modification - // However, if it's a directory, maybe it wasn't actually modified. - // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar - if stat.IsDir() && f.IsDir() { - if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() { - // Both directories are the same, don't record the change - return nil - } - } - change.Kind = ChangeModify - break - } - } - } + // However, if it's a directory, maybe it wasn't actually modified. + // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar + if stat.IsDir() && f.IsDir() { + if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() { + // Both directories are the same, don't record the change + return nil + } + } + change.Kind = ChangeModify + break + } + } + } - // Record change - changes = append(changes, change) - return nil - }) - if err != nil { - return nil, err - } - return changes, nil + // Record change + changes = append(changes, change) + return nil + }) + if err != nil { + return nil, err + } + return changes, nil } // Reset removes all changes to the filesystem, reverting it to its initial state. func (mp *Mountpoint) Reset() error { - if err := os.RemoveAll(mp.Rw); err != nil { - return err - } - // We removed the RW directory itself along with its content: let's re-create an empty one. - if err := mp.createFolders(); err != nil { - return err - } - return nil + if err := os.RemoveAll(mp.Rw); err != nil { + return err + } + // We removed the RW directory itself along with its content: let's re-create an empty one. + if err := mp.createFolders(); err != nil { + return err + } + return nil } // Open opens the named file for reading. @@ -141,4 +141,4 @@ func (mp *Mountpoint) Reset() error { // return nil, err // } // return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname)) -// } \ No newline at end of file +// } diff --git a/fs/mount_linux.go b/fs/mount_linux.go index 6dd2a39af8..b36888f75c 100644 --- a/fs/mount_linux.go +++ b/fs/mount_linux.go @@ -2,7 +2,6 @@ package fs import "syscall" - func mount(source string, target string, fstype string, flags uintptr, data string) (err error) { return syscall.Mount(source, target, fstype, flags, data) } diff --git a/fs/store.go b/fs/store.go index ed4a539af1..c6612d9b19 100644 --- a/fs/store.go +++ b/fs/store.go @@ -4,9 +4,9 @@ import ( "database/sql" "errors" "fmt" - "github.com/coopernurse/gorp" "github.com/dotcloud/docker/future" _ "github.com/mattn/go-sqlite3" + "github.com/shykes/gorp" //Forked to implement CreateTablesOpts "io" "os" "path" @@ -29,8 +29,6 @@ func New(root string) (*Store, error) { if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) { return nil, err - } else if os.IsExist(err) { - isNewStore = false } db, err := sql.Open("sqlite3", path.Join(root, "db")) if err != nil { @@ -42,7 +40,7 @@ func New(root string) (*Store, error) { orm.AddTableWithName(Mountpoint{}, "mountpoints").SetKeys(false, "Root") orm.AddTableWithName(Tag{}, "tags").SetKeys(false, "TagName") if isNewStore { - if err := orm.CreateTables(); err != nil { + if err := orm.CreateTablesOpts(true); err != nil { return nil, err } } @@ -227,7 +225,7 @@ func (image *Image) Mountpoints() ([]*Mountpoint, error) { func (image *Image) Mount(root, rw string) (*Mountpoint, error) { var mountpoint *Mountpoint - if mp, err := image.fetchMountpoint(root, rw); err != nil { + if mp, err := image.store.FetchMountpoint(root, rw); err != nil { return nil, err } else if mp == nil { mountpoint, err = image.Mountpoint(root, rw) @@ -345,8 +343,8 @@ func (mp *Mountpoint) Deregister() error { return err } -func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) { - res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=? and Root=? and Rw=?", image.Id, root, rw) +func (store *Store) FetchMountpoint(root, rw string) (*Mountpoint, error) { + res, err := store.orm.Select(Mountpoint{}, "select * from mountpoints where Root=? and Rw=?", root, rw) if err != nil { return nil, err } else if len(res) < 1 || res[0] == nil { @@ -354,7 +352,7 @@ func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) { } mp := res[0].(*Mountpoint) - mp.Store = image.store + mp.Store = store return mp, nil } diff --git a/fs/store_test.go b/fs/store_test.go index 954c83d865..d5715d1ba0 100644 --- a/fs/store_test.go +++ b/fs/store_test.go @@ -1,9 +1,9 @@ package fs import ( + "../fake" "errors" "fmt" - "../fake" "io/ioutil" "os" "testing" diff --git a/image/image.go b/image/image.go deleted file mode 100644 index 4702919ab4..0000000000 --- a/image/image.go +++ /dev/null @@ -1,362 +0,0 @@ -package image - -import ( - "encoding/json" - "errors" - "github.com/dotcloud/docker/future" - "io" - "io/ioutil" - "os" - "path" - "path/filepath" - "regexp" - "sort" - "strings" - "time" -) - -type Store struct { - *Index - Root string - Layers *LayerStore -} - -func New(root string) (*Store, error) { - abspath, err := filepath.Abs(root) - if err != nil { - return nil, err - } - if err := os.MkdirAll(abspath, 0700); err != nil && !os.IsExist(err) { - return nil, err - } - layers, err := NewLayerStore(path.Join(root, "layers")) - if err != nil { - return nil, err - } - if err := layers.Init(); err != nil { - return nil, err - } - return &Store{ - Root: abspath, - Index: NewIndex(path.Join(root, "index.json")), - Layers: layers, - }, nil -} - -// Import creates a new image from the contents of `archive` and registers it in the store as `name`. -// If `parent` is not nil, it will registered as the parent of the new image. -func (store *Store) Import(name string, archive io.Reader, parent *Image) (*Image, error) { - layer, err := store.Layers.AddLayer(archive) - if err != nil { - return nil, err - } - layers := []string{layer} - if parent != nil { - layers = append(layers, parent.Layers...) - } - var parentId string - if parent != nil { - parentId = parent.Id - } - return store.Create(name, parentId, layers...) -} - -func (store *Store) Create(name string, source string, layers ...string) (*Image, error) { - image, err := NewImage(name, layers, source) - if err != nil { - return nil, err - } - if err := store.Index.Add(name, image); err != nil { - return nil, err - } - return image, nil -} - -// Index - -type Index struct { - Path string - ByName map[string]*History - ById map[string]*Image -} - -func NewIndex(path string) *Index { - return &Index{ - Path: path, - ByName: make(map[string]*History), - ById: make(map[string]*Image), - } -} - -func (index *Index) Exists(id string) bool { - _, exists := index.ById[id] - return exists -} - -func (index *Index) Find(idOrName string) *Image { - // Load - if err := index.load(); err != nil { - return nil - } - // Lookup by ID - if image, exists := index.ById[idOrName]; exists { - return image - } - // Lookup by name - if history, exists := index.ByName[idOrName]; exists && history.Len() > 0 { - return (*history)[0] - } - return nil -} - -func (index *Index) Add(name string, image *Image) error { - // Load - if err := index.load(); err != nil { - return err - } - if _, exists := index.ByName[name]; !exists { - index.ByName[name] = new(History) - } else { - // If this image is already the latest version, don't add it. - if (*index.ByName[name])[0].Id == image.Id { - return nil - } - } - index.ByName[name].Add(image) - index.ById[image.Id] = image - // Save - if err := index.save(); err != nil { - return err - } - return nil -} - -func (index *Index) Copy(srcNameOrId, dstName string) (*Image, error) { - if srcNameOrId == "" || dstName == "" { - return nil, errors.New("Illegal image name") - } - // Load - if err := index.load(); err != nil { - return nil, err - } - src := index.Find(srcNameOrId) - if src == nil { - return nil, errors.New("No such image: " + srcNameOrId) - } - dst, err := NewImage(dstName, src.Layers, src.Id) - if err != nil { - return nil, err - } - if err := index.Add(dstName, dst); err != nil { - return nil, err - } - // Save - if err := index.save(); err != nil { - return nil, err - } - return dst, nil -} - -func (index *Index) Rename(oldName, newName string) error { - // Load - if err := index.load(); err != nil { - return err - } - if _, exists := index.ByName[oldName]; !exists { - return errors.New("Can't rename " + oldName + ": no such image.") - } - if _, exists := index.ByName[newName]; exists { - return errors.New("Can't rename to " + newName + ": name is already in use.") - } - index.ByName[newName] = index.ByName[oldName] - delete(index.ByName, oldName) - // Change the ID of all images, since they include the name - for _, image := range *index.ByName[newName] { - if id, err := generateImageId(newName, image.Layers); err != nil { - return err - } else { - oldId := image.Id - image.Id = id - index.ById[id] = image - delete(index.ById, oldId) - } - } - // Save - if err := index.save(); err != nil { - return err - } - return nil -} - -// Delete deletes all images with the name `name` -func (index *Index) Delete(name string) error { - // Load - if err := index.load(); err != nil { - return err - } - if _, exists := index.ByName[name]; !exists { - return errors.New("No such image: " + name) - } - // Remove from index lookup - for _, image := range *index.ByName[name] { - delete(index.ById, image.Id) - } - // Remove from name lookup - delete(index.ByName, name) - // Save - if err := index.save(); err != nil { - return err - } - return nil -} - -// DeleteMatch deletes all images whose name matches `pattern` -func (index *Index) DeleteMatch(pattern string) error { - // Load - if err := index.load(); err != nil { - return err - } - for name, history := range index.ByName { - if match, err := regexp.MatchString(pattern, name); err != nil { - return err - } else if match { - // Remove from index lookup - for _, image := range *history { - delete(index.ById, image.Id) - } - // Remove from name lookup - delete(index.ByName, name) - } - } - // Save - if err := index.save(); err != nil { - return err - } - return nil -} - -func (index *Index) Names() []string { - if err := index.load(); err != nil { - return []string{} - } - var names []string - for name := range index.ByName { - names = append(names, name) - } - sort.Strings(names) - return names -} - -func (index *Index) load() error { - jsonData, err := ioutil.ReadFile(index.Path) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - path := index.Path - if err := json.Unmarshal(jsonData, index); err != nil { - return err - } - index.Path = path - return nil -} - -func (index *Index) save() error { - jsonData, err := json.Marshal(index) - if err != nil { - return err - } - if err := ioutil.WriteFile(index.Path, jsonData, 0600); err != nil { - return err - } - return nil -} - -// History wraps an array of images so they can be sorted by date (most recent first) - -type History []*Image - -func (history *History) Len() int { - return len(*history) -} - -func (history *History) Less(i, j int) bool { - images := *history - return images[j].Created.Before(images[i].Created) -} - -func (history *History) Swap(i, j int) { - images := *history - tmp := images[i] - images[i] = images[j] - images[j] = tmp -} - -func (history *History) Add(image *Image) { - *history = append(*history, image) - sort.Sort(history) -} - -func (history *History) Del(id string) { - for idx, image := range *history { - if image.Id == id { - *history = append((*history)[:idx], (*history)[idx+1:]...) - } - } -} - -type Image struct { - Id string // Globally unique identifier - Layers []string // Absolute paths - Created time.Time - Parent string -} - -func (image *Image) IdParts() (string, string) { - if len(image.Id) < 8 { - return "", image.Id - } - hash := image.Id[len(image.Id)-8 : len(image.Id)] - name := image.Id[:len(image.Id)-9] - return name, hash -} - -func (image *Image) IdIsFinal() bool { - return len(image.Layers) == 1 -} - -func generateImageId(name string, layers []string) (string, error) { - if len(layers) == 0 { - return "", errors.New("No layers provided.") - } - var hash string - if len(layers) == 1 { - hash = path.Base(layers[0]) - } else { - var ids string - for _, layer := range layers { - ids += path.Base(layer) - } - if h, err := future.ComputeId(strings.NewReader(ids)); err != nil { - return "", err - } else { - hash = h - } - } - return name + ":" + hash, nil -} - -func NewImage(name string, layers []string, parent string) (*Image, error) { - id, err := generateImageId(name, layers) - if err != nil { - return nil, err - } - return &Image{ - Id: id, - Layers: layers, - Created: time.Now(), - Parent: parent, - }, nil -} diff --git a/rcli/http.go b/rcli/http.go index e6cb5657d9..cc8d3b149e 100644 --- a/rcli/http.go +++ b/rcli/http.go @@ -1,13 +1,12 @@ package rcli import ( + "fmt" "net/http" "net/url" "path" - "fmt" ) - // Use this key to encode an RPC call into an URL, // eg. domain.tld/path/to/method?q=get_user&q=gordon const ARG_URL_KEY = "q" @@ -16,18 +15,16 @@ func URLToCall(u *url.URL) (method string, args []string) { return path.Base(u.Path), u.Query()[ARG_URL_KEY] } - func ListenAndServeHTTP(addr string, service Service) error { return http.ListenAndServe(addr, http.HandlerFunc( - func (w http.ResponseWriter, r *http.Request) { + func(w http.ResponseWriter, r *http.Request) { cmd, args := URLToCall(r.URL) if err := call(service, r.Body, &AutoFlush{w}, append([]string{cmd}, args...)...); err != nil { - fmt.Fprintf(w, "Error: " + err.Error() + "\n") + fmt.Fprintf(w, "Error: "+err.Error()+"\n") } })) } - type AutoFlush struct { http.ResponseWriter } diff --git a/rcli/tcp.go b/rcli/tcp.go index 0a06d459ce..869a3bcdb6 100644 --- a/rcli/tcp.go +++ b/rcli/tcp.go @@ -1,13 +1,13 @@ package rcli import ( + "bufio" + "encoding/json" + "fmt" "io" "io/ioutil" - "net" "log" - "fmt" - "encoding/json" - "bufio" + "net" ) // Connect to a remote endpoint using protocol `proto` and address `addr`, @@ -44,7 +44,7 @@ func ListenAndServe(proto, addr string, service Service) error { go func() { if err := Serve(conn, service); err != nil { log.Printf("Error: " + err.Error() + "\n") - fmt.Fprintf(conn, "Error: " + err.Error() + "\n") + fmt.Fprintf(conn, "Error: "+err.Error()+"\n") } conn.Close() }() @@ -53,7 +53,6 @@ func ListenAndServe(proto, addr string, service Service) error { return nil } - // Parse an rcli call on a new connection, and pass it to `service` if it // is valid. func Serve(conn io.ReadWriter, service Service) error { @@ -68,4 +67,3 @@ func Serve(conn io.ReadWriter, service Service) error { } return nil } - diff --git a/rcli/types.go b/rcli/types.go index b8572cd896..52079291b6 100644 --- a/rcli/types.go +++ b/rcli/types.go @@ -8,13 +8,13 @@ package rcli // are the usual suspects. import ( + "errors" + "flag" "fmt" "io" - "reflect" - "flag" "log" + "reflect" "strings" - "errors" ) type Service interface { @@ -25,7 +25,6 @@ type Service interface { type Cmd func(io.ReadCloser, io.Writer, ...string) error type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error - func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error { if len(args) == 0 { args = []string{"help"} @@ -63,7 +62,7 @@ func getMethod(service Service, name string) Cmd { return nil } } - methodName := "Cmd"+strings.ToUpper(name[:1])+strings.ToLower(name[1:]) + methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) method, exists := reflect.TypeOf(service).MethodByName(methodName) if !exists { return nil @@ -91,4 +90,3 @@ func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet } return flags } -