소스 검색

Merge branch 'master' into add_unix_socket-feature

Victor Vieux 12 년 전
부모
커밋
d52af3f58f

+ 0 - 1
FIXME

@@ -16,7 +16,6 @@ to put them - so we put them here :)
 * Unify build commands and regular commands
 * Unify build commands and regular commands
 * Move source code into src/ subdir for clarity
 * Move source code into src/ subdir for clarity
 * Clean up the Makefile, it's a mess
 * Clean up the Makefile, it's a mess
-* docker buidl: show short IDs
 * docker build: on non-existent local path for ADD, don't show full absolute path on the host
 * docker build: on non-existent local path for ADD, don't show full absolute path on the host
 * mount into /dockerinit rather than /sbin/init
 * mount into /dockerinit rather than /sbin/init
 * docker tag foo REPO:TAG
 * docker tag foo REPO:TAG

+ 2 - 2
README.md

@@ -181,7 +181,7 @@ Running an irc bouncer
 ----------------------
 ----------------------
 
 
 ```bash
 ```bash
-BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc $USER $PASSWORD)
+BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc zncrun $USER $PASSWORD)
 echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine"
 echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine"
 ```
 ```
 
 
@@ -262,7 +262,7 @@ Setting up a dev environment
 Instructions that have been verified to work on Ubuntu 12.10,
 Instructions that have been verified to work on Ubuntu 12.10,
 
 
 ```bash
 ```bash
-sudo apt-get -y install lxc wget bsdtar curl golang git
+sudo apt-get -y install lxc curl xz-utils golang git
 
 
 export GOPATH=~/go/
 export GOPATH=~/go/
 export PATH=$GOPATH/bin:$PATH
 export PATH=$GOPATH/bin:$PATH

+ 8 - 0
Vagrantfile

@@ -3,6 +3,7 @@
 
 
 BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
 BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
 BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
 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_REGION = ENV['AWS_REGION'] || "us-east-1"
 AWS_REGION = ENV['AWS_REGION'] || "us-east-1"
 AWS_AMI    = ENV['AWS_AMI']    || "ami-d0f89fb9"
 AWS_AMI    = ENV['AWS_AMI']    || "ami-d0f89fb9"
 FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS']
 FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS']
@@ -67,6 +68,13 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
     rs.image    = /Ubuntu/
     rs.image    = /Ubuntu/
   end
   end
 
 
+  config.vm.provider :vmware_fusion do |f, override|
+    override.vm.box = BOX_NAME
+    override.vm.box_url = VF_BOX_URI
+    override.vm.synced_folder ".", "/vagrant", disabled: true
+    f.vmx["displayName"] = "docker"
+  end
+
   config.vm.provider :virtualbox do |vb|
   config.vm.provider :virtualbox do |vb|
     config.vm.box = BOX_NAME
     config.vm.box = BOX_NAME
     config.vm.box_url = BOX_URI
     config.vm.box_url = BOX_URI

+ 2 - 1
api_params.go

@@ -1,7 +1,8 @@
 package docker
 package docker
 
 
 type APIHistory struct {
 type APIHistory struct {
-	ID        string `json:"Id"`
+	ID        string   `json:"Id"`
+	Tags      []string `json:",omitempty"`
 	Created   int64
 	Created   int64
 	CreatedBy string `json:",omitempty"`
 	CreatedBy string `json:",omitempty"`
 }
 }

+ 46 - 4
archive.go

