Przeglądaj źródła

Merge branch 'master' into add-libcontainer

Conflicts:
	runtime.go

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
Michael Crosby 11 lat temu
rodzic
commit
27a43692c2

+ 1 - 0
.gitignore

@@ -22,3 +22,4 @@ bundles/
 .git/
 .git/
 vendor/pkg/
 vendor/pkg/
 pyenv
 pyenv
+Vagrantfile

+ 0 - 1
MAINTAINERS

@@ -6,4 +6,3 @@ Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
 api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
 api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
 Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
 Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
 Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
 Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
-Vagrantfile: Cristian Staretu <cristian.staretu@gmail.com> (@unclejack)

+ 0 - 206
Vagrantfile

@@ -1,206 +0,0 @@
-# -*- mode: ruby -*-
-# vi: set ft=ruby :
-
-BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
-BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
-VF_BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64_vmware_fusion.box"
-AWS_BOX_URI = ENV['BOX_URI'] || "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
-AWS_REGION = ENV['AWS_REGION'] || "us-east-1"
-AWS_AMI = ENV['AWS_AMI'] || "ami-69f5a900"
-AWS_INSTANCE_TYPE = ENV['AWS_INSTANCE_TYPE'] || 't1.micro'
-SSH_PRIVKEY_PATH = ENV['SSH_PRIVKEY_PATH']
-PRIVATE_NETWORK = ENV['PRIVATE_NETWORK']
-
-# Boolean that forwards the Docker dynamic ports 49000-49900
-# See http://docs.docker.io/en/latest/use/port_redirection/ for more
-# $ FORWARD_DOCKER_PORTS=1 vagrant [up|reload]
-FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS']
-VAGRANT_RAM = ENV['VAGRANT_RAM'] || 512
-VAGRANT_CORES = ENV['VAGRANT_CORES'] || 1
-
-# You may also provide a comma-separated list of ports
-# for Vagrant to forward. For example:
-# $ FORWARD_PORTS=8080,27017 vagrant [up|reload]
-FORWARD_PORTS = ENV['FORWARD_PORTS']
-
-# A script to upgrade from the 12.04 kernel to the raring backport kernel (3.8)
-# and install docker.
-$script = <<SCRIPT
-# The username to add to the docker group will be passed as the first argument
-# to the script.  If nothing is passed, default to "vagrant".
-user="$1"
-if [ -z "$user" ]; then
-    user=vagrant
-fi
-
-# Enable memory cgroup and swap accounting
-sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"/g' /etc/default/grub
-update-grub
-
-# Adding an apt gpg key is idempotent.
-apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
-
-# Creating the docker.list file is idempotent, but it may overwrite desired
-# settings if it already exists.  This could be solved with md5sum but it
-# doesn't seem worth it.
-echo 'deb http://get.docker.io/ubuntu docker main' > \
-    /etc/apt/sources.list.d/docker.list
-
-# Update remote package metadata.  'apt-get update' is idempotent.
-apt-get update -q
-
-# Install docker.  'apt-get install' is idempotent.
-apt-get install -q -y lxc-docker
-
-usermod -a -G docker "$user"
-
-tmp=`mktemp -q` && {
-    # Only install the backport kernel, don't bother upgrading if the backport is
-    # already installed.  We want parse the output of apt so we need to save it
-    # with 'tee'.  NOTE: The installation of the kernel will trigger dkms to
-    # install vboxguest if needed.
-    apt-get install -q -y --no-upgrade linux-image-generic-lts-raring | \
-        tee "$tmp"
-
-    # Parse the number of installed packages from the output
-    NUM_INST=`awk '$2 == "upgraded," && $4 == "newly" { print $3 }' "$tmp"`
-    rm "$tmp"
-}
-
-# If the number of installed packages is greater than 0, we want to reboot (the
-# backport kernel was installed but is not running).
-if [ "$NUM_INST" -gt 0 ];
-then
-    echo "Rebooting down to activate new kernel."
-    echo "/vagrant will not be mounted.  Use 'vagrant halt' followed by"
-    echo "'vagrant up' to ensure /vagrant is mounted."
-    shutdown -r now
-fi
-SCRIPT
-
-# We need to install the virtualbox guest additions *before* we do the normal
-# docker installation.  As such this script is prepended to the common docker
-# install script above.  This allows the install of the backport kernel to
-# trigger dkms to build the virtualbox guest module install.
-$vbox_script = <<VBOX_SCRIPT + $script
-# Install the VirtualBox guest additions if they aren't already installed.
-if [ ! -d /opt/VBoxGuestAdditions-4.3.6/ ]; then
-    # Update remote package metadata.  'apt-get update' is idempotent.
-    apt-get update -q
-
-    # Kernel Headers and dkms are required to build the vbox guest kernel
-    # modules.
-    apt-get install -q -y linux-headers-generic-lts-raring dkms
-
-    echo 'Downloading VBox Guest Additions...'
-    wget -cq http://dlc.sun.com.edgesuite.net/virtualbox/4.3.6/VBoxGuestAdditions_4.3.6.iso
-    echo "95648fcdb5d028e64145a2fe2f2f28c946d219da366389295a61fed296ca79f0  VBoxGuestAdditions_4.3.6.iso" | sha256sum --check || exit 1
-
-    mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.3.6.iso /mnt
-    /mnt/VBoxLinuxAdditions.run --nox11
-    umount /mnt
-fi
-VBOX_SCRIPT
-
-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
-
-  # Use the specified private key path if it is specified and not empty.
-  if SSH_PRIVKEY_PATH
-      config.ssh.private_key_path = SSH_PRIVKEY_PATH
-  end
-
-  config.ssh.forward_agent = true
-end
-
-# Providers were added on Vagrant >= 1.1.0
-#
-# NOTE: The vagrant "vm.provision" appends its arguments to a list and executes
-# them in order.  If you invoke "vm.provision :shell, :inline => $script"
-# twice then vagrant will run the script two times.  Unfortunately when you use
-# providers and the override argument to set up provisioners (like the vbox
-# guest extensions) they 1) don't replace the other provisioners (they append
-# to the end of the list) and 2) you can't control the order the provisioners
-# are executed (you can only append to the list).  If you want the virtualbox
-# only script to run before the other script, you have to jump through a lot of
-# hoops.
-#
-# Here is my only repeatable solution: make one script that is common ($script)
-# and another script that is the virtual box guest *prepended* to the common
-# script.  Only ever use "vm.provision" *one time* per provider.  That means
-# every single provider has an override, and every single one configures
-# "vm.provision".  Much saddness, but such is life.
-Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
-  config.vm.provider :aws do |aws, override|
-    username = "ubuntu"
-    override.vm.box_url = AWS_BOX_URI
-    override.vm.provision :shell, :inline => $script, :args => username
-    aws.access_key_id = ENV["AWS_ACCESS_KEY"]
-    aws.secret_access_key = ENV["AWS_SECRET_KEY"]
-    aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
-    override.ssh.username = username
-    aws.region = AWS_REGION
-    aws.ami    = AWS_AMI
-    aws.instance_type = AWS_INSTANCE_TYPE
-  end
-
-  config.vm.provider :rackspace do |rs, override|
-    override.vm.provision :shell, :inline => $script
-    rs.username = ENV["RS_USERNAME"]
-    rs.api_key  = ENV["RS_API_KEY"]
-    rs.public_key_path = ENV["RS_PUBLIC_KEY"]
-    rs.flavor   = /512MB/
-    rs.image    = /Ubuntu/
-  end
-
-  config.vm.provider :vmware_fusion do |f, override|
-    override.vm.box_url = VF_BOX_URI
-    override.vm.synced_folder ".", "/vagrant", disabled: true
-    override.vm.provision :shell, :inline => $script
-    f.vmx["displayName"] = "docker"
-  end
-
-  config.vm.provider :virtualbox do |vb, override|
-    override.vm.provision :shell, :inline => $vbox_script
-    vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
-    vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
-    vb.customize ["modifyvm", :id, "--memory", VAGRANT_RAM]
-    vb.customize ["modifyvm", :id, "--cpus", VAGRANT_CORES]
-  end
-end
-
-# If this is a version 1 config, virtualbox is the only option.  A version 2
-# config would have already been set in the above provider section.
-Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
-  config.vm.provision :shell, :inline => $vbox_script
-end
-
-# Setup port forwarding per loaded environment variables
-forward_ports = FORWARD_DOCKER_PORTS.nil? ? [] : [*49153..49900]
-forward_ports += FORWARD_PORTS.split(',').map{|i| i.to_i } if FORWARD_PORTS
-if forward_ports.any?
-  Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
-    forward_ports.each do |port|
-      config.vm.forward_port port, port
-    end
-  end
-
-  Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
-    forward_ports.each do |port|
-      config.vm.network :forwarded_port, :host => port, :guest => port, auto_correct: true
-    end
-  end
-end
-
-if !PRIVATE_NETWORK.nil?
-  Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
-    config.vm.network :hostonly, PRIVATE_NETWORK
-  end
-
-  Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
-    config.vm.network "private_network", ip: PRIVATE_NETWORK
-  end
-end
-

