1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge branch 'master' into remove_bsdtar

Conflicts:
	docs/sources/contributing/devenvironment.rst
This commit is contained in:
Guillaume J. Charmes 2013-06-14 14:34:30 -07:00
commit abf85b2508
18 changed files with 225 additions and 32 deletions

View file

@ -7,10 +7,12 @@ type APIHistory struct {
} }
type APIImages struct { type APIImages struct {
Repository string `json:",omitempty"` Repository string `json:",omitempty"`
Tag string `json:",omitempty"` Tag string `json:",omitempty"`
ID string `json:"Id"` ID string `json:"Id"`
Created int64 Created int64
Size int64
VirtualSize int64
} }
type APIInfo struct { type APIInfo struct {
@ -29,12 +31,14 @@ type APIRmi struct {
} }
type APIContainers struct { type APIContainers struct {
ID string `json:"Id"` ID string `json:"Id"`
Image string Image string
Command string Command string
Created int64 Created int64
Status string Status string
Ports string Ports string
SizeRw int64
SizeRootFs int64
} }
type APISearch struct { type APISearch struct {

View file

@ -20,6 +20,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@ -727,6 +728,15 @@ func (cli *DockerCli) CmdPush(args ...string) error {
if err != nil { if err != nil {
return err return err
} }
nameParts := strings.SplitN(name, "/", 2)
validNamespace := regexp.MustCompile(`^([a-z0-9_]{4,30})$`)
if !validNamespace.MatchString(nameParts[0]) {
return fmt.Errorf("Invalid namespace name (%s), only [a-z0-9_] are allowed, size between 4 and 30", nameParts[0])
}
validRepo := regexp.MustCompile(`^([a-zA-Z0-9-_.]+)$`)
if !validRepo.MatchString(nameParts[1]) {
return fmt.Errorf("Invalid repository name (%s), only [a-zA-Z0-9-_.] are allowed", nameParts[1])
}
v := url.Values{} v := url.Values{}
v.Set("registry", *registry) v.Set("registry", *registry)
@ -811,7 +821,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
if !*quiet { if !*quiet {
fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED") fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE")
} }
for _, out := range outs { for _, out := range outs {
@ -829,7 +839,12 @@ func (cli *DockerCli) CmdImages(args ...string) error {
} else { } else {
fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID)) fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID))
} }
fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
if out.VirtualSize > 0 {
fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize))
} else {
fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size))
}
} else { } else {
if *noTrunc { if *noTrunc {
fmt.Fprintln(w, out.ID) fmt.Fprintln(w, out.ID)
@ -888,15 +903,20 @@ func (cli *DockerCli) CmdPs(args ...string) error {
} }
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
if !*quiet { if !*quiet {
fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS") fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tSIZE")
} }
for _, out := range outs { for _, out := range outs {
if !*quiet { if !*quiet {
if *noTrunc { if *noTrunc {
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
} else { } else {
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
}
if out.SizeRootFs > 0 {
fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
} else {
fmt.Fprintf(w, "%s\n", utils.HumanSize(out.SizeRw))
} }
} else { } else {
if *noTrunc { if *noTrunc {

View file

@ -13,6 +13,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -919,3 +920,26 @@ func validateID(id string) error {
} }
return nil return nil
} }
// GetSize, return real size, virtual size
func (container *Container) GetSize() (int64, int64) {
var sizeRw, sizeRootfs int64
filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
if fileInfo != nil {
sizeRw += fileInfo.Size()
}
return nil
})
_, err := os.Stat(container.RootfsPath())
if err == nil {
filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
if fileInfo != nil {
sizeRootfs += fileInfo.Size()
}
return nil
})
}
return sizeRw, sizeRootfs
}

View file