@@ -1,8 +1,10 @@
 package docker
 package docker
 
 
 import (
 import (
+	"bufio"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
@@ -20,6 +22,37 @@ const (
 	Xz
 	Xz
 )
 )
 
 
+func DetectCompression(source []byte) Compression {
+	for _, c := range source[:10] {
+		utils.Debugf("%x", c)
+	}
+
+	sourceLen := len(source)
+	for compression, m := range map[Compression][]byte{
+		Bzip2: {0x42, 0x5A, 0x68},
+		Gzip:  {0x1F, 0x8B, 0x08},
+		Xz:    {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
+	} {
+		fail := false
+		if len(m) > sourceLen {
+			utils.Debugf("Len too short")
+			continue
+		}
+		i := 0
+		for _, b := range m {
+			if b != source[i] {
+				fail = true
+				break
+			}
+			i++
+		}
+		if !fail {
+			return compression
+		}
+	}
+	return Uncompressed
+}
+
 func (compression *Compression) Flag() string {
 func (compression *Compression) Flag() string {
 	switch *compression {
 	switch *compression {
 	case Bzip2:
 	case Bzip2:
@@ -47,14 +80,23 @@ func (compression *Compression) Extension() string {
 }
 }
 
 
 func Tar(path string, compression Compression) (io.Reader, error) {
 func Tar(path string, compression Compression) (io.Reader, error) {
-	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")
-	return CmdStream(cmd)
+	return CmdStream(exec.Command("tar", "-f", "-", "-C", path, "-c"+compression.Flag(), "."))
 }
 }
 
 
 // FIXME: specify behavior when target path exists vs. doesn't exist.
 // FIXME: specify behavior when target path exists vs. doesn't exist.
 func Untar(archive io.Reader, path string) error {
 func Untar(archive io.Reader, path string) error {
-	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
-	cmd.Stdin = archive
+
+	bufferedArchive := bufio.NewReaderSize(archive, 10)
+	buf, err := bufferedArchive.Peek(10)
+	if err != nil {
+		return err
+	}
+	compression := DetectCompression(buf)
+
+	utils.Debugf("Archive compression detected: %s", compression.Extension())
+
+	cmd := exec.Command("tar", "-f", "-", "-C", path, "-x"+compression.Flag())
+	cmd.Stdin = bufferedArchive
 	// Hardcode locale environment for predictable outcome regardless of host configuration.
 	// Hardcode locale environment for predictable outcome regardless of host configuration.
 	//   (see https://github.com/dotcloud/docker/issues/355)
 	//   (see https://github.com/dotcloud/docker/issues/355)
 	cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
 	cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}

+ 46 - 5
archive_test.go

@@ -1,10 +1,13 @@
 package docker
 package docker
 
 
 import (
 import (
+	"bytes"
+	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
+	"path"
 	"testing"
 	"testing"
 	"time"
 	"time"
 )
 )
@@ -58,20 +61,58 @@ func TestCmdStreamGood(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestTarUntar(t *testing.T) {
-	archive, err := Tar(".", Uncompressed)
+func tarUntar(t *testing.T, origin string, compression Compression) error {
+	archive, err := Tar(origin, compression)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+
+	buf := make([]byte, 10)
+	if _, err := archive.Read(buf); err != nil {
+		return err
+	}
+	archive = io.MultiReader(bytes.NewReader(buf), archive)
+
+	detectedCompression := DetectCompression(buf)
+	if detectedCompression.Extension() != compression.Extension() {
+		return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
+	}
+
 	tmp, err := ioutil.TempDir("", "docker-test-untar")
 	tmp, err := ioutil.TempDir("", "docker-test-untar")
 	if err != nil {
 	if err != nil {
-		t.Fatal(err)
+		return err
 	}
 	}
 	defer os.RemoveAll(tmp)
 	defer os.RemoveAll(tmp)
 	if err := Untar(archive, tmp); err != nil {
 	if err := Untar(archive, tmp); err != nil {
-		t.Fatal(err)
+		return err
 	}
 	}
 	if _, err := os.Stat(tmp); err != nil {
 	if _, err := os.Stat(tmp); err != nil {
-		t.Fatalf("Error stating %s: %s", tmp, err.Error())
+		return err
+	}
+	return nil
+}
+
+func TestTarUntar(t *testing.T) {
+	origin, err := ioutil.TempDir("", "docker-test-untar-origin")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(origin)
+	if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
+		t.Fatal(err)
+	}
+	if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
+		t.Fatal(err)
+	}
+
+	for _, c := range []Compression{
+		Uncompressed,
+		Gzip,
+		Bzip2,
+		Xz,
+	} {
+		if err := tarUntar(t, origin, c); err != nil {
+			t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
+		}
 	}
 	}
 }
 }

+ 12 - 5
buildfile.go

@@ -101,6 +101,7 @@ func (b *buildFile) CmdRun(args string) error {
 	if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
 	if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
 		return err
 		return err
 	} else if cache != nil {
 	} else if cache != nil {
+		fmt.Fprintf(b.out, " ---> Using cache\n")
 		utils.Debugf("[BUILDER] Use cached version")
 		utils.Debugf("[BUILDER] Use cached version")
 		b.image = cache.ID
 		b.image = cache.ID
 		return nil
 		return nil
@@ -185,6 +186,7 @@ func (b *buildFile) CmdAdd(args string) error {
 		return err
 		return err
 	}
 	}
 	b.tmpContainers[container.ID] = struct{}{}
 	b.tmpContainers[container.ID] = struct{}{}
+	fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID))
 
 
 	if err := container.EnsureMounted(); err != nil {
 	if err := container.EnsureMounted(); err != nil {
 		return err
 		return err
@@ -235,6 +237,7 @@ func (b *buildFile) run() (string, error) {
 		return "", err
 		return "", err
 	}
 	}
 	b.tmpContainers[c.ID] = struct{}{}
 	b.tmpContainers[c.ID] = struct{}{}
+	fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID))
 
 
 	//start the container
 	//start the container
 	if err := c.Start(); err != nil {
 	if err := c.Start(); err != nil {
@@ -261,6 +264,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
 		if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
 		if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
 			return err
 			return err
 		} else if cache != nil {
 		} else if cache != nil {
+			fmt.Fprintf(b.out, " ---> Using cache\n")
 			utils.Debugf("[BUILDER] Use cached version")
 			utils.Debugf("[BUILDER] Use cached version")
 			b.image = cache.ID
 			b.image = cache.ID
 			return nil
 			return nil
@@ -274,6 +278,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
 			return err
 			return err
 		}
 		}
 		b.tmpContainers[container.ID] = struct{}{}
 		b.tmpContainers[container.ID] = struct{}{}
+		fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID))
 
 
 		if err := container.EnsureMounted(); err != nil {
 		if err := container.EnsureMounted(); err != nil {
 			return err
 			return err
@@ -314,6 +319,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
 		b.context = name
 		b.context = name
 	}
 	}
 	file := bufio.NewReader(dockerfile)
 	file := bufio.NewReader(dockerfile)