+ 44 - 0
api/common.go

@@ -0,0 +1,44 @@
+package api
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/utils"
+	"mime"
+	"strings"
+)
+
+const (
+	APIVERSION        = 1.9
+	DEFAULTHTTPHOST   = "127.0.0.1"
+	DEFAULTUNIXSOCKET = "/var/run/docker.sock"
+)
+
+func ValidateHost(val string) (string, error) {
+	host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val)
+	if err != nil {
+		return val, err
+	}
+	return host, nil
+}
+
+//TODO remove, used on < 1.5 in getContainersJSON
+func displayablePorts(ports *engine.Table) string {
+	result := []string{}
+	for _, port := range ports.Data {
+		if port.Get("IP") == "" {
+			result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
+		} else {
+			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
+		}
+	}
+	return strings.Join(result, ", ")
+}
+
+func MatchesContentType(contentType, expectedType string) bool {
+	mimetype, _, err := mime.ParseMediaType(contentType)
+	if err != nil {
+		utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
+	}
+	return err == nil && mimetype == expectedType
+}

+ 0 - 41
api/api.go → api/server.go

@@ -17,7 +17,6 @@ import (
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
-	"mime"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"net/http/pprof"
 	"net/http/pprof"
@@ -29,31 +28,12 @@ import (
 	"time"
 	"time"
 )
 )
 
 
-// FIXME: move code common to client and server to common.go
-const (
-	APIVERSION        = 1.9
-	DEFAULTHTTPHOST   = "127.0.0.1"
-	DEFAULTUNIXSOCKET = "/var/run/docker.sock"
-)
-
 var (
 var (
 	activationLock chan struct{}
 	activationLock chan struct{}
 )
 )
 
 
-func ValidateHost(val string) (string, error) {
-	host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val)
-	if err != nil {
-		return val, err
-	}
-	return host, nil
-}
-
 type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
 type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
 
 
