This commit is contained in:
Solomon Hykes 2013-03-09 19:44:09 -08:00
parent 93ba6dd82b
commit c59fff422f
21 changed files with 273 additions and 303 deletions

View File

@ -1,8 +1,8 @@
package client package client
import ( import (
"github.com/dotcloud/docker/rcli"
"github.com/dotcloud/docker/future" "github.com/dotcloud/docker/future"
"github.com/dotcloud/docker/rcli"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -112,7 +112,7 @@ func InteractiveMode(scripts ...string) error {
return err return err
} }
io.WriteString(rcfile, "enable -n help\n") io.WriteString(rcfile, "enable -n help\n")
os.Setenv("PATH", tmp + ":" + os.Getenv("PATH")) os.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
os.Setenv("PS1", "\\h docker> ") os.Setenv("PS1", "\\h docker> ")
shell := exec.Command("/bin/bash", append([]string{"--rcfile", rcfile.Name()}, scripts...)...) shell := exec.Command("/bin/bash", append([]string{"--rcfile", rcfile.Name()}, scripts...)...)
shell.Stdin = os.Stdin shell.Stdin = os.Stdin

View File

@ -15,7 +15,6 @@ type Termios struct {
Ospeed uintptr Ospeed uintptr
} }
const ( const (
// Input flags // Input flags
inpck = 0x010 inpck = 0x010
@ -35,113 +34,110 @@ const (
) )
const ( const (
HUPCL = 0x4000 HUPCL = 0x4000
ICANON = 0x100 ICANON = 0x100
ICRNL = 0x100 ICRNL = 0x100
IEXTEN = 0x400 IEXTEN = 0x400
BRKINT = 0x2 BRKINT = 0x2
CFLUSH = 0xf CFLUSH = 0xf
CLOCAL = 0x8000 CLOCAL = 0x8000
CREAD = 0x800 CREAD = 0x800
CS5 = 0x0 CS5 = 0x0
CS6 = 0x100 CS6 = 0x100
CS7 = 0x200 CS7 = 0x200
CS8 = 0x300 CS8 = 0x300
CSIZE = 0x300 CSIZE = 0x300
CSTART = 0x11 CSTART = 0x11
CSTATUS = 0x14 CSTATUS = 0x14
CSTOP = 0x13 CSTOP = 0x13
CSTOPB = 0x400 CSTOPB = 0x400
CSUSP = 0x1a CSUSP = 0x1a
IGNBRK = 0x1 IGNBRK = 0x1
IGNCR = 0x80 IGNCR = 0x80
IGNPAR = 0x4 IGNPAR = 0x4
IMAXBEL = 0x2000 IMAXBEL = 0x2000
INLCR = 0x40 INLCR = 0x40
INPCK = 0x10 INPCK = 0x10
ISIG = 0x80 ISIG = 0x80
ISTRIP = 0x20 ISTRIP = 0x20
IUTF8 = 0x4000 IUTF8 = 0x4000
IXANY = 0x800 IXANY = 0x800
IXOFF = 0x400 IXOFF = 0x400
IXON = 0x200 IXON = 0x200
NOFLSH = 0x80000000 NOFLSH = 0x80000000
OCRNL = 0x10 OCRNL = 0x10
OFDEL = 0x20000 OFDEL = 0x20000
OFILL = 0x80 OFILL = 0x80
ONLCR = 0x2 ONLCR = 0x2
ONLRET = 0x40 ONLRET = 0x40
ONOCR = 0x20 ONOCR = 0x20
ONOEOT = 0x8 ONOEOT = 0x8
OPOST = 0x1 OPOST = 0x1
RENB = 0x1000 RENB = 0x1000
PARMRK = 0x8 PARMRK = 0x8
PARODD = 0x2000 PARODD = 0x2000
TOSTOP = 0x400000 TOSTOP = 0x400000
VDISCARD = 0xf VDISCARD = 0xf
VDSUSP = 0xb VDSUSP = 0xb
VEOF = 0x0 VEOF = 0x0
VEOL = 0x1 VEOL = 0x1
VEOL2 = 0x2 VEOL2 = 0x2
VERASE = 0x3 VERASE = 0x3
VINTR = 0x8 VINTR = 0x8
VKILL = 0x5 VKILL = 0x5
VLNEXT = 0xe VLNEXT = 0xe
VMIN = 0x10 VMIN = 0x10
VQUIT = 0x9 VQUIT = 0x9
VREPRINT = 0x6 VREPRINT = 0x6
VSTART = 0xc VSTART = 0xc
VSTATUS = 0x12 VSTATUS = 0x12
VSTOP = 0xd VSTOP = 0xd
VSUSP = 0xa VSUSP = 0xa
VT0 = 0x0 VT0 = 0x0
VT1 = 0x10000 VT1 = 0x10000
VTDLY = 0x10000 VTDLY = 0x10000
VTIME = 0x11 VTIME = 0x11
ECHO = 0x00000008 ECHO = 0x00000008
PENDIN = 0x20000000 PENDIN = 0x20000000
) )
type State struct { type State struct {
termios Termios termios Termios
} }
// IsTerminal returns true if the given file descriptor is a terminal. // IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool { func IsTerminal(fd int) bool {
var termios Termios var termios Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0) _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0 return err == 0
} }
// MakeRaw put the terminal connected to the given file descriptor into raw // MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be // mode and returns the previous state of the terminal so that it can be
// restored. // restored.
func MakeRaw(fd int) (*State, error) { func MakeRaw(fd int) (*State, error) {
var oldState State var oldState State
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
return nil, err return nil, err
} }
newState := oldState.termios newState := oldState.termios
newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
newState.Iflag |= ICRNL newState.Iflag |= ICRNL
newState.Oflag |= ONLCR newState.Oflag |= ONLCR
newState.Lflag &^= ECHO | ICANON | ISIG newState.Lflag &^= ECHO | ICANON | ISIG
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
return nil, err return nil, err
} }
return &oldState, nil return &oldState, nil
} }
// Restore restores the terminal connected to the given file descriptor to a // Restore restores the terminal connected to the given file descriptor to a
// previous state. // previous state.
func Restore(fd int, state *State) error { func Restore(fd int, state *State) error {
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
return err return err
} }