+	stepN := 0
 	for {
 	for {
 		line, err := file.ReadString('\n')
 		line, err := file.ReadString('\n')
 		if err != nil {
 		if err != nil {
@@ -334,12 +340,13 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
 		}
 		}
 		instruction := strings.ToLower(strings.Trim(tmp[0], " "))
 		instruction := strings.ToLower(strings.Trim(tmp[0], " "))
 		arguments := strings.Trim(tmp[1], " ")
 		arguments := strings.Trim(tmp[1], " ")
-
-		fmt.Fprintf(b.out, "%s %s (%s)\n", strings.ToUpper(instruction), arguments, b.image)
+		stepN += 1
+		// FIXME: only count known instructions as build steps
+		fmt.Fprintf(b.out, "Step %d : %s %s\n", stepN, strings.ToUpper(instruction), arguments)
 
 
 		method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
 		method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
 		if !exists {
 		if !exists {
-			fmt.Fprintf(b.out, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
+			fmt.Fprintf(b.out, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction))
 			continue
 			continue
 		}
 		}
 		ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
 		ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
@@ -347,10 +354,10 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
 			return "", ret.(error)
 			return "", ret.(error)
 		}
 		}
 
 
-		fmt.Fprintf(b.out, "===> %v\n", b.image)
+		fmt.Fprintf(b.out, " ---> %v\n", utils.TruncateID(b.image))
 	}
 	}
 	if b.image != "" {
 	if b.image != "" {
-		fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image)
+		fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(b.image))
 		return b.image, nil
 		return b.image, nil
 	}
 	}
 	return "", fmt.Errorf("An error occured during the build\n")
 	return "", fmt.Errorf("An error occured during the build\n")

+ 13 - 31
commands.go

@@ -632,7 +632,10 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
 
 
 	for _, out := range outs {
 	for _, out := range outs {
-		fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
+		if out.Tags != nil {
+			out.ID = out.Tags[0]
+		}
+		fmt.Fprintf(w, "%s \t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
 	}
 	}
 	w.Flush()
 	w.Flush()
 	return nil
 	return nil
@@ -1063,6 +1066,10 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
+	if !container.State.Running {
+		return fmt.Errorf("Impossible to attach to a stopped container, start it first")
+	}
+
 	splitStderr := container.Config.Tty
 	splitStderr := container.Config.Tty
 
 
 	connections := 1
 	connections := 1
@@ -1262,16 +1269,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		fmt.Fprintln(os.Stderr, "WARNING: ", warning)
 		fmt.Fprintln(os.Stderr, "WARNING: ", warning)
 	}
 	}
 
 