@ -47,28 +47,40 @@ List containers
"Image": "base:latest", "Image": "base:latest",
"Command": "echo 1", "Command": "echo 1",
"Created": 1367854155, "Created": 1367854155,
"Status": "Exit 0" "Status": "Exit 0",
"Ports":"",
"SizeRw":12288,
"SizeRootFs":0
}, },
{ {
"Id": "9cd87474be90", "Id": "9cd87474be90",
"Image": "base:latest", "Image": "base:latest",
"Command": "echo 222222", "Command": "echo 222222",
"Created": 1367854155, "Created": 1367854155,
"Status": "Exit 0" "Status": "Exit 0",
"Ports":"",
"SizeRw":12288,
"SizeRootFs":0
}, },
{ {
"Id": "3176a2479c92", "Id": "3176a2479c92",
"Image": "base:latest", "Image": "base:latest",
"Command": "echo 3333333333333333", "Command": "echo 3333333333333333",
"Created": 1367854154, "Created": 1367854154,
"Status": "Exit 0" "Status": "Exit 0",
"Ports":"",
"SizeRw":12288,
"SizeRootFs":0
}, },
{ {
"Id": "4cb07b47f9fb", "Id": "4cb07b47f9fb",
"Image": "base:latest", "Image": "base:latest",
"Command": "echo 444444444444444444444444444444444", "Command": "echo 444444444444444444444444444444444",
"Created": 1367854152, "Created": 1367854152,
"Status": "Exit 0" "Status": "Exit 0",
"Ports":"",
"SizeRw":12288,
"SizeRootFs":0
} }
] ]
@ -488,13 +500,17 @@ List Images
"Repository":"base", "Repository":"base",
"Tag":"ubuntu-12.10", "Tag":"ubuntu-12.10",
"Id":"b750fe79269d", "Id":"b750fe79269d",
"Created":1364102658 "Created":1364102658,
"Size":24653,
"VirtualSize":180116135
}, },
{ {
"Repository":"base", "Repository":"base",
"Tag":"ubuntu-quantal", "Tag":"ubuntu-quantal",
"Id":"b750fe79269d", "Id":"b750fe79269d",
"Created":1364102658 "Created":1364102658,
"Size":24653,
"VirtualSize":180116135
} }
] ]
@ -643,7 +659,8 @@ Inspect an image
"Image":"base", "Image":"base",
"Volumes":null, "Volumes":null,
"VolumesFrom":"" "VolumesFrom":""
} },
"Size": 6824592
} }
:statuscode 200: no error :statuscode 200: no error

View file

@ -8,6 +8,33 @@
:: ::
Usage: docker import [OPTIONS] URL|- [REPOSITORY [TAG]] Usage: docker import URL|- [REPOSITORY [TAG]]
Create a new filesystem image from the contents of a tarball Create a new filesystem image from the contents of a tarball
At this time, the URL must start with ``http`` and point to a single file archive (.tar, .tar.gz, .bzip)
containing a root filesystem. If you would like to import from a local directory or archive,
you can use the ``-`` parameter to take the data from standard in.
Examples
--------
Import from a remote location
.............................
``$ docker import http://example.com/exampleimage.tgz exampleimagerepo``
Import from a local file
........................
Import to docker via pipe and standard in
``$ cat exampleimage.tgz | docker import - exampleimagelocal``
Import from a local directory
.............................
``$ sudo tar -c . | docker import - exampleimagedir``
Note the ``sudo`` in this example -- you must preserve the ownership of the files (especially root ownership)
during the archiving with tar. If you are not root (or sudo) when you tar, then the ownerships might not get preserved.

View file

@ -33,7 +33,7 @@ Installation
sudo apt-get install python-software-properties sudo apt-get install python-software-properties
sudo add-apt-repository ppa:gophers/go sudo add-apt-repository ppa:gophers/go
sudo apt-get update sudo apt-get update
sudo apt-get -y install lxc xz-utils curl golang-stable git sudo apt-get -y install lxc xz-utils curl golang-stable git aufs-tools
export GOPATH=~/go/ export GOPATH=~/go/
export PATH=$GOPATH/bin:$PATH export PATH=$GOPATH/bin:$PATH

View file

