Merge pull request #911 from dotcloud/add_port_redirection_doc
* Documentation: add port redirection doc
diff --git a/FIXME b/FIXME
index e252fb2..b88757e 100644
--- a/FIXME
+++ b/FIXME
@@ -16,3 +16,21 @@
* 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
+* bring back git revision info, looks like it was lost
+* Simple command to remove all untagged images
+* Simple command to clean up containers for disk space
+* Caching after an ADD
+* entry point config
+* bring back git revision info, looks like it was lost
diff --git a/Makefile b/Makefile
index 9527d3f..eca409c 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,7 @@
release: $(BINRELEASE)
s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz
+ s3cmd -P put docker-latest.tgz s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-latest.tgz
srcrelease: $(SRCRELEASE)
deps: $(DOCKER_DIR)
@@ -60,6 +61,7 @@
$(BINRELEASE): $(SRCRELEASE)
rm -f $(BINRELEASE)
cd $(SRCRELEASE); make; cp -R bin docker-$(RELEASE_VERSION); tar -f ../$(BINRELEASE) -zv -c docker-$(RELEASE_VERSION)
+ cd $(SRCRELEASE); cp -R bin docker-latest; tar -f ../docker-latest.tgz -zv -c docker-latest
clean:
@rm -rf $(dir $(DOCKER_BIN))
diff --git a/Vagrantfile b/Vagrantfile
index 3d56826..5b3a1f4 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -5,11 +5,13 @@
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 @@
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 5e1a6d7..e870ca7 100644
--- a/api.go
+++ b/api.go
@@ -434,17 +434,23 @@
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.")
@@ -453,6 +459,7 @@
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 33b915c..2a641f8 100644
--- a/api_params.go
+++ b/api_params.go
@@ -7,10 +7,12 @@
}
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 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/archive.go b/archive.go
index 0646662..44fdd56 100644
--- a/archive.go
+++ b/archive.go
@@ -51,6 +51,7 @@
return CmdStream(cmd)
}
+// FIXME: specify behavior when target path exists vs. doesn't exist.
func Untar(archive io.Reader, path string) error {
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
cmd.Stdin = archive
@@ -64,6 +65,30 @@
return nil
}
+// UntarPath is a convenience function which looks for an archive
+// at filesystem path `src`, and unpacks it at `dst`.
+func UntarPath(src, dst string) error {
+ if archive, err := os.Open(src); err != nil {
+ return err
+ } else if err := Untar(archive, dst); err != nil {
+ return err
+ }
+ return nil
+}
+
+// CopyWithTar creates a tar archive of filesystem path `src`, and
+// unpacks it at filesystem path `dst`.
+// The archive is streamed directly with fixed buffering and no
+// intermediary disk IO.
+//
+func CopyWithTar(src, dst string) error {
+ archive, err := Tar(src, Uncompressed)
+ if err != nil {
+ return err
+ }
+ return Untar(archive, dst)
+}
+
// CmdStream executes a command, and returns its stdout as a stream.
// If the command fails to run or doesn't complete successfully, an error
// will be returned, including anything written on stderr.
diff --git a/builder.go b/builder.go
index 808b7ef..02c51fb 100644
--- a/builder.go
+++ b/builder.go
@@ -2,11 +2,14 @@
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 @@
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/buildfile.go b/buildfile.go
index 3c706b0..de350a8 100644
--- a/buildfile.go
+++ b/buildfile.go
@@ -178,6 +178,8 @@
cmd := b.config.Cmd
// Create the container and start it
+ b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
+ b.config.Image = b.image
container, err := b.builder.Create(b.config)
if err != nil {
return err
@@ -200,21 +202,17 @@
if err := os.MkdirAll(destPath, 0700); err != nil {
return err
}
-
- files, err := ioutil.ReadDir(path.Join(b.context, orig))
- if err != nil {
+ if err := CopyWithTar(origPath, destPath); err != nil {
return err
}
- for _, fi := range files {
- if err := utils.CopyDirectory(path.Join(origPath, fi.Name()), path.Join(destPath, fi.Name())); err != nil {
- return err
- }
- }
- } else {
+ // First try to unpack the source as an archive
+ } else if err := UntarPath(origPath, destPath); err != nil {
+ utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
+ // If that fails, just copy it as a regular file
if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
return err
}
- if err := utils.CopyDirectory(origPath, destPath); err != nil {
+ if err := CopyWithTar(origPath, destPath); err != nil {
return err
}
}
diff --git a/buildfile_test.go b/buildfile_test.go
index d9c60a7..33e6a31 100644
--- a/buildfile_test.go
+++ b/buildfile_test.go
@@ -23,6 +23,12 @@
run sh -c 'echo root:testpass > /tmp/passwd'
run mkdir -p /var/run/sshd`
+// FIXME: test building with a context
+
+// FIXME: test building with a local ADD as first command
+
+// FIXME: test building with 2 successive overlapping ADD commands
+
func TestBuild(t *testing.T) {
dockerfiles := []string{Dockerfile, DockerfileNoNewLine}
for _, Dockerfile := range dockerfiles {
diff --git a/commands.go b/commands.go
index 63c787c..04eb994 100644
--- a/commands.go
+++ b/commands.go
@@ -20,6 +20,7 @@
"path"
"path/filepath"
"reflect"
+ "regexp"
"strconv"
"strings"
"syscall"
@@ -727,6 +728,15 @@
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)
@@ -811,7 +821,7 @@
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 {
@@ -829,7 +839,12 @@
} 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)
@@ -888,15 +903,20 @@
}
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 4d2032a..f60de21 100644
--- a/container.go
+++ b/container.go
@@ -13,6 +13,7 @@
"os"
"os/exec"
"path"
+ "path/filepath"
"sort"
"strconv"
"strings"
@@ -919,3 +920,26 @@
}
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 7423661..2e23999 100644
--- a/docker/docker.go
+++ b/docker/docker.go
@@ -34,6 +34,7 @@
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 @@
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 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 @@
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 8354760..fb69168 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 @@
"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 @@
"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 @@
"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 34a7138..66bcf5d 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 5d937c5..ce2ecd4 100644
--- a/docs/sources/contributing/devenvironment.rst
+++ b/docs/sources/contributing/devenvironment.rst
@@ -33,7 +33,7 @@
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 daa21f3..f2b2cce 100644
--- a/graph.go
+++ b/graph.go
@@ -90,6 +90,15 @@
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 250731e..318f835 100644
--- a/hack/Vagrantfile
+++ b/hack/Vagrantfile
@@ -22,7 +22,7 @@
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/hack/dockerbuilder/Dockerfile b/hack/dockerbuilder/Dockerfile
index 5b2504d..50993ed 100644
--- a/hack/dockerbuilder/Dockerfile
+++ b/hack/dockerbuilder/Dockerfile
@@ -27,5 +27,4 @@
# Copy dockerbuilder files into the container
add . /src
run cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
-run cp /src/s3cfg /.s3cfg
cmd ["dockerbuilder"]
diff --git a/hack/dockerbuilder/dockerbuilder b/hack/dockerbuilder/dockerbuilder
index d2e4396..9fa05ce 100644
--- a/hack/dockerbuilder/dockerbuilder
+++ b/hack/dockerbuilder/dockerbuilder
@@ -13,12 +13,10 @@
export REVISION=$1
-if [ -z "$AWS_ID" ]; then
- echo "Warning: environment variable AWS_ID is not set. Won't upload to S3."
-fi
-
-if [ -z "$AWS_KEY" ]; then
- echo "Warning: environment variable AWS_KEY is not set. Won't upload to S3."
+if [ -z "$AWS_ID" -o -z "$AWS_KEY" ]; then
+ echo "Warning: either AWS_ID or AWS_KEY environment variable not set. Won't upload to S3."
+else
+ /bin/echo -e "[default]\naccess_key = $AWS_ID\nsecret_key = $AWS_KEY\n" > /.s3cfg
fi
if [ -z "$GPG_KEY" ]; then
@@ -35,6 +33,9 @@
make release RELEASE_VERSION=$REVISION
fi
+# Remove credentials from container
+rm -f /.s3cfg
+
if [ -z "$NO_UBUNTU" ]; then
export PATH=`echo $PATH | sed 's#/usr/local/go/bin:##g'`
(cd packaging/ubuntu && make ubuntu)
diff --git a/hack/dockerbuilder/s3cfg b/hack/dockerbuilder/s3cfg
deleted file mode 100644
index 963af7d..0000000
--- a/hack/dockerbuilder/s3cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-[default]
-access_key = $AWS_ID
-secret_key = $AWS_KEY
diff --git a/image.go b/image.go
index 4bd8f2d..cd76b8c 100644
--- a/image.go
+++ b/image.go
@@ -13,6 +13,7 @@
"os"
"os/exec"
"path"
+ "path/filepath"
"strings"
"time"
)
@@ -29,6 +30,7 @@
Config *Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
graph *Graph
+ Size int64
}
func LoadImage(root string) (*Image, error) {
@@ -94,6 +96,18 @@
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 @@
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 bb1a40e..541c29c 100644
--- a/mount.go
+++ b/mount.go
@@ -2,13 +2,18 @@
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 35a2a28..62a963f 100644
--- a/packaging/debian/control
+++ b/packaging/debian/control
@@ -10,7 +10,7 @@
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 b0560eb..404f38a 100644
--- a/packaging/ubuntu/control
+++ b/packaging/ubuntu/control
@@ -8,7 +8,7 @@
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 64798f2..131b027 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -328,7 +328,7 @@
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 @@
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 @@
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 1c22bd0..c37e292 100644
--- a/runtime.go
+++ b/runtime.go
@@ -32,6 +32,7 @@
autoRestart bool
volumes *Graph
srv *Server
+ Dns []string
}
var sysInitPath string
@@ -245,11 +246,12 @@
}
// 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 34d525a..30e3ec6 100644
--- a/server.go
+++ b/server.go
@@ -174,6 +174,8 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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) 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 @@
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 532757c..7fdec18 100644
--- a/server_test.go
+++ b/server_test.go
@@ -147,3 +147,25 @@
}
}
+
+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 7556b19..e304a8d 100644
--- a/testing/Vagrantfile
+++ b/testing/Vagrantfile
@@ -30,7 +30,7 @@
# 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 c3f9e57..b77c1ea 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -70,7 +70,7 @@
readProgress int // How much has been read so far (bytes)
lastUpdate int // How many bytes read at least update
template string // Template to print. Default "%v/%v (%v)"
- sf *StreamFormatter
+ sf *StreamFormatter
}
func (r *progressReader) Read(p []byte) (n int, err error) {
@@ -103,7 +103,7 @@
return io.ReadCloser(r.reader).Close()
}
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
- tpl := string(template)
+ tpl := string(template)
if tpl == "" {
tpl = string(sf.FormatProgress("", "%v/%v (%v)"))
}
@@ -135,6 +135,20 @@
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
@@ -534,6 +548,7 @@
}, nil
}
+// FIXME: this is deprecated by CopyWithTar in archive.go
func CopyDirectory(source, dest string) error {
if output, err := exec.Command("cp", "-ra", source, dest).CombinedOutput(); err != nil {
return fmt.Errorf("Error copy: %s (%s)", err, output)
@@ -585,7 +600,7 @@
sf.used = true
str := fmt.Sprintf(format, a...)
if sf.json {
- b, err := json.Marshal(&JSONMessage{Status:str});
+ b, err := json.Marshal(&JSONMessage{Status: str})
if err != nil {
return sf.FormatError(err)
}
@@ -597,7 +612,7 @@
func (sf *StreamFormatter) FormatError(err error) []byte {
sf.used = true
if sf.json {
- if b, err := json.Marshal(&JSONMessage{Error:err.Error()}); err == nil {
+ if b, err := json.Marshal(&JSONMessage{Error: err.Error()}); err == nil {
return b
}
return []byte("{\"error\":\"format error\"}")
@@ -608,10 +623,10 @@
func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
sf.used = true
if sf.json {
- b, err := json.Marshal(&JSONMessage{Status: action, Progress:str})
+ b, err := json.Marshal(&JSONMessage{Status: action, Progress: str})
if err != nil {
- return nil
- }
+ return nil
+ }
return b
}
return []byte(action + " " + str + "\r")
@@ -620,3 +635,20 @@
func (sf *StreamFormatter) Used() bool {
return sf.used
}
+
+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 4413f44..eec06d5 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -261,3 +261,16 @@
&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)
+ }
+}