Browse Source

Merge master within fs

creack 12 years ago
parent
commit
150a4fe7e5

+ 2 - 2
Vagrantfile

@@ -7,11 +7,11 @@ Vagrant::Config.run do |config|
   # please see the online documentation at vagrantup.com.
   # please see the online documentation at vagrantup.com.
 
 
   # Every Vagrant virtual environment requires a box to build off of.
   # Every Vagrant virtual environment requires a box to build off of.
-  config.vm.box = "quantal64"
+  config.vm.box = "quantal64_3.5.0-25"
 
 
   # The url from where the 'config.vm.box' box will be fetched if it
   # The url from where the 'config.vm.box' box will be fetched if it
   # doesn't already exist on the user's system.
   # doesn't already exist on the user's system.
-  config.vm.box_url = "http://unworkable.org/~niallo/quantal64.box"
+  config.vm.box_url = "http://get.docker.io/vbox/ubuntu/12.10/quantal64_3.5.0-25.box"
 
 
   # Boot with a GUI so you can see the screen. (Default is headless)
   # Boot with a GUI so you can see the screen. (Default is headless)
   # config.vm.boot_mode = :gui
   # config.vm.boot_mode = :gui

+ 1 - 0
client/client.go

@@ -111,6 +111,7 @@ func InteractiveMode(scripts ...string) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	defer os.Remove(rcfile.Name())
 	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> ")

+ 7 - 6
container.go