-	splitStderr := !config.Tty
-
-	connections := 0
-	if config.AttachStdin || config.AttachStdout || (!splitStderr && config.AttachStderr) {
-		connections += 1
-	}
-	if splitStderr && config.AttachStderr {
-		connections += 1
-	}
-
 	//start the container
 	//start the container
 	_, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
 	_, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
 	if err != nil {
 	if err != nil {
@@ -1280,19 +1277,11 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 
 
 	if !config.AttachStdout && !config.AttachStderr {
 	if !config.AttachStdout && !config.AttachStderr {
 		fmt.Println(out.ID)
 		fmt.Println(out.ID)
-	}
-	if connections > 0 {
-		chErrors := make(chan error, connections)
+	} else {
 		if config.Tty {
 		if config.Tty {
 			cli.monitorTtySize(out.ID)
 			cli.monitorTtySize(out.ID)
 		}
 		}
 
 
-		if splitStderr && config.AttachStderr {
-			go func() {
-				chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
-			}()
-		}
-
 		v := url.Values{}
 		v := url.Values{}
 		v.Set("logs", "1")
 		v.Set("logs", "1")
 		v.Set("stream", "1")
 		v.Set("stream", "1")
@@ -1303,19 +1292,12 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		if config.AttachStdout {
 		if config.AttachStdout {
 			v.Set("stdout", "1")
 			v.Set("stdout", "1")
 		}
 		}
-		if !splitStderr && config.AttachStderr {
+		if config.AttachStderr {
 			v.Set("stderr", "1")
 			v.Set("stderr", "1")
 		}
 		}
-		go func() {
-			chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
-		}()
-		for connections > 0 {
-			err := <-chErrors
-			if err != nil {
-				utils.Debugf("Error hijack: %s", err)
-				return err
-			}
-			connections -= 1
+		if err := cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout); err != nil {
+			utils.Debugf("Error hijack: %s", err)
+			return err
 		}
 		}
 	}
 	}
 	return nil
 	return nil

+ 1 - 1
contrib/install.sh

@@ -8,7 +8,7 @@
 
 
 echo "Ensuring basic dependencies are installed..."
 echo "Ensuring basic dependencies are installed..."
 apt-get -qq update
 apt-get -qq update
-apt-get -qq install lxc wget bsdtar
+apt-get -qq install lxc wget
 
 
 echo "Looking in /proc/filesystems to see if we have AUFS support..."
 echo "Looking in /proc/filesystems to see if we have AUFS support..."
 if grep -q aufs /proc/filesystems
 if grep -q aufs /proc/filesystems

+ 1 - 0
docs/sources/api/docker_remote_api_v1.2.rst

@@ -691,6 +691,7 @@ Get the history of an image
 	   [
 	   [
 		{
 		{
 			"Id":"b750fe79269d",
 			"Id":"b750fe79269d",
+			"Tag":["base:latest"],
 			"Created":1364102658,
 			"Created":1364102658,
 			"CreatedBy":"/bin/bash"
 			"CreatedBy":"/bin/bash"
 		},
 		},

+ 0 - 8
docs/sources/concepts/containers.rst

@@ -1,8 +0,0 @@
-:title: Introduction
-:description: An introduction to docker and standard containers?
-:keywords: containers, lxc, concepts, explanation, docker, documentation
-
-
-:note: This version of the introduction is temporary, just to make sure we don't break the links from the website when the documentation is updated
-
-This document has been moved to  :ref:`introduction`, please update your bookmarks.

+ 0 - 125
docs/sources/concepts/introduction.rst

@@ -1,125 +0,0 @@
-:title: Introduction
-:description: An introduction to docker and standard containers?
-:keywords: containers, lxc, concepts, explanation
-
-Introduction
-============
-
-Docker -- The Linux container runtime
--------------------------------------
-
-Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
-
-Docker is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
-
-
-- **Heterogeneous payloads** Any combination of binaries, libraries, configuration files, scripts, virtualenvs, jars, gems, tarballs, you name it. No more juggling between domain-specific tools. Docker can deploy and run them all.
-- **Any server** Docker can run on any x64 machine with a modern linux kernel - whether it's a laptop, a bare metal server or a VM. This makes it perfect for multi-cloud deployments.
-- **Isolation** docker isolates processes from each other and from the underlying host, using lightweight containers.
-- **Repeatability** Because containers are isolated in their own filesystem, they behave the same regardless of where, when, and alongside what they run.
-
-.. image:: images/lego_docker.jpg
-
-
-What is a Standard Container?
------------------------------
-
-Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in
-a format that is self-describing and portable, so that any compliant runtime can run it without extra dependency, regardless of the underlying machine and the contents of the container.
-
-The spec for Standard Containers is currently work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
-
-A great analogy for this is the shipping container. Just like Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery.
-
-Standard operations
-~~~~~~~~~~~~~~~~~~~
-
-Just like shipping containers, Standard Containers define a set of STANDARD OPERATIONS. Shipping containers can be lifted, stacked, locked, loaded, unloaded and labelled. Similarly, standard containers can be started, stopped, copied, snapshotted, downloaded, uploaded and tagged.
-
-
-Content-agnostic
-~~~~~~~~~~~~~~~~~~~
-
-Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffee or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
-
-
-Infrastructure-agnostic
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be transported to thousands of facilities around the world, and manipulated by a wide variety of equipment. A shipping container can be packed in a factory in Ukraine, transported by truck to the nearest routing center, stacked onto a train, loaded into a German boat by an Australian-built crane, stored in a warehouse at a US facility, etc. Similarly, a standard container can be bundled on my laptop, uploaded to S3, downloaded, run and snapshotted by a build server at Equinix in Virginia, uploaded to 10 staging servers in a home-made Openstack cluster, then sent to 30 production instances across 3 EC2 regions.
-
-
-Designed for automation
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Because they offer the same standard operations regardless of content and infrastructure, Standard Containers, just like their physical counterpart, are extremely well-suited for automation. In fact, you could say automation is their secret weapon.
-
-Many things that once required time-consuming and error-prone human effort can now be programmed. Before shipping containers, a bag of powder coffee was hauled, dragged, dropped, rolled and stacked by 10 different people in 10 different locations by the time it reached its destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The process was slow, inefficient and cost a fortune - and was entirely different depending on the facility and the type of goods.
-
-Similarly, before Standard Containers, by the time a software component ran in production, it had been individually built, configured, bundled, documented, patched, vendored, templated, tweaked and instrumented by 10 different people on 10 different computers. Builds failed, libraries conflicted, mirrors crashed, post-it notes were lost, logs were misplaced, cluster updates were half-broken. The process was slow, inefficient and cost a fortune - and was entirely different depending on the language and infrastructure provider.
-
-
-Industrial-grade delivery
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded on the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away.
-
-With Standard Containers we can put an end to that embarrassment, by making INDUSTRIAL-GRADE DELIVERY of software a reality.
-
-
-Standard Container Specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-(TODO)
-
-Image format
-~~~~~~~~~~~~
-
-Standard operations
-~~~~~~~~~~~~~~~~~~~
-
--  Copy
--  Run
--  Stop
--  Wait
--  Commit
--  Attach standard streams
--  List filesystem changes
--  ...
-
-Execution environment
-~~~~~~~~~~~~~~~~~~~~~
-
-Root filesystem
-^^^^^^^^^^^^^^^
-
-Environment variables
-^^^^^^^^^^^^^^^^^^^^^
-
-Process arguments
-^^^^^^^^^^^^^^^^^
-
-Networking
-^^^^^^^^^^
-
-Process namespacing
-^^^^^^^^^^^^^^^^^^^
-
-Resource limits
-^^^^^^^^^^^^^^^
-
-Process monitoring
-^^^^^^^^^^^^^^^^^^
-
-Logging
-^^^^^^^
-
-Signals
-^^^^^^^
-
-Pseudo-terminal allocation
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Security
-^^^^^^^^
-

+ 19 - 5
docs/sources/conf.py

@@ -20,6 +20,20 @@ import sys, os
 
 
 # -- General configuration -----------------------------------------------------
 # -- General configuration -----------------------------------------------------
 
 
+
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# the 'redirect_home.html' page redirects using a http meta refresh which, according
+# to official sources is more or less equivalent of a 301.
+
+html_additional_pages = {
+    'concepts/containers': 'redirect_home.html',
+    'concepts/introduction': 'redirect_home.html',
+    }
+
+
+
 # If your documentation needs a minimal Sphinx version, state it here.
 # If your documentation needs a minimal Sphinx version, state it here.
 #needs_sphinx = '1.0'
 #needs_sphinx = '1.0'
 
 
@@ -120,7 +134,11 @@ html_theme_path = ['../theme']
 # The name of an image file (within the static path) to use as favicon of the
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
 # pixels large.
-#html_favicon = None
+
+# We use a png favicon. This is not compatible with internet explorer, but looks
+# much better on all other browsers. However, sphynx doesn't like it (it likes
+# .ico better) so we have just put it in the template rather than used this setting
+# html_favicon = 'favicon.png'
 
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # relative to this directory. They are copied after the builtin static files,
@@ -138,10 +156,6 @@ html_static_path = ['static_files']
 # Custom sidebar templates, maps document names to template names.
 # Custom sidebar templates, maps document names to template names.
 #html_sidebars = {}
 #html_sidebars = {}
 
 
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
 # If false, no module index is generated.
 # If false, no module index is generated.
 #html_domain_indices = True
 #html_domain_indices = True
 
 

+ 1 - 1
docs/sources/contributing/devenvironment.rst

@@ -33,7 +33,7 @@ Installation
     sudo apt-get install python-software-properties
     sudo apt-get install python-software-properties
     sudo add-apt-repository ppa:gophers/go
     sudo add-apt-repository ppa:gophers/go
     sudo apt-get update
     sudo apt-get update
-    sudo apt-get -y install lxc wget bsdtar curl golang-stable git aufs-tools
+    sudo apt-get -y install lxc xz-utils curl golang-stable git aufs-tools
 
 
     export GOPATH=~/go/
     export GOPATH=~/go/
     export PATH=$GOPATH/bin:$PATH
     export PATH=$GOPATH/bin:$PATH

+ 0 - 27
docs/sources/index/variable.rst

@@ -1,27 +0,0 @@
-:title: Index Environment Variable
-:description: Setting this environment variable on the docker server will change the URL docker index.
-:keywords: docker, index environment variable, documentation 
-
-=================================
-Docker Index Environment Variable
-=================================
-
-Variable
---------
-
-.. code-block:: sh
-
-    DOCKER_INDEX_URL
-
-Setting this environment variable on the docker server will change the URL docker index.
-This address is used in commands such as ``docker login``, ``docker push`` and ``docker pull``.
-The docker daemon doesn't need to be restarted for this parameter to take effect.
-
-Example
--------
-
-.. code-block:: sh
-
-    docker -d &
-    export DOCKER_INDEX_URL="https://index.docker.io"
-

+ 1 - 2
docs/sources/installation/binaries.rst

@@ -30,8 +30,7 @@ Dependencies:
 * 3.8 Kernel (read more about :ref:`kernel`)
 * 3.8 Kernel (read more about :ref:`kernel`)
 * AUFS filesystem support
 * AUFS filesystem support
 * lxc
 * lxc
-* bsdtar
-
+* xz-utils
 
 
 Get the docker binary:
 Get the docker binary:
 ----------------------
 ----------------------

+ 11 - 3
docs/sources/use/builder.rst

@@ -15,10 +15,18 @@ steps and commit them along the way, giving you a final image.
 1. Usage
 1. Usage
 ========
 ========
 
 
-To use Docker Builder, assemble the steps into a text file (commonly referred to
-as a Dockerfile) and supply this to `docker build` on STDIN, like so:
+To build an image from a source repository, create a description file called `Dockerfile`
+at the root of your repository. This file will describe the steps to assemble
+the image.
 
 
-    ``docker build - < Dockerfile``
+Then call `docker build` with the path of your source repository as argument:
+
+    ``docker build .``
+
+You can specify a repository and tag at which to save the new image if the
+build succeeds:
+
+    ``docker build -t shykes/myapp .``
 
 
 Docker will run your steps one-by-one, committing the result if necessary, 
 Docker will run your steps one-by-one, committing the result if necessary, 
 before finally outputting the ID of your new image.
 before finally outputting the ID of your new image.

+ 25 - 0
docs/sources/use/workingwithrepository.rst

@@ -77,3 +77,28 @@ Now you can commit this image to the repository
     # for example docker push dhrp/kickassapp
     # for example docker push dhrp/kickassapp
     docker push <image-name>
     docker push <image-name>
 
 
+
+Changing the server to connect to
+----------------------------------
+
+When you are running your own index and/or registry, You can change the server the docker client will connect to.
+
+Variable
+^^^^^^^^
+
+.. code-block:: sh
+
+    DOCKER_INDEX_URL
+
+Setting this environment variable on the docker server will change the URL docker index.
+This address is used in commands such as ``docker login``, ``docker push`` and ``docker pull``.
+The docker daemon doesn't need to be restarted for this parameter to take effect.
+
+Example
+^^^^^^^
+
+.. code-block:: sh
+
+    docker -d &
+    export DOCKER_INDEX_URL="https://index.docker.io"
+

+ 5 - 7
docs/theme/docker/layout.html

@@ -40,6 +40,8 @@
 
 
     {%- set script_files = script_files + ['_static/js/docs.js'] %}
     {%- set script_files = script_files + ['_static/js/docs.js'] %}
 
 
+    <link rel="canonical" href="http://docs.docker.io/en/latest/{{ pagename }}/">
+
     {%- for cssfile in css_files %}
     {%- for cssfile in css_files %}
     <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
     <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
     {%- endfor %}
     {%- endfor %}
@@ -48,9 +50,8 @@
     <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
     <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
     {%- endfor %}
     {%- endfor %}
 
 
-    {%- if favicon %}
-    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
-    {%- endif %}
+    <link rel="shortcut icon" href="{{ pathto('_static/favicon.png', 1) }}"/>
+
 
 
     {%- block extrahead %}{% endblock %}
     {%- block extrahead %}{% endblock %}
 
 
@@ -104,11 +105,8 @@
     <!-- Docs nav
     <!-- Docs nav
      ================================================== -->
      ================================================== -->
     <div class="row" style="position: relative">
     <div class="row" style="position: relative">
-        <div class="span3" style="height:100%;" >
-
-        </div>
 
 
-        <div class="span3 sidebar bs-docs-sidebar" style="position: absolute">
+        <div class="span3 sidebar bs-docs-sidebar">
             {{ toctree(collapse=False, maxdepth=3) }}
             {{ toctree(collapse=False, maxdepth=3) }}
         </div>
         </div>
 
 

+ 12 - 0
docs/theme/docker/redirect_home.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Page Moved</title>
+    <meta http-equiv="refresh" content="0; url=http://docks.docker.io/en/latest/">
+</head>
+<body>
+
+This page has moved. Perhaps you should visit the <a href="http://docs.docker.io/" title="documentation homepage">Documentation Homepage</a>
+
+</body>
+</html>

+ 6 - 4
docs/theme/docker/static/css/main.css

@@ -168,10 +168,13 @@ section.header {
 .sidebar {
 .sidebar {
   font-weight: normal;
   font-weight: normal;
   float: left;
   float: left;
-  min-height: 475px;
+  /*  min-height: 475px;*/
+
   background: #ececec;
   background: #ececec;
-  border-left: 1px solid #bbbbbb;
-  border-right: 1px solid #cccccc;
+  /*  border-left: 1px solid #bbbbbb;*/
+
+  /*  border-right: 1px solid #cccccc;*/
+
   position: relative;
   position: relative;
 }
 }
 .sidebar ul {
 .sidebar ul {
@@ -357,7 +360,6 @@ section.header {
   #global {
   #global {
     /* TODO: Fix this to be relative to the navigation size */
     /* TODO: Fix this to be relative to the navigation size */
   
   
-    padding-top: 600px;
   }
   }
   #fork-us {
   #fork-us {
     display: none;
     display: none;

+ 15 - 14
docs/theme/docker/static/css/main.less

@@ -226,20 +226,21 @@ section.header {
 
 
 }
 }
 
 
+  .sidebar {
+  //  font-family: "Maven Pro";
+    font-weight: normal;
+  //  margin-top: 38px;
+    float: left;
+  //  width: 220px;
+  /*  min-height: 475px;*/
+  //  margin-bottom: 28px;
+  //  padding-bottom: 120px;
+    background: #ececec;
+  /*  border-left: 1px solid #bbbbbb;*/
+  /*  border-right: 1px solid #cccccc;*/
+    position: relative;
+
 
 
-.sidebar {
-//  font-family: "Maven Pro";
-  font-weight: normal;
-//  margin-top: 38px;
-  float: left;
-//  width: 220px;
-  min-height: 475px;
-//  margin-bottom: 28px;
-//  padding-bottom: 120px;
-  background: #ececec;
-  border-left: 1px solid #bbbbbb;
-  border-right: 1px solid #cccccc;
-  position: relative;
 
 
   ul {
   ul {
     padding: 0px;
     padding: 0px;
@@ -471,7 +472,7 @@ section.header {
   }
   }
   #global {
   #global {
   /* TODO: Fix this to be relative to the navigation size */
   /* TODO: Fix this to be relative to the navigation size */
-    padding-top: 600px;
+//    padding-top: 600px;
   }
   }
   #fork-us {
   #fork-us {
     display: none;
     display: none;

BIN
docs/theme/docker/static/favicon.ico


BIN
docs/theme/docker/static/favicon.png


+ 12 - 0
graph_test.go

@@ -192,11 +192,19 @@ func TestDelete(t *testing.T) {
 	}
 	}
 	assertNImages(graph, t, 0)
 	assertNImages(graph, t, 0)
 
 
+	archive, err = fakeTar()
+	if err != nil {
+		t.Fatal(err)
+	}
 	// Test 2 create (same name) / 1 delete
 	// Test 2 create (same name) / 1 delete
 	img1, err := graph.Create(archive, nil, "Testing", "", nil)
 	img1, err := graph.Create(archive, nil, "Testing", "", nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	archive, err = fakeTar()
+	if err != nil {
+		t.Fatal(err)
+	}
 	if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
 	if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -212,6 +220,10 @@ func TestDelete(t *testing.T) {
 	}
 	}
 	assertNImages(graph, t, 1)
 	assertNImages(graph, t, 1)
 
 
+	archive, err = fakeTar()
+	if err != nil {
+		t.Fatal(err)
+	}
 	// Test delete twice (pull -> rm -> pull -> rm)
 	// Test delete twice (pull -> rm -> pull -> rm)
 	if err := graph.Register(archive, false, img1); err != nil {
 	if err := graph.Register(archive, false, img1); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)

+ 1 - 1
hack/Vagrantfile

@@ -22,7 +22,7 @@ Vagrant::Config.run do |config|
   pkg_cmd = "touch #{DOCKER_PATH}; "
   pkg_cmd = "touch #{DOCKER_PATH}; "
   # Install docker dependencies
   # Install docker dependencies
   pkg_cmd << "export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; " \
   pkg_cmd << "export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; " \
-    "apt-get install -q -y lxc bsdtar git aufs-tools golang make linux-image-extra-3.8.0-19-generic; " \
+    "apt-get install -q -y lxc git aufs-tools golang make linux-image-extra-3.8.0-19-generic; " \
     "chown -R #{USER}.#{USER} #{GOPATH}; " \
     "chown -R #{USER}.#{USER} #{GOPATH}; " \
     "install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile"
     "install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile"
   config.vm.provision :shell, :inline => pkg_cmd
   config.vm.provision :shell, :inline => pkg_cmd

+ 12 - 3
server.go

@@ -218,12 +218,24 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	lookupMap := make(map[string][]string)
+	for name, repository := range srv.runtime.repositories.Repositories {
+		for tag, id := range repository {
+			// If the ID already has a reverse lookup, do not update it unless for "latest"
+			if _, exists := lookupMap[id]; !exists {
+				lookupMap[id] = []string{}
+			}
+			lookupMap[id] = append(lookupMap[id], name+":"+tag)
+		}
+	}
+
 	outs := []APIHistory{} //produce [] when empty instead of 'null'
 	outs := []APIHistory{} //produce [] when empty instead of 'null'
 	err = image.WalkHistory(func(img *Image) error {
 	err = image.WalkHistory(func(img *Image) error {
 		var out APIHistory
 		var out APIHistory
 		out.ID = srv.runtime.repositories.ImageName(img.ShortID())
 		out.ID = srv.runtime.repositories.ImageName(img.ShortID())
 		out.Created = img.Created.Unix()
 		out.Created = img.Created.Unix()
 		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
 		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
+		out.Tags = lookupMap[img.ID]
 		outs = append(outs, out)
 		outs = append(outs, out)
 		return nil
 		return nil
 	})
 	})
@@ -930,9 +942,6 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
 		if container.State.Ghost {
 		if container.State.Ghost {
 			return fmt.Errorf("Impossible to attach to a ghost container")
 			return fmt.Errorf("Impossible to attach to a ghost container")
 		}
 		}
-		if !container.State.Running {
-			return fmt.Errorf("Impossible to attach to a stopped container, start it first")
-		}
 
 
 		var (
 		var (
 			cStdin           io.ReadCloser
 			cStdin           io.ReadCloser

+ 1 - 1
testing/Vagrantfile

@@ -30,7 +30,7 @@ Vagrant::Config.run do |config|
     # Install docker dependencies
     # Install docker dependencies
     pkg_cmd << "apt-get install -q -y python-software-properties; " \
     pkg_cmd << "apt-get install -q -y python-software-properties; " \
       "add-apt-repository -y ppa:gophers/go/ubuntu; apt-get update -qq; " \
       "add-apt-repository -y ppa:gophers/go/ubuntu; apt-get update -qq; " \
-      "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc bsdtar git golang-stable aufs-tools make; "
+      "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc git golang-stable aufs-tools make; "
     # Activate new kernel
     # Activate new kernel
     pkg_cmd << "shutdown -r +1; "
     pkg_cmd << "shutdown -r +1; "
     config.vm.provision :shell, :inline => pkg_cmd
     config.vm.provision :shell, :inline => pkg_cmd