Merge remote-tracking branch 'origin/registry-update'
+ Registry: Add the new registry support
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 72bf381..93c3997 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,13 @@
 # Changelog
 
-## 0.2.1 (2012-05-01)
+## 0.2.2 (2013-05-03)
+ + Support for data volumes ('docker run -v=PATH')
+ + Share data volumes between containers ('docker run -volumes-from')
+ + Improved documentation
+ * Upgrade to Go 1.0.3
+ * Various upgrades to the dev environment for contributors
+
+## 0.2.1 (2013-05-01)
  + 'docker commit -run' bundles a layer with default runtime options: command, ports etc. 
  * Improve install process on Vagrant
  + New Dockerfile operation: "maintainer"
@@ -10,7 +17,7 @@
  + 'docker -d -r': restart crashed containers at daemon startup
  * Runtime: improve test coverage
 
-## 0.2.0 (2012-04-23)
+## 0.2.0 (2013-04-23)
  - Runtime: ghost containers can be killed and waited for
  * Documentation: update install intructions
  - Packaging: fix Vagrantfile
diff --git a/Vagrantfile b/Vagrantfile
index 95bd8c0..06e8b47 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -1,8 +1,8 @@
 # -*- mode: ruby -*-
 # vi: set ft=ruby :
 
-BOX_NAME = "ubuntu"
-BOX_URI = "http://files.vagrantup.com/precise64.box"
+BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
+BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
 PPA_KEY = "E61D797F63561DC6"
 
 Vagrant::Config.run do |config|
diff --git a/commands.go b/commands.go
index 06443b5..4812599 100644
--- a/commands.go
+++ b/commands.go
@@ -10,6 +10,7 @@
 	"log"
 	"net/http"
 	"net/url"
+	"path/filepath"
 	"runtime"
 	"strconv"
 	"strings"
@@ -18,7 +19,7 @@
 	"unicode"
 )
 
-const VERSION = "0.2.1"
+const VERSION = "0.2.2"
 
 var (
 	GIT_COMMIT string
@@ -400,7 +401,8 @@
 }
 
 func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
-	cmd := rcli.Subcmd(stdout, "rm", "CONTAINER [CONTAINER...]", "Remove a container")
+	cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container")
+	v := cmd.Bool("v", false, "Remove the volumes associated to the container")
 	if err := cmd.Parse(args); err != nil {
 		return nil
 	}
@@ -408,15 +410,40 @@
 		cmd.Usage()
 		return nil
 	}
+	volumes := make(map[string]struct{})
 	for _, name := range cmd.Args() {
 		container := srv.runtime.Get(name)
 		if container == nil {
 			return fmt.Errorf("No such container: %s", name)
 		}
+		// Store all the deleted containers volumes
+		for _, volumeId := range container.Volumes {
+			volumes[volumeId] = struct{}{}
+		}
 		if err := srv.runtime.Destroy(container); err != nil {
 			fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
 		}
 	}
+	if *v {
+		// Retrieve all volumes from all remaining containers
+		usedVolumes := make(map[string]*Container)
+		for _, container := range srv.runtime.List() {
+			for _, containerVolumeId := range container.Volumes {
+				usedVolumes[containerVolumeId] = container
+			}
+		}
+
+		for volumeId := range volumes {
+			// If the requested volu
+			if c, exists := usedVolumes[volumeId]; exists {
+				fmt.Fprintf(stdout, "The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
+				continue
+			}
+			if err := srv.runtime.volumes.Delete(volumeId); err != nil {
+				return err
+			}
+		}
+	}
 	return nil
 }
 
@@ -918,6 +945,25 @@
 	return false
 }
 
+// PathOpts stores a unique set of absolute paths
+type PathOpts map[string]struct{}
+
+func NewPathOpts() PathOpts {
+	return make(PathOpts)
+}
+
+func (opts PathOpts) String() string {
+	return fmt.Sprintf("%v", map[string]struct{}(opts))
+}
+
+func (opts PathOpts) Set(val string) error {
+	if !filepath.IsAbs(val) {
+		return fmt.Errorf("%s is not an absolute path", val)
+	}
+	opts[filepath.Clean(val)] = struct{}{}
+	return nil
+}
+
 func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
 	cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
 	force := cmd.Bool("f", false, "Force")