@ -90,6 +90,15 @@ func (graph *Graph) Get(name string) (*Image, error) {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID) return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
} }
img.graph = graph img.graph = graph
if img.Size == 0 {
root, err := img.root()
if err != nil {
return nil, err
}
if err := StoreSize(img, root); err != nil {
return nil, err
}
}
graph.lockSumMap.Lock() graph.lockSumMap.Lock()
defer graph.lockSumMap.Unlock() defer graph.lockSumMap.Unlock()
if _, exists := graph.checksumLock[img.ID]; !exists { if _, exists := graph.checksumLock[img.ID]; !exists {

2
hack/Vagrantfile vendored
View file

@ -22,7 +22,7 @@ Vagrant::Config.run do |config|
pkg_cmd = "touch #{DOCKER_PATH}; " pkg_cmd = "touch #{DOCKER_PATH}; "
# Install docker dependencies # Install docker dependencies
pkg_cmd << "export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; " \ pkg_cmd << "export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; " \
"apt-get install -q -y lxc bsdtar git golang make linux-image-extra-3.8.0-19-generic; " \ "apt-get install -q -y lxc bsdtar git aufs-tools golang make linux-image-extra-3.8.0-19-generic; " \
"chown -R #{USER}.#{USER} #{GOPATH}; " \ "chown -R #{USER}.#{USER} #{GOPATH}; " \
"install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile" "install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile"
config.vm.provision :shell, :inline => pkg_cmd config.vm.provision :shell, :inline => pkg_cmd

View file

@ -13,6 +13,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"strings" "strings"
"time" "time"
) )
@ -29,6 +30,7 @@ type Image struct {
Config *Config `json:"config,omitempty"` Config *Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"` Architecture string `json:"architecture,omitempty"`
graph *Graph graph *Graph
Size int64
} }
func LoadImage(root string) (*Image, error) { func LoadImage(root string) (*Image, error) {
@ -94,6 +96,18 @@ func StoreImage(img *Image, layerData Archive, root string, store bool) error {
if err := Untar(layerData, layer); err != nil { if err := Untar(layerData, layer); err != nil {
return err return err
} }
return StoreSize(img, root)
}
func StoreSize(img *Image, root string) error {
layer := layerPath(root)
filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
img.Size += fileInfo.Size()
return nil
})
// Store the json ball // Store the json ball
jsonData, err := json.Marshal(img) jsonData, err := json.Marshal(img)
if err != nil { if err != nil {
@ -363,6 +377,15 @@ func (img *Image) Checksum() (string, error) {
return hash, nil return hash, nil
} }
func (img *Image) getParentsSize(size int64) int64 {
parentImage, err := img.GetParent()
if err != nil || parentImage == nil {
return size
}
size += parentImage.Size
return parentImage.getParentsSize(size)
}
// Build an Image object from raw json data // Build an Image object from raw json data
func NewImgJSON(src []byte) (*Image, error) { func NewImgJSON(src []byte) (*Image, error) {
ret := &Image{} ret := &Image{}

View file

@ -2,13 +2,18 @@ package docker
import ( import (
"fmt" "fmt"
"github.com/dotcloud/docker/utils"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"syscall" "syscall"
"time" "time"
) )
func Unmount(target string) error { func Unmount(target string) error {
if err := exec.Command("auplink", target, "flush").Run(); err != nil {
utils.Debugf("[warning]: couldn't run auplink before unmount: %s", err)
}
if err := syscall.Unmount(target, 0); err != nil { if err := syscall.Unmount(target, 0); err != nil {
return err return err
} }

View file

@ -10,7 +10,7 @@ Homepage: http://github.com/dotcloud/docker
Package: lxc-docker Package: lxc-docker
Architecture: linux-any Architecture: linux-any
Depends: ${shlibs:Depends}, ${misc:Depends}, lxc, bsdtar Depends: ${shlibs:Depends}, ${misc:Depends}, lxc, bsdtar, aufs-tools
Description: Linux container runtime Description: Linux container runtime
Docker complements LXC with a high-level API which operates at the process Docker complements LXC with a high-level API which operates at the process
level. It runs unix processes with strong guarantees of isolation and level. It runs unix processes with strong guarantees of isolation and

View file

@ -8,7 +8,7 @@ Homepage: http://github.com/dotcloud/docker
Package: lxc-docker Package: lxc-docker
Architecture: linux-any Architecture: linux-any
Depends: ${misc:Depends},${shlibs:Depends},lxc,bsdtar Depends: ${misc:Depends},${shlibs:Depends},lxc,bsdtar,aufs-tools
Conflicts: docker Conflicts: docker
Description: lxc-docker is a Linux container runtime Description: lxc-docker is a Linux container runtime
Docker complements LXC with a high-level API which operates at the process Docker complements LXC with a high-level API which operates at the process

View file

@ -328,7 +328,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token
return nil return nil
} }
func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) { func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) {
imgListJSON, err := json.Marshal(imgList) imgListJSON, err := json.Marshal(imgList)
if err != nil { if err != nil {
return nil, err return nil, err
@ -347,6 +347,9 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat
req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
req.ContentLength = int64(len(imgListJSON)) req.ContentLength = int64(len(imgListJSON))
req.Header.Set("X-Docker-Token", "true") req.Header.Set("X-Docker-Token", "true")
if validate {
req.Header["X-Docker-Endpoints"] = regs
}
res, err := r.client.Do(req) res, err := r.client.Do(req)
if err != nil { if err != nil {
@ -364,7 +367,9 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat
req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
req.ContentLength = int64(len(imgListJSON)) req.ContentLength = int64(len(imgListJSON))
req.Header.Set("X-Docker-Token", "true") req.Header.Set("X-Docker-Token", "true")
if validate {
req.Header["X-Docker-Endpoints"] = regs
}
res, err = r.client.Do(req) res, err = r.client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -174,6 +174,8 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
out.Tag = tag out.Tag = tag
out.ID = image.ID out.ID = image.ID
out.Created = image.Created.Unix() out.Created = image.Created.Unix()
out.Size = image.Size
out.VirtualSize = image.getParentsSize(0) + image.Size
outs = append(outs, out) outs = append(outs, out)
} }
} }
@ -183,6 +185,8 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
var out APIImages var out APIImages
out.ID = image.ID out.ID = image.ID
out.Created = image.Created.Unix() out.Created = image.Created.Unix()
out.Size = image.Size
out.VirtualSize = image.getParentsSize(0) + image.Size
outs = append(outs, out) outs = append(outs, out)
} }
} }
@ -268,6 +272,8 @@ func (srv *Server) Containers(all bool, n int, since, before string) []APIContai
c.Created = container.Created.Unix() c.Created = container.Created.Unix()
c.Status = container.State.String() c.Status = container.State.String()
c.Ports = container.NetworkSettings.PortMappingHuman() c.Ports = container.NetworkSettings.PortMappingHuman()
c.SizeRw, c.SizeRootFs = container.GetSize()
retContainers = append(retContainers, c) retContainers = append(retContainers, c)
} }
return retContainers return retContainers
@ -497,7 +503,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/"))) srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
} }
repoData, err := r.PushImageJSONIndex(srvName, imgList, false) repoData, err := r.PushImageJSONIndex(srvName, imgList, false, nil)
if err != nil { if err != nil {
return err return err
} }
@ -521,7 +527,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
} }
} }
if _, err := r.PushImageJSONIndex(srvName, imgList, true); err != nil { if _, err := r.PushImageJSONIndex(srvName, imgList, true, repoData.Endpoints); err != nil {
return err return err
} }
return nil return nil
@ -652,6 +658,10 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
func (srv *Server) ContainerCreate(config *Config) (string, error) { func (srv *Server) ContainerCreate(config *Config) (string, error) {
if config.Memory != 0 && config.Memory < 524288 {
return "", fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)")
}
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
config.Memory = 0 config.Memory = 0
} }

