diff --git a/MAINTAINERS b/MAINTAINERS index b708af7745b89582e97f190cea15cf8086f11b41..5c02fa671be1c464c379db756f60d6e53093b84e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -409,6 +409,8 @@ made through a pull request. "fredlf", "james", "moxiegirl", + "thaJeztah", + "jamtur01", "spf13", "sven" ] diff --git a/api/client/build.go b/api/client/build.go index fb022e38d9484fa4bc90167b39892631b76ba1ba..e83de976beb187bd030dea120591c114af48103d 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -96,20 +96,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error { } else { root := cmd.Arg(0) if urlutil.IsGitURL(root) { - remoteURL := cmd.Arg(0) - if !urlutil.IsGitTransport(remoteURL) { - remoteURL = "https://" + remoteURL - } - - root, err = ioutil.TempDir("", "docker-build-git") + root, err = utils.GitClone(root) if err != nil { return err } defer os.RemoveAll(root) - - if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil { - return fmt.Errorf("Error trying to use git: %s (%s)", err, output) - } } if _, err := os.Stat(root); err != nil { return err @@ -182,7 +173,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { includes = append(includes, ".dockerignore", *dockerfileName) } - if err = utils.ValidateContextDirectory(root, excludes); err != nil { + if err := utils.ValidateContextDirectory(root, excludes); err != nil { return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err) } options := &archive.TarOptions{ diff --git a/api/client/diff.go b/api/client/diff.go index a22734d046f056cd206de33ccfb1aeee3bfb5ea0..6000c6b388f66a53a85c5a5fc938a0af6bfc1d89 100644 --- a/api/client/diff.go +++ b/api/client/diff.go @@ -31,8 +31,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error { } changes := []types.ContainerChange{} - err = json.NewDecoder(rdr).Decode(&changes) - if err != nil { + if err := json.NewDecoder(rdr).Decode(&changes); err != nil { return err } diff --git a/api/client/export.go b/api/client/export.go index 8f1642f609da98b135c48524564b4d55bab7b3fd..1ff46f9b5701dc35bc49d6850be1af5cf4de4c66 100644 --- a/api/client/export.go +++ b/api/client/export.go @@ -3,7 +3,6 @@ package client import ( "errors" "io" - "net/url" "os" flag "github.com/docker/docker/pkg/mflag" @@ -34,19 +33,9 @@ func (cli *DockerCli) CmdExport(args ...string) error { return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") } - if len(cmd.Args()) == 1 { - image := cmd.Arg(0) - if err := cli.stream("GET", "/containers/"+image+"/export", nil, output, nil); err != nil { - return err - } - } else { - v := url.Values{} - for _, arg := range cmd.Args() { - v.Add("names", arg) - } - if err := cli.stream("GET", "/containers/get?"+v.Encode(), nil, output, nil); err != nil { - return err - } + image := cmd.Arg(0) + if err := cli.stream("GET", "/containers/"+image+"/export", nil, output, nil); err != nil { + return err } return nil diff --git a/api/client/history.go b/api/client/history.go index 79c6f3f7a62442f80c7e1c0baf92a502691760a0..31b8535031f54c4141e84114320143c18ce7f3e0 100644 --- a/api/client/history.go +++ b/api/client/history.go @@ -30,8 +30,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error { } history := []types.ImageHistory{} - err = json.NewDecoder(rdr).Decode(&history) - if err != nil { + if err := json.NewDecoder(rdr).Decode(&history); err != nil { return err } diff --git a/api/client/images.go b/api/client/images.go index 32440d48d12616438475df0e6dc23474e59c2bc8..e39c473749e41b4fbddec5a4e87cc137a242c87b 100644 --- a/api/client/images.go +++ b/api/client/images.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "net/url" - "strings" "text/tabwriter" "time" @@ -18,74 +17,6 @@ import ( "github.com/docker/docker/utils" ) -// FIXME: --viz and --tree are deprecated. Remove them in a future version. -func (cli *DockerCli) walkTree(noTrunc bool, images []*types.Image, byParent map[string][]*types.Image, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *types.Image, prefix string)) { - length := len(images) - if length > 1 { - for index, image := range images { - if index+1 == length { - printNode(cli, noTrunc, image, prefix+"└─") - if subimages, exists := byParent[image.ID]; exists { - cli.walkTree(noTrunc, subimages, byParent, prefix+" ", printNode) - } - } else { - printNode(cli, noTrunc, image, prefix+"\u251C─") - if subimages, exists := byParent[image.ID]; exists { - cli.walkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode) - } - } - } - } else { - for _, image := range images { - printNode(cli, noTrunc, image, prefix+"└─") - if subimages, exists := byParent[image.ID]; exists { - cli.walkTree(noTrunc, subimages, byParent, prefix+" ", printNode) - } - } - } -} - -// FIXME: --viz and --tree are deprecated. Remove them in a future version. -func (cli *DockerCli) printVizNode(noTrunc bool, image *types.Image, prefix string) { - var ( - imageID string - parentID string - ) - if noTrunc { - imageID = image.ID - parentID = image.ParentId - } else { - imageID = stringid.TruncateID(image.ID) - parentID = stringid.TruncateID(image.ParentId) - } - if parentID == "" { - fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID) - } else { - fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID) - } - if image.RepoTags[0] != ":" { - fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", - imageID, imageID, strings.Join(image.RepoTags, "\\n")) - } -} - -// FIXME: --viz and --tree are deprecated. Remove them in a future version. -func (cli *DockerCli) printTreeNode(noTrunc bool, image *types.Image, prefix string) { - var imageID string - if noTrunc { - imageID = image.ID - } else { - imageID = stringid.TruncateID(image.ID) - } - - fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.VirtualSize))) - if image.RepoTags[0] != ":" { - fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.RepoTags, ", ")) - } else { - fmt.Fprint(cli.out, "\n") - } -} - // CmdImages lists the images in a specified repository, or all top-level images if no repository is specified. // // Usage: docker images [OPTIONS] [REPOSITORY] @@ -95,9 +26,6 @@ func (cli *DockerCli) CmdImages(args ...string) error { all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") showDigests := cmd.Bool([]string{"-digests"}, false, "Show digests") - // FIXME: --viz and --tree are deprecated. Remove them in a future version. - flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format") - flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format") flFilter := opts.NewListOpts(nil) cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided") @@ -116,158 +44,83 @@ func (cli *DockerCli) CmdImages(args ...string) error { } matchName := cmd.Arg(0) - // FIXME: --viz and --tree are deprecated. Remove them in a future version. - if *flViz || *flTree { - v := url.Values{ - "all": []string{"1"}, - } - if len(imageFilterArgs) > 0 { - filterJSON, err := filters.ToParam(imageFilterArgs) - if err != nil { - return err - } - v.Set("filters", filterJSON) - } - - rdr, _, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil) + v := url.Values{} + if len(imageFilterArgs) > 0 { + filterJSON, err := filters.ToParam(imageFilterArgs) if err != nil { return err } + v.Set("filters", filterJSON) + } - images := []types.Image{} - err = json.NewDecoder(rdr).Decode(&images) - if err != nil { - return err - } - - var ( - printNode func(cli *DockerCli, noTrunc bool, image *types.Image, prefix string) - startImage *types.Image - - roots = []*types.Image{} - byParent = make(map[string][]*types.Image) - ) - - for _, image := range images { - if image.ParentId == "" { - roots = append(roots, &image) - } else { - if children, exists := byParent[image.ParentId]; exists { - children = append(children, &image) - } else { - byParent[image.ParentId] = []*types.Image{&image} - } - } + if cmd.NArg() == 1 { + // FIXME rename this parameter, to not be confused with the filters flag + v.Set("filter", matchName) + } + if *all { + v.Set("all", "1") + } - if matchName != "" { - if matchName == image.ID || matchName == stringid.TruncateID(image.ID) { - startImage = &image - } + rdr, _, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil) + if err != nil { + return err + } - for _, repotag := range image.RepoTags { - if repotag == matchName { - startImage = &image - } - } - } - } + images := []types.Image{} + if err := json.NewDecoder(rdr).Decode(&images); err != nil { + return err + } - if *flViz { - fmt.Fprintf(cli.out, "digraph docker {\n") - printNode = (*DockerCli).printVizNode + w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) + if !*quiet { + if *showDigests { + fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE") } else { - printNode = (*DockerCli).printTreeNode + fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") } + } - if startImage != nil { - root := []*types.Image{startImage} - cli.walkTree(*noTrunc, root, byParent, "", printNode) - } else if matchName == "" { - cli.walkTree(*noTrunc, roots, byParent, "", printNode) - } - if *flViz { - fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") - } - } else { - v := url.Values{} - if len(imageFilterArgs) > 0 { - filterJSON, err := filters.ToParam(imageFilterArgs) - if err != nil { - return err - } - v.Set("filters", filterJSON) + for _, image := range images { + ID := image.ID + if !*noTrunc { + ID = stringid.TruncateID(ID) } - if cmd.NArg() == 1 { - // FIXME rename this parameter, to not be confused with the filters flag - v.Set("filter", matchName) - } - if *all { - v.Set("all", "1") - } + repoTags := image.RepoTags + repoDigests := image.RepoDigests - rdr, _, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil) - if err != nil { - return err + if len(repoTags) == 1 && repoTags[0] == ":" && len(repoDigests) == 1 && repoDigests[0] == "@" { + // dangling image - clear out either repoTags or repoDigsts so we only show it once below + repoDigests = []string{} } - images := []types.Image{} - err = json.NewDecoder(rdr).Decode(&images) - if err != nil { - return err - } - - w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - if !*quiet { - if *showDigests { - fmt.Fprintln(w, "REPOSITORY\tTAG\tDIGEST\tIMAGE ID\tCREATED\tVIRTUAL SIZE") + // combine the tags and digests lists + tagsAndDigests := append(repoTags, repoDigests...) + for _, repoAndRef := range tagsAndDigests { + repo, ref := parsers.ParseRepositoryTag(repoAndRef) + // default tag and digest to none - if there's a value, it'll be set below + tag := "" + digest := "" + if utils.DigestReference(ref) { + digest = ref } else { - fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") - } - } - - for _, image := range images { - ID := image.ID - if !*noTrunc { - ID = stringid.TruncateID(ID) - } - - repoTags := image.RepoTags - repoDigests := image.RepoDigests - - if len(repoTags) == 1 && repoTags[0] == ":" && len(repoDigests) == 1 && repoDigests[0] == "@" { - // dangling image - clear out either repoTags or repoDigsts so we only show it once below - repoDigests = []string{} + tag = ref } - // combine the tags and digests lists - tagsAndDigests := append(repoTags, repoDigests...) - for _, repoAndRef := range tagsAndDigests { - repo, ref := parsers.ParseRepositoryTag(repoAndRef) - // default tag and digest to none - if there's a value, it'll be set below - tag := "" - digest := "" - if utils.DigestReference(ref) { - digest = ref - } else { - tag = ref - } - - if !*quiet { - if *showDigests { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize))) - } else { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize))) - } + if !*quiet { + if *showDigests { + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize))) } else { - fmt.Fprintln(w, ID) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize))) } + } else { + fmt.Fprintln(w, ID) } } + } - if !*quiet { - w.Flush() - } + if !*quiet { + w.Flush() } return nil } diff --git a/api/client/inspect.go b/api/client/inspect.go index db281795cdb4228d65fdcc23a12d6862f5eb9538..0f327cb4db8d2ae90e4d2ebd56d3f54026b2b244 100644 --- a/api/client/inspect.go +++ b/api/client/inspect.go @@ -34,7 +34,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { } indented := new(bytes.Buffer) - indented.WriteByte('[') + indented.WriteString("[\n") status := 0 isImage := false diff --git a/api/client/ps.go b/api/client/ps.go index 44f5ff0d21ea257c3de4167d742b735299c2528d..6c40c6867b8710ec38037fdfb31af0959d38f720 100644 --- a/api/client/ps.go +++ b/api/client/ps.go @@ -92,8 +92,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { } containers := []types.Container{} - err = json.NewDecoder(rdr).Decode(&containers) - if err != nil { + if err := json.NewDecoder(rdr).Decode(&containers); err != nil { return err } diff --git a/api/client/rename.go b/api/client/rename.go index 278f471f2371d79769970d95b93c4046b22c3782..ebe16963ddf1982570a03dd5f6c4709be2056897 100644 --- a/api/client/rename.go +++ b/api/client/rename.go @@ -1,20 +1,19 @@ package client -import "fmt" +import ( + "fmt" + + flag "github.com/docker/docker/pkg/mflag" +) // CmdRename renames a container. // // Usage: docker rename OLD_NAME NEW_NAME func (cli *DockerCli) CmdRename(args ...string) error { cmd := cli.Subcmd("rename", "OLD_NAME NEW_NAME", "Rename a container", true) - if err := cmd.Parse(args); err != nil { - return nil - } + cmd.Require(flag.Exact, 2) + cmd.ParseFlags(args, true) - if cmd.NArg() != 2 { - cmd.Usage() - return nil - } oldName := cmd.Arg(0) newName := cmd.Arg(1) diff --git a/api/client/rmi.go b/api/client/rmi.go index 11c9ff32d04d8b9c553adb24b2ae195e0cb08359..a8590dc8203fd32a4a442f2a7d478bac2cf16811 100644 --- a/api/client/rmi.go +++ b/api/client/rmi.go @@ -37,8 +37,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error { encounteredError = fmt.Errorf("Error: failed to remove one or more images") } else { dels := []types.ImageDelete{} - err = json.NewDecoder(rdr).Decode(&dels) - if err != nil { + if err := json.NewDecoder(rdr).Decode(&dels); err != nil { fmt.Fprintf(cli.err, "%s\n", err) encounteredError = fmt.Errorf("Error: failed to remove one or more images") continue diff --git a/api/client/search.go b/api/client/search.go index 4e493b234ac422dbf9a68a12b9e84fc942f5b18a..e606d479f14d26bc004546413e2aeef905c97ad8 100644 --- a/api/client/search.go +++ b/api/client/search.go @@ -51,8 +51,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error { } results := ByStars{} - err = json.NewDecoder(rdr).Decode(&results) - if err != nil { + if err := json.NewDecoder(rdr).Decode(&results); err != nil { return err } diff --git a/api/client/top.go b/api/client/top.go index 4975f47597f9e20b62ba08c2a9230d3053b01d0f..ee16fdbf605ae06ad499daf9615c8c2d3e90edbf 100644 --- a/api/client/top.go +++ b/api/client/top.go @@ -31,8 +31,7 @@ func (cli *DockerCli) CmdTop(args ...string) error { } procList := types.ContainerProcessList{} - err = json.NewDecoder(stream).Decode(&procList) - if err != nil { + if err := json.NewDecoder(stream).Decode(&procList); err != nil { return err } diff --git a/api/common.go b/api/common.go index cb627824e96ab0f92be314e68e61921e27cd3310..4a9523cd45c97e24f805ea1908403282926f81b5 100644 --- a/api/common.go +++ b/api/common.go @@ -107,8 +107,7 @@ func MatchesContentType(contentType, expectedType string) bool { // LoadOrCreateTrustKey attempts to load the libtrust key at the given path, // otherwise generates a new one func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) { - err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700) - if err != nil { + if err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700); err != nil { return nil, err } trustKey, err := libtrust.LoadKeyFile(trustKeyPath) diff --git a/api/server/server.go b/api/server/server.go index 2e7ffbf95ed60490bb40391149b677e14a95747c..f962aa30d728d1510219c91f6c06d2481d4320bd 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -277,8 +277,7 @@ func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, if vars == nil { return fmt.Errorf("Missing parameter") } - err := parseForm(r) - if err != nil { + if err := parseForm(r); err != nil { return err } @@ -289,7 +288,7 @@ func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, if sigStr := r.Form.Get("signal"); sigStr != "" { // Check if we passed the signal as a number: // The largest legal signal is 31, so let's parse on 5 bits - sig, err = strconv.ParseUint(sigStr, 10, 5) + sig, err := strconv.ParseUint(sigStr, 10, 5) if err != nil { // The signal is not a number, treat it as a string (either like // "KILL" or like "SIGKILL") @@ -301,7 +300,7 @@ func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, } } - if err = s.daemon.ContainerKill(name, sig); err != nil { + if err := s.daemon.ContainerKill(name, sig); err != nil { return err } @@ -406,15 +405,6 @@ func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w ht return writeJSON(w, http.StatusOK, legacyImages) } -func (s *Server) getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if version.GreaterThan("1.6") { - w.WriteHeader(http.StatusNotFound) - return fmt.Errorf("This is now implemented in the client.") - } - eng.ServeHTTP(w, r) - return nil -} - func (s *Server) getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.Header().Set("Content-Type", "application/json") @@ -1589,7 +1579,6 @@ func createRouter(s *Server, eng *engine.Engine) *mux.Router { "/info": s.getInfo, "/version": s.getVersion, "/images/json": s.getImagesJSON, - "/images/viz": s.getImagesViz, "/images/search": s.getImagesSearch, "/images/get": s.getImagesGet, "/images/{name:.*}/get": s.getImagesGet, diff --git a/builder/internals.go b/builder/internals.go index ba7d45bcb18cc10093b808f1a0a0562df1c8d615..adeadd87c85f1d24c77fc8381e6408b880d80b62 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -32,7 +32,6 @@ import ( "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/progressreader" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/tarsum" "github.com/docker/docker/pkg/urlutil" @@ -148,8 +147,15 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp // do the copy (e.g. hash value if cached). Don't actually do // the copy until we've looked at all src files for _, orig := range args[0 : len(args)-1] { - err := calcCopyInfo(b, cmdName, ©Infos, orig, dest, allowRemote, allowDecompression) - if err != nil { + if err := calcCopyInfo( + b, + cmdName, + ©Infos, + orig, + dest, + allowRemote, + allowDecompression, + ); err != nil { return err } } @@ -483,7 +489,7 @@ func (b *Builder) processImageFrom(img *imagepkg.Image) error { fmt.Fprintf(b.ErrStream, "# Executing %d build triggers\n", nTriggers) } - // Copy the ONBUILD triggers, and remove them from the config, since the config will be commited. + // Copy the ONBUILD triggers, and remove them from the config, since the config will be committed. onBuildTriggers := b.Config.OnBuild b.Config.OnBuild = []string{} @@ -646,14 +652,12 @@ func (b *Builder) addContext(container *daemon.Container, orig, dest string, dec err error destExists = true origPath = path.Join(b.contextPath, orig) - destPath = path.Join(container.RootfsPath(), dest) + destPath string ) - if destPath != container.RootfsPath() { - destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath()) - if err != nil { - return err - } + destPath, err = container.GetResourcePath(dest) + if err != nil { + return err } // Preserve the trailing '/' diff --git a/builder/job.go b/builder/job.go index acffa8b4607b8416ef16941bffc4b9a6ea5a6b20..0ad488aae855cb58b796a5d999473ea98a916874 100644 --- a/builder/job.go +++ b/builder/job.go @@ -6,7 +6,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "strings" "sync" @@ -22,6 +21,7 @@ import ( "github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" ) // whitelist of commands allowed for a commit/import @@ -106,19 +106,12 @@ func Build(d *daemon.Daemon, buildConfig *Config) error { if buildConfig.RemoteURL == "" { context = ioutil.NopCloser(buildConfig.Context) } else if urlutil.IsGitURL(buildConfig.RemoteURL) { - if !urlutil.IsGitTransport(buildConfig.RemoteURL) { - buildConfig.RemoteURL = "https://" + buildConfig.RemoteURL - } - root, err := ioutil.TempDir("", "docker-build-git") + root, err := utils.GitClone(buildConfig.RemoteURL) if err != nil { return err } defer os.RemoveAll(root) - if output, err := exec.Command("git", "clone", "--recursive", buildConfig.RemoteURL, root).CombinedOutput(); err != nil { - return fmt.Errorf("Error trying to use git: %s (%s)", err, output) - } - c, err := archive.Tar(root, archive.Uncompressed) if err != nil { return err diff --git a/builder/parser/line_parsers.go b/builder/parser/line_parsers.go index 5f65a8762fa2fb4b213410e5c86cfd41cb02b5f2..8db360ca3753a5eb225beb1dfa69b72562c021ad 100644 --- a/builder/parser/line_parsers.go +++ b/builder/parser/line_parsers.go @@ -233,7 +233,7 @@ func parseString(rest string) (*Node, map[string]bool, error) { // parseJSON converts JSON arrays to an AST. func parseJSON(rest string) (*Node, map[string]bool, error) { var myJson []interface{} - if err := json.Unmarshal([]byte(rest), &myJson); err != nil { + if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJson); err != nil { return nil, nil, err } diff --git a/cliconfig/config.go b/cliconfig/config.go index 19a92fbd8542474cc40d1b0432e592019658d10a..2a27589d20dd9eeb81c08e27adfbe282b9286c3f 100644 --- a/cliconfig/config.go +++ b/cliconfig/config.go @@ -166,8 +166,7 @@ func (configFile *ConfigFile) Save() error { return err } - err = ioutil.WriteFile(configFile.filename, data, 0600) - if err != nil { + if err := ioutil.WriteFile(configFile.filename, data, 0600); err != nil { return err } diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f3b83315873f43f4eec28a0e3592ba26c4990ab3..5b7a102a6839602f4c7e45dbe72003195cb9f38c 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1151,6 +1151,7 @@ _docker() { --dns --dns-search --exec-driver -e + --exec-opt --fixed-cidr --fixed-cidr-v6 --graph -g diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index d3237588effe86e4e9cc61042b6d8f399cda7bf6..c5359118538fc4dd5800adca4538ab2e8195f509 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -51,6 +51,7 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -s d -l daemon -d 'Enable complete -c docker -f -n '__fish_docker_no_subcommand' -l dns -d 'Force Docker to use specific DNS servers' complete -c docker -f -n '__fish_docker_no_subcommand' -l dns-search -d 'Force Docker to use specific DNS search domains' complete -c docker -f -n '__fish_docker_no_subcommand' -s e -l exec-driver -d 'Force the Docker runtime to use a specific exec driver' +complete -c docker -f -n '__fish_docker_no_subcommand' -l exec-opt -d 'Set exec driver options' complete -c docker -f -n '__fish_docker_no_subcommand' -l fixed-cidr -d 'IPv4 subnet for fixed IPs (e.g. 10.20.0.0/16)' complete -c docker -f -n '__fish_docker_no_subcommand' -l fixed-cidr-v6 -d 'IPv6 subnet for fixed IPs (e.g.: 2001:a02b/48)' complete -c docker -f -n '__fish_docker_no_subcommand' -s G -l group -d 'Group to assign the unix socket specified by -H when running in daemon mode' diff --git a/contrib/docker-device-tool/device_tool.go b/contrib/docker-device-tool/device_tool.go index 9ad094a341a7799b23cf36d2bb4d88a136bda802..0a0b0803d39ac8205baff550d0fbc56e93fc66fb 100644 --- a/contrib/docker-device-tool/device_tool.go +++ b/contrib/docker-device-tool/device_tool.go @@ -125,7 +125,7 @@ func main() { err = devices.ResizePool(size) if err != nil { - fmt.Println("Error resizeing pool: ", err) + fmt.Println("Error resizing pool: ", err) os.Exit(1) } diff --git a/contrib/init/openrc/docker.initd b/contrib/init/openrc/docker.initd index a9d21b17089a384d45a31005d8a6684b3182c033..f251e9af5a51a3f25b6565c70a4f34ec2d366921 100755 --- a/contrib/init/openrc/docker.initd +++ b/contrib/init/openrc/docker.initd @@ -7,6 +7,7 @@ DOCKER_LOGFILE=${DOCKER_LOGFILE:-/var/log/${SVCNAME}.log} DOCKER_PIDFILE=${DOCKER_PIDFILE:-/run/${SVCNAME}.pid} DOCKER_BINARY=${DOCKER_BINARY:-/usr/bin/docker} DOCKER_OPTS=${DOCKER_OPTS:-} +UNSHARE_BINARY=${UNSHARE_BINARY:-/usr/bin/unshare} start() { checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE" @@ -16,11 +17,12 @@ start() { ebegin "Starting docker daemon" start-stop-daemon --start --background \ - --exec "$DOCKER_BINARY" \ + --exec "$UNSHARE_BINARY" \ --pidfile "$DOCKER_PIDFILE" \ --stdout "$DOCKER_LOGFILE" \ --stderr "$DOCKER_LOGFILE" \ - -- -d -p "$DOCKER_PIDFILE" \ + -- --mount \ + -- "$DOCKER_BINARY" -d -p "$DOCKER_PIDFILE" \ $DOCKER_OPTS eend $? } diff --git a/contrib/init/sysvinit-debian/docker b/contrib/init/sysvinit-debian/docker index cf33c837791a056a7367d31f363c8a92fd1e9043..35fd71f13e3a9b13db35533bd625a44cdc1e85ce 100755 --- a/contrib/init/sysvinit-debian/docker +++ b/contrib/init/sysvinit-debian/docker @@ -30,6 +30,7 @@ DOCKER_SSD_PIDFILE=/var/run/$BASE-ssd.pid DOCKER_LOGFILE=/var/log/$BASE.log DOCKER_OPTS= DOCKER_DESC="Docker" +UNSHARE=${UNSHARE:-/usr/bin/unshare} # Get lsb functions . /lib/lsb/init-functions @@ -99,11 +100,11 @@ case "$1" in log_begin_msg "Starting $DOCKER_DESC: $BASE" start-stop-daemon --start --background \ --no-close \ - --exec "$DOCKER" \ + --exec "$UNSHARE" \ --pidfile "$DOCKER_SSD_PIDFILE" \ --make-pidfile \ - -- \ - -d -p "$DOCKER_PIDFILE" \ + -- --mount \ + -- "$DOCKER" -d -p "$DOCKER_PIDFILE" \ $DOCKER_OPTS \ >> "$DOCKER_LOGFILE" 2>&1 log_end_msg $? diff --git a/contrib/init/upstart/docker.conf b/contrib/init/upstart/docker.conf index 4ad6058ed03ec7faef6c5ee12474703a364de7c0..5e8df6e3c207f6c72f2644f9afb3959bdb53d28f 100644 --- a/contrib/init/upstart/docker.conf +++ b/contrib/init/upstart/docker.conf @@ -37,7 +37,7 @@ script if [ -f /etc/default/$UPSTART_JOB ]; then . /etc/default/$UPSTART_JOB fi - exec "$DOCKER" -d $DOCKER_OPTS + exec unshare -m -- "$DOCKER" -d $DOCKER_OPTS end script # Don't emit "started" event until docker.sock is ready. diff --git a/daemon/config.go b/daemon/config.go index 952fb5f743474a36e445b8955095ee583995f940..43b08531b52e1660c1b189f3da12c63d1e766d73 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -29,6 +29,7 @@ type Config struct { GraphDriver string GraphOptions []string ExecDriver string + ExecOptions []string Mtu int SocketGroup string EnableCors bool @@ -70,6 +71,7 @@ func (config *Config) InstallFlags() { flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API") opts.IPVar(&config.Bridge.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports") opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options") + opts.ListVar(&config.ExecOptions, []string{"-exec-opt"}, "Set exec driver options") // FIXME: why the inconsistency between "hosts" and "sockets"? opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use") opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use") diff --git a/daemon/container.go b/daemon/container.go index 8a9f87d215bbe610017d0d85ae04dd973f50b49a..0ec409e12cd5bc76c12ee85124c9e9fc6e93caa7 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -149,8 +149,7 @@ func (container *Container) toDisk() error { return err } - err = ioutil.WriteFile(pth, data, 0666) - if err != nil { + if err := ioutil.WriteFile(pth, data, 0666); err != nil { return err } @@ -211,12 +210,37 @@ func (container *Container) LogEvent(action string) { ) } -func (container *Container) getResourcePath(path string) (string, error) { +// Evaluates `path` in the scope of the container's basefs, with proper path +// sanitisation. Symlinks are all scoped to the basefs of the container, as +// though the container's basefs was `/`. +// +// The basefs of a container is the host-facing path which is bind-mounted as +// `/` inside the container. This method is essentially used to access a +// particular path inside the container as though you were a process in that +// container. +// +// NOTE: The returned path is *only* safely scoped inside the container's basefs +// if no component of the returned path changes (such as a component +// symlinking to a different path) between using this method and using the +// path. See symlink.FollowSymlinkInScope for more details. +func (container *Container) GetResourcePath(path string) (string, error) { cleanPath := filepath.Join("/", path) return symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs) } -func (container *Container) getRootResourcePath(path string) (string, error) { +// Evaluates `path` in the scope of the container's root, with proper path +// sanitisation. Symlinks are all scoped to the root of the container, as +// though the container's root was `/`. +// +// The root of a container is the host-facing configuration metadata directory. +// Only use this method to safely access the container's `container.json` or +// other metadata files. If in doubt, use container.GetResourcePath. +// +// NOTE: The returned path is *only* safely scoped inside the container's root +// if no component of the returned path changes (such as a component +// symlinking to a different path) between using this method and using the +// path. See symlink.FollowSymlinkInScope for more details. +func (container *Container) GetRootResourcePath(path string) (string, error) { cleanPath := filepath.Join("/", path) return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) } @@ -515,7 +539,7 @@ func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser { } func (container *Container) buildHostnameFile() error { - hostnamePath, err := container.getRootResourcePath("hostname") + hostnamePath, err := container.GetRootResourcePath("hostname") if err != nil { return err } @@ -529,7 +553,7 @@ func (container *Container) buildHostnameFile() error { func (container *Container) buildHostsFiles(IP string) error { - hostsPath, err := container.getRootResourcePath("hosts") + hostsPath, err := container.GetRootResourcePath("hosts") if err != nil { return err } @@ -759,7 +783,7 @@ func (container *Container) Unpause() error { func (container *Container) Kill() error { if !container.IsRunning() { - return nil + return fmt.Errorf("Container %s is not running", container.ID) } // 1. Send SIGKILL @@ -895,7 +919,7 @@ func (container *Container) Unmount() error { } func (container *Container) logPath(name string) (string, error) { - return container.getRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name)) + return container.GetRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name)) } func (container *Container) ReadLog(name string) (io.Reader, error) { @@ -907,11 +931,11 @@ func (container *Container) ReadLog(name string) (io.Reader, error) { } func (container *Container) hostConfigPath() (string, error) { - return container.getRootResourcePath("hostconfig.json") + return container.GetRootResourcePath("hostconfig.json") } func (container *Container) jsonPath() (string, error) { - return container.getRootResourcePath("config.json") + return container.GetRootResourcePath("config.json") } // This method must be exported to be used from the lxc template @@ -981,7 +1005,7 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) { } }() - basePath, err := container.getResourcePath(resource) + basePath, err := container.GetResourcePath(resource) if err != nil { return nil, err } @@ -1083,7 +1107,7 @@ func (container *Container) setupContainerDns() error { if err != nil { return err } - container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") + container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") if err != nil { return err } @@ -1244,7 +1268,7 @@ func (container *Container) initializeNetworking() error { return err } - hostsPath, err := container.getRootResourcePath("hosts") + hostsPath, err := container.GetRootResourcePath("hosts") if err != nil { return err } @@ -1375,7 +1399,7 @@ func (container *Container) setupWorkingDirectory() error { if container.Config.WorkingDir != "" { container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) - pth, err := container.getResourcePath(container.Config.WorkingDir) + pth, err := container.GetResourcePath(container.Config.WorkingDir) if err != nil { return err } @@ -1515,6 +1539,9 @@ func (container *Container) getNetworkedContainer() (*Container, error) { if err != nil { return nil, err } + if container == nc { + return nil, fmt.Errorf("cannot join own network") + } if !nc.IsRunning() { return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) } diff --git a/daemon/daemon.go b/daemon/daemon.go index d186854ad8c5f4de7726be8ee4813633ad002349..05de402174c820cf377a6bf23f30f799bce09221 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -225,7 +225,6 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err if container.IsRunning() { logrus.Debugf("killing old running container %s", container.ID) - existingPid := container.Pid container.SetStopped(&execdriver.ExitStatus{ExitCode: 0}) // We only have to handle this for lxc because the other drivers will ensure that @@ -237,11 +236,6 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err cmd := &execdriver.Command{ ID: container.ID, } - var err error - cmd.ProcessConfig.Process, err = os.FindProcess(existingPid) - if err != nil { - logrus.Debugf("cannot find existing process for %d", existingPid) - } daemon.execDriver.Terminate(cmd) } @@ -829,7 +823,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService // Load storage driver driver, err := graphdriver.New(config.Root, config.GraphOptions) if err != nil { - return nil, fmt.Errorf("error intializing graphdriver: %v", err) + return nil, fmt.Errorf("error initializing graphdriver: %v", err) } logrus.Debugf("Using graph driver %s", driver) // register cleanup for graph driver @@ -948,7 +942,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService sysInfo := sysinfo.New(false) const runDir = "/var/run/docker" - ed, err := execdrivers.NewDriver(config.ExecDriver, runDir, config.Root, sysInitPath, sysInfo) + ed, err := execdrivers.NewDriver(config.ExecDriver, config.ExecOptions, runDir, config.Root, sysInitPath, sysInfo) if err != nil { return nil, err } @@ -1187,8 +1181,7 @@ func tempDir(rootDir string) (string, error) { if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" { tmpDir = filepath.Join(rootDir, "tmp") } - err := os.MkdirAll(tmpDir, 0700) - return tmpDir, err + return tmpDir, os.MkdirAll(tmpDir, 0700) } func checkKernel() error { diff --git a/daemon/delete.go b/daemon/delete.go index d398741d7567809a959b4fd8384615ce14064e13..464193b283ab1079d2815a17e7b8398485935766 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -129,6 +129,7 @@ func (daemon *Daemon) commonRm(container *Container, forceRemove bool) (err erro if err != nil && forceRemove { daemon.idIndex.Delete(container.ID) daemon.containers.Delete(container.ID) + os.RemoveAll(container.root) } }() diff --git a/daemon/exec.go b/daemon/exec.go index 22872adc44e89ad23ae4b4cd19376eb950cef89f..9aa102690f4acfed2b9b6c7f7d250f40643a151d 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -214,8 +214,7 @@ func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout // the exitStatus) even after the cmd is done running. go func() { - err := container.Exec(execConfig) - if err != nil { + if err := container.Exec(execConfig); err != nil { execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err) } }() diff --git a/daemon/execdriver/execdrivers/execdrivers.go b/daemon/execdriver/execdrivers/execdrivers.go index f6f97c930266d964352f94850278f903cde428cb..dde0be1f0f463eecf2d9943b016afc4b13e9c1b9 100644 --- a/daemon/execdriver/execdrivers/execdrivers.go +++ b/daemon/execdriver/execdrivers/execdrivers.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/pkg/sysinfo" ) -func NewDriver(name, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) { +func NewDriver(name string, options []string, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) { switch name { case "lxc": // we want to give the lxc driver the full docker root because it needs @@ -18,7 +18,7 @@ func NewDriver(name, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) ( // to be backwards compatible return lxc.NewDriver(root, libPath, initPath, sysInfo.AppArmor) case "native": - return native.NewDriver(path.Join(root, "execdriver", "native"), initPath) + return native.NewDriver(path.Join(root, "execdriver", "native"), initPath, options) } return nil, fmt.Errorf("unknown exec driver %s", name) } diff --git a/daemon/execdriver/lxc/init.go b/daemon/execdriver/lxc/init.go index 6cdbf775ea90c423f07e247372fc979a0d2eb7a2..eca1c02e21d68f9a8d414f2a08d21b6ec6e95241 100644 --- a/daemon/execdriver/lxc/init.go +++ b/daemon/execdriver/lxc/init.go @@ -4,7 +4,6 @@ import ( "encoding/json" "flag" "fmt" - "io/ioutil" "log" "os" "os/exec" @@ -107,12 +106,13 @@ func getArgs() *InitArgs { func setupEnv(args *InitArgs) error { // Get env var env []string - content, err := ioutil.ReadFile(".dockerenv") + dockerenv, err := os.Open(".dockerenv") if err != nil { return fmt.Errorf("Unable to load environment variables: %v", err) } - if err := json.Unmarshal(content, &env); err != nil { - return fmt.Errorf("Unable to unmarshal environment variables: %v", err) + defer dockerenv.Close() + if err := json.NewDecoder(dockerenv).Decode(&env); err != nil { + return fmt.Errorf("Unable to decode environment variables: %v", err) } // Propagate the plugin-specific container env variable env = append(env, "container="+os.Getenv("container")) diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index ad13e1c1eb700bb5d91627ec22d8df4f79ca71a8..afc3f1e45ee4fdb8402f5353ecc9ffe9632b7ff9 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -8,12 +8,14 @@ import ( "os" "os/exec" "path/filepath" + "strings" "sync" "syscall" "time" "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/reexec" sysinfo "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/term" @@ -39,7 +41,7 @@ type driver struct { sync.Mutex } -func NewDriver(root, initPath string) (*driver, error) { +func NewDriver(root, initPath string, options []string) (*driver, error) { meminfo, err := sysinfo.ReadMemInfo() if err != nil { return nil, err @@ -52,11 +54,45 @@ func NewDriver(root, initPath string) (*driver, error) { if err := apparmor.InstallDefaultProfile(); err != nil { return nil, err } + + // choose cgroup manager + // this makes sure there are no breaking changes to people + // who upgrade from versions without native.cgroupdriver opt cgm := libcontainer.Cgroupfs if systemd.UseSystemd() { cgm = libcontainer.SystemdCgroups } + // parse the options + for _, option := range options { + key, val, err := parsers.ParseKeyValueOpt(option) + if err != nil { + return nil, err + } + key = strings.ToLower(key) + switch key { + case "native.cgroupdriver": + // override the default if they set options + switch val { + case "systemd": + if systemd.UseSystemd() { + cgm = libcontainer.SystemdCgroups + } else { + // warn them that they chose the wrong driver + logrus.Warn("You cannot use systemd as native.cgroupdriver, using cgroupfs instead") + } + case "cgroupfs": + cgm = libcontainer.Cgroupfs + default: + return nil, fmt.Errorf("Unknown native.cgroupdriver given %q. try cgroupfs or systemd", val) + } + default: + return nil, fmt.Errorf("Unknown option %s\n", key) + } + } + + logrus.Debugf("Using %v as native.cgroupdriver", cgm) + f, err := libcontainer.New( root, cgm, diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index b5d67fa119d0c72d0eeebf00c1fd3bac2cdcdb4e..42b9d76bedf09313e9f1083e288ca6ee53dbb560 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -218,7 +218,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { } defer file.Close() - if err = file.Truncate(size); err != nil { + if err := file.Truncate(size); err != nil { return "", err } } @@ -697,7 +697,7 @@ func (devices *DeviceSet) setupBaseImage() error { logrus.Debugf("Creating filesystem on base device-mapper thin volume") - if err = devices.activateDeviceIfNeeded(info); err != nil { + if err := devices.activateDeviceIfNeeded(info); err != nil { return err } @@ -706,7 +706,7 @@ func (devices *DeviceSet) setupBaseImage() error { } info.Initialized = true - if err = devices.saveMetadata(info); err != nil { + if err := devices.saveMetadata(info); err != nil { info.Initialized = false return err } @@ -1099,14 +1099,14 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { // If we didn't just create the data or metadata image, we need to // load the transaction id and migrate old metadata if !createdLoopback { - if err = devices.initMetaData(); err != nil { + if err := devices.initMetaData(); err != nil { return err } } // Right now this loads only NextDeviceId. If there is more metadata // down the line, we might have to move it earlier. - if err = devices.loadDeviceSetMetaData(); err != nil { + if err := devices.loadDeviceSetMetaData(); err != nil { return err } @@ -1528,8 +1528,7 @@ func (devices *DeviceSet) MetadataDevicePath() string { func (devices *DeviceSet) getUnderlyingAvailableSpace(loopFile string) (uint64, error) { buf := new(syscall.Statfs_t) - err := syscall.Statfs(loopFile, buf) - if err != nil { + if err := syscall.Statfs(loopFile, buf); err != nil { logrus.Warnf("Couldn't stat loopfile filesystem %v: %v", loopFile, err) return 0, err } diff --git a/daemon/info.go b/daemon/info.go index 270abda598c112449b39108e8837bcba0b1a92fa..df1c0530ccc0a4a051124ca41a0bc79a5316a5ac 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -33,11 +33,15 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { if s, err := operatingsystem.GetOperatingSystem(); err == nil { operatingSystem = s } - if inContainer, err := operatingsystem.IsContainerized(); err != nil { - logrus.Errorf("Could not determine if daemon is containerized: %v", err) - operatingSystem += " (error determining if containerized)" - } else if inContainer { - operatingSystem += " (containerized)" + + // Don't do containerized check on Windows + if runtime.GOOS != "windows" { + if inContainer, err := operatingsystem.IsContainerized(); err != nil { + logrus.Errorf("Could not determine if daemon is containerized: %v", err) + operatingSystem += " (error determining if containerized)" + } else if inContainer { + operatingSystem += " (containerized)" + } } meminfo, err := system.ReadMemInfo() diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index f1397dc3268b249ac05a0f6624a2002a20d3b5df..2fe04d20655926a92de727ccdb0939a1171e0975 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -233,7 +233,7 @@ func InitDriver(config *Config) error { // Configure iptables for link support if config.EnableIptables { if err := setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq); err != nil { - logrus.Errorf("Error configuing iptables: %s", err) + logrus.Errorf("Error configuring iptables: %s", err) return err } // call this on Firewalld reload @@ -355,7 +355,7 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error { if !iptables.Exists(iptables.Filter, "FORWARD", dropArgs...) { logrus.Debugf("Disable inter-container communication") - if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, dropArgs...)...); err != nil { + if output, err := iptables.Raw(append([]string{"-A", "FORWARD"}, dropArgs...)...); err != nil { return fmt.Errorf("Unable to prevent intercontainer communication: %s", err) } else if len(output) != 0 { return fmt.Errorf("Error disabling intercontainer communication: %s", output) @@ -366,7 +366,7 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error { if !iptables.Exists(iptables.Filter, "FORWARD", acceptArgs...) { logrus.Debugf("Enable inter-container communication") - if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, acceptArgs...)...); err != nil { + if output, err := iptables.Raw(append([]string{"-A", "FORWARD"}, acceptArgs...)...); err != nil { return fmt.Errorf("Unable to allow intercontainer communication: %s", err) } else if len(output) != 0 { return fmt.Errorf("Error enabling intercontainer communication: %s", output) diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/daemon/networkdriver/ipallocator/allocator_test.go index fffe6e3389c6ffe4185b5c9c8a68c8fb1ee116c6..6c5c0e4dbcc4ef46c6e7c2adbc9491c3828144b8 100644 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ b/daemon/networkdriver/ipallocator/allocator_test.go @@ -601,7 +601,7 @@ func TestRegisterBadTwice(t *testing.T) { Mask: []byte{255, 255, 255, 248}, } if err := a.RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered { - t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err) + t.Fatalf("Expected ErrNetworkAlreadyRegistered error, got %v", err) } } diff --git a/daemon/stats_collector.go b/daemon/stats_collector.go index 5677a8634a6f33eeb693ae0d0b56dd2c7e1b6d20..22239743a69d570bff60d1b2cd73409e49ab54a8 100644 --- a/daemon/stats_collector.go +++ b/daemon/stats_collector.go @@ -76,22 +76,42 @@ func (s *statsCollector) unsubscribe(c *Container, ch chan interface{}) { } func (s *statsCollector) run() { + type publishersPair struct { + container *Container + publisher *pubsub.Publisher + } + // we cannot determine the capacity here. + // it will grow enough in first iteration + var pairs []publishersPair + for range time.Tick(s.interval) { + systemUsage, err := s.getSystemCpuUsage() + if err != nil { + logrus.Errorf("collecting system cpu usage: %v", err) + continue + } + + // it does not make sense in the first iteration, + // but saves allocations in further iterations + pairs = pairs[:0] + + s.m.Lock() for container, publisher := range s.publishers { - systemUsage, err := s.getSystemCpuUsage() - if err != nil { - logrus.Errorf("collecting system cpu usage for %s: %v", container.ID, err) - continue - } - stats, err := container.Stats() + // copy pointers here to release the lock ASAP + pairs = append(pairs, publishersPair{container, publisher}) + } + s.m.Unlock() + + for _, pair := range pairs { + stats, err := pair.container.Stats() if err != nil { if err != execdriver.ErrNotRunning { - logrus.Errorf("collecting stats for %s: %v", container.ID, err) + logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err) } continue } stats.SystemUsage = systemUsage - publisher.Publish(stats) + pair.publisher.Publish(stats) } } } diff --git a/daemon/volumes.go b/daemon/volumes.go index 4d15023ba717c3b98227b35cd89f6f4f8e3068b9..ea117a1e3fa60b178dbc90f703f0897b902e602b 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -13,7 +13,6 @@ import ( "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/symlink" - "github.com/docker/docker/pkg/system" ) type volumeMount struct { @@ -47,7 +46,7 @@ func (container *Container) createVolumes() error { continue } - realPath, err := container.getResourcePath(path) + realPath, err := container.GetResourcePath(path) if err != nil { return err } @@ -314,21 +313,6 @@ func copyExistingContents(source, destination string) error { return copyOwnership(source, destination) } -// copyOwnership copies the permissions and uid:gid of the source file -// into the destination file -func copyOwnership(source, destination string) error { - stat, err := system.Stat(source) - if err != nil { - return err - } - - if err := os.Chown(destination, int(stat.Uid()), int(stat.Gid())); err != nil { - return err - } - - return os.Chmod(destination, os.FileMode(stat.Mode())) -} - func (container *Container) mountVolumes() error { for dest, source := range container.Volumes { v := container.daemon.volumes.Get(source) @@ -336,7 +320,7 @@ func (container *Container) mountVolumes() error { return fmt.Errorf("could not find volume for %s:%s, impossible to mount", source, dest) } - destPath, err := container.getResourcePath(dest) + destPath, err := container.GetResourcePath(dest) if err != nil { return err } @@ -347,7 +331,7 @@ func (container *Container) mountVolumes() error { } for _, mnt := range container.specialMounts() { - destPath, err := container.getResourcePath(mnt.Destination) + destPath, err := container.GetResourcePath(mnt.Destination) if err != nil { return err } @@ -360,7 +344,7 @@ func (container *Container) mountVolumes() error { func (container *Container) unmountVolumes() { for dest := range container.Volumes { - destPath, err := container.getResourcePath(dest) + destPath, err := container.GetResourcePath(dest) if err != nil { logrus.Errorf("error while unmounting volumes %s: %v", destPath, err) continue @@ -372,7 +356,7 @@ func (container *Container) unmountVolumes() { } for _, mnt := range container.specialMounts() { - destPath, err := container.getResourcePath(mnt.Destination) + destPath, err := container.GetResourcePath(mnt.Destination) if err != nil { logrus.Errorf("error while unmounting volumes %s: %v", destPath, err) continue diff --git a/daemon/volumes_linux.go b/daemon/volumes_linux.go new file mode 100644 index 0000000000000000000000000000000000000000..93fea816598a24f8a633d1bde9cd4eb8684ce8ac --- /dev/null +++ b/daemon/volumes_linux.go @@ -0,0 +1,24 @@ +// +build !windows + +package daemon + +import ( + "os" + + "github.com/docker/docker/pkg/system" +) + +// copyOwnership copies the permissions and uid:gid of the source file +// into the destination file +func copyOwnership(source, destination string) error { + stat, err := system.Stat(source) + if err != nil { + return err + } + + if err := os.Chown(destination, int(stat.Uid()), int(stat.Gid())); err != nil { + return err + } + + return os.Chmod(destination, os.FileMode(stat.Mode())) +} diff --git a/daemon/volumes_windows.go b/daemon/volumes_windows.go new file mode 100644 index 0000000000000000000000000000000000000000..ca1199a542d15f6635a787b4da2e91077e81e3fa --- /dev/null +++ b/daemon/volumes_windows.go @@ -0,0 +1,8 @@ +// +build windows + +package daemon + +// Not supported on Windows +func copyOwnership(source, destination string) error { + return nil +} diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index 0196b6364eef629fc1e3e1cf6c561582e0c935a6..4e7cafe4661d55e0a235109486edd4abfb76ce1a 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -124,124 +124,165 @@ unix://[/path/to/socket] to use. **-v**, **--version**=*true*|*false* Print version information and quit. Default is false. +**--exec-opt**=[] + Set exec driver options. See EXEC DRIVER OPTIONS. + **--selinux-enabled**=*true*|*false* Enable selinux support. Default is false. SELinux does not presently support the BTRFS storage driver. # COMMANDS -**docker-attach(1)** +**attach** Attach to a running container + See **docker-attach(1)** for full documentation on the **attach** command. -**docker-build(1)** +**build** Build an image from a Dockerfile + See **docker-build(1)** for full documentation on the **build** command. -**docker-commit(1)** +**commit** Create a new image from a container's changes + See **docker-commit(1)** for full documentation on the **commit** command. -**docker-cp(1)** +**cp** Copy files/folders from a container's filesystem to the host + See **docker-cp(1)** for full documentation on the **cp** command. -**docker-create(1)** +**create** Create a new container + See **docker-create(1)** for full documentation on the **create** command. -**docker-diff(1)** +**diff** Inspect changes on a container's filesystem + See **docker-diff(1)** for full documentation on the **diff** command. -**docker-events(1)** +**events** Get real time events from the server + See **docker-events(1)** for full documentation on the **events** command. -**docker-exec(1)** +**exec** Run a command in a running container + See **docker-exec(1)** for full documentation on the **exec** command. -**docker-export(1)** +**export** Stream the contents of a container as a tar archive + See **docker-export(1)** for full documentation on the **export** command. -**docker-history(1)** +**history** Show the history of an image + See **docker-history(1)** for full documentation on the **history** command. -**docker-images(1)** +**images** List images + See **docker-images(1)** for full documentation on the **images** command. -**docker-import(1)** +**import** Create a new filesystem image from the contents of a tarball + See **docker-import(1)** for full documentation on the **import** command. -**docker-info(1)** +**info** Display system-wide information + See **docker-info(1)** for full documentation on the **info** command. -**docker-inspect(1)** +**inspect** Return low-level information on a container or image + See **docker-inspect(1)** for full documentation on the **inspect** command. -**docker-kill(1)** +**kill** Kill a running container (which includes the wrapper process and everything inside it) + See **docker-kill(1)** for full documentation on the **kill** command. -**docker-load(1)** +**load** Load an image from a tar archive + See **docker-load(1)** for full documentation on the **load** command. -**docker-login(1)** +**login** Register or login to a Docker Registry + See **docker-login(1)** for full documentation on the **login** command. -**docker-logout(1)** +**logout** Log the user out of a Docker Registry + See **docker-logout(1)** for full documentation on the **logout** command. -**docker-logs(1)** +**logs** Fetch the logs of a container + See **docker-logs(1)** for full documentation on the **logs** command. -**docker-pause(1)** +**pause** Pause all processes within a container + See **docker-pause(1)** for full documentation on the **pause** command. -**docker-port(1)** +**port** Lookup the public-facing port which is NAT-ed to PRIVATE_PORT + See **docker-port(1)** for full documentation on the **port** command. -**docker-ps(1)** +**ps** List containers + See **docker-ps(1)** for full documentation on the **ps** command. -**docker-pull(1)** +**pull** Pull an image or a repository from a Docker Registry + See **docker-pull(1)** for full documentation on the **pull** command. -**docker-push(1)** +**push** Push an image or a repository to a Docker Registry + See **docker-push(1)** for full documentation on the **push** command. -**docker-restart(1)** +**restart** Restart a running container + See **docker-restart(1)** for full documentation on the **restart** command. -**docker-rm(1)** +**rm** Remove one or more containers + See **docker-rm(1)** for full documentation on the **rm** command. -**docker-rmi(1)** +**rmi** Remove one or more images + See **docker-rmi(1)** for full documentation on the **rmi** command. -**docker-run(1)** +**run** Run a command in a new container + See **docker-run(1)** for full documentation on the **run** command. -**docker-save(1)** +**save** Save an image to a tar archive + See **docker-save(1)** for full documentation on the **save** command. -**docker-search(1)** +**search** Search for an image in the Docker index + See **docker-search(1)** for full documentation on the **search** command. -**docker-start(1)** +**start** Start a stopped container + See **docker-start(1)** for full documentation on the **start** command. -**docker-stats(1)** +**stats** Display a live stream of one or more containers' resource usage statistics + See **docker-stats(1)** for full documentation on the **stats** command. -**docker-stop(1)** +**stop** Stop a running container + See **docker-stop(1)** for full documentation on the **stop** command. -**docker-tag(1)** +**tag** Tag an image into a repository + See **docker-tag(1)** for full documentation on the **tag** command. -**docker-top(1)** +**top** Lookup the running processes of a container + See **docker-top(1)** for full documentation on the **top** command. -**docker-unpause(1)** +**unpause** Unpause all processes within a container + See **docker-unpause(1)** for full documentation on the **unpause** command. -**docker-version(1)** +**version** Show the Docker version information + See **docker-version(1)** for full documentation on the **version** command. -**docker-wait(1)** +**wait** Block until a container stops, then print its exit code + See **docker-wait(1)** for full documentation on the **wait** command. # STORAGE DRIVER OPTIONS @@ -319,6 +360,18 @@ for data and metadata: --storage-opt dm.metadatadev=/dev/vdc \ --storage-opt dm.basesize=20G +# EXEC DRIVER OPTIONS + +Use the **--exec-opt** flags to specify options to the exec-driver. The only +driver that accepts this flag is the *native* (libcontainer) driver. As a +result, you must also specify **-s=**native for this option to have effect. The +following is the only *native* option: + +#### native.cgroupdriver +Specifies the management of the container's `cgroups`. You can specify +`cgroupfs` or `systemd`. If you specify `systemd` and it is not available, the +system uses `cgroupfs`. + #### Client For specific client examples please see the man page for the specific Docker command. For example: diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index e425175f61657d58673fb7d2938192efa4379936..fb08e289e1a91833573eb9bdb86699559ddc24fc 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -34,6 +34,7 @@ pages: - ['installation/ubuntulinux.md', 'Installation', 'Ubuntu'] - ['installation/mac.md', 'Installation', 'Mac OS X'] - ['installation/windows.md', 'Installation', 'Microsoft Windows'] +- ['installation/testing-windows-docker-client.md', 'Installation', 'Building and testing the Windows Docker client'] - ['installation/amazon.md', 'Installation', 'Amazon EC2'] - ['installation/archlinux.md', 'Installation', 'Arch Linux'] - ['installation/binaries.md', 'Installation', 'Binaries'] @@ -76,7 +77,7 @@ pages: - ['docker-hub/accounts.md', 'Docker Hub', 'Accounts'] - ['docker-hub/repos.md', 'Docker Hub', 'Repositories'] - ['docker-hub/builds.md', 'Docker Hub', 'Automated Builds'] -- ['docker-hub/official_repos.md', 'Docker Hub', 'Official repo guidelines'] +- ['docker-hub/official_repos.md', 'Docker Hub', 'Official Repositories'] # Docker Hub Enterprise: - ['docker-hub-enterprise/index.md', 'Docker Hub Enterprise', 'Overview' ] @@ -195,21 +196,21 @@ pages: - ['terms/image.md', '**HIDDEN**'] - - # Project: - ['project/index.md', '**HIDDEN**'] -- ['project/who-written-for.md', 'Contribute', 'README first'] -- ['project/software-required.md', 'Contribute', 'Get required software'] -- ['project/set-up-git.md', 'Contribute', 'Configure Git for contributing'] -- ['project/set-up-dev-env.md', 'Contribute', 'Work with a development container'] -- ['project/test-and-docs.md', 'Contribute', 'Run tests and test documentation'] -- ['project/make-a-contribution.md', 'Contribute', 'Understand contribution workflow'] -- ['project/find-an-issue.md', 'Contribute', 'Find an issue'] -- ['project/work-issue.md', 'Contribute', 'Work on an issue'] -- ['project/create-pr.md', 'Contribute', 'Create a pull request'] -- ['project/review-pr.md', 'Contribute', 'Participate in the PR review'] -- ['project/advanced-contributing.md', 'Contribute', 'Advanced contributing'] -- ['project/get-help.md', 'Contribute', 'Where to get help'] -- ['project/coding-style.md', 'Contribute', 'Coding style guide'] -- ['project/doc-style.md', 'Contribute', 'Documentation style guide'] +- ['project/who-written-for.md', 'Contributor', 'README first'] +- ['project/software-required.md', 'Contributor', 'Get required software for Linux or OS X'] +- ['project/software-req-win.md', 'Contributor', 'Get required software for Windows'] +- ['project/set-up-git.md', 'Contributor', 'Configure Git for contributing'] +- ['project/set-up-dev-env.md', 'Contributor', 'Work with a development container'] +- ['project/test-and-docs.md', 'Contributor', 'Run tests and test documentation'] +- ['project/make-a-contribution.md', 'Contributor', 'Understand contribution workflow'] +- ['project/find-an-issue.md', 'Contributor', 'Find an issue'] +- ['project/work-issue.md', 'Contributor', 'Work on an issue'] +- ['project/create-pr.md', 'Contributor', 'Create a pull request'] +- ['project/review-pr.md', 'Contributor', 'Participate in the PR review'] +- ['project/advanced-contributing.md', 'Contributor', 'Advanced contributing'] +- ['project/get-help.md', 'Contributor', 'Where to get help'] +- ['project/coding-style.md', 'Contributor', 'Coding style guide'] +- ['project/doc-style.md', 'Contributor', 'Documentation style guide'] + diff --git a/docs/sources/articles/b2d_volume_resize.md b/docs/sources/articles/b2d_volume_resize.md index 65238c669b579bf75f280aa8cfe5301c5ec6f6a6..53c85909557249b2ec2a87e2bf3199fa59d95882 100644 --- a/docs/sources/articles/b2d_volume_resize.md +++ b/docs/sources/articles/b2d_volume_resize.md @@ -60,7 +60,7 @@ You might need to create the bus before you can add the ISO. ## 5. Add the new VDI image In the settings for the Boot2Docker image in VirtualBox, remove the VMDK image -from the SATA contoller and add the VDI image. +from the SATA controller and add the VDI image. diff --git a/docs/sources/articles/baseimages.md b/docs/sources/articles/baseimages.md index a54f5307ad57089d81b9c1583b4e871d44c4b9e0..a1a7665b704e86082d95aeb9eb58275dfeddc316 100644 --- a/docs/sources/articles/baseimages.md +++ b/docs/sources/articles/baseimages.md @@ -65,4 +65,4 @@ There are lots more resources available to help you write your 'Dockerfile`. * There's a [complete guide to all the instructions](/reference/builder/) available for use in a `Dockerfile` in the reference section. * To help you write a clear, readable, maintainable `Dockerfile`, we've also written a [`Dockerfile` Best Practices guide](/articles/dockerfile_best-practices). -* If you're working on an Official Repo, be sure to check out the [Official Repo Guidelines](/docker-hub/official_repos/). +* If your goal is to create a new Official Repository, be sure to read up on Docker's [Official Repositories](/docker-hub/official_repos/). diff --git a/docs/sources/articles/basics.md b/docs/sources/articles/basics.md index 94264ece64e8855ac6af031a344510b3b383ca13..7d7c154091b7cfb81d850219c39b530f397891a2 100644 --- a/docs/sources/articles/basics.md +++ b/docs/sources/articles/basics.md @@ -172,7 +172,7 @@ will be stored (as a diff). See which images you already have using the # Commit your container to a new named image $ docker commit - # List your containers + # List your images $ docker images You now have an image state from which you can create new instances. diff --git a/docs/sources/articles/dockerfile_best-practices.md b/docs/sources/articles/dockerfile_best-practices.md index 425eb86583ac3a92d84ae74e4afd07448f4f3f44..2604b22453bdc3910e770fab1d9b8c957114e449 100644 --- a/docs/sources/articles/dockerfile_best-practices.md +++ b/docs/sources/articles/dockerfile_best-practices.md @@ -32,13 +32,14 @@ ephemeral as possible. By “ephemeral,” we mean that it can be stopped and destroyed and a new one built and put in place with an absolute minimum of set-up and configuration. -### Use [a .dockerignore file](https://docs.docker.com/reference/builder/#the-dockerignore-file) +### Use a .dockerignore file -For faster uploading and efficiency during `docker build`, you should use -a `.dockerignore` file to exclude files or directories from the build -context and final image. For example, unless`.git` is needed by your build -process or scripts, you should add it to `.dockerignore`, which can save many -megabytes worth of upload time. +In most cases, it's best to put each Dockerfile in an empty directory. Then, +add to that directory only the files needed for building the Dockerfile. To +increase the build's performance, you can exclude files and directories by +adding a `.dockerignore` file to that directory as well. This file supports +exclusion patterns similar to `.gitignore` files. For information on creating one, +see the [.dockerignore file](../../reference/builder/#dockerignore-file). ### Avoid installing unnecessary packages @@ -419,9 +420,9 @@ fail catastrophically if the new build's context is missing the resource being added. Adding a separate tag, as recommended above, will help mitigate this by allowing the `Dockerfile` author to make a choice. -## Examples for official repositories +## Examples for Official Repositories -These Official Repos have exemplary `Dockerfile`s: +These Official Repositories have exemplary `Dockerfile`s: * [Go](https://registry.hub.docker.com/_/golang/) * [Perl](https://registry.hub.docker.com/_/perl/) diff --git a/docs/sources/articles/networking.md b/docs/sources/articles/networking.md index 18529a0864208d1c38bfbbca54425e544bb46a75..823b450c756564f8d74d3d5d442ef9ad2017aaab 100644 --- a/docs/sources/articles/networking.md +++ b/docs/sources/articles/networking.md @@ -576,7 +576,7 @@ As soon as the router wants to send an IPv6 packet to the first container it will transmit a neighbor solicitation request, asking, who has `2001:db8::c009`? But it will get no answer because noone on this subnet has this address. The container with this address is hidden behind the Docker host. -The Docker host has to listen to neighbor solication requests for the container +The Docker host has to listen to neighbor solicitation requests for the container address and send a response that itself is the device that is responsible for the address. This is done by a Kernel feature called `NDP Proxy`. You can enable it by executing diff --git a/docs/sources/articles/puppet.md b/docs/sources/articles/puppet.md index 50504cd475b21fc5d9f30e8b285ecd2847490c16..a1b3d273a4e33cdd55d45fb292353299b9de6186 100644 --- a/docs/sources/articles/puppet.md +++ b/docs/sources/articles/puppet.md @@ -1,5 +1,5 @@ page_title: Using Puppet -page_description: Installating and using Puppet +page_description: Installing and using Puppet page_keywords: puppet, installation, usage, docker, documentation # Using Puppet diff --git a/docs/sources/articles/security.md b/docs/sources/articles/security.md index 39a247c38e9e00d4814c35777fc2048bc038b638..42d15e88c045217a5f585842ad32fa0dfbbd5dff 100644 --- a/docs/sources/articles/security.md +++ b/docs/sources/articles/security.md @@ -249,7 +249,7 @@ may still be utilized by Docker containers on supported kernels, by directly using the clone syscall, or utilizing the 'unshare' utility. Using this, some users may find it possible to drop more capabilities from their process as user namespaces provide -an artifical capabilities set. Likewise, however, this artifical +an artificial capabilities set. Likewise, however, this artificial capabilities set may require use of 'capsh' to restrict the user-namespace capabilities set when using 'unshare'. diff --git a/docs/sources/docker-hub/official_repos.md b/docs/sources/docker-hub/official_repos.md index a101d88c1dc62c2f332ec7aae4de37b948625008..eb73b4bc20117be59ecbeb3a837738d552212e92 100644 --- a/docs/sources/docker-hub/official_repos.md +++ b/docs/sources/docker-hub/official_repos.md @@ -1,189 +1,106 @@ -page_title: Guidelines for official repositories on Docker Hub +page_title: Official Repositories on Docker Hub page_description: Guidelines for Official Repositories on Docker Hub page_keywords: Docker, docker, registry, accounts, plans, Dockerfile, Docker Hub, docs, official, image, documentation -# Guidelines for creating and documenting official repositories - -## Introduction - -You’ve been given the job of creating an image for an Official Repository -hosted on [Docker Hub Registry](https://registry.hub.docker.com/). These are -our guidelines for getting that task done. Even if you’re not -planning to create an Official Repo, you can think of these guidelines as best -practices for image creation generally. - -This document consists of two major sections: - -* A list of expected files, resources and supporting items for your image, -along with best practices for creating those items -* Examples embodying those practices - -## Expected files and resources - -### A Git repository - -Your image needs to live in a Git repository, preferably on GitHub. (If you’d -like to use a different provider, please [contact us](mailto:feedback@docker.com) -directly.) Docker **strongly** recommends that this repo be publicly -accessible. - -If the repo is private or has otherwise limited access, you must provide a -means of at least “read-only” access for both general users and for the -docker-library maintainers, who need access for review and building purposes. - -### A Dockerfile - -Complete information on `Dockerfile`s can be found in the [Reference section](https://docs.docker.com/reference/builder/). -We also have a page discussing [best practices for writing `Dockerfile`s](/articles/dockerfile_best-practices). -Your `Dockerfile` should adhere to the following: - -* It must be written either by using `FROM scratch` or be based on another, -established Official Image. -* It must follow `Dockerfile` best practices. These are discussed on the -[best practices page](/articles/dockerfile_best-practices). In addition, -Docker engineer Michael Crosby has some good tips for `Dockerfiles` in -this [blog post](http://crosbymichael.com/dockerfile-best-practices-take-2.html). - -While [`ONBUILD` triggers](https://docs.docker.com/reference/builder/#onbuild) -are not required, if you choose to use them you should: - -* Build both `ONBUILD` and non-`ONBUILD` images, with the `ONBUILD` image -built `FROM` the non-`ONBUILD` image. -* The `ONBUILD` image should be specifically tagged, for example, `ruby: -latest`and `ruby:onbuild`, or `ruby:2` and `ruby:2-onbuild` - -### A short description - -Include a brief description of your image (in plaintext). Only one description -is required; you don’t need additional descriptions for each tag. The file -should also: - -* Be named `README-short.txt` -* Reside in the repo for the “latest” tag -* Not exceed 100 characters - -### A logo - -Include a logo of your company or the product (png format preferred). Only one -logo is required; you don’t need additional logo files for each tag. The logo -file should have the following characteristics: - -* Be named `logo.png` -* Should reside in the repo for the “latest” tag -* Should fit inside a 200px square, maximized in one dimension (preferably the -width) -* Square or wide (landscape) is preferred over tall (portrait), but exceptions -can be made based on the logo needed - -### A long description - -Include a comprehensive description of your image (in Markdown format, GitHub -flavor preferred). Only one description is required; you don’t need additional -descriptions for each tag. The file should also: - -* Be named `README.md` -* Reside in the repo for the “latest” tag -* Be no longer than absolutely necessary, while still addressing all the -content requirements - -In terms of content, the long description must include the following sections: - -* Overview & links -* How-to/usage -* Issues & contributions - -#### Overview and links - -This section should provide: - -* an overview of the software contained in the image, similar to the -introduction in a Wikipedia entry - -* a selection of links to outside resources that help to describe the software - -* a *mandatory* link to the `Dockerfile` - -#### How-to/usage - -A section that describes how to run and use the image, including common use -cases and example `Dockerfile`s (if applicable). Try to provide clear, step-by- -step instructions wherever possible. - -##### Issues and contributions - -In this section, point users to any resources that can help them contribute to -the project. Include contribution guidelines and any specific instructions -related to your development practices. Include a link to -[Docker’s resources for contributors](https://docs.docker.com/contributing/contributing/). -Be sure to include contact info, handles, etc. for official maintainers. - -Also include information letting users know where they can go for help and how -they can file issues with the repo. Point them to any specific IRC channels, -issue trackers, contacts, additional “how-to” information or other resources. - -### License - -Include a file, `LICENSE`, of any applicable license. Docker recommends using -the license of the software contained in the image, provided it allows Docker, -Inc. to legally build and distribute the image. Otherwise, Docker recommends -adopting the [Expat license](http://directory.fsf.org/wiki/License:Expat) -(a.k.a., the MIT or X11 license). - -## Examples - -Below are sample short and long description files for an imaginary image -containing Ruby on Rails. - -### Short description - -`README-short.txt` - -`Ruby on Rails is an open-source application framework written in Ruby. It emphasizes best practices such as convention over configuration, active record pattern, and the model-view-controller pattern.` - -### Long description - -`README.md` - -```markdown -# What is Ruby on Rails - -Ruby on Rails, often simply referred to as Rails, is an open source web application framework which runs via the Ruby programming language. It is a full-stack framework: it allows creating pages and applications that gather information from the web server, talk to or query the database, and render templates out of the box. As a result, Rails features a routing system that is independent of the web server. - -> [wikipedia.org/wiki/Ruby_on_Rails](https://en.wikipedia.org/wiki/Ruby_on_Rails) - -# How to use this image - -## Create a `Dockerfile` in your rails app project - - FROM rails:onbuild - -Put this file in the root of your app, next to the `Gemfile`. - -This image includes multiple `ONBUILD` triggers so that should be all that you need for most applications. The build will `ADD . /usr/src/app`, `RUN bundle install`, `EXPOSE 3000`, and set the default command to `rails server`. - -Then build and run the Docker image. - - docker build -t my-rails-app . - docker run --name some-rails-app -d my-rails-app - -Test it by visiting `http://container-ip:3000` in a browser. On the other hand, if you need access outside the host on port 8080: - - docker run --name some-rails-app -p 8080:3000 -d my-rails-app - -Then go to `http://localhost:8080` or `http://host-ip:8080` in a browser. -``` - -For more examples, take a look at these repos: - -* [Go](https://github.com/docker-library/golang) -* [PostgreSQL](https://github.com/docker-library/postgres) -* [Buildpack-deps](https://github.com/docker-library/buildpack-deps) -* ["Hello World" minimal container](https://github.com/docker-library/hello-world) -* [Node](https://github.com/docker-library/node) - -## Submit your repo - -Once you've checked off everything in these guidelines, and are confident your -image is ready for primetime, please contact us at -[partners@docker.com](mailto:partners@docker.com) to have your project -considered for the Official Repos program. +# Official Repositories on Docker Hub + +The Docker [Official Repositories](http://registry.hub.docker.com/official) are +a curated set of Docker repositories that are promoted on Docker Hub and +supported by Docker, Inc. They are designed to: + +* Provide essential base OS repositories (for example, + [`ubuntu`](https://registry.hub.docker.com/_/ubuntu/), + [`centos`](https://registry.hub.docker.com/_/centos/)) that serve as the + starting point for the majority of users. + +* Provide drop-in solutions for popular programming language runtimes, data + stores, and other services, similar to what a Platform-as-a-Service (PAAS) + would offer. + +* Exemplify [`Dockerfile` best practices](/articles/dockerfile_best-practices) + and provide clear documentation to serve as a reference for other `Dockerfile` + authors. + +* Ensure that security updates are applied in a timely manner. This is + particularly important as many Official Repositories are some of the most + popular on Docker Hub. + +* Provide a channel for software vendors to redistribute up-to-date and + supported versions of their products. Organization accounts on Docker Hub can + also serve this purpose, without the careful review or restrictions on what + can be published. + +Docker, Inc. sponsors a dedicated team that is responsible for reviewing and +publishing all Official Repositories content. This team works in collaboration +with upstream software maintainers, security experts, and the broader Docker +community. + +While it is preferrable to have upstream software authors maintaining their +corresponding Official Repositories, this is not a strict requirement. Creating +and maintaining images for Official Repositories is a public process. It takes +place openly on GitHub where participation is encouraged. Anyone can provide +feedback, contribute code, suggest process changes, or even propose a new +Official Repository. + +## Should I use Official Repositories? + +New Docker users are encouraged to use the Official Repositories in their +projects. These repositories have clear documentation, promote best practices, +and are designed for the most common use cases. Advanced users are encouraged to +review the Official Repositories as part of their `Dockerfile` learning process. + +A common rationale for diverging from Official Repositories is to optimize for +image size. For instance, many of the programming language stack images contain +a complete build toolchain to support installation of modules that depend on +optimized code. An advanced user could build a custom image with just the +necessary pre-compiled libraries to save space. + +A number of language stacks such as +[`python`](https://registry.hub.docker.com/_/python/) and +[`ruby`](https://registry.hub.docker.com/_/ruby/) have `-slim` tag variants +designed to fill the need for optimization. Even when these "slim" variants are +insufficient, it is still recommended to inherit from an Official Repository +base OS image to leverage the ongoing maintenance work, rather than duplicating +these efforts. + +## How can I get involved? + +All Official Repositories contain a **User Feedback** section in their +documentation which covers the details for that specific repository. In most +cases, the GitHub repository which contains the Dockerfiles for an Official +Repository also has an active issue tracker. General feedback and support +questions should be directed to `#docker-library` on Freenode IRC. + +## How do I create a new Official Repository? + +From a high level, an Official Repository starts out as a proposal in the form +of a set of GitHub pull requests. You'll find detailed and objective proposal +requirements in the following GitHub repositories: + +* [docker-library/official-images](https://github.com/docker-library/official-images) + +* [docker-library/docs](https://github.com/docker-library/docs) + +The Official Repositories team, with help from community contributors, formally +review each proposal and provide feedback to the author. This initial review +process may require a bit of back and forth before the proposal is accepted. + +There are also subjective considerations during the review process. These +subjective concerns boil down to the basic question: "is this image generally +useful?" For example, the [`python`](https://registry.hub.docker.com/_/python/) +Official Repository is "generally useful" to the large Python developer +community, whereas an obscure text adventure game written in Python last week is +not. + +When a new proposal is accepted, the author becomes responsibile for keeping +their images up-to-date and responding to user feedback. The Official +Repositories team becomes responsibile for publishing the images and +documentation on Docker Hub. Updates to the Official Repository follow the same +pull request process, though with less review. The Official Repositories team +ultimately acts as a gatekeeper for all changes, which helps mitigate the risk +of quality and security issues from being introduced. + +> **Note**: If you are interested in proposing an Official Repository, but would +> like to discuss it with Docker, Inc. privately first, please send your +> inquiries to partners@docker.com. There is no fast-track or pay-for-status +> option. diff --git a/docs/sources/docker-hub/repos.md b/docs/sources/docker-hub/repos.md index 0a2fa6550094a8994cc411cda5f9523990757c92..a48040fb5580640c93cb259d0dab414e5fcf63b4 100644 --- a/docs/sources/docker-hub/repos.md +++ b/docs/sources/docker-hub/repos.md @@ -51,10 +51,10 @@ private to public. You can also collaborate on Docker Hub with organizations and groups. You can read more about that [here](accounts/). -## Official repositories +## Official Repositories -The Docker Hub contains a number of [official -repositories](http://registry.hub.docker.com/official). These are +The Docker Hub contains a number of [Official +Repositories](http://registry.hub.docker.com/official). These are certified repositories from vendors and contributors to Docker. They contain Docker images from vendors like Canonical, Oracle, and Red Hat that you can use to build applications and services. @@ -63,9 +63,9 @@ If you use Official Repositories you know you're using a supported, optimized and up-to-date image to power your applications. > **Note:** -> If you would like to contribute an official repository for your -> organization, product or team you can see more information -> [here](https://github.com/docker/stackbrew). +> If you would like to contribute an Official Repository for your +> organization, see [Official Repositories on Docker +> Hub](/docker-hub/official_repos) for more information. ## Private repositories diff --git a/docs/sources/installation/SUSE.md b/docs/sources/installation/SUSE.md index 2a0aa91d9f331045e4698261bbd9303060fa29cf..756ed6b5c143098b7a45ec2ec0f149cda4ced49a 100644 --- a/docs/sources/installation/SUSE.md +++ b/docs/sources/installation/SUSE.md @@ -8,7 +8,7 @@ Docker is available in **openSUSE 12.3 and later**. Please note that due to its current limitations Docker is able to run only **64 bit** architecture. Docker is not part of the official repositories of openSUSE 12.3 and -openSUSE 13.1. Hence it is neccessary to add the [Virtualization +openSUSE 13.1. Hence it is necessary to add the [Virtualization repository](https://build.opensuse.org/project/show/Virtualization) from [OBS](https://build.opensuse.org/) to install the `docker` package. diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index aeee1ecb1f680604c373fbe0a44620b69e726a94..da9e5f59b132b4cfcfce275b941dd25b3e344461 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -28,9 +28,10 @@ To install the latest Debian package (may not be the latest Docker release): To verify that everything has worked as expected: - $ sudo docker run -i -t ubuntu /bin/bash + $ sudo docker run --rm hello-world -Which should download the `ubuntu` image, and then start `bash` in a container. +This command downloads and runs the `hello-world` image in a container. When the +container runs, it prints an informational message. Then, it exits. > **Note**: > If you want to enable memory and swap accounting see diff --git a/docs/sources/installation/rhel.md b/docs/sources/installation/rhel.md index 7be8debce5b1c1c8f6094afcb66cb5d5cef44c6f..b3bd7aa1d08526e778de96974f101b7c2cb48b6e 100644 --- a/docs/sources/installation/rhel.md +++ b/docs/sources/installation/rhel.md @@ -7,7 +7,7 @@ page_keywords: Docker, Docker documentation, requirements, linux, rhel Docker is supported on the following versions of RHEL: - [*Red Hat Enterprise Linux 7 (64-bit)*](#red-hat-enterprise-linux-7-installation) -- [*Red Hat Enterprise Linux 6.5 (64-bit)*](#red-hat-enterprise-linux-6.5-installation) or later +- [*Red Hat Enterprise Linux 6.6 (64-bit)*](#red-hat-enterprise-linux-66-installation) or later ## Kernel support @@ -41,14 +41,14 @@ Portal](https://access.redhat.com/). Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). -## Red Hat Enterprise Linux 6.5 installation +## Red Hat Enterprise Linux 6.6 installation You will need **64 bit** [RHEL -6.5](https://access.redhat.com/site/articles/3078#RHEL6) or later, with -a RHEL 6 kernel version 2.6.32-431 or higher as this has specific kernel -fixes to allow Docker to work. +6.6](https://access.redhat.com/site/articles/3078#RHEL6) or later, with +a RHEL 6 kernel version 2.6.32-504.16.2 or higher as this has specific kernel +fixes to allow Docker to work. Related issues: [#9856](https://github.com/docker/docker/issues/9856). -Docker is available for **RHEL6.5** on EPEL. Please note that +Docker is available for **RHEL6.6** on EPEL. Please note that this package is part of [Extra Packages for Enterprise Linux (EPEL)](https://fedoraproject.org/wiki/EPEL), a community effort to create and maintain additional packages for the RHEL distribution. diff --git a/docs/sources/project/advanced-contributing.md b/docs/sources/project/advanced-contributing.md index f20cbfff9fa0ea0eb2c2a1e863c73f5f3c4f5fc0..7ee7a86cbe5d8f15371ff09b893387b985187f31 100644 --- a/docs/sources/project/advanced-contributing.md +++ b/docs/sources/project/advanced-contributing.md @@ -67,7 +67,7 @@ The following provides greater detail on the process: The design proposals are all online in our GitHub pull requests. + 3Akind%2Fproposal" target="_blank">all online in our GitHub pull requests. 3. Talk to the community about your idea. diff --git a/docs/sources/project/create-pr.md b/docs/sources/project/create-pr.md index e9123c463f378a407d1f0119066551baf449a88e..613ab691123d15fac18b9fd0e68c7c56f789b73e 100644 --- a/docs/sources/project/create-pr.md +++ b/docs/sources/project/create-pr.md @@ -77,7 +77,7 @@ Always rebase and squash your commits before making a pull request. `git commit -s` - Make sure your message includes docker/docker repository. that instead. You'll need to convert what you see in the guide to what is appropriate to your tool. -5. Open a terminal window on your local host and change to your home directory. In Windows, you'll work in your Boot2Docker window instead of Powershell or cmd. +5. Open a terminal window on your local host and change to your home directory. $ cd ~ + + In Windows, you'll work in your Boot2Docker window instead of Powershell or + a `cmd` window. 6. Create a `repos` directory. diff --git a/docs/sources/project/software-req-win.md b/docs/sources/project/software-req-win.md new file mode 100644 index 0000000000000000000000000000000000000000..a7f1378929ec36c2ceb8bf0e77b318223c3f651e --- /dev/null +++ b/docs/sources/project/software-req-win.md @@ -0,0 +1,258 @@ +page_title: Set up for development on Windows +page_description: How to set up a server to test Docker Windows client +page_keywords: development, inception, container, image Dockerfile, dependencies, Go, artifacts, windows + + +# Get the required software for Windows + +This page explains how to get the software you need to use a a Windows Server +2012 or Windows 8 machine for Docker development. Before you begin contributing +you must have: + +- a GitHub account +- Git for Windows (msysGit) +- TDM-GCC, a compiler suite for Windows +- MinGW (tar and xz) +- Go language + +> **Note**: This installation prcedure refers to the `C:\` drive. If you system's main drive +is `D:\` you'll need to substitute that in where appropriate in these +instructions. + +### Get a GitHub account + +To contribute to the Docker project, you will need a GitHub account. A free account is +fine. All the Docker project repositories are public and visible to everyone. + +You should also have some experience using both the GitHub application and `git` +on the command line. + +## Install Git for Windows + +Git for Windows includes several tools including msysGit, which is a build +environment. The environment contains the tools you need for development such as +Git and a Git Bash shell. + +1. Browse to the [Git for Windows](https://msysgit.github.io/) download page. + +2. Click **Download**. + + Windows prompts you to save the file to your machine. + +3. Run the saved file. + + The system displays the **Git Setup** wizard. + +4. Click the **Next** button to move through the wizard and accept all the defaults. + +5. Click **Finish** when you are done. + +## Installing TDM-GCC + +TDM-GCC is a compiler suite for Windows. You'll use this suite to compile the +Docker Go code as you develop. + +1. Browse to + [tdm-gcc download page](http://tdm-gcc.tdragon.net/download). + +2. Click on the lastest 64-bit version of the package. + + Windows prompts you to save the file to your machine + +3. Set up the suite by running the downloaded file. + + The system opens the **TDM-GCC Setup** wizard. + +4. Click **Create**. + +5. Click the **Next** button to move through the wizard and accept all the defaults. + +6. Click **Finish** when you are done. + + +## Installing MinGW (tar and xz) + +MinGW is a minimalist port of the GNU Compiler Collection (GCC). In this +procedure, you first download and install the MinGW installation manager. Then, +you use the manager to install the `tar` and `xz` tools from the collection. + +1. Browse to MinGW + [SourceForge](http://sourceforge.net/projects/mingw/). + +2. Click **Download**. + + Windows prompts you to save the file to your machine + +3. Run the downloaded file. + + The system opens the **MinGW Installation Manager Setup Tool** + +4. Choose **Install** install the MinGW Installation Manager. + +5. Press **Continue**. + + The system installs and then opens the MinGW Installation Manager. + +6. Press **Continue** after the install completes to open the manager. + +7. Select **All Packages > MSYS Base System** from the left hand menu. + + The system displays the available packages. + +8. Click on the the **msys-tar bin** package and choose **Mark for Installation**. + +9. Click on the **msys-xz bin** package and choose **Mark for Installation**. + +10. Select **Installation > Apply Changes**, to install the selected packages. + + The system displays the **Schedule of Pending Actions Dialog**. + + ![windows-mingw](/project/images/windows-mingw.png) + +11. Press **Apply** + + MingGW installs the packages for you. + +12. Close the dialog and the MinGW Installation Manager. + + +## Set up your environment variables + +You'll need to add the compiler to your `Path` environment variable. + +1. Open the **Control Panel**. + +2. Choose **System and Security > System**. + +3. Click the **Advanced system settings** link in the sidebar. + + The system opens the **System Properties** dialog. + +3. Select the **Advanced** tab. + +4. Click **Environment Variables**. + + The system opens the **Environment Variables dialog** dialog. + +5. Locate the **System variables** area and scroll to the **Path** + variable. + + ![windows-mingw](/project/images/path_variable.png) + +6. Click **Edit** to edit the variable (you can also double-click it). + + The system opens the **Edit System Variable** dialog. + +7. Make sure the `Path` includes `C:\TDM-GCC64\bin` + + ![include gcc](/project/images/include_gcc.png) + + If you don't see `C:\TDM-GCC64\bin`, add it. + +8. Press **OK** to close this dialog. + +9. Press **OK** twice to close out of the remaining dialogs. + +## Install Go and cross-compile it + +In this section, you install the Go language. Then, you build the source so that it can cross-compile for `linux/amd64` architectures. + +1. Open [Go Language download](http://golang.org/dl/) page in your browser. + +2. Locate and click the latest `.msi` installer. + + The system prompts you to save the file. + +3. Run the installer. + + The system opens the **Go Programming Langauge Setup** dialog. + +4. Select all the defaults to install. + +5. Press **Finish** to close the installation dialog. + +6. Start a command prompt. + +7. Change to the Go `src` directory. + + cd c:\Go\src + +8. Set the following Go variables + + c:\Go\src> set GOOS=linux + c:\Go\src> set GOARCH=amd64 + +9. Compile the source. + + c:\Go\src> make.bat + + Compiling the source also adds a number of variables to your Windows environment. + +## Get the Docker repository + +In this step, you start a Git `bash` terminal and get the Docker source code from +Github. + +1. Locate the **Git Bash** program and start it. + + Recall that **Git Bash** came with the Git for Windows installation. **Git + Bash** just as it sounds allows you to run a Bash terminal on Windows. + + ![Git Bash](/project/images/git_bash.png) + +2. Change to the root directory. + + $ cd /c/ + +3. Make a `gopath` directory. + + $ mkdir gopath + +4. Go get the `docker/docker` repository. + + $ go.exe get github.com/docker/docker package github.com/docker/docker + imports github.com/docker/docker + imports github.com/docker/docker: no buildable Go source files in C:\gopath\src\github.com\docker\docker + + In the next steps, you create environment variables for you Go paths. + +5. Open the **Control Panel** on your system. + +6. Choose **System and Security > System**. + +7. Click the **Advanced system settings** link in the sidebar. + + The system opens the **System Properties** dialog. + +8. Select the **Advanced** tab. + +9. Click **Environment Variables**. + + The system opens the **Environment Variables dialog** dialog. + +10. Locate the **System variables** area and scroll to the **Path** + variable. + +11. Click **New**. + + Now you are going to create some new variables. These paths you'll create in the next procedure; but you can set them now. + +12. Enter `GOPATH` for the **Variable Name**. + +13. For the **Variable Value** enter the following: + + C:\gopath;C:\gopath\src\github.com\docker\docker\vendor + + +14. Press **OK** to close this dialog. + + The system adds `GOPATH` to the list of **System Variables**. + +15. Press **OK** twice to close out of the remaining dialogs. + + +## Where to go next + +In the next section, you'll [learn how to set up and configure Git for +contributing to Docker](/project/set-up-git/). \ No newline at end of file diff --git a/docs/sources/project/software-required.md b/docs/sources/project/software-required.md index 08a4243aec685cb5e0e490da5f6e0ebe13d7465c..15b9a693526b9489cf44b74555bee07ff0406d39 100644 --- a/docs/sources/project/software-required.md +++ b/docs/sources/project/software-required.md @@ -2,9 +2,10 @@ page_title: Get the required software page_description: Describes the software required to contribute to Docker page_keywords: GitHub account, repository, Docker, Git, Go, make, -# Get the required software +# Get the required software for Linux or OS X -Before you begin contributing you must have: +This page explains how to get the software you need to use a Linux or OS X +machine for Docker development. Before you begin contributing you must have: * a GitHub account * `git` diff --git a/docs/sources/project/test-and-docs.md b/docs/sources/project/test-and-docs.md index 31615abe95eb1a1e9a39c60cd712ae114bb411ca..23b6b0914d6ea7e258ac4628d3937fb61d63b7c9 100644 --- a/docs/sources/project/test-and-docs.md +++ b/docs/sources/project/test-and-docs.md @@ -40,7 +40,7 @@ units each have unit tests and then, together, integration tests that test the interface between the components. The `integration` and `integration-cli` directories in the Docker repository contain integration test code. -Testing is its own speciality. If you aren't familiar with testing techniques, +Testing is its own specialty. If you aren't familiar with testing techniques, there is a lot of information available to you on the Web. For now, you should understand that, the Docker maintainers may ask you to write a new test or change an existing one. @@ -230,6 +230,46 @@ with new memory settings. 6. Restart your container and try your test again. +## Testing just the Windows client + +This explains how to test the Windows client on a Windows server set up as a +development environment. You'll use the **Git Bash** came with the Git for +Windows installation. **Git Bash** just as it sounds allows you to run a Bash +terminal on Windows. + +1. If you don't have one, start a Git Bash terminal. + + ![Git Bash](/project/images/git_bash.png) + +2. Change to the `docker` source directory. + + $ cd /c/gopath/src/github.com/docker/docker + +3. Set `DOCKER_CLIENTONLY` as follows: + + $ export DOCKER_CLIENTONLY=1 + + This ensures you are building only the client binary instead of both the + binary and the daemon. + +4. Set `DOCKER_TEST_HOST` to the `tcp://IP_ADDRESS:2376` value; substitute your +machine's actual IP address, for example: + + $ export DOCKER_TEST_HOST=tcp://263.124.23.200:2376 + +5. Make the binary and the test: + + $ hack/make.sh binary test-integration-cli + + Many tests are skipped on Windows for various reasons. You see which tests + were skipped by re-running the make and passing in the + `TESTFLAGS='-test.v'` value. + + +You can now choose to make changes to the Docker source or the tests. If you +make any changes just run these commands again. + + ## Build and test the documentation The Docker documentation source files are under `docs/sources`. The content is diff --git a/docs/sources/reference/api/docker_remote_api_v1.10.md b/docs/sources/reference/api/docker_remote_api_v1.10.md index cd1a248091c7f2fd1720973a17de5c55c580c240..e13dccf9bae77232bf0d7fc89b77290338d1bb47 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.10.md +++ b/docs/sources/reference/api/docker_remote_api_v1.10.md @@ -1047,7 +1047,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: diff --git a/docs/sources/reference/api/docker_remote_api_v1.11.md b/docs/sources/reference/api/docker_remote_api_v1.11.md index 6aa29456d4565a565c86c018a036bab6c2dc240a..5a2a0e4525341c0aa6e4b6e3698101fd35c0255d 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.11.md +++ b/docs/sources/reference/api/docker_remote_api_v1.11.md @@ -1053,7 +1053,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: diff --git a/docs/sources/reference/api/docker_remote_api_v1.12.md b/docs/sources/reference/api/docker_remote_api_v1.12.md index 439b2a219b2b515460ee6999971534765fe553fe..999aca9ae9839820d26b5b2a4cc8c19e7c6c442d 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.12.md +++ b/docs/sources/reference/api/docker_remote_api_v1.12.md @@ -1115,7 +1115,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: diff --git a/docs/sources/reference/api/docker_remote_api_v1.13.md b/docs/sources/reference/api/docker_remote_api_v1.13.md index f3de203eba31c32467550854babce6555bbf9e05..287008ce31d84edb3ef081fb206bdfed344fdb53 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.13.md +++ b/docs/sources/reference/api/docker_remote_api_v1.13.md @@ -1104,7 +1104,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: diff --git a/docs/sources/reference/api/docker_remote_api_v1.14.md b/docs/sources/reference/api/docker_remote_api_v1.14.md index e3c559d6c281fdaf7f5b86e761be4333a6e90be2..58634b296cada3f4538a7b925bfaf7011e9c4326 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.14.md +++ b/docs/sources/reference/api/docker_remote_api_v1.14.md @@ -1114,7 +1114,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: diff --git a/docs/sources/reference/api/docker_remote_api_v1.15.md b/docs/sources/reference/api/docker_remote_api_v1.15.md index d83275112af703559472d8314b64ad06667047a3..8fcf8cb187ed205f0fbf3d424b12ef442bf774ce 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.15.md +++ b/docs/sources/reference/api/docker_remote_api_v1.15.md @@ -1261,7 +1261,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: @@ -1495,7 +1495,7 @@ Get a tarball containing all images and metadata for the repository specified by `name`. If `name` is a specific name and tag (e.g. ubuntu:latest), then only that image -(and its parents) are returned. If `name` is an image ID, similarly only tha +(and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the 'repositories' file in the tarball, as there were no image names referenced. diff --git a/docs/sources/reference/api/docker_remote_api_v1.16.md b/docs/sources/reference/api/docker_remote_api_v1.16.md index 3110ccbff4372143d7f52b5eb1cb8f26804548b4..9c6159b9d50238afffad2bcbb843e9f723c1dec8 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.16.md +++ b/docs/sources/reference/api/docker_remote_api_v1.16.md @@ -1262,7 +1262,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: @@ -1509,7 +1509,7 @@ Get a tarball containing all images and metadata for the repository specified by `name`. If `name` is a specific name and tag (e.g. ubuntu:latest), then only that image -(and its parents) are returned. If `name` is an image ID, similarly only tha +(and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the 'repositories' file in the tarball, as there were no image names referenced. diff --git a/docs/sources/reference/api/docker_remote_api_v1.17.md b/docs/sources/reference/api/docker_remote_api_v1.17.md index 1551ab18647d5fbb64a60398c5f16d2981bc1069..80f4fccf003017f72ad935f560a55901d14c4e81 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.17.md +++ b/docs/sources/reference/api/docker_remote_api_v1.17.md @@ -1140,7 +1140,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: @@ -1675,7 +1675,7 @@ Get a tarball containing all images and metadata for the repository specified by `name`. If `name` is a specific name and tag (e.g. ubuntu:latest), then only that image -(and its parents) are returned. If `name` is an image ID, similarly only tha +(and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the 'repositories' file in the tarball, as there were no image names referenced. diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index a7e5e55919caf2d24b91bf038fa81edfb030b72b..a91ca8417a121e282c19e0810b3ca3569edb7b15 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -91,6 +91,7 @@ Query Parameters: - **filters** - a json encoded value of the filters (a map[string][]string) to process on the containers list. Available filters: - exited=<int> -- containers with exit code of <int> - status=(restarting|running|paused|exited) + - label=`key` or `key=value` of a container label Status Codes: @@ -1191,6 +1192,7 @@ Query Parameters: - **all** – 1/True/true or 0/False/false, default false - **filters** – a json encoded value of the filters (a map[string][]string) to process on the images list. Available filters: - dangling=true + - label=`key` or `key=value` of an image label ### Build image from a Dockerfile @@ -1250,7 +1252,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: @@ -1791,7 +1793,7 @@ Get a tarball containing all images and metadata for the repository specified by `name`. If `name` is a specific name and tag (e.g. ubuntu:latest), then only that image -(and its parents) are returned. If `name` is an image ID, similarly only tha +(and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the 'repositories' file in the tarball, as there were no image names referenced. diff --git a/docs/sources/reference/api/docker_remote_api_v1.19.md b/docs/sources/reference/api/docker_remote_api_v1.19.md index a3b580f1b4a83fdd42c4df4ce99f9c261981c311..cede2e1073dd7038d011d55e4683067f583ef611 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.19.md +++ b/docs/sources/reference/api/docker_remote_api_v1.19.md @@ -91,6 +91,7 @@ Query Parameters: - **filters** - a json encoded value of the filters (a map[string][]string) to process on the containers list. Available filters: - exited=<int> -- containers with exit code of <int> - status=(restarting|running|paused|exited) + - label=`key` or `key=value` of a container label Status Codes: @@ -1194,6 +1195,7 @@ Query Parameters: - **all** – 1/True/true or 0/False/false, default false - **filters** – a json encoded value of the filters (a map[string][]string) to process on the images list. Available filters: - dangling=true + - label=`key` or `key=value` of an image label ### Build image from a Dockerfile @@ -1253,7 +1255,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: @@ -1794,7 +1796,7 @@ Get a tarball containing all images and metadata for the repository specified by `name`. If `name` is a specific name and tag (e.g. ubuntu:latest), then only that image -(and its parents) are returned. If `name` is an image ID, similarly only tha +(and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the 'repositories' file in the tarball, as there were no image names referenced. diff --git a/docs/sources/reference/api/docker_remote_api_v1.9.md b/docs/sources/reference/api/docker_remote_api_v1.9.md index bef67a071476e886085c6a95924b2403e4fb9880..26c6b453e0345437a44d8c0cb368dc48b23f43f6 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.9.md +++ b/docs/sources/reference/api/docker_remote_api_v1.9.md @@ -1052,7 +1052,7 @@ Query Parameters: Request Headers: - **Content-type** – should be set to `"application/tar"`. -- **X-Registry-Config** – base64-encoded ConfigFile objec +- **X-Registry-Config** – base64-encoded ConfigFile object Status Codes: diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index 83121e6bd9379c56aaa4658b6f190584e7022d7f..7dbe549237f1318b12022446095690de50c20659 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -41,10 +41,11 @@ whole context must be transferred to the daemon. The Docker CLI reports > repository, the entire contents of your hard drive will get sent to the daemon (and > thus to the machine running the daemon). You probably don't want that. -In most cases, it's best to put each Dockerfile in an empty directory, and then add only -the files needed for building that Dockerfile to that directory. To further speed up the -build, you can exclude files and directories by adding a `.dockerignore` file to the same -directory. +In most cases, it's best to put each Dockerfile in an empty directory. Then, +only add the files needed for building the Dockerfile to the directory. To +increase the build's performance, you can exclude files and directories by +adding a `.dockerignore` file to the directory. For information about how to +[create a `.dockerignore` file](#the-dockerignore-file) on this page. You can specify a repository and tag at which to save the new image if the build succeeds: @@ -128,7 +129,7 @@ modifiers as specified below: * `${variable:-word}` indicates that if `variable` is set then the result will be that value. If `variable` is not set then `word` will be the result. -* `${variable:+word}` indiates that if `variable` is set then `word` will be +* `${variable:+word}` indicates that if `variable` is set then `word` will be the result, otherwise the result is the empty string. In all cases, `word` can be any string, including additional environment @@ -158,7 +159,7 @@ The instructions that handle environment variables in the `Dockerfile` are: `ONBUILD` instructions are **NOT** supported for environment replacement, even the instructions above. -Environment variable subtitution will use the same value for each variable +Environment variable substitution will use the same value for each variable throughout the entire command. In other words, in this example: ENV abc=hello @@ -169,43 +170,67 @@ will result in `def` having a value of `hello`, not `bye`. However, `ghi` will have a value of `bye` because it is not part of the same command that set `abc` to `bye`. -## The `.dockerignore` file +### .dockerignore file -If a file named `.dockerignore` exists in the source repository, then it -is interpreted as a newline-separated list of exclusion patterns. -Exclusion patterns match files or directories relative to the source repository -that will be excluded from the context. Globbing is done using Go's +If a file named `.dockerignore` exists in the root of `PATH`, then Docker +interprets it as a newline-separated list of exclusion patterns. Docker excludes +files or directories relative to `PATH` that match these exclusion patterns. If +there are any `.dockerignore` files in `PATH` subdirectories, Docker treats +them as normal files. + +Filepaths in `.dockerignore` are absolute with the current directory as the +root. Wildcards are allowed but the search is not recursive. Globbing (file name +expansion) is done using Go's [filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. -> **Note**: -> The `.dockerignore` file can even be used to ignore the `Dockerfile` and -> `.dockerignore` files. This might be useful if you are copying files from -> the root of the build context into your new container but do not want to -> include the `Dockerfile` or `.dockerignore` files (e.g. `ADD . /someDir/`). +You can specify exceptions to exclusion rules. To do this, simply prefix a +pattern with an `!` (exclamation mark) in the same way you would in a +`.gitignore` file. Currently there is no support for regular expressions. +Formats like `[^temp*]` are ignored. -The following example shows the use of the `.dockerignore` file to exclude the -`.git` directory from the context. Its effect can be seen in the changed size of -the uploaded context. +The following is an example `.dockerignore` file: + +``` + */temp* + */*/temp* + temp? + *.md + !LICENCSE.md +``` + +This file causes the following build behavior: + +| Rule | Behavior | +|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `*/temp*` | Exclude all files with names starting with`temp` in any subdirectory below the root directory. For example, a file named`/somedir/temporary.txt` is ignored. | +| `*/*/temp*` | Exclude files starting with name `temp` from any subdirectory that is two levels below the root directory. For example, the file `/somedir/subdir/temporary.txt` is ignored. | +| `temp?` | Exclude the files that match the pattern in the root directory. For example, the files `tempa`, `tempb` in the root directory are ignored. | +| `*.md ` | Exclude all markdown files. | +| `!LICENSE.md` | Exception to the exclude all Markdown files is this file, `LICENSE.md`, include this file in the build. | + +The placement of `!` exception rules influences the matching algorithm; the +last line of the `.dockerignore` that matches a particular file determines +whether it is included or excluded. In the above example, the `LICENSE.md` file +matches both the `*.md` and `!LICENSE.md` rule. If you reverse the lines in the +example: + +``` + */temp* + */*/temp* + temp? + !LICENCSE.md + *.md +``` + +The build would exclude `LICENSE.md` because the last `*.md` rule adds all +Markdown files back onto the ignore list. The `!LICENSE.md` rule has no effect +because the subsequent `*.md` rule overrides it. + +You can even use the `.dockerignore` file to ignore the `Dockerfile` and +`.dockerignore` files. This is useful if you are copying files from the root of +the build context into your new container but do not want to include the +`Dockerfile` or `.dockerignore` files (e.g. `ADD . /someDir/`). - $ docker build . - Uploading context 18.829 MB - Uploading context - Step 0 : FROM busybox - ---> 769b9341d937 - Step 1 : CMD echo Hello World - ---> Using cache - ---> 99cc1ad10469 - Successfully built 99cc1ad10469 - $ echo ".git" > .dockerignore - $ docker build . - Uploading context 6.76 MB - Uploading context - Step 0 : FROM busybox - ---> 769b9341d937 - Step 1 : CMD echo Hello World - ---> Using cache - ---> 99cc1ad10469 - Successfully built 99cc1ad10469 ## FROM @@ -299,7 +324,7 @@ The cache for `RUN` instructions can be invalidated by `ADD` instructions. See the layers with `dirperm1` option. More details on `dirperm1` option can be found at [`aufs` man page](http://aufs.sourceforge.net/aufs3/man.html) - If your system doesnt have support for `dirperm1`, the issue describes a workaround. + If your system doesn't have support for `dirperm1`, the issue describes a workaround. ## CMD @@ -368,7 +393,7 @@ default specified in `CMD`. The `LABEL` instruction adds metadata to an image. A `LABEL` is a key-value pair. To include spaces within a `LABEL` value, use quotes and -blackslashes as you would in command-line parsing. +backslashes as you would in command-line parsing. LABEL "com.example.vendor"="ACME Incorporated" diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index a871162049b0a037027b777cea0c7aa245d6b1f4..c69f0a170e2facf380804f20a094650669af2873 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -442,7 +442,7 @@ Currently supported options are: > Otherwise, set this flag for migrating existing Docker daemons to a > daemon with a supported environment. -### Docker exec-driver option +### Docker execdriver option The Docker daemon uses a specifically built `libcontainer` execution driver as its interface to the Linux kernel `namespaces`, `cgroups`, and `SELinux`. @@ -452,6 +452,21 @@ https://linuxcontainers.org/) via the `lxc` execution driver, however, this is not where the primary development of new functionality is taking place. Add `-e lxc` to the daemon flags to use the `lxc` execution driver. +#### Options for the native execdriver + +You can configure the `native` (libcontainer) execdriver using options specified +with the `--exec-opt` flag. All the flag's options have the `native` prefix. A +single `native.cgroupdriver` option is available. + +The `native.cgroupdriver` option specifies the management of the container's +cgroups. You can specify `cgroupfs` or `systemd`. If you specify `systemd` and +it is not available, the system uses `cgroupfs`. By default, if no option is +specified, the execdriver first tries `systemd` and falls back to `cgroupfs`. +This example sets the execdriver to `cgroupfs`: + + $ sudo docker -d --exec-opt native.cgroupdriver=cgroupfs + +Setting this option applies to all containers the daemon launches. ### Daemon DNS options @@ -636,12 +651,13 @@ refer to any of the files in the context. For example, your build can use an [*ADD*](/reference/builder/#add) instruction to reference a file in the context. -The `URL` parameter can specify the location of a Git repository; in this -case, the repository is the context. The Git repository is recursively -cloned with its submodules. The system does a fresh `git clone -recursive` -in a temporary directory on your local host. Then, this clone is sent to -the Docker daemon as the context. Local clones give you the ability to -access private repositories using local user credentials, VPN's, and so forth. +The `URL` parameter can specify the location of a Git repository; +the repository acts as the build context. The system recursively clones the repository +and its submodules using a `git clone --depth 1 --recursive` command. +This command runs in a temporary directory on your local host. +After the command succeeds, the directory is sent to the Docker daemon as the context. +Local clones give you the ability to access private repositories using +local user credentials, VPN's, and so forth. Instead of specifying a context, you can pass a single Dockerfile in the `URL` or pipe the file in via `STDIN`. To pipe a Dockerfile from `STDIN`: @@ -652,6 +668,26 @@ If you use STDIN or specify a `URL`, the system places the contents into a file called `Dockerfile`, and any `-f`, `--file` option is ignored. In this scenario, there is no context. +By default the `docker build` command will look for a `Dockerfile` at the +root of the build context. The `-f`, `--file`, option lets you specify +the path to an alternative file to use instead. This is useful +in cases where the same set of files are used for multiple builds. The path +must be to a file within the build context. If a relative path is specified +then it must to be relative to the current directory. + +In most cases, it's best to put each Dockerfile in an empty directory. Then, add +to that directory only the files needed for building the Dockerfile. To increase +the build's performance, you can exclude files and directories by adding a +`.dockerignore` file to that directory as well. For information on creating one, +see the [.dockerignore file](../../reference/builder/#dockerignore-file). + +If the Docker client loses connection to the daemon, the build is canceled. +This happens if you interrupt the Docker client with `ctrl-c` or if the Docker +client is killed for any reason. + +> **Note:** Currently only the "run" phase of the build can be canceled until +> pull cancelation is implemented). + ### Return code On a successful build, a return code of success `0` will be returned. @@ -672,55 +708,11 @@ INFO[0000] The command [/bin/sh -c exit 13] returned a non-zero code: 13 $ echo $? 1 ``` - -### .dockerignore file - -If a file named `.dockerignore` exists in the root of `PATH` then it -is interpreted as a newline-separated list of exclusion patterns. -Exclusion patterns match files or directories relative to `PATH` that -will be excluded from the context. Globbing is done using Go's -[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. - -Please note that `.dockerignore` files in other subdirectories are -considered as normal files. Filepaths in `.dockerignore` are absolute with -the current directory as the root. Wildcards are allowed but the search -is not recursive. - -#### Example .dockerignore file - */temp* - */*/temp* - temp? - -The first line above `*/temp*`, would ignore all files with names starting with -`temp` from any subdirectory below the root directory. For example, a file named -`/somedir/temporary.txt` would be ignored. The second line `*/*/temp*`, will -ignore files starting with name `temp` from any subdirectory that is two levels -below the root directory. For example, the file `/somedir/subdir/temporary.txt` -would get ignored in this case. The last line in the above example `temp?` -will ignore the files that match the pattern from the root directory. -For example, the files `tempa`, `tempb` are ignored from the root directory. -Currently there is no support for regular expressions. Formats -like `[^temp*]` are ignored. - -By default the `docker build` command will look for a `Dockerfile` at the -root of the build context. The `-f`, `--file`, option lets you specify -the path to an alternative file to use instead. This is useful -in cases where the same set of files are used for multiple builds. The path -must be to a file within the build context. If a relative path is specified -then it must to be relative to the current directory. - -If the Docker client loses connection to the daemon, the build is canceled. -This happens if you interrupt the Docker client with `ctrl-c` or if the Docker -client is killed for any reason. - -> **Note:** Currently only the "run" phase of the build can be canceled until -> pull cancelation is implemented). - See also: [*Dockerfile Reference*](/reference/builder). -#### Examples +### Examples $ docker build . Uploading context 10240 bytes @@ -789,7 +781,8 @@ affect the build cache. This example shows the use of the `.dockerignore` file to exclude the `.git` directory from the context. Its effect can be seen in the changed size of the -uploaded context. +uploaded context. The builder reference contains detailed information on +[creating a .dockerignore file](../../builder/#dockerignore-file) $ docker build -t vieux/apache:2.0 . diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 7218fab6492994db2a281ea3668096670b274e97..990faaf6c0810fdd0ad0bd48f7abe512ba674732 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -380,7 +380,7 @@ This means the daemon will wait for 100 ms, then 200 ms, 400, 800, 1600, and so on until either the `on-failure` limit is hit, or when you `docker stop` or `docker rm -f` the container. -If a container is succesfully restarted (the container is started and runs +If a container is successfully restarted (the container is started and runs for at least 10 seconds), the delay is reset to its default value of 100 ms. You can specify the maximum amount of times Docker will try to restart the diff --git a/docs/sources/terms/repository.md b/docs/sources/terms/repository.md index 84963b4bf94a1aba3eec03d083bec6b7cdb95794..4b8579924fbf34cdf88a13f95e24afe856350f67 100644 --- a/docs/sources/terms/repository.md +++ b/docs/sources/terms/repository.md @@ -29,7 +29,7 @@ A Fully Qualified Image Name (FQIN) can be made up of 3 parts: If you create a new repository which you want to share, you will need to set at least the `user_name`, as the `default` blank `user_name` prefix is -reserved for official Docker images. +reserved for [Official Repositories](/docker-hub/official_repos). For more information see [*Working with Repositories*](/userguide/dockerrepos/#working-with-the-repository) diff --git a/docs/sources/userguide/dockerimages.md b/docs/sources/userguide/dockerimages.md index 6219466546cf6676b4169b9d03bb66e2ad51acb9..c29b01032c94bc1b5ccfda4fc873d8fc74629c04 100644 --- a/docs/sources/userguide/dockerimages.md +++ b/docs/sources/userguide/dockerimages.md @@ -131,11 +131,11 @@ term `sinatra`. We can see we've returned a lot of images that use the term `sinatra`. We've returned a list of image names, descriptions, Stars (which measure the social popularity of images - if a user likes an image then they can "star" it), and -the Official and Automated build statuses. Official repositories are built and -maintained by the [Stackbrew](https://github.com/docker/stackbrew) project, -and Automated repositories are [Automated Builds]( -/userguide/dockerrepos/#automated-builds) that allow you to validate the source -and content of an image. +the Official and Automated build statuses. +[Official Repositories](/docker-hub/official_repos) are a carefully curated set +of Docker repositories supported by Docker, Inc. Automated repositories are +[Automated Builds](/userguide/dockerrepos/#automated-builds) that allow you to +validate the source and content of an image. We've reviewed the images available to use and we decided to use the `training/sinatra` image. So far we've seen two types of images repositories, diff --git a/docs/sources/userguide/dockerrepos.md b/docs/sources/userguide/dockerrepos.md index efa6ca3d0df22da2f3d063f55b95048ed8d65649..8fc2ba637fdfb0a5ad916de1fa9b4c5de3c6f1a7 100644 --- a/docs/sources/userguide/dockerrepos.md +++ b/docs/sources/userguide/dockerrepos.md @@ -51,12 +51,12 @@ name, user name, or description: tianon/centos CentOS 5 and 6, created using rinse instea... 21 ... -There you can see two example results: `centos` and -`tianon/centos`. The second result shows that it comes from -the public repository of a user, named `tianon/`, while the first result, -`centos`, doesn't explicitly list a repository which means that it comes from the -trusted top-level namespace. The `/` character separates a user's -repository from the image name. +There you can see two example results: `centos` and `tianon/centos`. The second +result shows that it comes from the public repository of a user, named +`tianon/`, while the first result, `centos`, doesn't explicitly list a +repository which means that it comes from the trusted top-level namespace for +[Official Repositories](/docker-hub/official_repos). The `/` character separates +a user's repository from the image name. Once you've found the image you want, you can download it with `docker pull `: diff --git a/engine/streams_test.go b/engine/streams_test.go index 476a721baf71e9d77a74d1a38db5811a74959edf..c22338a32e7524d84ba14656cac0d5138b47ffaf 100644 --- a/engine/streams_test.go +++ b/engine/streams_test.go @@ -182,7 +182,7 @@ func TestInputAddEmpty(t *testing.T) { t.Fatal(err) } if len(data) > 0 { - t.Fatalf("Read from empty input shoul yield no data") + t.Fatalf("Read from empty input should yield no data") } } diff --git a/graph/export.go b/graph/export.go index ee626aae63429987987434121ca4d7b05da18cf7..ae061a8a0fdc87cec147bd78bdd186d2b51b0d63 100644 --- a/graph/export.go +++ b/graph/export.go @@ -83,8 +83,15 @@ func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error { } // write repositories, if there is something to write if len(rootRepoMap) > 0 { - rootRepoJson, _ := json.Marshal(rootRepoMap) - if err := ioutil.WriteFile(path.Join(tempdir, "repositories"), rootRepoJson, os.FileMode(0644)); err != nil { + f, err := os.OpenFile(path.Join(tempdir, "repositories"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + f.Close() + return err + } + if err := json.NewEncoder(f).Encode(rootRepoMap); err != nil { + return err + } + if err := f.Close(); err != nil { return err } } else { diff --git a/graph/graph.go b/graph/graph.go index 5159a93223dd640a12b3765e6fbdfb959ae86177..9b2d7c2ee9634b742a16c1bfb193e8bccff654d9 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -348,9 +348,8 @@ func (graph *Graph) Delete(name string) error { tmp, err := graph.Mktemp("") graph.idIndex.Delete(id) if err == nil { - err = os.Rename(graph.ImageRoot(id), tmp) - // On err make tmp point to old dir and cleanup unused tmp dir - if err != nil { + if err := os.Rename(graph.ImageRoot(id), tmp); err != nil { + // On err make tmp point to old dir and cleanup unused tmp dir os.RemoveAll(tmp) tmp = graph.ImageRoot(id) } diff --git a/graph/load.go b/graph/load.go index be968bab5b3e268aba097bed5331eb9ffc94278b..d978b1ee8e8ce6e75d1ee797f581c7ae326ba3f7 100644 --- a/graph/load.go +++ b/graph/load.go @@ -58,22 +58,26 @@ func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error { } } - repositoriesJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", "repositories")) - if err == nil { - repositories := map[string]Repository{} - if err := json.Unmarshal(repositoriesJson, &repositories); err != nil { + reposJSONFile, err := os.Open(path.Join(tmpImageDir, "repo", "repositories")) + if err != nil { + if !os.IsNotExist(err) { return err } + return nil + } + defer reposJSONFile.Close() - for imageName, tagMap := range repositories { - for tag, address := range tagMap { - if err := s.SetLoad(imageName, tag, address, true, outStream); err != nil { - return err - } + repositories := map[string]Repository{} + if err := json.NewDecoder(reposJSONFile).Decode(&repositories); err != nil { + return err + } + + for imageName, tagMap := range repositories { + for tag, address := range tagMap { + if err := s.SetLoad(imageName, tag, address, true, outStream); err != nil { + return err } } - } else if !os.IsNotExist(err) { - return err } return nil diff --git a/graph/pull.go b/graph/pull.go index 0ebf75abb7c8051919323a668f6a285e0fcc5bb2..c3c064fc5842a986db77ad9d018a2d5fd59d0aa3 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -537,8 +537,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis di.err <- downloadFunc(di) }(&downloads[i]) } else { - err := downloadFunc(&downloads[i]) - if err != nil { + if err := downloadFunc(&downloads[i]); err != nil { return false, err } } @@ -548,8 +547,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis for i := len(downloads) - 1; i >= 0; i-- { d := &downloads[i] if d.err != nil { - err := <-d.err - if err != nil { + if err := <-d.err; err != nil { return false, err } } diff --git a/graph/push.go b/graph/push.go index 62ff94e0c41b9dd660c3904d3cc32ea256b98651..1b33288d8fedf5de74f501254f8d35a4bcb73a7c 100644 --- a/graph/push.go +++ b/graph/push.go @@ -367,8 +367,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o logrus.Debugf("Pushing layer: %s", layer.ID) if layer.Config != nil && metadata.Image != layer.ID { - err = runconfig.Merge(&metadata, layer.Config) - if err != nil { + if err := runconfig.Merge(&metadata, layer.Config); err != nil { return err } } diff --git a/graph/tags.go b/graph/tags.go index 39f0ffc29a93bf86f2938f8de380687b82fd1aa0..abffe2f562c7662a288aa4ff248829188b5aaf9c 100644 --- a/graph/tags.go +++ b/graph/tags.go @@ -115,11 +115,12 @@ func (store *TagStore) save() error { } func (store *TagStore) reload() error { - jsonData, err := ioutil.ReadFile(store.path) + f, err := os.Open(store.path) if err != nil { return err } - if err := json.Unmarshal(jsonData, store); err != nil { + defer f.Close() + if err := json.NewDecoder(f).Decode(&store); err != nil { return err } return nil diff --git a/image/image.go b/image/image.go index 90714d6dbe773e0c90d18684771a2935c97ea8d0..a34d2b940849f1ad94fc13501068a35670f6bcad 100644 --- a/image/image.go +++ b/image/image.go @@ -268,8 +268,7 @@ func NewImgJSON(src []byte) (*Image, error) { func ValidateID(id string) error { validHex := regexp.MustCompile(`^([a-f0-9]{64})$`) if ok := validHex.MatchString(id); !ok { - err := fmt.Errorf("image ID '%s' is invalid", id) - return err + return fmt.Errorf("image ID '%s' is invalid", id) } return nil } diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go index 07bb9315966f008d2abbb0d6bff833a084bad554..202799cbf153b1da5863dd5bf848bfef1381a4f0 100644 --- a/integration-cli/check_test.go +++ b/integration-cli/check_test.go @@ -24,13 +24,58 @@ func (s *TimerSuite) TearDownTest(c *check.C) { fmt.Printf("%-60s%.2f\n", c.TestName(), time.Since(s.start).Seconds()) } +func init() { + check.Suite(&DockerSuite{}) +} + type DockerSuite struct { TimerSuite } func (s *DockerSuite) TearDownTest(c *check.C) { deleteAllContainers() + deleteAllImages() s.TimerSuite.TearDownTest(c) } -var _ = check.Suite(&DockerSuite{}) +func init() { + check.Suite(&DockerRegistrySuite{ + ds: &DockerSuite{}, + }) +} + +type DockerRegistrySuite struct { + ds *DockerSuite + reg *testRegistryV2 +} + +func (s *DockerRegistrySuite) SetUpTest(c *check.C) { + s.reg = setupRegistry(c) + s.ds.SetUpTest(c) +} + +func (s *DockerRegistrySuite) TearDownTest(c *check.C) { + s.reg.Close() + s.ds.TearDownTest(c) +} + +func init() { + check.Suite(&DockerDaemonSuite{ + ds: &DockerSuite{}, + }) +} + +type DockerDaemonSuite struct { + ds *DockerSuite + d *Daemon +} + +func (s *DockerDaemonSuite) SetUpTest(c *check.C) { + s.d = NewDaemon(c) + s.ds.SetUpTest(c) +} + +func (s *DockerDaemonSuite) TearDownTest(c *check.C) { + s.d.Stop() + s.ds.TearDownTest(c) +} diff --git a/integration-cli/docker_api_attach_test.go b/integration-cli/docker_api_attach_test.go index ce7f85d306814528eef88e8a3b2612644a2f30f0..c784d5c369cef23c437d7943b4faac5af2733a04 100644 --- a/integration-cli/docker_api_attach_test.go +++ b/integration-cli/docker_api_attach_test.go @@ -40,24 +40,38 @@ func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) { expected := []byte("hello") actual := make([]byte, len(expected)) - outChan := make(chan string) + + outChan := make(chan error) go func() { - if _, err := ws.Read(actual); err != nil { - c.Fatal(err) - } - outChan <- "done" + _, err := ws.Read(actual) + outChan <- err + close(outChan) }() - inChan := make(chan string) + inChan := make(chan error) go func() { - if _, err := ws.Write(expected); err != nil { + _, err := ws.Write(expected) + inChan <- err + close(inChan) + }() + + select { + case err := <-inChan: + if err != nil { c.Fatal(err) } - inChan <- "done" - }() + case <-time.After(5 * time.Second): + c.Fatal("Timeout writing to ws") + } - <-inChan - <-outChan + select { + case err := <-outChan: + if err != nil { + c.Fatal(err) + } + case <-time.After(5 * time.Second): + c.Fatal("Timeout reading from ws") + } if !bytes.Equal(expected, actual) { c.Fatal("Expected output on websocket to match input") diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 24efbb7a27cb15b75ec4031cc0f7ab7b3f1e2606..1fec3912e6de1ec6b8370c70af0e6e44319210da 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -176,7 +176,7 @@ func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) { c.Fatal(out, err) } - name := "testing" + name := "TestContainerApiStartDupVolumeBinds" config := map[string]interface{}{ "Image": "busybox", "Volumes": map[string]struct{}{volPath: {}}, @@ -260,15 +260,14 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) { c.Fatalf("Error on container creation: %v, output: %q", err, out) } type b struct { - body []byte - err error + status int + body []byte + err error } bc := make(chan b, 1) go func() { status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) - c.Assert(status, check.Equals, http.StatusOK) - c.Assert(err, check.IsNil) - bc <- b{body, err} + bc <- b{status, body, err} }() // allow some time to stream the stats from the container @@ -283,9 +282,8 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) { case <-time.After(2 * time.Second): c.Fatal("stream was not closed after container was removed") case sr := <-bc: - if sr.err != nil { - c.Fatal(sr.err) - } + c.Assert(sr.err, check.IsNil) + c.Assert(sr.status, check.Equals, http.StatusOK) dec := json.NewDecoder(bytes.NewBuffer(sr.body)) var s *types.Stats @@ -297,6 +295,7 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) { } func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) { + // TODO: this test does nothing because we are c.Assert'ing in goroutine var ( name = "statscontainer" runCmd = exec.Command(dockerBinary, "create", "--name", name, "busybox", "top") @@ -339,8 +338,8 @@ func (s *DockerSuite) TestBuildApiDockerfilePath(c *check.C) { c.Fatalf("failed to close tar archive: %v", err) } - status, body, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar") - c.Assert(status, check.Equals, http.StatusInternalServerError) + res, body, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar") + c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) c.Assert(err, check.IsNil) out, err := readBody(body) @@ -365,8 +364,8 @@ RUN find /tmp/`, } defer server.Close() - status, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json") - c.Assert(status, check.Equals, http.StatusOK) + res, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json") + c.Assert(res.StatusCode, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) buf, err := readBody(body) @@ -393,8 +392,8 @@ RUN echo from dockerfile`, } defer git.Close() - status, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") - c.Assert(status, check.Equals, http.StatusOK) + res, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") + c.Assert(res.StatusCode, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) buf, err := readBody(body) @@ -421,8 +420,8 @@ RUN echo from Dockerfile`, defer git.Close() // Make sure it tries to 'dockerfile' query param value - status, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json") - c.Assert(status, check.Equals, http.StatusOK) + res, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json") + c.Assert(res.StatusCode, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) buf, err := readBody(body) @@ -450,8 +449,8 @@ RUN echo from dockerfile`, defer git.Close() // Make sure it tries to 'dockerfile' query param value - status, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") - c.Assert(status, check.Equals, http.StatusOK) + res, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json") + c.Assert(res.StatusCode, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) buf, err := readBody(body) @@ -483,8 +482,8 @@ func (s *DockerSuite) TestBuildApiDockerfileSymlink(c *check.C) { c.Fatalf("failed to close tar archive: %v", err) } - status, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar") - c.Assert(status, check.Equals, http.StatusInternalServerError) + res, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar") + c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) c.Assert(err, check.IsNil) out, err := readBody(body) @@ -614,14 +613,14 @@ func (s *DockerSuite) TestContainerApiTop(c *check.C) { } func (s *DockerSuite) TestContainerApiCommit(c *check.C) { - out, err := exec.Command(dockerBinary, "run", "-d", "busybox", "/bin/sh", "-c", "touch /test").CombinedOutput() + cName := "testapicommit" + out, err := exec.Command(dockerBinary, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test").CombinedOutput() if err != nil { c.Fatal(err, out) } - id := strings.TrimSpace(string(out)) - name := "testcommit" + stringid.GenerateRandomID() - status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+id, nil) + name := "TestContainerApiCommit" + status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil) c.Assert(status, check.Equals, http.StatusCreated) c.Assert(err, check.IsNil) @@ -632,7 +631,6 @@ func (s *DockerSuite) TestContainerApiCommit(c *check.C) { if err := json.Unmarshal(b, &img); err != nil { c.Fatal(err) } - defer deleteImages(img.Id) cmd, err := inspectField(img.Id, "Config.Cmd") if err != nil { @@ -644,7 +642,7 @@ func (s *DockerSuite) TestContainerApiCommit(c *check.C) { // sanity check, make sure the image is what we think it is out, err = exec.Command(dockerBinary, "run", img.Id, "ls", "/test").CombinedOutput() if err != nil { - c.Fatalf("error checking commited image: %v - %q", err, string(out)) + c.Fatalf("error checking committed image: %v - %q", err, string(out)) } } @@ -721,7 +719,7 @@ func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) { "Image": "busybox", } - create := func(ct string) (int, io.ReadCloser, error) { + create := func(ct string) (*http.Response, io.ReadCloser, error) { jsonData := bytes.NewBuffer(nil) if err := json.NewEncoder(jsonData).Encode(config); err != nil { c.Fatal(err) @@ -730,21 +728,21 @@ func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) { } // Try with no content-type - status, body, err := create("") - c.Assert(status, check.Equals, http.StatusInternalServerError) + res, body, err := create("") c.Assert(err, check.IsNil) + c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) body.Close() // Try with wrong content-type - status, body, err = create("application/xml") - c.Assert(status, check.Equals, http.StatusInternalServerError) + res, body, err = create("application/xml") c.Assert(err, check.IsNil) + c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) body.Close() // now application/json - status, body, err = create("application/json") - c.Assert(status, check.Equals, http.StatusCreated) + res, body, err = create("application/json") c.Assert(err, check.IsNil) + c.Assert(res.StatusCode, check.Equals, http.StatusCreated) body.Close() } @@ -775,8 +773,8 @@ func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) { "NetworkDisabled":false, "OnBuild":null}` - status, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") - c.Assert(status, check.Equals, http.StatusCreated) + res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") + c.Assert(res.StatusCode, check.Equals, http.StatusCreated) c.Assert(err, check.IsNil) b, err := readBody(body) @@ -809,13 +807,13 @@ func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { "Memory": 524287 }` - status, body, _ := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") + res, body, _ := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") b, err2 := readBody(body) if err2 != nil { c.Fatal(err2) } - c.Assert(status, check.Equals, http.StatusInternalServerError) + c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) c.Assert(strings.Contains(string(b), "Minimum memory limit allowed is 4MB"), check.Equals, true) } @@ -832,12 +830,31 @@ func (s *DockerSuite) TestStartWithTooLowMemoryLimit(c *check.C) { "Memory": 524287 }` - status, body, _ := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json") + res, body, _ := sockRequestRaw("POST", "/containers/"+containerID+"/start", strings.NewReader(config), "application/json") b, err2 := readBody(body) if err2 != nil { c.Fatal(err2) } - c.Assert(status, check.Equals, http.StatusInternalServerError) + c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError) c.Assert(strings.Contains(string(b), "Minimum memory limit allowed is 4MB"), check.Equals, true) } + +func (s *DockerSuite) TestContainerApiRename(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "--name", "TestContainerApiRename", "-d", "busybox", "sh") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + containerID := strings.TrimSpace(out) + newName := "TestContainerApiRenameNew" + statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil) + + // 204 No Content is expected, not 200 + c.Assert(statusCode, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + name, err := inspectField(containerID, "Name") + if name != "/"+newName { + c.Fatalf("Failed to rename container, expected %v, got %v. Container rename API failed", newName, name) + } +} diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go index a0f029d40fadfd0bdf90f63fe7a888cd80c7e867..e88fbaeaad9c396dab4bcddb2bbb55c265675cae 100644 --- a/integration-cli/docker_api_images_test.go +++ b/integration-cli/docker_api_images_test.go @@ -30,7 +30,6 @@ func (s *DockerSuite) TestApiImagesFilter(c *check.C) { name := "utest:tag1" name2 := "utest/docker:tag2" name3 := "utest:5000/docker:tag3" - defer deleteImages(name, name2, name3) for _, n := range []string{name, name2, name3} { if out, err := exec.Command(dockerBinary, "tag", "busybox", n).CombinedOutput(); err != nil { c.Fatal(err, out) @@ -74,11 +73,10 @@ func (s *DockerSuite) TestApiImagesSaveAndLoad(c *check.C) { c.Fatal(err) } id := strings.TrimSpace(out) - defer deleteImages("saveandload") - status, body, err := sockRequestRaw("GET", "/images/"+id+"/get", nil, "") - c.Assert(status, check.Equals, http.StatusOK) + res, body, err := sockRequestRaw("GET", "/images/"+id+"/get", nil, "") c.Assert(err, check.IsNil) + c.Assert(res.StatusCode, check.Equals, http.StatusOK) defer body.Close() @@ -86,9 +84,9 @@ func (s *DockerSuite) TestApiImagesSaveAndLoad(c *check.C) { c.Fatal(err, out) } - status, loadBody, err := sockRequestRaw("POST", "/images/load", body, "application/x-tar") - c.Assert(status, check.Equals, http.StatusOK) + res, loadBody, err := sockRequestRaw("POST", "/images/load", body, "application/x-tar") c.Assert(err, check.IsNil) + c.Assert(res.StatusCode, check.Equals, http.StatusOK) defer loadBody.Close() diff --git a/integration-cli/docker_api_logs_test.go b/integration-cli/docker_api_logs_test.go index bf0e1fbf49a3fb7cfed55198f665e56c9d303e16..f9284494d2e88e07f08c52a62b72c216df0e876c 100644 --- a/integration-cli/docker_api_logs_test.go +++ b/integration-cli/docker_api_logs_test.go @@ -1,28 +1,46 @@ package main import ( + "bufio" "bytes" "fmt" "net/http" "os/exec" + "strings" + "time" "github.com/go-check/check" ) func (s *DockerSuite) TestLogsApiWithStdout(c *check.C) { - name := "logs_test" - - runCmd := exec.Command(dockerBinary, "run", "-d", "-t", "--name", name, "busybox", "bin/sh", "-c", "sleep 10 && echo "+name) - if out, _, err := runCommandWithOutput(runCmd); err != nil { - c.Fatal(out, err) + out, _ := dockerCmd(c, "run", "-d", "-t", "busybox", "/bin/sh", "-c", "while true; do echo hello; sleep 1; done") + id := strings.TrimSpace(out) + if err := waitRun(id); err != nil { + c.Fatal(err) } - status, body, err := sockRequest("GET", fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1×tamps=1", name), nil) - c.Assert(status, check.Equals, http.StatusOK) - c.Assert(err, check.IsNil) - - if !bytes.Contains(body, []byte(name)) { - c.Fatalf("Expected %s, got %s", name, string(body[:])) + type logOut struct { + out string + res *http.Response + err error + } + chLog := make(chan logOut) + + go func() { + res, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1×tamps=1", id), nil, "") + out, _ := bufio.NewReader(body).ReadString('\n') + chLog <- logOut{strings.TrimSpace(out), res, err} + }() + + select { + case l := <-chLog: + c.Assert(l.err, check.IsNil) + c.Assert(l.res.StatusCode, check.Equals, http.StatusOK) + if !strings.HasSuffix(l.out, "hello") { + c.Fatalf("expected log output to container 'hello', but it does not") + } + case <-time.After(2 * time.Second): + c.Fatal("timeout waiting for logs to exit") } } diff --git a/integration-cli/docker_cli_attach_test.go b/integration-cli/docker_cli_attach_test.go index 11ae1584ad2b2cbd1fcc2b38f1ef290e4ea5d1b4..fc2ea1a1d1ed78e11e08eb5faea31576d9866aa8 100644 --- a/integration-cli/docker_cli_attach_test.go +++ b/integration-cli/docker_cli_attach_test.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "fmt" "io" "os/exec" "strings" @@ -89,7 +90,6 @@ func (s *DockerSuite) TestAttachMultipleAndRestart(c *check.C) { } func (s *DockerSuite) TestAttachTtyWithoutStdin(c *check.C) { - cmd := exec.Command(dockerBinary, "run", "-d", "-ti", "busybox") out, _, err := runCommandWithOutput(cmd) if err != nil { @@ -108,29 +108,32 @@ func (s *DockerSuite) TestAttachTtyWithoutStdin(c *check.C) { } }() - done := make(chan struct{}) + done := make(chan error) go func() { defer close(done) cmd := exec.Command(dockerBinary, "attach", id) if _, err := cmd.StdinPipe(); err != nil { - c.Fatal(err) + done <- err + return } expected := "cannot enable tty mode" if out, _, err := runCommandWithOutput(cmd); err == nil { - c.Fatal("attach should have failed") + done <- fmt.Errorf("attach should have failed") + return } else if !strings.Contains(out, expected) { - c.Fatalf("attach failed with error %q: expected %q", out, expected) + done <- fmt.Errorf("attach failed with error %q: expected %q", out, expected) + return } }() select { - case <-done: + case err := <-done: + c.Assert(err, check.IsNil) case <-time.After(attachWait): c.Fatal("attach is running but should have failed") } - } func (s *DockerSuite) TestAttachDisconnect(c *check.C) { @@ -161,7 +164,7 @@ func (s *DockerSuite) TestAttachDisconnect(c *check.C) { c.Fatal(err) } if strings.TrimSpace(out) != "hello" { - c.Fatalf("exepected 'hello', got %q", out) + c.Fatalf("expected 'hello', got %q", out) } if err := stdin.Close(); err != nil { @@ -174,7 +177,7 @@ func (s *DockerSuite) TestAttachDisconnect(c *check.C) { c.Fatal(err) } if running != "true" { - c.Fatal("exepected container to still be running") + c.Fatal("expected container to still be running") } } diff --git a/integration-cli/docker_cli_attach_unix_test.go b/integration-cli/docker_cli_attach_unix_test.go index 5567c92a0b943d68432da5481696dd51c171cac8..82808a5b087f1eb12917c1cc0743924bafd89a97 100644 --- a/integration-cli/docker_cli_attach_unix_test.go +++ b/integration-cli/docker_cli_attach_unix_test.go @@ -27,14 +27,14 @@ func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) { c.Fatal(err) } - done := make(chan struct{}) - + errChan := make(chan error) go func() { - defer close(done) + defer close(errChan) _, tty, err := pty.Open() if err != nil { - c.Fatalf("could not open pty: %v", err) + errChan <- err + return } attachCmd := exec.Command(dockerBinary, "attach", id) attachCmd.Stdin = tty @@ -42,7 +42,8 @@ func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) { attachCmd.Stderr = tty if err := attachCmd.Run(); err != nil { - c.Fatalf("attach returned error %s", err) + errChan <- err + return } }() @@ -51,7 +52,8 @@ func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) { c.Fatalf("error thrown while waiting for container: %s, %v", out, err) } select { - case <-done: + case err := <-errChan: + c.Assert(err, check.IsNil) case <-time.After(attachWait): c.Fatal("timed out without attach returning") } @@ -71,12 +73,10 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) { cmd.Stdout = tty cmd.Stderr = tty - detached := make(chan struct{}) + errChan := make(chan error) go func() { - if err := cmd.Run(); err != nil { - c.Fatalf("attach returned error %s", err) - } - close(detached) + errChan <- cmd.Run() + close(errChan) }() time.Sleep(500 * time.Millisecond) @@ -87,7 +87,12 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) { time.Sleep(100 * time.Millisecond) cpty.Write([]byte{17}) - <-detached + select { + case err := <-errChan: + c.Assert(err, check.IsNil) + case <-time.After(5 * time.Second): + c.Fatal("timeout while detaching") + } cpty, tty, err = pty.Open() if err != nil { @@ -119,9 +124,7 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) { select { case err := <-readErr: - if err != nil { - c.Fatal(err) - } + c.Assert(err, check.IsNil) case <-time.After(2 * time.Second): c.Fatal("timeout waiting for attach read") } @@ -172,7 +175,7 @@ func (s *DockerSuite) TestAttachDetach(c *check.C) { c.Fatal(err) } if strings.TrimSpace(out) != "hello" { - c.Fatalf("exepected 'hello', got %q", out) + c.Fatalf("expected 'hello', got %q", out) } // escape sequence @@ -195,7 +198,7 @@ func (s *DockerSuite) TestAttachDetach(c *check.C) { c.Fatal(err) } if running != "true" { - c.Fatal("exepected container to still be running") + c.Fatal("expected container to still be running") } go func() { @@ -243,7 +246,7 @@ func (s *DockerSuite) TestAttachDetachTruncatedID(c *check.C) { c.Fatal(err) } if strings.TrimSpace(out) != "hello" { - c.Fatalf("exepected 'hello', got %q", out) + c.Fatalf("expected 'hello', got %q", out) } // escape sequence @@ -266,7 +269,7 @@ func (s *DockerSuite) TestAttachDetachTruncatedID(c *check.C) { c.Fatal(err) } if running != "true" { - c.Fatal("exepected container to still be running") + c.Fatal("expected container to still be running") } go func() { diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 72d15c177bbff6d69c9b7bfb1444db71451bbda5..b74dce2cfa9065dfff4d1de8a6ffb77a59257d86 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -15,7 +15,6 @@ import ( "runtime" "strconv" "strings" - "sync" "text/template" "time" @@ -27,7 +26,6 @@ import ( func (s *DockerSuite) TestBuildJSONEmptyRun(c *check.C) { name := "testbuildjsonemptyrun" - defer deleteImages(name) _, err := buildImage( name, @@ -45,7 +43,6 @@ func (s *DockerSuite) TestBuildJSONEmptyRun(c *check.C) { func (s *DockerSuite) TestBuildEmptyWhitespace(c *check.C) { name := "testbuildemptywhitespace" - defer deleteImages(name) _, err := buildImage( name, @@ -65,7 +62,6 @@ func (s *DockerSuite) TestBuildEmptyWhitespace(c *check.C) { func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) { name := "testbuildshcmdjsonentrypoint" - defer deleteImages(name) _, err := buildImage( name, @@ -99,7 +95,6 @@ func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) { func (s *DockerSuite) TestBuildEnvironmentReplacementUser(c *check.C) { name := "testbuildenvironmentreplacement" - defer deleteImages(name) _, err := buildImage(name, ` FROM scratch @@ -123,7 +118,6 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementUser(c *check.C) { func (s *DockerSuite) TestBuildEnvironmentReplacementVolume(c *check.C) { name := "testbuildenvironmentreplacement" - defer deleteImages(name) _, err := buildImage(name, ` FROM scratch @@ -153,7 +147,6 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementVolume(c *check.C) { func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) { name := "testbuildenvironmentreplacement" - defer deleteImages(name) _, err := buildImage(name, ` FROM scratch @@ -183,7 +176,6 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) { func (s *DockerSuite) TestBuildEnvironmentReplacementWorkdir(c *check.C) { name := "testbuildenvironmentreplacement" - defer deleteImages(name) _, err := buildImage(name, ` FROM busybox @@ -200,7 +192,6 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementWorkdir(c *check.C) { func (s *DockerSuite) TestBuildEnvironmentReplacementAddCopy(c *check.C) { name := "testbuildenvironmentreplacement" - defer deleteImages(name) ctx, err := fakeContext(` FROM scratch @@ -236,8 +227,6 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementAddCopy(c *check.C) { func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) { name := "testbuildenvironmentreplacement" - defer deleteImages(name) - _, err := buildImage(name, ` FROM busybox @@ -305,8 +294,6 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) { func (s *DockerSuite) TestBuildHandleEscapes(c *check.C) { name := "testbuildhandleescapes" - defer deleteImages(name) - _, err := buildImage(name, ` FROM scratch @@ -395,8 +382,6 @@ func (s *DockerSuite) TestBuildOnBuildLowercase(c *check.C) { name := "testbuildonbuildlowercase" name2 := "testbuildonbuildlowercase2" - defer deleteImages(name, name2) - _, err := buildImage(name, ` FROM busybox @@ -427,7 +412,6 @@ func (s *DockerSuite) TestBuildOnBuildLowercase(c *check.C) { func (s *DockerSuite) TestBuildEnvEscapes(c *check.C) { name := "testbuildenvescapes" - defer deleteImages(name) _, err := buildImage(name, ` FROM busybox @@ -450,7 +434,6 @@ func (s *DockerSuite) TestBuildEnvEscapes(c *check.C) { func (s *DockerSuite) TestBuildEnvOverwrite(c *check.C) { name := "testbuildenvoverwrite" - defer deleteImages(name) _, err := buildImage(name, ` @@ -478,8 +461,6 @@ func (s *DockerSuite) TestBuildEnvOverwrite(c *check.C) { func (s *DockerSuite) TestBuildOnBuildForbiddenMaintainerInSourceImage(c *check.C) { name := "testbuildonbuildforbiddenmaintainerinsourceimage" - defer deleteImages("onbuild") - defer deleteImages(name) createCmd := exec.Command(dockerBinary, "create", "busybox", "true") out, _, _, err := runCommandWithStdoutStderr(createCmd) @@ -510,8 +491,6 @@ func (s *DockerSuite) TestBuildOnBuildForbiddenMaintainerInSourceImage(c *check. func (s *DockerSuite) TestBuildOnBuildForbiddenFromInSourceImage(c *check.C) { name := "testbuildonbuildforbiddenfrominsourceimage" - defer deleteImages("onbuild") - defer deleteImages(name) createCmd := exec.Command(dockerBinary, "create", "busybox", "true") out, _, _, err := runCommandWithStdoutStderr(createCmd) @@ -542,8 +521,6 @@ func (s *DockerSuite) TestBuildOnBuildForbiddenFromInSourceImage(c *check.C) { func (s *DockerSuite) TestBuildOnBuildForbiddenChainedInSourceImage(c *check.C) { name := "testbuildonbuildforbiddenchainedinsourceimage" - defer deleteImages("onbuild") - defer deleteImages(name) createCmd := exec.Command(dockerBinary, "create", "busybox", "true") out, _, _, err := runCommandWithStdoutStderr(createCmd) @@ -576,9 +553,6 @@ func (s *DockerSuite) TestBuildOnBuildCmdEntrypointJSON(c *check.C) { name1 := "onbuildcmd" name2 := "onbuildgenerated" - defer deleteImages(name2) - defer deleteImages(name1) - _, err := buildImage(name1, ` FROM busybox ONBUILD CMD ["hello world"] @@ -611,9 +585,6 @@ func (s *DockerSuite) TestBuildOnBuildEntrypointJSON(c *check.C) { name1 := "onbuildcmd" name2 := "onbuildgenerated" - defer deleteImages(name2) - defer deleteImages(name1) - _, err := buildImage(name1, ` FROM busybox ONBUILD ENTRYPOINT ["echo"]`, @@ -642,7 +613,6 @@ ONBUILD ENTRYPOINT ["echo"]`, func (s *DockerSuite) TestBuildCacheADD(c *check.C) { name := "testbuildtwoimageswithadd" - defer deleteImages(name) server, err := fakeStorage(map[string]string{ "robots.txt": "hello", "index.html": "world", @@ -677,7 +647,6 @@ func (s *DockerSuite) TestBuildCacheADD(c *check.C) { func (s *DockerSuite) TestBuildLastModified(c *check.C) { name := "testbuildlastmodified" - defer deleteImages(name) server, err := fakeStorage(map[string]string{ "file": "hello", @@ -743,7 +712,6 @@ RUN ls -le /file` func (s *DockerSuite) TestBuildSixtySteps(c *check.C) { name := "foobuildsixtysteps" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\n"+strings.Repeat("ADD foo /\n", 60), map[string]string{ "foo": "test1", @@ -760,7 +728,6 @@ func (s *DockerSuite) TestBuildSixtySteps(c *check.C) { func (s *DockerSuite) TestBuildAddSingleFileToRoot(c *check.C) { name := "testaddimg" - defer deleteImages(name) ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -786,7 +753,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte // Issue #3960: "ADD src ." hangs func (s *DockerSuite) TestBuildAddSingleFileToWorkdir(c *check.C) { name := "testaddsinglefiletoworkdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox ADD test_file .`, map[string]string{ @@ -797,23 +763,22 @@ ADD test_file .`, } defer ctx.Close() - done := make(chan struct{}) + errChan := make(chan error) go func() { - if _, err := buildImageFromContext(name, ctx, true); err != nil { - c.Fatal(err) - } - close(done) + _, err := buildImageFromContext(name, ctx, true) + errChan <- err + close(errChan) }() select { case <-time.After(5 * time.Second): c.Fatal("Build with adding to workdir timed out") - case <-done: + case err := <-errChan: + c.Assert(err, check.IsNil) } } func (s *DockerSuite) TestBuildAddSingleFileToExistDir(c *check.C) { name := "testaddsinglefiletoexistdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -847,7 +812,6 @@ func (s *DockerSuite) TestBuildCopyAddMultipleFiles(c *check.C) { defer server.Close() name := "testcopymultiplefilestofile" - defer deleteImages(name) ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -884,7 +848,6 @@ RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' func (s *DockerSuite) TestBuildAddMultipleFilesToFile(c *check.C) { name := "testaddmultiplefilestofile" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD file1.txt file2.txt test `, @@ -906,7 +869,6 @@ func (s *DockerSuite) TestBuildAddMultipleFilesToFile(c *check.C) { func (s *DockerSuite) TestBuildJSONAddMultipleFilesToFile(c *check.C) { name := "testjsonaddmultiplefilestofile" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD ["file1.txt", "file2.txt", "test"] `, @@ -928,7 +890,6 @@ func (s *DockerSuite) TestBuildJSONAddMultipleFilesToFile(c *check.C) { func (s *DockerSuite) TestBuildAddMultipleFilesToFileWild(c *check.C) { name := "testaddmultiplefilestofilewild" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD file*.txt test `, @@ -950,7 +911,6 @@ func (s *DockerSuite) TestBuildAddMultipleFilesToFileWild(c *check.C) { func (s *DockerSuite) TestBuildJSONAddMultipleFilesToFileWild(c *check.C) { name := "testjsonaddmultiplefilestofilewild" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD ["file*.txt", "test"] `, @@ -972,7 +932,6 @@ func (s *DockerSuite) TestBuildJSONAddMultipleFilesToFileWild(c *check.C) { func (s *DockerSuite) TestBuildCopyMultipleFilesToFile(c *check.C) { name := "testcopymultiplefilestofile" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch COPY file1.txt file2.txt test `, @@ -994,7 +953,6 @@ func (s *DockerSuite) TestBuildCopyMultipleFilesToFile(c *check.C) { func (s *DockerSuite) TestBuildJSONCopyMultipleFilesToFile(c *check.C) { name := "testjsoncopymultiplefilestofile" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch COPY ["file1.txt", "file2.txt", "test"] `, @@ -1016,7 +974,6 @@ func (s *DockerSuite) TestBuildJSONCopyMultipleFilesToFile(c *check.C) { func (s *DockerSuite) TestBuildAddFileWithWhitespace(c *check.C) { name := "testaddfilewithwhitespace" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN mkdir "/test dir" RUN mkdir "/test_dir" @@ -1052,7 +1009,6 @@ RUN [ $(cat "/test dir/test_file6") = 'test6' ]`, func (s *DockerSuite) TestBuildCopyFileWithWhitespace(c *check.C) { name := "testcopyfilewithwhitespace" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN mkdir "/test dir" RUN mkdir "/test_dir" @@ -1088,7 +1044,6 @@ RUN [ $(cat "/test dir/test_file6") = 'test6' ]`, func (s *DockerSuite) TestBuildAddMultipleFilesToFileWithWhitespace(c *check.C) { name := "testaddmultiplefilestofilewithwhitespace" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox ADD [ "test file1", "test file2", "test" ] `, @@ -1110,7 +1065,6 @@ func (s *DockerSuite) TestBuildAddMultipleFilesToFileWithWhitespace(c *check.C) func (s *DockerSuite) TestBuildCopyMultipleFilesToFileWithWhitespace(c *check.C) { name := "testcopymultiplefilestofilewithwhitespace" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox COPY [ "test file1", "test file2", "test" ] `, @@ -1132,7 +1086,6 @@ func (s *DockerSuite) TestBuildCopyMultipleFilesToFileWithWhitespace(c *check.C) func (s *DockerSuite) TestBuildCopyWildcard(c *check.C) { name := "testcopywildcard" - defer deleteImages(name) server, err := fakeStorage(map[string]string{ "robots.txt": "hello", "index.html": "world", @@ -1183,7 +1136,6 @@ func (s *DockerSuite) TestBuildCopyWildcard(c *check.C) { func (s *DockerSuite) TestBuildCopyWildcardNoFind(c *check.C) { name := "testcopywildcardnofind" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox COPY file*.txt /tmp/ `, nil) @@ -1204,7 +1156,6 @@ func (s *DockerSuite) TestBuildCopyWildcardNoFind(c *check.C) { func (s *DockerSuite) TestBuildCopyWildcardCache(c *check.C) { name := "testcopywildcardcache" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox COPY file1.txt /tmp/`, map[string]string{ @@ -1238,7 +1189,6 @@ func (s *DockerSuite) TestBuildCopyWildcardCache(c *check.C) { func (s *DockerSuite) TestBuildAddSingleFileToNonExistingDir(c *check.C) { name := "testaddsinglefiletononexistingdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1264,7 +1214,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, func (s *DockerSuite) TestBuildAddDirContentToRoot(c *check.C) { name := "testadddircontenttoroot" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1288,7 +1237,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, func (s *DockerSuite) TestBuildAddDirContentToExistingDir(c *check.C) { name := "testadddircontenttoexistingdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1314,7 +1262,6 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`, func (s *DockerSuite) TestBuildAddWholeDirToRoot(c *check.C) { name := "testaddwholedirtoroot" - defer deleteImages(name) ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1342,7 +1289,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte // Testing #5941 func (s *DockerSuite) TestBuildAddEtcToRoot(c *check.C) { name := "testaddetctoroot" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD . /`, map[string]string{ @@ -1361,7 +1307,6 @@ ADD . /`, // Testing #9401 func (s *DockerSuite) TestBuildAddPreservesFilesSpecialBits(c *check.C) { name := "testaddpreservesfilesspecialbits" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox ADD suidbin /usr/bin/suidbin RUN chmod 4755 /usr/bin/suidbin @@ -1384,7 +1329,6 @@ RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`, func (s *DockerSuite) TestBuildCopySingleFileToRoot(c *check.C) { name := "testcopysinglefiletoroot" - defer deleteImages(name) ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1410,7 +1354,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte // Issue #3960: "ADD src ." hangs - adapted for COPY func (s *DockerSuite) TestBuildCopySingleFileToWorkdir(c *check.C) { name := "testcopysinglefiletoworkdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox COPY test_file .`, map[string]string{ @@ -1421,23 +1364,22 @@ COPY test_file .`, } defer ctx.Close() - done := make(chan struct{}) + errChan := make(chan error) go func() { - if _, err := buildImageFromContext(name, ctx, true); err != nil { - c.Fatal(err) - } - close(done) + _, err := buildImageFromContext(name, ctx, true) + errChan <- err + close(errChan) }() select { case <-time.After(5 * time.Second): c.Fatal("Build with adding to workdir timed out") - case <-done: + case err := <-errChan: + c.Assert(err, check.IsNil) } } func (s *DockerSuite) TestBuildCopySingleFileToExistDir(c *check.C) { name := "testcopysinglefiletoexistdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1463,7 +1405,6 @@ RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' func (s *DockerSuite) TestBuildCopySingleFileToNonExistDir(c *check.C) { name := "testcopysinglefiletononexistdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1488,7 +1429,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, func (s *DockerSuite) TestBuildCopyDirContentToRoot(c *check.C) { name := "testcopydircontenttoroot" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1512,7 +1452,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, func (s *DockerSuite) TestBuildCopyDirContentToExistDir(c *check.C) { name := "testcopydircontenttoexistdir" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1538,7 +1477,6 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`, func (s *DockerSuite) TestBuildCopyWholeDirToRoot(c *check.C) { name := "testcopywholedirtoroot" - defer deleteImages(name) ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group @@ -1565,7 +1503,6 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte func (s *DockerSuite) TestBuildCopyEtcToRoot(c *check.C) { name := "testcopyetctoroot" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch COPY . /`, map[string]string{ @@ -1583,7 +1520,6 @@ COPY . /`, func (s *DockerSuite) TestBuildCopyDisallowRemote(c *check.C) { name := "testcopydisallowremote" - defer deleteImages(name) _, out, err := buildImageWithOut(name, `FROM scratch COPY https://index.docker.io/robots.txt /`, true) @@ -1604,7 +1540,6 @@ func (s *DockerSuite) TestBuildAddBadLinks(c *check.C) { var ( name = "test-link-absolute" ) - defer deleteImages(name) ctx, err := fakeContext(dockerfile, nil) if err != nil { c.Fatal(err) @@ -1692,7 +1627,6 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) { name = "test-link-absolute-volume" dockerfile = "" ) - defer deleteImages(name) tempDir, err := ioutil.TempDir("", "test-link-absolute-volume-temp-") if err != nil { @@ -1737,7 +1671,6 @@ func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) { { name := "testbuildinaccessiblefiles" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\nADD . /foo/", map[string]string{"fileWithoutReadAccess": "foo"}) if err != nil { c.Fatal(err) @@ -1770,7 +1703,6 @@ func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) { } { name := "testbuildinaccessibledirectory" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\nADD . /foo/", map[string]string{"directoryWeCantStat/bar": "foo"}) if err != nil { c.Fatal(err) @@ -1809,7 +1741,6 @@ func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) { } { name := "testlinksok" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\nADD . /foo/", nil) if err != nil { c.Fatal(err) @@ -1829,7 +1760,6 @@ func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) { } { name := "testbuildignoredinaccessible" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\nADD . /foo/", map[string]string{ "directoryWeCantStat/bar": "foo", @@ -1867,7 +1797,6 @@ func (s *DockerSuite) TestBuildForceRm(c *check.C) { c.Fatalf("failed to get the container count: %s", err) } name := "testbuildforcerm" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\nRUN true\nRUN thiswillfail", nil) if err != nil { c.Fatal(err) @@ -1899,11 +1828,7 @@ func (s *DockerSuite) TestBuildForceRm(c *check.C) { // * When docker events sees container start, close the "docker build" command // * Wait for docker events to emit a dying event. func (s *DockerSuite) TestBuildCancelationKillsSleep(c *check.C) { - var wg sync.WaitGroup - defer wg.Wait() - name := "testbuildcancelation" - defer deleteImages(name) // (Note: one year, will never finish) ctx, err := fakeContext("FROM busybox\nRUN sleep 31536000", nil) @@ -1920,26 +1845,21 @@ func (s *DockerSuite) TestBuildCancelationKillsSleep(c *check.C) { containerID := make(chan string) startEpoch := daemonTime(c).Unix() + // Watch for events since epoch. + eventsCmd := exec.Command( + dockerBinary, "events", + "--since", strconv.FormatInt(startEpoch, 10)) + stdout, err := eventsCmd.StdoutPipe() + if err != nil { + c.Fatal(err) + } + if err := eventsCmd.Start(); err != nil { + c.Fatal(err) + } + defer eventsCmd.Process.Kill() - wg.Add(1) // Goroutine responsible for watching start/die events from `docker events` go func() { - defer wg.Done() - // Watch for events since epoch. - eventsCmd := exec.Command( - dockerBinary, "events", - "--since", strconv.FormatInt(startEpoch, 10)) - stdout, err := eventsCmd.StdoutPipe() - err = eventsCmd.Start() - if err != nil { - c.Fatalf("failed to start 'docker events': %s", err) - } - - go func() { - <-finish - eventsCmd.Process.Kill() - }() - cid := <-containerID matchStart := regexp.MustCompile(cid + `(.*) start$`) @@ -1957,19 +1877,13 @@ func (s *DockerSuite) TestBuildCancelationKillsSleep(c *check.C) { close(eventDie) } } - - err = eventsCmd.Wait() - if err != nil && !IsKilled(err) { - c.Fatalf("docker events had bad exit status: %s", err) - } }() buildCmd := exec.Command(dockerBinary, "build", "-t", name, ".") buildCmd.Dir = ctx.Dir stdoutBuild, err := buildCmd.StdoutPipe() - err = buildCmd.Start() - if err != nil { + if err := buildCmd.Start(); err != nil { c.Fatalf("failed to run build: %s", err) } @@ -1994,14 +1908,12 @@ func (s *DockerSuite) TestBuildCancelationKillsSleep(c *check.C) { // Send a kill to the `docker build` command. // Causes the underlying build to be cancelled due to socket close. - err = buildCmd.Process.Kill() - if err != nil { + if err := buildCmd.Process.Kill(); err != nil { c.Fatalf("error killing build command: %s", err) } // Get the exit status of `docker build`, check it exited because killed. - err = buildCmd.Wait() - if err != nil && !IsKilled(err) { + if err := buildCmd.Wait(); err != nil && !IsKilled(err) { c.Fatalf("wait failed during build run: %T %s", err, err) } @@ -2018,7 +1930,6 @@ func (s *DockerSuite) TestBuildCancelationKillsSleep(c *check.C) { func (s *DockerSuite) TestBuildRm(c *check.C) { name := "testbuildrm" - defer deleteImages(name) ctx, err := fakeContext("FROM scratch\nADD foo /\nADD foo /", map[string]string{"foo": "bar"}) if err != nil { c.Fatal(err) @@ -2112,7 +2023,6 @@ func (s *DockerSuite) TestBuildWithVolumes(c *check.C) { "/test8]": emptyMap, } ) - defer deleteImages(name) _, err := buildImage(name, `FROM scratch VOLUME /test1 @@ -2146,7 +2056,6 @@ func (s *DockerSuite) TestBuildWithVolumes(c *check.C) { func (s *DockerSuite) TestBuildMaintainer(c *check.C) { name := "testbuildmaintainer" expected := "dockerio" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch MAINTAINER dockerio`, @@ -2166,7 +2075,6 @@ func (s *DockerSuite) TestBuildMaintainer(c *check.C) { func (s *DockerSuite) TestBuildUser(c *check.C) { name := "testbuilduser" expected := "dockerio" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd @@ -2188,7 +2096,6 @@ func (s *DockerSuite) TestBuildUser(c *check.C) { func (s *DockerSuite) TestBuildRelativeWorkdir(c *check.C) { name := "testbuildrelativeworkdir" expected := "/test2/test3" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN [ "$PWD" = '/' ] @@ -2214,7 +2121,6 @@ func (s *DockerSuite) TestBuildRelativeWorkdir(c *check.C) { func (s *DockerSuite) TestBuildWorkdirWithEnvVariables(c *check.C) { name := "testbuildworkdirwithenvvariables" expected := "/test1/test2" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ENV DIRPATH /test1 @@ -2236,7 +2142,6 @@ func (s *DockerSuite) TestBuildWorkdirWithEnvVariables(c *check.C) { func (s *DockerSuite) TestBuildRelativeCopy(c *check.C) { name := "testbuildrelativecopy" - defer deleteImages(name) dockerfile := ` FROM busybox WORKDIR /test1 @@ -2276,7 +2181,6 @@ func (s *DockerSuite) TestBuildRelativeCopy(c *check.C) { func (s *DockerSuite) TestBuildEnv(c *check.C) { name := "testbuildenv" expected := "[PATH=/test:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PORT=2375]" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ENV PATH /test:$PATH @@ -2299,7 +2203,6 @@ func (s *DockerSuite) TestBuildContextCleanup(c *check.C) { testRequires(c, SameHostDaemon) name := "testbuildcontextcleanup" - defer deleteImages(name) entries, err := ioutil.ReadDir("/var/lib/docker/tmp") if err != nil { c.Fatalf("failed to list contents of tmp dir: %s", err) @@ -2325,7 +2228,6 @@ func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *check.C) { testRequires(c, SameHostDaemon) name := "testbuildcontextcleanup" - defer deleteImages(name) entries, err := ioutil.ReadDir("/var/lib/docker/tmp") if err != nil { c.Fatalf("failed to list contents of tmp dir: %s", err) @@ -2350,7 +2252,6 @@ func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *check.C) { func (s *DockerSuite) TestBuildCmd(c *check.C) { name := "testbuildcmd" expected := "{[/bin/echo Hello World]}" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch CMD ["/bin/echo", "Hello World"]`, @@ -2370,7 +2271,6 @@ func (s *DockerSuite) TestBuildCmd(c *check.C) { func (s *DockerSuite) TestBuildExpose(c *check.C) { name := "testbuildexpose" expected := "map[2375/tcp:{}]" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch EXPOSE 2375`, @@ -2413,7 +2313,6 @@ func (s *DockerSuite) TestBuildExposeMorePorts(c *check.C) { tmpl.Execute(buf, portList) name := "testbuildexpose" - defer deleteImages(name) _, err := buildImage(name, buf.String(), true) if err != nil { c.Fatal(err) @@ -2458,7 +2357,6 @@ func (s *DockerSuite) TestBuildExposeOrder(c *check.C) { id1 := buildID("testbuildexpose1", "80 2375") id2 := buildID("testbuildexpose2", "2375 80") - defer deleteImages("testbuildexpose1", "testbuildexpose2") if id1 != id2 { c.Errorf("EXPOSE should invalidate the cache only when ports actually changed") } @@ -2467,7 +2365,6 @@ func (s *DockerSuite) TestBuildExposeOrder(c *check.C) { func (s *DockerSuite) TestBuildExposeUpperCaseProto(c *check.C) { name := "testbuildexposeuppercaseproto" expected := "map[5678/udp:{}]" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch EXPOSE 5678/UDP`, @@ -2488,7 +2385,6 @@ func (s *DockerSuite) TestBuildExposeHostPort(c *check.C) { // start building docker file with ip:hostPort:containerPort name := "testbuildexpose" expected := "map[5678/tcp:{}]" - defer deleteImages(name) _, out, err := buildImageWithOut(name, `FROM scratch EXPOSE 192.168.1.2:2375:5678`, @@ -2513,7 +2409,6 @@ func (s *DockerSuite) TestBuildExposeHostPort(c *check.C) { func (s *DockerSuite) TestBuildEmptyEntrypointInheritance(c *check.C) { name := "testbuildentrypointinheritance" name2 := "testbuildentrypointinheritance2" - defer deleteImages(name, name2) _, err := buildImage(name, `FROM busybox @@ -2554,7 +2449,6 @@ func (s *DockerSuite) TestBuildEmptyEntrypointInheritance(c *check.C) { func (s *DockerSuite) TestBuildEmptyEntrypoint(c *check.C) { name := "testbuildentrypoint" - defer deleteImages(name) expected := "{[]}" _, err := buildImage(name, @@ -2577,7 +2471,6 @@ func (s *DockerSuite) TestBuildEmptyEntrypoint(c *check.C) { func (s *DockerSuite) TestBuildEntrypoint(c *check.C) { name := "testbuildentrypoint" expected := "{[/bin/echo]}" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch ENTRYPOINT ["/bin/echo"]`, @@ -2617,7 +2510,6 @@ func (s *DockerSuite) TestBuildOnBuildLimitedInheritence(c *check.C) { if err != nil { c.Fatalf("build failed to complete: %s, %v", out1, err) } - defer deleteImages(name1) } { name2 := "testonbuildtrigger2" @@ -2634,7 +2526,6 @@ func (s *DockerSuite) TestBuildOnBuildLimitedInheritence(c *check.C) { if err != nil { c.Fatalf("build failed to complete: %s, %v", out2, err) } - defer deleteImages(name2) } { name3 := "testonbuildtrigger3" @@ -2652,7 +2543,6 @@ func (s *DockerSuite) TestBuildOnBuildLimitedInheritence(c *check.C) { c.Fatalf("build failed to complete: %s, %v", out3, err) } - defer deleteImages(name3) } // ONBUILD should be run in second build. @@ -2669,7 +2559,6 @@ func (s *DockerSuite) TestBuildOnBuildLimitedInheritence(c *check.C) { func (s *DockerSuite) TestBuildWithCache(c *check.C) { name := "testbuildwithcache" - defer deleteImages(name) id1, err := buildImage(name, `FROM scratch MAINTAINER dockerio @@ -2696,7 +2585,6 @@ func (s *DockerSuite) TestBuildWithCache(c *check.C) { func (s *DockerSuite) TestBuildWithoutCache(c *check.C) { name := "testbuildwithoutcache" name2 := "testbuildwithoutcache2" - defer deleteImages(name, name2) id1, err := buildImage(name, `FROM scratch MAINTAINER dockerio @@ -2723,8 +2611,6 @@ func (s *DockerSuite) TestBuildWithoutCache(c *check.C) { func (s *DockerSuite) TestBuildConditionalCache(c *check.C) { name := "testbuildconditionalcache" - name2 := "testbuildconditionalcache2" - defer deleteImages(name, name2) dockerfile := ` FROM busybox @@ -2761,13 +2647,11 @@ func (s *DockerSuite) TestBuildConditionalCache(c *check.C) { if id3 != id2 { c.Fatal("Should have used the cache") } - } func (s *DockerSuite) TestBuildADDLocalFileWithCache(c *check.C) { name := "testbuildaddlocalfilewithcache" name2 := "testbuildaddlocalfilewithcache2" - defer deleteImages(name, name2) dockerfile := ` FROM busybox MAINTAINER dockerio @@ -2796,7 +2680,6 @@ func (s *DockerSuite) TestBuildADDLocalFileWithCache(c *check.C) { func (s *DockerSuite) TestBuildADDMultipleLocalFileWithCache(c *check.C) { name := "testbuildaddmultiplelocalfilewithcache" name2 := "testbuildaddmultiplelocalfilewithcache2" - defer deleteImages(name, name2) dockerfile := ` FROM busybox MAINTAINER dockerio @@ -2825,7 +2708,6 @@ func (s *DockerSuite) TestBuildADDMultipleLocalFileWithCache(c *check.C) { func (s *DockerSuite) TestBuildADDLocalFileWithoutCache(c *check.C) { name := "testbuildaddlocalfilewithoutcache" name2 := "testbuildaddlocalfilewithoutcache2" - defer deleteImages(name, name2) dockerfile := ` FROM busybox MAINTAINER dockerio @@ -2854,7 +2736,6 @@ func (s *DockerSuite) TestBuildADDLocalFileWithoutCache(c *check.C) { func (s *DockerSuite) TestBuildCopyDirButNotFile(c *check.C) { name := "testbuildcopydirbutnotfile" name2 := "testbuildcopydirbutnotfile2" - defer deleteImages(name, name2) dockerfile := ` FROM scratch COPY dir /tmp/` @@ -2888,7 +2769,6 @@ func (s *DockerSuite) TestBuildADDCurrentDirWithCache(c *check.C) { name3 := name + "3" name4 := name + "4" name5 := name + "5" - defer deleteImages(name, name2, name3, name4, name5) dockerfile := ` FROM scratch MAINTAINER dockerio @@ -2950,7 +2830,6 @@ func (s *DockerSuite) TestBuildADDCurrentDirWithCache(c *check.C) { func (s *DockerSuite) TestBuildADDCurrentDirWithoutCache(c *check.C) { name := "testbuildaddcurrentdirwithoutcache" name2 := "testbuildaddcurrentdirwithoutcache2" - defer deleteImages(name, name2) dockerfile := ` FROM scratch MAINTAINER dockerio @@ -2977,7 +2856,6 @@ func (s *DockerSuite) TestBuildADDCurrentDirWithoutCache(c *check.C) { func (s *DockerSuite) TestBuildADDRemoteFileWithCache(c *check.C) { name := "testbuildaddremotefilewithcache" - defer deleteImages(name) server, err := fakeStorage(map[string]string{ "baz": "hello", }) @@ -3010,7 +2888,6 @@ func (s *DockerSuite) TestBuildADDRemoteFileWithCache(c *check.C) { func (s *DockerSuite) TestBuildADDRemoteFileWithoutCache(c *check.C) { name := "testbuildaddremotefilewithoutcache" name2 := "testbuildaddremotefilewithoutcache2" - defer deleteImages(name, name2) server, err := fakeStorage(map[string]string{ "baz": "hello", }) @@ -3046,8 +2923,6 @@ func (s *DockerSuite) TestBuildADDRemoteFileMTime(c *check.C) { name3 := name + "3" name4 := name + "4" - defer deleteImages(name, name2, name3, name4) - files := map[string]string{"baz": "hello"} server, err := fakeStorage(files) if err != nil { @@ -3115,7 +2990,6 @@ func (s *DockerSuite) TestBuildADDRemoteFileMTime(c *check.C) { func (s *DockerSuite) TestBuildADDLocalAndRemoteFilesWithCache(c *check.C) { name := "testbuildaddlocalandremotefilewithcache" - defer deleteImages(name) server, err := fakeStorage(map[string]string{ "baz": "hello", }) @@ -3167,7 +3041,6 @@ CMD ["cat", "/foo"]`, } name := "contexttar" buildCmd := exec.Command(dockerBinary, "build", "-t", name, "-") - defer deleteImages(name) buildCmd.Stdin = context if out, _, err := runCommandWithOutput(buildCmd); err != nil { @@ -3194,15 +3067,12 @@ func (s *DockerSuite) TestBuildNoContext(c *check.C) { if out, _ := dockerCmd(c, "run", "--rm", "nocontext"); out != "ok\n" { c.Fatalf("run produced invalid output: %q, expected %q", out, "ok") } - - deleteImages("nocontext") } // TODO: TestCaching func (s *DockerSuite) TestBuildADDLocalAndRemoteFilesWithoutCache(c *check.C) { name := "testbuildaddlocalandremotefilewithoutcache" name2 := "testbuildaddlocalandremotefilewithoutcache2" - defer deleteImages(name, name2) server, err := fakeStorage(map[string]string{ "baz": "hello", }) @@ -3237,7 +3107,6 @@ func (s *DockerSuite) TestBuildADDLocalAndRemoteFilesWithoutCache(c *check.C) { func (s *DockerSuite) TestBuildWithVolumeOwnership(c *check.C) { name := "testbuildimg" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox:latest @@ -3269,7 +3138,6 @@ func (s *DockerSuite) TestBuildWithVolumeOwnership(c *check.C) { // utilizing cache func (s *DockerSuite) TestBuildEntrypointRunCleanup(c *check.C) { name := "testbuildcmdcleanup" - defer deleteImages(name) if _, err := buildImage(name, `FROM busybox RUN echo "hello"`, @@ -3303,7 +3171,6 @@ func (s *DockerSuite) TestBuildEntrypointRunCleanup(c *check.C) { func (s *DockerSuite) TestBuildForbiddenContextPath(c *check.C) { name := "testbuildforbidpath" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD ../../ test/ `, @@ -3325,7 +3192,6 @@ func (s *DockerSuite) TestBuildForbiddenContextPath(c *check.C) { func (s *DockerSuite) TestBuildADDFileNotFound(c *check.C) { name := "testbuildaddnotfound" - defer deleteImages(name) ctx, err := fakeContext(`FROM scratch ADD foo /usr/local/bar`, map[string]string{"bar": "hello"}) @@ -3344,7 +3210,6 @@ func (s *DockerSuite) TestBuildADDFileNotFound(c *check.C) { func (s *DockerSuite) TestBuildInheritance(c *check.C) { name := "testbuildinheritance" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch @@ -3384,7 +3249,6 @@ func (s *DockerSuite) TestBuildInheritance(c *check.C) { func (s *DockerSuite) TestBuildFails(c *check.C) { name := "testbuildfails" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN sh -c "exit 23"`, @@ -3400,7 +3264,6 @@ func (s *DockerSuite) TestBuildFails(c *check.C) { func (s *DockerSuite) TestBuildFailsDockerfileEmpty(c *check.C) { name := "testbuildfails" - defer deleteImages(name) _, err := buildImage(name, ``, true) if err != nil { if !strings.Contains(err.Error(), "The Dockerfile (Dockerfile) cannot be empty") { @@ -3413,7 +3276,6 @@ func (s *DockerSuite) TestBuildFailsDockerfileEmpty(c *check.C) { func (s *DockerSuite) TestBuildOnBuild(c *check.C) { name := "testbuildonbuild" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ONBUILD RUN touch foobar`, @@ -3432,7 +3294,6 @@ func (s *DockerSuite) TestBuildOnBuild(c *check.C) { func (s *DockerSuite) TestBuildOnBuildForbiddenChained(c *check.C) { name := "testbuildonbuildforbiddenchained" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ONBUILD ONBUILD RUN touch foobar`, @@ -3448,7 +3309,6 @@ func (s *DockerSuite) TestBuildOnBuildForbiddenChained(c *check.C) { func (s *DockerSuite) TestBuildOnBuildForbiddenFrom(c *check.C) { name := "testbuildonbuildforbiddenfrom" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ONBUILD FROM scratch`, @@ -3464,7 +3324,6 @@ func (s *DockerSuite) TestBuildOnBuildForbiddenFrom(c *check.C) { func (s *DockerSuite) TestBuildOnBuildForbiddenMaintainer(c *check.C) { name := "testbuildonbuildforbiddenmaintainer" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ONBUILD MAINTAINER docker.io`, @@ -3481,7 +3340,6 @@ func (s *DockerSuite) TestBuildOnBuildForbiddenMaintainer(c *check.C) { // gh #2446 func (s *DockerSuite) TestBuildAddToSymlinkDest(c *check.C) { name := "testbuildaddtosymlinkdest" - defer deleteImages(name) ctx, err := fakeContext(`FROM busybox RUN mkdir /foo RUN ln -s /foo /bar @@ -3502,7 +3360,6 @@ func (s *DockerSuite) TestBuildAddToSymlinkDest(c *check.C) { func (s *DockerSuite) TestBuildEscapeWhitespace(c *check.C) { name := "testbuildescaping" - defer deleteImages(name) _, err := buildImage(name, ` FROM busybox @@ -3526,7 +3383,6 @@ docker.com>" func (s *DockerSuite) TestBuildVerifyIntString(c *check.C) { // Verify that strings that look like ints are still passed as strings name := "testbuildstringing" - defer deleteImages(name) _, err := buildImage(name, ` FROM busybox @@ -3535,7 +3391,7 @@ func (s *DockerSuite) TestBuildVerifyIntString(c *check.C) { out, rc, err := runCommandWithOutput(exec.Command(dockerBinary, "inspect", name)) if rc != 0 || err != nil { - c.Fatalf("Unexcepted error from inspect: rc: %v err: %v", rc, err) + c.Fatalf("Unexpected error from inspect: rc: %v err: %v", rc, err) } if !strings.Contains(out, "\"123\"") { @@ -3546,7 +3402,6 @@ func (s *DockerSuite) TestBuildVerifyIntString(c *check.C) { func (s *DockerSuite) TestBuildDockerignore(c *check.C) { name := "testbuilddockerignore" - defer deleteImages(name) dockerfile := ` FROM busybox ADD . /bla @@ -3555,20 +3410,29 @@ func (s *DockerSuite) TestBuildDockerignore(c *check.C) { RUN [[ ! -e /bla/src/_vendor ]] RUN [[ ! -e /bla/.gitignore ]] RUN [[ ! -e /bla/README.md ]] + RUN [[ ! -e /bla/dir/foo ]] + RUN [[ ! -e /bla/foo ]] RUN [[ ! -e /bla/.git ]]` ctx, err := fakeContext(dockerfile, map[string]string{ "Makefile": "all:", ".git/HEAD": "ref: foo", "src/x.go": "package main", "src/_vendor/v.go": "package main", + "dir/foo": "", ".gitignore": "", "README.md": "readme", - ".dockerignore": ".git\npkg\n.gitignore\nsrc/_vendor\n*.md", + ".dockerignore": ` +.git +pkg +.gitignore +src/_vendor +*.md +dir`, }) - defer ctx.Close() if err != nil { c.Fatal(err) } + defer ctx.Close() if _, err := buildImageFromContext(name, ctx, true); err != nil { c.Fatal(err) } @@ -3576,7 +3440,6 @@ func (s *DockerSuite) TestBuildDockerignore(c *check.C) { func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *check.C) { name := "testbuilddockerignorecleanpaths" - defer deleteImages(name) dockerfile := ` FROM busybox ADD . /tmp/ @@ -3596,9 +3459,57 @@ func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *check.C) { } } +func (s *DockerSuite) TestBuildDockerignoreExceptions(c *check.C) { + name := "testbuilddockerignoreexceptions" + defer deleteImages(name) + dockerfile := ` + FROM busybox + ADD . /bla + RUN [[ -f /bla/src/x.go ]] + RUN [[ -f /bla/Makefile ]] + RUN [[ ! -e /bla/src/_vendor ]] + RUN [[ ! -e /bla/.gitignore ]] + RUN [[ ! -e /bla/README.md ]] + RUN [[ -e /bla/dir/dir/foo ]] + RUN [[ ! -e /bla/dir/foo1 ]] + RUN [[ -f /bla/dir/e ]] + RUN [[ -f /bla/dir/e-dir/foo ]] + RUN [[ ! -e /bla/foo ]] + RUN [[ ! -e /bla/.git ]]` + ctx, err := fakeContext(dockerfile, map[string]string{ + "Makefile": "all:", + ".git/HEAD": "ref: foo", + "src/x.go": "package main", + "src/_vendor/v.go": "package main", + "dir/foo": "", + "dir/foo1": "", + "dir/dir/f1": "", + "dir/dir/foo": "", + "dir/e": "", + "dir/e-dir/foo": "", + ".gitignore": "", + "README.md": "readme", + ".dockerignore": ` +.git +pkg +.gitignore +src/_vendor +*.md +dir +!dir/e* +!dir/dir/foo`, + }) + if err != nil { + c.Fatal(err) + } + defer ctx.Close() + if _, err := buildImageFromContext(name, ctx, true); err != nil { + c.Fatal(err) + } +} + func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *check.C) { name := "testbuilddockerignoredockerfile" - defer deleteImages(name) dockerfile := ` FROM busybox ADD . /tmp/ @@ -3627,7 +3538,6 @@ func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *check.C) { func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *check.C) { name := "testbuilddockerignoredockerfile" - defer deleteImages(name) dockerfile := ` FROM busybox ADD . /tmp/ @@ -3658,7 +3568,6 @@ func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *check.C) { func (s *DockerSuite) TestBuildDockerignoringDockerignore(c *check.C) { name := "testbuilddockerignoredockerignore" - defer deleteImages(name) dockerfile := ` FROM busybox ADD . /tmp/ @@ -3682,7 +3591,6 @@ func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *check.C) { var id2 string name := "testbuilddockerignoretouchdockerfile" - defer deleteImages(name) dockerfile := ` FROM busybox ADD . /tmp/` @@ -3732,7 +3640,6 @@ func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *check.C) { func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *check.C) { name := "testbuilddockerignorewholedir" - defer deleteImages(name) dockerfile := ` FROM busybox COPY . / @@ -3741,6 +3648,7 @@ func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *check.C) { ctx, err := fakeContext(dockerfile, map[string]string{ "Dockerfile": "FROM scratch", "Makefile": "all:", + ".gitignore": "", ".dockerignore": ".*\n", }) defer ctx.Close() @@ -3754,7 +3662,6 @@ func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *check.C) { func (s *DockerSuite) TestBuildLineBreak(c *check.C) { name := "testbuildlinebreak" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN sh -c 'echo root:testpass \ @@ -3770,7 +3677,6 @@ RUN [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]`, func (s *DockerSuite) TestBuildEOLInLine(c *check.C) { name := "testbuildeolinline" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN sh -c 'echo root:testpass > /tmp/passwd' @@ -3786,7 +3692,6 @@ RUN [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]`, func (s *DockerSuite) TestBuildCommentsShebangs(c *check.C) { name := "testbuildcomments" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox # This is an ordinary comment. @@ -3805,7 +3710,6 @@ RUN [ "$(/hello.sh)" = "hello world" ]`, func (s *DockerSuite) TestBuildUsersAndGroups(c *check.C) { name := "testbuildusers" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox @@ -3868,7 +3772,6 @@ RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/10 func (s *DockerSuite) TestBuildEnvUsage(c *check.C) { name := "testbuildenvusage" - defer deleteImages(name) dockerfile := `FROM busybox ENV HOME /root ENV PATH $HOME/bin:$PATH @@ -3904,7 +3807,6 @@ RUN [ "$ghi" = "def" ] func (s *DockerSuite) TestBuildEnvUsage2(c *check.C) { name := "testbuildenvusage2" - defer deleteImages(name) dockerfile := `FROM busybox ENV abc=def RUN [ "$abc" = "def" ] @@ -4007,7 +3909,6 @@ RUN [ "$eee1,$eee2,$eee3,$eee4" = 'foo,foo,foo,foo' ] func (s *DockerSuite) TestBuildAddScript(c *check.C) { name := "testbuildaddscript" - defer deleteImages(name) dockerfile := ` FROM busybox ADD test /test @@ -4030,7 +3931,6 @@ RUN [ "$(cat /testfile)" = 'test!' ]` func (s *DockerSuite) TestBuildAddTar(c *check.C) { name := "testbuildaddtar" - defer deleteImages(name) ctx := func() *FakeContext { dockerfile := ` @@ -4085,7 +3985,6 @@ RUN cat /existing-directory-trailing-slash/test/foo | grep Hi` func (s *DockerSuite) TestBuildAddTarXz(c *check.C) { name := "testbuildaddtarxz" - defer deleteImages(name) ctx := func() *FakeContext { dockerfile := ` @@ -4136,7 +4035,6 @@ func (s *DockerSuite) TestBuildAddTarXz(c *check.C) { func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) { name := "testbuildaddtarxzgz" - defer deleteImages(name) ctx := func() *FakeContext { dockerfile := ` @@ -4195,7 +4093,6 @@ func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) { func (s *DockerSuite) TestBuildFromGIT(c *check.C) { name := "testbuildfromgit" - defer deleteImages(name) git, err := fakeGIT("repo", map[string]string{ "Dockerfile": `FROM busybox ADD first /first @@ -4223,7 +4120,6 @@ func (s *DockerSuite) TestBuildFromGIT(c *check.C) { func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *check.C) { name := "testbuildcmdcleanuponentrypoint" - defer deleteImages(name) if _, err := buildImage(name, `FROM scratch CMD ["test"] @@ -4256,7 +4152,6 @@ func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *check.C) { func (s *DockerSuite) TestBuildClearCmd(c *check.C) { name := "testbuildclearcmd" - defer deleteImages(name) _, err := buildImage(name, `From scratch ENTRYPOINT ["/bin/bash"] @@ -4276,7 +4171,6 @@ func (s *DockerSuite) TestBuildClearCmd(c *check.C) { func (s *DockerSuite) TestBuildEmptyCmd(c *check.C) { name := "testbuildemptycmd" - defer deleteImages(name) if _, err := buildImage(name, "FROM scratch\nMAINTAINER quux\n", true); err != nil { c.Fatal(err) } @@ -4291,14 +4185,10 @@ func (s *DockerSuite) TestBuildEmptyCmd(c *check.C) { func (s *DockerSuite) TestBuildOnBuildOutput(c *check.C) { name := "testbuildonbuildparent" - defer deleteImages(name) if _, err := buildImage(name, "FROM busybox\nONBUILD RUN echo foo\n", true); err != nil { c.Fatal(err) } - childname := "testbuildonbuildchild" - defer deleteImages(childname) - _, out, err := buildImageWithOut(name, "FROM "+name+"\nMAINTAINER quux\n", true) if err != nil { c.Fatal(err) @@ -4312,7 +4202,6 @@ func (s *DockerSuite) TestBuildOnBuildOutput(c *check.C) { func (s *DockerSuite) TestBuildInvalidTag(c *check.C) { name := "abcd:" + stringutils.GenerateRandomAlphaOnlyString(200) - defer deleteImages(name) _, out, err := buildImageWithOut(name, "FROM scratch\nMAINTAINER quux\n", true) // if the error doesnt check for illegal tag name, or the image is built // then this should fail @@ -4323,7 +4212,6 @@ func (s *DockerSuite) TestBuildInvalidTag(c *check.C) { func (s *DockerSuite) TestBuildCmdShDashC(c *check.C) { name := "testbuildcmdshc" - defer deleteImages(name) if _, err := buildImage(name, "FROM busybox\nCMD echo cmd\n", true); err != nil { c.Fatal(err) } @@ -4346,7 +4234,6 @@ func (s *DockerSuite) TestBuildCmdSpaces(c *check.C) { // the arg separator to make sure ["echo","hi"] and ["echo hi"] don't // look the same name := "testbuildcmdspaces" - defer deleteImages(name) var id1 string var id2 string var err error @@ -4380,7 +4267,6 @@ func (s *DockerSuite) TestBuildCmdSpaces(c *check.C) { func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *check.C) { name := "testbuildcmdjson" - defer deleteImages(name) if _, err := buildImage(name, "FROM busybox\nCMD [\"echo\", \"cmd\"]", true); err != nil { c.Fatal(err) } @@ -4400,7 +4286,6 @@ func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *check.C) { func (s *DockerSuite) TestBuildErrorInvalidInstruction(c *check.C) { name := "testbuildignoreinvalidinstruction" - defer deleteImages(name) out, _, err := buildImageWithOut(name, "FROM busybox\nfoo bar", true) if err == nil { @@ -4410,7 +4295,6 @@ func (s *DockerSuite) TestBuildErrorInvalidInstruction(c *check.C) { } func (s *DockerSuite) TestBuildEntrypointInheritance(c *check.C) { - defer deleteImages("parent", "child") if _, err := buildImage("parent", ` FROM busybox @@ -4447,8 +4331,6 @@ func (s *DockerSuite) TestBuildEntrypointInheritanceInspect(c *check.C) { expected = `["/bin/sh","-c","echo quux"]` ) - defer deleteImages(name, name2) - if _, err := buildImage(name, "FROM busybox\nENTRYPOINT /foo/bar", true); err != nil { c.Fatal(err) } @@ -4481,7 +4363,6 @@ func (s *DockerSuite) TestBuildEntrypointInheritanceInspect(c *check.C) { func (s *DockerSuite) TestBuildRunShEntrypoint(c *check.C) { name := "testbuildentrypoint" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox ENTRYPOINT /bin/echo`, @@ -4500,7 +4381,6 @@ func (s *DockerSuite) TestBuildRunShEntrypoint(c *check.C) { func (s *DockerSuite) TestBuildExoticShellInterpolation(c *check.C) { name := "testbuildexoticshellinterpolation" - defer deleteImages(name) _, err := buildImage(name, ` FROM busybox @@ -4534,7 +4414,6 @@ func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *check.C) { // as a "string" insead of "JSON array" and pass it on to "sh -c" and // it should barf on it. name := "testbuildsinglequotefails" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox @@ -4550,7 +4429,6 @@ func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *check.C) { func (s *DockerSuite) TestBuildVerboseOut(c *check.C) { name := "testbuildverboseout" - defer deleteImages(name) _, out, err := buildImageWithOut(name, `FROM busybox @@ -4568,7 +4446,6 @@ RUN echo 123`, func (s *DockerSuite) TestBuildWithTabs(c *check.C) { name := "testbuildwithtabs" - defer deleteImages(name) _, err := buildImage(name, "FROM busybox\nRUN echo\tone\t\ttwo", true) if err != nil { @@ -4588,7 +4465,6 @@ func (s *DockerSuite) TestBuildWithTabs(c *check.C) { func (s *DockerSuite) TestBuildLabels(c *check.C) { name := "testbuildlabel" expected := `{"License":"GPL","Vendor":"Acme"}` - defer deleteImages(name) _, err := buildImage(name, `FROM busybox LABEL Vendor=Acme @@ -4608,7 +4484,6 @@ func (s *DockerSuite) TestBuildLabels(c *check.C) { func (s *DockerSuite) TestBuildLabelsCache(c *check.C) { name := "testbuildlabelcache" - defer deleteImages(name) id1, err := buildImage(name, `FROM busybox @@ -4659,7 +4534,6 @@ func (s *DockerSuite) TestBuildStderr(c *check.C) { // This test just makes sure that no non-error output goes // to stderr name := "testbuildstderr" - defer deleteImages(name) _, _, stderr, err := buildImageWithStdoutStderr(name, "FROM busybox\nRUN echo one", true) if err != nil { @@ -4685,7 +4559,6 @@ func (s *DockerSuite) TestBuildChownSingleFile(c *check.C) { testRequires(c, UnixCli) // test uses chown: not available on windows name := "testbuildchownsinglefile" - defer deleteImages(name) ctx, err := fakeContext(` FROM busybox @@ -4765,7 +4638,6 @@ func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) { func (s *DockerSuite) TestBuildXZHost(c *check.C) { name := "testbuildxzhost" - defer deleteImages(name) ctx, err := fakeContext(` FROM busybox @@ -4796,7 +4668,6 @@ func (s *DockerSuite) TestBuildVolumesRetainContents(c *check.C) { name = "testbuildvolumescontent" expected = "some text" ) - defer deleteImages(name) ctx, err := fakeContext(` FROM busybox COPY content /foo/file @@ -4929,7 +4800,6 @@ func (s *DockerSuite) TestBuildRenamedDockerfile(c *check.C) { func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) { testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows - defer deleteImages("test1") ctx, err := fakeContext(`FROM busybox RUN echo from dockerfile`, @@ -4954,7 +4824,6 @@ func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) { func (s *DockerSuite) TestBuildWithTwoDockerfiles(c *check.C) { testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows - defer deleteImages("test1") ctx, err := fakeContext(`FROM busybox RUN echo from Dockerfile`, @@ -4978,7 +4847,6 @@ RUN echo from Dockerfile`, } func (s *DockerSuite) TestBuildFromURLWithF(c *check.C) { - defer deleteImages("test1") server, err := fakeStorage(map[string]string{"baz": `FROM busybox RUN echo from baz @@ -5013,7 +4881,6 @@ RUN echo from Dockerfile`, } func (s *DockerSuite) TestBuildFromStdinWithF(c *check.C) { - defer deleteImages("test1") ctx, err := fakeContext(`FROM busybox RUN echo from Dockerfile`, @@ -5121,8 +4988,6 @@ func (s *DockerSuite) TestBuildDockerfileOutsideContext(c *check.C) { if err == nil { c.Fatalf("Expected error. Out: %s", out) } - deleteImages(name) - } func (s *DockerSuite) TestBuildSpaces(c *check.C) { @@ -5134,7 +4999,6 @@ func (s *DockerSuite) TestBuildSpaces(c *check.C) { ) name := "testspaces" - defer deleteImages(name) ctx, err := fakeContext("FROM busybox\nCOPY\n", map[string]string{ "Dockerfile": "FROM busybox\nCOPY\n", @@ -5199,7 +5063,6 @@ func (s *DockerSuite) TestBuildSpaces(c *check.C) { func (s *DockerSuite) TestBuildSpacesWithQuotes(c *check.C) { // Test to make sure that spaces in quotes aren't lost name := "testspacesquotes" - defer deleteImages(name) dockerfile := `FROM busybox RUN echo " \ @@ -5212,7 +5075,7 @@ RUN echo " \ expecting := "\n foo \n" if !strings.Contains(out, expecting) { - c.Fatalf("Bad output: %q expecting to contian %q", out, expecting) + c.Fatalf("Bad output: %q expecting to contain %q", out, expecting) } } @@ -5275,7 +5138,6 @@ func (s *DockerSuite) TestBuildMissingArgs(c *check.C) { } func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) { - defer deleteImages("sc") _, out, err := buildImageWithOut("sc", "FROM scratch", true) if err == nil { c.Fatalf("Build was supposed to fail") @@ -5286,7 +5148,6 @@ func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) { } func (s *DockerSuite) TestBuildDotDotFile(c *check.C) { - defer deleteImages("sc") ctx, err := fakeContext("FROM busybox\n", map[string]string{ "..gitme": "", @@ -5302,7 +5163,6 @@ func (s *DockerSuite) TestBuildDotDotFile(c *check.C) { } func (s *DockerSuite) TestBuildNotVerbose(c *check.C) { - defer deleteImages("verbose") ctx, err := fakeContext("FROM busybox\nENV abc=hi\nRUN echo $abc there", map[string]string{}) if err != nil { @@ -5337,8 +5197,6 @@ func (s *DockerSuite) TestBuildNotVerbose(c *check.C) { func (s *DockerSuite) TestBuildRUNoneJSON(c *check.C) { name := "testbuildrunonejson" - defer deleteImages(name, "hello-world") - ctx, err := fakeContext(`FROM hello-world:frozen RUN [ "/hello" ]`, map[string]string{}) if err != nil { @@ -5361,7 +5219,6 @@ RUN [ "/hello" ]`, map[string]string{}) func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { name := "testbuildresourceconstraints" - defer deleteImages(name, "hello-world") ctx, err := fakeContext(` FROM hello-world:frozen @@ -5425,7 +5282,6 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) { name := "testbuildemptystringvolume" - defer deleteImages(name) _, err := buildImage(name, ` FROM busybox diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go index bd4518434183d2ce3c87964624b1994bb383909a..b9b319cf94a41e02266ee7a2d58430e369ed4df3 100644 --- a/integration-cli/docker_cli_by_digest_test.go +++ b/integration-cli/docker_cli_by_digest_test.go @@ -33,7 +33,6 @@ func setupImageWithTag(tag string) (string, error) { if out, _, err := runCommandWithOutput(cmd); err != nil { return "", fmt.Errorf("image tagging failed: %s, %v", out, err) } - defer deleteImages(repoAndTag) // delete the container as we don't need it any more if err := deleteContainer(containerName); err != nil { @@ -63,9 +62,7 @@ func setupImageWithTag(tag string) (string, error) { return pushDigest, nil } -func (s *DockerSuite) TestPullByTagDisplaysDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *check.C) { pushDigest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -77,7 +74,6 @@ func (s *DockerSuite) TestPullByTagDisplaysDigest(c *check.C) { if err != nil { c.Fatalf("error pulling by tag: %s, %v", out, err) } - defer deleteImages(repoName) // the pull output includes "Digest: ", so find that matches := digestRegex.FindStringSubmatch(out) @@ -90,12 +86,9 @@ func (s *DockerSuite) TestPullByTagDisplaysDigest(c *check.C) { if pushDigest != pullDigest { c.Fatalf("push digest %q didn't match pull digest %q", pushDigest, pullDigest) } - } -func (s *DockerSuite) TestPullByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPullByDigest(c *check.C) { pushDigest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -108,7 +101,6 @@ func (s *DockerSuite) TestPullByDigest(c *check.C) { if err != nil { c.Fatalf("error pulling by digest: %s, %v", out, err) } - defer deleteImages(imageReference) // the pull output includes "Digest: ", so find that matches := digestRegex.FindStringSubmatch(out) @@ -121,12 +113,9 @@ func (s *DockerSuite) TestPullByDigest(c *check.C) { if pushDigest != pullDigest { c.Fatalf("push digest %q didn't match pull digest %q", pushDigest, pullDigest) } - } -func (s *DockerSuite) TestCreateByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestCreateByDigest(c *check.C) { pushDigest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -148,12 +137,9 @@ func (s *DockerSuite) TestCreateByDigest(c *check.C) { if res != imageReference { c.Fatalf("unexpected Config.Image: %s (expected %s)", res, imageReference) } - } -func (s *DockerSuite) TestRunByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestRunByDigest(c *check.C) { pushDigest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -184,12 +170,9 @@ func (s *DockerSuite) TestRunByDigest(c *check.C) { if res != imageReference { c.Fatalf("unexpected Config.Image: %s (expected %s)", res, imageReference) } - } -func (s *DockerSuite) TestRemoveImageByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestRemoveImageByDigest(c *check.C) { digest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -220,12 +203,9 @@ func (s *DockerSuite) TestRemoveImageByDigest(c *check.C) { } else if !strings.Contains(err.Error(), "No such image") { c.Fatalf("expected 'No such image' output, got %v", err) } - } -func (s *DockerSuite) TestBuildByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestBuildByDigest(c *check.C) { digest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -248,7 +228,6 @@ func (s *DockerSuite) TestBuildByDigest(c *check.C) { // do the build name := "buildbydigest" - defer deleteImages(name) _, err = buildImage(name, fmt.Sprintf( `FROM %s CMD ["/bin/echo", "Hello World"]`, imageReference), @@ -266,12 +245,9 @@ func (s *DockerSuite) TestBuildByDigest(c *check.C) { if res != imageID { c.Fatalf("Image %s, expected %s", res, imageID) } - } -func (s *DockerSuite) TestTagByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestTagByDigest(c *check.C) { digest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -306,12 +282,9 @@ func (s *DockerSuite) TestTagByDigest(c *check.C) { if tagID != expectedID { c.Fatalf("expected image id %q, got %q", expectedID, tagID) } - } -func (s *DockerSuite) TestListImagesWithoutDigests(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestListImagesWithoutDigests(c *check.C) { digest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -338,9 +311,7 @@ func (s *DockerSuite) TestListImagesWithoutDigests(c *check.C) { } -func (s *DockerSuite) TestListImagesWithDigests(c *check.C) { - defer setupRegistry(c)() - defer deleteImages(repoName+":tag1", repoName+":tag2") +func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) { // setup image1 digest1, err := setupImageWithTag("tag1") @@ -348,7 +319,6 @@ func (s *DockerSuite) TestListImagesWithDigests(c *check.C) { c.Fatalf("error setting up image: %v", err) } imageReference1 := fmt.Sprintf("%s@%s", repoName, digest1) - defer deleteImages(imageReference1) c.Logf("imageReference1 = %s", imageReference1) // pull image1 by digest @@ -377,7 +347,6 @@ func (s *DockerSuite) TestListImagesWithDigests(c *check.C) { c.Fatalf("error setting up image: %v", err) } imageReference2 := fmt.Sprintf("%s@%s", repoName, digest2) - defer deleteImages(imageReference2) c.Logf("imageReference2 = %s", imageReference2) // pull image1 by digest @@ -489,12 +458,9 @@ func (s *DockerSuite) TestListImagesWithDigests(c *check.C) { if !busyboxRe.MatchString(out) { c.Fatalf("expected %q: %s", busyboxRe.String(), out) } - } -func (s *DockerSuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C) { pushDigest, err := setupImage() if err != nil { c.Fatalf("error setting up image: %v", err) @@ -508,7 +474,6 @@ func (s *DockerSuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C) { c.Fatalf("error pulling by digest: %s, %v", out, err) } // just in case... - defer deleteImages(imageReference) imageID, err := inspectField(imageReference, ".Id") if err != nil { @@ -519,5 +484,4 @@ func (s *DockerSuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C) { if _, err := runCommand(cmd); err != nil { c.Fatalf("error deleting image by id: %v", err) } - } diff --git a/integration-cli/docker_cli_commit_test.go b/integration-cli/docker_cli_commit_test.go index a75621f3a3de64116d28383ba28e737f25d6acc7..391cd4ebc5a341e1ed40a7bea02419b344e9545c 100644 --- a/integration-cli/docker_cli_commit_test.go +++ b/integration-cli/docker_cli_commit_test.go @@ -33,10 +33,6 @@ func (s *DockerSuite) TestCommitAfterContainerIsDone(c *check.C) { if out, _, err = runCommandWithOutput(inspectCmd); err != nil { c.Fatalf("failed to inspect image: %s, %v", out, err) } - - deleteContainer(cleanedContainerID) - deleteImages(cleanedImageID) - } func (s *DockerSuite) TestCommitWithoutPause(c *check.C) { @@ -65,10 +61,6 @@ func (s *DockerSuite) TestCommitWithoutPause(c *check.C) { if out, _, err = runCommandWithOutput(inspectCmd); err != nil { c.Fatalf("failed to inspect image: %s, %v", out, err) } - - deleteContainer(cleanedContainerID) - deleteImages(cleanedImageID) - } //test commit a paused container should not unpause it after commit @@ -92,8 +84,6 @@ func (s *DockerSuite) TestCommitPausedContainer(c *check.C) { if err != nil { c.Fatalf("failed to commit container to image: %s, %v", out, err) } - cleanedImageID := strings.TrimSpace(out) - defer deleteImages(cleanedImageID) cmd = exec.Command(dockerBinary, "inspect", "-f", "{{.State.Paused}}", cleanedContainerID) out, _, _, err = runCommandWithStdoutStderr(cmd) @@ -120,7 +110,6 @@ func (s *DockerSuite) TestCommitNewFile(c *check.C) { c.Fatal(err) } imageID = strings.Trim(imageID, "\r\n") - defer deleteImages(imageID) cmd = exec.Command(dockerBinary, "run", imageID, "cat", "/foo") @@ -161,7 +150,6 @@ func (s *DockerSuite) TestCommitHardlink(c *check.C) { c.Fatal(imageID, err) } imageID = strings.Trim(imageID, "\r\n") - defer deleteImages(imageID) cmd = exec.Command(dockerBinary, "run", "-t", "hardlinks", "ls", "-di", "file1", "file2") secondOuput, _, err := runCommandWithOutput(cmd) @@ -185,7 +173,6 @@ func (s *DockerSuite) TestCommitHardlink(c *check.C) { } func (s *DockerSuite) TestCommitTTY(c *check.C) { - defer deleteImages("ttytest") cmd := exec.Command(dockerBinary, "run", "-t", "--name", "tty", "busybox", "/bin/ls") if _, err := runCommand(cmd); err != nil { @@ -220,7 +207,6 @@ func (s *DockerSuite) TestCommitWithHostBindMount(c *check.C) { } imageID = strings.Trim(imageID, "\r\n") - defer deleteImages(imageID) cmd = exec.Command(dockerBinary, "run", "bindtest", "true") @@ -248,7 +234,6 @@ func (s *DockerSuite) TestCommitChange(c *check.C) { c.Fatal(imageId, err) } imageId = strings.Trim(imageId, "\r\n") - defer deleteImages(imageId) expected := map[string]string{ "Config.ExposedPorts": "map[8080/tcp:{}]", @@ -274,11 +259,10 @@ func (s *DockerSuite) TestCommitMergeConfigRun(c *check.C) { id := strings.TrimSpace(out) dockerCmd(c, "commit", `--run={"Cmd": ["cat", "/tmp/foo"]}`, id, "commit-test") - defer deleteImages("commit-test") out, _ = dockerCmd(c, "run", "--name", name, "commit-test") if strings.TrimSpace(out) != "testing" { - c.Fatal("run config in commited container was not merged") + c.Fatal("run config in committed container was not merged") } type cfg struct { diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go index 10c499982e07c5ff6ef0c02872ad0e539dff9d3b..646a8eafe023bf93e0db123383ec21f7c44442c1 100644 --- a/integration-cli/docker_cli_create_test.go +++ b/integration-cli/docker_cli_create_test.go @@ -259,7 +259,6 @@ func (s *DockerSuite) TestCreateLabels(c *check.C) { func (s *DockerSuite) TestCreateLabelFromImage(c *check.C) { imageName := "testcreatebuildlabel" - defer deleteImages(imageName) _, err := buildImage(imageName, `FROM busybox LABEL k1=v1 k2=v2`, diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 2a945827e1caa5bb1e764b736681839697973d0e..e099995ad3aedc097c56ec5b0d28760a80936bcc 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -16,25 +16,23 @@ import ( "github.com/go-check/check" ) -func (s *DockerSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) { - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() - if out, err := d.Cmd("run", "-d", "--name", "top1", "-p", "1234:80", "--restart", "always", "busybox:latest", "top"); err != nil { + if out, err := s.d.Cmd("run", "-d", "--name", "top1", "-p", "1234:80", "--restart", "always", "busybox:latest", "top"); err != nil { c.Fatalf("Could not run top1: err=%v\n%s", err, out) } // --restart=no by default - if out, err := d.Cmd("run", "-d", "--name", "top2", "-p", "80", "busybox:latest", "top"); err != nil { + if out, err := s.d.Cmd("run", "-d", "--name", "top2", "-p", "80", "busybox:latest", "top"); err != nil { c.Fatalf("Could not run top2: err=%v\n%s", err, out) } testRun := func(m map[string]bool, prefix string) { var format string for cont, shouldRun := range m { - out, err := d.Cmd("ps") + out, err := s.d.Cmd("ps") if err != nil { c.Fatalf("Could not run ps: err=%v\n%q", err, out) } @@ -51,34 +49,30 @@ func (s *DockerSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) { testRun(map[string]bool{"top1": true, "top2": true}, "") - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatalf("Could not restart daemon: %v", err) } - testRun(map[string]bool{"top1": true, "top2": false}, "After daemon restart: ") - } -func (s *DockerSuite) TestDaemonRestartWithVolumesRefs(c *check.C) { - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatal(err) } - defer d.Stop() - if out, err := d.Cmd("run", "-d", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil { + if out, err := s.d.Cmd("run", "-d", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil { c.Fatal(err, out) } - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatal(err) } - if _, err := d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil { + if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil { c.Fatal(err) } - if out, err := d.Cmd("rm", "-fv", "volrestarttest2"); err != nil { + if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil { c.Fatal(err, out) } - v, err := d.Cmd("inspect", "--format", "{{ json .Volumes }}", "volrestarttest1") + v, err := s.d.Cmd("inspect", "--format", "{{ json .Volumes }}", "volrestarttest1") if err != nil { c.Fatal(err) } @@ -87,30 +81,25 @@ func (s *DockerSuite) TestDaemonRestartWithVolumesRefs(c *check.C) { if _, err := os.Stat(volumes["/foo"]); err != nil { c.Fatalf("Expected volume to exist: %s - %s", volumes["/foo"], err) } - } -func (s *DockerSuite) TestDaemonStartIptablesFalse(c *check.C) { - d := NewDaemon(c) - if err := d.Start("--iptables=false"); err != nil { +func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *check.C) { + if err := s.d.Start("--iptables=false"); err != nil { c.Fatalf("we should have been able to start the daemon with passing iptables=false: %v", err) } - d.Stop() - } // Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and // no longer has an IP associated, we should gracefully handle that case and associate // an IP with it rather than fail daemon start -func (s *DockerSuite) TestDaemonStartBridgeWithoutIPAssociation(c *check.C) { - d := NewDaemon(c) +func (s *DockerDaemonSuite) TestDaemonStartBridgeWithoutIPAssociation(c *check.C) { // rather than depending on brctl commands to verify docker0 is created and up // let's start the daemon and stop it, and then make a modification to run the // actual test - if err := d.Start(); err != nil { + if err := s.d.Start(); err != nil { c.Fatalf("Could not start daemon: %v", err) } - if err := d.Stop(); err != nil { + if err := s.d.Stop(); err != nil { c.Fatalf("Could not stop daemon: %v", err) } @@ -121,27 +110,18 @@ func (s *DockerSuite) TestDaemonStartBridgeWithoutIPAssociation(c *check.C) { c.Fatalf("failed to remove docker0 IP association: %v, stdout: %q, stderr: %q", err, stdout, stderr) } - if err := d.Start(); err != nil { + if err := s.d.Start(); err != nil { warning := "**WARNING: Docker bridge network in bad state--delete docker0 bridge interface to fix" c.Fatalf("Could not start daemon when docker0 has no IP address: %v\n%s", err, warning) } - - // cleanup - stop the daemon if test passed - if err := d.Stop(); err != nil { - c.Fatalf("Could not stop daemon: %v", err) - } - } -func (s *DockerSuite) TestDaemonIptablesClean(c *check.C) { - - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonIptablesClean(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() - if out, err := d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil { + if out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil { c.Fatalf("Could not run top: %s, %v", out, err) } @@ -157,7 +137,7 @@ func (s *DockerSuite) TestDaemonIptablesClean(c *check.C) { c.Fatalf("iptables output should have contained %q, but was %q", ipTablesSearchString, out) } - if err := d.Stop(); err != nil { + if err := s.d.Stop(); err != nil { c.Fatalf("Could not stop daemon: %v", err) } @@ -171,18 +151,14 @@ func (s *DockerSuite) TestDaemonIptablesClean(c *check.C) { if strings.Contains(out, ipTablesSearchString) { c.Fatalf("iptables output should not have contained %q, but was %q", ipTablesSearchString, out) } - } -func (s *DockerSuite) TestDaemonIptablesCreate(c *check.C) { - - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() - if out, err := d.Cmd("run", "-d", "--name", "top", "--restart=always", "-p", "80", "busybox:latest", "top"); err != nil { + if out, err := s.d.Cmd("run", "-d", "--name", "top", "--restart=always", "-p", "80", "busybox:latest", "top"); err != nil { c.Fatalf("Could not run top: %s, %v", out, err) } @@ -198,12 +174,12 @@ func (s *DockerSuite) TestDaemonIptablesCreate(c *check.C) { c.Fatalf("iptables output should have contained %q, but was %q", ipTablesSearchString, out) } - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatalf("Could not restart daemon: %v", err) } // make sure the container is not running - runningOut, err := d.Cmd("inspect", "--format='{{.State.Running}}'", "top") + runningOut, err := s.d.Cmd("inspect", "--format='{{.State.Running}}'", "top") if err != nil { c.Fatalf("Could not inspect on container: %s, %v", out, err) } @@ -221,69 +197,64 @@ func (s *DockerSuite) TestDaemonIptablesCreate(c *check.C) { if !strings.Contains(out, ipTablesSearchString) { c.Fatalf("iptables output after restart should have contained %q, but was %q", ipTablesSearchString, out) } - } -func (s *DockerSuite) TestDaemonLoggingLevel(c *check.C) { - d := NewDaemon(c) - - if err := d.Start("--log-level=bogus"); err == nil { - c.Fatal("Daemon should not have been able to start") - } +func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) { + c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level")) +} - d = NewDaemon(c) - if err := d.Start("--log-level=debug"); err != nil { +func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) { + if err := s.d.Start("--log-level=debug"); err != nil { c.Fatal(err) } - d.Stop() - content, _ := ioutil.ReadFile(d.logFile.Name()) + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if !strings.Contains(string(content), `level=debug`) { c.Fatalf(`Missing level="debug" in log file:\n%s`, string(content)) } +} - d = NewDaemon(c) - if err := d.Start("--log-level=fatal"); err != nil { +func (s *DockerDaemonSuite) TestDaemonLogLevelFatal(c *check.C) { + // we creating new daemons to create new logFile + if err := s.d.Start("--log-level=fatal"); err != nil { c.Fatal(err) } - d.Stop() - content, _ = ioutil.ReadFile(d.logFile.Name()) + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if strings.Contains(string(content), `level=debug`) { c.Fatalf(`Should not have level="debug" in log file:\n%s`, string(content)) } +} - d = NewDaemon(c) - if err := d.Start("-D"); err != nil { +func (s *DockerDaemonSuite) TestDaemonFlagD(c *check.C) { + if err := s.d.Start("-D"); err != nil { c.Fatal(err) } - d.Stop() - content, _ = ioutil.ReadFile(d.logFile.Name()) + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if !strings.Contains(string(content), `level=debug`) { c.Fatalf(`Missing level="debug" in log file using -D:\n%s`, string(content)) } +} - d = NewDaemon(c) - if err := d.Start("--debug"); err != nil { +func (s *DockerDaemonSuite) TestDaemonFlagDebug(c *check.C) { + if err := s.d.Start("--debug"); err != nil { c.Fatal(err) } - d.Stop() - content, _ = ioutil.ReadFile(d.logFile.Name()) + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if !strings.Contains(string(content), `level=debug`) { c.Fatalf(`Missing level="debug" in log file using --debug:\n%s`, string(content)) } +} - d = NewDaemon(c) - if err := d.Start("--debug", "--log-level=fatal"); err != nil { +func (s *DockerDaemonSuite) TestDaemonFlagDebugLogLevelFatal(c *check.C) { + if err := s.d.Start("--debug", "--log-level=fatal"); err != nil { c.Fatal(err) } - d.Stop() - content, _ = ioutil.ReadFile(d.logFile.Name()) + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if !strings.Contains(string(content), `level=debug`) { c.Fatalf(`Missing level="debug" in log file when using both --debug and --log-level=fatal:\n%s`, string(content)) } - } -func (s *DockerSuite) TestDaemonAllocatesListeningPort(c *check.C) { +func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) { listeningPorts := [][]string{ {"0.0.0.0", "0.0.0.0", "5678"}, {"127.0.0.1", "127.0.0.1", "1234"}, @@ -295,31 +266,25 @@ func (s *DockerSuite) TestDaemonAllocatesListeningPort(c *check.C) { cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2])) } - d := NewDaemon(c) - if err := d.StartWithBusybox(cmdArgs...); err != nil { + if err := s.d.StartWithBusybox(cmdArgs...); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() for _, hostDirective := range listeningPorts { - output, err := d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", hostDirective[1], hostDirective[2]), "busybox", "true") + output, err := s.d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", hostDirective[1], hostDirective[2]), "busybox", "true") if err == nil { c.Fatalf("Container should not start, expected port already allocated error: %q", output) } else if !strings.Contains(output, "port is already allocated") { c.Fatalf("Expected port is already allocated error: %q", output) } } - } // #9629 -func (s *DockerSuite) TestDaemonVolumesBindsRefs(c *check.C) { - d := NewDaemon(c) - - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonVolumesBindsRefs(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatal(err) } - defer d.Stop() tmp, err := ioutil.TempDir(os.TempDir(), "") if err != nil { @@ -331,28 +296,26 @@ func (s *DockerSuite) TestDaemonVolumesBindsRefs(c *check.C) { c.Fatal(err) } - if out, err := d.Cmd("create", "-v", tmp+":/foo", "--name=voltest", "busybox"); err != nil { + if out, err := s.d.Cmd("create", "-v", tmp+":/foo", "--name=voltest", "busybox"); err != nil { c.Fatal(err, out) } - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatal(err) } - if out, err := d.Cmd("run", "--volumes-from=voltest", "--name=consumer", "busybox", "/bin/sh", "-c", "[ -f /foo/test ]"); err != nil { + if out, err := s.d.Cmd("run", "--volumes-from=voltest", "--name=consumer", "busybox", "/bin/sh", "-c", "[ -f /foo/test ]"); err != nil { c.Fatal(err, out) } - } -func (s *DockerSuite) TestDaemonKeyGeneration(c *check.C) { +func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) { // TODO: skip or update for Windows daemon os.Remove("/etc/docker/key.json") - d := NewDaemon(c) - if err := d.Start(); err != nil { + if err := s.d.Start(); err != nil { c.Fatalf("Could not start daemon: %v", err) } - d.Stop() + s.d.Stop() k, err := libtrust.LoadKeyFile("/etc/docker/key.json") if err != nil { @@ -363,10 +326,9 @@ func (s *DockerSuite) TestDaemonKeyGeneration(c *check.C) { if len(kid) != 59 { c.Fatalf("Bad key ID: %s", kid) } - } -func (s *DockerSuite) TestDaemonKeyMigration(c *check.C) { +func (s *DockerDaemonSuite) TestDaemonKeyMigration(c *check.C) { // TODO: skip or update for Windows daemon os.Remove("/etc/docker/key.json") k1, err := libtrust.GenerateECP256PrivateKey() @@ -380,11 +342,10 @@ func (s *DockerSuite) TestDaemonKeyMigration(c *check.C) { c.Fatalf("Error saving private key: %s", err) } - d := NewDaemon(c) - if err := d.Start(); err != nil { + if err := s.d.Start(); err != nil { c.Fatalf("Could not start daemon: %v", err) } - d.Stop() + s.d.Stop() k2, err := libtrust.LoadKeyFile("/etc/docker/key.json") if err != nil { @@ -393,29 +354,25 @@ func (s *DockerSuite) TestDaemonKeyMigration(c *check.C) { if k1.KeyID() != k2.KeyID() { c.Fatalf("Key not migrated") } - } // Simulate an older daemon (pre 1.3) coming up with volumes specified in containers // without corresponding volume json -func (s *DockerSuite) TestDaemonUpgradeWithVolumes(c *check.C) { - d := NewDaemon(c) - +func (s *DockerDaemonSuite) TestDaemonUpgradeWithVolumes(c *check.C) { graphDir := filepath.Join(os.TempDir(), "docker-test") defer os.RemoveAll(graphDir) - if err := d.StartWithBusybox("-g", graphDir); err != nil { + if err := s.d.StartWithBusybox("-g", graphDir); err != nil { c.Fatal(err) } - defer d.Stop() tmpDir := filepath.Join(os.TempDir(), "test") defer os.RemoveAll(tmpDir) - if out, err := d.Cmd("create", "-v", tmpDir+":/foo", "--name=test", "busybox"); err != nil { + if out, err := s.d.Cmd("create", "-v", tmpDir+":/foo", "--name=test", "busybox"); err != nil { c.Fatal(err, out) } - if err := d.Stop(); err != nil { + if err := s.d.Stop(); err != nil { c.Fatal(err) } @@ -430,7 +387,7 @@ func (s *DockerSuite) TestDaemonUpgradeWithVolumes(c *check.C) { c.Fatal(err) } - if err := d.Start("-g", graphDir); err != nil { + if err := s.d.Start("-g", graphDir); err != nil { c.Fatal(err) } @@ -447,7 +404,7 @@ func (s *DockerSuite) TestDaemonUpgradeWithVolumes(c *check.C) { } // Now with just removing the volume config and not the volume data - if err := d.Stop(); err != nil { + if err := s.d.Stop(); err != nil { c.Fatal(err) } @@ -455,7 +412,7 @@ func (s *DockerSuite) TestDaemonUpgradeWithVolumes(c *check.C) { c.Fatal(err) } - if err := d.Start("-g", graphDir); err != nil { + if err := s.d.Start("-g", graphDir); err != nil { c.Fatal(err) } @@ -467,45 +424,37 @@ func (s *DockerSuite) TestDaemonUpgradeWithVolumes(c *check.C) { if len(dir) == 0 { c.Fatalf("expected volumes config dir to contain data for new volume") } - } // GH#11320 - verify that the daemon exits on failure properly // Note that this explicitly tests the conflict of {-b,--bridge} and {--bip} options as the means // to get a daemon init failure; no other tests for -b/--bip conflict are therefore required -func (s *DockerSuite) TestDaemonExitOnFailure(c *check.C) { - d := NewDaemon(c) - defer d.Stop() - +func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *check.C) { //attempt to start daemon with incorrect flags (we know -b and --bip conflict) - if err := d.Start("--bridge", "nosuchbridge", "--bip", "1.1.1.1"); err != nil { + if err := s.d.Start("--bridge", "nosuchbridge", "--bip", "1.1.1.1"); err != nil { //verify we got the right error if !strings.Contains(err.Error(), "Daemon exited and never started") { c.Fatalf("Expected daemon not to start, got %v", err) } // look in the log and make sure we got the message that daemon is shutting down - runCmd := exec.Command("grep", "Error starting daemon", d.LogfileName()) + runCmd := exec.Command("grep", "Error starting daemon", s.d.LogfileName()) if out, _, err := runCommandWithOutput(runCmd); err != nil { c.Fatalf("Expected 'Error starting daemon' message; but doesn't exist in log: %q, err: %v", out, err) } } else { //if we didn't get an error and the daemon is running, this is a failure - d.Stop() c.Fatal("Conflicting options should cause the daemon to error out with a failure") } - } -func (s *DockerSuite) TestDaemonUlimitDefaults(c *check.C) { +func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { testRequires(c, NativeExecDriver) - d := NewDaemon(c) - if err := d.StartWithBusybox("--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024"); err != nil { + if err := s.d.StartWithBusybox("--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024"); err != nil { c.Fatal(err) } - defer d.Stop() - out, err := d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -p)") + out, err := s.d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -p)") if err != nil { c.Fatal(out, err) } @@ -525,11 +474,11 @@ func (s *DockerSuite) TestDaemonUlimitDefaults(c *check.C) { } // Now restart daemon with a new default - if err := d.Restart("--default-ulimit", "nofile=43"); err != nil { + if err := s.d.Restart("--default-ulimit", "nofile=43"); err != nil { c.Fatal(err) } - out, err = d.Cmd("start", "-a", "test") + out, err = s.d.Cmd("start", "-a", "test") if err != nil { c.Fatal(err) } @@ -547,53 +496,46 @@ func (s *DockerSuite) TestDaemonUlimitDefaults(c *check.C) { if nproc != "2048" { c.Fatalf("exepcted `ulimit -p` to be 2048, got: %s", nproc) } - } // #11315 -func (s *DockerSuite) TestDaemonRestartRenameContainer(c *check.C) { - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatal(err) } - defer d.Stop() - if out, err := d.Cmd("run", "--name=test", "busybox"); err != nil { + if out, err := s.d.Cmd("run", "--name=test", "busybox"); err != nil { c.Fatal(err, out) } - if out, err := d.Cmd("rename", "test", "test2"); err != nil { + if out, err := s.d.Cmd("rename", "test", "test2"); err != nil { c.Fatal(err, out) } - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatal(err) } - if out, err := d.Cmd("start", "test2"); err != nil { + if out, err := s.d.Cmd("start", "test2"); err != nil { c.Fatal(err, out) } - } -func (s *DockerSuite) TestDaemonLoggingDriverDefault(c *check.C) { - d := NewDaemon(c) - - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatal(err) } - defer d.Stop() - out, err := d.Cmd("run", "-d", "busybox", "echo", "testline") + out, err := s.d.Cmd("run", "-d", "busybox", "echo", "testline") if err != nil { c.Fatal(out, err) } id := strings.TrimSpace(out) - if out, err := d.Cmd("wait", id); err != nil { + if out, err := s.d.Cmd("wait", id); err != nil { c.Fatal(out, err) } - logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log") + logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log") if _, err := os.Stat(logPath); err != nil { c.Fatal(err) @@ -621,72 +563,63 @@ func (s *DockerSuite) TestDaemonLoggingDriverDefault(c *check.C) { } } -func (s *DockerSuite) TestDaemonLoggingDriverDefaultOverride(c *check.C) { - d := NewDaemon(c) - - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatal(err) } - defer d.Stop() - out, err := d.Cmd("run", "-d", "--log-driver=none", "busybox", "echo", "testline") + out, err := s.d.Cmd("run", "-d", "--log-driver=none", "busybox", "echo", "testline") if err != nil { c.Fatal(out, err) } id := strings.TrimSpace(out) - if out, err := d.Cmd("wait", id); err != nil { + if out, err := s.d.Cmd("wait", id); err != nil { c.Fatal(out, err) } - logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log") + logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log") if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) { c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err) } } -func (s *DockerSuite) TestDaemonLoggingDriverNone(c *check.C) { - d := NewDaemon(c) - - if err := d.StartWithBusybox("--log-driver=none"); err != nil { +func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *check.C) { + if err := s.d.StartWithBusybox("--log-driver=none"); err != nil { c.Fatal(err) } - defer d.Stop() - out, err := d.Cmd("run", "-d", "busybox", "echo", "testline") + out, err := s.d.Cmd("run", "-d", "busybox", "echo", "testline") if err != nil { c.Fatal(out, err) } id := strings.TrimSpace(out) - if out, err := d.Cmd("wait", id); err != nil { + if out, err := s.d.Cmd("wait", id); err != nil { c.Fatal(out, err) } - logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log") + logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log") if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) { c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err) } } -func (s *DockerSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) { - d := NewDaemon(c) - - if err := d.StartWithBusybox("--log-driver=none"); err != nil { +func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) { + if err := s.d.StartWithBusybox("--log-driver=none"); err != nil { c.Fatal(err) } - defer d.Stop() - out, err := d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "echo", "testline") + out, err := s.d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "echo", "testline") if err != nil { c.Fatal(out, err) } id := strings.TrimSpace(out) - if out, err := d.Cmd("wait", id); err != nil { + if out, err := s.d.Cmd("wait", id); err != nil { c.Fatal(out, err) } - logPath := filepath.Join(d.folder, "graph", "containers", id, id+"-json.log") + logPath := filepath.Join(s.d.folder, "graph", "containers", id, id+"-json.log") if _, err := os.Stat(logPath); err != nil { c.Fatal(err) @@ -714,20 +647,17 @@ func (s *DockerSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) { } } -func (s *DockerSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) { - d := NewDaemon(c) - - if err := d.StartWithBusybox("--log-driver=none"); err != nil { +func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) { + if err := s.d.StartWithBusybox("--log-driver=none"); err != nil { c.Fatal(err) } - defer d.Stop() - out, err := d.Cmd("run", "-d", "busybox", "echo", "testline") + out, err := s.d.Cmd("run", "-d", "busybox", "echo", "testline") if err != nil { c.Fatal(out, err) } id := strings.TrimSpace(out) - out, err = d.Cmd("logs", id) + out, err = s.d.Cmd("logs", id) if err == nil { c.Fatalf("Logs should fail with \"none\" driver") } @@ -736,54 +666,50 @@ func (s *DockerSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) { } } -func (s *DockerSuite) TestDaemonDots(c *check.C) { - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonDots(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatal(err) } - defer d.Stop() // Now create 4 containers - if _, err := d.Cmd("create", "busybox"); err != nil { + if _, err := s.d.Cmd("create", "busybox"); err != nil { c.Fatalf("Error creating container: %q", err) } - if _, err := d.Cmd("create", "busybox"); err != nil { + if _, err := s.d.Cmd("create", "busybox"); err != nil { c.Fatalf("Error creating container: %q", err) } - if _, err := d.Cmd("create", "busybox"); err != nil { + if _, err := s.d.Cmd("create", "busybox"); err != nil { c.Fatalf("Error creating container: %q", err) } - if _, err := d.Cmd("create", "busybox"); err != nil { + if _, err := s.d.Cmd("create", "busybox"); err != nil { c.Fatalf("Error creating container: %q", err) } - d.Stop() + s.d.Stop() - d.Start("--log-level=debug") - d.Stop() - content, _ := ioutil.ReadFile(d.logFile.Name()) + s.d.Start("--log-level=debug") + s.d.Stop() + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if strings.Contains(string(content), "....") { c.Fatalf("Debug level should not have ....\n%s", string(content)) } - d.Start("--log-level=error") - d.Stop() - content, _ = ioutil.ReadFile(d.logFile.Name()) + s.d.Start("--log-level=error") + s.d.Stop() + content, _ = ioutil.ReadFile(s.d.logFile.Name()) if strings.Contains(string(content), "....") { c.Fatalf("Error level should not have ....\n%s", string(content)) } - d.Start("--log-level=info") - d.Stop() - content, _ = ioutil.ReadFile(d.logFile.Name()) + s.d.Start("--log-level=info") + s.d.Stop() + content, _ = ioutil.ReadFile(s.d.logFile.Name()) if !strings.Contains(string(content), "....") { c.Fatalf("Info level should have ....\n%s", string(content)) } - } -func (s *DockerSuite) TestDaemonUnixSockCleanedUp(c *check.C) { - d := NewDaemon(c) +func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) { dir, err := ioutil.TempDir("", "socket-cleanup-test") if err != nil { c.Fatal(err) @@ -791,26 +717,24 @@ func (s *DockerSuite) TestDaemonUnixSockCleanedUp(c *check.C) { defer os.RemoveAll(dir) sockPath := filepath.Join(dir, "docker.sock") - if err := d.Start("--host", "unix://"+sockPath); err != nil { + if err := s.d.Start("--host", "unix://"+sockPath); err != nil { c.Fatal(err) } - defer d.Stop() if _, err := os.Stat(sockPath); err != nil { c.Fatal("socket does not exist") } - if err := d.Stop(); err != nil { + if err := s.d.Stop(); err != nil { c.Fatal(err) } if _, err := os.Stat(sockPath); err == nil || !os.IsNotExist(err) { c.Fatal("unix socket is not cleaned up") } - } -func (s *DockerSuite) TestDaemonwithwrongkey(c *check.C) { +func (s *DockerDaemonSuite) TestDaemonwithwrongkey(c *check.C) { type Config struct { Crv string `json:"crv"` D string `json:"d"` @@ -821,12 +745,11 @@ func (s *DockerSuite) TestDaemonwithwrongkey(c *check.C) { } os.Remove("/etc/docker/key.json") - d := NewDaemon(c) - if err := d.Start(); err != nil { + if err := s.d.Start(); err != nil { c.Fatalf("Failed to start daemon: %v", err) } - if err := d.Stop(); err != nil { + if err := s.d.Stop(); err != nil { c.Fatalf("Could not stop daemon: %v", err) } @@ -855,46 +778,41 @@ func (s *DockerSuite) TestDaemonwithwrongkey(c *check.C) { c.Fatalf("Error ioutil.WriteFile: %s", err) } - d1 := NewDaemon(c) defer os.Remove("/etc/docker/key.json") - if err := d1.Start(); err == nil { - d1.Stop() - c.Fatalf("It should not be succssful to start daemon with wrong key: %v", err) + if err := s.d.Start(); err == nil { + c.Fatalf("It should not be successful to start daemon with wrong key: %v", err) } - content, _ := ioutil.ReadFile(d1.logFile.Name()) + content, _ := ioutil.ReadFile(s.d.logFile.Name()) if !strings.Contains(string(content), "Public Key ID does not match") { c.Fatal("Missing KeyID message from daemon logs") } - } -func (s *DockerSuite) TestDaemonRestartKillWait(c *check.C) { - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { +func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) { + if err := s.d.StartWithBusybox(); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() - out, err := d.Cmd("run", "-id", "busybox", "/bin/cat") + out, err := s.d.Cmd("run", "-id", "busybox", "/bin/cat") if err != nil { c.Fatalf("Could not run /bin/cat: err=%v\n%s", err, out) } containerID := strings.TrimSpace(out) - if out, err := d.Cmd("kill", containerID); err != nil { + if out, err := s.d.Cmd("kill", containerID); err != nil { c.Fatalf("Could not kill %s: err=%v\n%s", containerID, err, out) } - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatalf("Could not restart daemon: %v", err) } errchan := make(chan error) go func() { - if out, err := d.Cmd("wait", containerID); err != nil { + if out, err := s.d.Cmd("wait", containerID); err != nil { errchan <- fmt.Errorf("%v:\n%s", err, out) } close(errchan) @@ -908,26 +826,23 @@ func (s *DockerSuite) TestDaemonRestartKillWait(c *check.C) { c.Fatal(err) } } - } // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint -func (s *DockerSuite) TestHttpsInfo(c *check.C) { +func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) { const ( testDaemonHttpsAddr = "localhost:4271" ) - d := NewDaemon(c) - if err := d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", + if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", "--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHttpsAddr); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() //force tcp protocol host := fmt.Sprintf("tcp://%s", testDaemonHttpsAddr) daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"} - out, err := d.CmdWithArgs(daemonArgs, "info") + out, err := s.d.CmdWithArgs(daemonArgs, "info") if err != nil { c.Fatalf("Error Occurred: %s and output: %s", err, out) } @@ -935,22 +850,20 @@ func (s *DockerSuite) TestHttpsInfo(c *check.C) { // TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint // by using a rogue client certificate and checks that it fails with the expected error. -func (s *DockerSuite) TestHttpsInfoRogueCert(c *check.C) { +func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) { const ( errBadCertificate = "remote error: bad certificate" testDaemonHttpsAddr = "localhost:4271" ) - d := NewDaemon(c) - if err := d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", + if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", "--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHttpsAddr); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() //force tcp protocol host := fmt.Sprintf("tcp://%s", testDaemonHttpsAddr) daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"} - out, err := d.CmdWithArgs(daemonArgs, "info") + out, err := s.d.CmdWithArgs(daemonArgs, "info") if err == nil || !strings.Contains(out, errBadCertificate) { c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out) } @@ -958,22 +871,20 @@ func (s *DockerSuite) TestHttpsInfoRogueCert(c *check.C) { // TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint // which provides a rogue server certificate and checks that it fails with the expected error -func (s *DockerSuite) TestHttpsInfoRogueServerCert(c *check.C) { +func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) { const ( errCaUnknown = "x509: certificate signed by unknown authority" testDaemonRogueHttpsAddr = "localhost:4272" ) - d := NewDaemon(c) - if err := d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem", + if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem", "--tlskey", "fixtures/https/server-rogue-key.pem", "-H", testDaemonRogueHttpsAddr); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() //force tcp protocol host := fmt.Sprintf("tcp://%s", testDaemonRogueHttpsAddr) daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"} - out, err := d.CmdWithArgs(daemonArgs, "info") + out, err := s.d.CmdWithArgs(daemonArgs, "info") if err == nil || !strings.Contains(out, errCaUnknown) { c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out) } diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go index 35a9c0a21354646ca6c72a5ff64967499cfbef8b..80cc0c69d54b8280961acaacd5709e668e2b1e85 100644 --- a/integration-cli/docker_cli_events_test.go +++ b/integration-cli/docker_cli_events_test.go @@ -7,6 +7,7 @@ import ( "regexp" "strconv" "strings" + "sync" "time" "github.com/go-check/check" @@ -65,9 +66,28 @@ func (s *DockerSuite) TestEventsContainerFailStartDie(c *check.C) { } func (s *DockerSuite) TestEventsLimit(c *check.C) { - for i := 0; i < 30; i++ { - dockerCmd(c, "run", "busybox", "echo", strconv.Itoa(i)) + + var waitGroup sync.WaitGroup + errChan := make(chan error, 17) + + args := []string{"run", "--rm", "busybox", "true"} + for i := 0; i < 17; i++ { + waitGroup.Add(1) + go func() { + defer waitGroup.Done() + errChan <- exec.Command(dockerBinary, args...).Run() + }() + } + + waitGroup.Wait() + close(errChan) + + for err := range errChan { + if err != nil { + c.Fatalf("%q failed with error: %v", strings.Join(args, " "), err) + } } + eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix())) out, _, _ := runCommandWithOutput(eventsCmd) events := strings.Split(out, "\n") @@ -144,7 +164,6 @@ func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) { func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) { name := "testimageevents" - defer deleteImages(name) _, err := buildImage(name, `FROM scratch MAINTAINER "docker"`, @@ -180,8 +199,6 @@ func (s *DockerSuite) TestEventsImagePull(c *check.C) { since := daemonTime(c).Unix() testRequires(c, Network) - defer deleteImages("hello-world") - pullCmd := exec.Command(dockerBinary, "pull", "hello-world") if out, _, err := runCommandWithOutput(pullCmd); err != nil { c.Fatalf("pulling the hello-world image from has failed: %s, %v", out, err) @@ -211,8 +228,7 @@ func (s *DockerSuite) TestEventsImageImport(c *check.C) { if err != nil { c.Fatal(err) } - err = eventsCmd.Start() - if err != nil { + if err := eventsCmd.Start(); err != nil { c.Fatal(err) } defer eventsCmd.Process.Kill() @@ -343,12 +359,15 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) { nameID := make(map[string]string) for _, name := range []string{"container_1", "container_2"} { - out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", name, "busybox", "true")) + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", name, "busybox", "true")) + if err != nil { + c.Fatalf("Error: %v, Output: %s", err, out) + } + id, err := inspectField(name, "Id") if err != nil { c.Fatal(err) } - nameID[name] = strings.TrimSpace(out) - waitInspect(name, "{{.State.Runing }}", "false", 5) + nameID[name] = id } until := fmt.Sprintf("%d", daemonTime(c).Unix()) @@ -403,30 +422,23 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) { func (s *DockerSuite) TestEventsStreaming(c *check.C) { start := daemonTime(c).Unix() - finish := make(chan struct{}) - defer close(finish) id := make(chan string) eventCreate := make(chan struct{}) eventStart := make(chan struct{}) eventDie := make(chan struct{}) eventDestroy := make(chan struct{}) - go func() { - eventsCmd := exec.Command(dockerBinary, "events", "--since", strconv.FormatInt(start, 10)) - stdout, err := eventsCmd.StdoutPipe() - if err != nil { - c.Fatal(err) - } - err = eventsCmd.Start() - if err != nil { - c.Fatalf("failed to start 'docker events': %s", err) - } - - go func() { - <-finish - eventsCmd.Process.Kill() - }() + eventsCmd := exec.Command(dockerBinary, "events", "--since", strconv.FormatInt(start, 10)) + stdout, err := eventsCmd.StdoutPipe() + if err != nil { + c.Fatal(err) + } + if err := eventsCmd.Start(); err != nil { + c.Fatalf("failed to start 'docker events': %s", err) + } + defer eventsCmd.Process.Kill() + go func() { containerID := <-id matchCreate := regexp.MustCompile(containerID + `: \(from busybox:latest\) create$`) @@ -447,11 +459,6 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) { close(eventDestroy) } } - - err = eventsCmd.Wait() - if err != nil && !IsKilled(err) { - c.Fatalf("docker events had bad exit status: %s", err) - } }() runCmd := exec.Command(dockerBinary, "run", "-d", "busybox:latest", "true") @@ -495,5 +502,4 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) { case <-eventDestroy: // ignore, done } - } diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go index c6910d43215709ba83c76d08e35d0680ed847841..4b36d7b5321420e4005d1cafe2e363ff1e8f24b3 100644 --- a/integration-cli/docker_cli_exec_test.go +++ b/integration-cli/docker_cli_exec_test.go @@ -74,15 +74,14 @@ func (s *DockerSuite) TestExecInteractive(c *check.C) { if err := stdin.Close(); err != nil { c.Fatal(err) } - finish := make(chan struct{}) + errChan := make(chan error) go func() { - if err := execCmd.Wait(); err != nil { - c.Fatal(err) - } - close(finish) + errChan <- execCmd.Wait() + close(errChan) }() select { - case <-finish: + case err := <-errChan: + c.Assert(err, check.IsNil) case <-time.After(1 * time.Second): c.Fatal("docker exec failed to exit on stdin close") } @@ -117,28 +116,26 @@ func (s *DockerSuite) TestExecAfterContainerRestart(c *check.C) { } -func (s *DockerSuite) TestExecAfterDaemonRestart(c *check.C) { +func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *check.C) { testRequires(c, SameHostDaemon) - d := NewDaemon(c) - if err := d.StartWithBusybox(); err != nil { + if err := s.d.StartWithBusybox(); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - defer d.Stop() - if out, err := d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil { + if out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil { c.Fatalf("Could not run top: err=%v\n%s", err, out) } - if err := d.Restart(); err != nil { + if err := s.d.Restart(); err != nil { c.Fatalf("Could not restart daemon: %v", err) } - if out, err := d.Cmd("start", "top"); err != nil { + if out, err := s.d.Cmd("start", "top"); err != nil { c.Fatalf("Could not start top after daemon restart: err=%v\n%s", err, out) } - out, err := d.Cmd("exec", "top", "echo", "hello") + out, err := s.d.Cmd("exec", "top", "echo", "hello") if err != nil { c.Fatalf("Could not exec on container top: err=%v\n%s", err, out) } @@ -147,7 +144,6 @@ func (s *DockerSuite) TestExecAfterDaemonRestart(c *check.C) { if outStr != "hello" { c.Errorf("container should've printed hello, instead printed %q", outStr) } - } // Regression test for #9155, #9044 @@ -281,25 +277,29 @@ func (s *DockerSuite) TestExecTtyWithoutStdin(c *check.C) { } }() - done := make(chan struct{}) + errChan := make(chan error) go func() { - defer close(done) + defer close(errChan) cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true") if _, err := cmd.StdinPipe(); err != nil { - c.Fatal(err) + errChan <- err + return } expected := "cannot enable tty mode" if out, _, err := runCommandWithOutput(cmd); err == nil { - c.Fatal("exec should have failed") + errChan <- fmt.Errorf("exec should have failed") + return } else if !strings.Contains(out, expected) { - c.Fatalf("exec failed with error %q: expected %q", out, expected) + errChan <- fmt.Errorf("exec failed with error %q: expected %q", out, expected) + return } }() select { - case <-done: + case err := <-errChan: + c.Assert(err, check.IsNil) case <-time.After(3 * time.Second): c.Fatal("exec is running but should have failed") } @@ -329,17 +329,22 @@ func (s *DockerSuite) TestExecStopNotHanging(c *check.C) { c.Fatal(err) } - wait := make(chan struct{}) + type dstop struct { + out []byte + err error + } + + ch := make(chan dstop) go func() { - if out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput(); err != nil { - c.Fatal(out, err) - } - close(wait) + out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput() + ch <- dstop{out, err} + close(ch) }() select { case <-time.After(3 * time.Second): c.Fatal("Container stop timed out") - case <-wait: + case s := <-ch: + c.Assert(s.err, check.IsNil) } } @@ -362,6 +367,7 @@ func (s *DockerSuite) TestExecCgroup(c *check.C) { var wg sync.WaitGroup var mu sync.Mutex execCgroups := []sort.StringSlice{} + errChan := make(chan error) // exec a few times concurrently to get consistent failure for i := 0; i < 5; i++ { wg.Add(1) @@ -369,7 +375,8 @@ func (s *DockerSuite) TestExecCgroup(c *check.C) { cmd := exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/self/cgroup") out, _, err := runCommandWithOutput(cmd) if err != nil { - c.Fatal(out, err) + errChan <- err + return } cg := sort.StringSlice(strings.Split(string(out), "\n")) @@ -380,6 +387,11 @@ func (s *DockerSuite) TestExecCgroup(c *check.C) { }() } wg.Wait() + close(errChan) + + for err := range errChan { + c.Assert(err, check.IsNil) + } for _, cg := range execCgroups { if !reflect.DeepEqual(cg, containerCgroups) { diff --git a/integration-cli/docker_cli_export_import_test.go b/integration-cli/docker_cli_export_import_test.go index bc7b16356d94d38f4f70d32353af0fbfc039d26e..3370a96761cab7b64966b1ec94ab695c0ce29804 100644 --- a/integration-cli/docker_cli_export_import_test.go +++ b/integration-cli/docker_cli_export_import_test.go @@ -12,8 +12,6 @@ import ( func (s *DockerSuite) TestExportContainerAndImportImage(c *check.C) { containerID := "testexportcontainerandimportimage" - defer deleteImages("repo/testexp:v1") - runCmd := exec.Command(dockerBinary, "run", "-d", "--name", containerID, "busybox", "true") out, _, err := runCommandWithOutput(runCmd) if err != nil { @@ -51,8 +49,6 @@ func (s *DockerSuite) TestExportContainerAndImportImage(c *check.C) { func (s *DockerSuite) TestExportContainerWithOutputAndImportImage(c *check.C) { containerID := "testexportcontainerwithoutputandimportimage" - defer deleteImages("repo/testexp:v1") - runCmd := exec.Command(dockerBinary, "run", "-d", "--name", containerID, "busybox", "true") out, _, err := runCommandWithOutput(runCmd) if err != nil { diff --git a/integration-cli/docker_cli_history_test.go b/integration-cli/docker_cli_history_test.go index 8de400831487762d6cddba276f47af2080baadc1..d229f1a8c58f859e5744e37b7fd0ef413f399eb4 100644 --- a/integration-cli/docker_cli_history_test.go +++ b/integration-cli/docker_cli_history_test.go @@ -14,7 +14,6 @@ import ( // sort is not predictable it doesn't always fail. func (s *DockerSuite) TestBuildHistory(c *check.C) { name := "testbuildhistory" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN echo "A" RUN echo "B" @@ -85,7 +84,6 @@ func (s *DockerSuite) TestHistoryNonExistentImage(c *check.C) { func (s *DockerSuite) TestHistoryImageWithComment(c *check.C) { name := "testhistoryimagewithcomment" - defer deleteImages(name) // make a image through docker commit [ -m messages ] //runCmd := exec.Command(dockerBinary, "run", "-i", "-a", "stdin", "busybox", "echo", "foo") diff --git a/integration-cli/docker_cli_images_test.go b/integration-cli/docker_cli_images_test.go index cf219e88b85a9fd48103f179eaeff33fa3cdee49..0ab6462509340ffda69d8ba6a9fb4884fc15d07e 100644 --- a/integration-cli/docker_cli_images_test.go +++ b/integration-cli/docker_cli_images_test.go @@ -26,9 +26,6 @@ func (s *DockerSuite) TestImagesEnsureImageIsListed(c *check.C) { } func (s *DockerSuite) TestImagesOrderedByCreationDate(c *check.C) { - defer deleteImages("order:test_a") - defer deleteImages("order:test_c") - defer deleteImages("order:test_b") id1, err := buildImage("order:test_a", `FROM scratch MAINTAINER dockerio1`, true) @@ -80,9 +77,6 @@ func (s *DockerSuite) TestImagesFilterLabel(c *check.C) { imageName1 := "images_filter_test1" imageName2 := "images_filter_test2" imageName3 := "images_filter_test3" - defer deleteImages(imageName1) - defer deleteImages(imageName2) - defer deleteImages(imageName3) image1ID, err := buildImage(imageName1, `FROM scratch LABEL match me`, true) @@ -130,7 +124,6 @@ func (s *DockerSuite) TestImagesFilterLabel(c *check.C) { func (s *DockerSuite) TestImagesFilterSpaceTrimCase(c *check.C) { imageName := "images_filter_test" - defer deleteImages(imageName) buildImage(imageName, `FROM scratch RUN touch /test/foo @@ -189,7 +182,6 @@ func (s *DockerSuite) TestImagesEnsureDanglingImageOnlyListedOnce(c *check.C) { c.Fatalf("error tagging foobox: %s", err) } imageId := stringid.TruncateID(strings.TrimSpace(out)) - defer deleteImages(imageId) // overwrite the tag, making the previous image dangling cmd = exec.Command(dockerBinary, "tag", "-f", "busybox", "foobox") @@ -197,7 +189,6 @@ func (s *DockerSuite) TestImagesEnsureDanglingImageOnlyListedOnce(c *check.C) { if err != nil { c.Fatalf("error tagging foobox: %s", err) } - defer deleteImages("foobox") cmd = exec.Command(dockerBinary, "images", "-q", "-f", "dangling=true") out, _, err = runCommandWithOutput(cmd) diff --git a/integration-cli/docker_cli_import_test.go b/integration-cli/docker_cli_import_test.go index 02b857bfd958e91c00c1a316fe8c6b9fb1e1e942..201dbaa580e7fbeb0929f79252209f382d24d80b 100644 --- a/integration-cli/docker_cli_import_test.go +++ b/integration-cli/docker_cli_import_test.go @@ -27,7 +27,6 @@ func (s *DockerSuite) TestImportDisplay(c *check.C) { c.Fatalf("display is messed up: %d '\\n' instead of 1:\n%s", n, out) } image := strings.TrimSpace(out) - defer deleteImages(image) runCmd = exec.Command(dockerBinary, "run", "--rm", image, "true") out, _, err = runCommandWithOutput(runCmd) diff --git a/integration-cli/docker_cli_links_test.go b/integration-cli/docker_cli_links_test.go index 95b340be2dc4f30b0d9cb51ab122ae32d8edd54c..6bb173c10cc2000fd65b97de9dff9ba9ef21d0ca 100644 --- a/integration-cli/docker_cli_links_test.go +++ b/integration-cli/docker_cli_links_test.go @@ -310,3 +310,24 @@ func (s *DockerSuite) TestLinksUpdateOnRestart(c *check.C) { c.Fatalf("For 'onetwo' alias expected IP: %s, got: %s", realIP, ip) } } + +func (s *DockerSuite) TestLinksEnvs(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "-d", "-e", "e1=", "-e", "e2=v2", "-e", "e3=v3=v3", "--name=first", "busybox", "top") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatalf("Run of first failed: %s\n%s", out, err) + } + + runCmd = exec.Command(dockerBinary, "run", "--name=second", "--link=first:first", "busybox", "env") + + out, stde, rc, err := runCommandWithStdoutStderr(runCmd) + if err != nil || rc != 0 { + c.Fatalf("run of 2nd failed: rc: %d, out: %s\n err: %s", rc, out, stde) + } + + if !strings.Contains(out, "FIRST_ENV_e1=\n") || + !strings.Contains(out, "FIRST_ENV_e2=v2") || + !strings.Contains(out, "FIRST_ENV_e3=v3=v3") { + c.Fatalf("Incorrect output: %s", out) + } +} diff --git a/integration-cli/docker_cli_logs_test.go b/integration-cli/docker_cli_logs_test.go index a7c8916226ca63f33ff767761315fc317eedfe5f..0a3e1af981841edc702245a7521bcdd410cd2d17 100644 --- a/integration-cli/docker_cli_logs_test.go +++ b/integration-cli/docker_cli_logs_test.go @@ -260,16 +260,15 @@ func (s *DockerSuite) TestLogsFollowStopped(c *check.C) { c.Fatal(err) } - ch := make(chan struct{}) + errChan := make(chan error) go func() { - if err := logsCmd.Wait(); err != nil { - c.Fatal(err) - } - close(ch) + errChan <- logsCmd.Wait() + close(errChan) }() select { - case <-ch: + case err := <-errChan: + c.Assert(err, check.IsNil) case <-time.After(1 * time.Second): c.Fatal("Following logs is hanged") } @@ -298,9 +297,7 @@ func (s *DockerSuite) TestLogsFollowSlowStdoutConsumer(c *check.C) { logCmd := exec.Command(dockerBinary, "logs", "-f", cleanedContainerID) stdout, err := logCmd.StdoutPipe() - if err != nil { - c.Fatal(err) - } + c.Assert(err, check.IsNil) if err := logCmd.Start(); err != nil { c.Fatal(err) @@ -308,15 +305,11 @@ func (s *DockerSuite) TestLogsFollowSlowStdoutConsumer(c *check.C) { // First read slowly bytes1, err := consumeWithSpeed(stdout, 10, 50*time.Millisecond, stopSlowRead) - if err != nil { - c.Fatal(err) - } + c.Assert(err, check.IsNil) // After the container has finished we can continue reading fast bytes2, err := consumeWithSpeed(stdout, 32*1024, 0, nil) - if err != nil { - c.Fatal(err) - } + c.Assert(err, check.IsNil) actual := bytes1 + bytes2 expected := 200000 diff --git a/integration-cli/docker_cli_proxy_test.go b/integration-cli/docker_cli_proxy_test.go index c07ed64068517aeb59d7b2760792909a6dcc9d50..8b55c67d814456aa2ee8672f8d0b195f84b2dac6 100644 --- a/integration-cli/docker_cli_proxy_test.go +++ b/integration-cli/docker_cli_proxy_test.go @@ -22,7 +22,7 @@ func (s *DockerSuite) TestCliProxyDisableProxyUnixSock(c *check.C) { // Can't use localhost here since go has a special case to not use proxy if connecting to localhost // See https://golang.org/pkg/net/http/#ProxyFromEnvironment -func (s *DockerSuite) TestCliProxyProxyTCPSock(c *check.C) { +func (s *DockerDaemonSuite) TestCliProxyProxyTCPSock(c *check.C) { testRequires(c, SameHostDaemon) // get the IP to use to connect since we can't use localhost addrs, err := net.InterfaceAddrs() @@ -43,8 +43,7 @@ func (s *DockerSuite) TestCliProxyProxyTCPSock(c *check.C) { c.Fatal("could not find ip to connect to") } - d := NewDaemon(c) - if err := d.Start("-H", "tcp://"+ip+":2375"); err != nil { + if err := s.d.Start("-H", "tcp://"+ip+":2375"); err != nil { c.Fatal(err) } diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index ca2d67bc96c2bbc3ed8600307dc1eae631da44b2..bb34575fbaf483908f4ac190c559bdbb770a1ed8 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -255,7 +255,6 @@ func assertContainerList(out string, expected []string) bool { } func (s *DockerSuite) TestPsListContainersSize(c *check.C) { - cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "echo", "hello") runCommandWithOutput(cmd) cmd = exec.Command(dockerBinary, "ps", "-s", "-n=1") @@ -547,7 +546,7 @@ func (s *DockerSuite) TestPsListContainersFilterExited(c *check.C) { } ids = strings.Split(strings.TrimSpace(out), "\n") if len(ids) != 2 { - c.Fatalf("Should be 2 zero exited containerst got %d", len(ids)) + c.Fatalf("Should be 2 zero exited containers got %d", len(ids)) } if ids[0] != secondNonZero { c.Fatalf("First in list should be %q, got %q", secondNonZero, ids[0]) @@ -560,7 +559,6 @@ func (s *DockerSuite) TestPsListContainersFilterExited(c *check.C) { func (s *DockerSuite) TestPsRightTagName(c *check.C) { tag := "asybox:shmatest" - defer deleteImages(tag) if out, err := exec.Command(dockerBinary, "tag", "busybox", tag).CombinedOutput(); err != nil { c.Fatalf("Failed to tag image: %s, out: %q", err, out) } diff --git a/integration-cli/docker_cli_pull_test.go b/integration-cli/docker_cli_pull_test.go index 9dc9920cac7456fde16cc6fbad1483932a7a25f1..a3ded8f038da80ccbd45da34fc06b71eeb0168d3 100644 --- a/integration-cli/docker_cli_pull_test.go +++ b/integration-cli/docker_cli_pull_test.go @@ -9,11 +9,8 @@ import ( ) // See issue docker/docker#8141 -func (s *DockerSuite) TestPullImageWithAliases(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPullImageWithAliases(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) - defer deleteImages(repoName) repos := []string{} for _, tag := range []string{"recent", "fresh"} { @@ -25,7 +22,6 @@ func (s *DockerSuite) TestPullImageWithAliases(c *check.C) { if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "tag", "busybox", repo)); err != nil { c.Fatalf("Failed to tag image %v: error %v, output %q", repos, err, out) } - defer deleteImages(repo) if out, err := exec.Command(dockerBinary, "push", repo).CombinedOutput(); err != nil { c.Fatalf("Failed to push image %v: error %v, output %q", repo, err, string(out)) } @@ -50,7 +46,6 @@ func (s *DockerSuite) TestPullImageWithAliases(c *check.C) { c.Fatalf("Image %v shouldn't have been pulled down", repo) } } - } // pulling library/hello-world should show verified message @@ -61,7 +56,6 @@ func (s *DockerSuite) TestPullVerified(c *check.C) { // unless keychain is manually updated to contain the daemon's sign key. verifiedName := "hello-world" - defer deleteImages(verifiedName) // pull it expected := "The image you are pulling has been verified" @@ -88,8 +82,6 @@ func (s *DockerSuite) TestPullVerified(c *check.C) { func (s *DockerSuite) TestPullImageFromCentralRegistry(c *check.C) { testRequires(c, Network) - defer deleteImages("hello-world") - pullCmd := exec.Command(dockerBinary, "pull", "hello-world") if out, _, err := runCommandWithOutput(pullCmd); err != nil { c.Fatalf("pulling the hello-world image from the registry has failed: %s, %v", out, err) diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go index 3fc1ae3a0c8e6446343114dd79bac144c22ddecd..69a05ed821c78193d0f2649e1d0ccad7d17192a7 100644 --- a/integration-cli/docker_cli_push_test.go +++ b/integration-cli/docker_cli_push_test.go @@ -13,16 +13,13 @@ import ( ) // pulling an image from the central registry should work -func (s *DockerSuite) TestPushBusyboxImage(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) // tag the image to upload it to the private registry tagCmd := exec.Command(dockerBinary, "tag", "busybox", repoName) if out, _, err := runCommandWithOutput(tagCmd); err != nil { c.Fatalf("image tagging failed: %s, %v", out, err) } - defer deleteImages(repoName) pushCmd := exec.Command(dockerBinary, "push", repoName) if out, _, err := runCommandWithOutput(pushCmd); err != nil { @@ -38,37 +35,31 @@ func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) { } } -func (s *DockerSuite) TestPushUntagged(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) expected := "Repository does not exist" pushCmd := exec.Command(dockerBinary, "push", repoName) if out, _, err := runCommandWithOutput(pushCmd); err == nil { - c.Fatalf("pushing the image to the private registry should have failed: outuput %q", out) + c.Fatalf("pushing the image to the private registry should have failed: output %q", out) } else if !strings.Contains(out, expected) { c.Fatalf("pushing the image failed with an unexpected message: expected %q, got %q", expected, out) } } -func (s *DockerSuite) TestPushBadTag(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL) expected := "does not exist" pushCmd := exec.Command(dockerBinary, "push", repoName) if out, _, err := runCommandWithOutput(pushCmd); err == nil { - c.Fatalf("pushing the image to the private registry should have failed: outuput %q", out) + c.Fatalf("pushing the image to the private registry should have failed: output %q", out) } else if !strings.Contains(out, expected) { c.Fatalf("pushing the image failed with an unexpected message: expected %q, got %q", expected, out) } } -func (s *DockerSuite) TestPushMultipleTags(c *check.C) { - defer setupRegistry(c)() - +func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL) repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL) @@ -77,12 +68,10 @@ func (s *DockerSuite) TestPushMultipleTags(c *check.C) { if out, _, err := runCommandWithOutput(tagCmd1); err != nil { c.Fatalf("image tagging failed: %s, %v", out, err) } - defer deleteImages(repoTag1) tagCmd2 := exec.Command(dockerBinary, "tag", "busybox", repoTag2) if out, _, err := runCommandWithOutput(tagCmd2); err != nil { c.Fatalf("image tagging failed: %s, %v", out, err) } - defer deleteImages(repoTag2) pushCmd := exec.Command(dockerBinary, "push", repoName) if out, _, err := runCommandWithOutput(pushCmd); err != nil { @@ -90,14 +79,12 @@ func (s *DockerSuite) TestPushMultipleTags(c *check.C) { } } -func (s *DockerSuite) TestPushInterrupt(c *check.C) { - defer setupRegistry(c)() +func (s *DockerRegistrySuite) TestPushInterrupt(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) // tag the image and upload it to the private registry if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "tag", "busybox", repoName)); err != nil { c.Fatalf("image tagging failed: %s, %v", out, err) } - defer deleteImages(repoName) pushCmd := exec.Command(dockerBinary, "push", repoName) if err := pushCmd.Start(); err != nil { @@ -122,8 +109,7 @@ func (s *DockerSuite) TestPushInterrupt(c *check.C) { } } -func (s *DockerSuite) TestPushEmptyLayer(c *check.C) { - defer setupRegistry(c)() +func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL) emptyTarball, err := ioutil.TempFile("", "empty_tarball") if err != nil { diff --git a/integration-cli/docker_cli_rm_test.go b/integration-cli/docker_cli_rm_test.go index c330bb76ab053b87489617dc37198539e4cb6138..b8d1b843d1502fd80ae3a0a8eef20bf9546e5afd 100644 --- a/integration-cli/docker_cli_rm_test.go +++ b/integration-cli/docker_cli_rm_test.go @@ -87,7 +87,6 @@ func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) { // build first dockerfile img1, err := buildImage(img, dockerfile1, true) - defer deleteImages(img1) if err != nil { c.Fatalf("Could not build image %s: %v", img, err) } diff --git a/integration-cli/docker_cli_rmi_test.go b/integration-cli/docker_cli_rmi_test.go index 234fa22f0a4132a58a62c9366f0c872b9c3f8823..9dc2ee297a8d715a3a779bb46881320821374c99 100644 --- a/integration-cli/docker_cli_rmi_test.go +++ b/integration-cli/docker_cli_rmi_test.go @@ -108,7 +108,7 @@ func (s *DockerSuite) TestRmiImgIDForce(c *check.C) { runCmd = exec.Command(dockerBinary, "rmi", imgID) out, _, err = runCommandWithOutput(runCmd) if err == nil || !strings.Contains(out, fmt.Sprintf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", imgID)) { - c.Fatalf("rmi tagged in mutiple repos should have failed without force:%s, %v", out, err) + c.Fatalf("rmi tagged in multiple repos should have failed without force:%s, %v", out, err) } dockerCmd(c, "rmi", "-f", imgID) diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 8da6f3b879dd5905fcf99740b2460599819a6479..0cf5c31eeeac9fff5b1148b23ae1e29680f5f0ba 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -329,7 +329,7 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) { cmd = exec.Command(dockerBinary, "inspect", "-f", "{{.NetworkSettings.IPAddress}}", cID) ip, _, _, err := runCommandWithStdoutStderr(cmd) if err != nil { - c.Fatalf("faild to inspect container: %v, output: %q", err, ip) + c.Fatalf("failed to inspect container: %v, output: %q", err, ip) } ip = strings.TrimSpace(ip) cmd = exec.Command(dockerBinary, "run", "--link", cID+":test", "busybox", "/bin/cat", "/etc/hosts") @@ -447,7 +447,6 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) { if _, err := buildImage(name, dockerFile, false); err != nil { c.Fatal(err) } - defer deleteImages(name) out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-v", "/test/test", name)) if err != nil { @@ -604,7 +603,6 @@ func (s *DockerSuite) TestRunCreateVolume(c *check.C) { // Note that this bug happens only with symlinks with a target that starts with '/'. func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) { image := "docker-test-createvolumewithsymlink" - defer deleteImages(image) buildCmd := exec.Command(dockerBinary, "build", "-t", image, "-") buildCmd.Stdin = strings.NewReader(`FROM busybox @@ -644,7 +642,6 @@ func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) { // Tests that a volume path that has a symlink exists in a container mounting it with `--volumes-from`. func (s *DockerSuite) TestRunVolumesFromSymlinkPath(c *check.C) { name := "docker-test-volumesfromsymlinkpath" - defer deleteImages(name) buildCmd := exec.Command(dockerBinary, "build", "-t", name, "-") buildCmd.Stdin = strings.NewReader(`FROM busybox @@ -759,17 +756,22 @@ func (s *DockerSuite) TestRunTwoConcurrentContainers(c *check.C) { group := sync.WaitGroup{} group.Add(2) + errChan := make(chan error, 2) for i := 0; i < 2; i++ { go func() { defer group.Done() cmd := exec.Command(dockerBinary, "run", "busybox", "sleep", "2") - if _, err := runCommand(cmd); err != nil { - c.Fatal(err) - } + _, err := runCommand(cmd) + errChan <- err }() } group.Wait() + close(errChan) + + for err := range errChan { + c.Assert(err, check.IsNil) + } } func (s *DockerSuite) TestRunEnvironment(c *check.C) { @@ -1746,7 +1748,6 @@ func (s *DockerSuite) TestRunState(c *check.C) { // Test for #1737 func (s *DockerSuite) TestRunCopyVolumeUidGid(c *check.C) { name := "testrunvolumesuidgid" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd @@ -1772,7 +1773,6 @@ func (s *DockerSuite) TestRunCopyVolumeUidGid(c *check.C) { // Test for #1582 func (s *DockerSuite) TestRunCopyVolumeContent(c *check.C) { name := "testruncopyvolumecontent" - defer deleteImages(name) _, err := buildImage(name, `FROM busybox RUN mkdir -p /hello/local && echo hello > /hello/local/world`, @@ -1794,7 +1794,6 @@ func (s *DockerSuite) TestRunCopyVolumeContent(c *check.C) { func (s *DockerSuite) TestRunCleanupCmdOnEntrypoint(c *check.C) { name := "testrunmdcleanuponentrypoint" - defer deleteImages(name) if _, err := buildImage(name, `FROM busybox ENTRYPOINT ["echo"] @@ -1857,22 +1856,20 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) { if err := stdin.Close(); err != nil { c.Fatal(err) } - finish := make(chan struct{}) + finish := make(chan error) go func() { - if err := runCmd.Wait(); err != nil { - c.Fatal(err) - } + finish <- runCmd.Wait() close(finish) }() select { - case <-finish: + case err := <-finish: + c.Assert(err, check.IsNil) case <-time.After(1 * time.Second): c.Fatal("docker run failed to exit on stdin close") } state, err := inspectField(name, "State.Running") - if err != nil { - c.Fatal(err) - } + c.Assert(err, check.IsNil) + if state != "false" { c.Fatal("Container must be stopped after stdin closing") } @@ -2073,7 +2070,7 @@ func (s *DockerSuite) TestRunCidFileCleanupIfEmpty(c *check.C) { if err == nil { c.Fatalf("Run without command must fail. out=%s", out) } else if !strings.Contains(out, "No command specified") { - c.Fatalf("Run without command failed with wrong outpuc. out=%s\nerr=%v", out, err) + c.Fatalf("Run without command failed with wrong output. out=%s\nerr=%v", out, err) } if _, err := os.Stat(tmpCidFile); err == nil { @@ -2349,7 +2346,6 @@ func (s *DockerSuite) TestRunCreateVolumeEtc(c *check.C) { } func (s *DockerSuite) TestVolumesNoCopyData(c *check.C) { - defer deleteImages("dataimage") if _, err := buildImage("dataimage", `FROM busybox RUN mkdir -p /foo @@ -2430,7 +2426,6 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) { true); err != nil { c.Fatal(err) } - defer deleteImages("run_volumes_clean_paths") cmd := exec.Command(dockerBinary, "run", "-v", "/foo", "-v", "/bar/", "--name", "dark_helmet", "run_volumes_clean_paths") if out, _, err := runCommandWithOutput(cmd); err != nil { @@ -2665,6 +2660,14 @@ func (s *DockerSuite) TestContainerNetworkMode(c *check.C) { } } +func (s *DockerSuite) TestContainerNetworkModeToSelf(c *check.C) { + cmd := exec.Command(dockerBinary, "run", "--name=me", "--net=container:me", "busybox", "true") + out, _, err := runCommandWithOutput(cmd) + if err == nil || !strings.Contains(out, "cannot join own network") { + c.Fatalf("using container net mode to self should result in an error") + } +} + func (s *DockerSuite) TestRunModePidHost(c *check.C) { testRequires(c, NativeExecDriver, SameHostDaemon) @@ -2762,25 +2765,29 @@ func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *check.C) { } func (s *DockerSuite) TestRunTtyWithPipe(c *check.C) { - done := make(chan struct{}) + errChan := make(chan error) go func() { - defer close(done) + defer close(errChan) cmd := exec.Command(dockerBinary, "run", "-ti", "busybox", "true") if _, err := cmd.StdinPipe(); err != nil { - c.Fatal(err) + errChan <- err + return } expected := "cannot enable tty mode" if out, _, err := runCommandWithOutput(cmd); err == nil { - c.Fatal("run should have failed") + errChan <- fmt.Errorf("run should have failed") + return } else if !strings.Contains(out, expected) { - c.Fatalf("run failed with error %q: expected %q", out, expected) + errChan <- fmt.Errorf("run failed with error %q: expected %q", out, expected) + return } }() select { - case <-done: + case err := <-errChan: + c.Assert(err, check.IsNil) case <-time.After(3 * time.Second): c.Fatal("container is running but should have failed") } @@ -2875,19 +2882,19 @@ func (s *DockerSuite) TestRunAllowPortRangeThroughPublish(c *check.C) { } func (s *DockerSuite) TestRunOOMExitCode(c *check.C) { - done := make(chan struct{}) + errChan := make(chan error) go func() { - defer close(done) - + defer close(errChan) runCmd := exec.Command(dockerBinary, "run", "-m", "4MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done") out, exitCode, _ := runCommandWithOutput(runCmd) if expected := 137; exitCode != expected { - c.Fatalf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) + errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) } }() select { - case <-done: + case err := <-errChan: + c.Assert(err, check.IsNil) case <-time.After(30 * time.Second): c.Fatal("Timeout waiting for container to die on OOM") } @@ -3030,9 +3037,7 @@ func (s *DockerSuite) TestRunPidHostWithChildIsKillable(c *check.C) { }() select { case err := <-errchan: - if err != nil { - c.Fatal(err) - } + c.Assert(err, check.IsNil) case <-time.After(5 * time.Second): c.Fatal("Kill container timed out") } diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index 74fae1735259d7783565a000586d2221e71aae06..59b623162de44319d3f0cfcdde4ac4e588baf477 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -29,21 +29,22 @@ func (s *DockerSuite) TestRunRedirectStdout(c *check.C) { cmd.Stdin = tty cmd.Stdout = tty cmd.Stderr = tty - ch := make(chan struct{}) if err := cmd.Start(); err != nil { c.Fatalf("start err: %v", err) } + ch := make(chan error) go func() { - if err := cmd.Wait(); err != nil { - c.Fatalf("wait err=%v", err) - } + ch <- cmd.Wait() close(ch) }() select { case <-time.After(10 * time.Second): c.Fatal("command timeout") - case <-ch: + case err := <-ch: + if err != nil { + c.Fatalf("wait err=%v", err) + } } } @@ -213,7 +214,7 @@ func (s *DockerSuite) TestRunAttachDetach(c *check.C) { c.Fatal(err) } if strings.TrimSpace(out) != "hello" { - c.Fatalf("exepected 'hello', got %q", out) + c.Fatalf("expected 'hello', got %q", out) } // escape sequence @@ -236,7 +237,7 @@ func (s *DockerSuite) TestRunAttachDetach(c *check.C) { c.Fatal(err) } if running != "true" { - c.Fatal("exepected container to still be running") + c.Fatal("expected container to still be running") } go func() { diff --git a/integration-cli/docker_cli_save_load_test.go b/integration-cli/docker_cli_save_load_test.go index f74f69fcc14d38a92da9ca0defb9ab47fccff065..f83f6645ac6165c52be0aede9420149c8ca47741 100644 --- a/integration-cli/docker_cli_save_load_test.go +++ b/integration-cli/docker_cli_save_load_test.go @@ -131,7 +131,6 @@ func (s *DockerSuite) TestSaveSingleTag(c *check.C) { repoName := "foobar-save-single-tag-test" tagCmd := exec.Command(dockerBinary, "tag", "busybox:latest", fmt.Sprintf("%v:latest", repoName)) - defer deleteImages(repoName) if out, _, err := runCommandWithOutput(tagCmd); err != nil { c.Fatalf("failed to tag repo: %s, %v", out, err) } @@ -157,7 +156,6 @@ func (s *DockerSuite) TestSaveImageId(c *check.C) { repoName := "foobar-save-image-id-test" tagCmd := exec.Command(dockerBinary, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", repoName)) - defer deleteImages(repoName) if out, _, err := runCommandWithOutput(tagCmd); err != nil { c.Fatalf("failed to tag repo: %s, %v", out, err) } @@ -264,7 +262,6 @@ func (s *DockerSuite) TestSaveMultipleNames(c *check.C) { if out, _, err := runCommandWithOutput(tagCmd); err != nil { c.Fatalf("failed to tag repo: %s, %v", out, err) } - defer deleteImages(repoName + "-one") // Make two images tagCmd = exec.Command(dockerBinary, "tag", "emptyfs:latest", fmt.Sprintf("%v-two:latest", repoName)) @@ -272,7 +269,6 @@ func (s *DockerSuite) TestSaveMultipleNames(c *check.C) { if err != nil { c.Fatalf("failed to tag repo: %s, %v", out, err) } - defer deleteImages(repoName + "-two") out, _, err = runCommandPipelineWithOutput( exec.Command(dockerBinary, "save", fmt.Sprintf("%v-one", repoName), fmt.Sprintf("%v-two:latest", repoName)), @@ -311,9 +307,7 @@ func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) { tagBar := repoName + ":bar" idFoo := makeImage("busybox:latest", tagFoo) - defer deleteImages(idFoo) idBar := makeImage("busybox:latest", tagBar) - defer deleteImages(idBar) deleteImages(repoName) @@ -339,7 +333,7 @@ func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) { sort.Strings(actual) sort.Strings(expected) if !reflect.DeepEqual(expected, actual) { - c.Fatalf("achive does not contains the right layers: got %v, expected %v", actual, expected) + c.Fatalf("archive does not contains the right layers: got %v, expected %v", actual, expected) } } @@ -358,7 +352,6 @@ func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) { os.Mkdir(extractionDirectory, 0777) defer os.RemoveAll(tmpDir) - defer deleteImages(name) _, err = buildImage(name, `FROM busybox RUN adduser -D user && mkdir -p /opt/a/b && chown -R user:user /opt/a diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go index 52afac1af1a0757a1f3dce68907a0a1922f96517..fddc8c97bbf0dccdf9c601e8c44be493ed678c00 100644 --- a/integration-cli/docker_cli_start_test.go +++ b/integration-cli/docker_cli_start_test.go @@ -20,18 +20,19 @@ func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) { c.Fatal("Expected error but got none") } - ch := make(chan struct{}) + ch := make(chan error) go func() { // Attempt to start attached to the container that won't start // This should return an error immediately since the container can't be started if _, err := runCommand(exec.Command(dockerBinary, "start", "-a", "test2")); err == nil { - c.Fatal("Expected error but got none") + ch <- fmt.Errorf("Expected error but got none") } close(ch) }() select { - case <-ch: + case err := <-ch: + c.Assert(err, check.IsNil) case <-time.After(time.Second): c.Fatalf("Attach did not exit properly") } @@ -205,7 +206,7 @@ func (s *DockerSuite) TestStartMultipleContainers(c *check.C) { c.Fatal("Container should be stopped") } - // start all the three containers, container `child_first` start first which should be faild + // start all the three containers, container `child_first` start first which should be failed // container 'parent' start second and then start container 'child_second' cmd = exec.Command(dockerBinary, "start", "child_first", "parent", "child_second") out, _, err = runCommandWithOutput(cmd) diff --git a/integration-cli/docker_cli_tag_test.go b/integration-cli/docker_cli_tag_test.go index 1b4d36b7bf3d4c0d70eac6fc21f99f40be596c39..35225f9c1e5ecbca9a1beb72d2f15cc6d7fc24a3 100644 --- a/integration-cli/docker_cli_tag_test.go +++ b/integration-cli/docker_cli_tag_test.go @@ -18,9 +18,6 @@ func (s *DockerSuite) TestTagUnprefixedRepoByName(c *check.C) { if out, _, err := runCommandWithOutput(tagCmd); err != nil { c.Fatal(out, err) } - - deleteImages("testfoobarbaz") - } // tagging an image by ID in a new unprefixed repo should work @@ -36,9 +33,6 @@ func (s *DockerSuite) TestTagUnprefixedRepoByID(c *check.C) { if out, _, err = runCommandWithOutput(tagCmd); err != nil { c.Fatal(out, err) } - - deleteImages("testfoobarbaz") - } // ensure we don't allow the use of invalid repository names; these tag operations should fail @@ -104,8 +98,6 @@ func (s *DockerSuite) TestTagExistedNameWithoutForce(c *check.C) { if err == nil || !strings.Contains(out, "Conflict: Tag test is already set to image") { c.Fatal("tag busybox busybox:test should have failed,because busybox:test is existed") } - deleteImages("busybox:test") - } // tag an image with an existed tag name with -f option should work @@ -122,8 +114,6 @@ func (s *DockerSuite) TestTagExistedNameWithForce(c *check.C) { if out, _, err := runCommandWithOutput(tagCmd); err != nil { c.Fatal(out, err) } - deleteImages("busybox:test") - } // ensure tagging using official names works diff --git a/integration-cli/docker_cli_wait_test.go b/integration-cli/docker_cli_wait_test.go index 09d0272bcf03bf4cae8f5f4b05c6cb069f49ddfb..21f04faf0f8cdcc484718c9e66455e93d060516f 100644 --- a/integration-cli/docker_cli_wait_test.go +++ b/integration-cli/docker_cli_wait_test.go @@ -44,19 +44,29 @@ func (s *DockerSuite) TestWaitNonBlockedExitZero(c *check.C) { // blocking wait with 0 exit code func (s *DockerSuite) TestWaitBlockedExitZero(c *check.C) { + out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' SIGTERM; while true; do sleep 0.01; done") + containerID := strings.TrimSpace(out) - runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "sh", "-c", "sleep 10") - out, _, err := runCommandWithOutput(runCmd) - if err != nil { - c.Fatal(out, err) + if err := waitRun(containerID); err != nil { + c.Fatal(err) } - containerID := strings.TrimSpace(out) - runCmd = exec.Command(dockerBinary, "wait", containerID) - out, _, err = runCommandWithOutput(runCmd) + chWait := make(chan string) + go func() { + out, _, _ := runCommandWithOutput(exec.Command(dockerBinary, "wait", containerID)) + chWait <- out + }() - if err != nil || strings.TrimSpace(out) != "0" { - c.Fatal("failed to set up container", out, err) + time.Sleep(100 * time.Millisecond) + dockerCmd(c, "stop", containerID) + + select { + case status := <-chWait: + if strings.TrimSpace(status) != "0" { + c.Fatalf("expected exit 0, got %s", status) + } + case <-time.After(2 * time.Second): + c.Fatal("timeout waiting for `docker wait` to exit") } } @@ -97,19 +107,30 @@ func (s *DockerSuite) TestWaitNonBlockedExitRandom(c *check.C) { // blocking wait with random exit code func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) { - - runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "sh", "-c", "sleep 10; exit 99") - out, _, err := runCommandWithOutput(runCmd) - if err != nil { - c.Fatal(out, err) - } + out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "trap 'exit 99' SIGTERM; while true; do sleep 0.01; done") containerID := strings.TrimSpace(out) + if err := waitRun(containerID); err != nil { + c.Fatal(err) + } + if err := waitRun(containerID); err != nil { + c.Fatal(err) + } - runCmd = exec.Command(dockerBinary, "wait", containerID) - out, _, err = runCommandWithOutput(runCmd) + chWait := make(chan string) + go func() { + out, _, _ := runCommandWithOutput(exec.Command(dockerBinary, "wait", containerID)) + chWait <- out + }() - if err != nil || strings.TrimSpace(out) != "99" { - c.Fatal("failed to set up container", out, err) - } + time.Sleep(100 * time.Millisecond) + dockerCmd(c, "stop", containerID) + select { + case status := <-chWait: + if strings.TrimSpace(status) != "99" { + c.Fatalf("expected exit 99, got %s", status) + } + case <-time.After(2 * time.Second): + c.Fatal("timeout waiting for `docker wait` to exit") + } } diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 20e87244e7e4f763fe3fa8e9458d981d8462d58b..8386bb59ff962b6e2ca0def7ba9c921fab54dc3c 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -313,20 +313,20 @@ func sockRequest(method, endpoint string, data interface{}) (int, []byte, error) return -1, nil, err } - status, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json") + res, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json") if err != nil { b, _ := ioutil.ReadAll(body) - return status, b, err + return -1, b, err } var b []byte b, err = readBody(body) - return status, b, err + return res.StatusCode, b, err } -func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, io.ReadCloser, error) { +func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) { c, err := sockConn(time.Duration(10 * time.Second)) if err != nil { - return -1, nil, fmt.Errorf("could not dial docker daemon: %v", err) + return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err) } client := httputil.NewClientConn(c, nil) @@ -334,7 +334,7 @@ func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, io req, err := http.NewRequest(method, endpoint, data) if err != nil { client.Close() - return -1, nil, fmt.Errorf("could not create new request: %v", err) + return nil, nil, fmt.Errorf("could not create new request: %v", err) } if ct != "" { @@ -344,14 +344,14 @@ func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, io resp, err := client.Do(req) if err != nil { client.Close() - return -1, nil, fmt.Errorf("could not perform request: %v", err) + return nil, nil, fmt.Errorf("could not perform request: %v", err) } body := ioutils.NewReadCloserWrapper(resp.Body, func() error { defer client.Close() return resp.Body.Close() }) - return resp.StatusCode, body, err + return resp, body, nil } func readBody(b io.ReadCloser) ([]byte, error) { @@ -361,9 +361,7 @@ func readBody(b io.ReadCloser) ([]byte, error) { func deleteContainer(container string) error { container = strings.TrimSpace(strings.Replace(container, "\n", " ", -1)) - killArgs := strings.Split(fmt.Sprintf("kill %v", container), " ") - runCommand(exec.Command(dockerBinary, killArgs...)) - rmArgs := strings.Split(fmt.Sprintf("rm -v %v", container), " ") + rmArgs := strings.Split(fmt.Sprintf("rm -fv %v", container), " ") exitCode, err := runCommand(exec.Command(dockerBinary, rmArgs...)) // set error manually if not set if exitCode != 0 && err == nil { @@ -396,6 +394,58 @@ func deleteAllContainers() error { return nil } +var protectedImages = map[string]struct{}{} + +func init() { + out, err := exec.Command(dockerBinary, "images").CombinedOutput() + if err != nil { + panic(err) + } + lines := strings.Split(string(out), "\n")[1:] + for _, l := range lines { + if l == "" { + continue + } + fields := strings.Fields(l) + imgTag := fields[0] + ":" + fields[1] + // just for case if we have dangling images in tested daemon + if imgTag != ":" { + protectedImages[imgTag] = struct{}{} + } + } +} + +func deleteAllImages() error { + out, err := exec.Command(dockerBinary, "images").CombinedOutput() + if err != nil { + return err + } + lines := strings.Split(string(out), "\n")[1:] + var imgs []string + for _, l := range lines { + if l == "" { + continue + } + fields := strings.Fields(l) + imgTag := fields[0] + ":" + fields[1] + if _, ok := protectedImages[imgTag]; !ok { + if fields[0] == "" { + imgs = append(imgs, fields[2]) + continue + } + imgs = append(imgs, imgTag) + } + } + if len(imgs) == 0 { + return nil + } + args := append([]string{"rmi", "-f"}, imgs...) + if err := exec.Command(dockerBinary, args...).Run(); err != nil { + return err + } + return nil +} + func getPausedContainers() (string, error) { getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") out, exitCode, err := runCommandWithOutput(getPausedContainersCmd) @@ -1078,7 +1128,7 @@ func daemonTime(c *check.C) time.Time { return dt } -func setupRegistry(c *check.C) func() { +func setupRegistry(c *check.C) *testRegistryV2 { testRequires(c, RegistryHosting) reg, err := newTestRegistryV2(c) if err != nil { @@ -1096,8 +1146,7 @@ func setupRegistry(c *check.C) func() { if err != nil { c.Fatal("Timeout waiting for test registry to become available") } - - return func() { reg.Close() } + return reg } // appendBaseEnv appends the minimum set of environment variables to exec the diff --git a/integration-cli/utils.go b/integration-cli/utils.go index 4ca7158aeb7bd1ea91dec05566a060e30e0d11e2..f0de79ea8f0712cc132699f324ef88796f6764bb 100644 --- a/integration-cli/utils.go +++ b/integration-cli/utils.go @@ -169,8 +169,7 @@ func runCommandPipelineWithOutput(cmds ...*exec.Cmd) (output string, exitCode in } func unmarshalJSON(data []byte, result interface{}) error { - err := json.Unmarshal(data, result) - if err != nil { + if err := json.Unmarshal(data, result); err != nil { return err } diff --git a/integration/api_test.go b/integration/api_test.go index 614966f6e5f1da22751a65aad9d609b36fc44ee3..e45fa97e8288e94234bd9b0731bca00df484f6ac 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -434,7 +434,7 @@ func TestGetEnabledCors(t *testing.T) { t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth\", %s found.", allowHeaders) } if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" { - t.Errorf("Expected hearder Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods) + t.Errorf("Expected header Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods) } } @@ -648,7 +648,7 @@ func TestConstainersStartChunkedEncodingHostConfig(t *testing.T) { } if c.HostConfig.Binds[0] != "/tmp:/foo" { - t.Fatal("Chunked encoding not properly handled, execpted binds to be /tmp:/foo, got:", c.HostConfig.Binds[0]) + t.Fatal("Chunked encoding not properly handled, expected binds to be /tmp:/foo, got:", c.HostConfig.Binds[0]) } } diff --git a/integration/container_test.go b/integration/container_test.go index 01078734cfc896568c575f239fd40e582f8b6952..9256e9997f2a412ae188db9878a51fdcedf54b0a 100644 --- a/integration/container_test.go +++ b/integration/container_test.go @@ -213,7 +213,7 @@ func BenchmarkRunParallel(b *testing.B) { return } // if string(output) != "foo" { - // complete <- fmt.Errorf("Unexecpted output: %v", string(output)) + // complete <- fmt.Errorf("Unexpected output: %v", string(output)) // } if err := daemon.Rm(container); err != nil { complete <- err diff --git a/integration/runtime_test.go b/integration/runtime_test.go index 82f21b70083c089bae1de66893b11432908e5461..a2f22072c36b86e7228a112ea7b7b49407fe5c06 100644 --- a/integration/runtime_test.go +++ b/integration/runtime_test.go @@ -837,7 +837,7 @@ func TestDestroyWithInitLayer(t *testing.T) { // Make sure that the container does not exist in the driver if _, err := driver.Get(container.ID, ""); err == nil { - t.Fatal("Conttainer should not exist in the driver") + t.Fatal("Container should not exist in the driver") } // Make sure that the init layer is removed from the driver diff --git a/integration/utils.go b/integration/utils.go index 1d27cd6e42234c3b83c301629acf244475bb9c6b..62e02e9bb141f92730981aba7af55e30dc8e445e 100644 --- a/integration/utils.go +++ b/integration/utils.go @@ -41,7 +41,7 @@ func waitContainerStart(t *testing.T, timeout time.Duration) *daemon.Container { }) if container == nil { - t.Fatal("An error occured while waiting for the container to start") + t.Fatal("An error occurred while waiting for the container to start") } return container diff --git a/links/links.go b/links/links.go index 8bbacdd3d752773402ab0a743f70cb5e518bdaa1..935bff4ae3911abfb9f15dc836d3551402cf153d 100644 --- a/links/links.go +++ b/links/links.go @@ -107,8 +107,8 @@ func (l *Link) ToEnv() []string { if l.ChildEnvironment != nil { for _, v := range l.ChildEnvironment { - parts := strings.Split(v, "=") - if len(parts) != 2 { + parts := strings.SplitN(v, "=", 2) + if len(parts) < 2 { continue } // Ignore a few variables that are added during docker build (and not really relevant to linked containers) diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 7f188975036dd551c7d1085b44602b2541b3c9dc..4d8d26008759564116f52f7d19bb1a391c0904c6 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -391,6 +391,13 @@ func Tar(path string, compression Compression) (io.ReadCloser, error) { // TarWithOptions creates an archive from the directory at `path`, only including files whose relative // paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`. func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) { + + patterns, patDirs, exceptions, err := fileutils.CleanPatterns(options.ExcludePatterns) + + if err != nil { + return nil, err + } + pipeReader, pipeWriter := io.Pipe() compressWriter, err := CompressStream(pipeWriter, options.Compression) @@ -441,7 +448,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) // 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) + skip, err = fileutils.OptimizedMatches(relFilePath, patterns, patDirs) if err != nil { logrus.Debugf("Error matching %s", relFilePath, err) return err @@ -449,7 +456,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) } if skip { - if f.IsDir() { + if !exceptions && f.IsDir() { return filepath.SkipDir } return nil diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index dabb0d504bc13950912276c52f47cfd5d381859c..ae9b5a8cd2520f081b6f7963e9ba5eacf293f0f1 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -207,6 +207,315 @@ func TestCmdStreamGood(t *testing.T) { } } +func TestUntarPathWithInvalidDest(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempFolder) + invalidDestFolder := path.Join(tempFolder, "invalidDest") + // Create a src file + srcFile := path.Join(tempFolder, "src") + _, err = os.Create(srcFile) + if err != nil { + t.Fatalf("Fail to create the source file") + } + err = UntarPath(srcFile, invalidDestFolder) + if err == nil { + t.Fatalf("UntarPath with invalid destination path should throw an error.") + } +} + +func TestUntarPathWithInvalidSrc(t *testing.T) { + dest, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatalf("Fail to create the destination file") + } + defer os.RemoveAll(dest) + err = UntarPath("/invalid/path", dest) + if err == nil { + t.Fatalf("UntarPath with invalid src path should throw an error.") + } +} + +func TestUntarPath(t *testing.T) { + tmpFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpFolder) + srcFile := path.Join(tmpFolder, "src") + tarFile := path.Join(tmpFolder, "src.tar") + os.Create(path.Join(tmpFolder, "src")) + cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) + _, err = cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + destFolder := path.Join(tmpFolder, "dest") + err = os.MkdirAll(destFolder, 0740) + if err != nil { + t.Fatalf("Fail to create the destination file") + } + err = UntarPath(tarFile, destFolder) + if err != nil { + t.Fatalf("UntarPath shouldn't throw an error, %s.", err) + } + expectedFile := path.Join(destFolder, srcFile) + _, err = os.Stat(expectedFile) + if err != nil { + t.Fatalf("Destination folder should contain the source file but did not.") + } +} + +// Do the same test as above but with the destination as file, it should fail +func TestUntarPathWithDestinationFile(t *testing.T) { + tmpFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpFolder) + srcFile := path.Join(tmpFolder, "src") + tarFile := path.Join(tmpFolder, "src.tar") + os.Create(path.Join(tmpFolder, "src")) + cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) + _, err = cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + destFile := path.Join(tmpFolder, "dest") + _, err = os.Create(destFile) + if err != nil { + t.Fatalf("Fail to create the destination file") + } + err = UntarPath(tarFile, destFile) + if err == nil { + t.Fatalf("UntarPath should throw an error if the destination if a file") + } +} + +// Do the same test as above but with the destination folder already exists +// and the destination file is a directory +// It's working, see https://github.com/docker/docker/issues/10040 +func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) { + tmpFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpFolder) + srcFile := path.Join(tmpFolder, "src") + tarFile := path.Join(tmpFolder, "src.tar") + os.Create(srcFile) + cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) + _, err = cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + destFolder := path.Join(tmpFolder, "dest") + err = os.MkdirAll(destFolder, 0740) + if err != nil { + t.Fatalf("Fail to create the destination folder") + } + // Let's create a folder that will has the same path as the extracted file (from tar) + destSrcFileAsFolder := path.Join(destFolder, srcFile) + err = os.MkdirAll(destSrcFileAsFolder, 0740) + if err != nil { + t.Fatal(err) + } + err = UntarPath(tarFile, destFolder) + if err != nil { + t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder") + } +} + +func TestCopyWithTarInvalidSrc(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(nil) + } + destFolder := path.Join(tempFolder, "dest") + invalidSrc := path.Join(tempFolder, "doesnotexists") + err = os.MkdirAll(destFolder, 0740) + if err != nil { + t.Fatal(err) + } + err = CopyWithTar(invalidSrc, destFolder) + if err == nil { + t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") + } +} + +func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(nil) + } + srcFolder := path.Join(tempFolder, "src") + inexistentDestFolder := path.Join(tempFolder, "doesnotexists") + err = os.MkdirAll(srcFolder, 0740) + if err != nil { + t.Fatal(err) + } + err = CopyWithTar(srcFolder, inexistentDestFolder) + if err != nil { + t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") + } + _, err = os.Stat(inexistentDestFolder) + if err != nil { + t.Fatalf("CopyWithTar with an inexistent folder should create it.") + } +} + +// Test CopyWithTar with a file as src +func TestCopyWithTarSrcFile(t *testing.T) { + folder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(folder) + dest := path.Join(folder, "dest") + srcFolder := path.Join(folder, "src") + src := path.Join(folder, path.Join("src", "src")) + err = os.MkdirAll(srcFolder, 0740) + if err != nil { + t.Fatal(err) + } + err = os.MkdirAll(dest, 0740) + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(src, []byte("content"), 0777) + err = CopyWithTar(src, dest) + if err != nil { + t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) + } + _, err = os.Stat(dest) + // FIXME Check the content + if err != nil { + t.Fatalf("Destination file should be the same as the source.") + } +} + +// Test CopyWithTar with a folder as src +func TestCopyWithTarSrcFolder(t *testing.T) { + folder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(folder) + dest := path.Join(folder, "dest") + src := path.Join(folder, path.Join("src", "folder")) + err = os.MkdirAll(src, 0740) + if err != nil { + t.Fatal(err) + } + err = os.MkdirAll(dest, 0740) + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(path.Join(src, "file"), []byte("content"), 0777) + err = CopyWithTar(src, dest) + if err != nil { + t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) + } + _, err = os.Stat(dest) + // FIXME Check the content (the file inside) + if err != nil { + t.Fatalf("Destination folder should contain the source file but did not.") + } +} + +func TestCopyFileWithTarInvalidSrc(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempFolder) + destFolder := path.Join(tempFolder, "dest") + err = os.MkdirAll(destFolder, 0740) + if err != nil { + t.Fatal(err) + } + invalidFile := path.Join(tempFolder, "doesnotexists") + err = CopyFileWithTar(invalidFile, destFolder) + if err == nil { + t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") + } +} + +func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(nil) + } + defer os.RemoveAll(tempFolder) + srcFile := path.Join(tempFolder, "src") + inexistentDestFolder := path.Join(tempFolder, "doesnotexists") + _, err = os.Create(srcFile) + if err != nil { + t.Fatal(err) + } + err = CopyFileWithTar(srcFile, inexistentDestFolder) + if err != nil { + t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") + } + _, err = os.Stat(inexistentDestFolder) + if err != nil { + t.Fatalf("CopyWithTar with an inexistent folder should create it.") + } + // FIXME Test the src file and content +} + +func TestCopyFileWithTarSrcFolder(t *testing.T) { + folder, err := ioutil.TempDir("", "docker-archive-copyfilewithtar-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(folder) + dest := path.Join(folder, "dest") + src := path.Join(folder, "srcfolder") + err = os.MkdirAll(src, 0740) + if err != nil { + t.Fatal(err) + } + err = os.MkdirAll(dest, 0740) + if err != nil { + t.Fatal(err) + } + err = CopyFileWithTar(src, dest) + if err == nil { + t.Fatalf("CopyFileWithTar should throw an error with a folder.") + } +} + +func TestCopyFileWithTarSrcFile(t *testing.T) { + folder, err := ioutil.TempDir("", "docker-archive-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(folder) + dest := path.Join(folder, "dest") + srcFolder := path.Join(folder, "src") + src := path.Join(folder, path.Join("src", "src")) + err = os.MkdirAll(srcFolder, 0740) + if err != nil { + t.Fatal(err) + } + err = os.MkdirAll(dest, 0740) + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(src, []byte("content"), 0777) + err = CopyWithTar(src, dest+"/") + if err != nil { + t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err) + } + _, err = os.Stat(dest) + if err != nil { + t.Fatalf("Destination folder should contain the source file but did not.") + } +} + func TestTarFiles(t *testing.T) { // try without hardlinks if err := checkNoChanges(1000, false); err != nil { diff --git a/pkg/archive/archive_windows_test.go b/pkg/archive/archive_windows_test.go index b33e0fb0055a569d022fd6f18d991823f8ddf6d8..72bc71e06b4133974d61de98a3a0f1bf82feee9f 100644 --- a/pkg/archive/archive_windows_test.go +++ b/pkg/archive/archive_windows_test.go @@ -20,7 +20,7 @@ func TestCanonicalTarNameForPath(t *testing.T) { if out, err := CanonicalTarNameForPath(v.in); err != nil && !v.shouldFail { t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) } else if v.shouldFail && err == nil { - t.Fatalf("canonical path call should have pailed with error. in=%s out=%s", v.in, out) + t.Fatalf("canonical path call should have failed with error. in=%s out=%s", v.in, out) } else if !v.shouldFail && out != v.expected { t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) } diff --git a/pkg/archive/changes_test.go b/pkg/archive/changes_test.go index 53ec575b67ab3c8a927f1fab5e9e4532daa6e74c..290b2dd40222d801f53d2e1a7ab65b455d7b4c15 100644 --- a/pkg/archive/changes_test.go +++ b/pkg/archive/changes_test.go @@ -6,6 +6,7 @@ import ( "os/exec" "path" "sort" + "syscall" "testing" "time" ) @@ -91,17 +92,130 @@ func createSampleDir(t *testing.T, root string) { } } +func TestChangeString(t *testing.T) { + modifiyChange := Change{"change", ChangeModify} + toString := modifiyChange.String() + if toString != "C change" { + t.Fatalf("String() of a change with ChangeModifiy Kind should have been %s but was %s", "C change", toString) + } + addChange := Change{"change", ChangeAdd} + toString = addChange.String() + if toString != "A change" { + t.Fatalf("String() of a change with ChangeAdd Kind should have been %s but was %s", "A change", toString) + } + deleteChange := Change{"change", ChangeDelete} + toString = deleteChange.String() + if toString != "D change" { + t.Fatalf("String() of a change with ChangeDelete Kind should have been %s but was %s", "D change", toString) + } +} + +func TestChangesWithNoChanges(t *testing.T) { + rwLayer, err := ioutil.TempDir("", "docker-changes-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rwLayer) + layer, err := ioutil.TempDir("", "docker-changes-test-layer") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(layer) + createSampleDir(t, layer) + changes, err := Changes([]string{layer}, rwLayer) + if err != nil { + t.Fatal(err) + } + if len(changes) != 0 { + t.Fatalf("Changes with no difference should have detect no changes, but detected %d", len(changes)) + } +} + +func TestChangesWithChanges(t *testing.T) { + rwLayer, err := ioutil.TempDir("", "docker-changes-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rwLayer) + // Create a folder + dir1 := path.Join(rwLayer, "dir1") + os.MkdirAll(dir1, 0740) + deletedFile := path.Join(dir1, ".wh.file1-2") + ioutil.WriteFile(deletedFile, []byte{}, 0600) + modifiedFile := path.Join(dir1, "file1-1") + ioutil.WriteFile(modifiedFile, []byte{0x00}, 01444) + // Let's add a subfolder for a newFile + subfolder := path.Join(dir1, "subfolder") + os.MkdirAll(subfolder, 0740) + newFile := path.Join(subfolder, "newFile") + ioutil.WriteFile(newFile, []byte{}, 0740) + // Let's create folders that with have the role of layers with the same data + layer, err := ioutil.TempDir("", "docker-changes-test-layer") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(layer) + createSampleDir(t, layer) + os.MkdirAll(path.Join(layer, "dir1/subfolder"), 0740) + + // Let's modify modtime for dir1 to be sure it's the same for the two layer (to not having false positive) + fi, err := os.Stat(dir1) + if err != nil { + return + } + mtime := fi.ModTime() + stat := fi.Sys().(*syscall.Stat_t) + atime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) + + layerDir1 := path.Join(layer, "dir1") + os.Chtimes(layerDir1, atime, mtime) + + changes, err := Changes([]string{layer}, rwLayer) + if err != nil { + t.Fatal(err) + } + + sort.Sort(changesByPath(changes)) + + expectedChanges := []Change{ + {"/dir1/file1-1", ChangeModify}, + {"/dir1/file1-2", ChangeDelete}, + {"/dir1/subfolder", ChangeModify}, + {"/dir1/subfolder/newFile", ChangeAdd}, + } + + for i := 0; i < max(len(changes), len(expectedChanges)); i++ { + if i >= len(expectedChanges) { + t.Fatalf("unexpected change %s\n", changes[i].String()) + } + if i >= len(changes) { + t.Fatalf("no change for expected change %s\n", expectedChanges[i].String()) + } + if changes[i].Path == expectedChanges[i].Path { + if changes[i] != expectedChanges[i] { + t.Fatalf("Wrong change for %s, expected %s, got %s\n", changes[i].Path, changes[i].String(), expectedChanges[i].String()) + } + } else if changes[i].Path < expectedChanges[i].Path { + t.Fatalf("unexpected change %s\n", changes[i].String()) + } else { + t.Fatalf("no change for expected change %s != %s\n", expectedChanges[i].String(), changes[i].String()) + } + } +} + // Create an directory, copy it, make sure we report no changes between the two func TestChangesDirsEmpty(t *testing.T) { src, err := ioutil.TempDir("", "docker-changes-test") if err != nil { t.Fatal(err) } + defer os.RemoveAll(src) createSampleDir(t, src) dst := src + "-copy" if err := copyDir(src, dst); err != nil { t.Fatal(err) } + defer os.RemoveAll(dst) changes, err := ChangesDirs(dst, src) if err != nil { t.Fatal(err) @@ -291,3 +405,41 @@ func TestApplyLayer(t *testing.T) { t.Fatalf("Unexpected differences after reapplying mutation: %v", changes2) } } + +func TestChangesSizeWithNoChanges(t *testing.T) { + size := ChangesSize("/tmp", nil) + if size != 0 { + t.Fatalf("ChangesSizes with no changes should be 0, was %d", size) + } +} + +func TestChangesSizeWithOnlyDeleteChanges(t *testing.T) { + changes := []Change{ + {Path: "deletedPath", Kind: ChangeDelete}, + } + size := ChangesSize("/tmp", changes) + if size != 0 { + t.Fatalf("ChangesSizes with only delete changes should be 0, was %d", size) + } +} + +func TestChangesSize(t *testing.T) { + parentPath, err := ioutil.TempDir("", "docker-changes-test") + defer os.RemoveAll(parentPath) + addition := path.Join(parentPath, "addition") + if err := ioutil.WriteFile(addition, []byte{0x01, 0x01, 0x01}, 0744); err != nil { + t.Fatal(err) + } + modification := path.Join(parentPath, "modification") + if err = ioutil.WriteFile(modification, []byte{0x01, 0x01, 0x01}, 0744); err != nil { + t.Fatal(err) + } + changes := []Change{ + {Path: "addition", Kind: ChangeAdd}, + {Path: "modification", Kind: ChangeModify}, + } + size := ChangesSize(parentPath, changes) + if size != 6 { + t.Fatalf("ChangesSizes with only delete changes should be 0, was %d", size) + } +} diff --git a/pkg/archive/wrap_test.go b/pkg/archive/wrap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..46ab36697a75b6d68160758a9e7604770428d954 --- /dev/null +++ b/pkg/archive/wrap_test.go @@ -0,0 +1,98 @@ +package archive + +import ( + "archive/tar" + "bytes" + "io" + "testing" +) + +func TestGenerateEmptyFile(t *testing.T) { + archive, err := Generate("emptyFile") + if err != nil { + t.Fatal(err) + } + if archive == nil { + t.Fatal("The generated archive should not be nil.") + } + + expectedFiles := [][]string{ + {"emptyFile", ""}, + } + + tr := tar.NewReader(archive) + actualFiles := make([][]string, 0, 10) + i := 0 + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + buf := new(bytes.Buffer) + buf.ReadFrom(tr) + content := buf.String() + actualFiles = append(actualFiles, []string{hdr.Name, content}) + i++ + } + if len(actualFiles) != len(expectedFiles) { + t.Fatalf("Number of expected file %d, got %d.", len(expectedFiles), len(actualFiles)) + } + for i := 0; i < len(expectedFiles); i++ { + actual := actualFiles[i] + expected := expectedFiles[i] + if actual[0] != expected[0] { + t.Fatalf("Expected name '%s', Actual name '%s'", expected[0], actual[0]) + } + if actual[1] != expected[1] { + t.Fatalf("Expected content '%s', Actual content '%s'", expected[1], actual[1]) + } + } +} + +func TestGenerateWithContent(t *testing.T) { + archive, err := Generate("file", "content") + if err != nil { + t.Fatal(err) + } + if archive == nil { + t.Fatal("The generated archive should not be nil.") + } + + expectedFiles := [][]string{ + {"file", "content"}, + } + + tr := tar.NewReader(archive) + actualFiles := make([][]string, 0, 10) + i := 0 + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + buf := new(bytes.Buffer) + buf.ReadFrom(tr) + content := buf.String() + actualFiles = append(actualFiles, []string{hdr.Name, content}) + i++ + } + if len(actualFiles) != len(expectedFiles) { + t.Fatalf("Number of expected file %d, got %d.", len(expectedFiles), len(actualFiles)) + } + for i := 0; i < len(expectedFiles); i++ { + actual := actualFiles[i] + expected := expectedFiles[i] + if actual[0] != expected[0] { + t.Fatalf("Expected name '%s', Actual name '%s'", expected[0], actual[0]) + } + if actual[1] != expected[1] { + t.Fatalf("Expected content '%s', Actual content '%s'", expected[1], actual[1]) + } + } +} diff --git a/pkg/fileutils/fileutils.go b/pkg/fileutils/fileutils.go index ef2a6523dc398e8864ec3bc5cb3a8890e3a7ef86..fdafb53c7fefae46f53d4475bf1cbc3aacd51d7b 100644 --- a/pkg/fileutils/fileutils.go +++ b/pkg/fileutils/fileutils.go @@ -1,48 +1,137 @@ package fileutils import ( + "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" + "strings" "github.com/Sirupsen/logrus" ) -// Matches returns true if relFilePath matches any of the patterns -func Matches(relFilePath string, patterns []string) (bool, error) { - for _, exclude := range patterns { - matched, err := filepath.Match(exclude, relFilePath) +func Exclusion(pattern string) bool { + return pattern[0] == '!' +} + +func Empty(pattern string) bool { + return pattern == "" +} + +// Cleanpatterns takes a slice of patterns returns a new +// slice of patterns cleaned with filepath.Clean, stripped +// of any empty patterns and lets the caller know whether the +// slice contains any exception patterns (prefixed with !). +func CleanPatterns(patterns []string) ([]string, [][]string, bool, error) { + // Loop over exclusion patterns and: + // 1. Clean them up. + // 2. Indicate whether we are dealing with any exception rules. + // 3. Error if we see a single exclusion marker on it's own (!). + cleanedPatterns := []string{} + patternDirs := [][]string{} + exceptions := false + for _, pattern := range patterns { + // Eliminate leading and trailing whitespace. + pattern = strings.TrimSpace(pattern) + if Empty(pattern) { + continue + } + if Exclusion(pattern) { + if len(pattern) == 1 { + logrus.Errorf("Illegal exclusion pattern: %s", pattern) + return nil, nil, false, errors.New("Illegal exclusion pattern: !") + } + exceptions = true + } + pattern = filepath.Clean(pattern) + cleanedPatterns = append(cleanedPatterns, pattern) + if Exclusion(pattern) { + pattern = pattern[1:] + } + patternDirs = append(patternDirs, strings.Split(pattern, "/")) + } + + return cleanedPatterns, patternDirs, exceptions, nil +} + +// Matches returns true if file matches any of the patterns +// and isn't excluded by any of the subsequent patterns. +func Matches(file string, patterns []string) (bool, error) { + file = filepath.Clean(file) + + if file == "." { + // Don't let them exclude everything, kind of silly. + return false, nil + } + + patterns, patDirs, _, err := CleanPatterns(patterns) + if err != nil { + return false, err + } + + return OptimizedMatches(file, patterns, patDirs) +} + +// Matches is basically the same as fileutils.Matches() but optimized for archive.go. +// It will assume that the inputs have been preprocessed and therefore the function +// doen't need to do as much error checking and clean-up. This was done to avoid +// repeating these steps on each file being checked during the archive process. +// The more generic fileutils.Matches() can't make these assumptions. +func OptimizedMatches(file string, patterns []string, patDirs [][]string) (bool, error) { + matched := false + parentPath := filepath.Dir(file) + parentPathDirs := strings.Split(parentPath, "/") + + for i, pattern := range patterns { + negative := false + + if Exclusion(pattern) { + negative = true + pattern = pattern[1:] + } + + match, err := filepath.Match(pattern, file) if err != nil { - logrus.Errorf("Error matching: %s (pattern: %s)", relFilePath, exclude) + logrus.Errorf("Error matching: %s (pattern: %s)", file, pattern) return false, err } - if matched { - if filepath.Clean(relFilePath) == "." { - logrus.Errorf("Can't exclude whole path, excluding pattern: %s", exclude) - continue + + if !match && parentPath != "." { + // Check to see if the pattern matches one of our parent dirs. + if len(patDirs[i]) <= len(parentPathDirs) { + match, _ = filepath.Match(strings.Join(patDirs[i], "/"), + strings.Join(parentPathDirs[:len(patDirs[i])], "/")) } - logrus.Debugf("Skipping excluded path: %s", relFilePath) - return true, nil } + + if match { + matched = !negative + } + } + + if matched { + logrus.Debugf("Skipping excluded path: %s", file) } - return false, nil + return matched, nil } func CopyFile(src, dst string) (int64, error) { - if src == dst { + cleanSrc := filepath.Clean(src) + cleanDst := filepath.Clean(dst) + if cleanSrc == cleanDst { return 0, nil } - sf, err := os.Open(src) + sf, err := os.Open(cleanSrc) if err != nil { return 0, err } defer sf.Close() - if err := os.Remove(dst); err != nil && !os.IsNotExist(err) { + if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) { return 0, err } - df, err := os.Create(dst) + df, err := os.Create(cleanDst) if err != nil { return 0, err } diff --git a/pkg/fileutils/fileutils_test.go b/pkg/fileutils/fileutils_test.go index 16d00d7b95d970964d338c6a7d6381dc6774f5a1..ef931684c12812695cb0b9a045b6e8e6ca9bedc1 100644 --- a/pkg/fileutils/fileutils_test.go +++ b/pkg/fileutils/fileutils_test.go @@ -1,10 +1,125 @@ package fileutils import ( + "io/ioutil" "os" + "path" "testing" ) +// CopyFile with invalid src +func TestCopyFileWithInvalidSrc(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + defer os.RemoveAll(tempFolder) + if err != nil { + t.Fatal(err) + } + bytes, err := CopyFile("/invalid/file/path", path.Join(tempFolder, "dest")) + if err == nil { + t.Fatal("Should have fail to copy an invalid src file") + } + if bytes != 0 { + t.Fatal("Should have written 0 bytes") + } + +} + +// CopyFile with invalid dest +func TestCopyFileWithInvalidDest(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + defer os.RemoveAll(tempFolder) + if err != nil { + t.Fatal(err) + } + src := path.Join(tempFolder, "file") + err = ioutil.WriteFile(src, []byte("content"), 0740) + if err != nil { + t.Fatal(err) + } + bytes, err := CopyFile(src, path.Join(tempFolder, "/invalid/dest/path")) + if err == nil { + t.Fatal("Should have fail to copy an invalid src file") + } + if bytes != 0 { + t.Fatal("Should have written 0 bytes") + } + +} + +// CopyFile with same src and dest +func TestCopyFileWithSameSrcAndDest(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + defer os.RemoveAll(tempFolder) + if err != nil { + t.Fatal(err) + } + file := path.Join(tempFolder, "file") + err = ioutil.WriteFile(file, []byte("content"), 0740) + if err != nil { + t.Fatal(err) + } + bytes, err := CopyFile(file, file) + if err != nil { + t.Fatal(err) + } + if bytes != 0 { + t.Fatal("Should have written 0 bytes as it is the same file.") + } +} + +// CopyFile with same src and dest but path is different and not clean +func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + defer os.RemoveAll(tempFolder) + if err != nil { + t.Fatal(err) + } + testFolder := path.Join(tempFolder, "test") + err = os.MkdirAll(testFolder, 0740) + if err != nil { + t.Fatal(err) + } + file := path.Join(testFolder, "file") + sameFile := testFolder + "/../test/file" + err = ioutil.WriteFile(file, []byte("content"), 0740) + if err != nil { + t.Fatal(err) + } + bytes, err := CopyFile(file, sameFile) + if err != nil { + t.Fatal(err) + } + if bytes != 0 { + t.Fatal("Should have written 0 bytes as it is the same file.") + } +} + +func TestCopyFile(t *testing.T) { + tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + defer os.RemoveAll(tempFolder) + if err != nil { + t.Fatal(err) + } + src := path.Join(tempFolder, "src") + dest := path.Join(tempFolder, "dest") + ioutil.WriteFile(src, []byte("content"), 0777) + ioutil.WriteFile(dest, []byte("destContent"), 0777) + bytes, err := CopyFile(src, dest) + if err != nil { + t.Fatal(err) + } + if bytes != 7 { + t.Fatalf("Should have written %d bytes but wrote %d", 7, bytes) + } + actual, err := ioutil.ReadFile(dest) + if err != nil { + t.Fatal(err) + } + if string(actual) != "content" { + t.Fatalf("Dest content was '%s', expected '%s'", string(actual), "content") + } +} + // Reading a symlink to a directory must return the directory func TestReadSymlinkedDirectoryExistingDirectory(t *testing.T) { var err error @@ -79,3 +194,164 @@ func TestReadSymlinkedDirectoryToFile(t *testing.T) { t.Errorf("failed to remove symlink: %s", err) } } + +func TestWildcardMatches(t *testing.T) { + match, _ := Matches("fileutils.go", []string{"*"}) + if match != true { + t.Errorf("failed to get a wildcard match, got %v", match) + } +} + +// A simple pattern match should return true. +func TestPatternMatches(t *testing.T) { + match, _ := Matches("fileutils.go", []string{"*.go"}) + if match != true { + t.Errorf("failed to get a match, got %v", match) + } +} + +// An exclusion followed by an inclusion should return true. +func TestExclusionPatternMatchesPatternBefore(t *testing.T) { + match, _ := Matches("fileutils.go", []string{"!fileutils.go", "*.go"}) + if match != true { + t.Errorf("failed to get true match on exclusion pattern, got %v", match) + } +} + +// A folder pattern followed by an exception should return false. +func TestPatternMatchesFolderExclusions(t *testing.T) { + match, _ := Matches("docs/README.md", []string{"docs", "!docs/README.md"}) + if match != false { + t.Errorf("failed to get a false match on exclusion pattern, got %v", match) + } +} + +// A folder pattern followed by an exception should return false. +func TestPatternMatchesFolderWithSlashExclusions(t *testing.T) { + match, _ := Matches("docs/README.md", []string{"docs/", "!docs/README.md"}) + if match != false { + t.Errorf("failed to get a false match on exclusion pattern, got %v", match) + } +} + +// A folder pattern followed by an exception should return false. +func TestPatternMatchesFolderWildcardExclusions(t *testing.T) { + match, _ := Matches("docs/README.md", []string{"docs/*", "!docs/README.md"}) + if match != false { + t.Errorf("failed to get a false match on exclusion pattern, got %v", match) + } +} + +// A pattern followed by an exclusion should return false. +func TestExclusionPatternMatchesPatternAfter(t *testing.T) { + match, _ := Matches("fileutils.go", []string{"*.go", "!fileutils.go"}) + if match != false { + t.Errorf("failed to get false match on exclusion pattern, got %v", match) + } +} + +// A filename evaluating to . should return false. +func TestExclusionPatternMatchesWholeDirectory(t *testing.T) { + match, _ := Matches(".", []string{"*.go"}) + if match != false { + t.Errorf("failed to get false match on ., got %v", match) + } +} + +// A single ! pattern should return an error. +func TestSingleExclamationError(t *testing.T) { + _, err := Matches("fileutils.go", []string{"!"}) + if err == nil { + t.Errorf("failed to get an error for a single exclamation point, got %v", err) + } +} + +// A string preceded with a ! should return true from Exclusion. +func TestExclusion(t *testing.T) { + exclusion := Exclusion("!") + if !exclusion { + t.Errorf("failed to get true for a single !, got %v", exclusion) + } +} + +// Matches with no patterns +func TestMatchesWithNoPatterns(t *testing.T) { + matches, err := Matches("/any/path/there", []string{}) + if err != nil { + t.Fatal(err) + } + if matches { + t.Fatalf("Should not have match anything") + } +} + +// Matches with malformed patterns +func TestMatchesWithMalformedPatterns(t *testing.T) { + matches, err := Matches("/any/path/there", []string{"["}) + if err == nil { + t.Fatal("Should have failed because of a malformed syntax in the pattern") + } + if matches { + t.Fatalf("Should not have match anything") + } +} + +// An empty string should return true from Empty. +func TestEmpty(t *testing.T) { + empty := Empty("") + if !empty { + t.Errorf("failed to get true for an empty string, got %v", empty) + } +} + +func TestCleanPatterns(t *testing.T) { + cleaned, _, _, _ := CleanPatterns([]string{"docs", "config"}) + if len(cleaned) != 2 { + t.Errorf("expected 2 element slice, got %v", len(cleaned)) + } +} + +func TestCleanPatternsStripEmptyPatterns(t *testing.T) { + cleaned, _, _, _ := CleanPatterns([]string{"docs", "config", ""}) + if len(cleaned) != 2 { + t.Errorf("expected 2 element slice, got %v", len(cleaned)) + } +} + +func TestCleanPatternsExceptionFlag(t *testing.T) { + _, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md"}) + if !exceptions { + t.Errorf("expected exceptions to be true, got %v", exceptions) + } +} + +func TestCleanPatternsLeadingSpaceTrimmed(t *testing.T) { + _, _, exceptions, _ := CleanPatterns([]string{"docs", " !docs/README.md"}) + if !exceptions { + t.Errorf("expected exceptions to be true, got %v", exceptions) + } +} + +func TestCleanPatternsTrailingSpaceTrimmed(t *testing.T) { + _, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md "}) + if !exceptions { + t.Errorf("expected exceptions to be true, got %v", exceptions) + } +} + +func TestCleanPatternsErrorSingleException(t *testing.T) { + _, _, _, err := CleanPatterns([]string{"!"}) + if err == nil { + t.Errorf("expected error on single exclamation point, got %v", err) + } +} + +func TestCleanPatternsFolderSplit(t *testing.T) { + _, dirs, _, _ := CleanPatterns([]string{"docs/config/CONFIG.md"}) + if dirs[0][0] != "docs" { + t.Errorf("expected first element in dirs slice to be docs, got %v", dirs[0][1]) + } + if dirs[0][1] != "config" { + t.Errorf("expected first element in dirs slice to be config, got %v", dirs[0][1]) + } +} diff --git a/pkg/graphdb/graphdb_test.go b/pkg/graphdb/graphdb_test.go index 12dd524ed5342ae8da24f1eb4d5fc82bcc923a0a..1cd223bd9cd896db5913587e99008e252c975323 100644 --- a/pkg/graphdb/graphdb_test.go +++ b/pkg/graphdb/graphdb_test.go @@ -52,7 +52,7 @@ func TestGetRootEntity(t *testing.T) { t.Fatal("Entity should not be nil") } if e.ID() != "0" { - t.Fatalf("Enity id should be 0, got %s", e.ID()) + t.Fatalf("Entity id should be 0, got %s", e.ID()) } } @@ -74,7 +74,7 @@ func TestSetDuplicateEntity(t *testing.T) { t.Fatal(err) } if _, err := db.Set("/foo", "43"); err == nil { - t.Fatalf("Creating an entry with a duplciate path did not cause an error") + t.Fatalf("Creating an entry with a duplicate path did not cause an error") } } diff --git a/pkg/jsonlog/jsonlog_marshalling.go b/pkg/jsonlog/jsonlog_marshalling.go index 6244eb01a4fc2b6c7ac9b8cc72590f4d7e6993b9..abaa8a73baab6ca7a7b23d3ee3f9d7544be63846 100644 --- a/pkg/jsonlog/jsonlog_marshalling.go +++ b/pkg/jsonlog/jsonlog_marshalling.go @@ -65,8 +65,7 @@ import ( func (mj *JSONLog) MarshalJSON() ([]byte, error) { var buf bytes.Buffer buf.Grow(1024) - err := mj.MarshalJSONBuf(&buf) - if err != nil { + if err := mj.MarshalJSONBuf(&buf); err != nil { return nil, err } return buf.Bytes(), nil diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go index f2da1cd1b91aa3cafd5a7a86f32407bf4f8e1678..f0d20d99b06c3bdeb02cc694146ad52da7a235ed 100644 --- a/pkg/mflag/flag.go +++ b/pkg/mflag/flag.go @@ -486,8 +486,7 @@ func (f *FlagSet) Set(name, value string) error { if !ok { return fmt.Errorf("no such flag -%v", name) } - err := flag.Value.Set(value) - if err != nil { + if err := flag.Value.Set(value); err != nil { return err } if f.actual == nil { diff --git a/pkg/parsers/filters/parse.go b/pkg/parsers/filters/parse.go index 9c056bb3cf34ac463db6351890b0b36f1fdaf696..df5486d5158010af2b9ea5d81cdccd2feb12e6f9 100644 --- a/pkg/parsers/filters/parse.go +++ b/pkg/parsers/filters/parse.go @@ -58,8 +58,7 @@ func FromParam(p string) (Args, error) { if len(p) == 0 { return args, nil } - err := json.Unmarshal([]byte(p), &args) - if err != nil { + if err := json.NewDecoder(strings.NewReader(p)).Decode(&args); err != nil { return nil, err } return args, nil diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 76a61fa95f0597fc9487fe9869c1ad76da662a87..195a03e9a8ed8e692aaa4b025db3bad36629d2b3 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -23,20 +23,16 @@ func New(quiet bool) *SysInfo { sysInfo := &SysInfo{} if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { if !quiet { - logrus.Warnf("%v", err) + logrus.Warnf("Your kernel does not support cgroup memory limit: %v", err) } } else { - _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) - _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) - sysInfo.MemoryLimit = err1 == nil && err2 == nil - if !sysInfo.MemoryLimit && !quiet { - logrus.Warn("Your kernel does not support cgroup memory limit.") - } + // If memory cgroup is mounted, MemoryLimit is always enabled. + sysInfo.MemoryLimit = true - _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) - sysInfo.SwapLimit = err == nil + _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) + sysInfo.SwapLimit = err1 == nil if !sysInfo.SwapLimit && !quiet { - logrus.Warn("Your kernel does not support cgroup swap limit.") + logrus.Warn("Your kernel does not support swap memory limit.") } } @@ -58,5 +54,11 @@ func New(quiet bool) *SysInfo { } else { sysInfo.AppArmor = true } + + // Check if Devices cgroup is mounted, it is hard requirement for container security. + if _, err := cgroups.FindCgroupMountpoint("devices"); err != nil { + logrus.Fatalf("Error mounting devices cgroup: %v", err) + } + return sysInfo } diff --git a/pkg/system/lstat.go b/pkg/system/lstat.go index a966cd4881b2b29e7adf1ce43c000e058725c7af..d0e43b37097840db364eee335b1e1e516fc36064 100644 --- a/pkg/system/lstat.go +++ b/pkg/system/lstat.go @@ -12,8 +12,7 @@ import ( // Throws an error if the file does not exist func Lstat(path string) (*Stat_t, error) { s := &syscall.Stat_t{} - err := syscall.Lstat(path, s) - if err != nil { + if err := syscall.Lstat(path, s); err != nil { return nil, err } return fromStatT(s) diff --git a/pkg/system/stat_linux.go b/pkg/system/stat_linux.go index 928ba89e698d46c21a2d94e25064b8c8eb1a1c7e..3899b3e0eeac73b10dc4dc6376668ef3e153dcc6 100644 --- a/pkg/system/stat_linux.go +++ b/pkg/system/stat_linux.go @@ -20,8 +20,7 @@ func fromStatT(s *syscall.Stat_t) (*Stat_t, error) { // Throws an error if the file does not exist func Stat(path string) (*Stat_t, error) { s := &syscall.Stat_t{} - err := syscall.Stat(path, s) - if err != nil { + if err := syscall.Stat(path, s); err != nil { return nil, err } return fromStatT(s) diff --git a/pkg/term/winconsole/console_windows_test.go b/pkg/term/winconsole/console_windows_test.go index ee9d96834b8542959fe4e1fd5e5edf0985a170ff..edb5d6f66123a77edb36c66bf509de5eb03b1832 100644 --- a/pkg/term/winconsole/console_windows_test.go +++ b/pkg/term/winconsole/console_windows_test.go @@ -18,7 +18,7 @@ func helpsTestParseInt16OrDefault(t *testing.T, expectedValue int16, shouldFail t.Errorf(format, args) } if expectedValue != value { - t.Errorf("The value returned does not macth expected\n\tExpected:%v\n\t:Actual%v", expectedValue, value) + t.Errorf("The value returned does not match expected\n\tExpected:%v\n\t:Actual%v", expectedValue, value) t.Errorf(format, args) } } diff --git a/pkg/timeoutconn/timeoutconn.go b/pkg/timeoutconn/timeoutconn.go index 3a554559a4a97b63c81aae6b22ac855c6d90c0aa..d9534b5da7500b28a99b19dc066f39deab700c46 100644 --- a/pkg/timeoutconn/timeoutconn.go +++ b/pkg/timeoutconn/timeoutconn.go @@ -17,8 +17,7 @@ type conn struct { func (c *conn) Read(b []byte) (int, error) { if c.timeout > 0 { - err := c.Conn.SetReadDeadline(time.Now().Add(c.timeout)) - if err != nil { + if err := c.Conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil { return 0, err } } diff --git a/registry/registry_test.go b/registry/registry_test.go index b4bd4ee724c79df8855ec6ad98b1053c3f77a1ec..3f63eb6e257fa1ab0660af6e15e7d19d1797decb 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -736,7 +736,7 @@ func TestSearchRepositories(t *testing.T) { } assertEqual(t, results.NumResults, 1, "Expected 1 search results") assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query") - assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' a ot hae 42 stars") + assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars") } func TestValidRemoteName(t *testing.T) { diff --git a/registry/session.go b/registry/session.go index f7358bc1025d68fdebbdba7bef3a5eb960be3136..e65f82cd6103e485555abb91d21156e7dc735038 100644 --- a/registry/session.go +++ b/registry/session.go @@ -597,8 +597,7 @@ func (r *Session) SearchRepositories(term string) (*SearchResults, error) { return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Unexpected status code %d", res.StatusCode), res) } result := new(SearchResults) - err = json.NewDecoder(res.Body).Decode(result) - return result, err + return result, json.NewDecoder(res.Body).Decode(result) } func (r *Session) GetAuthConfig(withPasswd bool) *cliconfig.AuthConfig { diff --git a/registry/session_v2.go b/registry/session_v2.go index a14e434acf43e1b4b243a09f5d3fcf7f79b2f38e..4188e505bda51940850e11a7ccb488453b7e1677 100644 --- a/registry/session_v2.go +++ b/registry/session_v2.go @@ -387,10 +387,8 @@ func (r *Session) GetV2RemoteTags(ep *Endpoint, imageName string, auth *RequestA return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch for %s", res.StatusCode, imageName), res) } - decoder := json.NewDecoder(res.Body) var remote remoteTags - err = decoder.Decode(&remote) - if err != nil { + if err := json.NewDecoder(res.Body).Decode(&remote); err != nil { return nil, fmt.Errorf("Error while decoding the http response: %s", err) } return remote.Tags, nil diff --git a/runconfig/config_test.go b/runconfig/config_test.go index e36dacbf4479575fb8ea11e633d645bb2d503883..87fc6c6aaca34e9c4ed5907e6e0f9a34a6d0ac83 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -104,7 +104,7 @@ func TestParseRunVolumes(t *testing.T) { if config, hostConfig := mustParse(t, "-v /tmp -v /var"); hostConfig.Binds != nil { t.Fatalf("Error parsing volume flags, `-v /tmp -v /var` should not mount-bind anything. Received %v", hostConfig.Binds) } else if _, exists := config.Volumes["/tmp"]; !exists { - t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Recevied %v", config.Volumes) + t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes) } else if _, exists := config.Volumes["/var"]; !exists { t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes) } diff --git a/runconfig/merge.go b/runconfig/merge.go index ce6697dbfc6cf0c9636afcf37b29e82659b31764..9c9a3b436755362f90b9bd67e953678a7f8a9af9 100644 --- a/runconfig/merge.go +++ b/runconfig/merge.go @@ -41,7 +41,7 @@ func Merge(userConf, imageConf *Config) error { } if len(imageConf.PortSpecs) > 0 { // FIXME: I think we can safely remove this. Leaving it for now for the sake of reverse-compat paranoia. - logrus.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", ")) + logrus.Debugf("Migrating image port specs to container: %s", strings.Join(imageConf.PortSpecs, ", ")) if userConf.ExposedPorts == nil { userConf.ExposedPorts = make(nat.PortSet) } diff --git a/runconfig/parse.go b/runconfig/parse.go index 2cdb2d331d7270532ddbf14d2f6a2fece3ad80d9..47feac866a6367fdb96a3cee68e856e23fed34bc 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -277,7 +277,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err) } - restartPolicy, err := parseRestartPolicy(*flRestartPolicy) + restartPolicy, err := ParseRestartPolicy(*flRestartPolicy) if err != nil { return nil, nil, cmd, err } @@ -374,8 +374,8 @@ func convertKVStringsToMap(values []string) map[string]string { return result } -// parseRestartPolicy returns the parsed policy or an error indicating what is incorrect -func parseRestartPolicy(policy string) (RestartPolicy, error) { +// ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect +func ParseRestartPolicy(policy string) (RestartPolicy, error) { p := RestartPolicy{} if policy == "" { diff --git a/trust/trusts.go b/trust/trusts.go index c4a2f4158b21f420651a4e3f9fec4896c96315ce..885127ee5d6f1e9ba2d4a65339b1123788e4c7e0 100644 --- a/trust/trusts.go +++ b/trust/trusts.go @@ -62,8 +62,7 @@ func NewTrustStore(path string) (*TrustStore, error) { baseEndpoints: endpoints, } - err = t.reload() - if err != nil { + if err := t.reload(); err != nil { return nil, err } @@ -170,8 +169,7 @@ func (t *TrustStore) fetch() { continue } // TODO check if value differs - err = ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600) - if err != nil { + if err := ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600); err != nil { logrus.Infof("Error writing trust graph statement: %s", err) } fetchCount++ @@ -180,8 +178,7 @@ func (t *TrustStore) fetch() { if fetchCount > 0 { go func() { - err := t.reload() - if err != nil { + if err := t.reload(); err != nil { logrus.Infof("Reload of trust graph failed: %s", err) } }() diff --git a/utils/git.go b/utils/git.go new file mode 100644 index 0000000000000000000000000000000000000000..18e002d1842100b61737a5c615e21bf98ed62605 --- /dev/null +++ b/utils/git.go @@ -0,0 +1,47 @@ +package utils + +import ( + "fmt" + "io/ioutil" + "net/http" + "os/exec" + "strings" + + "github.com/docker/docker/pkg/urlutil" +) + +func GitClone(remoteURL string) (string, error) { + if !urlutil.IsGitTransport(remoteURL) { + remoteURL = "https://" + remoteURL + } + root, err := ioutil.TempDir("", "docker-build-git") + if err != nil { + return "", err + } + + clone := cloneArgs(remoteURL, root) + + if output, err := exec.Command("git", clone...).CombinedOutput(); err != nil { + return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output) + } + + return root, nil +} + +func cloneArgs(remoteURL, root string) []string { + args := []string{"clone", "--recursive"} + shallow := true + + if strings.HasPrefix(remoteURL, "http") { + res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL)) + if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" { + shallow = false + } + } + + if shallow { + args = append(args, "--depth", "1") + } + + return append(args, remoteURL, root) +} diff --git a/utils/git_test.go b/utils/git_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a82841ae1167e3349ba4e7da03de5d81b15ebb34 --- /dev/null +++ b/utils/git_test.go @@ -0,0 +1,56 @@ +package utils + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" +) + +func TestCloneArgsSmartHttp(t *testing.T) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + serverURL, _ := url.Parse(server.URL) + + serverURL.Path = "/repo.git" + gitURL := serverURL.String() + + mux.HandleFunc("/repo.git/info/refs", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query().Get("service") + w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", q)) + }) + + args := cloneArgs(gitURL, "/tmp") + exp := []string{"clone", "--recursive", "--depth", "1", gitURL, "/tmp"} + if !reflect.DeepEqual(args, exp) { + t.Fatalf("Expected %v, got %v", exp, args) + } +} + +func TestCloneArgsDumbHttp(t *testing.T) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + serverURL, _ := url.Parse(server.URL) + + serverURL.Path = "/repo.git" + gitURL := serverURL.String() + + mux.HandleFunc("/repo.git/info/refs", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + }) + + args := cloneArgs(gitURL, "/tmp") + exp := []string{"clone", "--recursive", gitURL, "/tmp"} + if !reflect.DeepEqual(args, exp) { + t.Fatalf("Expected %v, got %v", exp, args) + } +} +func TestCloneArgsGit(t *testing.T) { + args := cloneArgs("git://github.com/docker/docker", "/tmp") + exp := []string{"clone", "--recursive", "--depth", "1", "git://github.com/docker/docker", "/tmp"} + if !reflect.DeepEqual(args, exp) { + t.Fatalf("Expected %v, got %v", exp, args) + } +} diff --git a/volumes/repository.go b/volumes/repository.go index 0dac3753dad6ac9a5289a08b647f550ba62c7f53..71d6c0ad60afaa0380961e095e9797c45607a882 100644 --- a/volumes/repository.go +++ b/volumes/repository.go @@ -58,7 +58,7 @@ func (r *Repository) newVolume(path string, writable bool) (*Volume, error) { path = filepath.Clean(path) // Ignore the error here since the path may not exist - // Really just want to make sure the path we are using is real(or non-existant) + // Really just want to make sure the path we are using is real(or nonexistent) if cleanPath, err := filepath.EvalSymlinks(path); err == nil { path = cleanPath } diff --git a/volumes/volume.go b/volumes/volume.go index 87aa4ad25afaa5aa1b36997ff884fd6dcb18cf44..5b3b64601822b9c8cb2041e35a16784e084ce395 100644 --- a/volumes/volume.go +++ b/volumes/volume.go @@ -2,7 +2,6 @@ package volumes import ( "encoding/json" - "io/ioutil" "os" "path/filepath" "sync" @@ -81,17 +80,19 @@ func (v *Volume) ToDisk() error { } func (v *Volume) toDisk() error { - data, err := json.Marshal(v) + jsonPath, err := v.jsonPath() if err != nil { return err } - - pth, err := v.jsonPath() + f, err := os.OpenFile(jsonPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } - - return ioutil.WriteFile(pth, data, 0666) + if err := json.NewEncoder(f).Encode(v); err != nil { + f.Close() + return err + } + return f.Close() } func (v *Volume) FromDisk() error { @@ -114,14 +115,38 @@ func (v *Volume) FromDisk() error { } func (v *Volume) jsonPath() (string, error) { - return v.getRootResourcePath("config.json") + return v.GetRootResourcePath("config.json") } -func (v *Volume) getRootResourcePath(path string) (string, error) { + +// Evalutes `path` in the scope of the volume's root path, with proper path +// sanitisation. Symlinks are all scoped to the root of the volume, as +// though the volume's root was `/`. +// +// The volume's root path is the host-facing path of the root of the volume's +// mountpoint inside a container. +// +// NOTE: The returned path is *only* safely scoped inside the volume's root +// if no component of the returned path changes (such as a component +// symlinking to a different path) between using this method and using the +// path. See symlink.FollowSymlinkInScope for more details. +func (v *Volume) GetResourcePath(path string) (string, error) { cleanPath := filepath.Join("/", path) - return symlink.FollowSymlinkInScope(filepath.Join(v.configPath, cleanPath), v.configPath) + return symlink.FollowSymlinkInScope(filepath.Join(v.Path, cleanPath), v.Path) } -func (v *Volume) getResourcePath(path string) (string, error) { +// Evalutes `path` in the scope of the volume's config path, with proper path +// sanitisation. Symlinks are all scoped to the root of the config path, as +// though the config path was `/`. +// +// The config path of a volume is not exposed to the container and is just used +// to store volume configuration options and other internal information. If in +// doubt, you probably want to just use v.GetResourcePath. +// +// NOTE: The returned path is *only* safely scoped inside the volume's config +// path if no component of the returned path changes (such as a component +// symlinking to a different path) between using this method and using the +// path. See symlink.FollowSymlinkInScope for more details. +func (v *Volume) GetRootResourcePath(path string) (string, error) { cleanPath := filepath.Join("/", path) - return symlink.FollowSymlinkInScope(filepath.Join(v.Path, cleanPath), v.Path) + return symlink.FollowSymlinkInScope(filepath.Join(v.configPath, cleanPath), v.configPath) }