diff --git a/.mailmap b/.mailmap index 1f38e55e28..452ac41d8f 100644 --- a/.mailmap +++ b/.mailmap @@ -19,3 +19,7 @@ Andy Smith Thatcher Peskens + +Walter Stanish + +Roberto Hashioka diff --git a/AUTHORS b/AUTHORS index eb90cd02d9..7c7ba52477 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,6 +6,8 @@ Al Tobey Alexey Shamrin Andrea Luzzardi +Andreas Tiefenthaler +Andrew Munsell Andy Rothfusz Andy Smith Antony Messerli @@ -14,7 +16,9 @@ Brandon Liu Brian McCallister Bruno Bigras Caleb Spare +Calen Pennington Charles Hooper +Christopher Currie Daniel Gasienica Daniel Mizyrycki Daniel Robinson @@ -22,11 +26,14 @@ Daniel Von Fange Dominik Honnef Don Spaulding Dr Nic Williams +Elias Probst +Eric Hanchrow Evan Wies ezbercih Flavio Castelli Francisco Souza Frederick F. Kautz IV +Gareth Rushgrove Guillaume J. Charmes Harley Laue Hunter Blanks @@ -34,14 +41,21 @@ Jeff Lindsay Jeremy Grosser Joffrey F John Costa +Jon Wedaman Jonas Pfenniger Jonathan Rudenberg +Joseph Anthony Pasquale Holsten Julien Barbier Jérôme Petazzoni Ken Cochrane Kevin J. Lynagh +kim0 +Kiran Gangadharan Louis Opter +Marcus Farkas +Mark McGranaghan Maxim Treskin +meejah Michael Crosby Mikhail Sobolev Nate Jones @@ -50,18 +64,25 @@ Niall O'Higgins odk- Paul Bowsher Paul Hammond +Phil Spitler Piotr Bogdan +Renato Riccieri Santos Zannon Robert Obryk +Roberto Hashioka Sam Alba +Sam J Sharpe Shawn Siefkas Silas Sewell Solomon Hykes Sridhar Ratnakumar Thatcher Peskens Thomas Bikeev +Thomas Hansen Tianon Gravi Tim Terhorst -Troy Howard +Tobias Bieniek unclejack Victor Vieux Vivek Agarwal +Walter Stanish +Will Dietz diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e52eecb5..1144800150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,48 @@ # Changelog +## 0.4.4 (2013-06-19) + - Builder: fix a regression introduced in 0.4.3 which caused builds to fail on new clients. + +## 0.4.3 (2013-06-19) + + Builder: ADD of a local file will detect tar archives and unpack them + * Runtime: Remove bsdtar dependency + * Runtime: Add unix socket and multiple -H support + * Runtime: Prevent rm of running containers + * Runtime: Use go1.1 cookiejar + * Builder: ADD improvements: use tar for copy + automatically unpack local archives + * Builder: ADD uses tar/untar for copies instead of calling 'cp -ar' + * Builder: nicer output for 'docker build' + * Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. + * Client: HumanReadable ProgressBar sizes in pull + * Client: Fix docker version's git commit output + * API: Send all tags on History API call + * API: Add tag lookup to history command. Fixes #882 + - Runtime: Fix issue detaching from running TTY container + - Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311 + - Runtime: Fix race condition within Run command when attaching. + - Builder: fix a bug which caused builds to fail if ADD was the first command + - Documentation: fix missing command in irc bouncer example + +## 0.4.2 (2013-06-17) + - Packaging: Bumped version to work around an Ubuntu bug + +## 0.4.1 (2013-06-17) + + Remote Api: Add flag to enable cross domain requests + + Remote Api/Client: Add images and containers sizes in docker ps and docker images + + Runtime: Configure dns configuration host-wide with 'docker -d -dns' + + Runtime: Detect faulty DNS configuration and replace it with a public default + + Runtime: allow docker run : + + Runtime: you can now specify public port (ex: -p 80:4500) + * Client: allow multiple params in inspect + * Client: Print the container id before the hijack in `docker run` + * Registry: add regexp check on repo's name + * Registry: Move auth to the client + * Runtime: improved image removal to garbage-collect unreferenced parents + * Vagrantfile: Add the rest api port to vagrantfile's port_forward + * Upgrade to Go 1.1 + - Builder: don't ignore last line in Dockerfile when it doesn't end with \n + - Registry: Remove login check on pull + ## 0.4.0 (2013-06-03) + Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile + Introducing Remote API: control Docker programmatically using a simple HTTP/json API diff --git a/FIXME b/FIXME index e252fb2589..e182d38d30 100644 --- a/FIXME +++ b/FIXME @@ -16,3 +16,20 @@ to put them - so we put them here :) * Unify build commands and regular commands * Move source code into src/ subdir for clarity * Clean up the Makefile, it's a mess +* docker build: on non-existent local path for ADD, don't show full absolute path on the host +* mount into /dockerinit rather than /sbin/init +* docker tag foo REPO:TAG +* use size header for progress bar in pull +* Clean up context upload in build!!! +* Parallel pull +* Ensure /proc/sys/net/ipv4/ip_forward is 1 +* Force DNS to public! +* Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet +* Save metadata with import/export +* Upgrade dockerd without stopping containers +* bring back git revision info, looks like it was lost +* Simple command to remove all untagged images +* Simple command to clean up containers for disk space +* Caching after an ADD +* entry point config +* bring back git revision info, looks like it was lost diff --git a/Makefile b/Makefile index 9527d3f750..44497d7d32 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ endif GIT_COMMIT = $(shell git rev-parse --short HEAD) GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES") -BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS)" +BUILD_OPTIONS = -ldflags "-X main.GITCOMMIT $(GIT_COMMIT)$(GIT_STATUS)" SRC_DIR := $(GOPATH)/src @@ -46,6 +46,7 @@ whichrelease: release: $(BINRELEASE) s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz + s3cmd -P put docker-latest.tgz s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-latest.tgz srcrelease: $(SRCRELEASE) deps: $(DOCKER_DIR) @@ -60,6 +61,7 @@ $(SRCRELEASE): $(BINRELEASE): $(SRCRELEASE) rm -f $(BINRELEASE) cd $(SRCRELEASE); make; cp -R bin docker-$(RELEASE_VERSION); tar -f ../$(BINRELEASE) -zv -c docker-$(RELEASE_VERSION) + cd $(SRCRELEASE); cp -R bin docker-latest; tar -f ../docker-latest.tgz -zv -c docker-latest clean: @rm -rf $(dir $(DOCKER_BIN)) @@ -72,6 +74,9 @@ endif test: all @(cd $(DOCKER_DIR); sudo -E go test $(GO_OPTIONS)) +testall: all + @(cd $(DOCKER_DIR); sudo -E go test ./... $(GO_OPTIONS)) + fmt: @gofmt -s -l -w . diff --git a/README.md b/README.md index 1c909e5431..376ecea703 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Quick install on Ubuntu 12.04 and 12.10 --------------------------------------- ```bash -curl get.docker.io | sh -x +curl get.docker.io | sudo sh -x ``` Binary installs @@ -108,7 +108,7 @@ Note that some methods are community contributions and not yet officially suppor * [Ubuntu 12.04 and 12.10 (officially supported)](http://docs.docker.io/en/latest/installation/ubuntulinux/) * [Arch Linux](http://docs.docker.io/en/latest/installation/archlinux/) -* [MacOS X (with Vagrant)](http://docs.docker.io/en/latest/installation/macos/) +* [Mac OS X (with Vagrant)](http://docs.docker.io/en/latest/installation/vagrant/) * [Windows (with Vagrant)](http://docs.docker.io/en/latest/installation/windows/) * [Amazon EC2 (with Vagrant)](http://docs.docker.io/en/latest/installation/amazon/) @@ -181,7 +181,7 @@ Running an irc bouncer ---------------------- ```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" ``` @@ -216,7 +216,8 @@ PORT=$(docker port $JOB 4444) # Connect to the public port via the host's public address # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. -IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }') +# Replace *eth0* according to your local interface name. +IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }') echo hello world | nc $IP $PORT # Verify that the network connection worked @@ -262,14 +263,14 @@ Setting up a dev environment Instructions that have been verified to work on Ubuntu 12.10, ```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 PATH=$GOPATH/bin:$PATH mkdir -p $GOPATH/src/github.com/dotcloud cd $GOPATH/src/github.com/dotcloud -git clone git@github.com:dotcloud/docker.git +git clone https://github.com/dotcloud/docker.git cd docker go get -v github.com/dotcloud/docker/... diff --git a/Vagrantfile b/Vagrantfile index 3d568266af..aadabb8711 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -3,13 +3,16 @@ 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_REGION = ENV['AWS_REGION'] || "us-east-1" AWS_AMI = ENV['AWS_AMI'] || "ami-d0f89fb9" +FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS'] Vagrant::Config.run do |config| # Setup virtual machine box. This VM configuration code is always executed. config.vm.box = BOX_NAME config.vm.box_url = BOX_URI + config.vm.forward_port 4243, 4243 # Provision docker and new kernel if deployment was not done if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty? @@ -65,8 +68,29 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config| rs.image = /Ubuntu/ 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.box = BOX_NAME config.vm.box_url = BOX_URI end end + +if !FORWARD_DOCKER_PORTS.nil? + Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config| + (49000..49900).each do |port| + config.vm.forward_port port, port + end + end + + Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config| + (49000..49900).each do |port| + config.vm.network :forwarded_port, :host => port, :guest => port + end + end +end diff --git a/api.go b/api.go index a69f6a3c2d..18c5e5c67a 100644 --- a/api.go +++ b/api.go @@ -8,12 +8,16 @@ import ( "github.com/gorilla/mux" "io" "log" + "net" "net/http" + "os" "strconv" "strings" ) const APIVERSION = 1.2 +const DEFAULTHTTPHOST string = "127.0.0.1" +const DEFAULTHTTPPORT int = 4243 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { conn, _, err := w.(http.Hijacker).Hijack() @@ -438,17 +442,23 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { config := &Config{} + out := &APIRun{} + if err := json.NewDecoder(r.Body).Decode(config); err != nil { return err } + + if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns() { + out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns)) + config.Dns = defaultDns + } + id, err := srv.ContainerCreate(config) if err != nil { return err } + out.ID = id - out := &APIRun{ - ID: id, - } if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.") out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") @@ -457,6 +467,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.") out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.") } + b, err := json.Marshal(out) if err != nil { return err @@ -809,6 +820,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { localFct := fct f := func(w http.ResponseWriter, r *http.Request) { utils.Debugf("Calling %s %s", localMethod, localRoute) + if logging { log.Println(r.Method, r.RequestURI) } @@ -829,6 +841,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { w.WriteHeader(http.StatusNotFound) return } + if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil { httpError(w, err) } @@ -845,12 +858,21 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { return r, nil } -func ListenAndServe(addr string, srv *Server, logging bool) error { - log.Printf("Listening for HTTP on %s\n", addr) +func ListenAndServe(proto, addr string, srv *Server, logging bool) error { + log.Printf("Listening for HTTP on %s (%s)\n", addr, proto) r, err := createRouter(srv, logging) if err != nil { return err } - return http.ListenAndServe(addr, r) + l, e := net.Listen(proto, addr) + if e != nil { + return e + } + //as the daemon is launched as root, change to permission of the socket to allow non-root to connect + if proto == "unix" { + os.Chmod(addr, 0777) + } + httpSrv := http.Server{Addr: addr, Handler: r} + return httpSrv.Serve(l) } diff --git a/api_params.go b/api_params.go index 33b915cea5..b8af690c7f 100644 --- a/api_params.go +++ b/api_params.go @@ -1,16 +1,19 @@ package docker type APIHistory struct { - ID string `json:"Id"` + ID string `json:"Id"` + Tags []string `json:",omitempty"` Created int64 CreatedBy string `json:",omitempty"` } type APIImages struct { - Repository string `json:",omitempty"` - Tag string `json:",omitempty"` - ID string `json:"Id"` - Created int64 + Repository string `json:",omitempty"` + Tag string `json:",omitempty"` + ID string `json:"Id"` + Created int64 + Size int64 + VirtualSize int64 } type APIInfo struct { @@ -29,12 +32,14 @@ type APIRmi struct { } type APIContainers struct { - ID string `json:"Id"` - Image string - Command string - Created int64 - Status string - Ports string + ID string `json:"Id"` + Image string + Command string + Created int64 + Status string + Ports string + SizeRw int64 + SizeRootFs int64 } type APISearch struct { diff --git a/archive.go b/archive.go index 06466627a1..16401e29fb 100644 --- a/archive.go +++ b/archive.go @@ -1,12 +1,15 @@ package docker import ( + "bufio" "errors" "fmt" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" "os/exec" + "path" ) type Archive io.Reader @@ -20,6 +23,37 @@ const ( 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 { switch *compression { case Bzip2: @@ -46,14 +80,43 @@ func (compression *Compression) Extension() string { return "" } +// Tar creates an archive from the directory at `path`, and returns it as a +// stream of bytes. func Tar(path string, compression Compression) (io.Reader, error) { - cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".") - return CmdStream(cmd) + return TarFilter(path, compression, nil) } +// Tar creates an archive from the directory at `path`, only including files whose relative +// paths are included in `filter`. If `filter` is nil, then all files are included. +func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) { + args := []string{"tar", "-f", "-", "-C", path} + if filter == nil { + filter = []string{"."} + } + for _, f := range filter { + args = append(args, "-c"+compression.Flag(), f) + } + return CmdStream(exec.Command(args[0], args[1:]...)) +} + +// Untar reads a stream of bytes from `archive`, parses it as a tar archive, +// and unpacks it into the directory at `path`. +// The archive may be compressed with one of the following algorithgms: +// identity (uncompressed), gzip, bzip2, xz. +// FIXME: specify behavior when target path exists vs. doesn't exist. func Untar(archive io.Reader, path string) error { - cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x") - cmd.Stdin = archive + + 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. // (see https://github.com/dotcloud/docker/issues/355) cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"} @@ -64,6 +127,86 @@ func Untar(archive io.Reader, path string) error { return nil } +// TarUntar is a convenience function which calls Tar and Untar, with +// the output of one piped into the other. If either Tar or Untar fails, +// TarUntar aborts and returns the error. +func TarUntar(src string, filter []string, dst string) error { + utils.Debugf("TarUntar(%s %s %s)", src, filter, dst) + archive, err := TarFilter(src, Uncompressed, filter) + if err != nil { + return err + } + return Untar(archive, dst) +} + +// UntarPath is a convenience function which looks for an archive +// at filesystem path `src`, and unpacks it at `dst`. +func UntarPath(src, dst string) error { + if archive, err := os.Open(src); err != nil { + return err + } else if err := Untar(archive, dst); err != nil { + return err + } + return nil +} + +// CopyWithTar creates a tar archive of filesystem path `src`, and +// unpacks it at filesystem path `dst`. +// The archive is streamed directly with fixed buffering and no +// intermediary disk IO. +// +func CopyWithTar(src, dst string) error { + srcSt, err := os.Stat(src) + if err != nil { + return err + } + var dstExists bool + dstSt, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + dstExists = true + } + // Things that can go wrong if the source is a directory + if srcSt.IsDir() { + // The destination exists and is a regular file + if dstExists && !dstSt.IsDir() { + return fmt.Errorf("Can't copy a directory over a regular file") + } + // Things that can go wrong if the source is a regular file + } else { + utils.Debugf("The destination exists, it's a directory, and doesn't end in /") + // The destination exists, it's a directory, and doesn't end in / + if dstExists && dstSt.IsDir() && dst[len(dst)-1] != '/' { + return fmt.Errorf("Can't copy a regular file over a directory %s |%s|", dst, dst[len(dst)-1]) + } + } + // Create the destination + var dstDir string + if srcSt.IsDir() || dst[len(dst)-1] == '/' { + // The destination ends in /, or the source is a directory + // --> dst is the holding directory and needs to be created for -C + dstDir = dst + } else { + // The destination doesn't end in / + // --> dst is the file + dstDir = path.Dir(dst) + } + if !dstExists { + // Create the holding directory if necessary + utils.Debugf("Creating the holding directory %s", dstDir) + if err := os.MkdirAll(dstDir, 0700); err != nil && !os.IsExist(err) { + return err + } + } + if !srcSt.IsDir() { + return TarUntar(path.Dir(src), []string{path.Base(src)}, dstDir) + } + return TarUntar(src, nil, dstDir) +} + // CmdStream executes a command, and returns its stdout as a stream. // If the command fails to run or doesn't complete successfully, an error // will be returned, including anything written on stderr. diff --git a/archive_test.go b/archive_test.go index f583604497..bb4235ad5b 100644 --- a/archive_test.go +++ b/archive_test.go @@ -1,10 +1,13 @@ package docker import ( + "bytes" + "fmt" "io" "io/ioutil" "os" "os/exec" + "path" "testing" "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 { 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") if err != nil { - t.Fatal(err) + return err } defer os.RemoveAll(tmp) if err := Untar(archive, tmp); err != nil { - t.Fatal(err) + return err } 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) + } } } diff --git a/auth/auth.go b/auth/auth.go index 5f521ba3db..12c9471699 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -82,7 +82,7 @@ func decodeAuth(authStr string) (*AuthConfig, error) { func LoadConfig(rootPath string) (*AuthConfig, error) { confFile := path.Join(rootPath, CONFIGFILE) if _, err := os.Stat(confFile); err != nil { - return &AuthConfig{rootPath:rootPath}, ErrConfigFileMissing + return &AuthConfig{rootPath: rootPath}, ErrConfigFileMissing } b, err := ioutil.ReadFile(confFile) if err != nil { diff --git a/auth/auth_test.go b/auth/auth_test.go index 6c8d032cf7..ead69e8913 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -10,8 +10,8 @@ import ( func TestEncodeAuth(t *testing.T) { newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"} - authStr := EncodeAuth(newAuthConfig) - decAuthConfig, err := DecodeAuth(authStr) + authStr := encodeAuth(newAuthConfig) + decAuthConfig, err := decodeAuth(authStr) if err != nil { t.Fatal(err) } @@ -30,7 +30,7 @@ func TestLogin(t *testing.T) { os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com") defer os.Setenv("DOCKER_INDEX_URL", "") authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp") - status, err := Login(authConfig) + status, err := Login(authConfig, false) if err != nil { t.Fatal(err) } @@ -50,7 +50,7 @@ func TestCreateAccount(t *testing.T) { token := hex.EncodeToString(tokenBuffer)[:12] username := "ut" + token authConfig := NewAuthConfig(username, "test42", "docker-ut+"+token+"@example.com", "/tmp") - status, err := Login(authConfig) + status, err := Login(authConfig, false) if err != nil { t.Fatal(err) } @@ -60,7 +60,7 @@ func TestCreateAccount(t *testing.T) { t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status) } - status, err = Login(authConfig) + status, err = Login(authConfig, false) if err == nil { t.Fatalf("Expected error but found nil instead") } diff --git a/builder.go b/builder.go index 808b7efcab..02c51fb557 100644 --- a/builder.go +++ b/builder.go @@ -2,11 +2,14 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/utils" "os" "path" "time" ) +var defaultDns = []string{"8.8.8.8", "8.8.4.4"} + type Builder struct { runtime *Runtime repositories *TagStore @@ -66,15 +69,26 @@ func (builder *Builder) Create(config *Config) (*Container, error) { return nil, err } + if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns() { + //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns + builder.runtime.Dns = defaultDns + } + // If custom dns exists, then create a resolv.conf for the container - if len(config.Dns) > 0 { + if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 { + var dns []string + if len(config.Dns) > 0 { + dns = config.Dns + } else { + dns = builder.runtime.Dns + } container.ResolvConfPath = path.Join(container.root, "resolv.conf") f, err := os.Create(container.ResolvConfPath) if err != nil { return nil, err } defer f.Close() - for _, dns := range config.Dns { + for _, dns := range dns { if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil { return nil, err } diff --git a/builder_client.go b/builder_client.go index dc9528ff41..d11e7fc995 100644 --- a/builder_client.go +++ b/builder_client.go @@ -304,9 +304,9 @@ func (b *builderClient) Build(dockerfile, context io.Reader) (string, error) { return "", fmt.Errorf("An error occured during the build\n") } -func NewBuilderClient(addr string, port int) BuildFile { +func NewBuilderClient(proto, addr string) BuildFile { return &builderClient{ - cli: NewDockerCli(addr, port), + cli: NewDockerCli(proto, addr), config: &Config{}, tmpContainers: make(map[string]struct{}), tmpImages: make(map[string]struct{}), diff --git a/buildfile.go b/buildfile.go index 3c706b04ba..b8ac55640e 100644 --- a/buildfile.go +++ b/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 { return err } else if cache != nil { + fmt.Fprintf(b.out, " ---> Using cache\n") utils.Debugf("[BUILDER] Use cached version") b.image = cache.ID return nil @@ -178,11 +179,14 @@ func (b *buildFile) CmdAdd(args string) error { cmd := b.config.Cmd // Create the container and start it + b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)} + b.config.Image = b.image container, err := b.builder.Create(b.config) if err != nil { return err } b.tmpContainers[container.ID] = struct{}{} + fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID)) if err := container.EnsureMounted(); err != nil { return err @@ -191,30 +195,26 @@ func (b *buildFile) CmdAdd(args string) error { origPath := path.Join(b.context, orig) destPath := path.Join(container.RootfsPath(), dest) - + // Preserve the trailing '/' + if dest[len(dest)-1] == '/' { + destPath = destPath + "/" + } fi, err := os.Stat(origPath) if err != nil { return err } if fi.IsDir() { - if err := os.MkdirAll(destPath, 0700); err != nil { + if err := CopyWithTar(origPath, destPath); err != nil { return err } - - files, err := ioutil.ReadDir(path.Join(b.context, orig)) - if err != nil { - return err - } - for _, fi := range files { - if err := utils.CopyDirectory(path.Join(origPath, fi.Name()), path.Join(destPath, fi.Name())); err != nil { - return err - } - } - } else { + // First try to unpack the source as an archive + } else if err := UntarPath(origPath, destPath); err != nil { + utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err) + // If that fails, just copy it as a regular file if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil { return err } - if err := utils.CopyDirectory(origPath, destPath); err != nil { + if err := CopyWithTar(origPath, destPath); err != nil { return err } } @@ -237,6 +237,7 @@ func (b *buildFile) run() (string, error) { return "", err } b.tmpContainers[c.ID] = struct{}{} + fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID)) //start the container if err := c.Start(); err != nil { @@ -263,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 { return err } else if cache != nil { + fmt.Fprintf(b.out, " ---> Using cache\n") utils.Debugf("[BUILDER] Use cached version") b.image = cache.ID return nil @@ -276,6 +278,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { return err } b.tmpContainers[container.ID] = struct{}{} + fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID)) if err := container.EnsureMounted(); err != nil { return err @@ -316,6 +319,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { b.context = name } file := bufio.NewReader(dockerfile) + stepN := 0 for { line, err := file.ReadString('\n') if err != nil { @@ -336,12 +340,13 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { } instruction := strings.ToLower(strings.Trim(tmp[0], " ")) 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:])) 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 } ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface() @@ -349,10 +354,10 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, 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 != "" { - 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 "", fmt.Errorf("An error occured during the build\n") diff --git a/buildfile_test.go b/buildfile_test.go index d9c60a70d5..33e6a3146b 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -23,6 +23,12 @@ from ` + unitTestImageName + ` run sh -c 'echo root:testpass > /tmp/passwd' run mkdir -p /var/run/sshd` +// FIXME: test building with a context + +// FIXME: test building with a local ADD as first command + +// FIXME: test building with 2 successive overlapping ADD commands + func TestBuild(t *testing.T) { dockerfiles := []string{Dockerfile, DockerfileNoNewLine} for _, Dockerfile := range dockerfiles { diff --git a/commands.go b/commands.go index d09eb962ce..3d1efe7bf0 100644 --- a/commands.go +++ b/commands.go @@ -20,6 +20,7 @@ import ( "path" "path/filepath" "reflect" + "regexp" "strconv" "strings" "syscall" @@ -28,7 +29,7 @@ import ( "unicode" ) -const VERSION = "0.4.0" +const VERSION = "0.4.4" var ( GITCOMMIT string @@ -39,8 +40,8 @@ func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { return reflect.TypeOf(cli).MethodByName(methodName) } -func ParseCommands(addr string, port int, args ...string) error { - cli := NewDockerCli(addr, port) +func ParseCommands(proto, addr string, args ...string) error { + cli := NewDockerCli(proto, addr) if len(args) > 0 { method, exists := cli.getMethod(args[0]) @@ -73,7 +74,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { return nil } } - help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=\"%s:%d\": Host:port to bind/connect to\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", cli.host, cli.port) + help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[tcp://%s:%d]: tcp://host:port to bind/connect to or unix://path/to/socker to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTHTTPHOST, DEFAULTHTTPPORT) for _, command := range [][2]string{ {"attach", "Attach to a running container"}, {"build", "Build a container from a Dockerfile"}, @@ -196,7 +197,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { v := &url.Values{} v.Set("t", *tag) // Send the multipart request with correct content-type - req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody) + req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), multipartBody) if err != nil { return err } @@ -205,8 +206,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error { req.Header.Set("X-Docker-Context-Compression", compression.Flag()) fmt.Println("Uploading Context...") } - - resp, err := http.DefaultClient.Do(req) + dial, err := net.Dial(cli.proto, cli.addr) + if err != nil { + return err + } + clientconn := httputil.NewClientConn(dial, nil) + resp, err := clientconn.Do(req) + defer clientconn.Close() if err != nil { return err } @@ -634,7 +640,10 @@ func (cli *DockerCli) CmdHistory(args ...string) error { fmt.Fprintln(w, "ID\tCREATED\tCREATED BY") 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() return nil @@ -735,6 +744,15 @@ func (cli *DockerCli) CmdPush(args ...string) error { if err != nil { return err } + nameParts := strings.SplitN(name, "/", 2) + validNamespace := regexp.MustCompile(`^([a-z0-9_]{4,30})$`) + if !validNamespace.MatchString(nameParts[0]) { + return fmt.Errorf("Invalid namespace name (%s), only [a-z0-9_] are allowed, size between 4 and 30", nameParts[0]) + } + validRepo := regexp.MustCompile(`^([a-zA-Z0-9-_.]+)$`) + if !validRepo.MatchString(nameParts[1]) { + return fmt.Errorf("Invalid repository name (%s), only [a-zA-Z0-9-_.] are allowed", nameParts[1]) + } v := url.Values{} v.Set("registry", *registry) @@ -819,7 +837,7 @@ func (cli *DockerCli) CmdImages(args ...string) error { w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED") + fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE") } for _, out := range outs { @@ -837,7 +855,12 @@ func (cli *DockerCli) CmdImages(args ...string) error { } else { fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID)) } - fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) + fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) + if out.VirtualSize > 0 { + fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize)) + } else { + fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size)) + } } else { if *noTrunc { fmt.Fprintln(w, out.ID) @@ -896,15 +919,20 @@ func (cli *DockerCli) CmdPs(args ...string) error { } w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS") + fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tSIZE") } for _, out := range outs { if !*quiet { if *noTrunc { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) } else { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) + } + if out.SizeRootFs > 0 { + fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs)) + } else { + fmt.Fprintf(w, "%s\n", utils.HumanSize(out.SizeRw)) } } else { if *noTrunc { @@ -1016,10 +1044,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error { return nil } - if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", nil, os.Stdout); err != nil { + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", false, nil, os.Stdout); err != nil { return err } - if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", nil, os.Stderr); err != nil { + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", false, nil, os.Stderr); err != nil { return err } return nil @@ -1046,37 +1074,22 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return err } - splitStderr := container.Config.Tty - - connections := 1 - if splitStderr { - connections += 1 + if !container.State.Running { + return fmt.Errorf("Impossible to attach to a stopped container, start it first") } - chErrors := make(chan error, connections) + if container.Config.Tty { cli.monitorTtySize(cmd.Arg(0)) } - if splitStderr { - go func() { - chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?stream=1&stderr=1", false, nil, os.Stderr) - }() - } + v := url.Values{} v.Set("stream", "1") v.Set("stdin", "1") v.Set("stdout", "1") - if !splitStderr { - v.Set("stderr", "1") - } - go func() { - chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout) - }() - for connections > 0 { - err := <-chErrors - if err != nil { - return err - } - connections -= 1 + v.Set("stderr", "1") + + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout); err != nil { + return err } return nil } @@ -1245,16 +1258,6 @@ func (cli *DockerCli) CmdRun(args ...string) error { 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 _, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil) if err != nil { @@ -1263,19 +1266,11 @@ func (cli *DockerCli) CmdRun(args ...string) error { if !config.AttachStdout && !config.AttachStderr { fmt.Println(out.ID) - } - if connections > 0 { - chErrors := make(chan error, connections) + } else { if config.Tty { 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.Set("logs", "1") v.Set("stream", "1") @@ -1286,19 +1281,12 @@ func (cli *DockerCli) CmdRun(args ...string) error { if config.AttachStdout { v.Set("stdout", "1") } - if !splitStderr && config.AttachStderr { + if config.AttachStderr { 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 @@ -1327,7 +1315,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, params = bytes.NewBuffer(buf) } - req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), params) + req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params) if err != nil { return nil, -1, err } @@ -1337,7 +1325,13 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, } else if method == "POST" { req.Header.Set("Content-Type", "plain/text") } - resp, err := http.DefaultClient.Do(req) + dial, err := net.Dial(cli.proto, cli.addr) + if err != nil { + return nil, -1, err + } + clientconn := httputil.NewClientConn(dial, nil) + resp, err := clientconn.Do(req) + defer clientconn.Close() if err != nil { if strings.Contains(err.Error(), "connection refused") { return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") @@ -1362,7 +1356,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e if (method == "POST" || method == "PUT") && in == nil { in = bytes.NewReader([]byte{}) } - req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), in) + req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in) if err != nil { return err } @@ -1370,7 +1364,13 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e if method == "POST" { req.Header.Set("Content-Type", "plain/text") } - resp, err := http.DefaultClient.Do(req) + dial, err := net.Dial(cli.proto, cli.addr) + if err != nil { + return err + } + clientconn := httputil.NewClientConn(dial, nil) + resp, err := clientconn.Do(req) + defer clientconn.Close() if err != nil { if strings.Contains(err.Error(), "connection refused") { return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") @@ -1378,6 +1378,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e return err } defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -1415,19 +1416,24 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e } func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error { + req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil) if err != nil { return err } + req.Header.Set("User-Agent", "Docker-Client/"+VERSION) req.Header.Set("Content-Type", "plain/text") - dial, err := net.Dial("tcp", fmt.Sprintf("%s:%d", cli.host, cli.port)) + + dial, err := net.Dial(cli.proto, cli.addr) if err != nil { return err } clientconn := httputil.NewClientConn(dial, nil) - clientconn.Do(req) defer clientconn.Close() + // Server hijacks the connection, error 'connection closed' expected + clientconn.Do(req) + rwc, br := clientconn.Hijack() defer rwc.Close() @@ -1503,13 +1509,13 @@ func Subcmd(name, signature, description string) *flag.FlagSet { return flags } -func NewDockerCli(addr string, port int) *DockerCli { +func NewDockerCli(proto, addr string) *DockerCli { authConfig, _ := auth.LoadConfig(os.Getenv("HOME")) - return &DockerCli{addr, port, authConfig} + return &DockerCli{proto, addr, authConfig} } type DockerCli struct { - host string - port int + proto string + addr string authConfig *auth.AuthConfig } diff --git a/container.go b/container.go index 4d2032af2e..f60de21bdc 100644 --- a/container.go +++ b/container.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "sort" "strconv" "strings" @@ -919,3 +920,26 @@ func validateID(id string) error { } return nil } + +// GetSize, return real size, virtual size +func (container *Container) GetSize() (int64, int64) { + var sizeRw, sizeRootfs int64 + + filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error { + if fileInfo != nil { + sizeRw += fileInfo.Size() + } + return nil + }) + + _, err := os.Stat(container.RootfsPath()) + if err == nil { + filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error { + if fileInfo != nil { + sizeRootfs += fileInfo.Size() + } + return nil + }) + } + return sizeRw, sizeRootfs +} diff --git a/contrib/install.sh b/contrib/install.sh index 7db577a9da..cf097da670 100755 --- a/contrib/install.sh +++ b/contrib/install.sh @@ -8,7 +8,7 @@ echo "Ensuring basic dependencies are installed..." 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..." if grep -q aufs /proc/filesystems diff --git a/docker/docker.go b/docker/docker.go index 74236613a7..6d79972bd6 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -24,39 +24,29 @@ func main() { docker.SysInit() return } - host := "127.0.0.1" - port := 4243 // FIXME: Switch d and D ? (to be more sshd like) flDaemon := flag.Bool("d", false, "Daemon mode") flDebug := flag.Bool("D", false, "Debug mode") flAutoRestart := flag.Bool("r", false, "Restart previously running containers") bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge") pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") - flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to") flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") + flDns := flag.String("dns", "", "Set custom dns servers") + flHosts := docker.ListOpts{fmt.Sprintf("tcp://%s:%d", docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT)} + flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use") flag.Parse() + if len(flHosts) > 1 { + flHosts = flHosts[1:len(flHosts)] //trick to display a nice defaul value in the usage + } + for i, flHost := range flHosts { + flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost) + } + if *bridgeName != "" { docker.NetworkBridgeIface = *bridgeName } else { docker.NetworkBridgeIface = docker.DefaultNetworkBridge } - - if strings.Contains(*flHost, ":") { - hostParts := strings.Split(*flHost, ":") - if len(hostParts) != 2 { - log.Fatal("Invalid bind address format.") - os.Exit(-1) - } - if hostParts[0] != "" { - host = hostParts[0] - } - if p, err := strconv.Atoi(hostParts[1]); err == nil { - port = p - } - } else { - host = *flHost - } - if *flDebug { os.Setenv("DEBUG", "1") } @@ -66,12 +56,17 @@ func main() { flag.Usage() return } - if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors); err != nil { + if err := daemon(*pidfile, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil { log.Fatal(err) os.Exit(-1) } } else { - if err := docker.ParseCommands(host, port, flag.Args()...); err != nil { + if len(flHosts) > 1 { + log.Fatal("Please specify only one -H") + return + } + protoAddrParts := strings.SplitN(flHosts[0], "://", 2) + if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil { log.Fatal(err) os.Exit(-1) } @@ -105,10 +100,7 @@ func removePidFile(pidfile string) { } } -func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error { - if addr != "127.0.0.1" { - log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") - } +func daemon(pidfile string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error { if err := createPidFile(pidfile); err != nil { log.Fatal(err) } @@ -122,11 +114,37 @@ func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error removePidFile(pidfile) os.Exit(0) }() - - server, err := docker.NewServer(autoRestart, enableCors) + var dns []string + if flDns != "" { + dns = []string{flDns} + } + server, err := docker.NewServer(autoRestart, enableCors, dns) if err != nil { return err } - - return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true) + chErrors := make(chan error, len(protoAddrs)) + for _, protoAddr := range protoAddrs { + protoAddrParts := strings.SplitN(protoAddr, "://", 2) + if protoAddrParts[0] == "unix" { + syscall.Unlink(protoAddrParts[1]); + } else if protoAddrParts[0] == "tcp" { + if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { + log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") + } + } else { + log.Fatal("Invalid protocol format.") + os.Exit(-1) + } + go func() { + chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true) + }() + } + for i :=0 ; i < len(protoAddrs); i+=1 { + err := <-chErrors + if err != nil { + return err + } + } + return nil } + diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 824bfa4657..99e819655d 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -36,6 +36,7 @@ The client should send it's authConfig as POST on each call of /images/(name)/pu .. http:post:: /auth only checks the configuration but doesn't store it on the server Deleting an image is now improved, will only untag the image if it has chidrens and remove all the untagged parents if has any. + .. http:post:: /images//delete now returns a JSON with the list of images deleted/untagged diff --git a/docs/sources/api/docker_remote_api_v1.2.rst b/docs/sources/api/docker_remote_api_v1.2.rst index 3231692102..5448436d75 100644 --- a/docs/sources/api/docker_remote_api_v1.2.rst +++ b/docs/sources/api/docker_remote_api_v1.2.rst @@ -47,28 +47,40 @@ List containers "Image": "base:latest", "Command": "echo 1", "Created": 1367854155, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 }, { "Id": "9cd87474be90", "Image": "base:latest", "Command": "echo 222222", "Created": 1367854155, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 }, { "Id": "3176a2479c92", "Image": "base:latest", "Command": "echo 3333333333333333", "Created": 1367854154, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 }, { "Id": "4cb07b47f9fb", "Image": "base:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, - "Status": "Exit 0" + "Status": "Exit 0", + "Ports":"", + "SizeRw":12288, + "SizeRootFs":0 } ] @@ -488,13 +500,17 @@ List Images "Repository":"base", "Tag":"ubuntu-12.10", "Id":"b750fe79269d", - "Created":1364102658 + "Created":1364102658, + "Size":24653, + "VirtualSize":180116135 }, { "Repository":"base", "Tag":"ubuntu-quantal", "Id":"b750fe79269d", - "Created":1364102658 + "Created":1364102658, + "Size":24653, + "VirtualSize":180116135 } ] @@ -643,7 +659,8 @@ Inspect an image "Image":"base", "Volumes":null, "VolumesFrom":"" - } + }, + "Size": 6824592 } :statuscode 200: no error @@ -674,6 +691,7 @@ Get the history of an image [ { "Id":"b750fe79269d", + "Tag":["base:latest"], "Created":1364102658, "CreatedBy":"/bin/bash" }, @@ -1016,5 +1034,5 @@ In this version of the API, /attach, uses hijacking to transport stdin, stdout a To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode. - docker -d -H="192.168.1.9:4243" -api-enable-cors + docker -d -H="tcp://192.168.1.9:4243" -api-enable-cors diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 02691b4f56..118f42f6e8 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -15,7 +15,7 @@ To list available commands, either run ``docker`` with no parameters or execute $ docker Usage: docker [OPTIONS] COMMAND [arg...] - -H="127.0.0.1:4243": Host:port to bind/connect to + -H=[tcp://127.0.0.1:4243]: tcp://host:port to bind/connect to or unix://path/to/socket to use A self-sufficient runtime for linux containers. diff --git a/docs/sources/commandline/command/import.rst b/docs/sources/commandline/command/import.rst index 34a7138e07..66bcf5de52 100644 --- a/docs/sources/commandline/command/import.rst +++ b/docs/sources/commandline/command/import.rst @@ -8,6 +8,33 @@ :: - Usage: docker import [OPTIONS] URL|- [REPOSITORY [TAG]] + Usage: docker import URL|- [REPOSITORY [TAG]] Create a new filesystem image from the contents of a tarball + +At this time, the URL must start with ``http`` and point to a single file archive (.tar, .tar.gz, .bzip) +containing a root filesystem. If you would like to import from a local directory or archive, +you can use the ``-`` parameter to take the data from standard in. + +Examples +-------- + +Import from a remote location +............................. + +``$ docker import http://example.com/exampleimage.tgz exampleimagerepo`` + +Import from a local file +........................ + +Import to docker via pipe and standard in + +``$ cat exampleimage.tgz | docker import - exampleimagelocal`` + +Import from a local directory +............................. + +``$ sudo tar -c . | docker import - exampleimagedir`` + +Note the ``sudo`` in this example -- you must preserve the ownership of the files (especially root ownership) +during the archiving with tar. If you are not root (or sudo) when you tar, then the ownerships might not get preserved. diff --git a/docs/sources/concepts/containers.rst b/docs/sources/concepts/containers.rst deleted file mode 100644 index e08c3654c0..0000000000 --- a/docs/sources/concepts/containers.rst +++ /dev/null @@ -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. \ No newline at end of file diff --git a/docs/sources/concepts/introduction.rst b/docs/sources/concepts/introduction.rst deleted file mode 100644 index 7251b51786..0000000000 --- a/docs/sources/concepts/introduction.rst +++ /dev/null @@ -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 -^^^^^^^^ - diff --git a/docs/sources/conf.py b/docs/sources/conf.py index d443d34052..41dba70201 100644 --- a/docs/sources/conf.py +++ b/docs/sources/conf.py @@ -20,6 +20,20 @@ import sys, os # -- 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. #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 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # 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, # 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. #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. #html_domain_indices = True diff --git a/docs/sources/contributing/devenvironment.rst b/docs/sources/contributing/devenvironment.rst index 5d937c5a44..1f39364cb1 100644 --- a/docs/sources/contributing/devenvironment.rst +++ b/docs/sources/contributing/devenvironment.rst @@ -33,7 +33,7 @@ Installation sudo apt-get install python-software-properties sudo add-apt-repository ppa:gophers/go sudo apt-get update - sudo apt-get -y install lxc wget bsdtar curl golang-stable git + sudo apt-get -y install lxc xz-utils curl golang-stable git aufs-tools export GOPATH=~/go/ export PATH=$GOPATH/bin:$PATH diff --git a/docs/sources/examples/running_redis_service.rst b/docs/sources/examples/running_redis_service.rst index 48d083aa8f..4996802e93 100644 --- a/docs/sources/examples/running_redis_service.rst +++ b/docs/sources/examples/running_redis_service.rst @@ -72,7 +72,7 @@ Connect to the host os with the redis-cli. docker ps # grab the new container id docker port 6379 # grab the external port - ifconfig # grab the host ip address + ip addr show # grab the host ip address redis-cli -h -p redis 192.168.0.1:49153> set docker awesome OK diff --git a/docs/sources/examples/running_ssh_service.rst b/docs/sources/examples/running_ssh_service.rst index 6183c3a55d..c2f8b86aca 100644 --- a/docs/sources/examples/running_ssh_service.rst +++ b/docs/sources/examples/running_ssh_service.rst @@ -59,6 +59,7 @@ The password is 'screencast' # it has now given us a port to connect to # we have to connect using a public ip of our host $ hostname + # *ifconfig* is deprecated, better use *ip addr show* now $ ifconfig $ ssh root@192.168.33.10 -p 49153 # Ah! forgot to set root passwd @@ -70,6 +71,7 @@ The password is 'screencast' $ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D $ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22 + # *ifconfig* is deprecated, better use *ip addr show* now $ ifconfig $ ssh root@192.168.33.10 -p 49154 # Thanks for watching, Thatcher thatcher@dotcloud.com diff --git a/docs/sources/index/variable.rst b/docs/sources/index/variable.rst deleted file mode 100644 index 90eac3af8a..0000000000 --- a/docs/sources/index/variable.rst +++ /dev/null @@ -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" - diff --git a/docs/sources/installation/binaries.rst b/docs/sources/installation/binaries.rst index e7a07b6db1..6d87787752 100644 --- a/docs/sources/installation/binaries.rst +++ b/docs/sources/installation/binaries.rst @@ -30,8 +30,7 @@ Dependencies: * 3.8 Kernel (read more about :ref:`kernel`) * AUFS filesystem support * lxc -* bsdtar - +* xz-utils Get the docker binary: ---------------------- diff --git a/docs/sources/installation/kernel.rst b/docs/sources/installation/kernel.rst index 6f242e9e10..58730f8191 100644 --- a/docs/sources/installation/kernel.rst +++ b/docs/sources/installation/kernel.rst @@ -100,7 +100,7 @@ Memory and Swap Accounting on Debian/Ubuntu If you use Debian or Ubuntu kernels, and want to enable memory and swap accounting, you must add the following command-line parameters to your kernel:: - cgroup_enable=memory swapaccount + cgroup_enable=memory swapaccount=1 On Debian or Ubuntu systems, if you use the default GRUB bootloader, you can add those parameters by editing ``/etc/default/grub`` and extending @@ -110,6 +110,6 @@ add those parameters by editing ``/etc/default/grub`` and extending And replace it by the following one:: - GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount" + GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1" Then run ``update-grub``, and reboot. diff --git a/docs/sources/use/basics.rst b/docs/sources/use/basics.rst index 444b74db51..7c9e2e9055 100644 --- a/docs/sources/use/basics.rst +++ b/docs/sources/use/basics.rst @@ -33,11 +33,20 @@ Running an interactive shell # allocate a tty, attach stdin and stdout docker run -i -t base /bin/bash -Bind Docker to another host/port --------------------------------- +Bind Docker to another host/port or a unix socket +------------------------------------------------- -If you want Docker to listen to another port and bind to another ip -use -host and -port on both deamon and client +With -H it is possible to make the Docker daemon to listen on a specific ip and port. By default, it will listen on 127.0.0.1:4243 to allow only local connections but you can set it to 0.0.0.0:4243 or a specific host ip to give access to everybody. + +Similarly, the Docker client can use -H to connect to a custom port. + +-H accepts host and port assignment in the following format: tcp://[host][:port] or unix://path +For example: + +* tcp://host -> tcp connection on host:4243 +* tcp://host:port -> tcp connection on host:port +* tcp://:port -> tcp connection on 127.0.0.1:port +* unix://path/to/socket -> unix socket located at path/to/socket .. code-block:: bash @@ -46,6 +55,17 @@ use -host and -port on both deamon and client # Download a base image docker -H :5555 pull base +You can use multiple -H, for example, if you want to listen +on both tcp and a unix socket + +.. code-block:: bash + + # Run docker in daemon mode + sudo /docker -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock + # Download a base image + docker pull base + # OR + docker -H unix:///var/run/docker.sock pull base Starting a long-running worker process -------------------------------------- @@ -82,7 +102,8 @@ Expose a service on a TCP port # Connect to the public port via the host's public address # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. - IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }') + # Replace *eth0* according to your local interface name. + IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }') echo hello world | nc $IP $PORT # Verify that the network connection worked diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index f2e9ce97ce..5ceba4b210 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -15,10 +15,18 @@ steps and commit them along the way, giving you a final image. 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, before finally outputting the ID of your new image. @@ -130,9 +138,32 @@ curl was installed within the image. ``ADD `` -The `ADD` instruction will insert the files from the `` path of the context into `` path -of the container. -The context must be set in order to use this instruction. (see examples) +The `ADD` instruction will copy new files from and add them to the container's filesystem at path ``. + +`` must be the path to a file or directory relative to the source directory being built (also called the +context of the build). + +`` is the path at which the source will be copied in the destination container. + +The copy obeys the following rules: + +If `` is a directory, the entire directory is copied, including filesystem metadata. + +If `` is a tar archive in a recognized compression format (identity, gzip, bzip2 or xz), it +is unpacked as a directory. + +When a directory is copied or unpacked, it has the same behavior as 'tar -x': the result is the union of +a) whatever existed at the destination path and b) the contents of the source tree, with conflicts resolved +in favor of b on a file-by-file basis. + +If `` is any other kind of file, it is copied individually along with its metadata. In this case, +if `` ends with a trailing slash '/', it will be considered a directory and the contents of `` +will be written at `/base()`. +If `` does not end with a trailing slash, it will be considered a regular file and the contents +of `` will be written at ``. + +If `` doesn't exist, it is created along with all missing directories in its path. All new +files and directories are created with mode 0700, uid and gid 0. 3. Dockerfile Examples ====================== diff --git a/docs/sources/use/index.rst b/docs/sources/use/index.rst index a1086c1fd2..2f74f60718 100644 --- a/docs/sources/use/index.rst +++ b/docs/sources/use/index.rst @@ -14,6 +14,7 @@ Contents: basics workingwithrepository + port_redirection builder puppet diff --git a/docs/sources/use/port_redirection.rst b/docs/sources/use/port_redirection.rst new file mode 100644 index 0000000000..5cf848f9ea --- /dev/null +++ b/docs/sources/use/port_redirection.rst @@ -0,0 +1,25 @@ +:title: Port redirection +:description: usage about port redirection +:keywords: Usage, basic port, docker, documentation, examples + + +Port redirection +================ + +Docker can redirect public tcp ports to your container, so it can be reached over the network. +Port redirection is done on ``docker run`` using the -p flag. + +A port redirect is specified as PUBLIC:PRIVATE, where tcp port PUBLIC will be redirected to +tcp port PRIVATE. As a special case, the public port can be omitted, in which case a random +public port will be allocated. + +.. code-block:: bash + + # A random PUBLIC port is redirected to PRIVATE port 80 on the container + docker run -p 80 + + # PUBLIC port 80 is redirected to PRIVATE port 80 + docker run -p 80:80 + + +Default port redirects can be built into a container with the EXPOSE build command. diff --git a/docs/sources/use/workingwithrepository.rst b/docs/sources/use/workingwithrepository.rst index 6fccc01a98..45702597ab 100644 --- a/docs/sources/use/workingwithrepository.rst +++ b/docs/sources/use/workingwithrepository.rst @@ -77,3 +77,28 @@ Now you can commit this image to the repository # for example docker push dhrp/kickassapp docker push + +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" + diff --git a/docs/theme/docker/layout.html b/docs/theme/docker/layout.html index baaaec9155..707888a927 100755 --- a/docs/theme/docker/layout.html +++ b/docs/theme/docker/layout.html @@ -40,6 +40,8 @@ {%- set script_files = script_files + ['_static/js/docs.js'] %} + + {%- for cssfile in css_files %} {%- endfor %} @@ -48,9 +50,8 @@ {%- endfor %} - {%- if favicon %} - - {%- endif %} + + {%- block extrahead %}{% endblock %} @@ -104,11 +105,8 @@
-
-
- -