View file

@ -147,3 +147,25 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
} }
} }
func TestRunWithTooLowMemoryLimit(t *testing.T) {
runtime, err := newTestRuntime()
srv := &Server{runtime: runtime}
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
// Try to create a container with a memory limit of 1 byte less than the minimum allowed limit.
_, err = srv.ContainerCreate(
&Config{
Image: GetTestImage(runtime).ID,
Memory: 524287,
CpuShares: 1000,
Cmd: []string{"/bin/cat"},
},
)
if err == nil {
t.Errorf("Memory limit is smaller than the allowed limit. Container creation should've failed!")
}
}

2
testing/Vagrantfile vendored
View file

@ -30,7 +30,7 @@ Vagrant::Config.run do |config|
# Install docker dependencies # Install docker dependencies
pkg_cmd << "apt-get install -q -y python-software-properties; " \ pkg_cmd << "apt-get install -q -y python-software-properties; " \
"add-apt-repository -y ppa:gophers/go/ubuntu; apt-get update -qq; " \ "add-apt-repository -y ppa:gophers/go/ubuntu; apt-get update -qq; " \
"DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc bsdtar git golang-stable make; " "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc bsdtar git golang-stable aufs-tools make; "
# Activate new kernel # Activate new kernel
pkg_cmd << "shutdown -r +1; " pkg_cmd << "shutdown -r +1; "
config.vm.provision :shell, :inline => pkg_cmd config.vm.provision :shell, :inline => pkg_cmd

View file

@ -135,6 +135,20 @@ func HumanDuration(d time.Duration) string {
return fmt.Sprintf("%d years", d.Hours()/24/365) return fmt.Sprintf("%d years", d.Hours()/24/365)
} }
// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func HumanSize(size int64) string {
i := 0
var sizef float64
sizef = float64(size)
units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
for sizef >= 1000.0 {
sizef = sizef / 1000.0
i++
}
return fmt.Sprintf("%.4g %s", sizef, units[i])
}
func Trunc(s string, maxlen int) string { func Trunc(s string, maxlen int) string {
if len(s) <= maxlen { if len(s) <= maxlen {
return s return s

View file

@ -261,3 +261,16 @@ func TestCompareKernelVersion(t *testing.T) {
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"}, &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"},
-1) -1)
} }
func TestHumanSize(t *testing.T) {
size1000 := HumanSize(1000)
if size1000 != "1 kB" {
t.Errorf("1000 -> expected 1 kB, got %s", size1000)
}
size1024 := HumanSize(1024)
if size1024 != "1.024 kB" {
t.Errorf("1024 -> expected 1.024 kB, got %s", size1024)
}
}