-func init() {
-	engine.Register("serveapi", ServeApi)
-}
-
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 	conn, _, err := w.(http.Hijacker).Hijack()
 	conn, _, err := w.(http.Hijacker).Hijack()
 	if err != nil {
 	if err != nil {
@@ -133,27 +113,6 @@ func getBoolParam(value string) (bool, error) {
 	return ret, nil
 	return ret, nil
 }
 }
 
 
-//TODO remove, used on < 1.5 in getContainersJSON
-func displayablePorts(ports *engine.Table) string {
-	result := []string{}
-	for _, port := range ports.Data {
-		if port.Get("IP") == "" {
-			result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
-		} else {
-			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
-		}
-	}
-	return strings.Join(result, ", ")
-}
-
-func MatchesContentType(contentType, expectedType string) bool {
-	mimetype, _, err := mime.ParseMediaType(contentType)
-	if err != nil {
-		utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
-	}
-	return err == nil && mimetype == expectedType
-}
-
 func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var (
 	var (
 		authConfig, err = ioutil.ReadAll(r.Body)
 		authConfig, err = ioutil.ReadAll(r.Body)

+ 1 - 1
buildfile.go

@@ -110,7 +110,7 @@ func (b *buildFile) CmdFrom(name string) error {
 		b.config = image.Config
 		b.config = image.Config
 	}
 	}
 	if b.config.Env == nil || len(b.config.Env) == 0 {
 	if b.config.Env == nil || len(b.config.Env) == 0 {
-		b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
+		b.config.Env = append(b.config.Env, "HOME=/", "PATH="+defaultPathEnv)
 	}
 	}
 	// Process ONBUILD triggers if they exist
 	// Process ONBUILD triggers if they exist
 	if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
 	if nTriggers := len(b.config.OnBuild); nTriggers != 0 {

+ 40 - 0
builtins/builtins.go

@@ -0,0 +1,40 @@
+package builtins
+
+import (
+	"github.com/dotcloud/docker/engine"
+
+	"github.com/dotcloud/docker"
+	"github.com/dotcloud/docker/api"
+	"github.com/dotcloud/docker/networkdriver/lxc"
+)
+
+func Register(eng *engine.Engine) {
+	daemon(eng)
+	remote(eng)
+}
+
+// remote: a RESTful api for cross-docker communication
+func remote(eng *engine.Engine) {
+	eng.Register("serveapi", api.ServeApi)
+}
+
+// daemon: a default execution and storage backend for Docker on Linux,
+// with the following underlying components:
+//
+// * Pluggable storage drivers including aufs, vfs, lvm and btrfs.
+// * Pluggable execution drivers including lxc and chroot.
+//
+// In practice `daemon` still includes most core Docker components, including:
+//
+// * The reference registry client implementation
+// * Image management
+// * The build facility
+// * Logging
+//
+// These components should be broken off into plugins of their own.
+//
+func daemon(eng *engine.Engine) {
+	eng.Register("initserver", docker.InitServer)
+	eng.Register("init_networkdriver", lxc.InitDriver)
+	eng.Register("version", docker.GetVersion)
+}

+ 2 - 0
config.go

@@ -25,6 +25,7 @@ type DaemonConfig struct {
 	BridgeIP                    string
 	BridgeIP                    string
 	InterContainerCommunication bool
 	InterContainerCommunication bool
 	GraphDriver                 string
 	GraphDriver                 string
+	ExecDriver                  string
 	Mtu                         int
 	Mtu                         int
 	DisableNetwork              bool
 	DisableNetwork              bool
 }
 }
@@ -43,6 +44,7 @@ func DaemonConfigFromJob(job *engine.Job) *DaemonConfig {
 		DefaultIp:                   net.ParseIP(job.Getenv("DefaultIp")),
 		DefaultIp:                   net.ParseIP(job.Getenv("DefaultIp")),
 		InterContainerCommunication: job.GetenvBool("InterContainerCommunication"),
 		InterContainerCommunication: job.GetenvBool("InterContainerCommunication"),
 		GraphDriver:                 job.Getenv("GraphDriver"),
 		GraphDriver:                 job.Getenv("GraphDriver"),
+		ExecDriver:                  job.Getenv("ExecDriver"),
 	}
 	}
 	if dns := job.GetenvList("Dns"); dns != nil {
 	if dns := job.GetenvList("Dns"); dns != nil {
 		config.Dns = dns
 		config.Dns = dns

+ 3 - 1
container.go

@@ -23,6 +23,8 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+const defaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+
 var (
 var (
 	ErrNotATTY               = errors.New("The PTY is not a file")
 	ErrNotATTY               = errors.New("The PTY is not a file")
 	ErrNoTTY                 = errors.New("No PTY found")
 	ErrNoTTY                 = errors.New("No PTY found")
@@ -447,7 +449,7 @@ func (container *Container) Start() (err error) {
 	// Setup environment
 	// Setup environment
 	env := []string{
 	env := []string{
 		"HOME=/",
 		"HOME=/",
-		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+		"PATH=" + defaultPathEnv,
 		"HOSTNAME=" + container.Config.Hostname,
 		"HOSTNAME=" + container.Config.Hostname,
 	}
 	}
 
 

+ 35 - 0
contrib/mkimage-debootstrap.sh

@@ -44,6 +44,8 @@ debianStable=wheezy
 debianUnstable=sid
 debianUnstable=sid
 # this should match the name found at http://releases.ubuntu.com/
 # this should match the name found at http://releases.ubuntu.com/
 ubuntuLatestLTS=precise
 ubuntuLatestLTS=precise
+# this should match the name found at http://releases.tanglu.org/
+tangluLatest=aequorea
 
 
 while getopts v:i:a:p:dst name; do
 while getopts v:i:a:p:dst name; do
 	case "$name" in
 	case "$name" in
@@ -201,6 +203,17 @@ if [ -z "$strictDebootstrap" ]; then
 					s/ $suite-updates main/ ${suite}-security main/
 					s/ $suite-updates main/ ${suite}-security main/
 				" etc/apt/sources.list
 				" etc/apt/sources.list
 				;;
 				;;
+			Tanglu)
+				# add the updates repository
+				if [ "$suite" = "$tangluLatest" ]; then
+					# ${suite}-updates only applies to stable Tanglu versions
+					sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list
+				fi
+				;;
+			SteamOS)
+				# add contrib and non-free
+				sudo sed -i "s/ $suite main$/ $suite main contrib non-free/" etc/apt/sources.list
+				;;
 		esac
 		esac
 	fi
 	fi
 	
 	
@@ -248,6 +261,28 @@ else
 					fi
 					fi
 				fi
 				fi
 				;;
 				;;
+			Tanglu)
+				if [ "$suite" = "$tangluLatest" ]; then
+					# tag latest
+					$docker tag $repo:$suite $repo:latest
+				fi
+				if [ -r etc/lsb-release ]; then
+					lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
+					if [ "$lsbRelease" ]; then
+						# tag specific Tanglu version number, if available (1.0, 2.0, etc.)
+						$docker tag $repo:$suite $repo:$lsbRelease
+					fi
+				fi
+				;;
+			SteamOS)
+				if [ -r etc/lsb-release ]; then
+					lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
+					if [ "$lsbRelease" ]; then
+						# tag specific SteamOS version number, if available (1.0, 2.0, etc.)
+						$docker tag $repo:$suite $repo:$lsbRelease
+					fi
+				fi
+				;;
 		esac
 		esac
 	fi
 	fi
 fi
 fi

+ 1 - 1
contrib/vagrant-docker/README.md

@@ -31,7 +31,7 @@ stop on runlevel [!2345]
 respawn
 respawn
 
 
 script
 script
-    /usr/bin/docker -d -H=tcp://0.0.0.0:4243/
+    /usr/bin/docker -d -H=tcp://0.0.0.0:4243
 end script
 end script
 ```
 ```
 
 

+ 5 - 1
docker/docker.go

@@ -6,8 +6,8 @@ import (
 	"os"
 	"os"
 	"strings"
 	"strings"
 
 
-	_ "github.com/dotcloud/docker"
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/api"
+	"github.com/dotcloud/docker/builtins"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	flag "github.com/dotcloud/docker/pkg/mflag"
@@ -39,6 +39,7 @@ func main() {
 		flDefaultIp          = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")
 		flDefaultIp          = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")
 		flInterContainerComm = flag.Bool([]string{"#icc", "-icc"}, true, "Enable inter-container communication")
 		flInterContainerComm = flag.Bool([]string{"#icc", "-icc"}, true, "Enable inter-container communication")
 		flGraphDriver        = flag.String([]string{"s", "-storage-driver"}, "", "Force the docker runtime to use a specific storage driver")
 		flGraphDriver        = flag.String([]string{"s", "-storage-driver"}, "", "Force the docker runtime to use a specific storage driver")
+		flExecDriver         = flag.String([]string{"e", "-exec-driver"}, "", "Force the docker runtime to use a specific exec driver")
 		flHosts              = opts.NewListOpts(api.ValidateHost)
 		flHosts              = opts.NewListOpts(api.ValidateHost)
 		flMtu                = flag.Int([]string{"#mtu", "-mtu"}, 0, "Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available")
 		flMtu                = flag.Int([]string{"#mtu", "-mtu"}, 0, "Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available")
 	)
 	)
@@ -81,6 +82,8 @@ func main() {
 		if err != nil {
 		if err != nil {
 			log.Fatal(err)
 			log.Fatal(err)
 		}
 		}
+		// Load builtins
+		builtins.Register(eng)
 		// load the daemon in the background so we can immediately start
 		// load the daemon in the background so we can immediately start
 		// the http api so that connections don't fail while the daemon
 		// the http api so that connections don't fail while the daemon
 		// is booting
 		// is booting
@@ -98,6 +101,7 @@ func main() {
 			job.Setenv("DefaultIp", *flDefaultIp)
 			job.Setenv("DefaultIp", *flDefaultIp)
 			job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
 			job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
 			job.Setenv("GraphDriver", *flGraphDriver)
 			job.Setenv("GraphDriver", *flGraphDriver)
+			job.Setenv("ExecDriver", *flExecDriver)
 			job.SetenvInt("Mtu", *flMtu)
 			job.SetenvInt("Mtu", *flMtu)
 			if err := job.Run(); err != nil {
 			if err := job.Run(); err != nil {
 				log.Fatal(err)
 				log.Fatal(err)

+ 0 - 8
docs/sources/contributing/devenvironment.rst

@@ -92,14 +92,6 @@ To execute the test cases, run this command:
 
 
 	sudo make test
 	sudo make test
 
 
-
-Note: if you're running the tests in vagrant, you need to specify a dns entry in 
-the command (either edit the Makefile, or run the step manually): 
-
-.. code-block:: bash
-
-	sudo docker run -dns 8.8.8.8 -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test
-
 If the test are successful then the tail of the output should look something like this
 If the test are successful then the tail of the output should look something like this
 
 
 .. code-block:: bash
 .. code-block:: bash

+ 3 - 3
docs/sources/faq.rst

@@ -25,9 +25,9 @@ Does Docker run on Mac OS X or Windows?
 
 
    Not at this time, Docker currently only runs on Linux, but you can
    Not at this time, Docker currently only runs on Linux, but you can
    use VirtualBox to run Docker in a virtual machine on your box, and
    use VirtualBox to run Docker in a virtual machine on your box, and
-   get the best of both worlds. Check out the
-   :ref:`macosx` and :ref:`windows` installation
-   guides.
+   get the best of both worlds. Check out the :ref:`macosx` and
+   :ref:`windows` installation guides. The small Linux distribution boot2docker
+   can be run inside virtual machines on these two operating systems.
 
 
 How do containers compare to virtual machines?
 How do containers compare to virtual machines?
 ..............................................
 ..............................................

+ 1 - 1
docs/sources/index.rst

@@ -24,6 +24,6 @@ For a high-level overview of Docker, please see the `Introduction
 Docker, we have a `quick start <http://www.docker.io/gettingstarted>`_
 Docker, we have a `quick start <http://www.docker.io/gettingstarted>`_
 and a more in-depth guide to :ref:`ubuntu_linux` and other
 and a more in-depth guide to :ref:`ubuntu_linux` and other
 :ref:`installation_list` paths including prebuilt binaries,
 :ref:`installation_list` paths including prebuilt binaries,
-Vagrant-created VMs, Rackspace and Amazon instances.
+Rackspace and Amazon instances.
 
 
 Enough reading! :ref:`Try it out! <running_examples>`
 Enough reading! :ref:`Try it out! <running_examples>`

+ 1 - 110
docs/sources/installation/amazon.rst

@@ -10,8 +10,7 @@ Amazon EC2
 There are several ways to install Docker on AWS EC2:
 There are several ways to install Docker on AWS EC2:
 
 
 * :ref:`amazonquickstart` or
 * :ref:`amazonquickstart` or
-* :ref:`amazonstandard` or
-* :ref:`amazonvagrant`
+* :ref:`amazonstandard`
 
 
 **You'll need an** `AWS account <http://aws.amazon.com/>`_ **first, of course.**
 **You'll need an** `AWS account <http://aws.amazon.com/>`_ **first, of course.**
 
 
@@ -73,112 +72,4 @@ running Ubuntu. Just follow Step 1 from :ref:`amazonquickstart` to
 pick an image (or use one of your own) and skip the step with the
 pick an image (or use one of your own) and skip the step with the
 *User Data*. Then continue with the :ref:`ubuntu_linux` instructions.
 *User Data*. Then continue with the :ref:`ubuntu_linux` instructions.
 
 
-.. _amazonvagrant:
-
-Use Vagrant
------------
-
-.. include:: install_unofficial.inc
-  
-And finally, if you prefer to work through Vagrant, you can install
-Docker that way too. Vagrant 1.1 or higher is required.
-
-1. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
-2. Install the vagrant aws plugin
-
-   ::
-
-       vagrant plugin install vagrant-aws
-
-
-3. Get the docker sources, this will give you the latest Vagrantfile.
-
-   ::
-
-      git clone https://github.com/dotcloud/docker.git
-
-
-4. Check your AWS environment.
-
-   Create a keypair specifically for EC2, give it a name and save it
-   to your disk. *I usually store these in my ~/.ssh/ folder*.
-
-   Check that your default security group has an inbound rule to
-   accept SSH (port 22) connections.
-
-5. Inform Vagrant of your settings
-
-   Vagrant will read your access credentials from your environment, so
-   we need to set them there first. Make sure you have everything on
-   amazon aws setup so you can (manually) deploy a new image to EC2.
-
-   Note that where possible these variables are the same as those honored by
-   the ec2 api tools.
-   ::
-
-       export AWS_ACCESS_KEY=xxx
-       export AWS_SECRET_KEY=xxx
-       export AWS_KEYPAIR_NAME=xxx
-       export SSH_PRIVKEY_PATH=xxx
-
-       export BOX_NAME=xxx
-       export AWS_REGION=xxx
-       export AWS_AMI=xxx
-       export AWS_INSTANCE_TYPE=xxx
-
-   The required environment variables are:
-
-   * ``AWS_ACCESS_KEY`` - The API key used to make requests to AWS
-   * ``AWS_SECRET_KEY`` - The secret key to make AWS API requests
-   * ``AWS_KEYPAIR_NAME`` - The name of the keypair used for this EC2 instance
-   * ``SSH_PRIVKEY_PATH`` - The path to the private key for the named
-     keypair, for example ``~/.ssh/docker.pem``
-
-   There are a number of optional environment variables:
-
-   * ``BOX_NAME`` - The name of the vagrant box to use.  Defaults to
-     ``ubuntu``.
-   * ``AWS_REGION`` - The aws region to spawn the vm in.  Defaults to
-     ``us-east-1``.
-   * ``AWS_AMI`` - The aws AMI to start with as a base.  This must be
-     be an ubuntu 12.04 precise image.  You must change this value if
-     ``AWS_REGION`` is set to a value other than ``us-east-1``.
-     This is because AMIs are region specific.  Defaults to ``ami-69f5a900``.
-   * ``AWS_INSTANCE_TYPE`` - The aws instance type.  Defaults to ``t1.micro``.
-
-   You can check if they are set correctly by doing something like
-
-   ::
-
-      echo $AWS_ACCESS_KEY
-
-6. Do the magic!
-
-   ::
-
-      vagrant up --provider=aws
-
-
-   If it stalls indefinitely on ``[default] Waiting for SSH to become
-   available...``, Double check your default security zone on AWS
-   includes rights to SSH (port 22) to your container.
-
-   If you have an advanced AWS setup, you might want to have a look at
-   `vagrant-aws <https://github.com/mitchellh/vagrant-aws>`_.
-
-7. Connect to your machine
-
-   .. code-block:: bash
-
-      vagrant ssh
-
-8. Your first command
-
-   Now you are in the VM, run docker
-
-   .. code-block:: bash
-
-      sudo docker
-
-
 Continue with the :ref:`hello_world` example.
 Continue with the :ref:`hello_world` example.

+ 31 - 182
docs/sources/installation/windows.rst

@@ -1,223 +1,72 @@
 :title: Installation on Windows
 :title: Installation on Windows
 :description: Please note this project is currently under heavy development. It should not be used in production.
 :description: Please note this project is currently under heavy development. It should not be used in production.
-:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
+:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, boot2docker
 
 
 .. _windows:
 .. _windows:
 
 
 Windows
 Windows
 =======
 =======
 
 
-Docker can run on Windows using a VM like VirtualBox. You then run
-Linux within the VM.
+Docker can run on Windows using a virtualization platform like VirtualBox. A Linux
+distribution is run inside a virtual machine and that's where Docker will run. 
 
 
 Installation
 Installation
 ------------
 ------------
 
 
 .. include:: install_header.inc
 .. include:: install_header.inc
 
 
-.. include:: install_unofficial.inc
+1. Install virtualbox from https://www.virtualbox.org - or follow this `tutorial <http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7>`_.
 
 
-1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__
+2. Download the latest boot2docker.iso from https://github.com/boot2docker/boot2docker/releases.
 
 
-.. __: http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7
+3. Start VirtualBox.
 
 
-2. Install vagrant from http://www.vagrantup.com - or follow this tutorial__
+4. Create a new Virtual machine with the following settings:
 
 
-.. __: http://www.slideshare.net/julienbarbier42/install-vagrant-on-windows-7
+ - `Name: boot2docker`
+ - `Type: Linux`
+ - `Version: Linux 2.6 (64 bit)`
+ - `Memory size: 1024 MB`
+ - `Hard drive: Do not add a virtual hard drive`
 
 
-3. Install git with ssh from http://git-scm.com/downloads - or follow this tutorial__
+5. Open the settings of the virtual machine:
 
 
-.. __: http://www.slideshare.net/julienbarbier42/install-git-with-ssh-on-windows-7
+   5.1. go to Storage
 
 
+   5.2. click the empty slot below `Controller: IDE`
 
 
-We recommend having at least 2Gb of free disk space and 2Gb of RAM (or more).
+   5.3. click the disc icon on the right of `IDE Secondary Master`
 
 
-Opening a command prompt
-------------------------
+   5.4. click `Choose a virtual CD/DVD disk file`
 
 
-First open a cmd prompt. Press Windows key and then press “R”
-key. This will open the RUN dialog box for you. Type “cmd” and press
-Enter. Or you can click on Start, type “cmd” in the “Search programs
-and files” field, and click on cmd.exe.
+6. Browse to the path where you've saved the `boot2docker.iso`, select the `boot2docker.iso` and click open.
 
 
-.. image:: images/win/_01.gif
-   :alt: Git install
-   :align: center
+7. Click OK on the Settings dialog to save the changes and close the window.
 
 
-This should open a cmd prompt window.
+8. Start the virtual machine by clicking the green start button.
 
 
-.. image:: images/win/_02.gif
-   :alt: run docker
-   :align: center
-
-Alternatively, you can also use a Cygwin terminal, or Git Bash (or any
-other command line program you are usually using). The next steps
-would be the same.
-
-.. _launch_ubuntu:
-
-Launch an Ubuntu virtual server
--------------------------------
-
-Let’s download and run an Ubuntu image with docker binaries already
-installed.
-
-.. code-block:: bash
-
-	git clone https://github.com/dotcloud/docker.git 
-	cd docker
-	vagrant up
-
-.. image:: images/win/run_02_.gif
-   :alt: run docker
-   :align: center
-
-Congratulations! You are running an Ubuntu server with docker
-installed on it. You do not see it though, because it is running in
-the background.
-
-Log onto your Ubuntu server
----------------------------
-
-Let’s log into your Ubuntu server now. To do so you have two choices:
-
-- Use Vagrant on Windows command prompt OR
-- Use SSH
-
-Using Vagrant on Windows Command Prompt
-```````````````````````````````````````
-
-Run the following command
-
-.. code-block:: bash
-
-	vagrant ssh
-
-You may see an error message starting with “`ssh` executable not
-found”. In this case it means that you do not have SSH in your
-PATH. If you do not have SSH in your PATH you can set it up with the
-“set” command. For instance, if your ssh.exe is in the folder named
-“C:\Program Files (x86)\Git\bin”, then you can run the following
-command:
-
-.. code-block:: bash
-
-	set PATH=%PATH%;C:\Program Files (x86)\Git\bin
-
-.. image:: images/win/run_03.gif
-   :alt: run docker
-   :align: center
-
-Using SSH
-`````````
-
-First step is to get the IP and port of your Ubuntu server. Simply run:
-
-.. code-block:: bash
-
-	vagrant ssh-config 
-
-You should see an output with HostName and Port information. In this
-example, HostName is 127.0.0.1 and port is 2222. And the User is
-“vagrant”. The password is not shown, but it is also “vagrant”.
-
-.. image:: images/win/ssh-config.gif
-   :alt: run docker
-   :align: center
-
-You can now use this information for connecting via SSH to your
-server. To do so you can:
-
-- Use putty.exe OR
-- Use SSH from a terminal
-
-Use putty.exe
-'''''''''''''
-
-You can download putty.exe from this page
-http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html Launch
-putty.exe and simply enter the information you got from last step.
-
-.. image:: images/win/putty.gif
-   :alt: run docker
-   :align: center
-
-Open, and enter user = vagrant and password = vagrant.
-
-.. image:: images/win/putty_2.gif
-   :alt: run docker
-   :align: center
-
-SSH from a terminal
-'''''''''''''''''''
-
-You can also run this command on your favorite terminal (windows
-prompt, cygwin, git-bash, …). Make sure to adapt the IP and port from
-what you got from the vagrant ssh-config command.
-
-.. code-block:: bash
-
-	ssh vagrant@127.0.0.1 –p 2222
-
-Enter user = vagrant and password = vagrant.
-
-.. image:: images/win/cygwin.gif
-   :alt: run docker
-   :align: center
-
-Congratulations, you are now logged onto your Ubuntu Server, running
-on top of your Windows machine !
+9. The boot2docker virtual machine should boot now.
 
 
 Running Docker
 Running Docker
 --------------
 --------------
 
 
-First you have to be root in order to run docker. Simply run the
-following command:
-
-.. code-block:: bash
+boot2docker will log you in automatically so you can start using Docker right
+away.
 
 
-	sudo su
-
-You are now ready for the docker’s “hello world” example. Run
+Let's try the “hello world” example. Run
 
 
 .. code-block:: bash
 .. code-block:: bash
 
 
 	docker run busybox echo hello world
 	docker run busybox echo hello world
 
 
-.. image:: images/win/run_04.gif
-   :alt: run docker
-   :align: center
-
-All done!
-
-Now you can continue with the :ref:`hello_world` example.
+This will download the small busybox image and print hello world.
 
 
-Troubleshooting
----------------
 
 
-VM does not boot
-````````````````
-
-.. image:: images/win/ts_go_bios.JPG
-
-If you run into this error message "The VM failed to remain in the
-'running' state while attempting to boot", please check that your
-computer has virtualization technology available and activated by
-going to the BIOS. Here's an example for an HP computer (System
-configuration / Device configuration)
-
-.. image:: images/win/hp_bios_vm.JPG
-
-On some machines the BIOS menu can only be accessed before startup.
-To access BIOS in this scenario you should restart your computer and 
-press ESC/Enter when prompted to access the boot and BIOS controls. Typically
-the option to allow virtualization is contained within the BIOS/Security menu.
-
-Docker is not installed
-```````````````````````
+Observations
+------------
 
 
-.. image:: images/win/ts_no_docker.JPG
+Persistent storage
+``````````````````
 
 
-If you run into this error message "The program 'docker' is currently
-not installed", try deleting the docker folder and restart from
-:ref:`launch_ubuntu`
+The virtual machine created above lacks any persistent data storage. All images
+and containers will be lost when shutting down or rebooting the VM.

+ 15 - 2
docs/sources/reference/api/docker_remote_api_v1.8.rst

@@ -118,6 +118,7 @@ Create a container
                 "User":"",
                 "User":"",
                 "Memory":0,
                 "Memory":0,
                 "MemorySwap":0,
                 "MemorySwap":0,
+                "CpuShares":0,
                 "AttachStdin":false,
                 "AttachStdin":false,
                 "AttachStdout":true,
                 "AttachStdout":true,
                 "AttachStderr":true,
                 "AttachStderr":true,
@@ -153,7 +154,15 @@ Create a container
                 "Warnings":[]
                 "Warnings":[]
            }
            }
 
 
-        :jsonparam config: the container's configuration
+        :jsonparam Hostname: Container host name
+        :jsonparam User: Username or UID
+        :jsonparam Memory: Memory Limit in bytes
+        :jsonparam CpuShares: CPU shares (relative weight)
+        :jsonparam AttachStdin: 1/True/true or 0/False/false, attach to standard input. Default false
+        :jsonparam AttachStdout: 1/True/true or 0/False/false, attach to standard output. Default false
+        :jsonparam AttachStderr: 1/True/true or 0/False/false, attach to standard error. Default false
+        :jsonparam Tty: 1/True/true or 0/False/false, allocate a pseudo-tty. Default false
+        :jsonparam OpenStdin: 1/True/true or 0/False/false, keep stdin open even if not attached. Default false
         :query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
         :query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
         :statuscode 201: no error
         :statuscode 201: no error
         :statuscode 404: no such container
         :statuscode 404: no such container
@@ -394,7 +403,11 @@ Start a container
            HTTP/1.1 204 No Content
            HTTP/1.1 204 No Content
            Content-Type: text/plain
            Content-Type: text/plain
 
 
-        :jsonparam hostConfig: the container's host configuration (optional)
+        :jsonparam Binds: Create a bind mount to a directory or file with [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume.
+        :jsonparam LxcConf: Map of custom lxc options
+        :jsonparam PortBindings: Expose ports from the container, optionally publishing them via the HostPort flag
+        :jsonparam PublishAllPorts: 1/True/true or 0/False/false, publish all exposed ports to the host interfaces. Default false
+        :jsonparam Privileged: 1/True/true or 0/False/false, give extended privileges to this container. Default false
         :statuscode 204: no error
         :statuscode 204: no error
         :statuscode 404: no such container
         :statuscode 404: no such container
         :statuscode 500: server error
         :statuscode 500: server error

+ 15 - 2
docs/sources/reference/api/docker_remote_api_v1.9.rst

@@ -118,6 +118,7 @@ Create a container
                 "User":"",
                 "User":"",
                 "Memory":0,
                 "Memory":0,
                 "MemorySwap":0,
                 "MemorySwap":0,
+                "CpuShares":0,
                 "AttachStdin":false,
                 "AttachStdin":false,
                 "AttachStdout":true,
                 "AttachStdout":true,
                 "AttachStderr":true,
                 "AttachStderr":true,
@@ -153,7 +154,15 @@ Create a container
                 "Warnings":[]
                 "Warnings":[]
            }
            }
 
 
-        :jsonparam config: the container's configuration
+        :jsonparam Hostname: Container host name
+        :jsonparam User: Username or UID
+        :jsonparam Memory: Memory Limit in bytes
+        :jsonparam CpuShares: CPU shares (relative weight)
+        :jsonparam AttachStdin: 1/True/true or 0/False/false, attach to standard input. Default false
+        :jsonparam AttachStdout: 1/True/true or 0/False/false, attach to standard output. Default false
+        :jsonparam AttachStderr: 1/True/true or 0/False/false, attach to standard error. Default false
+        :jsonparam Tty: 1/True/true or 0/False/false, allocate a pseudo-tty. Default false
+        :jsonparam OpenStdin: 1/True/true or 0/False/false, keep stdin open even if not attached. Default false
         :query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
         :query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
         :statuscode 201: no error
         :statuscode 201: no error
         :statuscode 404: no such container
         :statuscode 404: no such container
@@ -394,7 +403,11 @@ Start a container
            HTTP/1.1 204 No Content
            HTTP/1.1 204 No Content
            Content-Type: text/plain
            Content-Type: text/plain
 
 
-        :jsonparam hostConfig: the container's host configuration (optional)
+        :jsonparam Binds: Create a bind mount to a directory or file with [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume.
+        :jsonparam LxcConf: Map of custom lxc options
+        :jsonparam PortBindings: Expose ports from the container, optionally publishing them via the HostPort flag
+        :jsonparam PublishAllPorts: 1/True/true or 0/False/false, publish all exposed ports to the host interfaces. Default false
+        :jsonparam Privileged: 1/True/true or 0/False/false, give extended privileges to this container. Default false
         :statuscode 204: no error
         :statuscode 204: no error
         :statuscode 404: no such container
         :statuscode 404: no such container
         :statuscode 500: server error
         :statuscode 500: server error

+ 1 - 1
docs/sources/reference/builder.rst

@@ -74,7 +74,7 @@ When you're done with your build, you're ready to look into
 2. Format
 2. Format
 =========
 =========
 
 
-The Dockerfile format is quite simple:
+Here is the format of the Dockerfile:
 
 
 ::
 ::
 
 

+ 1 - 0
docs/sources/reference/commandline/cli.rst

@@ -79,6 +79,7 @@ Commands
       -p, --pidfile="/var/run/docker.pid": Path to use for daemon PID file
       -p, --pidfile="/var/run/docker.pid": Path to use for daemon PID file
       -r, --restart=true: Restart previously running containers
       -r, --restart=true: Restart previously running containers
       -s, --storage-driver="": Force the docker runtime to use a specific storage driver
       -s, --storage-driver="": Force the docker runtime to use a specific storage driver
+      -e, --exec-driver="": Force the docker runtime to use a specific exec driver
       -v, --version=false: Print version information and quit
       -v, --version=false: Print version information and quit
       --mtu=0: Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available
       --mtu=0: Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available
 
 

+ 43 - 0
engine/engine.go

@@ -1,6 +1,7 @@
 package engine
 package engine
 
 
 import (
 import (
+	"bufio"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
@@ -136,6 +137,48 @@ func (eng *Engine) Job(name string, args ...string) *Job {
 	return job
 	return job
 }
 }
 
 
+// ParseJob creates a new job from a text description using a shell-like syntax.
+//
+// The following syntax is used to parse `input`:
+//
+// * Words are separated using standard whitespaces as separators.
+// * Quotes and backslashes are not interpreted.
+// * Words of the form 'KEY=[VALUE]' are added to the job environment.
+// * All other words are added to the job arguments.
+//
+// For example:
+//
+// job, _ := eng.ParseJob("VERBOSE=1 echo hello TEST=true world")
+//
+// The resulting job will have:
+//	job.Args={"echo", "hello", "world"}
+//	job.Env={"VERBOSE":"1", "TEST":"true"}
+//
+func (eng *Engine) ParseJob(input string) (*Job, error) {
+	// FIXME: use a full-featured command parser
+	scanner := bufio.NewScanner(strings.NewReader(input))
+	scanner.Split(bufio.ScanWords)
+	var (
+		cmd []string
+		env Env
+	)
+	for scanner.Scan() {
+		word := scanner.Text()
+		kv := strings.SplitN(word, "=", 2)
+		if len(kv) == 2 {
+			env.Set(kv[0], kv[1])
+		} else {
+			cmd = append(cmd, word)
+		}
+	}
+	if len(cmd) == 0 {
+		return nil, fmt.Errorf("empty command: '%s'", input)
+	}
+	job := eng.Job(cmd[0], cmd[1:]...)
+	job.Env().Init(&env)
+	return job, nil
+}
+
 func (eng *Engine) Logf(format string, args ...interface{}) (n int, err error) {
 func (eng *Engine) Logf(format string, args ...interface{}) (n int, err error) {
 	if os.Getenv("TEST") == "" {
 	if os.Getenv("TEST") == "" {
 		prefixedFormat := fmt.Sprintf("[%s] %s\n", eng, strings.TrimRight(format, "\n"))
 		prefixedFormat := fmt.Sprintf("[%s] %s\n", eng, strings.TrimRight(format, "\n"))

+ 38 - 0
engine/engine_test.go

@@ -5,6 +5,7 @@ import (
 	"os"
 	"os"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
+	"strings"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -114,3 +115,40 @@ func TestEngineLogf(t *testing.T) {
 		t.Fatalf("Test: Logf() should print at least as much as the input\ninput=%d\nprinted=%d", len(input), n)
 		t.Fatalf("Test: Logf() should print at least as much as the input\ninput=%d\nprinted=%d", len(input), n)
 	}
 	}
 }
 }
+
+func TestParseJob(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	// Verify that the resulting job calls to the right place
+	var called bool
+	eng.Register("echo", func(job *Job) Status {
+		called = true
+		return StatusOK
+	})
+	input := "echo DEBUG=1 hello world VERBOSITY=42"
+	job, err := eng.ParseJob(input)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if job.Name != "echo" {
+		t.Fatalf("Invalid job name: %v", job.Name)
+	}
+	if strings.Join(job.Args, ":::") != "hello:::world" {
+		t.Fatalf("Invalid job args: %v", job.Args)
+	}
+	if job.Env().Get("DEBUG") != "1" {
+		t.Fatalf("Invalid job env: %v", job.Env)
+	}
+	if job.Env().Get("VERBOSITY") != "42" {
+		t.Fatalf("Invalid job env: %v", job.Env)
+	}
+	if len(job.Env().Map()) != 2 {
+		t.Fatalf("Invalid job env: %v", job.Env)
+	}
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+	if !called {
+		t.Fatalf("Job was not called")
+	}
+}

+ 7 - 0
engine/env.go

@@ -36,6 +36,13 @@ func (env *Env) Exists(key string) bool {
 	return exists
 	return exists
 }
 }
 
 
+func (env *Env) Init(src *Env) {
+	(*env) = make([]string, 0, len(*src))
+	for _, val := range *src {
+		(*env) = append((*env), val)
+	}
+}
+
 func (env *Env) GetBool(key string) (value bool) {
 func (env *Env) GetBool(key string) (value bool) {
 	s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
 	s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
 	if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
 	if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {

+ 9 - 2
engine/job.go

@@ -74,7 +74,7 @@ func (job *Job) Run() error {
 		return err
 		return err
 	}
 	}
 	if job.status != 0 {
 	if job.status != 0 {
-		return fmt.Errorf("%s: %s", job.Name, errorMessage)
+		return fmt.Errorf("%s", errorMessage)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -102,6 +102,10 @@ func (job *Job) String() string {
 	return fmt.Sprintf("%s.%s%s", job.Eng, job.CallString(), job.StatusString())
 	return fmt.Sprintf("%s.%s%s", job.Eng, job.CallString(), job.StatusString())
 }
 }
 
 
+func (job *Job) Env() *Env {
+	return job.env
+}
+
 func (job *Job) EnvExists(key string) (value bool) {
 func (job *Job) EnvExists(key string) (value bool) {
 	return job.env.Exists(key)
 	return job.env.Exists(key)
 }
 }
@@ -197,11 +201,14 @@ func (job *Job) Printf(format string, args ...interface{}) (n int, err error) {
 }
 }
 
 
 func (job *Job) Errorf(format string, args ...interface{}) Status {
 func (job *Job) Errorf(format string, args ...interface{}) Status {
+	if format[len(format)-1] != '\n' {
+		format = format + "\n"
+	}
 	fmt.Fprintf(job.Stderr, format, args...)
 	fmt.Fprintf(job.Stderr, format, args...)
 	return StatusErr
 	return StatusErr
 }
 }
 
 
 func (job *Job) Error(err error) Status {
 func (job *Job) Error(err error) Status {
-	fmt.Fprintf(job.Stderr, "%s", err)
+	fmt.Fprintf(job.Stderr, "%s\n", err)
 	return StatusErr
 	return StatusErr
 }
 }

+ 2 - 3
execdriver/lxc/driver.go

@@ -301,9 +301,8 @@ func (d *driver) Info(id string) execdriver.Info {
 func (d *driver) GetPidsForContainer(id string) ([]int, error) {
 func (d *driver) GetPidsForContainer(id string) ([]int, error) {
 	pids := []int{}
 	pids := []int{}
 
 
-	// memory is chosen randomly, any cgroup used by docker works
-	subsystem := "memory"
-
+	// cpu is chosen because it is the only non optional subsystem in cgroups
+	subsystem := "cpu"
 	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
 	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
 	if err != nil {
 	if err != nil {
 		return pids, err
 		return pids, err

+ 1 - 1
hack/make/test-integration

@@ -12,5 +12,5 @@ bundle_test_integration() {
 # this "grep" hides some really irritating warnings that "go test -coverpkg"
 # this "grep" hides some really irritating warnings that "go test -coverpkg"
 # spews when it is given packages that aren't used
 # spews when it is given packages that aren't used
 bundle_test_integration 2>&1 \
 bundle_test_integration 2>&1 \
-	| grep -v '^warning: no packages being tested depend on ' \
+	| grep --line-buffered -v '^warning: no packages being tested depend on ' \
 	| tee $DEST/test.log
 	| tee $DEST/test.log

+ 8 - 48
integration/runtime_test.go

@@ -123,19 +123,8 @@ func init() {
 }
 }
 
 
 func setupBaseImage() {
 func setupBaseImage() {
-	eng, err := engine.New(unitTestStoreBase)
-	if err != nil {
-		log.Fatalf("Can't initialize engine at %s: %s", unitTestStoreBase, err)
-	}
-	job := eng.Job("initserver")
-	job.Setenv("Root", unitTestStoreBase)
-	job.SetenvBool("Autorestart", false)
-	job.Setenv("BridgeIface", unitTestNetworkBridge)
-	if err := job.Run(); err != nil {
-		log.Fatalf("Unable to create a runtime for tests: %s", err)
-	}
-
-	job = eng.Job("inspect", unitTestImageName, "image")
+	eng := newTestEngine(log.New(os.Stderr, "", 0), false, unitTestStoreBase)
+	job := eng.Job("inspect", unitTestImageName, "image")
 	img, _ := job.Stdout.AddEnv()
 	img, _ := job.Stdout.AddEnv()
 	// If the unit test is not found, try to download it.
 	// If the unit test is not found, try to download it.
 	if err := job.Run(); err != nil || img.Get("id") != unitTestImageID {
 	if err := job.Run(); err != nil || img.Get("id") != unitTestImageID {
@@ -575,18 +564,7 @@ func TestRestore(t *testing.T) {
 
 
 	// Here are are simulating a docker restart - that is, reloading all containers
 	// Here are are simulating a docker restart - that is, reloading all containers
 	// from scratch
 	// from scratch
-	root := eng.Root()
-	eng, err := engine.New(root)
-	if err != nil {
-		t.Fatal(err)
-	}
-	job := eng.Job("initserver")
-	job.Setenv("Root", eng.Root())
-	job.SetenvBool("Autorestart", false)
-	if err := job.Run(); err != nil {
-		t.Fatal(err)
-	}
-
+	eng = newTestEngine(t, false, eng.Root())
 	runtime2 := mkRuntimeFromEngine(eng, t)
 	runtime2 := mkRuntimeFromEngine(eng, t)
 	if len(runtime2.List()) != 2 {
 	if len(runtime2.List()) != 2 {
 		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
 		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
@@ -612,22 +590,14 @@ func TestRestore(t *testing.T) {
 }
 }
 
 
 func TestReloadContainerLinks(t *testing.T) {
 func TestReloadContainerLinks(t *testing.T) {
-	// FIXME: here we don't use NewTestEngine because it calls initserver with Autorestart=false,
-	// and we want to set it to true.
 	root, err := newTestDirectory(unitTestStoreBase)
 	root, err := newTestDirectory(unitTestStoreBase)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	eng, err := engine.New(root)
-	if err != nil {
-		t.Fatal(err)
-	}
-	job := eng.Job("initserver")
-	job.Setenv("Root", eng.Root())
-	job.SetenvBool("Autorestart", true)
-	if err := job.Run(); err != nil {
-		t.Fatal(err)
-	}
+	// FIXME: here we don't use NewTestEngine because it calls initserver with Autorestart=false,
+	// and we want to set it to true.
+
+	eng := newTestEngine(t, true, root)
 
 
 	runtime1 := mkRuntimeFromEngine(eng, t)
 	runtime1 := mkRuntimeFromEngine(eng, t)
 	defer nuke(runtime1)
 	defer nuke(runtime1)
@@ -668,17 +638,7 @@ func TestReloadContainerLinks(t *testing.T) {
 
 
 	// Here are are simulating a docker restart - that is, reloading all containers
 	// Here are are simulating a docker restart - that is, reloading all containers
 	// from scratch
 	// from scratch
-	eng, err = engine.New(root)
-	if err != nil {
-		t.Fatal(err)
-	}
-	job = eng.Job("initserver")
-	job.Setenv("Root", eng.Root())
-	job.SetenvBool("Autorestart", false)
-	if err := job.Run(); err != nil {
-		t.Fatal(err)
-	}
-
+	eng = newTestEngine(t, false, root)
 	runtime2 := mkRuntimeFromEngine(eng, t)
 	runtime2 := mkRuntimeFromEngine(eng, t)
 	if len(runtime2.List()) != 2 {
 	if len(runtime2.List()) != 2 {
 		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
 		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))

+ 1 - 15
integration/server_test.go

@@ -2,7 +2,6 @@ package docker
 
 
 import (
 import (
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
-	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/runconfig"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
@@ -258,20 +257,7 @@ func TestRestartKillWait(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	eng, err = engine.New(eng.Root())
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	job = eng.Job("initserver")
-	job.Setenv("Root", eng.Root())
-	job.SetenvBool("AutoRestart", false)
-	// TestGetEnabledCors and TestOptionsRoute require EnableCors=true
-	job.SetenvBool("EnableCors", true)
-	if err := job.Run(); err != nil {
-		t.Fatal(err)
-	}
-
+	eng = newTestEngine(t, false, eng.Root())
 	srv = mkServerFromEngine(eng, t)
 	srv = mkServerFromEngine(eng, t)
 
 
 	job = srv.Eng.Job("containers")
 	job = srv.Eng.Job("containers")

+ 20 - 25
integration/utils_test.go

@@ -15,6 +15,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
+	"github.com/dotcloud/docker/builtins"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
@@ -27,26 +28,12 @@ import (
 // Create a temporary runtime suitable for unit testing.
 // Create a temporary runtime suitable for unit testing.
 // Call t.Fatal() at the first error.
 // Call t.Fatal() at the first error.
 func mkRuntime(f utils.Fataler) *docker.Runtime {
 func mkRuntime(f utils.Fataler) *docker.Runtime {
-	root, err := newTestDirectory(unitTestStoreBase)
-	if err != nil {
-		f.Fatal(err)
-	}
-	config := &docker.DaemonConfig{
-		Root:        root,
-		AutoRestart: false,
-		Mtu:         docker.GetDefaultNetworkMtu(),
-	}
-
-	eng, err := engine.New(root)
-	if err != nil {
-		f.Fatal(err)
-	}
-
-	r, err := docker.NewRuntimeFromDirectory(config, eng)
-	if err != nil {
-		f.Fatal(err)
-	}
-	return r
+	eng := newTestEngine(f, false, "")
+	return mkRuntimeFromEngine(eng, f)
+	// FIXME:
+	// [...]
+	// Mtu:         docker.GetDefaultNetworkMtu(),
+	// [...]
 }
 }
 
 
 func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler, name string) (shortId string) {
 func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler, name string) (shortId string) {
@@ -185,20 +172,24 @@ func mkRuntimeFromEngine(eng *engine.Engine, t utils.Fataler) *docker.Runtime {
 	return runtime
 	return runtime
 }
 }
 
 
-func NewTestEngine(t utils.Fataler) *engine.Engine {
-	root, err := newTestDirectory(unitTestStoreBase)
-	if err != nil {
-		t.Fatal(err)
+func newTestEngine(t utils.Fataler, autorestart bool, root string) *engine.Engine {
+	if root == "" {
+		if dir, err := newTestDirectory(unitTestStoreBase); err != nil {
+			t.Fatal(err)
+		} else {
+			root = dir
+		}
 	}
 	}
 	eng, err := engine.New(root)
 	eng, err := engine.New(root)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	// Load default plugins
 	// Load default plugins
+	builtins.Register(eng)
 	// (This is manually copied and modified from main() until we have a more generic plugin system)
 	// (This is manually copied and modified from main() until we have a more generic plugin system)
 	job := eng.Job("initserver")
 	job := eng.Job("initserver")
 	job.Setenv("Root", root)
 	job.Setenv("Root", root)
-	job.SetenvBool("AutoRestart", false)
+	job.SetenvBool("AutoRestart", autorestart)
 	// TestGetEnabledCors and TestOptionsRoute require EnableCors=true
 	// TestGetEnabledCors and TestOptionsRoute require EnableCors=true
 	job.SetenvBool("EnableCors", true)
 	job.SetenvBool("EnableCors", true)
 	if err := job.Run(); err != nil {
 	if err := job.Run(); err != nil {
@@ -207,6 +198,10 @@ func NewTestEngine(t utils.Fataler) *engine.Engine {
 	return eng
 	return eng
 }
 }
 
 
+func NewTestEngine(t utils.Fataler) *engine.Engine {
+	return newTestEngine(t, false, "")
+}
+
 func newTestDirectory(templateDir string) (dir string, err error) {
 func newTestDirectory(templateDir string) (dir string, err error) {
 	return utils.TestDirectory(templateDir)
 	return utils.TestDirectory(templateDir)
 }
 }

+ 1 - 1
links/links.go

@@ -72,7 +72,7 @@ func (l *Link) ToEnv() []string {
 			if len(parts) != 2 {
 			if len(parts) != 2 {
 				continue
 				continue
 			}
 			}
-			// Ignore a few variables that are added during docker build
+			// Ignore a few variables that are added during docker build (and not really relevant to linked containers)
 			if parts[0] == "HOME" || parts[0] == "PATH" {
 			if parts[0] == "HOME" || parts[0] == "PATH" {
 				continue
 				continue
 			}
 			}

+ 0 - 6
networkdriver/lxc/driver.go

@@ -57,12 +57,6 @@ var (
 	currentInterfaces = make(map[string]*networkInterface)
 	currentInterfaces = make(map[string]*networkInterface)
 )
 )
 
 
-func init() {
-	if err := engine.Register("init_networkdriver", InitDriver); err != nil {
-		panic(err)
-	}
-}
-
 func InitDriver(job *engine.Job) engine.Status {
 func InitDriver(job *engine.Job) engine.Status {
 	var (
 	var (
 		network        *net.IPNet
 		network        *net.IPNet

+ 11 - 2
runtime.go

@@ -8,7 +8,7 @@ import (
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/execdriver"
 	"github.com/dotcloud/docker/execdriver"
 	"github.com/dotcloud/docker/execdriver/docker"
 	"github.com/dotcloud/docker/execdriver/docker"
-	_ "github.com/dotcloud/docker/execdriver/lxc"
+	"github.com/dotcloud/docker/execdriver/lxc"
 	"github.com/dotcloud/docker/graphdriver"
 	"github.com/dotcloud/docker/graphdriver"
 	"github.com/dotcloud/docker/graphdriver/aufs"
 	"github.com/dotcloud/docker/graphdriver/aufs"
 	_ "github.com/dotcloud/docker/graphdriver/btrfs"
 	_ "github.com/dotcloud/docker/graphdriver/btrfs"
@@ -704,7 +704,16 @@ func NewRuntimeFromDirectory(config *DaemonConfig, eng *engine.Engine) (*Runtime
 
 
 	sysInfo := sysinfo.New(false)
 	sysInfo := sysinfo.New(false)
 
 
-	ed, err := docker.NewDriver(config.Root)
+	var ed execdriver.Driver
+	utils.Debugf("execDriver: provided %s", config.ExecDriver)
+	if config.ExecDriver == "chroot" && false {
+		// chroot is presently a noop driver https://github.com/dotcloud/docker/pull/4189#issuecomment-35330655
+		ed, err = chroot.NewDriver()
+		utils.Debugf("execDriver: using chroot")
+	} else {
+		ed, err = lxc.NewDriver(config.Root, sysInfo.AppArmor)
+		utils.Debugf("execDriver: using lxc")
+	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 1 - 5
server.go

@@ -34,14 +34,10 @@ func (srv *Server) Close() error {
 	return srv.runtime.Close()
 	return srv.runtime.Close()
 }
 }
 
 
-func init() {
-	engine.Register("initserver", jobInitServer)
-}
-
 // jobInitApi runs the remote api server `srv` as a daemon,
 // jobInitApi runs the remote api server `srv` as a daemon,
 // Only one api server can run at the same time - this is enforced by a pidfile.
 // Only one api server can run at the same time - this is enforced by a pidfile.
 // The signals SIGINT, SIGQUIT and SIGTERM are intercepted for cleanup.
 // The signals SIGINT, SIGQUIT and SIGTERM are intercepted for cleanup.
-func jobInitServer(job *engine.Job) engine.Status {
+func InitServer(job *engine.Job) engine.Status {
 	job.Logf("Creating server")
 	job.Logf("Creating server")
 	srv, err := NewServer(job.Eng, DaemonConfigFromJob(job))
 	srv, err := NewServer(job.Eng, DaemonConfigFromJob(job))
 	if err != nil {
 	if err != nil {

+ 1 - 5
version.go

@@ -7,11 +7,7 @@ import (
 	"runtime"
 	"runtime"
 )
 )
 
 
-func init() {
-	engine.Register("version", jobVersion)
-}
-
-func jobVersion(job *engine.Job) engine.Status {
+func GetVersion(job *engine.Job) engine.Status {
 	if _, err := dockerVersion().WriteTo(job.Stdout); err != nil {
 	if _, err := dockerVersion().WriteTo(job.Stdout); err != nil {
 		job.Errorf("%s", err)
 		job.Errorf("%s", err)
 		return engine.StatusErr
 		return engine.StatusErr