@@ -53,12 +53,13 @@ type Container struct {
 }
 }
 
 
 type Config struct {
 type Config struct {
-	Hostname  string
-	User      string
-	Ram       int64
-	Ports     []int
-	Tty       bool // Attach standard streams to a tty, including stdin if it is not closed.
-	OpenStdin bool // Open stdin
+	Hostname   string
+	User       string
+	Memory     int64 // Memory limit (in bytes)
+	MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
+	Ports      []int
+	Tty        bool // Attach standard streams to a tty, including stdin if it is not closed.
+	OpenStdin  bool // Open stdin
 }
 }
 
 
 type NetworkSettings struct {
 type NetworkSettings struct {

+ 62 - 6
container_test.go

@@ -2,9 +2,12 @@ package docker
 
 
 import (
 import (
 	"./fs"
 	"./fs"
+	"bufio"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
+	"math/rand"
+	"os"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
@@ -23,7 +26,7 @@ func TestCommitRun(t *testing.T) {
 		[]string{"-c", "echo hello > /world"},
 		[]string{"-c", "echo hello > /world"},
 		GetTestImage(docker),
 		GetTestImage(docker),
 		&Config{
 		&Config{
-			Ram: 33554432,
+			Memory: 33554432,
 		},
 		},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -65,7 +68,7 @@ func TestCommitRun(t *testing.T) {
 		[]string{"/world"},
 		[]string{"/world"},
 		img,
 		img,
 		&Config{
 		&Config{
-			Ram: 33554432,
+			Memory: 33554432,
 		},
 		},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -100,7 +103,7 @@ func TestRun(t *testing.T) {
 		[]string{"-al"},
 		[]string{"-al"},
 		GetTestImage(docker),
 		GetTestImage(docker),
 		&Config{
 		&Config{
-			Ram: 33554432,
+			Memory: 33554432,
 		},
 		},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -349,7 +352,7 @@ func TestUser(t *testing.T) {
 	// Set a username
 	// Set a username
 	container, err = docker.Create(
 	container, err = docker.Create(
 		"user_root",
 		"user_root",
-		"/bin/id",
+		"id",
 		[]string{},
 		[]string{},
 		GetTestImage(docker),
 		GetTestImage(docker),
 		&Config{
 		&Config{
@@ -393,7 +396,7 @@ func TestUser(t *testing.T) {
 	// Set a different user by uid
 	// Set a different user by uid
 	container, err = docker.Create(
 	container, err = docker.Create(
 		"user_uid1",
 		"user_uid1",
-		"/usr/bin/id",
+		"id",
 		[]string{},
 		[]string{},
 		GetTestImage(docker),
 		GetTestImage(docker),
 		&Config{
 		&Config{
@@ -417,7 +420,7 @@ func TestUser(t *testing.T) {
 	// Set a different user by username
 	// Set a different user by username
 	container, err = docker.Create(
 	container, err = docker.Create(
 		"user_daemon",
 		"user_daemon",
-		"/usr/bin/id",
+		"id",
 		[]string{},
 		[]string{},
 		GetTestImage(docker),
 		GetTestImage(docker),
 		&Config{
 		&Config{
@@ -616,6 +619,59 @@ func TestEnv(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func grepFile(t *testing.T, path string, pattern string) {
+	f, err := os.Open(path)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+	r := bufio.NewReader(f)
+	var (
+		line string
+	)
+	err = nil
+	for err == nil {
+		line, err = r.ReadString('\n')
+		if strings.Contains(line, pattern) == true {
+			return
+		}
+	}
+	t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
+}
+
+func TestLXCConfig(t *testing.T) {
+	docker, err := newTestDocker()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer nuke(docker)
+	// Memory is allocated randomly for testing
+	rand.Seed(time.Now().UTC().UnixNano())
+	memMin := 33554432
+	memMax := 536870912
+	mem := memMin + rand.Intn(memMax-memMin)
+	container, err := docker.Create(
+		"config_test",
+		"/bin/true",
+		[]string{},
+		GetTestImage(docker),
+		&Config{
+			Hostname: "foobar",
+			Memory:   int64(mem),
+		},
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer docker.Destroy(container)
+	container.generateLXCConfig()
+	grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar")
+	grepFile(t, container.lxcConfigPath,
+		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
+	grepFile(t, container.lxcConfigPath,
+		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
+}
+
 func BenchmarkRunSequencial(b *testing.B) {
 func BenchmarkRunSequencial(b *testing.B) {
 	docker, err := newTestDocker()
 	docker, err := newTestDocker()
 	if err != nil {
 	if err != nil {

+ 44 - 23
install.sh

@@ -1,20 +1,46 @@
-# This script is meant for quick & easy install via 'curl URL-OF-SCRIPPT | bash'
-# Courtesy of Jeff Lindsay <progrium@gmail.com>
-
-cd /tmp
-
-echo "Ensuring dependencies are installed..."
-apt-get --yes install lxc wget bsdtar 2>&1 > /dev/null
-
-echo "Downloading docker binary..."
-wget -q https://dl.dropbox.com/u/20637798/docker.tar.gz 2>&1 > /dev/null
-tar -xf docker.tar.gz 2>&1 > /dev/null
+#!/bin/sh
+# This script is meant for quick & easy install via 'curl URL-OF-SCRIPT | sh'
+# Original version by Jeff Lindsay <progrium@gmail.com>
+# Revamped by Jerome Petazzoni <jerome@dotcloud.com>
+#
+# This script canonical location is http://get.docker.io/; to update it, run:
+# s3cmd put -m text/x-shellscript -P install.sh s3://get.docker.io/index
+
+echo "Ensuring basic dependencies are installed..."
+apt-get -qq update
+apt-get -qq install lxc wget bsdtar
+
+echo "Looking in /proc/filesystems to see if we have AUFS support..."
+if grep -q aufs /proc/filesystems
+then
+    echo "Found."
+else
+    echo "Ahem, it looks like the current kernel does not support AUFS."
+    echo "Let's see if we can load the AUFS module with modprobe..."
+    if modprobe aufs
+    then
+        echo "Module loaded."
+    else
+        echo "Ahem, things didn't turn out as expected."
+        KPKG=linux-image-extra-$(uname -r)
+        echo "Trying to install $KPKG..."
+        if apt-get -qq install $KPKG
+        then
+            echo "Installed."
+        else
+            echo "Oops, we couldn't install the -extra kernel."
+            echo "Are you sure you are running a supported version of Ubuntu?"
+            echo "Proceeding anyway, but Docker will probably NOT WORK!"
+        fi
+    fi
+fi
 
 
-echo "Installing into /usr/local/bin..."
-mv docker/docker /usr/local/bin
-mv dockerd/dockerd /usr/local/bin
+echo "Downloading docker binary and uncompressing into /usr/local/bin..."
+curl -s http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz |
+tar -C /usr/local/bin --strip-components=1 -zxf- \
+docker-master/docker docker-master/dockerd
 
 
-if [[ -f /etc/init/dockerd.conf ]]
+if [ -f /etc/init/dockerd.conf ]
 then
 then
   echo "Upstart script already exists."
   echo "Upstart script already exists."
 else
 else
@@ -22,13 +48,8 @@ else
   echo "exec /usr/local/bin/dockerd" > /etc/init/dockerd.conf
   echo "exec /usr/local/bin/dockerd" > /etc/init/dockerd.conf
 fi
 fi
 
 
-echo "Restarting dockerd..."
-restart dockerd > /dev/null
-
-echo "Cleaning up..."
-rmdir docker
-rmdir dockerd
-rm docker.tar.gz
+echo "Starting dockerd..."
+start dockerd > /dev/null
 
 
-echo "Finished!"
+echo "Done."
 echo
 echo

+ 19 - 3
lxc_template.go

@@ -85,16 +85,32 @@ lxc.mount.entry = /etc/resolv.conf {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
 lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
 lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
 
 
 # limits
 # limits
-{{if .Config.Ram}}
-lxc.cgroup.memory.limit_in_bytes = {{.Config.Ram}}
+{{if .Config.Memory}}
+lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}}
+lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}}
+{{with $memSwap := getMemorySwap .Config}}
+lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
+{{end}}
 {{end}}
 {{end}}
 `
 `
 
 
 var LxcTemplateCompiled *template.Template
 var LxcTemplateCompiled *template.Template
 
 
+func getMemorySwap(config *Config) int64 {
+	// By default, MemorySwap is set to twice the size of RAM.
+	// If you want to omit MemorySwap, set it to `-1'.
+	if config.MemorySwap < 0 {
+		return 0
+	}
+	return config.Memory * 2
+}
+
 func init() {
 func init() {
 	var err error
 	var err error
-	LxcTemplateCompiled, err = template.New("lxc").Parse(LxcTemplate)
+	funcMap := template.FuncMap{
+		"getMemorySwap": getMemorySwap,
+	}
+	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}

+ 21 - 5
puppet/modules/docker/manifests/init.pp

@@ -1,14 +1,19 @@
 class docker {
 class docker {
 
 
     # update this with latest docker binary distro
     # update this with latest docker binary distro
-    $docker_url = "https://dl.dropbox.com/u/20637798/docker.tar.gz"
+    $docker_url = "http://docker.io.s3.amazonaws.com/builds/$kernel/$hardwaremodel/docker-master.tgz"
     # update this with latest go binary distry
     # update this with latest go binary distry
     $go_url = "http://go.googlecode.com/files/go1.0.3.linux-amd64.tar.gz"
     $go_url = "http://go.googlecode.com/files/go1.0.3.linux-amd64.tar.gz"
 
 
-
     Package { ensure => "installed" }
     Package { ensure => "installed" }
 
 
-    package { ["lxc", "debootstrap", "wget", "bsdtar"]: }
+    package { ["lxc", "debootstrap", "wget", "bsdtar", "git",
+               "linux-image-3.5.0-25-generic",
+               "linux-image-extra-3.5.0-25-generic",
+               "virtualbox-guest-utils",
+               "linux-headers-3.5.0-25-generic"]: }
+
+    notify { "docker_url = $docker_url": withpath => true }
 
 
     exec { "debootstrap" :
     exec { "debootstrap" :
         require => Package["debootstrap"],
         require => Package["debootstrap"],
@@ -26,7 +31,7 @@ class docker {
     exec { "fetch-docker" :
     exec { "fetch-docker" :
         require => Package["wget"],
         require => Package["wget"],
         command => "/usr/bin/wget -O - $docker_url | /bin/tar xz -C /home/vagrant",
         command => "/usr/bin/wget -O - $docker_url | /bin/tar xz -C /home/vagrant",
-        creates => "/home/vagrant/docker/dockerd"
+        creates => "/home/vagrant/docker-master"
     }
     }
 
 
     file { "/etc/init/dockerd.conf":
     file { "/etc/init/dockerd.conf":
@@ -39,10 +44,21 @@ class docker {
 
 
     exec { "copy-docker-bin" :
     exec { "copy-docker-bin" :
         require => Exec["fetch-docker"],
         require => Exec["fetch-docker"],
-        command => "/bin/cp /home/vagrant/docker/docker /usr/local/bin",
+        command => "/bin/cp /home/vagrant/docker-master/docker /usr/local/bin",
         creates => "/usr/local/bin/docker"
         creates => "/usr/local/bin/docker"
     }
     }
 
 
+    exec { "copy-dockerd-bin" :
+        require => Exec["fetch-docker"],
+        command => "/bin/cp /home/vagrant/docker-master/dockerd /usr/local/bin",
+        creates => "/usr/local/bin/dockerd"
+    }
+
+    exec { "vbox-add" :
+        require => Package["linux-headers-3.5.0-25-generic"],
+        command => "/etc/init.d/vboxadd setup",
+    }
+
     service { "dockerd" :
     service { "dockerd" :
         ensure => "running",
         ensure => "running",
         start => "/sbin/initctl start dockerd",
         start => "/sbin/initctl start dockerd",

+ 2 - 1
puppet/modules/docker/templates/dockerd.conf

@@ -7,5 +7,6 @@ start on runlevel [3]
 respawn
 respawn
 
 
 script
 script
-    /home/vagrant/dockerd/dockerd
+    test -f /etc/default/locale && . /etc/default/locale || true
+    LANG=$LANG LC_ALL=$LANG /usr/local/bin/dockerd
 end script
 end script

+ 42 - 34
server/server.go

@@ -37,28 +37,43 @@ func (srv *Server) Name() string {
 	return "docker"
 	return "docker"
 }
 }
 
 
+// FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations
 func (srv *Server) Help() string {
 func (srv *Server) Help() string {
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
 	for _, cmd := range [][]interface{}{
 	for _, cmd := range [][]interface{}{
 		{"run", "Run a command in a container"},
 		{"run", "Run a command in a container"},
 		{"ps", "Display a list of containers"},
 		{"ps", "Display a list of containers"},
 		{"import", "Create a new filesystem image from the contents of a tarball"},
 		{"import", "Create a new filesystem image from the contents of a tarball"},
-		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
-		{"rm", "Remove containers"},
-		{"kill", "Kill a running container"},
-		{"wait", "Wait for the state of a container to change"},
-		{"stop", "Stop a running container"},
-		{"start", "Start a stopped container"},
-		{"restart", "Restart a running container"},
-		{"logs", "Fetch the logs of a container"},
+		{"attach", "Attach to a running container"},
+		{"cat", "Write the contents of a container's file to standard output"},
+		{"commit", "Create a new image from a container's changes"},
+		{"cp", "Create a copy of IMAGE and call it NAME"},
+		{"debug", "(debug only) (No documentation available)"},
 		{"diff", "Inspect changes on a container's filesystem"},
 		{"diff", "Inspect changes on a container's filesystem"},
-		{"commit", "Save the state of a container"},
-		{"attach", "Attach to the standard inputs and outputs of a running container"},
-		{"wait", "Block until a container exits, then print its exit code"},
+		{"images", "List images"},
 		{"info", "Display system-wide information"},
 		{"info", "Display system-wide information"},
+		{"inspect", "Return low-level information on a container"},
+		{"kill", "Kill a running container"},
+		{"layers", "(debug only) List filesystem layers"},
+		{"logs", "Fetch the logs of a container"},
+		{"ls", "List the contents of a container's directory"},
+		{"mirror", "(debug only) (No documentation available)"},
+		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
+		{"ps", "List containers"},
+		{"pull", "Download a new image from a remote location"},
+		{"put", "Import a new image from a local archive"},
+		{"reset", "Reset changes to a container's filesystem"},
+		{"restart", "Restart a running container"},
+		{"rm", "Remove a container"},
+		{"rmimage", "Remove an image"},
+		{"run", "Run a command in a new container"},
+		{"start", "Start a stopped container"},
+		{"stop", "Stop a running container"},
 		{"tar", "Stream the contents of a container as a tar archive"},
 		{"tar", "Stream the contents of a container as a tar archive"},
-		{"web", "Generate a web UI"},
-		{"images", "List images"},
+		{"umount", "(debug only) Mount a container's filesystem"},
+		{"wait", "Block until a container stops, then print its exit code"},
+		{"web", "A web UI for docker"},
+		{"write", "Write the contents of standard input to a container's file"},
 	} {
 	} {
 		help += fmt.Sprintf("    %-10.10s%s\n", cmd...)
 		help += fmt.Sprintf("    %-10.10s%s\n", cmd...)
 	}
 	}
@@ -69,7 +84,6 @@ func (srv *Server) Help() string {
 func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
 	cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -88,25 +102,24 @@ func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string
 
 
 // 'docker info': display system-wide information.
 // 'docker info': display system-wide information.
 func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
-	images, _ := srv.images.Images()
-	var imgcount int
-	if images == nil {
-		imgcount = 0
-	} else {
-		imgcount = len(images)
+	cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() > 1 {
+		cmd.Usage()
+		return nil
 	}
 	}
-
 	fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n",
 	fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n",
 		len(srv.containers.List()),
 		len(srv.containers.List()),
 		VERSION,
 		VERSION,
-		imgcount)
+		len(srv.images.ById))
 	return nil
 	return nil
 }
 }
 
 
 func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
 	cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -129,7 +142,6 @@ func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string
 func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
 	cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -152,7 +164,6 @@ func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...str
 func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
 	cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -175,7 +186,6 @@ func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...strin
 func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)")
 	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -198,7 +208,6 @@ func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...stri
 func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)")
 	cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -221,7 +230,6 @@ func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...strin
 func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output")
 	cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 2 {
 	if cmd.NArg() < 2 {
@@ -243,7 +251,6 @@ func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string)
 func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file")
 	cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 2 {
 	if cmd.NArg() < 2 {
@@ -265,7 +272,6 @@ func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...strin
 func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
 	cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 2 {
 	if cmd.NArg() < 2 {
@@ -289,7 +295,6 @@ func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string)
 func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
 	cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() < 1 {
 	if cmd.NArg() < 1 {
@@ -322,7 +327,6 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str
 func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
 	cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
-		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
 	if cmd.NArg() != 2 {
 	if cmd.NArg() != 2 {
@@ -459,7 +463,9 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
 	cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
 	cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
 	limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
 	limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
-	cmd.Parse(args)
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
 	if cmd.NArg() > 1 {
 	if cmd.NArg() > 1 {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
@@ -813,6 +819,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
 	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
 	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
 	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
 	fl_comment := cmd.String("c", "", "Comment")
 	fl_comment := cmd.String("c", "", "Comment")
+	fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
 	var fl_ports ports
 	var fl_ports ports
 	cmd.Var(&fl_ports, "p", "Map a network port to the container")
 	cmd.Var(&fl_ports, "p", "Map a network port to the container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
@@ -842,7 +849,8 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 		return errors.New("No such image: " + name)
 		return errors.New("No such image: " + name)
 	}
 	}
 	// Create new container
 	// Create new container
-	container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...)
+	container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
+		*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
 	if err != nil {
 	if err != nil {
 		return errors.New("Error creating container: " + err.Error())
 		return errors.New("Error creating container: " + err.Error())
 	}
 	}