View File

@ -1,6 +1,7 @@
package docker package docker
import ( import (
"./fs"
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
@ -14,7 +15,6 @@ import (
"strings" "strings"
"syscall" "syscall"
"time" "time"
"./fs"
) )
var sysInitPath string var sysInitPath string
@ -35,7 +35,7 @@ type Container struct {
Config *Config Config *Config
Mountpoint *fs.Mountpoint Mountpoint *fs.Mountpoint
State *State State *State
Image string Image string
SysInitPath string SysInitPath string
lxcConfigPath string lxcConfigPath string
@ -69,7 +69,7 @@ func createContainer(id string, root string, command string, args []string, imag
Path: command, Path: command,
Args: args, Args: args,
Config: config, Config: config,
Image: image.Id, Image: image.Id,
Mountpoint: mountpoint, Mountpoint: mountpoint,
State: newState(), State: newState(),

View File

@ -1,6 +1,7 @@
package docker package docker
import ( import (
"./fs"
"container/list" "container/list"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -8,14 +9,13 @@ import (
"os" "os"
"path" "path"
"sort" "sort"
"./fs"
) )
type Docker struct { type Docker struct {
root string root string
repository string repository string
containers *list.List containers *list.List
Store *fs.Store Store *fs.Store
} }
func (docker *Docker) List() []*Container { func (docker *Docker) List() []*Container {
@ -117,7 +117,7 @@ func NewFromDirectory(root string) (*Docker, error) {
root: root, root: root,
repository: path.Join(root, "containers"), repository: path.Join(root, "containers"),
containers: list.New(), containers: list.New(),
Store: store, Store: store,
} }
if err := os.MkdirAll(docker.repository, 0700); err != nil && !os.IsExist(err) { if err := os.MkdirAll(docker.repository, 0700); err != nil && !os.IsExist(err) {

View File

@ -2,10 +2,10 @@ package main
import ( import (
"flag" "flag"
"github.com/dotcloud/docker/client"
"log" "log"
"os" "os"
"path" "path"
"github.com/dotcloud/docker/client"
) )
func main() { func main() {
@ -27,4 +27,3 @@ func main() {
} }
} }
} }

View File

@ -1,17 +1,17 @@
package docker package docker
import ( import (
"./fs"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"testing" "testing"
"io"
"./fs"
) )
const testLayerPath string = "/var/lib/docker/docker-ut.tar" const testLayerPath string = "/var/lib/docker/docker-ut.tar"
func layerArchive(tarfile string) (io.Reader, error) { func layerArchive(tarfile string) (io.Reader, error) {
// FIXME: need to close f somewhere // FIXME: need to close f somewhere
f, err := os.Open(tarfile) f, err := os.Open(tarfile)
if err != nil { if err != nil {
@ -57,7 +57,7 @@ func newTestDocker() (*Docker, error) {
return docker, nil return docker, nil
} }
func GetTestImage(docker *Docker) (*fs.Image) { func GetTestImage(docker *Docker) *fs.Image {
imgs, err := docker.Store.Images() imgs, err := docker.Store.Images()
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -1,9 +1,9 @@
package main package main
import ( import (
"flag"
".." ".."
"../server" "../server"
"flag"
"log" "log"
) )

View File

@ -1,20 +1,19 @@
package fake package fake
import ( import (
"bytes"
"math/rand"
"io"
"archive/tar" "archive/tar"
"os/exec" "bytes"
"github.com/kr/pty" "github.com/kr/pty"
"io"
"math/rand"
"os/exec"
) )
func FakeTar() (io.Reader, error) { func FakeTar() (io.Reader, error) {
content := []byte("Hello world!\n") content := []byte("Hello world!\n")
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
tw := tar.NewWriter(buf) tw := tar.NewWriter(buf)
for _, name := range []string {"/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 := new(tar.Header)
hdr.Size = int64(len(content)) hdr.Size = int64(len(content))
hdr.Name = name hdr.Name = name
@ -27,7 +26,6 @@ func FakeTar() (io.Reader, error) {
return buf, nil return buf, nil
} }
func WriteFakeTar(dst io.Writer) error { func WriteFakeTar(dst io.Writer) error {
if data, err := FakeTar(); err != nil { if data, err := FakeTar(); err != nil {
return err return err
@ -37,7 +35,6 @@ func WriteFakeTar(dst io.Writer) error {
return nil return nil
} }
func RandomBytesChanged() uint { func RandomBytesChanged() uint {
return uint(rand.Int31n(24 * 1024 * 1024)) return uint(rand.Int31n(24 * 1024 * 1024))
} }
@ -54,7 +51,6 @@ func ContainerRunning() bool {
return false return false
} }
func StartCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadCloser, error) { func StartCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadCloser, error) {
if interactive { if interactive {
term, err := pty.Start(cmd) term, err := pty.Start(cmd)
@ -76,5 +72,3 @@ func StartCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadClose
} }
return stdin, stdout, nil return stdin, stdout, nil
} }

View File

@ -1,129 +1,129 @@
package fs package fs
import ( import (
"fmt" "fmt"
"path/filepath" "os"
"os" "path/filepath"
"strings" "strings"
) )
type ChangeType int type ChangeType int
const ( const (
ChangeModify = iota ChangeModify = iota
ChangeAdd ChangeAdd
ChangeDelete ChangeDelete
) )
type Change struct { type Change struct {
Path string Path string
Kind ChangeType Kind ChangeType
} }
func (change *Change) String() string { func (change *Change) String() string {
var kind string var kind string
switch change.Kind { switch change.Kind {
case ChangeModify: case ChangeModify:
kind = "C" kind = "C"
case ChangeAdd: case ChangeAdd:
kind = "A" kind = "A"
case ChangeDelete: case ChangeDelete:
kind = "D" kind = "D"
} }
return fmt.Sprintf("%s %s", kind, change.Path) return fmt.Sprintf("%s %s", kind, change.Path)
} }
func (store *Store) Changes(mp *Mountpoint) ([]Change, error) { func (store *Store) Changes(mp *Mountpoint) ([]Change, error) {
var changes []Change var changes []Change
image, err := store.Get(mp.Image) image, err := store.Get(mp.Image)
if err != nil { if err != nil {
return nil, err return nil, err
} }
layers, err := image.layers() layers, err := image.layers()
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error { err = filepath.Walk(mp.Rw, func(path string, f os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
// Rebase path // Rebase path
path, err = filepath.Rel(mp.Rw, path) path, err = filepath.Rel(mp.Rw, path)
if err != nil { if err != nil {
return err return err
} }
path = filepath.Join("/", path) path = filepath.Join("/", path)
// Skip root // Skip root
if path == "/" { if path == "/" {
return nil return nil
} }
// Skip AUFS metadata // Skip AUFS metadata
if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched { if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
return err return err
} }
change := Change{ change := Change{
Path: path, Path: path,
} }
// Find out what kind of modification happened // Find out what kind of modification happened
file := filepath.Base(path) file := filepath.Base(path)
// If there is a whiteout, then the file was removed // If there is a whiteout, then the file was removed
if strings.HasPrefix(file, ".wh.") { if strings.HasPrefix(file, ".wh.") {
originalFile := strings.TrimLeft(file, ".wh.") originalFile := strings.TrimLeft(file, ".wh.")
change.Path = filepath.Join(filepath.Dir(path), originalFile) change.Path = filepath.Join(filepath.Dir(path), originalFile)
change.Kind = ChangeDelete change.Kind = ChangeDelete
} else { } else {
// Otherwise, the file was added // Otherwise, the file was added
change.Kind = ChangeAdd change.Kind = ChangeAdd
// ...Unless it already existed in a top layer, in which case, it's a modification // ...Unless it already existed in a top layer, in which case, it's a modification
for _, layer := range layers { for _, layer := range layers {
stat, err := os.Stat(filepath.Join(layer, path)) stat, err := os.Stat(filepath.Join(layer, path))
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
} }
if err == nil { if err == nil {
// The file existed in the top layer, so that's a modification // The file existed in the top layer, so that's a modification
// However, if it's a directory, maybe it wasn't actually modified. // 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 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 stat.IsDir() && f.IsDir() {
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() { if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
// Both directories are the same, don't record the change // Both directories are the same, don't record the change
return nil return nil
} }
} }
change.Kind = ChangeModify change.Kind = ChangeModify
break break
} }
} }
} }
// Record change // Record change
changes = append(changes, change) changes = append(changes, change)
return nil return nil
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return changes, nil return changes, nil
} }
// Reset removes all changes to the filesystem, reverting it to its initial state. // Reset removes all changes to the filesystem, reverting it to its initial state.
func (mp *Mountpoint) Reset() error { func (mp *Mountpoint) Reset() error {
if err := os.RemoveAll(mp.Rw); err != nil { if err := os.RemoveAll(mp.Rw); err != nil {
return err return err
} }
// We removed the RW directory itself along with its content: let's re-create an empty one. // We removed the RW directory itself along with its content: let's re-create an empty one.
if err := mp.createFolders(); err != nil { if err := mp.createFolders(); err != nil {
return err return err
} }
return nil return nil
} }
// Open opens the named file for reading. // Open opens the named file for reading.
@ -141,4 +141,4 @@ func (mp *Mountpoint) Reset() error {
// return nil, err // return nil, err
// } // }
// return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname)) // return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname))
// } // }

View File

@ -1,25 +1,25 @@
package fs package fs
import ( import (
"../future"
"errors" "errors"
"path" "fmt"
"path/filepath"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"fmt" "path"
"../future" "path/filepath"
) )
type LayerStore struct { type LayerStore struct {
Root string Root string
} }
type Compression uint32 type Compression uint32
const ( const (
Uncompressed Compression = iota Uncompressed Compression = iota
Bzip2 Bzip2
Gzip Gzip
) )
@ -80,10 +80,9 @@ func (store *LayerStore) Init() error {
return os.Mkdir(store.Root, 0700) return os.Mkdir(store.Root, 0700)
} }
func (store *LayerStore) Mktemp() (string, error) { func (store *LayerStore) Mktemp() (string, error) {
tmpName := future.RandomId() tmpName := future.RandomId()
tmpPath := path.Join(store.Root, "tmp-" + tmpName) tmpPath := path.Join(store.Root, "tmp-"+tmpName)
if err := os.Mkdir(tmpPath, 0700); err != nil { if err := os.Mkdir(tmpPath, 0700); err != nil {
return "", err return "", err
} }
@ -94,7 +93,6 @@ func (store *LayerStore) layerPath(id string) string {
return path.Join(store.Root, id) return path.Join(store.Root, id)
} }
func (store *LayerStore) AddLayer(id string, archive Archive, stderr io.Writer, compression Compression) (string, error) { func (store *LayerStore) AddLayer(id string, archive Archive, stderr io.Writer, compression Compression) (string, error) {
if _, err := os.Stat(store.layerPath(id)); err == nil { if _, err := os.Stat(store.layerPath(id)); err == nil {
return "", errors.New("Layer already exists: " + id) return "", errors.New("Layer already exists: " + id)

View File

@ -1,14 +1,12 @@
package fs package fs
import ( import (
"io/ioutil"
"testing"
"os"
"github.com/dotcloud/docker/fake" "github.com/dotcloud/docker/fake"
"io/ioutil"
"os"
"testing"
) )
func TestLayersInit(t *testing.T) { func TestLayersInit(t *testing.T) {
store := tempStore(t) store := tempStore(t)
defer os.RemoveAll(store.Root) defer os.RemoveAll(store.Root)
@ -54,7 +52,6 @@ func TestAddLayerDuplicate(t *testing.T) {
} }
} }
/* /*
* HELPER FUNCTIONS * HELPER FUNCTIONS
*/ */

View File

@ -2,7 +2,6 @@ package fs
import "syscall" import "syscall"
func mount(source string, target string, fstype string, flags uintptr, data string) (err error) { func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
return syscall.Mount(source, target, fstype, flags, data) return syscall.Mount(source, target, fstype, flags, data)
} }

View File

@ -10,9 +10,9 @@ import (
"io" "io"
"os" "os"
"path" "path"
"path/filepath"
"syscall" "syscall"
"time" "time"
"path/filepath"
) )
type Store struct { type Store struct {
@ -168,7 +168,6 @@ type Image struct {
store *Store `db:"-"` store *Store `db:"-"`
} }
func (image *Image) Copy(pth string) (*Image, error) { func (image *Image) Copy(pth string) (*Image, error) {
if err := image.store.orm.Insert(&Path{Path: pth, Image: image.Id}); err != nil { if err := image.store.orm.Insert(&Path{Path: pth, Image: image.Id}); err != nil {
return nil, err return nil, err
@ -198,7 +197,7 @@ func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
func (image *Image) layers() ([]string, error) { func (image *Image) layers() ([]string, error) {
var list []string var list []string
var err error var err error
currentImg := image currentImg := image
for currentImg != nil { for currentImg != nil {
if layer := image.store.layers.Get(image.Id); layer != "" { if layer := image.store.layers.Get(image.Id); layer != "" {

View File

@ -1,9 +1,9 @@
package fs package fs
import ( import (
"../fake"
"errors" "errors"
"fmt" "fmt"
"../fake"
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"

View File

@ -1,12 +1,12 @@
package future package future
import ( import (
"crypto/sha256"
"io"
"fmt"
"time"
"bytes" "bytes"
"crypto/sha256"
"fmt"
"io"
"math/rand" "math/rand"
"time"
) )
func Seed() { func Seed() {
@ -30,18 +30,18 @@ func HumanDuration(d time.Duration) string {
return "About a minute" return "About a minute"
} else if minutes < 60 { } else if minutes < 60 {
return fmt.Sprintf("%d minutes", minutes) return fmt.Sprintf("%d minutes", minutes)
} else if hours := int(d.Hours()); hours == 1{ } else if hours := int(d.Hours()); hours == 1 {
return "About an hour" return "About an hour"
} else if hours < 48 { } else if hours < 48 {
return fmt.Sprintf("%d hours", hours) return fmt.Sprintf("%d hours", hours)
} else if hours < 24 * 7 * 2 { } else if hours < 24*7*2 {
return fmt.Sprintf("%d days", hours / 24) return fmt.Sprintf("%d days", hours/24)
} else if hours < 24 * 30 * 3 { } else if hours < 24*30*3 {
return fmt.Sprintf("%d weeks", hours / 24 / 7) return fmt.Sprintf("%d weeks", hours/24/7)
} else if hours < 24 * 365 * 2 { } else if hours < 24*365*2 {
return fmt.Sprintf("%d months", hours / 24 / 30) return fmt.Sprintf("%d months", hours/24/30)
} }
return fmt.Sprintf("%d years", d.Hours() / 24 / 365) return fmt.Sprintf("%d years", d.Hours()/24/365)
} }
func randomBytes() io.Reader { func randomBytes() io.Reader {
@ -60,4 +60,3 @@ func Go(f func() error) chan error {
}() }()
return ch return ch
} }

View File

@ -1,27 +1,25 @@
package image package image
import ( import (
"encoding/json"
"errors"
"github.com/dotcloud/docker/future"
"io" "io"
"io/ioutil" "io/ioutil"
"encoding/json" "os"
"time"
"path" "path"
"path/filepath" "path/filepath"
"errors"
"sort" "sort"
"os"
"github.com/dotcloud/docker/future"
"strings" "strings"
"time"
) )
type Store struct { type Store struct {
*Index *Index
Root string Root string
Layers *LayerStore Layers *LayerStore
} }
func New(root string) (*Store, error) { func New(root string) (*Store, error) {
abspath, err := filepath.Abs(root) abspath, err := filepath.Abs(root)
if err != nil { if err != nil {
@ -38,8 +36,8 @@ func New(root string) (*Store, error) {
return nil, err return nil, err
} }
return &Store{ return &Store{
Root: abspath, Root: abspath,
Index: NewIndex(path.Join(root, "index.json")), Index: NewIndex(path.Join(root, "index.json")),
Layers: layers, Layers: layers,
}, nil }, nil
} }
@ -47,7 +45,7 @@ func New(root string) (*Store, error) {
type Compression uint32 type Compression uint32
const ( const (
Uncompressed Compression = iota Uncompressed Compression = iota
Bzip2 Bzip2
Gzip Gzip
) )
@ -79,20 +77,19 @@ func (store *Store) Create(name string, source string, layers ...string) (*Image
return image, nil return image, nil
} }
// Index // Index
type Index struct { type Index struct {
Path string Path string
ByName map[string]*History ByName map[string]*History
ById map[string]*Image ById map[string]*Image
} }
func NewIndex(path string) *Index { func NewIndex(path string) *Index {
return &Index{ return &Index{
Path: path, Path: path,
ByName: make(map[string]*History), ByName: make(map[string]*History),
ById: make(map[string]*Image), ById: make(map[string]*Image),
} }
} }
@ -222,7 +219,7 @@ func (index *Index) Names() []string {
if err := index.load(); err != nil { if err := index.load(); err != nil {
return []string{} return []string{}
} }
var names[]string var names []string
for name := range index.ByName { for name := range index.ByName {
names = append(names, name) names = append(names, name)
} }
@ -285,23 +282,23 @@ func (history *History) Add(image *Image) {
func (history *History) Del(id string) { func (history *History) Del(id string) {
for idx, image := range *history { for idx, image := range *history {
if image.Id == id { if image.Id == id {
*history = append((*history)[:idx], (*history)[idx + 1:]...) *history = append((*history)[:idx], (*history)[idx+1:]...)
} }
} }
} }
type Image struct { type Image struct {
Id string // Globally unique identifier Id string // Globally unique identifier
Layers []string // Absolute paths Layers []string // Absolute paths
Created time.Time Created time.Time
Parent string Parent string
} }
func (image *Image) IdParts() (string, string) { func (image *Image) IdParts() (string, string) {
if len(image.Id) < 8 { if len(image.Id) < 8 {
return "", image.Id return "", image.Id
} }
hash := image.Id[len(image.Id)-8:len(image.Id)] hash := image.Id[len(image.Id)-8 : len(image.Id)]
name := image.Id[:len(image.Id)-9] name := image.Id[:len(image.Id)-9]
return name, hash return name, hash
} }
@ -322,7 +319,7 @@ func generateImageId(name string, layers []string) (string, error) {
for _, layer := range layers { for _, layer := range layers {
ids += path.Base(layer) ids += path.Base(layer)
} }
if h, err := future.ComputeId(strings.NewReader(ids)); err != nil { if h, err := future.ComputeId(strings.NewReader(ids)); err != nil {
return "", err return "", err
} else { } else {
hash = h hash = h
@ -337,9 +334,9 @@ func NewImage(name string, layers []string, parent string) (*Image, error) {
return nil, err return nil, err
} }
return &Image{ return &Image{
Id: id, Id: id,
Layers: layers, Layers: layers,
Created: time.Now(), Created: time.Now(),
Parent: parent, Parent: parent,
}, nil }, nil
} }

View File

@ -2,7 +2,6 @@ package docker
import "syscall" import "syscall"
func mount(source string, target string, fstype string, flags uintptr, data string) (err error) { func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
return syscall.Mount(source, target, fstype, flags, data) return syscall.Mount(source, target, fstype, flags, data)
} }

View File

@ -1,13 +1,12 @@
package rcli package rcli
import ( import (
"fmt"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"fmt"
) )
// Use this key to encode an RPC call into an URL, // Use this key to encode an RPC call into an URL,
// eg. domain.tld/path/to/method?q=get_user&q=gordon // eg. domain.tld/path/to/method?q=get_user&q=gordon
const ARG_URL_KEY = "q" 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] return path.Base(u.Path), u.Query()[ARG_URL_KEY]
} }
func ListenAndServeHTTP(addr string, service Service) error { func ListenAndServeHTTP(addr string, service Service) error {
return http.ListenAndServe(addr, http.HandlerFunc( 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) cmd, args := URLToCall(r.URL)
if err := call(service, r.Body, &AutoFlush{w}, append([]string{cmd}, args...)...); err != nil { 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 { type AutoFlush struct {
http.ResponseWriter http.ResponseWriter
} }

View File

@ -1,13 +1,13 @@
package rcli package rcli
import ( import (
"bufio"
"encoding/json"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"log" "log"
"fmt" "net"
"encoding/json"
"bufio"
) )
// Connect to a remote endpoint using protocol `proto` and address `addr`, // 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() { go func() {
if err := Serve(conn, service); err != nil { if err := Serve(conn, service); err != nil {
log.Printf("Error: " + err.Error() + "\n") log.Printf("Error: " + err.Error() + "\n")
fmt.Fprintf(conn, "Error: " + err.Error() + "\n") fmt.Fprintf(conn, "Error: "+err.Error()+"\n")
} }
conn.Close() conn.Close()
}() }()
@ -53,7 +53,6 @@ func ListenAndServe(proto, addr string, service Service) error {
return nil return nil
} }
// Parse an rcli call on a new connection, and pass it to `service` if it // Parse an rcli call on a new connection, and pass it to `service` if it
// is valid. // is valid.
func Serve(conn io.ReadWriter, service Service) error { func Serve(conn io.ReadWriter, service Service) error {
@ -68,4 +67,3 @@ func Serve(conn io.ReadWriter, service Service) error {
} }
return nil return nil
} }

View File

@ -8,13 +8,13 @@ package rcli
// are the usual suspects. // are the usual suspects.
import ( import (
"errors"
"flag"
"fmt" "fmt"
"io" "io"
"reflect"
"flag"
"log" "log"
"reflect"
"strings" "strings"
"errors"
) )
type Service interface { type Service interface {
@ -25,7 +25,6 @@ type Service interface {
type Cmd func(io.ReadCloser, io.Writer, ...string) error type Cmd func(io.ReadCloser, io.Writer, ...string) error
type CmdMethod func(Service, 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 { func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
if len(args) == 0 { if len(args) == 0 {
args = []string{"help"} args = []string{"help"}
@ -63,7 +62,7 @@ func getMethod(service Service, name string) Cmd {
return nil 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) method, exists := reflect.TypeOf(service).MethodByName(methodName)
if !exists { if !exists {
return nil return nil
@ -91,4 +90,3 @@ func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet
} }
return flags return flags
} }

View File

@ -1,15 +1,15 @@
package server package server
import ( import (
".."
"../fs"
"../future"
"../rcli"
"bufio" "bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
".."
"../future"
"../fs"
"../rcli"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@ -269,8 +269,8 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str
var obj interface{} var obj interface{}
if container := srv.containers.Get(name); container != nil { if container := srv.containers.Get(name); container != nil {
obj = container obj = container
//} else if image, err := srv.images.List(name); image != nil { //} else if image, err := srv.images.List(name); image != nil {
// obj = image // obj = image
} else { } else {
return errors.New("No such container or image: " + name) return errors.New("No such container or image: " + name)
} }