diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 93a68e0060f..95994fa94e2 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -46,42 +46,42 @@ { "ImportPath": "github.com/docker/docker/pkg/archive", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/pkg/fileutils", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/pkg/ioutils", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/pkg/pools", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/pkg/promise", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/pkg/system", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/pkg/units", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar", "Comment": "v1.4.1-108-g364720b", - "Rev": "364720b5e7e725cdc466171de873eefdb8609a33" + "Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf" }, { "ImportPath": "github.com/elazarl/go-bindata-assetfs", @@ -94,8 +94,7 @@ }, { "ImportPath": "github.com/fsouza/go-dockerclient", - "Comment": "0.2.1-334-g9c377ff", - "Rev": "9c377ffd9aed48a012adf1c3fd517fe98394120b" + "Rev": "d19717788084716e4adff0515be6289aa04bec46" }, { "ImportPath": "github.com/ghodss/yaml", diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive.go index ec45d8546dc..35566520b1c 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive.go @@ -30,11 +30,11 @@ type ( ArchiveReader io.Reader Compression int TarOptions struct { - Includes []string - Excludes []string - Compression Compression - NoLchown bool - Name string + IncludeFiles []string + ExcludePatterns []string + Compression Compression + NoLchown bool + Name string } // Archiver allows the reuse of most utility functions of this package @@ -378,7 +378,7 @@ func escapeName(name string) string { } // TarWithOptions creates an archive from the directory at `path`, only including files whose relative -// paths are included in `options.Includes` (if non-nil) or not in `options.Excludes`. +// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`. func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) { pipeReader, pipeWriter := io.Pipe() @@ -401,12 +401,14 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) // mutating the filesystem and we can see transient errors // from this - if options.Includes == nil { - options.Includes = []string{"."} + if options.IncludeFiles == nil { + options.IncludeFiles = []string{"."} } + seen := make(map[string]bool) + var renamedRelFilePath string // For when tar.Options.Name is set - for _, include := range options.Includes { + for _, include := range options.IncludeFiles { filepath.Walk(filepath.Join(srcPath, include), func(filePath string, f os.FileInfo, err error) error { if err != nil { log.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err) @@ -420,10 +422,19 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) return nil } - skip, err := fileutils.Matches(relFilePath, options.Excludes) - if err != nil { - log.Debugf("Error matching %s", relFilePath, err) - return err + skip := false + + // If "include" is an exact match for the current file + // then even if there's an "excludePatterns" pattern that + // matches it, don't skip it. IOW, assume an explicit 'include' + // is asking for that file no matter what - which is true + // for some files, like .dockerignore and Dockerfile (sometimes) + if include != relFilePath { + skip, err = fileutils.Matches(relFilePath, options.ExcludePatterns) + if err != nil { + log.Debugf("Error matching %s", relFilePath, err) + return err + } } if skip { @@ -433,6 +444,11 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) return nil } + if seen[relFilePath] { + return nil + } + seen[relFilePath] = true + // Rename the base resource if options.Name != "" && filePath == srcPath+"/"+filepath.Base(relFilePath) { renamedRelFilePath = relFilePath @@ -487,7 +503,7 @@ loop: // This keeps "../" as-is, but normalizes "/../" to "/" hdr.Name = filepath.Clean(hdr.Name) - for _, exclude := range options.Excludes { + for _, exclude := range options.ExcludePatterns { if strings.HasPrefix(hdr.Name, exclude) { continue loop } @@ -563,8 +579,8 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error { if options == nil { options = &TarOptions{} } - if options.Excludes == nil { - options.Excludes = []string{} + if options.ExcludePatterns == nil { + options.ExcludePatterns = []string{} } decompressedArchive, err := DecompressStream(archive) if err != nil { diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive_test.go index fdba6fb87cb..6cd95d5ad53 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive_test.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive_test.go @@ -165,8 +165,8 @@ func TestTarUntar(t *testing.T) { Gzip, } { changes, err := tarUntar(t, origin, &TarOptions{ - Compression: c, - Excludes: []string{"3"}, + Compression: c, + ExcludePatterns: []string{"3"}, }) if err != nil { @@ -196,8 +196,8 @@ func TestTarWithOptions(t *testing.T) { opts *TarOptions numChanges int }{ - {&TarOptions{Includes: []string{"1"}}, 1}, - {&TarOptions{Excludes: []string{"2"}}, 1}, + {&TarOptions{IncludeFiles: []string{"1"}}, 1}, + {&TarOptions{ExcludePatterns: []string{"2"}}, 1}, } for _, testCase := range cases { changes, err := tarUntar(t, origin, testCase.opts) diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/changes_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/changes_test.go index 34c0f0da646..6b8f2354b8a 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/changes_test.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/changes_test.go @@ -286,7 +286,7 @@ func TestApplyLayer(t *testing.T) { t.Fatal(err) } - if err := ApplyLayer(src, layerCopy); err != nil { + if _, err := ApplyLayer(src, layerCopy); err != nil { t.Fatal(err) } diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/diff.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/diff.go index ba22c41f3cd..ca282071f5c 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/diff.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/diff.go @@ -15,7 +15,7 @@ import ( "github.com/docker/docker/pkg/system" ) -func UnpackLayer(dest string, layer ArchiveReader) error { +func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) { tr := tar.NewReader(layer) trBuf := pools.BufioReader32KPool.Get(tr) defer pools.BufioReader32KPool.Put(trBuf) @@ -33,9 +33,11 @@ func UnpackLayer(dest string, layer ArchiveReader) error { break } if err != nil { - return err + return 0, err } + size += hdr.Size + // Normalize name, for safety and for a simple is-root check hdr.Name = filepath.Clean(hdr.Name) @@ -48,7 +50,7 @@ func UnpackLayer(dest string, layer ArchiveReader) error { if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { err = os.MkdirAll(parentPath, 0600) if err != nil { - return err + return 0, err } } } @@ -63,12 +65,12 @@ func UnpackLayer(dest string, layer ArchiveReader) error { aufsHardlinks[basename] = hdr if aufsTempdir == "" { if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { - return err + return 0, err } defer os.RemoveAll(aufsTempdir) } if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true); err != nil { - return err + return 0, err } } continue @@ -77,10 +79,10 @@ func UnpackLayer(dest string, layer ArchiveReader) error { path := filepath.Join(dest, hdr.Name) rel, err := filepath.Rel(dest, path) if err != nil { - return err + return 0, err } if strings.HasPrefix(rel, "..") { - return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest)) + return 0, breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest)) } base := filepath.Base(path) @@ -88,7 +90,7 @@ func UnpackLayer(dest string, layer ArchiveReader) error { originalBase := base[len(".wh."):] originalPath := filepath.Join(filepath.Dir(path), originalBase) if err := os.RemoveAll(originalPath); err != nil { - return err + return 0, err } } else { // If path exits we almost always just want to remove and replace it. @@ -98,7 +100,7 @@ func UnpackLayer(dest string, layer ArchiveReader) error { if fi, err := os.Lstat(path); err == nil { if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { if err := os.RemoveAll(path); err != nil { - return err + return 0, err } } } @@ -113,18 +115,18 @@ func UnpackLayer(dest string, layer ArchiveReader) error { linkBasename := filepath.Base(hdr.Linkname) srcHdr = aufsHardlinks[linkBasename] if srcHdr == nil { - return fmt.Errorf("Invalid aufs hardlink") + return 0, fmt.Errorf("Invalid aufs hardlink") } tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename)) if err != nil { - return err + return 0, err } defer tmpFile.Close() srcData = tmpFile } if err := createTarFile(path, dest, srcHdr, srcData, true); err != nil { - return err + return 0, err } // Directory mtimes must be handled at the end to avoid further @@ -139,27 +141,29 @@ func UnpackLayer(dest string, layer ArchiveReader) error { path := filepath.Join(dest, hdr.Name) ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} if err := syscall.UtimesNano(path, ts); err != nil { - return err + return 0, err } } - return nil + + return size, nil } // ApplyLayer parses a diff in the standard layer format from `layer`, and -// applies it to the directory `dest`. -func ApplyLayer(dest string, layer ArchiveReader) error { +// applies it to the directory `dest`. Returns the size in bytes of the +// contents of the layer. +func ApplyLayer(dest string, layer ArchiveReader) (int64, error) { dest = filepath.Clean(dest) // We need to be able to set any perms oldmask, err := system.Umask(0) if err != nil { - return err + return 0, err } defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform layer, err = DecompressStream(layer) if err != nil { - return err + return 0, err } return UnpackLayer(dest, layer) } diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/utils_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/utils_test.go index 3624fe5afa8..9048027203a 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/utils_test.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/archive/utils_test.go @@ -17,7 +17,8 @@ var testUntarFns = map[string]func(string, io.Reader) error{ return Untar(r, dest, nil) }, "applylayer": func(dest string, r io.Reader) error { - return ApplyLayer(dest, ArchiveReader(r)) + _, err := ApplyLayer(dest, ArchiveReader(r)) + return err }, } diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size.go index 264f388225e..7cfb57ba51f 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size.go @@ -39,7 +39,7 @@ var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", // HumanSize returns a human-readable approximation of a size // using SI standard (eg. "44kB", "17MB") -func HumanSize(size int64) string { +func HumanSize(size float64) string { return intToString(float64(size), 1000.0, decimapAbbrs) } diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size_test.go index 3e410b0db85..67c3b81e6b4 100644 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size_test.go +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/units/size_test.go @@ -23,9 +23,9 @@ func TestHumanSize(t *testing.T) { assertEquals(t, "1 MB", HumanSize(1000000)) assertEquals(t, "1.049 MB", HumanSize(1048576)) assertEquals(t, "2 MB", HumanSize(2*MB)) - assertEquals(t, "3.42 GB", HumanSize(int64(float64(3.42*GB)))) - assertEquals(t, "5.372 TB", HumanSize(int64(float64(5.372*TB)))) - assertEquals(t, "2.22 PB", HumanSize(int64(float64(2.22*PB)))) + assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB))) + assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB))) + assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB))) } func TestFromHumanSize(t *testing.T) { diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml index 5a19fae36fb..59233986537 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml @@ -2,6 +2,7 @@ language: go go: - 1.2.2 - 1.3.1 + - 1.4 - tip env: - GOARCH=amd64 diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS index 1470c64dfae..698901d9f4c 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS @@ -3,6 +3,7 @@ Aldrin Leal Andreas Jaekle Andrews Medina +Artem Sidorenko Andy Goldstein Ben McCann Carlos Diaz-Padron @@ -16,6 +17,7 @@ Dawn Chen Ed Eric Anderson Fabio Rehm + Fatih Arslan Flavia Missi Francisco Souza Jari Kolehmainen @@ -25,11 +27,14 @@ Jean-Baptiste Dalido Jeff Mitchell Jeffrey Hulten Johan Euphrosine +Kamil Domanski Karan Misra Kim, Hirokuni Lucas Clemente +Martin Sweeney Máximo Cuadros Ortiz Mike Dillon +Mrunal Patel Omeid Matten Paul Morie Peter Jihoon Kim diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown index 0f95d1fe28b..aa5d70ea619 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown @@ -22,7 +22,7 @@ import ( func main() { endpoint := "unix:///var/run/docker.sock" client, _ := docker.NewClient(endpoint) - imgs, _ := client.ListImages(true) + imgs, _ := client.ListImages(docker.ListImagesOptions{All: false}) for _, img := range imgs { fmt.Println("ID: ", img.ID) fmt.Println("RepoTags: ", img.RepoTags) diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go index 3d86ff26fe1..3b76a4a8239 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go @@ -460,24 +460,34 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin protocol = "tcp" address = c.endpointURL.Host } - dial, err := net.Dial(protocol, address) - if err != nil { - return err + var dial net.Conn + if c.TLSConfig != nil && protocol != "unix" { + dial, err = tlsDial(protocol, address, c.TLSConfig) + if err != nil { + return err + } + } else { + dial, err = net.Dial(protocol, address) + if err != nil { + return err + } } - defer dial.Close() clientconn := httputil.NewClientConn(dial, nil) + defer clientconn.Close() clientconn.Do(req) if success != nil { success <- struct{}{} <-success } rwc, br := clientconn.Hijack() + defer rwc.Close() errs := make(chan error, 2) exit := make(chan bool) go func() { defer close(exit) var err error if setRawTerminal { + // When TTY is ON, use regular copy _, err = io.Copy(stdout, br) } else { _, err = stdCopy(stdout, stderr, br) diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go index c600c84d797..40d6cf3cf40 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go @@ -358,6 +358,7 @@ type HostConfig struct { ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"` VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"` NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"` + IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"` RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"` } diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go index 9ce7b440d30..0659ebd0f82 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go @@ -56,6 +56,34 @@ type Exec struct { ID string `json:"Id,omitempty" yaml:"Id,omitempty"` } +// ExecProcessConfig is a type describing the command associated to a Exec +// instance. It's used in the ExecInspect type. +// +// See http://goo.gl/ypQULN for more details +type ExecProcessConfig struct { + Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"` + User string `json:"user,omitempty" yaml:"user,omitempty"` + Tty bool `json:"tty,omitempty" yaml:"tty,omitempty"` + EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"` + Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"` +} + +// ExecInspect is a type with details about a exec instance, including the +// exit code if the command has finished running. It's returned by a api +// call to /exec/(id)/json +// +// See http://goo.gl/ypQULN for more details +type ExecInspect struct { + ID string `json:"ID,omitempty" yaml:"ID,omitempty"` + Running bool `json:"Running,omitempty" yaml:"Running,omitempty"` + ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"` + OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"` + OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty"` + OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty"` + ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty"` + Container Container `json:"Container,omitempty" yaml:"Container,omitempty"` +} + // CreateExec sets up an exec instance in a running container `id`, returning the exec // instance, or an error in case of failure. // @@ -119,6 +147,26 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error { return err } +// InspectExec returns low-level information about the exec command id. +// +// See http://goo.gl/ypQULN for more details +func (c *Client) InspectExec(id string) (*ExecInspect, error) { + path := fmt.Sprintf("/exec/%s/json", id) + body, status, err := c.do("GET", path, nil) + if status == http.StatusNotFound { + return nil, &NoSuchExec{ID: id} + } + if err != nil { + return nil, err + } + var exec ExecInspect + err = json.Unmarshal(body, &exec) + if err != nil { + return nil, err + } + return &exec, nil +} + // NoSuchExec is the error returned when a given exec instance does not exist. type NoSuchExec struct { ID string diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go index 31de1627c48..22cced52a0b 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "reflect" "strings" "testing" ) @@ -126,3 +127,133 @@ func TestExecResize(t *testing.T) { t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) } } + +func TestExecInspect(t *testing.T) { + jsonExec := `{ + "ID": "32adfeeec34250f9530ce1dafd40c6233832315e065ea6b362d745e2f63cde0e", + "Running": true, + "ExitCode": 0, + "ProcessConfig": { + "privileged": false, + "user": "", + "tty": true, + "entrypoint": "bash", + "arguments": [] + }, + "OpenStdin": true, + "OpenStderr": true, + "OpenStdout": true, + "Container": { + "State": { + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Pid": 29392, + "ExitCode": 0, + "Error": "", + "StartedAt": "2015-01-21T17:08:59.634662178Z", + "FinishedAt": "0001-01-01T00:00:00Z" + }, + "ID": "922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521", + "Created": "2015-01-21T17:08:59.46407212Z", + "Path": "/bin/bash", + "Args": [ + "-lc", + "tsuru_unit_agent http://192.168.50.4:8080 689b30e0ab3adce374346de2e72512138e0e8b75 gtest /var/lib/tsuru/start && tail -f /dev/null" + ], + "Config": { + "Hostname": "922cd0568714", + "Domainname": "", + "User": "ubuntu", + "Memory": 0, + "MemorySwap": 0, + "CpuShares": 100, + "Cpuset": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "PortSpecs": null, + "ExposedPorts": { + "8888/tcp": {} + }, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "Cmd": [ + "/bin/bash", + "-lc", + "tsuru_unit_agent http://192.168.50.4:8080 689b30e0ab3adce374346de2e72512138e0e8b75 gtest /var/lib/tsuru/start && tail -f /dev/null" + ], + "Image": "tsuru/app-gtest", + "Volumes": null, + "WorkingDir": "", + "Entrypoint": null, + "NetworkDisabled": false, + "MacAddress": "", + "OnBuild": null + }, + "Image": "a88060b8b54fde0f7168c86742d0ce83b80f3f10925d85c98fdad9ed00bef544", + "NetworkSettings": { + "IPAddress": "172.17.0.8", + "IPPrefixLen": 16, + "MacAddress": "02:42:ac:11:00:08", + "LinkLocalIPv6Address": "fe80::42:acff:fe11:8", + "LinkLocalIPv6PrefixLen": 64, + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "Gateway": "172.17.42.1", + "IPv6Gateway": "", + "Bridge": "docker0", + "PortMapping": null, + "Ports": { + "8888/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "49156" + } + ] + } + }, + "ResolvConfPath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/resolv.conf", + "HostnamePath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/hostname", + "HostsPath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/hosts", + "Name": "/c7e43b72288ee9d0270a", + "Driver": "aufs", + "ExecDriver": "native-0.2", + "MountLabel": "", + "ProcessLabel": "", + "AppArmorProfile": "", + "RestartCount": 0, + "UpdateDns": false, + "Volumes": {}, + "VolumesRW": {} + } + }` + var expected ExecInspect + err := json.Unmarshal([]byte(jsonExec), &expected) + if err != nil { + t.Fatal(err) + } + fakeRT := &FakeRoundTripper{message: jsonExec, status: http.StatusOK} + client := newTestClient(fakeRT) + expectedID := "32adfeeec34250f9530ce1dafd40c6233832315e065ea6b362d745e2f63cde0e" + execObj, err := client.InspectExec(expectedID) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*execObj, expected) { + t.Errorf("ExecInspect: Expected %#v. Got %#v.", expected, *execObj) + } + req := fakeRT.requests[0] + if req.Method != "GET" { + t.Errorf("ExecInspect: wrong HTTP method. Want %q. Got %q.", "GET", req.Method) + } + expectedURL, _ := url.Parse(client.getURL("/exec/" + expectedID + "/json")) + if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path { + t.Errorf("ExecInspect: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) + } +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tar.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tar.go index 7c4a2043d9e..20051938526 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tar.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tar.go @@ -27,9 +27,9 @@ func createTarStream(srcPath string) (io.ReadCloser, error) { return nil, err } tarOpts := &archive.TarOptions{ - Excludes: excludes, - Compression: archive.Uncompressed, - NoLchown: true, + ExcludePatterns: excludes, + Compression: archive.Uncompressed, + NoLchown: true, } return archive.TarWithOptions(srcPath, tarOpts) } diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/bin/fmtpolice b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/bin/fmtpolice index d13bd0c1888..b0ea012c0db 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/bin/fmtpolice +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/bin/fmtpolice @@ -31,7 +31,7 @@ _lint_verbose() { _install_linter() { if [[ ! -x "${GOPATH}/bin/golint" ]] ; then - go get -u github.com/golang/lint/golint + go get -u -f github.com/golang/lint/golint fi } diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go index da24fb246bf..4f8c72b4bf7 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go @@ -34,7 +34,7 @@ import ( // For more details on the remote API, check http://goo.gl/G3plxW. type DockerServer struct { containers []*docker.Container - execs []*docker.Exec + execs []*docker.ExecInspect cMut sync.RWMutex images []docker.Image iMut sync.RWMutex @@ -99,7 +99,9 @@ func (s *DockerServer) buildMuxer() { s.mux.Path("/containers/{id:.*}/attach").Methods("POST").HandlerFunc(s.handlerWrapper(s.attachContainer)) s.mux.Path("/containers/{id:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeContainer)) s.mux.Path("/containers/{id:.*}/exec").Methods("POST").HandlerFunc(s.handlerWrapper(s.createExecContainer)) + s.mux.Path("/exec/{id:.*}/resize").Methods("POST").HandlerFunc(s.handlerWrapper(s.resizeExecContainer)) s.mux.Path("/exec/{id:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startExecContainer)) + s.mux.Path("/exec/{id:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectExecContainer)) s.mux.Path("/images/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.pullImage)) s.mux.Path("/build").Methods("POST").HandlerFunc(s.handlerWrapper(s.buildImage)) s.mux.Path("/images/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.listImages)) @@ -724,10 +726,31 @@ func (s *DockerServer) getImage(w http.ResponseWriter, r *http.Request) { } func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + container, _, err := s.findContainer(id) + if err != nil { + http.Error(w, err.Error(), http.StatusNotFound) + return + } + exec := docker.ExecInspect{ + ID: "id-exec-created-by-test", + Container: *container, + } + var params docker.CreateExecOptions + err = json.NewDecoder(r.Body).Decode(¶ms) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if len(params.Cmd) > 0 { + exec.ProcessConfig.EntryPoint = params.Cmd[0] + if len(params.Cmd) > 1 { + exec.ProcessConfig.Arguments = params.Cmd[1:] + } + } + s.execs = append(s.execs, &exec) w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") - exec := docker.Exec{ID: "id-exec-created-by-test"} - s.execs = append(s.execs, &exec) json.NewEncoder(w).Encode(map[string]string{"Id": exec.ID}) } @@ -742,3 +765,27 @@ func (s *DockerServer) startExecContainer(w http.ResponseWriter, r *http.Request } w.WriteHeader(http.StatusNotFound) } + +func (s *DockerServer) resizeExecContainer(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + for _, exec := range s.execs { + if exec.ID == id { + w.WriteHeader(http.StatusOK) + return + } + } + w.WriteHeader(http.StatusNotFound) +} + +func (s *DockerServer) inspectExecContainer(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + for _, exec := range s.execs { + if exec.ID == id { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(exec) + return + } + } + w.WriteHeader(http.StatusNotFound) +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go index d8763cbc1bf..8217fb1d64f 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go @@ -1089,3 +1089,83 @@ func TestDefaultHandler(t *testing.T) { t.Fatalf("DefaultHandler: Expected to return server.mux, got: %#v", server.DefaultHandler()) } } + +func TestCreateExecContainer(t *testing.T) { + server := DockerServer{} + addContainers(&server, 2) + server.buildMuxer() + recorder := httptest.NewRecorder() + body := `{"Cmd": ["bash", "-c", "ls"]}` + path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID) + request, _ := http.NewRequest("POST", path, strings.NewReader(body)) + server.ServeHTTP(recorder, request) + if recorder.Code != http.StatusOK { + t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) + } + serverExec := server.execs[0] + var got docker.Exec + err := json.NewDecoder(recorder.Body).Decode(&got) + if err != nil { + t.Fatal(err) + } + if got.ID != serverExec.ID { + t.Errorf("CreateExec: wrong value. Want %#v. Got %#v.", serverExec.ID, got.ID) + } + expected := docker.ExecInspect{ + ID: got.ID, + ProcessConfig: docker.ExecProcessConfig{ + EntryPoint: "bash", + Arguments: []string{"-c", "ls"}, + }, + Container: *server.containers[0], + } + if !reflect.DeepEqual(*serverExec, expected) { + t.Errorf("InspectContainer: wrong value. Want:\n%#v\nGot:\n%#v\n", expected, *serverExec) + } +} + +func TestInspectExecContainer(t *testing.T) { + server := DockerServer{} + addContainers(&server, 1) + server.buildMuxer() + recorder := httptest.NewRecorder() + body := `{"Cmd": ["bash", "-c", "ls"]}` + path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID) + request, _ := http.NewRequest("POST", path, strings.NewReader(body)) + server.ServeHTTP(recorder, request) + if recorder.Code != http.StatusOK { + t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) + } + var got docker.Exec + err := json.NewDecoder(recorder.Body).Decode(&got) + if err != nil { + t.Fatal(err) + } + path = fmt.Sprintf("/exec/%s/json", got.ID) + request, _ = http.NewRequest("GET", path, nil) + server.ServeHTTP(recorder, request) + if recorder.Code != http.StatusOK { + t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) + } + var got2 docker.ExecInspect + err = json.NewDecoder(recorder.Body).Decode(&got2) + if err != nil { + t.Fatal(err) + } + expected := docker.ExecInspect{ + ID: got.ID, + ProcessConfig: docker.ExecProcessConfig{ + EntryPoint: "bash", + Arguments: []string{"-c", "ls"}, + }, + Container: *server.containers[0], + } + got2.Container.State.StartedAt = expected.Container.State.StartedAt + got2.Container.State.FinishedAt = expected.Container.State.FinishedAt + got2.Container.Config = expected.Container.Config + got2.Container.Created = expected.Container.Created + got2.Container.NetworkSettings = expected.Container.NetworkSettings + if !reflect.DeepEqual(got2, expected) { + t.Errorf("InspectContainer: wrong value. Want:\n%#v\nGot:\n%#v\n", expected, got2) + } +} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tls.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tls.go new file mode 100644 index 00000000000..11d571761a8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tls.go @@ -0,0 +1,100 @@ +// Copyright 2014 go-dockerclient authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// The content is borrowed from Docker's own source code to provide a simple +// tls based dialer + +package docker + +import ( + "crypto/tls" + "errors" + "net" + "strings" + "time" +) + +type tlsClientCon struct { + *tls.Conn + rawConn net.Conn +} + +func (c *tlsClientCon) CloseWrite() error { + // Go standard tls.Conn doesn't provide the CloseWrite() method so we do it + // on its underlying connection. + if cwc, ok := c.rawConn.(interface { + CloseWrite() error + }); ok { + return cwc.CloseWrite() + } + return nil +} + +func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) { + // We want the Timeout and Deadline values from dialer to cover the + // whole process: TCP connection and TLS handshake. This means that we + // also need to start our own timers now. + timeout := dialer.Timeout + + if !dialer.Deadline.IsZero() { + deadlineTimeout := dialer.Deadline.Sub(time.Now()) + if timeout == 0 || deadlineTimeout < timeout { + timeout = deadlineTimeout + } + } + + var errChannel chan error + + if timeout != 0 { + errChannel = make(chan error, 2) + time.AfterFunc(timeout, func() { + errChannel <- errors.New("") + }) + } + + rawConn, err := dialer.Dial(network, addr) + if err != nil { + return nil, err + } + + colonPos := strings.LastIndex(addr, ":") + if colonPos == -1 { + colonPos = len(addr) + } + hostname := addr[:colonPos] + + // If no ServerName is set, infer the ServerName + // from the hostname we're connecting to. + if config.ServerName == "" { + // Make a copy to avoid polluting argument or default. + c := *config + c.ServerName = hostname + config = &c + } + + conn := tls.Client(rawConn, config) + + if timeout == 0 { + err = conn.Handshake() + } else { + go func() { + errChannel <- conn.Handshake() + }() + + err = <-errChannel + } + + if err != nil { + rawConn.Close() + return nil, err + } + + // This is Docker difference with standard's crypto/tls package: returned a + // wrapper which holds both the TLS and raw connections. + return &tlsClientCon{conn, rawConn}, nil +} + +func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) { + return tlsDialWithDialer(new(net.Dialer), network, addr, config) +}