diff --git a/container.go b/container.go
index 5040203..311e475 100644
--- a/container.go
+++ b/container.go
@@ -48,6 +48,7 @@
 	runtime *Runtime
 
 	waitLock chan struct{}
+	Volumes  map[string]string
 }
 
 type Config struct {
@@ -66,6 +67,8 @@
 	Cmd          []string
 	Dns          []string
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
+	Volumes      map[string]struct{}
+	VolumesFrom  string
 }
 
 func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) {
@@ -97,6 +100,11 @@
 	var flDns ListOpts
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
 
+	flVolumes := NewPathOpts()
+	cmd.Var(flVolumes, "v", "Attach a data volume")
+
+	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
+
 	if err := cmd.Parse(args); err != nil {
 		return nil, err
 	}
@@ -136,6 +144,8 @@
 		Cmd:          runCmd,
 		Dns:          flDns,
 		Image:        image,
+		Volumes:      flVolumes,
+		VolumesFrom:  *flVolumesFrom,
 	}
 
 	if *flMemory > 0 && !capabilities.SwapLimit {
@@ -394,10 +404,40 @@
 		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
 		container.Config.MemorySwap = -1
 	}
+	container.Volumes = make(map[string]string)
+
+	// Create the requested volumes volumes
+	for volPath := range container.Config.Volumes {
+		if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
+			return err
+		} else {
+			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
+				return nil
+			}
+			container.Volumes[volPath] = c.Id
+		}
+	}
+
+	if container.Config.VolumesFrom != "" {
+		c := container.runtime.Get(container.Config.VolumesFrom)
+		if c == nil {
+			return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
+		}
+		for volPath, id := range c.Volumes {
+			if _, exists := container.Volumes[volPath]; exists {
+				return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
+			}
+			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
+				return nil
+			}
+			container.Volumes[volPath] = id
+		}
+	}
 
 	if err := container.generateLXCConfig(); err != nil {
 		return err
 	}
+
 	params := []string{
 		"-n", container.Id,
 		"-f", container.lxcConfigPath(),
@@ -456,6 +496,7 @@
 
 	// Init the lock
 	container.waitLock = make(chan struct{})
+
 	container.ToDisk()
 	go container.monitor()
 	return nil
@@ -795,6 +836,22 @@
 	return path.Join(container.root, "rootfs")
 }
 
+func (container *Container) GetVolumes() (map[string]string, error) {
+	ret := make(map[string]string)
+	for volPath, id := range container.Volumes {
+		volume, err := container.runtime.volumes.Get(id)
+		if err != nil {
+			return nil, err
+		}
+		root, err := volume.root()
+		if err != nil {
+			return nil, err
+		}
+		ret[volPath] = path.Join(root, "layer")
+	}
+	return ret, nil
+}
+
 func (container *Container) rwPath() string {
 	return path.Join(container.root, "rw")
 }
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..9d1ce18
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,2 @@
+Sphinx==1.1.3
+sphinxcontrib-httpdomain==1.1.8
\ No newline at end of file
diff --git a/docs/sources/commandline/command/run.rst b/docs/sources/commandline/command/run.rst
index c2096b3..d5e571b 100644
--- a/docs/sources/commandline/command/run.rst
+++ b/docs/sources/commandline/command/run.rst
@@ -17,3 +17,6 @@
       -p=[]: Map a network port to the container
       -t=false: Allocate a pseudo-tty
       -u="": Username or UID
+      -d=[]: Set custom dns servers for the container
+      -v=[]: Creates a new volumes and mount it at the specified path.
+      -volumes-from="": Mount all volumes from the given container.
diff --git a/docs/sources/commandline/index.rst b/docs/sources/commandline/index.rst
index d5ea959..d19d39a 100644
--- a/docs/sources/commandline/index.rst
+++ b/docs/sources/commandline/index.rst
@@ -9,7 +9,7 @@
 Contents:
 
 .. toctree::
-  :maxdepth: 2
+  :maxdepth: 3
 
   basics
   workingwithrepository
