diff --git a/FIXME b/FIXME index e252fb2589..aaf4c23266 100644 --- a/FIXME +++ b/FIXME @@ -16,3 +16,15 @@ to put them - so we put them here :) * Unify build commands and regular commands * Move source code into src/ subdir for clarity * Clean up the Makefile, it's a mess +- docker buidl: show short IDs +- docker build: on non-existent local path for ADD, don't show full absolute path on the host +- mount into /dockerinit rather than /sbin/init +- docker tag foo REPO:TAG +- use size header for progress bar in pull +- Clean up context upload in build!!! +- Parallel pull +- Ensure /proc/sys/net/ipv4/ip_forward is 1 +- Force DNS to public! +- Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet +- Save metadata with import/export +- Upgrade dockerd without stopping containers diff --git a/Vagrantfile b/Vagrantfile index 3d568266af..5b3a1f476d 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -5,11 +5,13 @@ BOX_NAME = ENV['BOX_NAME'] || "ubuntu" BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box" AWS_REGION = ENV['AWS_REGION'] || "us-east-1" AWS_AMI = ENV['AWS_AMI'] || "ami-d0f89fb9" +FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS'] Vagrant::Config.run do |config| # Setup virtual machine box. This VM configuration code is always executed. config.vm.box = BOX_NAME config.vm.box_url = BOX_URI + config.vm.forward_port 4243, 4243 # Provision docker and new kernel if deployment was not done if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty? @@ -70,3 +72,17 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config| config.vm.box_url = BOX_URI end end + +if !FORWARD_DOCKER_PORTS.nil? + Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config| + (49000..49900).each do |port| + config.vm.forward_port port, port + end + end + + Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config| + (49000..49900).each do |port| + config.vm.network :forwarded_port, :host => port, :guest => port + end + end +end diff --git a/api.go b/api.go index ead35c8b3d..1f40aa7efc 100644 --- a/api.go +++ b/api.go @@ -438,17 +438,23 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { config := &Config{} + out := &APIRun{} + if err := json.NewDecoder(r.Body).Decode(config); err != nil { return err } + + if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns() { + out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns)) + config.Dns = defaultDns + } + id, err := srv.ContainerCreate(config) if err != nil { return err } + out.ID = id - out := &APIRun{ - ID: id, - } if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.") out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") @@ -457,6 +463,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.") out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.") } + b, err := json.Marshal(out) if err != nil { return err diff --git a/api_params.go b/api_params.go index 33b915cea5..2a641f8ccd 100644 --- a/api_params.go +++ b/api_params.go @@ -7,10 +7,12 @@ type APIHistory struct { } type APIImages struct { - Repository string `json:",omitempty"` - Tag string `json:",omitempty"` - ID string `json:"Id"` - Created int64 + Repository string `json:",omitempty"` + Tag string `json:",omitempty"` + ID string `json:"Id"` + Created int64 + Size int64 + VirtualSize int64 } type APIInfo struct { @@ -29,12 +31,14 @@ type APIRmi struct { } type APIContainers struct { - ID string `json:"Id"` - Image string - Command string - Created int64 - Status string - Ports string + ID string `json:"Id"` + Image string + Command string + Created int64 + Status string + Ports string + SizeRw int64 + SizeRootFs int64 } type APISearch struct { diff --git a/builder.go b/builder.go index 808b7efcab..02c51fb557 100644 --- a/builder.go +++ b/builder.go @@ -2,11 +2,14 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/utils" "os" "path" "time" ) +var defaultDns = []string{"8.8.8.8", "8.8.4.4"} + type Builder struct { runtime *Runtime repositories *TagStore @@ -66,15 +69,26 @@ func (builder *Builder) Create(config *Config) (*Container, error) { return nil, err } + if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns() { + //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns + builder.runtime.Dns = defaultDns + } + // If custom dns exists, then create a resolv.conf for the container - if len(config.Dns) > 0 { + if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 { + var dns []string + if len(config.Dns) > 0 { + dns = config.Dns + } else { + dns = builder.runtime.Dns + } container.ResolvConfPath = path.Join(container.root, "resolv.conf") f, err := os.Create(container.ResolvConfPath) if err != nil { return nil, err } defer f.Close() - for _, dns := range config.Dns { + for _, dns := range dns { if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil { return nil, err } diff --git a/commands.go b/commands.go index 1481290820..cdbf3b1538 100644 --- a/commands.go +++ b/commands.go @@ -20,6 +20,7 @@ import ( "path" "path/filepath" "reflect" + "regexp" "strconv" "strings" "syscall" @@ -736,6 +737,15 @@ func (cli *DockerCli) CmdPush(args ...string) error { if err != nil { 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.Set("registry", *registry) @@ -820,7 +830,7 @@ func (cli *DockerCli) CmdImages(args ...string) error { w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED") + fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE") } for _, out := range outs { @@ -838,7 +848,12 @@ func (cli *DockerCli) CmdImages(args ...string) error { } else { 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 { if *noTrunc { fmt.Fprintln(w, out.ID) @@ -897,15 +912,20 @@ func (cli *DockerCli) CmdPs(args ...string) error { } w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) 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 { if !*quiet { 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 { - 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 { if *noTrunc { diff --git a/container.go b/container.go index 4d2032af2e..f60de21bdc 100644 --- a/container.go +++ b/container.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "sort" "strconv" "strings" @@ -919,3 +920,26 @@ func validateID(id string) error { } 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 +} diff --git a/docker/docker.go b/docker/docker.go index 74236613a7..2e23999ad8 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -34,6 +34,7 @@ func main() { pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to") flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") + flDns := flag.String("dns", "", "Set custom dns servers") flag.Parse() if *bridgeName != "" { docker.NetworkBridgeIface = *bridgeName @@ -66,7 +67,7 @@ func main() { flag.Usage() return } - if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors); err != nil { + if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors, *flDns); err != nil { log.Fatal(err) os.Exit(-1) } @@ -105,7 +106,7 @@ func removePidFile(pidfile string) { } } -func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error { +func daemon(pidfile, addr string, port int, autoRestart, enableCors bool, flDns string) error { if addr != "127.0.0.1" { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } @@ -122,8 +123,11 @@ func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error removePidFile(pidfile) os.Exit(0) }() - - server, err := docker.NewServer(autoRestart, enableCors) + var dns []string + if flDns != "" { + dns = []string{flDns} + } + server, err := docker.NewServer(autoRestart, enableCors, dns) if err != nil { return err } diff --git a/docs/sources/api/docker_remote_api_v1.2.rst b/docs/sources/api/docker_remote_api_v1.2.rst index 8354760e2f..fb69168120 100644 --- a/docs/sources/api/docker_remote_api_v1.2.rst +++ b/docs/sources/api/docker_remote_api_v1.2.rst @@ -47,28 +47,40 @@ List containers "Image": "base:latest", "Command": "echo 1", "Created": 1367854155, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 }, { "Id": "9cd87474be90", "Image": "base:latest", "Command": "echo 222222", "Created": 1367854155, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 }, { "Id": "3176a2479c92", "Image": "base:latest", "Command": "echo 3333333333333333", "Created": 1367854154, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 }, { "Id": "4cb07b47f9fb", "Image": "base:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 } ] @@ -488,13 +500,17 @@ List Images "Repository":"base", "Tag":"ubuntu-12.10", "Id":"b750fe79269d", - "Created":1364102658 + "Created":1364102658, + "Size":24653, + "VirtualSize":180116135 }, { "Repository":"base", "Tag":"ubuntu-quantal", "Id":"b750fe79269d", - "Created":1364102658 + "Created":1364102658, + "Size":24653, + "VirtualSize":180116135 } ] @@ -643,7 +659,8 @@ Inspect an image "Image":"base", "Volumes":null, "VolumesFrom":"" - } + }, + "Size": 6824592 } :statuscode 200: no error diff --git a/docs/sources/commandline/command/import.rst b/docs/sources/commandline/command/import.rst index 34a7138e07..66bcf5de52 100644 --- a/docs/sources/commandline/command/import.rst +++ b/docs/sources/commandline/command/import.rst @@ -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 + +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. diff --git a/docs/sources/contributing/devenvironment.rst b/docs/sources/contributing/devenvironment.rst index 5d937c5a44..ce2ecd41e8 100644 --- a/docs/sources/contributing/devenvironment.rst +++ b/docs/sources/contributing/devenvironment.rst @@ -33,7 +33,7 @@ Installation sudo apt-get install python-software-properties sudo add-apt-repository ppa:gophers/go sudo apt-get update - sudo apt-get -y install lxc wget bsdtar curl golang-stable git + sudo apt-get -y install lxc wget bsdtar curl golang-stable git aufs-tools export GOPATH=~/go/ export PATH=$GOPATH/bin:$PATH diff --git a/graph.go b/graph.go index daa21f3053..f2b2ccec8e 100644 --- a/graph.go +++ b/graph.go @@ -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) } 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() defer graph.lockSumMap.Unlock() if _, exists := graph.checksumLock[img.ID]; !exists { diff --git a/hack/Vagrantfile b/hack/Vagrantfile index 250731ef47..318f835f4f 100644 --- a/hack/Vagrantfile +++ b/hack/Vagrantfile @@ -22,7 +22,7 @@ Vagrant::Config.run do |config| pkg_cmd = "touch #{DOCKER_PATH}; " # Install docker dependencies 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}; " \ "install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile" config.vm.provision :shell, :inline => pkg_cmd diff --git a/image.go b/image.go index 4bd8f2df31..cd76b8c432 100644 --- a/image.go +++ b/image.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "strings" "time" ) @@ -29,6 +30,7 @@ type Image struct { Config *Config `json:"config,omitempty"` Architecture string `json:"architecture,omitempty"` graph *Graph + Size int64 } 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 { 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 jsonData, err := json.Marshal(img) if err != nil { @@ -363,6 +377,15 @@ func (img *Image) Checksum() (string, error) { 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 func NewImgJSON(src []byte) (*Image, error) { ret := &Image{} diff --git a/mount.go b/mount.go index bb1a40eddb..541c29c13a 100644 --- a/mount.go +++ b/mount.go @@ -2,13 +2,18 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/utils" "os" + "os/exec" "path/filepath" "syscall" "time" ) 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 { return err } diff --git a/packaging/debian/control b/packaging/debian/control index 35a2a28cd1..62a963f59e 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -10,7 +10,7 @@ Homepage: http://github.com/dotcloud/docker Package: lxc-docker Architecture: linux-any -Depends: ${shlibs:Depends}, ${misc:Depends}, lxc, bsdtar +Depends: ${shlibs:Depends}, ${misc:Depends}, lxc, bsdtar, aufs-tools Description: Linux container runtime Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and diff --git a/packaging/ubuntu/control b/packaging/ubuntu/control index b0560ebf70..404f38a16d 100644 --- a/packaging/ubuntu/control +++ b/packaging/ubuntu/control @@ -8,7 +8,7 @@ Homepage: http://github.com/dotcloud/docker Package: lxc-docker Architecture: linux-any -Depends: ${misc:Depends},${shlibs:Depends},lxc,bsdtar +Depends: ${misc:Depends},${shlibs:Depends},lxc,bsdtar,aufs-tools Conflicts: docker Description: lxc-docker is a Linux container runtime Docker complements LXC with a high-level API which operates at the process diff --git a/registry/registry.go b/registry/registry.go index 64798f24ef..131b02708e 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -328,7 +328,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token 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) if err != nil { 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.ContentLength = int64(len(imgListJSON)) req.Header.Set("X-Docker-Token", "true") + if validate { + req.Header["X-Docker-Endpoints"] = regs + } res, err := r.client.Do(req) 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.ContentLength = int64(len(imgListJSON)) req.Header.Set("X-Docker-Token", "true") - + if validate { + req.Header["X-Docker-Endpoints"] = regs + } res, err = r.client.Do(req) if err != nil { return nil, err diff --git a/runtime.go b/runtime.go index 1c22bd085c..c37e292d22 100644 --- a/runtime.go +++ b/runtime.go @@ -32,6 +32,7 @@ type Runtime struct { autoRestart bool volumes *Graph srv *Server + Dns []string } var sysInitPath string @@ -245,11 +246,12 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) { } // FIXME: harmonize with NewGraph() -func NewRuntime(autoRestart bool) (*Runtime, error) { +func NewRuntime(autoRestart bool, dns []string) (*Runtime, error) { runtime, err := NewRuntimeFromDirectory("/var/lib/docker", autoRestart) if err != nil { return nil, err } + runtime.Dns = dns if k, err := utils.GetKernelVersion(); err != nil { log.Printf("WARNING: %s\n", err) diff --git a/server.go b/server.go index 34d525a35a..30e3ec6b3a 100644 --- a/server.go +++ b/server.go @@ -174,6 +174,8 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { out.Tag = tag out.ID = image.ID out.Created = image.Created.Unix() + out.Size = image.Size + out.VirtualSize = image.getParentsSize(0) + image.Size outs = append(outs, out) } } @@ -183,6 +185,8 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { var out APIImages out.ID = image.ID out.Created = image.Created.Unix() + out.Size = image.Size + out.VirtualSize = image.getParentsSize(0) + image.Size 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.Status = container.State.String() c.Ports = container.NetworkSettings.PortMappingHuman() + c.SizeRw, c.SizeRootFs = container.GetSize() + retContainers = append(retContainers, c) } 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, "/"))) } - repoData, err := r.PushImageJSONIndex(srvName, imgList, false) + repoData, err := r.PushImageJSONIndex(srvName, imgList, false, nil) if err != nil { 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 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) { + 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 { config.Memory = 0 } @@ -972,11 +982,11 @@ func (srv *Server) ImageInspect(name string) (*Image, error) { return nil, fmt.Errorf("No such image: %s", name) } -func NewServer(autoRestart, enableCors bool) (*Server, error) { +func NewServer(autoRestart, enableCors bool, dns ListOpts) (*Server, error) { if runtime.GOARCH != "amd64" { log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH) } - runtime, err := NewRuntime(autoRestart) + runtime, err := NewRuntime(autoRestart, dns) if err != nil { return nil, err } diff --git a/server_test.go b/server_test.go index 532757c61e..7fdec18f61 100644 --- a/server_test.go +++ b/server_test.go @@ -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!") + } + +} diff --git a/testing/Vagrantfile b/testing/Vagrantfile index 7556b191ac..e304a8d087 100644 --- a/testing/Vagrantfile +++ b/testing/Vagrantfile @@ -30,7 +30,7 @@ Vagrant::Config.run do |config| # Install docker dependencies pkg_cmd << "apt-get install -q -y python-software-properties; " \ "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 pkg_cmd << "shutdown -r +1; " config.vm.provision :shell, :inline => pkg_cmd diff --git a/utils/utils.go b/utils/utils.go index f0b0f22b46..83a7f322b4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -135,6 +135,20 @@ func HumanDuration(d time.Duration) string { 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 { if len(s) <= maxlen { return s @@ -628,3 +642,20 @@ func IsURL(str string) bool { func IsGIT(str string) bool { return strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "github.com/") } + +func CheckLocalDns() bool { + resolv, err := ioutil.ReadFile("/etc/resolv.conf") + if err != nil { + Debugf("Error openning resolv.conf: %s", err) + return false + } + for _, ip := range []string{ + "127.0.0.1", + "127.0.1.1", + } { + if strings.Contains(string(resolv), ip) { + return true + } + } + return false +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 4413f44efc..eec06d5134 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -261,3 +261,16 @@ func TestCompareKernelVersion(t *testing.T) { &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"}, -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) + } +}