diff --git a/docs/sources/conf.py b/docs/sources/conf.py
index c05c7d1..4c54d8b 100644
--- a/docs/sources/conf.py
+++ b/docs/sources/conf.py
@@ -25,7 +25,7 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
+extensions = ['sphinxcontrib.httpdomain']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
diff --git a/docs/sources/examples/couchdb_data_volumes.rst b/docs/sources/examples/couchdb_data_volumes.rst
new file mode 100644
index 0000000..5b50c73
--- /dev/null
+++ b/docs/sources/examples/couchdb_data_volumes.rst
@@ -0,0 +1,53 @@
+:title: Sharing data between 2 couchdb databases
+:description: Sharing data between 2 couchdb databases
+:keywords: docker, example, package installation, networking, couchdb, data volumes
+
+.. _running_redis_service:
+
+Create a redis service
+======================
+
+.. include:: example_header.inc
+
+Here's an example of using data volumes to share the same data between 2 couchdb containers.
+This could be used for hot upgrades, testing different versions of couchdb on the same data, etc.
+
+Create first database
+---------------------
+
+Note that we're marking /var/lib/couchdb as a data volume.
+
+.. code-block:: bash
+
+    COUCH1=$(docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
+
+Add data to the first database
+------------------------------
+
+We're assuming your docker host is reachable at `localhost`. If not, replace `localhost` with the public IP of your docker host.
+
+.. code-block:: bash
+
+    HOST=localhost
+    URL="http://$HOST:$(docker port $COUCH1 5984)/_utils/"
+    echo "Navigate to $URL in your browser, and use the couch interface to add data"
+
+Create second database
+----------------------
+
+This time, we're requesting shared access to $COUCH1's volumes.
+
+.. code-block:: bash
+
+    COUCH2=$(docker run -d -volumes-from $COUCH1) shykes/couchdb:2013-05-03)
+
+Browse data on the second database
+----------------------------------
+
+.. code-block:: bash
+
+    HOST=localhost
+    URL="http://$HOST:$(docker port $COUCH2 5984)/_utils/"
+    echo "Navigate to $URL in your browser. You should see the same data as in the first database!"
+
+Congratulations, you are running 2 Couchdb containers, completely isolated from each other *except* for their data.
diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst
index 6a616ec..7eb2ecb 100644
--- a/docs/sources/examples/index.rst
+++ b/docs/sources/examples/index.rst
@@ -18,3 +18,4 @@
    python_web_app
    running_redis_service
    running_ssh_service
+   couchdb_data_volumes
diff --git a/docs/sources/index.rst b/docs/sources/index.rst
index 4e724a0..e6a1482 100644
--- a/docs/sources/index.rst
+++ b/docs/sources/index.rst
@@ -16,6 +16,7 @@
    contributing/index
    commandline/index
    registry/index
+   index/index
    faq
 
 
diff --git a/docs/sources/index/index.rst b/docs/sources/index/index.rst
new file mode 100644
index 0000000..7637a4e
--- /dev/null
+++ b/docs/sources/index/index.rst
@@ -0,0 +1,15 @@
+:title: Docker Index documentation
+:description: Documentation for docker Index
+:keywords: docker, index, api
+
+
+
+Index
+=====
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   search
diff --git a/docs/sources/index/search.rst b/docs/sources/index/search.rst
new file mode 100644
index 0000000..498295f
--- /dev/null
+++ b/docs/sources/index/search.rst
@@ -0,0 +1,38 @@
+=======================
+Docker Index Search API
+=======================
+
+Search
+------
+
+.. http:get:: /v1/search
+
+   Search the Index given a search term. It accepts :http:method:`get` only.
+
+   **Example request**:
+
+   .. sourcecode:: http
+
+      GET /v1/search?q=search_term HTTP/1.1
+      Host: example.com
+      Accept: application/json
+
+   **Example response**:
+
+   .. sourcecode:: http
+
+      HTTP/1.1 200 OK
+      Vary: Accept
+      Content-Type: application/json
+
+      {"query":"search_term",
+        "num_results": 2,
+        "results" : [
+           {"name": "dotcloud/base", "description": "A base ubuntu64  image..."},
+           {"name": "base2", "description": "A base ubuntu64  image..."},
+         ]
+       }
+   
+   :query q: what you want to search for
+   :statuscode 200: no error
+   :statuscode 500: server error
\ No newline at end of file
diff --git a/docs/sources/registry/api.rst b/docs/sources/registry/api.rst
index 1cca9fb..ec2591a 100644
--- a/docs/sources/registry/api.rst
+++ b/docs/sources/registry/api.rst
@@ -84,7 +84,9 @@
 
 Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage.
 
-Token is only returned when it is a private repo, public repos do not require tokens to be returned. The Registry will still contact the Index to make sure the pull is authorized (“is it ok to download this repos without a Token?”).
+Token is only returned when the 'X-Docker-Token' header is sent with request.
+
+Basic Auth is required to pull private repos. Basic auth isn't required for pulling public repos, but if one is provided, it needs to be valid and for an active account.
 
 API (pulling repository foo/bar):
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -426,6 +428,8 @@
         - X-Docker-Token: true
 
     In this case, along with the 200 response, you’ll get a new token (if user auth is ok):
+    If authorization isn't correct you get a 401 response.
+    If account isn't active you will get a 403 response.
 
     **Response**:
         - 200 OK
diff --git a/lxc_template.go b/lxc_template.go
index 5ac62f5..e2be3f2 100644
--- a/lxc_template.go
+++ b/lxc_template.go
@@ -79,7 +79,11 @@
 
 # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
 lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
-
+{{if .Volumes}}
+{{range $virtualPath, $realPath := .GetVolumes}}
+lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
+{{end}}
+{{end}}
 
 # drop linux capabilities (apply mainly to the user root in the container)
 lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
diff --git a/packaging/ubuntu/changelog b/packaging/ubuntu/changelog
index 88f6c50..49eabfb 100644
--- a/packaging/ubuntu/changelog
+++ b/packaging/ubuntu/changelog
@@ -1,3 +1,12 @@
+lxc-docker (0.2.2-1) precise; urgency=low
+ - Support for data volumes ('docker run -v=PATH')
+ - Share data volumes between containers ('docker run -volumes-from')
+ - Improved documentation
+ - Upgrade to Go 1.0.3
+ - Various upgrades to the dev environment for contributors
+
+ -- dotCloud <ops@dotcloud.com>  Fri, 3 May 2013 00:00:00 -0700
+
 
 lxc-docker (0.2.1-1) precise; urgency=low
 
diff --git a/runtime.go b/runtime.go
index 6e03226..79a7170 100644
--- a/runtime.go
+++ b/runtime.go
@@ -32,6 +32,7 @@
 	capabilities   *Capabilities
 	kernelVersion  *KernelVersionInfo
 	autoRestart    bool
+	volumes        *Graph
 }
 
 var sysInitPath string
@@ -79,10 +80,10 @@
 }
 
 func (runtime *Runtime) mergeConfig(userConf, imageConf *Config) {
-	if userConf.Hostname != "" {
+	if userConf.Hostname == "" {
 		userConf.Hostname = imageConf.Hostname
 	}
-	if userConf.User != "" {
+	if userConf.User == "" {
 		userConf.User = imageConf.User
 	}
 	if userConf.Memory == 0 {
@@ -126,7 +127,7 @@
 		runtime.mergeConfig(config, img.Config)
 	}
 
-	if config.Cmd == nil {
+	if config.Cmd == nil || len(config.Cmd) == 0 {
 		return nil, fmt.Errorf("No command specified")
 	}
 
@@ -405,6 +406,10 @@
 	if err != nil {
 		return nil, err
 	}
+	volumes, err := NewGraph(path.Join(root, "volumes"))
+	if err != nil {
+		return nil, err
+	}
 	repositories, err := NewTagStore(path.Join(root, "repositories"), g)
 	if err != nil {
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
@@ -432,6 +437,7 @@
 		idIndex:        NewTruncIndex(),
 		capabilities:   &Capabilities{},
 		autoRestart:    autoRestart,
+		volumes:        volumes,
 	}
 
 	if err := runtime.restore(); err != nil {
diff --git a/utils.go b/utils.go
index 78e13ed..229b938 100644
--- a/utils.go
+++ b/utils.go
@@ -456,7 +456,7 @@
 	// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
 	for _, line := range strings.Split(string(output), "\n") {
 		parts := strings.Split(line, " ")
-		if parts[2] == "cgroup" {
+		if len(parts) == 6 && parts[2] == "cgroup" {
 			for _, opt := range strings.Split(parts[3], ",") {
 				if opt == cgroupType {
 					return parts[1], nil
@@ -466,4 +466,4 @@
 	}
 
 	return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
-}
\ No newline at end of file
+}