From dabdb2f88be6308917a27d58dcec853280c6226c Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Fri, 5 Jul 2013 16:49:55 -0700 Subject: [PATCH 01/80] testing, issue #1104: Make the test use static flags Upstream-commit: 4388bef996063b3b69e738082b6820d3a979921e Component: engine --- components/engine/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/engine/Makefile b/components/engine/Makefile index 9b06df3d64..46d003d878 100644 --- a/components/engine/Makefile +++ b/components/engine/Makefile @@ -11,7 +11,7 @@ BUILD_DIR := $(CURDIR)/.gopath GOPATH ?= $(BUILD_DIR) export GOPATH -GO_OPTIONS ?= +GO_OPTIONS ?= -a -ldflags='-w -d' ifeq ($(VERBOSE), 1) GO_OPTIONS += -v endif @@ -79,10 +79,10 @@ test: tar --exclude=${BUILD_SRC} -cz . | tar -xz -C ${BUILD_PATH} GOPATH=${CURDIR}/${BUILD_SRC} go get -d # Do the test - sudo -E GOPATH=${CURDIR}/${BUILD_SRC} go test ${GO_OPTIONS} + sudo -E GOPATH=${CURDIR}/${BUILD_SRC} CGO_ENABLED=0 go test ${GO_OPTIONS} testall: all - @(cd $(DOCKER_DIR); sudo -E go test ./... $(GO_OPTIONS)) + @(cd $(DOCKER_DIR); CGO_ENABLED=0 sudo -E go test ./... $(GO_OPTIONS)) fmt: @gofmt -s -l -w . From eb5095a47654ef7a38040a3ed1238ca26a7554be Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 17:12:12 -0400 Subject: [PATCH 02/80] Added version checker interface Upstream-commit: 1d01189f04f5187bd39e9212b7af3b3e83e86361 Component: engine --- components/engine/registry/registry.go | 42 +++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index fc84f19ec4..12ca3c4bfb 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -98,6 +98,35 @@ func ResolveRepositoryName(reposName string) (string, string, error) { return endpoint, reposName, err } +// VersionChecker is used to model entities which has a version. +// It is basically a tupple with name and version. +type VersionChecker interface { + Name() string + Version() string +} + +func setUserAgentHeader(req *http.Request, baseVersions []VersionChecker, extra ...VersionChecker) error { + if len(baseVersions)+len(extra) == 0 { + return nil + } + userAgent := make(map[string]string, len(baseVersions)+len(extra)) + + for _, v := range baseVersions { + userAgent[v.Name()] = v.Version() + } + for _, v := range extra { + userAgent[v.Name()] = v.Version() + } + + header, err := json.Marshal(userAgent) + userAgent = nil + if err != nil { + return err + } + req.Header.Set("User-Agent", string(header)) + return nil +} + func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) { for _, cookie := range c.Jar.Cookies(req.URL) { req.AddCookie(cookie) @@ -536,11 +565,12 @@ type ImgData struct { } type Registry struct { - client *http.Client - authConfig *auth.AuthConfig + client *http.Client + authConfig *auth.AuthConfig + baseVersions []VersionChecker } -func NewRegistry(root string, authConfig *auth.AuthConfig) (r *Registry, err error) { +func NewRegistry(root string, authConfig *auth.AuthConfig, baseVersions ...VersionChecker) (r *Registry, err error) { httpTransport := &http.Transport{ DisableKeepAlives: true, Proxy: http.ProxyFromEnvironment, @@ -553,5 +583,9 @@ func NewRegistry(root string, authConfig *auth.AuthConfig) (r *Registry, err err }, } r.client.Jar, err = cookiejar.New(nil) - return r, err + if err != nil { + return nil, err + } + r.baseVersions = baseVersions + return r, nil } From 4c0f88c1901c743206002a19815ab2b6abdff7b7 Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 17:24:54 -0400 Subject: [PATCH 03/80] inserted setUserAgent in each HTTP request Upstream-commit: 1bb8f60d5ae3810b465dd3c79a7a572fb017d078 Component: engine --- components/engine/registry/registry.go | 43 ++++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 12ca3c4bfb..c51df1ac2d 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -105,33 +105,30 @@ type VersionChecker interface { Version() string } -func setUserAgentHeader(req *http.Request, baseVersions []VersionChecker, extra ...VersionChecker) error { - if len(baseVersions)+len(extra) == 0 { - return nil +func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) { + for _, cookie := range c.Jar.Cookies(req.URL) { + req.AddCookie(cookie) } - userAgent := make(map[string]string, len(baseVersions)+len(extra)) + return c.Do(req) +} - for _, v := range baseVersions { +func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { + if len(r.baseVersions)+len(extra) == 0 { + return + } + userAgent := make(map[string]string, len(r.baseVersions)+len(extra)) + + for _, v := range r.baseVersions { userAgent[v.Name()] = v.Version() } for _, v := range extra { userAgent[v.Name()] = v.Version() } - header, err := json.Marshal(userAgent) + header, _ := json.Marshal(userAgent) userAgent = nil - if err != nil { - return err - } req.Header.Set("User-Agent", string(header)) - return nil -} - -func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) { - for _, cookie := range c.Jar.Cookies(req.URL) { - req.AddCookie(cookie) - } - return c.Do(req) + return } // Retrieve the history of a given image from the Registry. @@ -142,6 +139,9 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s return nil, err } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) + if err != nil { + return nil, err + } res, err := r.client.Do(req) if err != nil || res.StatusCode != 200 { if res != nil { @@ -188,6 +188,7 @@ func (r *Registry) GetRemoteImageJSON(imgID, registry string, token []string) ([ return nil, -1, fmt.Errorf("Failed to download json: %s", err) } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) + r.setUserAgent(req, nil) res, err := r.client.Do(req) if err != nil { return nil, -1, fmt.Errorf("Failed to download json: %s", err) @@ -215,6 +216,7 @@ func (r *Registry) GetRemoteImageLayer(imgID, registry string, token []string) ( return nil, fmt.Errorf("Error while getting from the server: %s\n", err) } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) + r.setUserAgent(req, nil) res, err := r.client.Do(req) if err != nil { return nil, err @@ -235,6 +237,7 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ return nil, err } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) + r.setUserAgent(req, nil) res, err := r.client.Do(req) if err != nil { return nil, err @@ -273,6 +276,7 @@ func (r *Registry) GetRepositoryData(indexEp, remote string) (*RepositoryData, e req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) } req.Header.Set("X-Docker-Token", "true") + r.setUserAgent(req, nil) res, err := r.client.Do(req) if err != nil { @@ -336,6 +340,7 @@ func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regis req.Header.Add("Content-type", "application/json") req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) req.Header.Set("X-Docker-Checksum", imgData.Checksum) + r.setUserAgent(req, nil) utils.Debugf("Setting checksum for %s: %s", imgData.ID, imgData.Checksum) res, err := doWithCookies(r.client, req) @@ -370,6 +375,7 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr req.ContentLength = -1 req.TransferEncoding = []string{"chunked"} req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) + r.setUserAgent(req, nil) res, err := doWithCookies(r.client, req) if err != nil { return fmt.Errorf("Failed to upload layer: %s", err) @@ -407,6 +413,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token } req.Header.Add("Content-type", "application/json") req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) + r.setUserAgent(req, nil) req.ContentLength = int64(len(revision)) res, err := doWithCookies(r.client, req) if err != nil { @@ -439,6 +446,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.ContentLength = int64(len(imgListJSON)) req.Header.Set("X-Docker-Token", "true") + r.setUserAgent(req, nil) if validate { req.Header["X-Docker-Endpoints"] = regs } @@ -459,6 +467,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.ContentLength = int64(len(imgListJSON)) req.Header.Set("X-Docker-Token", "true") + r.setUserAgent(req, nil) if validate { req.Header["X-Docker-Endpoints"] = regs } From d233f1b215f2db02d3a0fcae7e70e81f8f0b503a Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 17:33:28 -0400 Subject: [PATCH 04/80] added APIVersion when call NewRegistry Upstream-commit: 65185a565b8e05a2dd58e10d1c1ad560f4a255cf Component: engine --- components/engine/api_params.go | 12 ++++++++++++ components/engine/server.go | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/components/engine/api_params.go b/components/engine/api_params.go index b8af690c7f..217062b66f 100644 --- a/components/engine/api_params.go +++ b/components/engine/api_params.go @@ -66,6 +66,18 @@ type APIVersion struct { GoVersion string `json:",omitempty"` } +func (v *APIVersion) Name() string { + return "docker" +} + +func (v *APIVersion) Version() string { + r, err := json.Marshal(v) + if err != nil { + return r.Version + } + return string(r) +} + type APIWait struct { StatusCode int } diff --git a/components/engine/server.go b/components/engine/server.go index f1c0909516..c1ea5670e0 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -55,7 +55,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { } func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { - r, err := registry.NewRegistry(srv.runtime.root, nil) + r, err := registry.NewRegistry(srv.runtime.root, nil, srv.DockerVersion()) if err != nil { return nil, err } @@ -470,7 +470,7 @@ func (srv *Server) poolRemove(kind, key string) error { } func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error { - r, err := registry.NewRegistry(srv.runtime.root, authConfig) + r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.DockerVersion()) if err != nil { return err } @@ -687,7 +687,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo out = utils.NewWriteFlusher(out) img, err := srv.runtime.graph.Get(localName) - r, err2 := registry.NewRegistry(srv.runtime.root, authConfig) + r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.DockerVersion()) if err2 != nil { return err2 } From addc24cbb83e8eded667e3d1238e460512a67a92 Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 17:42:04 -0400 Subject: [PATCH 05/80] Insert version checkers when call NewRegistry() Upstream-commit: 5705a493080ba5571a7929d37e7926678a982cb4 Component: engine --- components/engine/api_params.go | 12 ------------ components/engine/server.go | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/components/engine/api_params.go b/components/engine/api_params.go index 217062b66f..b8af690c7f 100644 --- a/components/engine/api_params.go +++ b/components/engine/api_params.go @@ -66,18 +66,6 @@ type APIVersion struct { GoVersion string `json:",omitempty"` } -func (v *APIVersion) Name() string { - return "docker" -} - -func (v *APIVersion) Version() string { - r, err := json.Marshal(v) - if err != nil { - return r.Version - } - return string(r) -} - type APIWait struct { StatusCode int } diff --git a/components/engine/server.go b/components/engine/server.go index c1ea5670e0..b943ef2985 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -26,6 +26,33 @@ func (srv *Server) DockerVersion() APIVersion { } } +type plainVersionChecker struct { + name string + version string +} + +func (v *plainVersionChecker) Name() string { + return v.name +} + +func (v *plainVersionChecker) Version() string { + return v.version +} + +func (srv *Server) versionCheckers() []registry.VersionChecker { + v := srv.DockerVersion() + ret := make([]registry.VersionChecker, 0, 3) + ret = append(ret, &plainVersionChecker{"docker", v.Version}) + + if len(v.GoVersion) > 0 { + ret = append(ret, &plainVersionChecker{"go", v.GoVersion}) + } + if len(v.GitCommit) > 0 { + ret = append(ret, &plainVersionChecker{"git-commit", v.GitCommit}) + } + return ret +} + func (srv *Server) ContainerKill(name string) error { if container := srv.runtime.Get(name); container != nil { if err := container.Kill(); err != nil { @@ -55,7 +82,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { } func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { - r, err := registry.NewRegistry(srv.runtime.root, nil, srv.DockerVersion()) + r, err := registry.NewRegistry(srv.runtime.root, nil, srv.versionCheckers()...) if err != nil { return nil, err } @@ -470,7 +497,7 @@ func (srv *Server) poolRemove(kind, key string) error { } func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error { - r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.DockerVersion()) + r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.versionCheckers()...) if err != nil { return err } @@ -687,7 +714,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo out = utils.NewWriteFlusher(out) img, err := srv.runtime.graph.Get(localName) - r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.DockerVersion()) + r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.versionCheckers()...) if err2 != nil { return err2 } From 5042ef8b4a91c45026c048bf8cc37bd6b0552940 Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 17:48:37 -0400 Subject: [PATCH 06/80] added client's kernel version Upstream-commit: d40efc4648af6bb5c60b37a789effd602af1f132 Component: engine --- components/engine/registry/registry.go | 24 +++++++++++++++--------- components/engine/server.go | 7 ++++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index c51df1ac2d..683a64ab6a 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -119,9 +119,15 @@ func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { userAgent := make(map[string]string, len(r.baseVersions)+len(extra)) for _, v := range r.baseVersions { + if v == nil { + continue + } userAgent[v.Name()] = v.Version() } for _, v := range extra { + if v == nil { + continue + } userAgent[v.Name()] = v.Version() } @@ -188,7 +194,7 @@ func (r *Registry) GetRemoteImageJSON(imgID, registry string, token []string) ([ return nil, -1, fmt.Errorf("Failed to download json: %s", err) } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) - r.setUserAgent(req, nil) + r.setUserAgent(req) res, err := r.client.Do(req) if err != nil { return nil, -1, fmt.Errorf("Failed to download json: %s", err) @@ -216,7 +222,7 @@ func (r *Registry) GetRemoteImageLayer(imgID, registry string, token []string) ( return nil, fmt.Errorf("Error while getting from the server: %s\n", err) } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) - r.setUserAgent(req, nil) + r.setUserAgent(req) res, err := r.client.Do(req) if err != nil { return nil, err @@ -237,7 +243,7 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ return nil, err } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) - r.setUserAgent(req, nil) + r.setUserAgent(req) res, err := r.client.Do(req) if err != nil { return nil, err @@ -276,7 +282,7 @@ func (r *Registry) GetRepositoryData(indexEp, remote string) (*RepositoryData, e req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) } req.Header.Set("X-Docker-Token", "true") - r.setUserAgent(req, nil) + r.setUserAgent(req) res, err := r.client.Do(req) if err != nil { @@ -340,7 +346,7 @@ func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regis req.Header.Add("Content-type", "application/json") req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) req.Header.Set("X-Docker-Checksum", imgData.Checksum) - r.setUserAgent(req, nil) + r.setUserAgent(req) utils.Debugf("Setting checksum for %s: %s", imgData.ID, imgData.Checksum) res, err := doWithCookies(r.client, req) @@ -375,7 +381,7 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr req.ContentLength = -1 req.TransferEncoding = []string{"chunked"} req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) - r.setUserAgent(req, nil) + r.setUserAgent(req) res, err := doWithCookies(r.client, req) if err != nil { return fmt.Errorf("Failed to upload layer: %s", err) @@ -413,7 +419,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token } req.Header.Add("Content-type", "application/json") req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) - r.setUserAgent(req, nil) + r.setUserAgent(req) req.ContentLength = int64(len(revision)) res, err := doWithCookies(r.client, req) if err != nil { @@ -446,7 +452,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.ContentLength = int64(len(imgListJSON)) req.Header.Set("X-Docker-Token", "true") - r.setUserAgent(req, nil) + r.setUserAgent(req) if validate { req.Header["X-Docker-Endpoints"] = regs } @@ -467,7 +473,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.ContentLength = int64(len(imgListJSON)) req.Header.Set("X-Docker-Token", "true") - r.setUserAgent(req, nil) + r.setUserAgent(req) if validate { req.Header["X-Docker-Endpoints"] = regs } diff --git a/components/engine/server.go b/components/engine/server.go index b943ef2985..70bb0bb871 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -41,7 +41,7 @@ func (v *plainVersionChecker) Version() string { func (srv *Server) versionCheckers() []registry.VersionChecker { v := srv.DockerVersion() - ret := make([]registry.VersionChecker, 0, 3) + ret := make([]registry.VersionChecker, 0, 4) ret = append(ret, &plainVersionChecker{"docker", v.Version}) if len(v.GoVersion) > 0 { @@ -50,6 +50,11 @@ func (srv *Server) versionCheckers() []registry.VersionChecker { if len(v.GitCommit) > 0 { ret = append(ret, &plainVersionChecker{"git-commit", v.GitCommit}) } + kernelVersion, err := utils.GetKernelVersion() + if err == nil { + ret = append(ret, &plainVersionChecker{"kernel", kernelVersion.String()}) + } + return ret } From 9939aac9f56d4e9b38d2a64ccf374ea7a5b178ff Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 18:45:45 -0400 Subject: [PATCH 07/80] Removed an unnecessary error check. Upstream-commit: 26c8eae6fea53e9a78bd035614fff20086f00b17 Component: engine --- components/engine/registry/registry.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 683a64ab6a..26cefbbdef 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -145,9 +145,7 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s return nil, err } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) - if err != nil { - return nil, err - } + r.setUserAgent(req) res, err := r.client.Do(req) if err != nil || res.StatusCode != 200 { if res != nil { From 81ebd3a19cdb9bffe32e944be596042fb71ff38e Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 18:46:25 -0400 Subject: [PATCH 08/80] Removed an unnecessary nil assignment Upstream-commit: e832b01349fec2acee6ec3219dc9bfb61ad38764 Component: engine --- components/engine/registry/registry.go | 1 - 1 file changed, 1 deletion(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 26cefbbdef..920f945935 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -132,7 +132,6 @@ func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { } header, _ := json.Marshal(userAgent) - userAgent = nil req.Header.Set("User-Agent", string(header)) return } From 98e450c93d6ab6c0872537e5aad94dd1d4c0b79d Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Fri, 28 Jun 2013 19:29:02 -0400 Subject: [PATCH 09/80] format in the user agent header should follow RFC 2616 Upstream-commit: 34cf976866f66bc77b961f4e66a9dd8aad1ffb00 Component: engine --- components/engine/registry/registry.go | 59 +++++++++++++++++--------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 920f945935..0840ffbb83 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -116,23 +116,9 @@ func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { if len(r.baseVersions)+len(extra) == 0 { return } - userAgent := make(map[string]string, len(r.baseVersions)+len(extra)) - for _, v := range r.baseVersions { - if v == nil { - continue - } - userAgent[v.Name()] = v.Version() - } - for _, v := range extra { - if v == nil { - continue - } - userAgent[v.Name()] = v.Version() - } - - header, _ := json.Marshal(userAgent) - req.Header.Set("User-Agent", string(header)) + userAgent := appendVersions(r.baseVersionsStr, extra...) + req.Header.Set("User-Agent", userAgent) return } @@ -577,9 +563,43 @@ type ImgData struct { } type Registry struct { - client *http.Client - authConfig *auth.AuthConfig - baseVersions []VersionChecker + client *http.Client + authConfig *auth.AuthConfig + baseVersions []VersionChecker + baseVersionsStr string +} + +func validVersion(version VersionChecker) bool { + stopChars := " \t\r\n/" + if strings.ContainsAny(version.Name(), stopChars) { + return false + } + if strings.ContainsAny(version.Version(), stopChars) { + return false + } + return true +} + +func appendVersions(base string, versions ...VersionChecker) string { + if len(versions) == 0 { + return base + } + + var buf bytes.Buffer + if len(base) > 0 { + buf.Write([]byte(base)) + } + + for _, v := range versions { + if !validVersion(v) { + continue + } + buf.Write([]byte(v.Name())) + buf.Write([]byte("/")) + buf.Write([]byte(v.Version())) + buf.Write([]byte(" ")) + } + return buf.String() } func NewRegistry(root string, authConfig *auth.AuthConfig, baseVersions ...VersionChecker) (r *Registry, err error) { @@ -599,5 +619,6 @@ func NewRegistry(root string, authConfig *auth.AuthConfig, baseVersions ...Versi return nil, err } r.baseVersions = baseVersions + r.baseVersionsStr = appendVersions("", baseVersions...) return r, nil } From ebb59e49d1ecaef26f87222c1295a07672b653eb Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Mon, 1 Jul 2013 17:57:56 -0400 Subject: [PATCH 10/80] reduce the number of string copy operations. Upstream-commit: 73e79a3310f3976b61a295f45e12aead9af41962 Component: engine --- components/engine/registry/registry.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 0840ffbb83..03a2890105 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -116,9 +116,11 @@ func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { if len(r.baseVersions)+len(extra) == 0 { return } - - userAgent := appendVersions(r.baseVersionsStr, extra...) - req.Header.Set("User-Agent", userAgent) + if len(extra) == 0 { + req.Header.Set("User-Agent", r.baseVersionsStr) + } else { + req.Header.Set("User-Agent", appendVersions(r.baseVersionsStr, extra...)) + } return } From 750c01d0afc5a20233dc7e84a72b09e66c31902b Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 17:18:28 +0000 Subject: [PATCH 11/80] wip Upstream-commit: 941e3e2ef09092306a7a287ce62b6fb518af9c56 Component: engine --- components/engine/container.go | 18 ++++++++--- components/engine/runtime.go | 4 +-- components/engine/utils/utils.go | 54 ++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index 48661a3098..e54440a5ae 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -617,13 +617,21 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.cmd = exec.Command("lxc-start", params...) // Setup logging of stdout and stderr to disk - if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout")); err != nil { + /* + if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout"), ""); err != nil { return err } - if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr")); err != nil { + if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr"), ""); err != nil { return err } - + */ + if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { + return err + } + if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil { + return err + } + var err error if container.Config.Tty { err = container.startPty() @@ -678,13 +686,13 @@ func (container *Container) StdinPipe() (io.WriteCloser, error) { func (container *Container) StdoutPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() - container.stdout.AddWriter(writer) + container.stdout.AddWriter(writer, "") return utils.NewBufReader(reader), nil } func (container *Container) StderrPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() - container.stderr.AddWriter(writer) + container.stderr.AddWriter(writer, "") return utils.NewBufReader(reader), nil } diff --git a/components/engine/runtime.go b/components/engine/runtime.go index 5b0f7b2b2a..d73dd16f70 100644 --- a/components/engine/runtime.go +++ b/components/engine/runtime.go @@ -168,12 +168,12 @@ func (runtime *Runtime) Register(container *Container) error { return nil } -func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst string) error { +func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error { log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) if err != nil { return err } - src.AddWriter(log) + src.AddWriter(log, stream) return nil } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index df615844a7..3139e380a6 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -247,30 +247,52 @@ func (r *bufReader) Close() error { type WriteBroadcaster struct { sync.Mutex - writers map[io.WriteCloser]struct{} + writers map[StreamWriter][]byte } -func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser) { +type StreamWriter struct { + wc io.WriteCloser + stream string +} + +func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser, stream string) { w.Lock() - w.writers[writer] = struct{}{} + sw := StreamWriter{wc: writer, stream: stream} + w.writers[sw] = []byte{} w.Unlock() } -// FIXME: Is that function used? -// FIXME: This relies on the concrete writer type used having equality operator -func (w *WriteBroadcaster) RemoveWriter(writer io.WriteCloser) { - w.Lock() - delete(w.writers, writer) - w.Unlock() +type JSONLog struct { + Log string `json:"log,omitempty"` + Stream string `json:"stream,omitempty"` + Created time.Time `json:"time"` } func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { w.Lock() defer w.Unlock() - for writer := range w.writers { - if n, err := writer.Write(p); err != nil || n != len(p) { + for sw := range w.writers { + lp := p + if sw.stream != "" { + w.writers[sw] = append(w.writers[sw], p...) + s := string(p) + if s[len(s)-1] == '\n' { + /* lp, err = json.Marshal(&JSONLog{Log: s, Stream: sw.stream, Created: time.Now()}) + if err != nil { + // On error, evict the writer + delete(w.writers, sw) + continue + } + */ + lp = []byte("[" + time.Now().String() + "] [" + sw.stream + "] " + s) + w.writers[sw] = []byte{} + } else { + continue + } + } + if n, err := sw.wc.Write(lp); err != nil || n != len(lp) { // On error, evict the writer - delete(w.writers, writer) + delete(w.writers, sw) } } return len(p), nil @@ -279,15 +301,15 @@ func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { func (w *WriteBroadcaster) CloseWriters() error { w.Lock() defer w.Unlock() - for writer := range w.writers { - writer.Close() + for sw := range w.writers { + sw.wc.Close() } - w.writers = make(map[io.WriteCloser]struct{}) + w.writers = make(map[StreamWriter][]byte) return nil } func NewWriteBroadcaster() *WriteBroadcaster { - return &WriteBroadcaster{writers: make(map[io.WriteCloser]struct{})} + return &WriteBroadcaster{writers: make(map[StreamWriter][]byte)} } func GetTotalUsedFds() int { From 015f243412c98b55fd6a1b742b814bddc7effe92 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 15:12:25 -0900 Subject: [PATCH 12/80] Add verbose output to docker build Verbose output is enabled by default and the flag -q can be used to suppress the verbose output. Upstream-commit: 474191dd7bca9eedaccb9de1771eecfce7dfebbb Component: engine --- components/engine/api.go | 9 ++++++++- components/engine/buildfile.go | 11 ++++++++++- components/engine/buildfile_test.go | 2 +- components/engine/commands.go | 6 ++++++ .../engine/docs/sources/commandline/command/build.rst | 1 + 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index c4a5222dc6..9e51f4de77 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -756,6 +756,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } remoteURL := r.FormValue("remote") repoName := r.FormValue("t") + rawSuppressOutput := r.FormValue("q") tag := "" if strings.Contains(repoName, ":") { remoteParts := strings.Split(repoName, ":") @@ -802,7 +803,13 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } context = c } - b := NewBuildFile(srv, utils.NewWriteFlusher(w)) + + suppressOutput, err := getBoolParam(rawSuppressOutput) + if err != nil { + return err + } + + b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput) id, err := b.Build(context) if err != nil { fmt.Fprintf(w, "Error build: %s\n", err) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 38e6c330d6..02ef00a854 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -28,6 +28,7 @@ type buildFile struct { maintainer string config *Config context string + verbose bool lastContainer *Container tmpContainers map[string]struct{} @@ -303,6 +304,13 @@ func (b *buildFile) run() (string, error) { return "", err } + if b.verbose { + err = <-c.Attach(nil, nil, b.out, b.out) + if err != nil { + return "", err + } + } + // Wait for it to finish if ret := c.Wait(); ret != 0 { return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret) @@ -450,7 +458,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) { return "", fmt.Errorf("An error occured during the build\n") } -func NewBuildFile(srv *Server, out io.Writer) BuildFile { +func NewBuildFile(srv *Server, out io.Writer, verbose bool) BuildFile { return &buildFile{ builder: NewBuilder(srv.runtime), runtime: srv.runtime, @@ -459,5 +467,6 @@ func NewBuildFile(srv *Server, out io.Writer) BuildFile { out: out, tmpContainers: make(map[string]struct{}), tmpImages: make(map[string]struct{}), + verbose: verbose, } } diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 9250f73765..0bd5a3d1c4 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -117,7 +117,7 @@ func TestBuild(t *testing.T) { pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard) + buildfile := NewBuildFile(srv, ioutil.Discard, false) if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil { t.Fatal(err) } diff --git a/components/engine/commands.go b/components/engine/commands.go index feab558259..4a69fb9108 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -157,6 +157,8 @@ func mkBuildContext(dockerfile string, files [][2]string) (Archive, error) { func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") + suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") + if err := cmd.Parse(args); err != nil { return nil } @@ -194,6 +196,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // Upload the build context v := &url.Values{} v.Set("t", *tag) + + if *suppressOutput { + v.Set("q", "1") + } if isRemote { v.Set("remote", cmd.Arg(0)) } diff --git a/components/engine/docs/sources/commandline/command/build.rst b/components/engine/docs/sources/commandline/command/build.rst index 1645002ba2..45b6d2ec8e 100644 --- a/components/engine/docs/sources/commandline/command/build.rst +++ b/components/engine/docs/sources/commandline/command/build.rst @@ -11,6 +11,7 @@ Usage: docker build [OPTIONS] PATH | URL | - Build a new container image from the source code at PATH -t="": Tag to be applied to the resulting image in case of success. + -q=false: Suppress verbose build output. When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context From 7bf5a45cfe77c9237723e1a4fccaff0a4e461fc4 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 15:37:26 -0900 Subject: [PATCH 13/80] Fix buildfile tests after rebase Upstream-commit: 49044a96089487b9df075fa972e83e4c05c7fae8 Component: engine --- components/engine/buildfile_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 0bd5a3d1c4..514d2f89dc 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -137,7 +137,7 @@ func TestVolume(t *testing.T) { pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard) + buildfile := NewBuildFile(srv, ioutil.Discard, false) imgId, err := buildfile.Build(mkTestContext(` from %s VOLUME /test @@ -153,7 +153,7 @@ CMD Hello world if len(img.Config.Volumes) == 0 { t.Fail() } - for key, _ := range img.Config.Volumes { + for key := range img.Config.Volumes { if key != "/test" { t.Fail() } From 89646a08c959d6720c4d7e6f92490179f5fe464d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 15:52:08 -0900 Subject: [PATCH 14/80] Revert changes from PR 1030 With streaming output of the build changes in 1030 are no longer required. Upstream-commit: 1104d443cc49fd2a6b9c94a2c9724468f9860799 Component: engine --- components/engine/buildfile.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 02ef00a854..7ade058c69 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -30,7 +30,6 @@ type buildFile struct { context string verbose bool - lastContainer *Container tmpContainers map[string]struct{} tmpImages map[string]struct{} @@ -255,7 +254,6 @@ func (b *buildFile) CmdAdd(args string) error { return err } b.tmpContainers[container.ID] = struct{}{} - b.lastContainer = container if err := container.EnsureMounted(); err != nil { return err @@ -291,7 +289,6 @@ func (b *buildFile) run() (string, error) { return "", err } b.tmpContainers[c.ID] = struct{}{} - b.lastContainer = c fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID)) // override the entry point that may have been picked up from the base image @@ -345,7 +342,6 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { return err } b.tmpContainers[container.ID] = struct{}{} - b.lastContainer = container fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID)) id = container.ID if err := container.EnsureMounted(); err != nil { @@ -373,29 +369,6 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { } func (b *buildFile) Build(context io.Reader) (string, error) { - defer func() { - // If we have an error and a container, the display the logs - if b.lastContainer != nil { - fmt.Fprintf(b.out, "******** Logs from last container (%s) *******\n", b.lastContainer.ShortID()) - - cLog, err := b.lastContainer.ReadLog("stdout") - if err != nil { - utils.Debugf("Error reading logs (stdout): %s", err) - } - if _, err := io.Copy(b.out, cLog); err != nil { - utils.Debugf("Error streaming logs (stdout): %s", err) - } - cLog, err = b.lastContainer.ReadLog("stderr") - if err != nil { - utils.Debugf("Error reading logs (stderr): %s", err) - } - if _, err := io.Copy(b.out, cLog); err != nil { - utils.Debugf("Error streaming logs (stderr): %s", err) - } - fmt.Fprintf(b.out, "************* End of logs for %s *************\n", b.lastContainer.ShortID()) - } - }() - // FIXME: @creack any reason for using /tmp instead of ""? // FIXME: @creack "name" is a terrible variable name name, err := ioutil.TempDir("/tmp", "docker-build") @@ -448,7 +421,6 @@ func (b *buildFile) Build(context io.Reader) (string, error) { return "", ret.(error) } - b.lastContainer = nil fmt.Fprintf(b.out, " ---> %v\n", utils.TruncateID(b.image)) } if b.image != "" { From 3442b5e984e38f6d8005ccfb7706ffb264490c67 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 12 Jul 2013 06:22:56 -0900 Subject: [PATCH 15/80] Add param to api docs for verbose build output Upstream-commit: d0c73c28df5627bc7158fd54860f6751b4dab0f9 Component: engine --- components/engine/docs/sources/api/docker_remote_api_v1.3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index b0955ce496..7f7c7db8a8 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst @@ -881,6 +881,7 @@ Build an image from Dockerfile via stdin The Content-type header should be set to "application/tar". :query t: tag to be applied to the resulting image in case of success + :query q: suppress verbose build output :statuscode 200: no error :statuscode 500: server error From f649b0a0574b3d7ce1a15d6177a35ed5b9000888 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Fri, 12 Jul 2013 13:55:26 -0400 Subject: [PATCH 16/80] updated the help commands on a few commands that were not correct Upstream-commit: 4174e7aa7a6600a2cfedd1c568000556cf1daf79 Component: engine --- components/engine/commands.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b3b1a57199..20195e2e39 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -469,7 +469,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error { cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container") + nSeconds := cmd.Int("t", 10, "wait t seconds before stopping the container") if err := cmd.Parse(args); err != nil { return nil } @@ -494,7 +494,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error { cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container") + nSeconds := cmd.Int("t", 10, "wait t seconds before restarting the container") if err := cmd.Parse(args); err != nil { return nil } @@ -773,7 +773,7 @@ func (cli *DockerCli) CmdImport(args ...string) error { } func (cli *DockerCli) CmdPush(args ...string) error { - cmd := Subcmd("push", "[OPTION] NAME", "Push an image or a repository to the registry") + cmd := Subcmd("push", "NAME", "Push an image or a repository to the registry") if err := cmd.Parse(args); err != nil { return nil } From aa8559ee188aaba02cdb1e0227027180d4d0fe12 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Fri, 12 Jul 2013 14:05:26 -0400 Subject: [PATCH 17/80] updated the rmi command docs, the had typos Upstream-commit: 364f48d6c7386874ee3fa0696b7a466f76fcf698 Component: engine --- components/engine/docs/sources/commandline/command/rmi.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/docs/sources/commandline/command/rmi.rst b/components/engine/docs/sources/commandline/command/rmi.rst index a0131886d6..954e5222c6 100644 --- a/components/engine/docs/sources/commandline/command/rmi.rst +++ b/components/engine/docs/sources/commandline/command/rmi.rst @@ -8,6 +8,6 @@ :: - Usage: docker rmimage [OPTIONS] IMAGE + Usage: docker rmi IMAGE [IMAGE...] - Remove an image + Remove one or more images From 1aa023548232481c3251864f39b614eeb78db3f5 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Fri, 12 Jul 2013 17:56:55 -0700 Subject: [PATCH 18/80] Hack: use helper functions in tests for less copy-pasting Upstream-commit: 080243f0407a90cdacf128dc3b53a802549d7797 Component: engine --- components/engine/container_test.go | 117 ++++------------------------ components/engine/runtime_test.go | 72 ++--------------- components/engine/utils_test.go | 25 ++++-- 3 files changed, 39 insertions(+), 175 deletions(-) diff --git a/components/engine/container_test.go b/components/engine/container_test.go index e7f6818eb6..bc1eaf99ec 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -39,16 +39,11 @@ func TestIDFormat(t *testing.T) { func TestMultipleAttachRestart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( - &Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"/bin/sh", "-c", - "i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"}, - }, + container, hostConfig, _ := mkContainer( + runtime, + []string{"_", "/bin/sh", "-c", "i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"}, + t, ) - if err != nil { - t.Fatal(err) - } defer runtime.Destroy(container) // Simulate 3 client attaching to the container and stop/restart @@ -65,7 +60,6 @@ func TestMultipleAttachRestart(t *testing.T) { if err != nil { t.Fatal(err) } - hostConfig := &HostConfig{} if err := container.Start(hostConfig); err != nil { t.Fatal(err) } @@ -140,19 +134,8 @@ func TestMultipleAttachRestart(t *testing.T) { func TestDiff(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - - builder := NewBuilder(runtime) - // Create a container and remove a file - container1, err := builder.Create( - &Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"/bin/rm", "/etc/passwd"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t) defer runtime.Destroy(container1) if err := container1.Run(); err != nil { @@ -185,15 +168,7 @@ func TestDiff(t *testing.T) { } // Create a new container from the commited image - container2, err := builder.Create( - &Config{ - Image: img.ID, - Cmd: []string{"cat", "/etc/passwd"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container2, _, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t) defer runtime.Destroy(container2) if err := container2.Run(); err != nil { @@ -212,15 +187,7 @@ func TestDiff(t *testing.T) { } // Create a new containere - container3, err := builder.Create( - &Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"rm", "/bin/httpd"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container3, _, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t) defer runtime.Destroy(container3) if err := container3.Run(); err != nil { @@ -246,17 +213,7 @@ func TestDiff(t *testing.T) { func TestCommitAutoRun(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - - builder := NewBuilder(runtime) - container1, err := builder.Create( - &Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"/bin/sh", "-c", "echo hello > /world"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container1, _, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t) defer runtime.Destroy(container1) if container1.State.Running { @@ -279,14 +236,7 @@ func TestCommitAutoRun(t *testing.T) { } // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world - container2, err := builder.Create( - &Config{ - Image: img.ID, - }, - ) - if err != nil { - t.Fatal(err) - } + container2, hostConfig, _ := mkContainer(runtime, []string{img.ID}, t) defer runtime.Destroy(container2) stdout, err := container2.StdoutPipe() if err != nil { @@ -296,7 +246,6 @@ func TestCommitAutoRun(t *testing.T) { if err != nil { t.Fatal(err) } - hostConfig := &HostConfig{} if err := container2.Start(hostConfig); err != nil { t.Fatal(err) } @@ -324,17 +273,7 @@ func TestCommitRun(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - - container1, err := builder.Create( - &Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"/bin/sh", "-c", "echo hello > /world"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container1, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t) defer runtime.Destroy(container1) if container1.State.Running { @@ -357,16 +296,7 @@ func TestCommitRun(t *testing.T) { } // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world - - container2, err := builder.Create( - &Config{ - Image: img.ID, - Cmd: []string{"cat", "/world"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container2, hostConfig, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t) defer runtime.Destroy(container2) stdout, err := container2.StdoutPipe() if err != nil { @@ -376,7 +306,6 @@ func TestCommitRun(t *testing.T) { if err != nil { t.Fatal(err) } - hostConfig := &HostConfig{} if err := container2.Start(hostConfig); err != nil { t.Fatal(err) } @@ -403,18 +332,7 @@ func TestCommitRun(t *testing.T) { func TestStart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( - &Config{ - Image: GetTestImage(runtime).ID, - Memory: 33554432, - CpuShares: 1000, - Cmd: []string{"/bin/cat"}, - OpenStdin: true, - }, - ) - if err != nil { - t.Fatal(err) - } + container, hostConfig, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t) defer runtime.Destroy(container) cStdin, err := container.StdinPipe() @@ -422,7 +340,6 @@ func TestStart(t *testing.T) { t.Fatal(err) } - hostConfig := &HostConfig{} if err := container.Start(hostConfig); err != nil { t.Fatal(err) } @@ -445,15 +362,7 @@ func TestStart(t *testing.T) { func TestRun(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( - &Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"ls", "-al"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container) if container.State.Running { diff --git a/components/engine/runtime_test.go b/components/engine/runtime_test.go index 9d43bd46e5..66d92c8100 100644 --- a/components/engine/runtime_test.go +++ b/components/engine/runtime_test.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/dotcloud/docker/utils" "io" - "io/ioutil" "log" "net" "os" @@ -247,36 +246,13 @@ func TestGet(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - - container1, err := builder.Create(&Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"ls", "-al"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container1, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container1) - container2, err := builder.Create(&Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"ls", "-al"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container2, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container2) - container3, err := builder.Create(&Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"ls", "-al"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container3, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container3) if runtime.Get(container1.ID) != container1 { @@ -431,46 +407,14 @@ func TestAllocateUDPPortLocalhost(t *testing.T) { } func TestRestore(t *testing.T) { - - root, err := ioutil.TempDir("", "docker-test") - if err != nil { - t.Fatal(err) - } - if err := os.Remove(root); err != nil { - t.Fatal(err) - } - if err := utils.CopyDirectory(unitTestStoreBase, root); err != nil { - t.Fatal(err) - } - - runtime1, err := NewRuntimeFromDirectory(root, false) - if err != nil { - t.Fatal(err) - } - - builder := NewBuilder(runtime1) - + runtime1 := mkRuntime(t) + defer nuke(runtime1) // Create a container with one instance of docker - container1, err := builder.Create(&Config{ - Image: GetTestImage(runtime1).ID, - Cmd: []string{"ls", "-al"}, - }, - ) - if err != nil { - t.Fatal(err) - } + container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t) defer runtime1.Destroy(container1) // Create a second container meant to be killed - container2, err := builder.Create(&Config{ - Image: GetTestImage(runtime1).ID, - Cmd: []string{"/bin/cat"}, - OpenStdin: true, - }, - ) - if err != nil { - t.Fatal(err) - } + container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t) defer runtime1.Destroy(container2) // Start the container non blocking @@ -505,7 +449,7 @@ func TestRestore(t *testing.T) { // Here are are simulating a docker restart - that is, reloading all containers // from scratch - runtime2, err := NewRuntimeFromDirectory(root, false) + runtime2, err := NewRuntimeFromDirectory(runtime1.root, false) if err != nil { t.Fatal(err) } diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 4951d3a02d..d1859ff8b4 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -84,20 +84,28 @@ func readFile(src string, t *testing.T) (content string) { } // Create a test container from the given runtime `r` and run arguments `args`. -// The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. +// If the image name is "_", (eg. []string{"-i", "-t", "_", "bash"}, it is +// dynamically replaced by the current test image. // The caller is responsible for destroying the container. // Call t.Fatal() at the first error. -func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig) { +func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) { config, hostConfig, _, err := ParseRun(args, nil) + defer func() { + if err != nil && t != nil { + t.Fatal(err) + } + }() if err != nil { - t.Fatal(err) + return nil, nil, err + } + if config.Image == "_" { + config.Image = GetTestImage(r).ID } - config.Image = GetTestImage(r).ID c, err := NewBuilder(r).Create(config) if err != nil { - t.Fatal(err) + return nil, nil, err } - return c, hostConfig + return c, hostConfig, nil } // Create a test container, start it, wait for it to complete, destroy it, @@ -110,7 +118,10 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e t.Fatal(err) } }() - container, hostConfig := mkContainer(r, args, t) + container, hostConfig, err := mkContainer(r, args, t) + if err != nil { + return "", err + } defer r.Destroy(container) stdout, err := container.StdoutPipe() if err != nil { From 80066854b8cbb39027349d91f8eb708e5ec820c5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 15 Jul 2013 16:17:58 +0000 Subject: [PATCH 19/80] store both logs in a same file, as JSON Upstream-commit: 599f85d4e4362f24dc2850c71a689671122c456b Component: engine --- components/engine/commands_test.go | 12 +++++------ components/engine/container.go | 10 +-------- components/engine/server.go | 28 ++++++++++++------------ components/engine/utils/utils.go | 34 ++++++++++++++++-------------- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/components/engine/commands_test.go b/components/engine/commands_test.go index 3f4c53db03..233c6337d4 100644 --- a/components/engine/commands_test.go +++ b/components/engine/commands_test.go @@ -59,7 +59,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error return nil } - // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname func TestRunHostname(t *testing.T) { stdout, stdoutPipe := io.Pipe() @@ -91,7 +90,6 @@ func TestRunHostname(t *testing.T) { } - // TestAttachStdin checks attaching to stdin without stdout and stderr. // 'docker run -i -a stdin' should sends the client's stdin to the command, // then detach from it and print the container id. @@ -144,15 +142,17 @@ func TestRunAttachStdin(t *testing.T) { }) // Check logs - if cmdLogs, err := container.ReadLog("stdout"); err != nil { + if cmdLogs, err := container.ReadLog("json"); err != nil { t.Fatal(err) } else { if output, err := ioutil.ReadAll(cmdLogs); err != nil { t.Fatal(err) } else { - expectedLog := "hello\nhi there\n" - if string(output) != expectedLog { - t.Fatalf("Unexpected logs: should be '%s', not '%s'\n", expectedLog, output) + expectedLogs := []string{"{\"log\":\"hello\\n\",\"stream\":\"stdout\"", "{\"log\":\"hi there\\n\",\"stream\":\"stdout\""} + for _, expectedLog := range expectedLogs { + if !strings.Contains(string(output), expectedLog) { + t.Fatalf("Unexpected logs: should contains '%s', it is not '%s'\n", expectedLog, output) + } } } } diff --git a/components/engine/container.go b/components/engine/container.go index 7b0070094a..1011f7a6e3 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -640,21 +640,13 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.cmd = exec.Command("lxc-start", params...) // Setup logging of stdout and stderr to disk - /* - if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout"), ""); err != nil { - return err - } - if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr"), ""); err != nil { - return err - } - */ if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { return err } if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil { return err } - + var err error if container.Config.Tty { err = container.startPty() diff --git a/components/engine/server.go b/components/engine/server.go index c43cae0c38..6129e3eb95 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -2,6 +2,7 @@ package docker import ( "bufio" + "encoding/json" "errors" "fmt" "github.com/dotcloud/docker/auth" @@ -1042,20 +1043,21 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std } //logs if logs { - if stdout { - cLog, err := container.ReadLog("stdout") - if err != nil { - utils.Debugf("Error reading logs (stdout): %s", err) - } else if _, err := io.Copy(out, cLog); err != nil { - utils.Debugf("Error streaming logs (stdout): %s", err) - } + cLog, err := container.ReadLog("json") + if err != nil { + utils.Debugf("Error reading logs (json): %s", err) } - if stderr { - cLog, err := container.ReadLog("stderr") - if err != nil { - utils.Debugf("Error reading logs (stderr): %s", err) - } else if _, err := io.Copy(out, cLog); err != nil { - utils.Debugf("Error streaming logs (stderr): %s", err) + dec := json.NewDecoder(cLog) + for { + var l utils.JSONLog + if err := dec.Decode(&l); err == io.EOF { + break + } else if err != nil { + utils.Debugf("Error streaming logs: %s", err) + break + } + if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { + fmt.Fprintf(out, "%s", l.Log) } } } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 3ec853bf68..9e6f0c9c0d 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -247,47 +247,49 @@ func (r *bufReader) Close() error { type WriteBroadcaster struct { sync.Mutex - writers map[StreamWriter][]byte + buf *bytes.Buffer + writers map[StreamWriter]bool } type StreamWriter struct { - wc io.WriteCloser + wc io.WriteCloser stream string } func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser, stream string) { w.Lock() sw := StreamWriter{wc: writer, stream: stream} - w.writers[sw] = []byte{} + w.writers[sw] = true w.Unlock() } type JSONLog struct { - Log string `json:"log,omitempty"` - Stream string `json:"stream,omitempty"` + Log string `json:"log,omitempty"` + Stream string `json:"stream,omitempty"` Created time.Time `json:"time"` } func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { w.Lock() defer w.Unlock() + w.buf.Write(p) for sw := range w.writers { lp := p if sw.stream != "" { - w.writers[sw] = append(w.writers[sw], p...) - s := string(p) - if s[len(s)-1] == '\n' { - /* lp, err = json.Marshal(&JSONLog{Log: s, Stream: sw.stream, Created: time.Now()}) + lp = nil + for { + line, err := w.buf.ReadString('\n') + if err != nil { + w.buf.Write([]byte(line)) + break + } + b, err := json.Marshal(&JSONLog{Log: line, Stream: sw.stream, Created: time.Now()}) if err != nil { // On error, evict the writer delete(w.writers, sw) continue } - */ - lp = []byte("[" + time.Now().String() + "] [" + sw.stream + "] " + s) - w.writers[sw] = []byte{} - } else { - continue + lp = append(lp, b...) } } if n, err := sw.wc.Write(lp); err != nil || n != len(lp) { @@ -304,12 +306,12 @@ func (w *WriteBroadcaster) CloseWriters() error { for sw := range w.writers { sw.wc.Close() } - w.writers = make(map[StreamWriter][]byte) + w.writers = make(map[StreamWriter]bool) return nil } func NewWriteBroadcaster() *WriteBroadcaster { - return &WriteBroadcaster{writers: make(map[StreamWriter][]byte)} + return &WriteBroadcaster{writers: make(map[StreamWriter]bool), buf: bytes.NewBuffer(nil)} } func GetTotalUsedFds() int { From f98fac335f947fcf65398ea73f4fa68f9fb3be21 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Sun, 14 Jul 2013 15:26:57 -0900 Subject: [PATCH 20/80] Do not overwrite container volumes from config Fixes #819 Use same persistent volume when a container is restarted Upstream-commit: 92cbb7cc80a63299b5670a9fcbb2d11789200696 Component: engine --- components/engine/container.go | 51 +++++++++++++++-------------- components/engine/container_test.go | 43 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index c714e9f0e1..53d720b771 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -516,8 +516,6 @@ func (container *Container) Start(hostConfig *HostConfig) error { log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n") container.Config.MemorySwap = -1 } - container.Volumes = make(map[string]string) - container.VolumesRW = make(map[string]bool) // Create the requested bind mounts binds := make(map[string]BindMap) @@ -557,30 +555,35 @@ func (container *Container) Start(hostConfig *HostConfig) error { // FIXME: evaluate volumes-from before individual volumes, so that the latter can override the former. // Create the requested volumes volumes - for volPath := range container.Config.Volumes { - volPath = path.Clean(volPath) - // If an external bind is defined for this volume, use that as a source - if bindMap, exists := binds[volPath]; exists { - container.Volumes[volPath] = bindMap.SrcPath - if strings.ToLower(bindMap.Mode) == "rw" { - container.VolumesRW[volPath] = true + if container.Volumes == nil || len(container.Volumes) == 0 { + container.Volumes = make(map[string]string) + container.VolumesRW = make(map[string]bool) + + for volPath := range container.Config.Volumes { + volPath = path.Clean(volPath) + // If an external bind is defined for this volume, use that as a source + if bindMap, exists := binds[volPath]; exists { + container.Volumes[volPath] = bindMap.SrcPath + if strings.ToLower(bindMap.Mode) == "rw" { + container.VolumesRW[volPath] = true + } + // Otherwise create an directory in $ROOT/volumes/ and use that + } else { + c, err := container.runtime.volumes.Create(nil, container, "", "", nil) + if err != nil { + return err + } + srcPath, err := c.layer() + if err != nil { + return err + } + container.Volumes[volPath] = srcPath + container.VolumesRW[volPath] = true // RW by default } - // Otherwise create an directory in $ROOT/volumes/ and use that - } else { - c, err := container.runtime.volumes.Create(nil, container, "", "", nil) - if err != nil { - return err + // Create the mountpoint + if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + return nil } - srcPath, err := c.layer() - if err != nil { - return err - } - container.Volumes[volPath] = srcPath - container.VolumesRW[volPath] = true // RW by default - } - // Create the mountpoint - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { - return nil } } diff --git a/components/engine/container_test.go b/components/engine/container_test.go index f431c7dc9a..2209bc0be0 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1300,3 +1300,46 @@ func TestVolumesFromReadonlyMount(t *testing.T) { t.Fail() } } + +// Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819. +func TestRestartWithVolumes(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + container, err := NewBuilder(runtime).Create(&Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"echo", "-n", "foobar"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + + for key := range container.Config.Volumes { + if key != "/test" { + t.Fail() + } + } + + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + + expected := container.Volumes["/test"] + if expected == "" { + t.Fail() + } + // Run the container again to verify the volume path persists + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + + actual := container.Volumes["/test"] + if expected != actual { + t.Fatalf("Expected volume path: %s Actual path: %s", expected, actual) + } +} From 723f4f9ef033b7d13b098634dbec7adbae40b51d Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 15 Jul 2013 14:55:51 -0700 Subject: [PATCH 21/80] Bump version to 0.5.0 Upstream-commit: bc21b3ebf0dadc51e47d971217d132e5299831a0 Component: engine --- components/engine/CHANGELOG.md | 11 +++++++++++ components/engine/commands.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index f4912ae661..92694148a6 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.5.0 (2013-07-16) + + Remote API: Add /top endpoint + + Runtime: host directories can be mounted as volumes with 'docker run -b' + + Runtime: Add UDP support + + Builder: Add ENTRYPOINT instruction + + Builder: Add VOLUMES instruction + * Runtime: Add options to docker login + * Builder: Display full output by default + - Registry: Fix issues when pushing to 3rd part registries + - Runtime: Skip `hostname` when merging config + ## 0.4.8 (2013-07-01) + Builder: New build operation ENTRYPOINT adds an executable entry point to the container. - Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID. diff --git a/components/engine/commands.go b/components/engine/commands.go index b581590bc2..d814197ac6 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -27,7 +27,7 @@ import ( "unicode" ) -const VERSION = "0.4.8" +const VERSION = "0.5.0" var ( GITCOMMIT string From df2dfb45a85b7f7bd94a511ee1a85a4bac6e3af9 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 15 Jul 2013 17:18:11 -0700 Subject: [PATCH 22/80] Merge -b and -v options Upstream-commit: eefbadd230d2788b0bdf0daac38ada0d145e3861 Component: engine --- components/engine/commands.go | 18 +++++++++++++++--- components/engine/container.go | 20 ++++++++++++-------- components/engine/container_test.go | 7 +++---- components/engine/utils_test.go | 14 +++++++++----- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b581590bc2..ac1c88055f 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1249,10 +1249,22 @@ func (opts PathOpts) String() string { } func (opts PathOpts) Set(val string) error { - if !filepath.IsAbs(val) { - return fmt.Errorf("%s is not an absolute path", val) + var containerPath string + + splited := strings.SplitN(val, ":", 2) + if len(splited) == 1 { + containerPath = splited[0] + val = filepath.Clean(splited[0]) + } else { + containerPath = splited[1] + val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1])) } - opts[filepath.Clean(val)] = struct{}{} + + if !filepath.IsAbs(containerPath) { + utils.Debugf("%s is not an absolute path", containerPath) + return fmt.Errorf("%s is not an absolute path", containerPath) + } + opts[val] = struct{}{} return nil } diff --git a/components/engine/container.go b/components/engine/container.go index c714e9f0e1..3772cf29d2 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -121,14 +121,11 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, cmd.Var(&flDns, "dns", "Set custom dns servers") flVolumes := NewPathOpts() - cmd.Var(flVolumes, "v", "Attach a data volume") + cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container") flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") - var flBinds ListOpts - cmd.Var(&flBinds, "b", "Bind mount a volume from the host (e.g. -b /host:/container)") - if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err } @@ -146,11 +143,17 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } } + var binds []string + // add any bind targets to the list of container volumes - for _, bind := range flBinds { + for bind := range flVolumes { arr := strings.Split(bind, ":") - dstDir := arr[1] - flVolumes[dstDir] = struct{}{} + if len(arr) > 1 { + dstDir := arr[1] + flVolumes[dstDir] = struct{}{} + binds = append(binds, bind) + delete(flVolumes, bind) + } } parsedArgs := cmd.Args() @@ -187,7 +190,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Entrypoint: entrypoint, } hostConfig := &HostConfig{ - Binds: flBinds, + Binds: binds, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -493,6 +496,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s func (container *Container) Start(hostConfig *HostConfig) error { container.State.Lock() defer container.State.Unlock() + if len(hostConfig.Binds) == 0 { hostConfig, _ = container.ReadHostConfig() } diff --git a/components/engine/container_test.go b/components/engine/container_test.go index f431c7dc9a..6e82dd5ebd 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1231,19 +1231,18 @@ func TestBindMounts(t *testing.T) { writeFile(path.Join(tmpDir, "touch-me"), "", t) // Test reading from a read-only bind mount - stdout, _ := runContainer(r, []string{"-b", fmt.Sprintf("%s:/tmp:ro", tmpDir), "_", "ls", "/tmp"}, t) + stdout, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "_", "ls", "/tmp"}, t) if !strings.Contains(stdout, "touch-me") { t.Fatal("Container failed to read from bind mount") } // test writing to bind mount - runContainer(r, []string{"-b", fmt.Sprintf("%s:/tmp:rw", tmpDir), "_", "touch", "/tmp/holla"}, t) + runContainer(r, []string{"-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "_", "touch", "/tmp/holla"}, t) readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist // test mounting to an illegal destination directory - if _, err := runContainer(r, []string{"-b", fmt.Sprintf("%s:.", tmpDir), "ls", "."}, nil); err == nil { + if _, err := runContainer(r, []string{"-v", fmt.Sprintf("%s:.", tmpDir), "ls", "."}, nil); err == nil { t.Fatal("Container bind mounted illegal directory") - } } diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 4951d3a02d..afa6a3a1a1 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -1,13 +1,13 @@ package docker import ( + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" "path" "strings" "testing" - "github.com/dotcloud/docker/utils" ) // This file contains utility functions for docker's unit test suite. @@ -87,17 +87,18 @@ func readFile(src string, t *testing.T) (content string) { // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. // The caller is responsible for destroying the container. // Call t.Fatal() at the first error. -func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig) { +func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) { config, hostConfig, _, err := ParseRun(args, nil) if err != nil { - t.Fatal(err) + return nil, nil, err } config.Image = GetTestImage(r).ID c, err := NewBuilder(r).Create(config) if err != nil { t.Fatal(err) + return nil, nil, err } - return c, hostConfig + return c, hostConfig, nil } // Create a test container, start it, wait for it to complete, destroy it, @@ -110,7 +111,10 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e t.Fatal(err) } }() - container, hostConfig := mkContainer(r, args, t) + container, hostConfig, err := mkContainer(r, args, t) + if err != nil { + return "", err + } defer r.Destroy(container) stdout, err := container.StdoutPipe() if err != nil { From a4a2ab57ce1029fda0aba526786747f7610890a8 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 16 Jul 2013 10:14:21 -0700 Subject: [PATCH 23/80] Update docs Upstream-commit: 18e91d5f85a008ef48d724db964330ddaa1bbf10 Component: engine --- components/engine/docs/sources/commandline/command/run.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index 4529013b2e..1a14a9b616 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -23,7 +23,6 @@ -t=false: Allocate a pseudo-tty -u="": Username or UID -d=[]: Set custom dns servers for the container - -v=[]: Creates a new volume and mounts it at the specified path. + -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume. -volumes-from="": Mount all volumes from the given container. - -b=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro] -entrypoint="": Overwrite the default entrypoint set by the image. From 73fd748fb27b141ab5ad3bcc05dd1f2268eb7ebb Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Tue, 16 Jul 2013 13:45:43 -0700 Subject: [PATCH 24/80] Testing, issue #1217: Add coverage testing into docker-ci Upstream-commit: 6e8bfc8d12ca062dc4c311d528784c1dec27079d Component: engine --- components/engine/testing/buildbot/master.cfg | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/components/engine/testing/buildbot/master.cfg b/components/engine/testing/buildbot/master.cfg index 65399bb1a1..61912808ec 100644 --- a/components/engine/testing/buildbot/master.cfg +++ b/components/engine/testing/buildbot/master.cfg @@ -2,6 +2,7 @@ import os from buildbot.buildslave import BuildSlave from buildbot.schedulers.forcesched import ForceScheduler from buildbot.schedulers.basic import SingleBranchScheduler +from buildbot.schedulers.timed import Nightly from buildbot.changes import filter from buildbot.config import BuilderConfig from buildbot.process.factory import BuildFactory @@ -40,12 +41,16 @@ c['db'] = {'db_url':"sqlite:///state.sqlite"} c['slaves'] = [BuildSlave('buildworker', BUILDBOT_PWD)] c['slavePortnum'] = PORT_MASTER -c['schedulers'] = [ForceScheduler(name='trigger',builderNames=[BUILDER_NAME])] -c['schedulers'].append(SingleBranchScheduler(name="all", - change_filter=filter.ChangeFilter(branch='master'),treeStableTimer=None, - builderNames=[BUILDER_NAME])) +# Schedulers +c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME, + 'coverage'])] +c['schedulers'] += [SingleBranchScheduler(name="all", + change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None, + builderNames=[BUILDER_NAME])] +c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage'], + hour=0, minute=30)] -# Builder +# Builders factory = BuildFactory() factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True, command=["sh", "-c", Interpolate("cd ..; rm -rf build; export GOPATH={0}; " @@ -53,6 +58,16 @@ factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True, "go test -v".format(BUILDER_PATH,GITHUB_DOCKER,DOCKER_BUILD_PATH))])) c['builders'] = [BuilderConfig(name=BUILDER_NAME,slavenames=['buildworker'], factory=factory)] +# Docker coverage test +coverage_cmd = ('GOPATH=`pwd` go get -d github.com/dotcloud/docker\n' + 'GOPATH=`pwd` go get github.com/axw/gocov/gocov\n' + 'sudo -E GOPATH=`pwd` ./bin/gocov test github.com/dotcloud/docker | ' + './bin/gocov report') +factory = BuildFactory() +factory.addStep(ShellCommand(description='Coverage',logEnviron=False,usePTY=True, + command=coverage_cmd)) +c['builders'] += [BuilderConfig(name='coverage',slavenames=['buildworker'], + factory=factory)] # Status authz_cfg = authz.Authz(auth=auth.BasicAuth([(TEST_USER, TEST_PWD)]), From 2953a9d955582d9ce7ce80723a5442438480efc2 Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Tue, 16 Jul 2013 17:04:41 -0700 Subject: [PATCH 25/80] Update repository information. Upstream-commit: 0356081c0ad2f1d95fb435f9a491f7a7a63cbf33 Component: engine --- .../sources/use/workingwithrepository.rst | 124 +++++++++++------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/components/engine/docs/sources/use/workingwithrepository.rst b/components/engine/docs/sources/use/workingwithrepository.rst index 243c99afdf..3cdbfe49d6 100644 --- a/components/engine/docs/sources/use/workingwithrepository.rst +++ b/components/engine/docs/sources/use/workingwithrepository.rst @@ -7,21 +7,69 @@ Working with Repositories ========================= +A *repository* is a hosted collection of tagged :ref:`images +` that together create the file system for a container. The +repository's name is a tag that indicates the provenance of the +repository, i.e. who created it and where the original copy is +located. -Top-level repositories and user repositories --------------------------------------------- +You can find one or more repositories hosted on a *registry*. There +can be an implicit or explicit host name as part of the repository +tag. The implicit registry is located at ``index.docker.io``, the home +of "top-level" repositories and the Central Index. This registry may +also include public "user" repositories. -Generally, there are two types of repositories: Top-level repositories -which are controlled by the people behind Docker, and user -repositories. +So Docker is not only a tool for creating and managing your own +:ref:`containers ` -- **Docker is also a tool for +sharing**. The Docker project provides a Central Registry to host +public repositories, namespaced by user, and a Central Index which +provides user authentication and search over all the public +repositories. You can host your own Registry too! Docker acts as a +client for these services via ``docker search, pull, login`` and +``push``. -* Top-level repositories can easily be recognized by not having a ``/`` (slash) in their name. These repositories can generally be trusted. -* User repositories always come in the form of ``/``. This is what your published images will look like. -* User images are not checked, it is therefore up to you whether or not you trust the creator of this image. +Top-level, User, and Your Own Repositories +------------------------------------------ +There are two types of public repositories: *top-level* repositories +which are controlled by the Docker team, and *user* repositories +created by individual contributors. -Find public images available on the index ------------------------------------------ +* Top-level repositories can easily be recognized by **not** having a + ``/`` (slash) in their name. These repositories can generally be + trusted. +* User repositories always come in the form of + ``/``. This is what your published images will + look like if you push to the public Central Registry. +* Only the authenticated user can push to their *username* namespace + on the Central Registry. +* User images are not checked, it is therefore up to you whether or + not you trust the creator of this image. + +Right now (version 0.5), private repositories are only possible by +hosting `your own registry +`_. To push or pull to a +repository on your own registry, you must prefix the tag with the +address of the registry's host, like this: + +.. code-block:: bash + + # Tag to create a repository with the full registry location. + # The location (e.g. localhost.localdomain:5000) becomes + # a permanent part of the repository name + docker tag 0u812deadbeef localhost.localdomain:5000/repo_name + + # Push the new repository to its home location on localhost + docker push localhost.localdomain:5000/repo_name + +Once a repository has your registry's host name as part of the tag, +you can push and pull it like any other repository, but it will +**not** be searchable (or indexed at all) in the Central Index, and +there will be no user name checking performed. Your registry will +function completely independently from the Central Index. + +Find public images available on the Central Index +------------------------------------------------- Seach by name, namespace or description @@ -37,68 +85,48 @@ Download them simply by their name docker pull -Very similarly you can search for and browse the index online on https://index.docker.io +Very similarly you can search for and browse the index online on +https://index.docker.io -Connecting to the repository ----------------------------- +Connecting to the Central Registry +---------------------------------- -You can create a user on the central docker repository online, or by running +You can create a user on the central Docker Index online, or by running .. code-block:: bash docker login +This will prompt you for a username, which will become a public +namespace for your public repositories. -If your username does not exist it will prompt you to also enter a password and your e-mail address. It will then -automatically log you in. +If your username does not exist it will prompt you to also enter a +password and your e-mail address. It will then automatically log you +in. Committing a container to a named image --------------------------------------- -In order to commit to the repository it is required to have committed your container to an image with your namespace. +In order to commit to the repository it is required to have committed +your container to an image within your username namespace. .. code-block:: bash # for example docker commit $CONTAINER_ID dhrp/kickassapp - docker commit / + docker commit / -Pushing a container to the repository ------------------------------------------ +Pushing a container to its repository +------------------------------------ -In order to push an image to the repository you need to have committed your container to a named image (see above) +In order to push an image to its repository you need to have committed +your container to a named image (see above) Now you can commit this image to the repository .. code-block:: bash # for example docker push dhrp/kickassapp - docker push - - -Changing the server to connect to ----------------------------------- - -When you are running your own index and/or registry, You can change the server the docker client will connect to. - -Variable -^^^^^^^^ - -.. code-block:: sh - - DOCKER_INDEX_URL - -Setting this environment variable on the docker server will change the URL docker index. -This address is used in commands such as ``docker login``, ``docker push`` and ``docker pull``. -The docker daemon doesn't need to be restarted for this parameter to take effect. - -Example -^^^^^^^ - -.. code-block:: sh - - docker -d & - export DOCKER_INDEX_URL="https://index.docker.io" - + docker push / From be8eb02780915a73b4c3ef5c3dc1cbb69864fdc6 Mon Sep 17 00:00:00 2001 From: Louis Opter Date: Wed, 17 Jul 2013 01:02:07 -0700 Subject: [PATCH 26/80] Always stop the opposite goroutine in network_proxy.go (closes #1213) Upstream-commit: c766d064ac6c2183321cb2e47ea8c0b0b2d2d238 Component: engine --- components/engine/network_proxy.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/network_proxy.go b/components/engine/network_proxy.go index 905773e533..fb91cc1b37 100644 --- a/components/engine/network_proxy.go +++ b/components/engine/network_proxy.go @@ -68,6 +68,7 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) { from.CloseWrite() } } + to.CloseRead() event <- written } utils.Debugf("Forwarding traffic between tcp/%v and tcp/%v", client.RemoteAddr(), backend.RemoteAddr()) From 7d6b6db98e70e77903c78091d4918a4f9dc7f0fb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 17 Jul 2013 15:48:53 +0000 Subject: [PATCH 27/80] fix docker rmi via id Upstream-commit: 5a934fc923af316a6e82b9dd11169484b3b744f6 Component: engine --- components/engine/server.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index c43cae0c38..954bbb208f 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -870,7 +870,6 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error { if len(srv.runtime.repositories.ByID()[id]) != 0 { return ErrImageReferenced } - // If the image is not referenced but has children, go recursive referenced := false byParents, err := srv.runtime.graph.ByParent() @@ -924,8 +923,22 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error { } func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, error) { - //Untag the current image imgs := []APIRmi{} + + //If delete by id, see if the id belong only to one repository + if strings.Contains(img.ID, repoName) && tag == "" { + for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] { + parsedRepo := strings.Split(repoAndTag, ":")[0] + if strings.Contains(img.ID, repoName) { + repoName = parsedRepo + } else if repoName != parsedRepo { + // the id belongs to multiple repos, like base:latest and user:test, + // in that case return conflict + return imgs, nil + } + } + } + //Untag the current image tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag) if err != nil { return nil, err From 28398dc429db15dca9ae0d3ebdf147a389a6fca8 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Wed, 17 Jul 2013 13:46:11 -0400 Subject: [PATCH 28/80] updated with notes from @vieux Upstream-commit: d0e8ca1257f7f969a035d3d78b55e08c067e8a20 Component: engine --- components/engine/commands.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 20195e2e39..3223c962ac 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -94,8 +94,8 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"pull", "Pull an image or a repository from the docker registry server"}, {"push", "Push an image or a repository to the docker registry server"}, {"restart", "Restart a running container"}, - {"rm", "Remove a container"}, - {"rmi", "Remove an image"}, + {"rm", "Remove one or more containers"}, + {"rmi", "Remove one or more images"}, {"run", "Run a command in a new container"}, {"search", "Search for an image in the docker index"}, {"start", "Start a stopped container"}, @@ -469,7 +469,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error { cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before stopping the container") + nSeconds := cmd.Int("t", 10, "Number of seconds to try to stop for before killing the container. Default=10") if err := cmd.Parse(args); err != nil { return nil } @@ -494,7 +494,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error { cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before restarting the container") + nSeconds := cmd.Int("t", 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10") if err := cmd.Parse(args); err != nil { return nil } @@ -638,7 +638,7 @@ func (cli *DockerCli) CmdPort(args ...string) error { // 'docker rmi IMAGE' removes all images with the name IMAGE func (cli *DockerCli) CmdRmi(args ...string) error { - cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image") + cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images") if err := cmd.Parse(args); err != nil { return nil } @@ -703,7 +703,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error { } func (cli *DockerCli) CmdRm(args ...string) error { - cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container") + cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers") v := cmd.Bool("v", false, "Remove the volumes associated to the container") if err := cmd.Parse(args); err != nil { return nil From 76eb7762ab7caf667a1376be7e01e24540692469 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 17 Jul 2013 11:39:38 -0700 Subject: [PATCH 29/80] Small changes in changelog wording Upstream-commit: 8af945f353379a96eb036d67fb24d489c5e16808 Component: engine --- components/engine/CHANGELOG.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index 92694148a6..dff042f1da 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,15 +1,17 @@ # Changelog ## 0.5.0 (2013-07-16) - + Remote API: Add /top endpoint - + Runtime: host directories can be mounted as volumes with 'docker run -b' - + Runtime: Add UDP support - + Builder: Add ENTRYPOINT instruction - + Builder: Add VOLUMES instruction - * Runtime: Add options to docker login - * Builder: Display full output by default - - Registry: Fix issues when pushing to 3rd part registries - - Runtime: Skip `hostname` when merging config + + Runtime: List all processes running inside a container with 'docker top' + + Runtime: Host directories can be mounted as volumes with 'docker run -b' + + Runtime: Containers can expose public UDP ports + + Runtime: Optionally specify an exact public port (eg. '-p 80:4500') + + Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries + + Builder: ENTRYPOINT instruction sets a default binary entry point to a container + + Builder: VOLUME instruction marks a part of the container as persistent data + * Builder: 'docker build' displays the full output of a build by default + * Runtime: 'docker login' supports additional options + - Runtime: Dont save a container's hostname when committing an image. + - Registry: Fix issues when uploading images to a private registry ## 0.4.8 (2013-07-01) + Builder: New build operation ENTRYPOINT adds an executable entry point to the container. From 926b49f01623f4a7f9fef6814ead72241423a73b Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 17 Jul 2013 11:51:26 -0700 Subject: [PATCH 30/80] Changed date on changelog Upstream-commit: ac14c463d55494fdab88d36ea74a4ecef8ab48dc Component: engine --- components/engine/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index dff042f1da..0ed6495ae8 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.5.0 (2013-07-16) +## 0.5.0 (2013-07-17) + Runtime: List all processes running inside a container with 'docker top' + Runtime: Host directories can be mounted as volumes with 'docker run -b' + Runtime: Containers can expose public UDP ports From a44b5f6d5b87db2844dab6535b6432811e844c5e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 17 Jul 2013 19:24:54 +0000 Subject: [PATCH 31/80] change rm usage in docs Upstream-commit: 9cf2b41c053d557af6c563311b4953c4b9bab6d6 Component: engine --- components/engine/docs/sources/commandline/command/rm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/docs/sources/commandline/command/rm.rst b/components/engine/docs/sources/commandline/command/rm.rst index dc6d91632d..8a2309ce79 100644 --- a/components/engine/docs/sources/commandline/command/rm.rst +++ b/components/engine/docs/sources/commandline/command/rm.rst @@ -10,4 +10,4 @@ Usage: docker rm [OPTIONS] CONTAINER - Remove a container + Remove one or more containers From 97e62e30e8eff39efc2b4bde576077ba41b869fc Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Wed, 17 Jul 2013 18:56:40 -0700 Subject: [PATCH 32/80] Make dockerfile docs easier to find. Clean up formatting. Upstream-commit: aa5671411b727d7142c289b5c1b473209aeaf630 Component: engine --- .../engine/docs/sources/use/builder.rst | 160 ++++++++++-------- 1 file changed, 94 insertions(+), 66 deletions(-) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index ceda9c99dd..9ea8033b98 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -1,25 +1,27 @@ -:title: Docker Builder +:title: Dockerfile Builder :description: Docker Builder specifes a simple DSL which allows you to automate the steps you would normally manually take to create an image. :keywords: builder, docker, Docker Builder, automation, image creation -============== -Docker Builder -============== +================== +Dockerfile Builder +================== + +**Docker can act as a builder** and read instructions from a text +Dockerfile to automate the steps you would otherwise make manually to +create an image. Executing ``docker build`` will run your steps and +commit them along the way, giving you a final image. .. contents:: Table of Contents -Docker Builder specifes a simple DSL which allows you to automate the steps you -would normally manually take to create an image. Docker Build will run your -steps and commit them along the way, giving you a final image. - 1. Usage ======== -To build an image from a source repository, create a description file called `Dockerfile` -at the root of your repository. This file will describe the steps to assemble -the image. +To build an image from a source repository, create a description file +called ``Dockerfile`` at the root of your repository. This file will +describe the steps to assemble the image. -Then call `docker build` with the path of your source repository as argument: +Then call ``docker build`` with the path of your source repository as +argument: ``docker build .`` @@ -36,136 +38,162 @@ before finally outputting the ID of your new image. The Dockerfile format is quite simple: - ``instruction arguments`` +:: + + # Comment + INSTRUCTION arguments The Instruction is not case-sensitive, however convention is for them to be UPPERCASE in order to distinguish them from arguments more easily. -Dockerfiles are evaluated in order, therefore the first instruction must be -`FROM` in order to specify the base image from which you are building. +Docker evaluates the instructions in a Dockerfile in order. **The first +instruction must be `FROM`** in order to specify the base image from +which you are building. -Docker will ignore lines in Dockerfiles prefixed with "`#`", so you may add -comment lines. A comment marker in the rest of the line will be treated as an -argument. +Docker will ignore **comment lines** *beginning* with ``#``. A comment +marker anywhere in the rest of the line will be treated as an argument. 3. Instructions =============== -Docker builder comes with a set of instructions, described below. +Here is the set of instructions you can use in a ``Dockerfile`` for +building images. 3.1 FROM -------- ``FROM `` -The `FROM` instruction sets the base image for subsequent instructions. As such, -a valid Dockerfile must have it as its first instruction. +The ``FROM`` instruction sets the :ref:`base_image_def` for subsequent +instructions. As such, a valid Dockerfile must have ``FROM`` as its +first instruction. -`FROM` can be included multiple times within a single Dockerfile in order to -create multiple images. Simply make a note of the last image id output by the -commit before each new `FROM` command. +``FROM`` must be the first non-comment instruction in the +``Dockerfile``. + +``FROM`` can appear multiple times within a single Dockerfile in order +to create multiple images. Simply make a note of the last image id +output by the commit before each new ``FROM`` command. 3.2 MAINTAINER -------------- ``MAINTAINER `` -The `MAINTAINER` instruction allows you to set the Author field of the generated -images. +The ``MAINTAINER`` instruction allows you to set the *Author* field of +the generated images. 3.3 RUN ------- ``RUN `` -The `RUN` instruction will execute any commands on the current image and commit -the results. The resulting committed image will be used for the next step in the -Dockerfile. +The ``RUN`` instruction will execute any commands on the current image +and commit the results. The resulting committed image will be used for +the next step in the Dockerfile. -Layering `RUN` instructions and generating commits conforms to the -core concepts of Docker where commits are cheap and containers can be created -from any point in an image's history, much like source control. +Layering ``RUN`` instructions and generating commits conforms to the +core concepts of Docker where commits are cheap and containers can be +created from any point in an image's history, much like source +control. 3.4 CMD ------- ``CMD `` -The `CMD` instruction sets the command to be executed when running the image. -This is functionally equivalent to running -`docker commit -run '{"Cmd": }'` outside the builder. +The ``CMD`` instruction sets the command to be executed when running +the image. This is functionally equivalent to running ``docker commit +-run '{"Cmd": }'`` outside the builder. -.. note:: - Don't confuse `RUN` with `CMD`. `RUN` actually runs a command and commits - the result; `CMD` does not execute anything at build time, but specifies the - intended command for the image. +.. note:: + Don't confuse `RUN` with `CMD`. `RUN` actually runs a + command and commits the result; `CMD` does not execute anything at + build time, but specifies the intended command for the image. 3.5 EXPOSE ---------- ``EXPOSE [...]`` -The `EXPOSE` instruction sets ports to be publicly exposed when running the -image. This is functionally equivalent to running -`docker commit -run '{"PortSpecs": ["", ""]}'` outside the builder. +The ``EXPOSE`` instruction sets ports to be publicly exposed when +running the image. This is functionally equivalent to running ``docker +commit -run '{"PortSpecs": ["", ""]}'`` outside the +builder. 3.6 ENV ------- ``ENV `` -The `ENV` instruction sets the environment variable `` to the value -``. This value will be passed to all future ``RUN`` instructions. This is -functionally equivalent to prefixing the command with `=` +The ``ENV`` instruction sets the environment variable ```` to the +value ````. This value will be passed to all future ``RUN`` +instructions. This is functionally equivalent to prefixing the command +with ``=`` -.. note:: - The environment variables will persist when a container is run from the resulting image. +.. note:: + The environment variables will persist when a container is run + from the resulting image. 3.7 ADD ------- ``ADD `` -The `ADD` instruction will copy new files from and add them to the container's filesystem at path ``. +The ``ADD`` instruction will copy new files from and add them to +the container's filesystem at path ````. -`` must be the path to a file or directory relative to the source directory being built (also called the -context of the build) or a remote file URL. +```` must be the path to a file or directory relative to the +source directory being built (also called the *context* of the build) or +a remote file URL. -`` is the path at which the source will be copied in the destination container. +```` is the path at which the source will be copied in the +destination container. The copy obeys the following rules: -If `` is a directory, the entire directory is copied, including filesystem metadata. +* If ```` is a directory, the entire directory is copied, + including filesystem metadata. +* If ````` is a tar archive in a recognized compression format + (identity, gzip, bzip2 or xz), it is unpacked as a directory. -If `` is a tar archive in a recognized compression format (identity, gzip, bzip2 or xz), it -is unpacked as a directory. + When a directory is copied or unpacked, it has the same behavior as + ``tar -x``: the result is the union of -When a directory is copied or unpacked, it has the same behavior as 'tar -x': the result is the union of -a) whatever existed at the destination path and b) the contents of the source tree, with conflicts resolved -in favor of b on a file-by-file basis. + 1. whatever existed at the destination path and + 2. the contents of the source tree, -If `` is any other kind of file, it is copied individually along with its metadata. In this case, -if `` ends with a trailing slash '/', it will be considered a directory and the contents of `` -will be written at `/base()`. -If `` does not end with a trailing slash, it will be considered a regular file and the contents -of `` will be written at ``. + with conflicts resolved in favor of 2) on a file-by-file basis. -If `` doesn't exist, it is created along with all missing directories in its path. All new -files and directories are created with mode 0700, uid and gid 0. +* If ```` is any other kind of file, it is copied individually + along with its metadata. In this case, if ```` ends with a + trailing slash ``/``, it will be considered a directory and the + contents of ```` will be written at ``/base()``. +* If ```` does not end with a trailing slash, it will be + considered a regular file and the contents of ```` will be + written at ````. +* If ```` doesn't exist, it is created along with all missing + directories in its path. All new files and directories are created + with mode 0700, uid and gid 0. 3.8 ENTRYPOINT ------------- ``ENTRYPOINT /bin/echo`` -The `ENTRYPOINT` instruction adds an entry command that will not be overwritten when arguments are passed to docker run, unlike the behavior of `CMD`. This allows arguments to be passed to the entrypoint. i.e. `docker run -d` will pass the "-d" argument to the entrypoint. +The ``ENTRYPOINT`` instruction adds an entry command that will not be +overwritten when arguments are passed to docker run, unlike the +behavior of ``CMD``. This allows arguments to be passed to the +entrypoint. i.e. ``docker run -d`` will pass the "-d" argument +to the entrypoint. 3.9 VOLUME ---------- ``VOLUME ["/data"]`` -The `VOLUME` instruction will add one or more new volumes to any container created from the image. +The ``VOLUME`` instruction will add one or more new volumes to any +container created from the image. 4. Dockerfile Examples ====================== From 9423c7f84d421e0bfc0f54a619271fb6019bf83a Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 13:25:47 +0000 Subject: [PATCH 33/80] add legacy support Upstream-commit: a926cd4d880904258e01ea521ecd9e1b908f2b97 Component: engine --- components/engine/server.go | 45 ++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index 6129e3eb95..d275fe814b 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -1044,20 +1044,39 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std //logs if logs { cLog, err := container.ReadLog("json") - if err != nil { - utils.Debugf("Error reading logs (json): %s", err) - } - dec := json.NewDecoder(cLog) - for { - var l utils.JSONLog - if err := dec.Decode(&l); err == io.EOF { - break - } else if err != nil { - utils.Debugf("Error streaming logs: %s", err) - break + if err != nil && os.IsNotExist(err) { + // Legacy logs + if stdout { + cLog, err := container.ReadLog("stdout") + if err != nil { + utils.Debugf("Error reading logs (stdout): %s", err) + } else if _, err := io.Copy(out, cLog); err != nil { + utils.Debugf("Error streaming logs (stdout): %s", err) + } } - if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { - fmt.Fprintf(out, "%s", l.Log) + if stderr { + cLog, err := container.ReadLog("stderr") + if err != nil { + utils.Debugf("Error reading logs (stderr): %s", err) + } else if _, err := io.Copy(out, cLog); err != nil { + utils.Debugf("Error streaming logs (stderr): %s", err) + } + } + } else if err != nil { + utils.Debugf("Error reading logs (json): %s", err) + } else { + dec := json.NewDecoder(cLog) + for { + var l utils.JSONLog + if err := dec.Decode(&l); err == io.EOF { + break + } else if err != nil { + utils.Debugf("Error streaming logs: %s", err) + break + } + if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { + fmt.Fprintf(out, "%s", l.Log) + } } } } From e1b72763dc7558e1e5fc65b73f3072891ea4a03f Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 13:29:40 +0000 Subject: [PATCH 34/80] add debug and simplify docker logs Upstream-commit: 1b0fd7ead33722d8782634d54cbd797f284aa085 Component: engine --- components/engine/commands.go | 5 +---- components/engine/server.go | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b581590bc2..def2ff72d7 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1099,10 +1099,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error { return nil } - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", false, nil, cli.out); err != nil { - return err - } - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", false, nil, cli.err); err != nil { + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out); err != nil { return err } return nil diff --git a/components/engine/server.go b/components/engine/server.go index d275fe814b..958dc75663 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -1046,6 +1046,7 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std cLog, err := container.ReadLog("json") if err != nil && os.IsNotExist(err) { // Legacy logs + utils.Debugf("Old logs format") if stdout { cLog, err := container.ReadLog("stdout") if err != nil { From 1df4154799a298a057fca34e9858fccdd4dbcb39 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 8 Jul 2013 17:06:06 -0900 Subject: [PATCH 35/80] Add unit tests for buildfile config instructions Add tests for instructions in the buildfile that modify the config of the resulting image. Upstream-commit: e7f3f6fa5a10f890a1774a2320d31e236af56be9 Component: engine --- components/engine/buildfile_test.go | 100 ++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 2a1bebe156..14edbc088f 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -105,23 +105,11 @@ CMD Hello world func TestBuild(t *testing.T) { for _, ctx := range testContexts { - runtime := mkRuntime(t) - defer nuke(runtime) - - srv := &Server{ - runtime: runtime, - pullingPool: make(map[string]struct{}), - pushingPool: make(map[string]struct{}), - } - - buildfile := NewBuildFile(srv, ioutil.Discard, false) - if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil { - t.Fatal(err) - } + buildImage(ctx, t) } } -func TestVolume(t *testing.T) { +func buildImage(context testContextTemplate, t *testing.T) *Image { runtime, err := newTestRuntime() if err != nil { t.Fatal(err) @@ -133,20 +121,27 @@ func TestVolume(t *testing.T) { pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard, false) - imgId, err := buildfile.Build(mkTestContext(` -from %s -VOLUME /test -CMD Hello world -`, nil, t)) + + id, err := buildfile.Build(mkTestContext(context.dockerfile, context.files, t)) if err != nil { t.Fatal(err) } - img, err := srv.ImageInspect(imgId) + + img, err := srv.ImageInspect(id) if err != nil { t.Fatal(err) } + return img +} + +func TestVolume(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + volume /test + cmd Hello world + `, nil}, t) + if len(img.Config.Volumes) == 0 { t.Fail() } @@ -156,3 +151,66 @@ CMD Hello world } } } + +func TestBuildMaintainer(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + maintainer dockerio + `, nil}, t) + + if img.Author != "dockerio" { + t.Fail() + } +} + +func TestBuildEnv(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + env port 4243 + `, + nil}, t) + + if img.Config.Env[0] != "port=4243" { + t.Fail() + } +} + +func TestBuildCmd(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + cmd ["/bin/echo", "Hello World"] + `, + nil}, t) + + if img.Config.Cmd[0] != "/bin/echo" { + t.Log(img.Config.Cmd[0]) + t.Fail() + } + if img.Config.Cmd[1] != "Hello World" { + t.Log(img.Config.Cmd[1]) + t.Fail() + } +} + +func TestBuildExpose(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + expose 4243 + `, + nil}, t) + + if img.Config.PortSpecs[0] != "4243" { + t.Fail() + } +} + +func TestBuildEntrypoint(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + entrypoint ["/bin/echo"] + `, + nil}, t) + + if img.Config.Entrypoint[0] != "/bin/echo" { + } +} From 608601a19192fc555959cf047ec9f643cc56e1cb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 16:24:12 +0000 Subject: [PATCH 36/80] change -b -> -v and add udp example Upstream-commit: b083418257edbcb769dd1bf9a6a3dafd334d5969 Component: engine --- components/engine/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index 0ed6495ae8..00e358c136 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -2,8 +2,8 @@ ## 0.5.0 (2013-07-17) + Runtime: List all processes running inside a container with 'docker top' - + Runtime: Host directories can be mounted as volumes with 'docker run -b' - + Runtime: Containers can expose public UDP ports + + Runtime: Host directories can be mounted as volumes with 'docker run -v' + + Runtime: Containers can expose public UDP ports (eg, '-p 123/udp') + Runtime: Optionally specify an exact public port (eg. '-p 80:4500') + Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries + Builder: ENTRYPOINT instruction sets a default binary entry point to a container From 3427bb07afb397f47e0027a900fdca6b493ce90f Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Thu, 18 Jul 2013 14:22:49 -0400 Subject: [PATCH 37/80] documentation. Upstream-commit: cd209f406e889feaba50103e5ce50f7dcd23767a Component: engine --- components/engine/registry/registry.go | 8 ++++++++ components/engine/server.go | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 03a2890105..6ba80cbea5 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -112,6 +112,8 @@ func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) { return c.Do(req) } +// Set the user agent field in the header based on the versions provided +// in NewRegistry() and extra. func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { if len(r.baseVersions)+len(extra) == 0 { return @@ -582,6 +584,12 @@ func validVersion(version VersionChecker) bool { return true } +// Convert versions to a string and append the string to the string base. +// +// Each VersionChecker will be converted to a string in the format of +// "product/version", where the "product" is get from the Name() method, while +// version is get from the Version() method. Several pieces of verson information +// will be concatinated and separated by space. func appendVersions(base string, versions ...VersionChecker) string { if len(versions) == 0 { return base diff --git a/components/engine/server.go b/components/engine/server.go index 70bb0bb871..925e4e3386 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -26,6 +26,11 @@ func (srv *Server) DockerVersion() APIVersion { } } +// plainVersionChecker is a simple implementation of +// the interface VersionChecker, which is used +// to provide version information for some product, +// component, etc. It stores the product name and the version +// in string and returns them on calls to Name() and Version(). type plainVersionChecker struct { name string version string @@ -39,6 +44,10 @@ func (v *plainVersionChecker) Version() string { return v.version } +// versionCheckers() returns version informations of: +// docker, go, git-commit (of the docker) and the host's kernel. +// +// Such information will be used on call to NewRegistry(). func (srv *Server) versionCheckers() []registry.VersionChecker { v := srv.DockerVersion() ret := make([]registry.VersionChecker, 0, 4) From 325ed80b66147ef60134b738ea7143d5cf3db0ea Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 20:50:04 +0000 Subject: [PATCH 38/80] switch version to -dev Upstream-commit: 0089dd05e910852d5b821074c73287e730040630 Component: engine --- components/engine/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index f78effe944..936b23fea2 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -27,7 +27,7 @@ import ( "unicode" ) -const VERSION = "0.5.0" +const VERSION = "0.5.0-dev" var ( GITCOMMIT string From 7bd70e19f4cedca4b5f187872181835ba5eba522 Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Thu, 18 Jul 2013 19:04:51 -0700 Subject: [PATCH 39/80] Make docs build without warnings or errors. Minor additional cleanup. Upstream-commit: 54f9cdb0c30ef7d192c4ae59c669a7a5fd7d1003 Component: engine --- .../docs/sources/api/docker_remote_api.rst | 61 +++++++++++++------ .../sources/api/docker_remote_api_v1.0.rst | 9 ++- .../sources/api/docker_remote_api_v1.1.rst | 4 ++ .../sources/api/docker_remote_api_v1.2.rst | 5 ++ .../sources/api/docker_remote_api_v1.3.rst | 5 ++ .../engine/docs/sources/api/index_api.rst | 2 +- .../docs/sources/api/registry_index_spec.rst | 5 +- components/engine/docs/sources/index.rst | 2 - .../engine/docs/sources/use/builder.rst | 8 +-- .../sources/use/workingwithrepository.rst | 2 +- 10 files changed, 73 insertions(+), 30 deletions(-) diff --git a/components/engine/docs/sources/api/docker_remote_api.rst b/components/engine/docs/sources/api/docker_remote_api.rst index 183347c23b..f8a1381c53 100644 --- a/components/engine/docs/sources/api/docker_remote_api.rst +++ b/components/engine/docs/sources/api/docker_remote_api.rst @@ -2,6 +2,9 @@ :description: API Documentation for Docker :keywords: API, Docker, rcli, REST, documentation +.. COMMENT use http://pythonhosted.org/sphinxcontrib-httpdomain/ to +.. document the REST API. + ================= Docker Remote API ================= @@ -13,15 +16,23 @@ Docker Remote API - The Remote API is replacing rcli - Default port in the docker deamon is 4243 -- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr -- Since API version 1.2, the auth configuration is now handled client side, so the client has to send the authConfig as POST in /images/(name)/push +- The API tends to be REST, but for some complex commands, like attach + or pull, the HTTP connection is hijacked to transport stdout stdin + and stderr +- Since API version 1.2, the auth configuration is now handled client + side, so the client has to send the authConfig as POST in + /images/(name)/push 2. Versions =========== -The current verson of the API is 1.3 -Calling /images//insert is the same as calling /v1.3/images//insert -You can still call an old version of the api using /v1.0/images//insert +The current verson of the API is 1.3 + +Calling /images//insert is the same as calling +/v1.3/images//insert + +You can still call an old version of the api using +/v1.0/images//insert :doc:`docker_remote_api_v1.3` ***************************** @@ -29,19 +40,21 @@ You can still call an old version of the api using /v1.0/images//insert What's new ---------- -Listing processes (/top): - -- List the processes inside a container +.. http:get:: /containers/(id)/top + **New!** List the processes running inside a container. Builder (/build): - Simplify the upload of the build context -- Simply stream a tarball instead of multipart upload with 4 intermediary buffers +- Simply stream a tarball instead of multipart upload with 4 + intermediary buffers - Simpler, less memory usage, less disk usage and faster -.. Note:: -The /build improvements are not reverse-compatible. Pre 1.3 clients will break on /build. +.. Warning:: + + The /build improvements are not reverse-compatible. Pre 1.3 clients + will break on /build. List containers (/containers/json): @@ -49,7 +62,8 @@ List containers (/containers/json): Start containers (/containers//start): -- You can now pass host-specific configuration (e.g. bind mounts) in the POST body for start calls +- You can now pass host-specific configuration (e.g. bind mounts) in + the POST body for start calls :doc:`docker_remote_api_v1.2` ***************************** @@ -60,14 +74,25 @@ What's new ---------- The auth configuration is now handled by the client. -The client should send it's authConfig as POST on each call of /images/(name)/push -.. http:get:: /auth is now deprecated -.. http:post:: /auth only checks the configuration but doesn't store it on the server +The client should send it's authConfig as POST on each call of +/images/(name)/push -Deleting an image is now improved, will only untag the image if it has chidrens and remove all the untagged parents if has any. +.. http:get:: /auth -.. http:post:: /images//delete now returns a JSON with the list of images deleted/untagged + **Deprecated.** + +.. http:post:: /auth + + Only checks the configuration but doesn't store it on the server + + Deleting an image is now improved, will only untag the image if it + has chidren and remove all the untagged parents if has any. + +.. http:post:: /images//delete + + Now returns a JSON structure with the list of images + deleted/untagged. :doc:`docker_remote_api_v1.1` @@ -82,7 +107,7 @@ What's new .. http:post:: /images/(name)/insert .. http:post:: /images/(name)/push -Uses json stream instead of HTML hijack, it looks like this: + Uses json stream instead of HTML hijack, it looks like this: .. sourcecode:: http diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.0.rst b/components/engine/docs/sources/api/docker_remote_api_v1.0.rst index a789337093..5aa98cbe59 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.0.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.0.rst @@ -1,3 +1,8 @@ +.. use orphan to suppress "WARNING: document isn't included in any toctree" +.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata + +:orphan: + :title: Remote API v1.0 :description: API Documentation for Docker :keywords: API, Docker, rcli, REST, documentation @@ -300,8 +305,8 @@ Start a container :statuscode 500: server error -Stop a contaier -*************** +Stop a container +**************** .. http:post:: /containers/(id)/stop diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.1.rst b/components/engine/docs/sources/api/docker_remote_api_v1.1.rst index 3e0ef34eba..e0159ddb65 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.1.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.1.rst @@ -1,3 +1,7 @@ +.. use orphan to suppress "WARNING: document isn't included in any toctree" +.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata + +:orphan: :title: Remote API v1.1 :description: API Documentation for Docker diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.2.rst b/components/engine/docs/sources/api/docker_remote_api_v1.2.rst index a6c2c31920..96ee6bb9bb 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.2.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.2.rst @@ -1,3 +1,8 @@ +.. use orphan to suppress "WARNING: document isn't included in any toctree" +.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata + +:orphan: + :title: Remote API v1.2 :description: API Documentation for Docker :keywords: API, Docker, rcli, REST, documentation diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index 9f33365e81..273ec2e98d 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst @@ -1,3 +1,8 @@ +.. use orphan to suppress "WARNING: document isn't included in any toctree" +.. per http://sphinx-doc.org/markup/misc.html#file-wide-metadata + +:orphan: + :title: Remote API v1.3 :description: API Documentation for Docker :keywords: API, Docker, rcli, REST, documentation diff --git a/components/engine/docs/sources/api/index_api.rst b/components/engine/docs/sources/api/index_api.rst index 42dc49a5d7..1d4f475caf 100644 --- a/components/engine/docs/sources/api/index_api.rst +++ b/components/engine/docs/sources/api/index_api.rst @@ -452,7 +452,7 @@ User Register "username": "foobar"'} :jsonparameter email: valid email address, that needs to be confirmed - :jsonparameter username: min 4 character, max 30 characters, must match the regular expression [a-z0-9_]. + :jsonparameter username: min 4 character, max 30 characters, must match the regular expression [a-z0-9\_]. :jsonparameter password: min 5 characters **Example Response**: diff --git a/components/engine/docs/sources/api/registry_index_spec.rst b/components/engine/docs/sources/api/registry_index_spec.rst index c1854194b2..3ae39e37d9 100644 --- a/components/engine/docs/sources/api/registry_index_spec.rst +++ b/components/engine/docs/sources/api/registry_index_spec.rst @@ -367,7 +367,8 @@ POST /v1/users {"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'} **Validation**: - - **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_]. + - **username**: min 4 character, max 30 characters, must match the regular + expression [a-z0-9\_]. - **password**: min 5 characters **Valid**: return HTTP 200 @@ -566,4 +567,4 @@ Next request:: --------------------- - 1.0 : May 6th 2013 : initial release -- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new source namespace. \ No newline at end of file +- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new source namespace. diff --git a/components/engine/docs/sources/index.rst b/components/engine/docs/sources/index.rst index 05e69dd8e5..ba8f60c3fa 100644 --- a/components/engine/docs/sources/index.rst +++ b/components/engine/docs/sources/index.rst @@ -2,8 +2,6 @@ :description: An overview of the Docker Documentation :keywords: containers, lxc, concepts, explanation -.. _introduction: - Welcome ======= diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index 9ea8033b98..7f370609c8 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -1,6 +1,6 @@ -:title: Dockerfile Builder -:description: Docker Builder specifes a simple DSL which allows you to automate the steps you would normally manually take to create an image. -:keywords: builder, docker, Docker Builder, automation, image creation +:title: Dockerfiles for Images +:description: Dockerfiles use a simple DSL which allows you to automate the steps you would normally manually take to create an image. +:keywords: builder, docker, Dockerfile, automation, image creation ================== Dockerfile Builder @@ -177,7 +177,7 @@ The copy obeys the following rules: with mode 0700, uid and gid 0. 3.8 ENTRYPOINT -------------- +-------------- ``ENTRYPOINT /bin/echo`` diff --git a/components/engine/docs/sources/use/workingwithrepository.rst b/components/engine/docs/sources/use/workingwithrepository.rst index 3cdbfe49d6..4a2e39aea1 100644 --- a/components/engine/docs/sources/use/workingwithrepository.rst +++ b/components/engine/docs/sources/use/workingwithrepository.rst @@ -119,7 +119,7 @@ your container to an image within your username namespace. Pushing a container to its repository ------------------------------------- +------------------------------------- In order to push an image to its repository you need to have committed your container to a named image (see above) From a6f1fb98b1bf385b6169609ca602413ffa9d634a Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 02:47:35 +0000 Subject: [PATCH 40/80] fix overwrites EXPOSE Upstream-commit: a0eec14c7da5b213302c2675801aaf788e84efed Component: engine --- components/engine/utils.go | 2 +- components/engine/utils_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/engine/utils.go b/components/engine/utils.go index 33cfbe506f..16efccd387 100644 --- a/components/engine/utils.go +++ b/components/engine/utils.go @@ -78,7 +78,7 @@ func MergeConfig(userConf, imageConf *Config) { imageNat, _ := parseNat(imagePortSpec) for _, userPortSpec := range userConf.PortSpecs { userNat, _ := parseNat(userPortSpec) - if imageNat.Proto == userNat.Proto && imageNat.Frontend == userNat.Frontend { + if imageNat.Proto == userNat.Proto && imageNat.Backend == userNat.Backend { found = true } } diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 1bd18f4af0..5ea3682bac 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -148,7 +148,7 @@ func TestMergeConfig(t *testing.T) { volumesUser["/test3"] = struct{}{} configUser := &Config{ Dns: []string{"3.3.3.3"}, - PortSpecs: []string{"2222:3333", "3333:3333"}, + PortSpecs: []string{"3333:2222", "3333:3333"}, Env: []string{"VAR2=3", "VAR3=3"}, Volumes: volumesUser, } @@ -165,11 +165,11 @@ func TestMergeConfig(t *testing.T) { } if len(configUser.PortSpecs) != 3 { - t.Fatalf("Expected 3 portSpecs, 1111:1111, 2222:3333 and 3333:3333, found %d", len(configUser.PortSpecs)) + t.Fatalf("Expected 3 portSpecs, 1111:1111, 3333:2222 and 3333:3333, found %d", len(configUser.PortSpecs)) } for _, portSpecs := range configUser.PortSpecs { - if portSpecs != "1111:1111" && portSpecs != "2222:3333" && portSpecs != "3333:3333" { - t.Fatalf("Expected 1111:1111 or 2222:3333 or 3333:3333, found %s", portSpecs) + if portSpecs != "1111:1111" && portSpecs != "3333:2222" && portSpecs != "3333:3333" { + t.Fatalf("Expected 1111:1111 or 3333:2222 or 3333:3333, found %s", portSpecs) } } if len(configUser.Env) != 3 { From 31b43ff70e4fde323653f6bd55e31561697cace9 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 03:01:39 +0000 Subject: [PATCH 41/80] add regression test from @crosbymichael Upstream-commit: 2b5386f039b5c99cf0f64fb3091cc14e4446dc64 Component: engine --- components/engine/utils_test.go | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 5ea3682bac..78f3afa66c 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -190,3 +190,45 @@ func TestMergeConfig(t *testing.T) { } } } + +func TestMergeConfigPublicPortNotHonored(t *testing.T) { + volumesImage := make(map[string]struct{}) + volumesImage["/test1"] = struct{}{} + volumesImage["/test2"] = struct{}{} + configImage := &Config{ + Dns: []string{"1.1.1.1", "2.2.2.2"}, + PortSpecs: []string{"1111", "2222"}, + Env: []string{"VAR1=1", "VAR2=2"}, + Volumes: volumesImage, + } + + volumesUser := make(map[string]struct{}) + volumesUser["/test3"] = struct{}{} + configUser := &Config{ + Dns: []string{"3.3.3.3"}, + PortSpecs: []string{"1111:3333"}, + Env: []string{"VAR2=3", "VAR3=3"}, + Volumes: volumesUser, + } + + MergeConfig(configUser, configImage) + + contains := func(a []string, expect string) bool { + for _, p := range a { + if p == expect { + return true + } + } + return false + } + + if !contains(configUser.PortSpecs, "2222") { + t.Logf("Expected '2222' Ports: %v", configUser.PortSpecs) + t.Fail() + } + + if !contains(configUser.PortSpecs, "1111:3333") { + t.Logf("Expected '1111:3333' Ports: %v", configUser.PortSpecs) + t.Fail() + } +} From 3498daf9b5fe147e8151e44e931d9236e45aeea0 Mon Sep 17 00:00:00 2001 From: unclejack Date: Mon, 8 Jul 2013 11:01:16 +0300 Subject: [PATCH 42/80] add support for container ID files (a la pidfile) Upstream-commit: 64e74cefb746caa7f2a581149bbd523dd1ac9215 Component: engine --- components/engine/commands.go | 14 ++++++++++++++ components/engine/container.go | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 936b23fea2..42cccf6b96 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1341,6 +1341,20 @@ func (cli *DockerCli) CmdRun(args ...string) error { for _, warning := range runResult.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } + if len(hostConfig.ContainerIDFile) > 0 { + if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { + return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) + } + file, err := os.Create(hostConfig.ContainerIDFile) + if err != nil { + return fmt.Errorf("failed to create the container ID file: %s", err) + } + + defer file.Close() + if _, err = file.WriteString(runResult.ID); err != nil { + return fmt.Errorf("failed to write the container ID to the file: %s", err) + } + } //start the container if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { diff --git a/components/engine/container.go b/components/engine/container.go index 95c5ba0f72..4443ad52a3 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -80,7 +80,8 @@ type Config struct { } type HostConfig struct { - Binds []string + Binds []string + ContainerIDFile string } type BindMap struct { @@ -103,6 +104,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached") flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)") + flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file") if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -190,7 +192,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Entrypoint: entrypoint, } hostConfig := &HostConfig{ - Binds: binds, + Binds: binds, + ContainerIDFile: *flContainerIDFile, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { From ce2e737919772137afa268bd944e55c176f28ccc Mon Sep 17 00:00:00 2001 From: unclejack Date: Thu, 11 Jul 2013 23:38:43 +0300 Subject: [PATCH 43/80] docs - add cidfile flag to run docs Upstream-commit: 221ee504aa06d06eb868898cca2fcc020a861e84 Component: engine --- components/engine/docs/sources/commandline/command/run.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index 1a14a9b616..bfd35738fa 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -14,6 +14,7 @@ -a=map[]: Attach to stdin, stdout or stderr. -c=0: CPU shares (relative weight) + -cidfile="": Write the container ID to the file -d=false: Detached mode: leave the container running in the background -e=[]: Set environment variables -h="": Container host name From 59b7a438c9d2201429d643a94740cba1fbd34748 Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 12 Jul 2013 00:50:03 +0300 Subject: [PATCH 44/80] docs - add example for cidfile Upstream-commit: 2a3b91e3b66c48c6a26dbd673957a46c1afacbbe Component: engine --- .../engine/docs/sources/commandline/command/run.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index bfd35738fa..19efd85821 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -27,3 +27,13 @@ -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume. -volumes-from="": Mount all volumes from the given container. -entrypoint="": Overwrite the default entrypoint set by the image. + + +Examples +-------- + +.. code-block:: bash + + docker run -cidfile /tmp/docker_test.cid ubuntu echo "test" + +| This will create a container and print "test" to the console. The cidfile flag makes docker attempt to create a new file and write the container ID to it. If the file exists already, docker will return an error. Docker will close this file when docker run exits. From 1b1ac70ce1e733a2c7a93c8933f0fded1a69af5d Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 12 Jul 2013 01:11:44 +0300 Subject: [PATCH 45/80] create the cidfile before creating the container This change makes docker attempt to create the container ID file and open it before attempting to create the container. This avoids leaving a stale container behind if docker has failed to create and open the container ID file. The container ID is written to the file after the container is created. Upstream-commit: 25be79208a1473a65be883989ae49b7c71081a83 Component: engine --- components/engine/commands.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 42cccf6b96..7f8f9eec0b 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1311,6 +1311,18 @@ func (cli *DockerCli) CmdRun(args ...string) error { return nil } + var containerIDFile *os.File + if len(hostConfig.ContainerIDFile) > 0 { + if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { + return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) + } + containerIDFile, err = os.Create(hostConfig.ContainerIDFile) + if err != nil { + return fmt.Errorf("failed to create the container ID file: %s", err) + } + defer containerIDFile.Close() + } + //create the container body, statusCode, err := cli.call("POST", "/containers/create", config) //if image not found try to pull it @@ -1342,16 +1354,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } if len(hostConfig.ContainerIDFile) > 0 { - if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { - return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) - } - file, err := os.Create(hostConfig.ContainerIDFile) - if err != nil { - return fmt.Errorf("failed to create the container ID file: %s", err) - } - - defer file.Close() - if _, err = file.WriteString(runResult.ID); err != nil { + if _, err = containerIDFile.WriteString(runResult.ID); err != nil { return fmt.Errorf("failed to write the container ID to the file: %s", err) } } From f380201be8fc9f5173d89ad5b7188733cd93390f Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 13:56:36 +0000 Subject: [PATCH 46/80] fix error in utils tests Upstream-commit: 2e3b660dd0d49dc78f4c486e952ea6db9c007d6a Component: engine --- components/engine/utils/utils_test.go | 31 ++++++++------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/components/engine/utils/utils_test.go b/components/engine/utils/utils_test.go index 2c68be50e3..5caa809f67 100644 --- a/components/engine/utils/utils_test.go +++ b/components/engine/utils/utils_test.go @@ -60,9 +60,9 @@ func TestWriteBroadcaster(t *testing.T) { // Test 1: Both bufferA and bufferB should contain "foo" bufferA := &dummyWriter{} - writer.AddWriter(bufferA) + writer.AddWriter(bufferA, "") bufferB := &dummyWriter{} - writer.AddWriter(bufferB) + writer.AddWriter(bufferB, "") writer.Write([]byte("foo")) if bufferA.String() != "foo" { @@ -76,7 +76,7 @@ func TestWriteBroadcaster(t *testing.T) { // Test2: bufferA and bufferB should contain "foobar", // while bufferC should only contain "bar" bufferC := &dummyWriter{} - writer.AddWriter(bufferC) + writer.AddWriter(bufferC, "") writer.Write([]byte("bar")) if bufferA.String() != "foobar" { @@ -91,35 +91,22 @@ func TestWriteBroadcaster(t *testing.T) { t.Errorf("Buffer contains %v", bufferC.String()) } - // Test3: Test removal - writer.RemoveWriter(bufferB) - writer.Write([]byte("42")) - if bufferA.String() != "foobar42" { - t.Errorf("Buffer contains %v", bufferA.String()) - } - if bufferB.String() != "foobar" { - t.Errorf("Buffer contains %v", bufferB.String()) - } - if bufferC.String() != "bar42" { - t.Errorf("Buffer contains %v", bufferC.String()) - } - - // Test4: Test eviction on failure + // Test3: Test eviction on failure bufferA.failOnWrite = true writer.Write([]byte("fail")) - if bufferA.String() != "foobar42" { + if bufferA.String() != "foobar" { t.Errorf("Buffer contains %v", bufferA.String()) } - if bufferC.String() != "bar42fail" { + if bufferC.String() != "barfail" { t.Errorf("Buffer contains %v", bufferC.String()) } // Even though we reset the flag, no more writes should go in there bufferA.failOnWrite = false writer.Write([]byte("test")) - if bufferA.String() != "foobar42" { + if bufferA.String() != "foobar" { t.Errorf("Buffer contains %v", bufferA.String()) } - if bufferC.String() != "bar42failtest" { + if bufferC.String() != "barfailtest" { t.Errorf("Buffer contains %v", bufferC.String()) } @@ -141,7 +128,7 @@ func TestRaceWriteBroadcaster(t *testing.T) { writer := NewWriteBroadcaster() c := make(chan bool) go func() { - writer.AddWriter(devNullCloser(0)) + writer.AddWriter(devNullCloser(0), "") c <- true }() writer.Write([]byte("hello")) From 1ef21555e984d3354ea98f6970e76cf11857c1ef Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Fri, 19 Jul 2013 10:11:21 -0500 Subject: [PATCH 47/80] Make the ENTRYPOINT example work The incantation listed in the ENTRYPOINT example didn't actually pass the arguments to your script. Changing the definition to an array fixes this. Upstream-commit: e8ad82f9ba126414e1813fadfb17167a34afa8d4 Component: engine --- components/engine/docs/sources/use/builder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index 9ea8033b98..3f7a8e8ef1 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -179,7 +179,7 @@ The copy obeys the following rules: 3.8 ENTRYPOINT ------------- - ``ENTRYPOINT /bin/echo`` + ``ENTRYPOINT ["/bin/echo"]`` The ``ENTRYPOINT`` instruction adds an entry command that will not be overwritten when arguments are passed to docker run, unlike the From 6e52a096e90c22f304b503be807dfbf80014a9d3 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 15:56:00 +0000 Subject: [PATCH 48/80] remove usage from tests Upstream-commit: ea1258852493a83754553163db1db52a72ffb8fc Component: engine --- components/engine/container.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/container.go b/components/engine/container.go index d0a0dd7714..f18aa0fe74 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -94,6 +94,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container") if len(args) > 0 && args[0] != "--help" { cmd.SetOutput(ioutil.Discard) + cmd.Usage = nil } flHostname := cmd.String("h", "", "Container host name") From d5db6e05ee05d3b52889e0f7b047444b72f006b6 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 17:22:16 +0000 Subject: [PATCH 49/80] add container=lxc in default env Upstream-commit: 67f1e3f5ed4d3061e07e910e28ac866b7bb13e18 Component: engine --- components/engine/container.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/container.go b/components/engine/container.go index d0a0dd7714..f472b199ea 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -640,6 +640,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { params = append(params, "-e", "HOME=/", "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "-e", "container=lxc", ) for _, elem := range container.Config.Env { From 885edf50ba36a40d8fd7455eec5f6710ed884155 Mon Sep 17 00:00:00 2001 From: dsissitka Date: Sat, 20 Jul 2013 21:27:55 -0400 Subject: [PATCH 50/80] Fixed a couple of minor syntax errors. Upstream-commit: 32663bf431b64d1169509bacba36e3c90e131b44 Component: engine --- components/engine/docs/sources/contributing/devenvironment.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/engine/docs/sources/contributing/devenvironment.rst b/components/engine/docs/sources/contributing/devenvironment.rst index 6f5d6c1dc1..869cc43749 100644 --- a/components/engine/docs/sources/contributing/devenvironment.rst +++ b/components/engine/docs/sources/contributing/devenvironment.rst @@ -46,11 +46,13 @@ in a standard build environment. You can run an interactive session in the newly built container: :: + docker run -i -t docker bash To extract the binaries from the container: :: + docker run docker sh -c 'cat $(which docker)' > docker-build && chmod +x docker-build From d051cf7ec4071775663c8daae13cec04ded327e5 Mon Sep 17 00:00:00 2001 From: dsissitka Date: Sun, 21 Jul 2013 18:30:51 -0400 Subject: [PATCH 51/80] Added top to the list of commands in the sidebar. Upstream-commit: 788935175e8500b451a844f6971ac62dd099bdfc Component: engine --- components/engine/docs/sources/commandline/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/engine/docs/sources/commandline/index.rst b/components/engine/docs/sources/commandline/index.rst index f1a3e2da45..a7296b27da 100644 --- a/components/engine/docs/sources/commandline/index.rst +++ b/components/engine/docs/sources/commandline/index.rst @@ -37,5 +37,6 @@ Contents: start stop tag + top version - wait \ No newline at end of file + wait From 52d25803516835443e9d3b64cfb88c10284bb6e6 Mon Sep 17 00:00:00 2001 From: David Sissitka Date: Sun, 21 Jul 2013 19:00:18 -0400 Subject: [PATCH 52/80] Updated the stop command's docs. Upstream-commit: 1d02a7ffb63915055f5fd9bda420bd08a8679da1 Component: engine --- components/engine/commands.go | 2 +- components/engine/docs/sources/commandline/command/stop.rst | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index f0e1695b3f..86385f4187 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -475,7 +475,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error { cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container") - nSeconds := cmd.Int("t", 10, "Number of seconds to try to stop for before killing the container. Default=10") + nSeconds := cmd.Int("t", 10, "Number of seconds to wait for the container to stop before killing it.") if err := cmd.Parse(args); err != nil { return nil } diff --git a/components/engine/docs/sources/commandline/command/stop.rst b/components/engine/docs/sources/commandline/command/stop.rst index 3d571563ec..6a64908eae 100644 --- a/components/engine/docs/sources/commandline/command/stop.rst +++ b/components/engine/docs/sources/commandline/command/stop.rst @@ -8,6 +8,8 @@ :: - Usage: docker stop [OPTIONS] NAME + Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] Stop a running container + + -t=10: Number of seconds to wait for the container to stop before killing it. From edbf8f58a1d6cea66e7b6f74c474f415c59055e0 Mon Sep 17 00:00:00 2001 From: Stefan Praszalowicz Date: Sun, 21 Jul 2013 17:11:47 -0700 Subject: [PATCH 53/80] Support networkless containers with new docker run option '-n' Upstream-commit: 3342bdb33184b83cac66921807c5403168d13f6b Component: engine --- components/engine/container.go | 86 +++++++++++-------- components/engine/container_test.go | 38 ++++++++ .../docs/sources/commandline/command/run.rst | 1 + components/engine/lxc_template.go | 5 ++ 4 files changed, 93 insertions(+), 37 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index f18aa0fe74..6ae9ab5a44 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -58,25 +58,26 @@ type Container struct { } type Config struct { - Hostname string - User string - Memory int64 // Memory limit (in bytes) - MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap - CpuShares int64 // CPU shares (relative weight vs. other containers) - AttachStdin bool - AttachStdout bool - AttachStderr bool - PortSpecs []string - Tty bool // Attach standard streams to a tty, including stdin if it is not closed. - OpenStdin bool // Open stdin - StdinOnce bool // If true, close stdin after the 1 attached client disconnects. - Env []string - Cmd []string - Dns []string - Image string // Name of the image as it was passed by the operator (eg. could be symbolic) - Volumes map[string]struct{} - VolumesFrom string - Entrypoint []string + Hostname string + User string + Memory int64 // Memory limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap + CpuShares int64 // CPU shares (relative weight vs. other containers) + AttachStdin bool + AttachStdout bool + AttachStderr bool + PortSpecs []string + Tty bool // Attach standard streams to a tty, including stdin if it is not closed. + OpenStdin bool // Open stdin + StdinOnce bool // If true, close stdin after the 1 attached client disconnects. + Env []string + Cmd []string + Dns []string + Image string // Name of the image as it was passed by the operator (eg. could be symbolic) + Volumes map[string]struct{} + VolumesFrom string + Entrypoint []string + NetworkEnabled bool } type HostConfig struct { @@ -106,6 +107,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)") flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file") + flNetwork := cmd.Bool("n", true, "Enable networking for this container") if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -174,23 +176,24 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } config := &Config{ - Hostname: *flHostname, - PortSpecs: flPorts, - User: *flUser, - Tty: *flTty, - OpenStdin: *flStdin, - Memory: *flMemory, - CpuShares: *flCpuShares, - AttachStdin: flAttach.Get("stdin"), - AttachStdout: flAttach.Get("stdout"), - AttachStderr: flAttach.Get("stderr"), - Env: flEnv, - Cmd: runCmd, - Dns: flDns, - Image: image, - Volumes: flVolumes, - VolumesFrom: *flVolumesFrom, - Entrypoint: entrypoint, + Hostname: *flHostname, + PortSpecs: flPorts, + User: *flUser, + Tty: *flTty, + NetworkEnabled: *flNetwork, + OpenStdin: *flStdin, + Memory: *flMemory, + CpuShares: *flCpuShares, + AttachStdin: flAttach.Get("stdin"), + AttachStdout: flAttach.Get("stdout"), + AttachStderr: flAttach.Get("stderr"), + Env: flEnv, + Cmd: runCmd, + Dns: flDns, + Image: image, + Volumes: flVolumes, + VolumesFrom: *flVolumesFrom, + Entrypoint: entrypoint, } hostConfig := &HostConfig{ Binds: binds, @@ -626,7 +629,9 @@ func (container *Container) Start(hostConfig *HostConfig) error { } // Networking - params = append(params, "-g", container.network.Gateway.String()) + if container.Config.NetworkEnabled { + params = append(params, "-g", container.network.Gateway.String()) + } // User if container.Config.User != "" { @@ -727,6 +732,10 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) { } func (container *Container) allocateNetwork() error { + if !container.Config.NetworkEnabled { + return nil + } + iface, err := container.runtime.networkManager.Allocate() if err != nil { return err @@ -753,6 +762,9 @@ func (container *Container) allocateNetwork() error { } func (container *Container) releaseNetwork() { + if !container.Config.NetworkEnabled { + return + } container.network.Release() container.network = nil container.NetworkSettings = &NetworkSettings{} diff --git a/components/engine/container_test.go b/components/engine/container_test.go index 028c03a318..8d4bbf6c74 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1251,3 +1251,41 @@ func TestRestartWithVolumes(t *testing.T) { t.Fatalf("Expected volume path: %s Actual path: %s", expected, actual) } } + +func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + config, hc, _, err := ParseRun([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil) + if err != nil { + t.Fatal(err) + } + c, err := NewBuilder(runtime).Create(config) + if err != nil { + t.Fatal(err) + } + stdout, err := c.StdoutPipe() + if err != nil { + t.Fatal(err) + } + + defer runtime.Destroy(c) + if err := c.Start(hc); err != nil { + t.Fatal(err) + } + c.WaitTimeout(500 * time.Millisecond) + c.Wait() + output, err := ioutil.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + + interfaces := regexp.MustCompile(`(?m)^[0-9]+: [a-zA-Z0-9]+`).FindAllString(string(output), -1) + if len(interfaces) != 1 { + t.Fatalf("Wrong interface count in test container: expected [1: lo], got [%s]", interfaces) + } + if interfaces[0] != "1: lo" { + t.Fatalf("Wrong interface in test container: expected [1: lo], got [%s]", interfaces) + } + +} diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index 19efd85821..db67ef0705 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -20,6 +20,7 @@ -h="": Container host name -i=false: Keep stdin open even if not attached -m=0: Memory limit (in bytes) + -n=true: Enable networking for this container -p=[]: Map a network port to the container -t=false: Allocate a pseudo-tty -u="": Username or UID diff --git a/components/engine/lxc_template.go b/components/engine/lxc_template.go index 93b795e901..27670c8076 100644 --- a/components/engine/lxc_template.go +++ b/components/engine/lxc_template.go @@ -13,6 +13,7 @@ lxc.utsname = {{.Id}} {{end}} #lxc.aa_profile = unconfined +{{if .Config.NetworkEnabled}} # network configuration lxc.network.type = veth lxc.network.flags = up @@ -20,6 +21,10 @@ lxc.network.link = {{.NetworkSettings.Bridge}} lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}} +{{else}} +# Network configuration disabled +lxc.network.type = empty +{{end}} # root filesystem {{$ROOTFS := .RootfsPath}} From ca9f0f0f1d03aa55725621064d1efbc2ee779ea9 Mon Sep 17 00:00:00 2001 From: Stefan Praszalowicz Date: Sun, 21 Jul 2013 17:49:09 -0700 Subject: [PATCH 54/80] Support completely disabling network configuration with docker -d -b none Upstream-commit: 49673fc45cc5cfc15219bf1eb6eaff7621696919 Component: engine --- components/engine/container.go | 8 ++++++-- components/engine/network.go | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index 6ae9ab5a44..189e882c44 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -514,8 +514,12 @@ func (container *Container) Start(hostConfig *HostConfig) error { if err := container.EnsureMounted(); err != nil { return err } - if err := container.allocateNetwork(); err != nil { - return err + if container.runtime.networkManager.disabled { + container.Config.NetworkEnabled = false + } else { + if err := container.allocateNetwork(); err != nil { + return err + } } // Make sure the config is compatible with the current kernel diff --git a/components/engine/network.go b/components/engine/network.go index 0f98c899f1..c71ecfb3ae 100644 --- a/components/engine/network.go +++ b/components/engine/network.go @@ -17,6 +17,7 @@ var NetworkBridgeIface string const ( DefaultNetworkBridge = "docker0" + DisableNetworkBridge = "none" portRangeStart = 49153 portRangeEnd = 65535 ) @@ -453,10 +454,16 @@ type NetworkInterface struct { manager *NetworkManager extPorts []*Nat + disabled bool } // Allocate an external TCP port and map it to the interface func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) { + + if iface.disabled { + return nil, fmt.Errorf("Trying to allocate port for interface %v, which is disabled", iface) // FIXME + } + nat, err := parseNat(spec) if err != nil { return nil, err @@ -552,6 +559,11 @@ func parseNat(spec string) (*Nat, error) { // Release: Network cleanup - release all resources func (iface *NetworkInterface) Release() { + + if iface.disabled { + return + } + for _, nat := range iface.extPorts { utils.Debugf("Unmaping %v/%v", nat.Proto, nat.Frontend) if err := iface.manager.portMapper.Unmap(nat.Frontend, nat.Proto); err != nil { @@ -579,10 +591,17 @@ type NetworkManager struct { tcpPortAllocator *PortAllocator udpPortAllocator *PortAllocator portMapper *PortMapper + + disabled bool } // Allocate a network interface func (manager *NetworkManager) Allocate() (*NetworkInterface, error) { + + if manager.disabled { + return &NetworkInterface{disabled: true}, nil + } + ip, err := manager.ipAllocator.Acquire() if err != nil { return nil, err @@ -596,6 +615,14 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) { } func newNetworkManager(bridgeIface string) (*NetworkManager, error) { + + if bridgeIface == DisableNetworkBridge { + manager := &NetworkManager{ + disabled: true, + } + return manager, nil + } + addr, err := getIfaceAddr(bridgeIface) if err != nil { // If the iface is not found, try to create it From 466341e775d86c7ccece302d97ed2f05bf14ac74 Mon Sep 17 00:00:00 2001 From: Stefan Praszalowicz Date: Sun, 21 Jul 2013 18:01:52 -0700 Subject: [PATCH 55/80] Document -b none Upstream-commit: 964e826a9beb69a0246ed22c7935fd86df6712e1 Component: engine --- components/engine/docker/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index fb7c465369..2db50bf328 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -28,7 +28,7 @@ func main() { flDaemon := flag.Bool("d", false, "Daemon mode") flDebug := flag.Bool("D", false, "Debug mode") flAutoRestart := flag.Bool("r", false, "Restart previously running containers") - bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge") + bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking") pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.") flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") From 02d950665461707b288f8021d2c0670c5988cdb8 Mon Sep 17 00:00:00 2001 From: Caleb Spare Date: Sun, 7 Jul 2013 23:24:52 -0700 Subject: [PATCH 56/80] Test pulling remote files using ADD in a buildfile. Upstream-commit: f236e62d9d288ac695129157d1753512f5cd2b0a Component: engine --- components/engine/buildfile_test.go | 117 +++++++++++++++++++++------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 14edbc088f..602af5061b 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -3,13 +3,17 @@ package docker import ( "fmt" "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "strings" "testing" ) // mkTestContext generates a build context from the contents of the provided dockerfile. // This context is suitable for use as an argument to BuildFile.Build() func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive { - context, err := mkBuildContext(fmt.Sprintf(dockerfile, unitTestImageID), files) + context, err := mkBuildContext(dockerfile, files) if err != nil { t.Fatal(err) } @@ -22,6 +26,8 @@ type testContextTemplate struct { dockerfile string // Additional files in the context, eg [][2]string{"./passwd", "gordon"} files [][2]string + // Additional remote files to host on a local HTTP server. + remoteFiles [][2]string } // A table of all the contexts to build and test. @@ -29,27 +35,31 @@ type testContextTemplate struct { var testContexts = []testContextTemplate{ { ` -from %s +from {IMAGE} run sh -c 'echo root:testpass > /tmp/passwd' run mkdir -p /var/run/sshd run [ "$(cat /tmp/passwd)" = "root:testpass" ] run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ] `, nil, + nil, }, { ` -from %s +from {IMAGE} add foo /usr/lib/bla/bar -run [ "$(cat /usr/lib/bla/bar)" = 'hello world!' ] +run [ "$(cat /usr/lib/bla/bar)" = 'hello' ] +add http://{SERVERADDR}/baz /usr/lib/baz/quux +run [ "$(cat /usr/lib/baz/quux)" = 'world!' ] `, - [][2]string{{"foo", "hello world!"}}, + [][2]string{{"foo", "hello"}}, + [][2]string{{"/baz", "world!"}}, }, { ` -from %s +from {IMAGE} add f / run [ "$(cat /f)" = "hello" ] add f /abc @@ -71,38 +81,70 @@ run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ] {"f", "hello"}, {"d/ga", "bu"}, }, + nil, }, { ` -from %s +from {IMAGE} env FOO BAR run [ "$FOO" = "BAR" ] `, nil, - }, - - { - ` -from %s -ENTRYPOINT /bin/echo -CMD Hello world -`, nil, }, { ` -from %s +from {IMAGE} +ENTRYPOINT /bin/echo +CMD Hello world +`, + nil, + nil, + }, + + { + ` +from {IMAGE} VOLUME /test CMD Hello world `, nil, + nil, }, } // FIXME: test building with 2 successive overlapping ADD commands +func constructDockerfile(template string, ip net.IP, port string) string { + serverAddr := fmt.Sprintf("%s:%s", ip, port) + replacer := strings.NewReplacer("{IMAGE}", unitTestImageID, "{SERVERADDR}", serverAddr) + return replacer.Replace(template) +} + +func mkTestingFileServer(files [][2]string) (*httptest.Server, error) { + mux := http.NewServeMux() + for _, file := range files { + name, contents := file[0], file[1] + mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(contents)) + }) + } + + // This is how httptest.NewServer sets up a net.Listener, except that our listener must accept remote + // connections (from the container). + listener, err := net.Listen("tcp", ":0") + if err != nil { + return nil, err + } + + s := httptest.NewUnstartedServer(mux) + s.Listener = listener + s.Start() + return s, nil +} + func TestBuild(t *testing.T) { for _, ctx := range testContexts { buildImage(ctx, t) @@ -121,9 +163,24 @@ func buildImage(context testContextTemplate, t *testing.T) *Image { pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard, false) - id, err := buildfile.Build(mkTestContext(context.dockerfile, context.files, t)) + httpServer, err := mkTestingFileServer(context.remoteFiles) + if err != nil { + t.Fatal(err) + } + defer httpServer.Close() + + idx := strings.LastIndex(httpServer.URL, ":") + if idx < 0 { + t.Fatalf("could not get port from test http server address %s", httpServer.URL) + } + port := httpServer.URL[idx+1:] + + ip := runtime.networkManager.bridgeNetwork.IP + dockerfile := constructDockerfile(context.dockerfile, ip, port) + + buildfile := NewBuildFile(srv, ioutil.Discard, false) + id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err != nil { t.Fatal(err) } @@ -137,10 +194,10 @@ func buildImage(context testContextTemplate, t *testing.T) *Image { func TestVolume(t *testing.T) { img := buildImage(testContextTemplate{` - from %s + from {IMAGE} volume /test cmd Hello world - `, nil}, t) + `, nil, nil}, t) if len(img.Config.Volumes) == 0 { t.Fail() @@ -154,9 +211,9 @@ func TestVolume(t *testing.T) { func TestBuildMaintainer(t *testing.T) { img := buildImage(testContextTemplate{` - from %s + from {IMAGE} maintainer dockerio - `, nil}, t) + `, nil, nil}, t) if img.Author != "dockerio" { t.Fail() @@ -165,10 +222,10 @@ func TestBuildMaintainer(t *testing.T) { func TestBuildEnv(t *testing.T) { img := buildImage(testContextTemplate{` - from %s + from {IMAGE} env port 4243 `, - nil}, t) + nil, nil}, t) if img.Config.Env[0] != "port=4243" { t.Fail() @@ -177,10 +234,10 @@ func TestBuildEnv(t *testing.T) { func TestBuildCmd(t *testing.T) { img := buildImage(testContextTemplate{` - from %s + from {IMAGE} cmd ["/bin/echo", "Hello World"] `, - nil}, t) + nil, nil}, t) if img.Config.Cmd[0] != "/bin/echo" { t.Log(img.Config.Cmd[0]) @@ -194,10 +251,10 @@ func TestBuildCmd(t *testing.T) { func TestBuildExpose(t *testing.T) { img := buildImage(testContextTemplate{` - from %s + from {IMAGE} expose 4243 `, - nil}, t) + nil, nil}, t) if img.Config.PortSpecs[0] != "4243" { t.Fail() @@ -206,10 +263,10 @@ func TestBuildExpose(t *testing.T) { func TestBuildEntrypoint(t *testing.T) { img := buildImage(testContextTemplate{` - from %s + from {IMAGE} entrypoint ["/bin/echo"] `, - nil}, t) + nil, nil}, t) if img.Config.Entrypoint[0] != "/bin/echo" { } From adbaef1554c2ef026c3bb1ecd59df1ce2fd6aa26 Mon Sep 17 00:00:00 2001 From: Caleb Spare Date: Mon, 8 Jul 2013 00:43:22 -0700 Subject: [PATCH 57/80] Buildfile: for ADD command, determine filename from URL. This is used if the destination is a directory. This makes the URL download behavior more closely match file copying. Fixes #1142. Upstream-commit: 2b0ebf5d32c65276ce50fce168f32483ffb9c311 Component: engine --- components/engine/buildfile.go | 21 ++++++++++++++++++++- components/engine/buildfile_test.go | 16 ++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 7ade058c69..75ebdd7a7c 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -7,6 +7,7 @@ import ( "github.com/dotcloud/docker/utils" "io" "io/ioutil" + "net/url" "os" "path" "reflect" @@ -201,6 +202,24 @@ func (b *buildFile) addRemote(container *Container, orig, dest string) error { } defer file.Body.Close() + // If the destination is a directory, figure out the filename. + if strings.HasSuffix(dest, "/") { + u, err := url.Parse(orig) + if err != nil { + return err + } + path := u.Path + if strings.HasSuffix(path, "/") { + path = path[:len(path)-1] + } + parts := strings.Split(path, "/") + filename := parts[len(parts)-1] + if filename == "" { + return fmt.Errorf("cannot determine filename from url: %s", u) + } + dest = dest + filename + } + return container.Inject(file.Body, dest) } @@ -208,7 +227,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error { origPath := path.Join(b.context, orig) destPath := path.Join(container.RootfsPath(), dest) // Preserve the trailing '/' - if dest[len(dest)-1] == '/' { + if strings.HasSuffix(dest, "/") { destPath = destPath + "/" } fi, err := os.Stat(origPath) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 602af5061b..b7eca52336 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -84,6 +84,22 @@ run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ] nil, }, + { + ` +from {IMAGE} +add http://{SERVERADDR}/x /a/b/c +run [ "$(cat /a/b/c)" = "hello" ] +add http://{SERVERADDR}/x?foo=bar / +run [ "$(cat /x)" = "hello" ] +add http://{SERVERADDR}/x /d/ +run [ "$(cat /d/x)" = "hello" ] +add http://{SERVERADDR} /e +run [ "$(cat /e)" = "blah" ] +`, + nil, + [][2]string{{"/x", "hello"}, {"/", "blah"}}, + }, + { ` from {IMAGE} From ed8e1bd7e6b319c614ffd1a2b50bae4ee2dd71c6 Mon Sep 17 00:00:00 2001 From: Caleb Spare Date: Mon, 8 Jul 2013 01:14:01 -0700 Subject: [PATCH 58/80] Remove some trailing whitespace. Upstream-commit: 416fdaa3d5d7b84f7e36fdfcff7153e3a38262c9 Component: engine --- .../engine/docs/sources/use/builder.rst | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index 7f370609c8..ea98541dbf 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -30,7 +30,7 @@ build succeeds: ``docker build -t shykes/myapp .`` -Docker will run your steps one-by-one, committing the result if necessary, +Docker will run your steps one-by-one, committing the result if necessary, before finally outputting the ID of your new image. 2. Format @@ -43,7 +43,7 @@ The Dockerfile format is quite simple: # Comment INSTRUCTION arguments -The Instruction is not case-sensitive, however convention is for them to be +The Instruction is not case-sensitive, however convention is for them to be UPPERCASE in order to distinguish them from arguments more easily. Docker evaluates the instructions in a Dockerfile in order. **The first @@ -106,7 +106,7 @@ The ``CMD`` instruction sets the command to be executed when running the image. This is functionally equivalent to running ``docker commit -run '{"Cmd": }'`` outside the builder. -.. note:: +.. note:: Don't confuse `RUN` with `CMD`. `RUN` actually runs a command and commits the result; `CMD` does not execute anything at build time, but specifies the intended command for the image. @@ -131,7 +131,7 @@ value ````. This value will be passed to all future ``RUN`` instructions. This is functionally equivalent to prefixing the command with ``=`` -.. note:: +.. note:: The environment variables will persist when a container is run from the resulting image. @@ -158,10 +158,10 @@ The copy obeys the following rules: (identity, gzip, bzip2 or xz), it is unpacked as a directory. When a directory is copied or unpacked, it has the same behavior as - ``tar -x``: the result is the union of + ``tar -x``: the result is the union of 1. whatever existed at the destination path and - 2. the contents of the source tree, + 2. the contents of the source tree, with conflicts resolved in favor of 2) on a file-by-file basis. @@ -203,14 +203,14 @@ container created from the image. # Nginx # # VERSION 0.0.1 - + FROM ubuntu MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com" - + # make sure the package repository is up to date RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list RUN apt-get update - + RUN apt-get install -y inotify-tools nginx apache2 openssh-server .. code-block:: bash @@ -218,12 +218,12 @@ container created from the image. # Firefox over VNC # # VERSION 0.3 - + FROM ubuntu # make sure the package repository is up to date RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list RUN apt-get update - + # Install vnc, xvfb in order to create a 'fake' display and firefox RUN apt-get install -y x11vnc xvfb firefox RUN mkdir /.vnc @@ -231,7 +231,7 @@ container created from the image. RUN x11vnc -storepasswd 1234 ~/.vnc/passwd # Autostart firefox (might not be the best way, but it does the trick) RUN bash -c 'echo "firefox" >> /.bashrc' - + EXPOSE 5900 CMD ["x11vnc", "-forever", "-usepw", "-create"] From e3ecb715a38209f25483251b6f544fac97eb8052 Mon Sep 17 00:00:00 2001 From: Caleb Spare Date: Mon, 8 Jul 2013 01:29:13 -0700 Subject: [PATCH 59/80] Update ADD documentation to specify new behavior. Upstream-commit: c383d598809daec6bccd60bd211c10f6a4ef60b0 Component: engine --- components/engine/docs/sources/use/builder.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index ea98541dbf..302527a740 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -152,6 +152,14 @@ destination container. The copy obeys the following rules: +* If ```` is a URL and ```` does not end with a trailing slash, + then a file is downloaded from the URL and copied to ````. +* If ```` is a URL and ```` does end with a trailing slash, + then the filename is inferred from the URL and the file is downloaded to + ``/``. For instance, ``ADD http://example.com/foobar /`` + would create the file ``/foobar``. The URL must have a nontrivial path + so that an appropriate filename can be discovered in this case + (``http://example.com`` will not work). * If ```` is a directory, the entire directory is copied, including filesystem metadata. * If ````` is a tar archive in a recognized compression format From 7d9d3be42244fbf9e7d1b3342dbb589bb6f98381 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 22 Jul 2013 14:52:05 +0000 Subject: [PATCH 60/80] fix error message when invalid directory Upstream-commit: 74a2b13687787a33b0963f020f704d3cdde4b06d Component: engine --- components/engine/commands.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/engine/commands.go b/components/engine/commands.go index 86385f4187..b0e32162e6 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -185,6 +185,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error { } else if utils.IsURL(cmd.Arg(0)) || utils.IsGIT(cmd.Arg(0)) { isRemote = true } else { + if _, err := os.Stat(cmd.Arg(0)); err != nil { + return err + } context, err = Tar(cmd.Arg(0), Uncompressed) } var body io.Reader From 48287f8a5dee309c42e7f3ebf30e73a23e8577af Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 22 Jul 2013 16:26:05 +0000 Subject: [PATCH 61/80] fix test env Upstream-commit: 5c1af383eb14121174b43fa706f524d4eedd5cad Component: engine --- components/engine/container_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/container_test.go b/components/engine/container_test.go index 028c03a318..a90018decc 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -959,6 +959,7 @@ func TestEnv(t *testing.T) { goodEnv := []string{ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOME=/", + "container=lxc", } sort.Strings(goodEnv) if len(goodEnv) != len(actualEnv) { From 4f8c565f24443a5b9f513840c9b7ee5693378cd8 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Mon, 22 Jul 2013 12:06:24 -0700 Subject: [PATCH 62/80] Allocate a /16 IP range by default, with fallback to /24. Try a total of 12 ranges instead of 3. Upstream-commit: 4714f102d72f03159acd0f7be71cde3d169c06b8 Component: engine --- components/engine/network.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/components/engine/network.go b/components/engine/network.go index 0f98c899f1..d2d6668b26 100644 --- a/components/engine/network.go +++ b/components/engine/network.go @@ -111,10 +111,29 @@ func checkRouteOverlaps(dockerNetwork *net.IPNet) error { return nil } +// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, +// and attempts to configure it with an address which doesn't conflict with any other interface on the host. +// If it can't find an address which doesn't conflict, it will return an error. func CreateBridgeIface(ifaceName string) error { - // FIXME: try more IP ranges - // FIXME: try bigger ranges! /24 is too small. - addrs := []string{"172.16.42.1/24", "10.0.42.1/24", "192.168.42.1/24"} + addrs := []string{ + // Here we don't follow the convention of using the 1st IP of the range for the gateway. + // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. + // In theory this shouldn't matter - in practice there's bound to be a few scripts relying + // on the internal addressing or other stupid things like that. + // The shouldn't, but hey, let's not break them unless we really have to. + "172.16.42.1/16", + "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive + "10.1.42.1/16", + "10.42.42.1/16", + "172.16.42.1/24", + "172.16.43.1/24", + "172.16.44.1/24", + "10.0.42.1/24", + "10.0.43.1/24", + "192.168.42.1/24", + "192.168.43.1/24", + "192.168.44.1/24", + } var ifaceAddr string for _, addr := range addrs { From 8d7759fbb0d4e9493e0a8011d873202361f9e518 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Mon, 22 Jul 2013 18:32:55 -0700 Subject: [PATCH 63/80] Hack: first draft of the maintainer boot camp. Work in progress! Upstream-commit: ce43f4af1cf7c2692c4c6203bc5872053d089863 Component: engine --- components/engine/hack/bootcamp/README.md | 92 +++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 components/engine/hack/bootcamp/README.md diff --git a/components/engine/hack/bootcamp/README.md b/components/engine/hack/bootcamp/README.md new file mode 100644 index 0000000000..f4cc91f19e --- /dev/null +++ b/components/engine/hack/bootcamp/README.md @@ -0,0 +1,92 @@ +# Docker maintainer bootcamp + +## Introduction: we need more maintainers + +Docker is growing incredibly fast. At the time of writing, it has received over 200 contributions from 90 people, +and its API is used by dozens of 3d-party tools. Over 1,000 issues have been opened. As the first production deployments +start going live, the growth will only accelerate. + +Also at the time of writing, Docker has 3 full-time maintainers, and 7 part-time subsystem maintainers. If docker +is going to live up to the expectations, we need more than that. + +This document describes a *bootcamp* to guide and train volunteers interested in helping the project, either with individual +contributions, maintainer work, or both. + +This bootcamp is an experiment. If you decide to go through it, consider yourself an alpha-tester. You should expect quirks, +and report them to us as you encounter them to help us smooth out the process. + + +## How it works + +The maintainer bootcamp is a 12-step program - one step for each of the maintainer's responsibilities. The aspiring maintainer must +validate all 12 steps by 1) studying it, 2) practicing it, and 3) getting endorsed for it. + +Steps are all equally important and can be validated in any order. Validating all 12 steps is a pre-requisite for becoming a core +maintainer, but even 1 step will make you a better contributor! + +### List of steps + +#### 1) Be a power user + +Use docker daily, build cool things with it, know its quirks inside and out. + + +#### 2) Help users + +Answer questions on irc, twitter, email, in person. + + +#### 3) Manage the bug tracker + +Help triage tickets - ask the right questions, find duplicates, reference relevant resources, know when to close a ticket when necessary, take the time to go over older tickets. + + +#### 4) Improve the documentation + +Follow the documentation from scratch regularly and make sure it is still up-to-date. Find and fix inconsistencies. Remove stale information. Find a frequently asked question that is not documented. Simplify the content and the form. + + +#### 5) Evangelize the principles of docker + +Understand what the underlying goals and principle of docker are. Explain design decisions based on what docker is, and what it is not. When someone is not using docker, find how docker can be valuable to them. If they are using docker, find how they can use it better. + + +#### 6) Fix bugs + +Self-explanatory. Contribute improvements to docker which solve defects. Bugfixes should be well-tested, and prioritized by impact to the user. + + +#### 7) Improve the testing infrastructure + +Automated testing is complicated and should be perpetually improved. Invest time to improve the current tooling. Refactor existing tests, create new ones, make testing more accessible to developers, add new testing capabilities (integration tests, mocking, stress test...), improve integration between tests and documentation... + + +#### 8) Contribute features + +Improve docker to do more things, or get better at doing the same things. Features should be well-tested, not break existing APIs, respect the project goals. They should make the user's life measurably better. Features should be discussed ahead of time to avoid wasting time and duplicating effort. + + +#### 9) Refactor internals + +Improve docker to repay technical debt. Simplify code layout, improve performance, add missing comments, reduce the number of files and functions, rename functions and variables to be more readable, go over FIXMEs, etc. + +#### 10) Review and merge contributions + +Review pull requests in a timely manner, review code in detail and offer feedback. Keep a high bar without being pedantic. Share the load of testing and merging pull requests. + +#### 11) Release + +Manage a release of docker from beginning to end. Tests, final review, tags, builds, upload to mirrors, distro packaging, etc. + +#### 12) Train other maintainers + +Contribute to training other maintainers. Give advic + + +### How to study a step + +### How to practice a step + +### How to get endorsed for a step + + From 4966c3626567c68210b05751507fe545fe1bb53e Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Mon, 22 Jul 2013 18:36:36 -0700 Subject: [PATCH 64/80] Hack: completed step 12 of the bootcamp Upstream-commit: 5714f0a74ef6f6b0be521d7e1e21c1cab1a9ad73 Component: engine --- components/engine/hack/bootcamp/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/engine/hack/bootcamp/README.md b/components/engine/hack/bootcamp/README.md index f4cc91f19e..88440e8c64 100644 --- a/components/engine/hack/bootcamp/README.md +++ b/components/engine/hack/bootcamp/README.md @@ -80,8 +80,7 @@ Manage a release of docker from beginning to end. Tests, final review, tags, bui #### 12) Train other maintainers -Contribute to training other maintainers. Give advic - +Contribute to training other maintainers. Give advice, delegate work, help organize the bootcamp. This also means contribute to the maintainer's manual, look for ways to improve the project organization etc. ### How to study a step From f428eaec77f1d151b3e7eaba7e7357c3838da163 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Mon, 22 Jul 2013 18:39:58 -0700 Subject: [PATCH 65/80] Typo in 3rd-party Upstream-commit: 6745bdd0b3a7944985614dae194538645eacb4e7 Component: engine --- components/engine/hack/bootcamp/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/hack/bootcamp/README.md b/components/engine/hack/bootcamp/README.md index 88440e8c64..2c3d356daf 100644 --- a/components/engine/hack/bootcamp/README.md +++ b/components/engine/hack/bootcamp/README.md @@ -3,7 +3,7 @@ ## Introduction: we need more maintainers Docker is growing incredibly fast. At the time of writing, it has received over 200 contributions from 90 people, -and its API is used by dozens of 3d-party tools. Over 1,000 issues have been opened. As the first production deployments +and its API is used by dozens of 3rd-party tools. Over 1,000 issues have been opened. As the first production deployments start going live, the growth will only accelerate. Also at the time of writing, Docker has 3 full-time maintainers, and 7 part-time subsystem maintainers. If docker From d56c0e2aac92069b0a1f5260782eea5ad2f1ea72 Mon Sep 17 00:00:00 2001 From: Stefan Praszalowicz Date: Mon, 22 Jul 2013 19:00:35 -0700 Subject: [PATCH 66/80] Invert network disable flag and logic (unbreaks TestAllocate*PortLocalhost) Upstream-commit: bc172e5e5f1f231f878727a180a8da46e653c0a7 Component: engine --- components/engine/container.go | 84 +++++++++++++++---------------- components/engine/lxc_template.go | 8 +-- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index 189e882c44..f172604356 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -58,26 +58,26 @@ type Container struct { } type Config struct { - Hostname string - User string - Memory int64 // Memory limit (in bytes) - MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap - CpuShares int64 // CPU shares (relative weight vs. other containers) - AttachStdin bool - AttachStdout bool - AttachStderr bool - PortSpecs []string - Tty bool // Attach standard streams to a tty, including stdin if it is not closed. - OpenStdin bool // Open stdin - StdinOnce bool // If true, close stdin after the 1 attached client disconnects. - Env []string - Cmd []string - Dns []string - Image string // Name of the image as it was passed by the operator (eg. could be symbolic) - Volumes map[string]struct{} - VolumesFrom string - Entrypoint []string - NetworkEnabled bool + Hostname string + User string + Memory int64 // Memory limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap + CpuShares int64 // CPU shares (relative weight vs. other containers) + AttachStdin bool + AttachStdout bool + AttachStderr bool + PortSpecs []string + Tty bool // Attach standard streams to a tty, including stdin if it is not closed. + OpenStdin bool // Open stdin + StdinOnce bool // If true, close stdin after the 1 attached client disconnects. + Env []string + Cmd []string + Dns []string + Image string // Name of the image as it was passed by the operator (eg. could be symbolic) + Volumes map[string]struct{} + VolumesFrom string + Entrypoint []string + NetworkDisabled bool } type HostConfig struct { @@ -176,24 +176,24 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } config := &Config{ - Hostname: *flHostname, - PortSpecs: flPorts, - User: *flUser, - Tty: *flTty, - NetworkEnabled: *flNetwork, - OpenStdin: *flStdin, - Memory: *flMemory, - CpuShares: *flCpuShares, - AttachStdin: flAttach.Get("stdin"), - AttachStdout: flAttach.Get("stdout"), - AttachStderr: flAttach.Get("stderr"), - Env: flEnv, - Cmd: runCmd, - Dns: flDns, - Image: image, - Volumes: flVolumes, - VolumesFrom: *flVolumesFrom, - Entrypoint: entrypoint, + Hostname: *flHostname, + PortSpecs: flPorts, + User: *flUser, + Tty: *flTty, + NetworkDisabled: !*flNetwork, + OpenStdin: *flStdin, + Memory: *flMemory, + CpuShares: *flCpuShares, + AttachStdin: flAttach.Get("stdin"), + AttachStdout: flAttach.Get("stdout"), + AttachStderr: flAttach.Get("stderr"), + Env: flEnv, + Cmd: runCmd, + Dns: flDns, + Image: image, + Volumes: flVolumes, + VolumesFrom: *flVolumesFrom, + Entrypoint: entrypoint, } hostConfig := &HostConfig{ Binds: binds, @@ -515,7 +515,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { return err } if container.runtime.networkManager.disabled { - container.Config.NetworkEnabled = false + container.Config.NetworkDisabled = true } else { if err := container.allocateNetwork(); err != nil { return err @@ -633,7 +633,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { } // Networking - if container.Config.NetworkEnabled { + if !container.Config.NetworkDisabled { params = append(params, "-g", container.network.Gateway.String()) } @@ -736,7 +736,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) { } func (container *Container) allocateNetwork() error { - if !container.Config.NetworkEnabled { + if container.Config.NetworkDisabled { return nil } @@ -766,7 +766,7 @@ func (container *Container) allocateNetwork() error { } func (container *Container) releaseNetwork() { - if !container.Config.NetworkEnabled { + if container.Config.NetworkDisabled { return } container.network.Release() diff --git a/components/engine/lxc_template.go b/components/engine/lxc_template.go index 27670c8076..d1f67e3376 100644 --- a/components/engine/lxc_template.go +++ b/components/engine/lxc_template.go @@ -13,7 +13,10 @@ lxc.utsname = {{.Id}} {{end}} #lxc.aa_profile = unconfined -{{if .Config.NetworkEnabled}} +{{if .Config.NetworkDisabled}} +# network is disabled (-n=false) +lxc.network.type = empty +{{else}} # network configuration lxc.network.type = veth lxc.network.flags = up @@ -21,9 +24,6 @@ lxc.network.link = {{.NetworkSettings.Bridge}} lxc.network.name = eth0 lxc.network.mtu = 1500 lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}} -{{else}} -# Network configuration disabled -lxc.network.type = empty {{end}} # root filesystem From fab05b9aee075fdd63da7d772b3ee9af2754ae1f Mon Sep 17 00:00:00 2001 From: Thatcher Peskens Date: Mon, 22 Jul 2013 20:26:40 -0700 Subject: [PATCH 67/80] Added new docker logo to the documentation header, and added other links. Upstream-commit: 58a1c5720a42fde72c27532ce1122609225bc3bb Component: engine --- components/engine/docs/sources/use/basics.rst | 4 ++-- components/engine/docs/theme/docker/layout.html | 13 ++++++------- .../engine/docs/theme/docker/static/css/main.css | 10 +++++----- .../docs/theme/docker/static/css/main.less | 10 +++------- .../theme/docker/static/img/docker-top-logo.png | Bin 0 -> 3426 bytes .../docker/static/img/external-link-icon.png | Bin 0 -> 2917 bytes 6 files changed, 16 insertions(+), 21 deletions(-) create mode 100644 components/engine/docs/theme/docker/static/img/docker-top-logo.png create mode 100644 components/engine/docs/theme/docker/static/img/external-link-icon.png diff --git a/components/engine/docs/sources/use/basics.rst b/components/engine/docs/sources/use/basics.rst index 7c9e2e9055..7ce86416dd 100644 --- a/components/engine/docs/sources/use/basics.rst +++ b/components/engine/docs/sources/use/basics.rst @@ -51,7 +51,7 @@ For example: .. code-block:: bash # Run docker in daemon mode - sudo /docker -H 0.0.0.0:5555 & + sudo /docker -H 0.0.0.0:5555 -d & # Download a base image docker -H :5555 pull base @@ -61,7 +61,7 @@ on both tcp and a unix socket .. code-block:: bash # Run docker in daemon mode - sudo /docker -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock + sudo /docker -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock -d & # Download a base image docker pull base # OR diff --git a/components/engine/docs/theme/docker/layout.html b/components/engine/docs/theme/docker/layout.html index f4f17f2dba..198cd5d7d8 100755 --- a/components/engine/docs/theme/docker/layout.html +++ b/components/engine/docs/theme/docker/layout.html @@ -68,19 +68,18 @@
- +
@@ -96,7 +95,7 @@ -

DOCUMENTATION

+

DOCUMENTATION

diff --git a/components/engine/docs/theme/docker/static/css/main.css b/components/engine/docs/theme/docker/static/css/main.css index c79d590e07..0b11890f71 100755 --- a/components/engine/docs/theme/docker/static/css/main.css +++ b/components/engine/docs/theme/docker/static/css/main.css @@ -34,12 +34,12 @@ h4 { .navbar .nav li a { padding: 22px 15px 22px; } -.navbar .brand { - padding: 13px 10px 13px 28px ; -} .navbar-dotcloud .container { border-bottom: 2px #000000 solid; } +.inline-icon { + margin-bottom: 6px; +} /* * Responsive YouTube, Vimeo, Embed, and HTML5 Videos with CSS * http://www.jonsuh.com @@ -82,7 +82,7 @@ h4 { .btn-custom { background-color: #292929 !important; background-repeat: repeat-x; - filter: progid:dximagetransform.microsoft.gradient(startColorstr="#515151", endColorstr="#282828"); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#515151", endColorstr="#282828"); background-image: -khtml-gradient(linear, left top, left bottom, from(#515151), to(#282828)); background-image: -moz-linear-gradient(top, #515151, #282828); background-image: -ms-linear-gradient(top, #515151, #282828); @@ -301,7 +301,7 @@ section.header { height: 28px; line-height: 28px; background-color: #43484c; - filter: progid:dximagetransform.microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35'); + filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35'); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c)); background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%); background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%); diff --git a/components/engine/docs/theme/docker/static/css/main.less b/components/engine/docs/theme/docker/static/css/main.less index c8c38dc8ed..8c470518d8 100644 --- a/components/engine/docs/theme/docker/static/css/main.less +++ b/components/engine/docs/theme/docker/static/css/main.less @@ -53,13 +53,6 @@ h1, h2, h3, h4 { padding: 22px 15px 22px; } } - - .brand { - padding: 13px 10px 13px 28px ; - // padding-left: 30px; - - } - background-color: white; } @@ -67,6 +60,9 @@ h1, h2, h3, h4 { border-bottom: 2px @black solid; } +.inline-icon { + margin-bottom: 6px; +} /* * Responsive YouTube, Vimeo, Embed, and HTML5 Videos with CSS diff --git a/components/engine/docs/theme/docker/static/img/docker-top-logo.png b/components/engine/docs/theme/docker/static/img/docker-top-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4955f499bdc39daa24c3e6b779283e113eeaf1ce GIT binary patch literal 3426 zcmZwKS2!CA8wc>%B|>qG7*%bJlA5hmdn>hx)EI=yLvmrZ|ZwGxxvlgPOc%y4!F{vWomsbb&KH1&1?q~i!r{o z$o3|2k+NJZ^#Sdy4E0;Z-vSK>arcLQ$_O#Ya}vKBHSR$Tucvx+#WJ z3>6r8_J7IzIrJnwF(LEAg0r(I{rW9Y(a3TFWeq`GxuL6iV}7%`CI|ypzX*5p+V(vn z`t{D7DKwm&UG!E9xge1&dS43ccve}HulcjastKq37c8rHSYH|4`WLe&a4B<<+qYeM z9)>RUpxvBbY}KDPD#%Uux*5bmq<$%>Hxy4??d=vuO$40^gcUkOWqT>FJp=GjhGj`! z*@9h_xha8Dh+=z`Qgk@IKkM4s&VUR`CZ!JU8^3)c7G?VXQ<8F+_(Jx-L~ZfL=5Th- zvQE%-nN4B4__Ov|nW#g*TJjTJy%9FUoC-tevZoS>+af>}Ptujw#19DZygp4MF&o!; z=vC$INI1I&q*R7;Pb^03vX7Xy1TiJK7>HROft?cRQ z=H_#0PaoN(Yr7j=kUz=2+}ce>H_kL321L`oy1d`<0D4->q+nQg=u#HXG*V7s(pM7d z#_gOY;}MkVxm8lPzp>WjgDFk(e46kihg?9c$O%A3$6<6UFQGF{c7EJyzI%bTyoCq( zI`MhY$}sYsa5YpiZiaBAC0K~DIZ8@*+9PotMyCyEC{^@d2yAWlLS`sTlUx=EkWizf& zTY%@Yn-#g?M=h=5)CL%=5Ed2S?&&!V&$rnw33JfldtrUT5WlU$wPl%Ri8}h&HrLM| zxmDgxvnhm95T4zUNmMbGj*2?lXR^nPdY4EH=!?$?V@#5)pwi;pLF_L0m?973r!sdj zk5Nb^UuAJSr=-Q>oD;)zfHy`sIglz&kkH9n5D5oi@U}%S= zwEXEaT{~@U95sbJbzK3B3R6~#ny^p0$B*40)yU}B97vt!m;kV{?{17+13r07`O39? zD25bcn*IK1IM~BEw9oQd+Iw{Ub9;w+>bYjfN4^ZzHpT4|;hT`UO6P;fgnETkyPCJy z0pHQ0lIsn3rE9HwZxgxRbnuL{2ypyn&!IG;FJl^25rK%MZ=`p|g;N&sg`twew2Ghi z+y67N&;ax3>u{35e7}ozG06*=K9~C`*yIqCIhN90`>0JZ&?aENzs;)0QAJLc(eL4r zj;eF*m{YBMXp7bEOS*ZwfU}R-} z?7QRhKP_2v#GQOrPOtH`#5dmC`<%&KX4T;~GF`|R#j`p8>buSJiWCgnlv3_lDKQ*( z??^w;IC$1QgnyDzt$DWGfFi`TI=UXA=d2b0;yvdcX80|MwiLE2@OtJ`_4ZTVTbhT{fJKbvo}<)5w$?31wDLgk*yPU#;`I|7Y_@Dk z9bk1Zj&`J5^vwaFCdF;TK(8;_>QZFNdVQPZFE|?DKdGext@ro68J|xrJW%5WN{AyTb zQN`LGsQc%ecm z#3~yWkJvJ@ppQ*$Q#DhIqh1S23Y)?s_^XIJSIKfx08I1q9E2=o`2zA6a8wXYkmozi z^Nl=yEnpkMIr~~ESgsSA2#;;qzwS>hKdNv{CTt*f_ki>n>?`TEeY@QE zRyF3_s~w2V-tUbmh)+cL#DLQsrH=R8^_-F?|5_Q2{fQE%hImRv7zSO{lklx_5~v}yLVo&aKKcXE}xBl@);gGVQVgPF)=k+tBLCud-_F@1p44STUUwW1NFLGRL^^2LSSwCb8J!Z8Yw~&iQFBI=rYsxvZv3uh z9=AO0GkhSu(m-e6{!5cg&^$8Z6N|aE2l$ZVQ@q`7>RnEuimbc5!OP`-MIXQ;{8Q+| z_lX;4No&GQF^{X0Q^2#2uv`ta&6rDy>gwvb$g`E5wRV5shPR-y9mAKeyfzhK9Lx6` zvOwLbCvmj(t0CKo!MLl4M=9(u#BWB#{cv95jfmPO?Xushg)XE7e$k$rglsjZYV<~iv1xtJAA<4+?tc`fO91U9 zESH)jxT@wkY}IPbvSjiSykt6l6%6XsAn z0waENhrXXQu*(F^&2p&la!#SiCV%_dMG5m5y{DG2Y8ljNH=z38so>$Ej z2(g9lp?1rm4vgd3agCOAyXN1#^n9E~@kqOW+Hm)T3wD1)8~8vrModdf%W4z7*&bp; zwrLI*M>lF!Gt}ZZE+T0IxEigfGey}|&lz~-RCi$>82YyFX{ENEm=|>v3D8NVX z0|iDFrHe`xZ_4n&4WwT9x{US7IOGdeIHDfC-am!DIH=I6s7GuV4F|y3`$OK;FyByI zQam|i#?*-L$f^Yfa*UT)mHEtGTy`s+&n-N5F-R1@UO`-a|Mac~+Gpc3SUZyWuLMx& z@$1iHvpKBaFjp*UcBC}1N&LFp^CYT@oyo@QQU0>mhh;6S(rILWydqa9)9mzG3NR;0 zzTrGk$+Do6)UDr|S-BR*qeOEhOaCmpTKqknBmxors@r96Z^}}!=|D0hc!$1$lB4=j zoQa_4$rUe$dcNT>UI!JP15iHyMyNZyG=8y?RLAsM`?ZwQ)s|Zl0js9=mF|P!QK?LO z-L;4LO_OU^?pnD8?EV5=P2|C~&aa|jzu#G!z$zsc2H$dLT$3K>N4X6j3&IWAoUIB7 zzfz|gJ4CHNfHv;{jdH((S6Rs!DTG)CXvd*hnEqN|Pv>oN-ju=Z-{PRNd14O|gy8dX z_hInOJV034M7Dq`n7tUJ>B#|fc+rIvsU9uBWi)x%m)`O_7X5pvrcv4lpOzo yh7FBd{rYz(v$g({T(lmwVyxD`tc_gJZ~=-s?9joYKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001xNkl$Cw%(46SJctNH^mn=q07@yJ8~+9XcPCzfBYN71 P00000NkvXXu0mjfh3RP# literal 0 HcmV?d00001 From 001001e2115464013e9e11d414b0eedf42af0217 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 23 Jul 2013 15:04:31 +0000 Subject: [PATCH 68/80] change dockercfg to json and support multiple auth remote Upstream-commit: 3bae188b8dc51911a44ea1c7b5681f9f07f9d3af Component: engine --- components/engine/api.go | 58 ++---------------- components/engine/auth/auth.go | 104 +++++++++++++++++---------------- components/engine/commands.go | 52 +++++++++-------- 3 files changed, 87 insertions(+), 127 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index b6ab7badfa..975134f22d 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -81,54 +81,15 @@ func getBoolParam(value string) (bool, error) { return ret, nil } -func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if version > 1.1 { - w.WriteHeader(http.StatusNotFound) - return nil - } - authConfig, err := auth.LoadConfig(srv.runtime.root) - if err != nil { - if err != auth.ErrConfigFileMissing { - return err - } - authConfig = &auth.AuthConfig{} - } - b, err := json.Marshal(&auth.AuthConfig{Username: authConfig.Username, Email: authConfig.Email}) - if err != nil { - return err - } - writeJSON(w, b) - return nil -} - func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { authConfig := &auth.AuthConfig{} err := json.NewDecoder(r.Body).Decode(authConfig) if err != nil { return err } - status := "" - if version > 1.1 { - status, err = auth.Login(authConfig, false) - if err != nil { - return err - } - } else { - localAuthConfig, err := auth.LoadConfig(srv.runtime.root) - if err != nil { - if err != auth.ErrConfigFileMissing { - return err - } - } - if authConfig.Username == localAuthConfig.Username { - authConfig.Password = localAuthConfig.Password - } - - newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root) - status, err = auth.Login(newAuthConfig, true) - if err != nil { - return err - } + status, err := auth.Login(authConfig) + if err != nil { + return err } if status != "" { b, err := json.Marshal(&APIAuth{Status: status}) @@ -429,16 +390,8 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { authConfig := &auth.AuthConfig{} - if version > 1.1 { - if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { - return err - } - } else { - localAuthConfig, err := auth.LoadConfig(srv.runtime.root) - if err != nil && err != auth.ErrConfigFileMissing { - return err - } - authConfig = localAuthConfig + if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { + return err } if err := parseForm(r); err != nil { return err @@ -854,7 +807,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{ "GET": { - "/auth": getAuth, "/version": getVersion, "/info": getInfo, "/images/json": getImagesJSON, diff --git a/components/engine/auth/auth.go b/components/engine/auth/auth.go index 97df928b6b..8f4d09fb8f 100644 --- a/components/engine/auth/auth.go +++ b/components/engine/auth/auth.go @@ -25,19 +25,15 @@ var ( ) type AuthConfig struct { - Username string `json:"username"` - Password string `json:"password"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth"` Email string `json:"email"` - rootPath string } -func NewAuthConfig(username, password, email, rootPath string) *AuthConfig { - return &AuthConfig{ - Username: username, - Password: password, - Email: email, - rootPath: rootPath, - } +type ConfigFile struct { + Configs map[string]AuthConfig `json:"configs,omitempty"` + rootPath string } func IndexServerAddress() string { @@ -54,61 +50,83 @@ func encodeAuth(authConfig *AuthConfig) string { } // decode the auth string -func decodeAuth(authStr string) (*AuthConfig, error) { +func decodeAuth(authStr string) (string, string, error) { decLen := base64.StdEncoding.DecodedLen(len(authStr)) decoded := make([]byte, decLen) authByte := []byte(authStr) n, err := base64.StdEncoding.Decode(decoded, authByte) if err != nil { - return nil, err + return "", "", err } if n > decLen { - return nil, fmt.Errorf("Something went wrong decoding auth config") + return "", "", fmt.Errorf("Something went wrong decoding auth config") } arr := strings.Split(string(decoded), ":") if len(arr) != 2 { - return nil, fmt.Errorf("Invalid auth configuration file") + return "", "", fmt.Errorf("Invalid auth configuration file") } password := strings.Trim(arr[1], "\x00") - return &AuthConfig{Username: arr[0], Password: password}, nil + return arr[0], password, nil } // load up the auth config information and return values // FIXME: use the internal golang config parser -func LoadConfig(rootPath string) (*AuthConfig, error) { +func LoadConfig(rootPath string) (*ConfigFile, error) { + configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath} confFile := path.Join(rootPath, CONFIGFILE) if _, err := os.Stat(confFile); err != nil { - return &AuthConfig{rootPath: rootPath}, ErrConfigFileMissing + return &configFile, ErrConfigFileMissing } b, err := ioutil.ReadFile(confFile) if err != nil { return nil, err } - arr := strings.Split(string(b), "\n") - if len(arr) < 2 { - return nil, fmt.Errorf("The Auth config file is empty") + + if err := json.Unmarshal(b, &configFile.Configs); err != nil { + arr := strings.Split(string(b), "\n") + if len(arr) < 2 { + return nil, fmt.Errorf("The Auth config file is empty") + } + authConfig := AuthConfig{} + origAuth := strings.Split(arr[0], " = ") + authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1]) + if err != nil { + return nil, err + } + origEmail := strings.Split(arr[1], " = ") + authConfig.Email = origEmail[1] + configFile.Configs[IndexServerAddress()] = authConfig + } else { + for k, authConfig := range configFile.Configs { + authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) + if err != nil { + return nil, err + } + configFile.Configs[k] = authConfig + } } - origAuth := strings.Split(arr[0], " = ") - origEmail := strings.Split(arr[1], " = ") - authConfig, err := decodeAuth(origAuth[1]) - if err != nil { - return nil, err - } - authConfig.Email = origEmail[1] - authConfig.rootPath = rootPath - return authConfig, nil + return &configFile, nil } // save the auth config -func SaveConfig(authConfig *AuthConfig) error { - confFile := path.Join(authConfig.rootPath, CONFIGFILE) - if len(authConfig.Email) == 0 { +func SaveConfig(configFile *ConfigFile) error { + confFile := path.Join(configFile.rootPath, CONFIGFILE) + if len(configFile.Configs) == 0 { os.Remove(confFile) return nil } - lines := "auth = " + encodeAuth(authConfig) + "\n" + "email = " + authConfig.Email + "\n" - b := []byte(lines) - err := ioutil.WriteFile(confFile, b, 0600) + for k, authConfig := range configFile.Configs { + authConfig.Auth = encodeAuth(&authConfig) + authConfig.Username = "" + authConfig.Password = "" + configFile.Configs[k] = authConfig + } + + b, err := json.Marshal(configFile.Configs) + if err != nil { + return err + } + err = ioutil.WriteFile(confFile, b, 0600) if err != nil { return err } @@ -116,8 +134,7 @@ func SaveConfig(authConfig *AuthConfig) error { } // try to register/login to the registry server -func Login(authConfig *AuthConfig, store bool) (string, error) { - storeConfig := false +func Login(authConfig *AuthConfig) (string, error) { client := &http.Client{} reqStatusCode := 0 var status string @@ -143,7 +160,6 @@ func Login(authConfig *AuthConfig, store bool) (string, error) { if reqStatusCode == 201 { status = "Account created. Please use the confirmation link we sent" + " to your e-mail to activate it." - storeConfig = true } else if reqStatusCode == 403 { return "", fmt.Errorf("Login: Your account hasn't been activated. " + "Please check your e-mail for a confirmation link.") @@ -162,14 +178,7 @@ func Login(authConfig *AuthConfig, store bool) (string, error) { } if resp.StatusCode == 200 { status = "Login Succeeded" - storeConfig = true } else if resp.StatusCode == 401 { - if store { - authConfig.Email = "" - if err := SaveConfig(authConfig); err != nil { - return "", err - } - } return "", fmt.Errorf("Wrong login/password, please try again") } else { return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, @@ -181,10 +190,5 @@ func Login(authConfig *AuthConfig, store bool) (string, error) { } else { return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody) } - if storeConfig && store { - if err := SaveConfig(authConfig); err != nil { - return "", err - } - } return status, nil } diff --git a/components/engine/commands.go b/components/engine/commands.go index b0e32162e6..17597320b5 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -313,16 +313,21 @@ func (cli *DockerCli) CmdLogin(args ...string) error { email string ) + authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()] + if !ok { + authconfig = auth.AuthConfig{} + } + if *flUsername == "" { - fmt.Fprintf(cli.out, "Username (%s): ", cli.authConfig.Username) + fmt.Fprintf(cli.out, "Username (%s): ", authconfig.Username) username = readAndEchoString(cli.in, cli.out) if username == "" { - username = cli.authConfig.Username + username = authconfig.Username } } else { username = *flUsername } - if username != cli.authConfig.Username { + if username != authconfig.Username { if *flPassword == "" { fmt.Fprintf(cli.out, "Password: ") password = readString(cli.in, cli.out) @@ -334,31 +339,30 @@ func (cli *DockerCli) CmdLogin(args ...string) error { } if *flEmail == "" { - fmt.Fprintf(cli.out, "Email (%s): ", cli.authConfig.Email) + fmt.Fprintf(cli.out, "Email (%s): ", authconfig.Email) email = readAndEchoString(cli.in, cli.out) if email == "" { - email = cli.authConfig.Email + email = authconfig.Email } } else { email = *flEmail } } else { - password = cli.authConfig.Password - email = cli.authConfig.Email + password = authconfig.Password + email = authconfig.Email } if oldState != nil { term.RestoreTerminal(cli.terminalFd, oldState) } - cli.authConfig.Username = username - cli.authConfig.Password = password - cli.authConfig.Email = email + authconfig.Username = username + authconfig.Password = password + authconfig.Email = email + cli.configFile.Configs[auth.IndexServerAddress()] = authconfig - body, statusCode, err := cli.call("POST", "/auth", cli.authConfig) + body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[auth.IndexServerAddress()]) if statusCode == 401 { - cli.authConfig.Username = "" - cli.authConfig.Password = "" - cli.authConfig.Email = "" - auth.SaveConfig(cli.authConfig) + delete(cli.configFile.Configs, auth.IndexServerAddress()) + auth.SaveConfig(cli.configFile) return err } if err != nil { @@ -368,10 +372,10 @@ func (cli *DockerCli) CmdLogin(args ...string) error { var out2 APIAuth err = json.Unmarshal(body, &out2) if err != nil { - auth.LoadConfig(os.Getenv("HOME")) + cli.configFile, _ = auth.LoadConfig(os.Getenv("HOME")) return err } - auth.SaveConfig(cli.authConfig) + auth.SaveConfig(cli.configFile) if out2.Status != "" { fmt.Fprintf(cli.out, "%s\n", out2.Status) } @@ -802,10 +806,10 @@ func (cli *DockerCli) CmdPush(args ...string) error { // Custom repositories can have different rules, and we must also // allow pushing by image ID. if len(strings.SplitN(name, "/", 2)) == 1 { - return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in / (ex: %s/%s)", cli.authConfig.Username, name) + return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in / (ex: %s/%s)", cli.configFile.Configs[auth.IndexServerAddress()].Username, name) } - buf, err := json.Marshal(cli.authConfig) + buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) if err != nil { return err } @@ -1410,11 +1414,11 @@ func (cli *DockerCli) CmdRun(args ...string) error { func (cli *DockerCli) checkIfLogged(action string) error { // If condition AND the login failed - if cli.authConfig.Username == "" { + if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" { if err := cli.CmdLogin(""); err != nil { return err } - if cli.authConfig.Username == "" { + if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" { return fmt.Errorf("Please login prior to %s. ('docker login')", action) } } @@ -1670,11 +1674,11 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc err = out } - authConfig, _ := auth.LoadConfig(os.Getenv("HOME")) + configFile, _ := auth.LoadConfig(os.Getenv("HOME")) return &DockerCli{ proto: proto, addr: addr, - authConfig: authConfig, + configFile: configFile, in: in, out: out, err: err, @@ -1686,7 +1690,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc type DockerCli struct { proto string addr string - authConfig *auth.AuthConfig + configFile *auth.ConfigFile in io.ReadCloser out io.Writer err io.Writer From d1efb7843b268ddf08b021ccf27946d7c59217d5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 10 Jul 2013 12:55:05 +0000 Subject: [PATCH 69/80] basic version of the /events endpoint Upstream-commit: b5da816487d68853a8ac46630cb3118646f71d2d Component: engine --- components/engine/api.go | 28 ++++++++++++++++++++++++- components/engine/api_params.go | 15 +++++++------- components/engine/commands.go | 31 ++++++++++++++++++++-------- components/engine/runtime_test.go | 12 +++++------ components/engine/server.go | 34 +++++++++++++++++++++++-------- components/engine/utils/utils.go | 15 ++++++++++++++ 6 files changed, 104 insertions(+), 31 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index b6ab7badfa..9d0348b608 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -217,6 +217,31 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques return nil } +func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + events := make(chan utils.JSONMessage) + srv.Lock() + srv.events[r.RemoteAddr] = events + srv.Unlock() + w.Header().Set("Content-Type", "application/json") + wf := utils.NewWriteFlusher(w) + for { + event := <-events + b, err := json.Marshal(event) + if err != nil { + continue + } + _, err = wf.Write(b) + if err != nil { + utils.Debugf("%s", err) + srv.Lock() + delete(srv.events, r.RemoteAddr) + srv.Unlock() + return err + } + } + return nil +} + func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") @@ -855,8 +880,9 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{ "GET": { "/auth": getAuth, - "/version": getVersion, + "/events": getEvents, "/info": getInfo, + "/version": getVersion, "/images/json": getImagesJSON, "/images/viz": getImagesViz, "/images/search": getImagesSearch, diff --git a/components/engine/api_params.go b/components/engine/api_params.go index b371ca314f..26d70711a1 100644 --- a/components/engine/api_params.go +++ b/components/engine/api_params.go @@ -17,13 +17,14 @@ type APIImages struct { } type APIInfo struct { - Debug bool - Containers int - Images int - NFd int `json:",omitempty"` - NGoroutines int `json:",omitempty"` - MemoryLimit bool `json:",omitempty"` - SwapLimit bool `json:",omitempty"` + Debug bool + Containers int + Images int + NFd int `json:",omitempty"` + NGoroutines int `json:",omitempty"` + MemoryLimit bool `json:",omitempty"` + SwapLimit bool `json:",omitempty"` + NEventsListener int `json:",omitempty"` } type APITop struct { diff --git a/components/engine/commands.go b/components/engine/commands.go index b0e32162e6..12647feead 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -78,6 +78,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"build", "Build a container from a Dockerfile"}, {"commit", "Create a new image from a container's changes"}, {"diff", "Inspect changes on a container's filesystem"}, + {"events", "Get real time events from the server"}, {"export", "Stream the contents of a container as a tar archive"}, {"history", "Show the history of an image"}, {"images", "List images"}, @@ -466,6 +467,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "") fmt.Fprintf(cli.out, "Fds: %d\n", out.NFd) fmt.Fprintf(cli.out, "Goroutines: %d\n", out.NGoroutines) + fmt.Fprintf(cli.out, "EventsListeners: %d\n", out.NEventsListener) } if !out.MemoryLimit { fmt.Fprintf(cli.err, "WARNING: No memory limit support\n") @@ -1055,6 +1057,23 @@ func (cli *DockerCli) CmdCommit(args ...string) error { return nil } +func (cli *DockerCli) CmdEvents(args ...string) error { + cmd := Subcmd("events", "", "Get real time events from the server") + if err := cmd.Parse(args); err != nil { + return nil + } + + if cmd.NArg() != 0 { + cmd.Usage() + return nil + } + + if err := cli.stream("GET", "/events", nil, cli.out); err != nil { + return err + } + return nil +} + func (cli *DockerCli) CmdExport(args ...string) error { cmd := Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive") if err := cmd.Parse(args); err != nil { @@ -1509,19 +1528,13 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e if resp.Header.Get("Content-Type") == "application/json" { dec := json.NewDecoder(resp.Body) for { - var m utils.JSONMessage - if err := dec.Decode(&m); err == io.EOF { + var jm utils.JSONMessage + if err := dec.Decode(&jm); err == io.EOF { break } else if err != nil { return err } - if m.Progress != "" { - fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress) - } else if m.Error != "" { - return fmt.Errorf(m.Error) - } else { - fmt.Fprintf(out, "%s\n", m.Status) - } + jm.Display(out) } } else { if _, err := io.Copy(out, resp.Body); err != nil { diff --git a/components/engine/runtime_test.go b/components/engine/runtime_test.go index 66d92c8100..807097404d 100644 --- a/components/engine/runtime_test.go +++ b/components/engine/runtime_test.go @@ -17,12 +17,12 @@ import ( ) const ( - unitTestImageName = "docker-test-image" - unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 - unitTestNetworkBridge = "testdockbr0" - unitTestStoreBase = "/var/lib/docker/unit-tests" - testDaemonAddr = "127.0.0.1:4270" - testDaemonProto = "tcp" + unitTestImageName = "docker-test-image" + unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 + unitTestNetworkBridge = "testdockbr0" + unitTestStoreBase = "/var/lib/docker/unit-tests" + testDaemonAddr = "127.0.0.1:4270" + testDaemonProto = "tcp" ) var globalRuntime *Runtime diff --git a/components/engine/server.go b/components/engine/server.go index b92ed8fd73..2499d64397 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -32,8 +32,9 @@ func (srv *Server) DockerVersion() APIVersion { func (srv *Server) ContainerKill(name string) error { if container := srv.runtime.Get(name); container != nil { if err := container.Kill(); err != nil { - return fmt.Errorf("Error restarting container %s: %s", name, err) + return fmt.Errorf("Error killing container %s: %s", name, err) } + srv.SendEvent("kill", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -52,6 +53,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { if _, err := io.Copy(out, data); err != nil { return err } + srv.SendEvent("export", name) return nil } return fmt.Errorf("No such container: %s", name) @@ -209,13 +211,14 @@ func (srv *Server) DockerInfo() *APIInfo { imgcount = len(images) } return &APIInfo{ - Containers: len(srv.runtime.List()), - Images: imgcount, - MemoryLimit: srv.runtime.capabilities.MemoryLimit, - SwapLimit: srv.runtime.capabilities.SwapLimit, - Debug: os.Getenv("DEBUG") != "", - NFd: utils.GetTotalUsedFds(), - NGoroutines: runtime.NumGoroutine(), + Containers: len(srv.runtime.List()), + Images: imgcount, + MemoryLimit: srv.runtime.capabilities.MemoryLimit, + SwapLimit: srv.runtime.capabilities.SwapLimit, + Debug: os.Getenv("DEBUG") != "", + NFd: utils.GetTotalUsedFds(), + NGoroutines: runtime.NumGoroutine(), + NEventsListener: len(srv.events), } } @@ -810,6 +813,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) { } return "", err } + srv.SendEvent("create", container.ShortID()) return container.ShortID(), nil } @@ -818,6 +822,7 @@ func (srv *Server) ContainerRestart(name string, t int) error { if err := container.Restart(t); err != nil { return fmt.Errorf("Error restarting container %s: %s", name, err) } + srv.SendEvent("restart", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -837,6 +842,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error { if err := srv.runtime.Destroy(container); err != nil { return fmt.Errorf("Error destroying container %s: %s", name, err) } + srv.SendEvent("destroy", name) if removeVolume { // Retrieve all volumes from all remaining containers @@ -903,6 +909,7 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error { return err } *imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)}) + srv.SendEvent("delete", utils.TruncateID(id)) return nil } return nil @@ -946,6 +953,7 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro } if tagDeleted { imgs = append(imgs, APIRmi{Untagged: img.ShortID()}) + srv.SendEvent("untagged", img.ShortID()) } if len(srv.runtime.repositories.ByID()[img.ID]) == 0 { if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil { @@ -1018,6 +1026,7 @@ func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error { if err := container.Start(hostConfig); err != nil { return fmt.Errorf("Error starting container %s: %s", name, err) } + srv.SendEvent("start", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -1029,6 +1038,7 @@ func (srv *Server) ContainerStop(name string, t int) error { if err := container.Stop(t); err != nil { return fmt.Errorf("Error stopping container %s: %s", name, err) } + srv.SendEvent("stop", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -1162,15 +1172,23 @@ func NewServer(flGraphPath string, autoRestart, enableCors bool, dns ListOpts) ( enableCors: enableCors, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), + events: make(map[string]chan utils.JSONMessage), } runtime.srv = srv return srv, nil } +func (srv *Server) SendEvent(action, id string) { + for _, c := range srv.events { + c <- utils.JSONMessage{Status: action, ID: id} + } +} + type Server struct { sync.Mutex runtime *Runtime enableCors bool pullingPool map[string]struct{} pushingPool map[string]struct{} + events map[string]chan utils.JSONMessage } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 77b3f879cd..1523835f99 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -611,8 +611,23 @@ type JSONMessage struct { Status string `json:"status,omitempty"` Progress string `json:"progress,omitempty"` Error string `json:"error,omitempty"` + ID string `json:"id,omitempty"` } +func (jm *JSONMessage) Display(out io.Writer) (error) { + if jm.Progress != "" { + fmt.Fprintf(out, "%s %s\r", jm.Status, jm.Progress) + } else if jm.Error != "" { + return fmt.Errorf(jm.Error) + } else if jm.ID != "" { + fmt.Fprintf(out, "%s: %s\n", jm.ID, jm.Status) + } else { + fmt.Fprintf(out, "%s\n", jm.Status) + } + return nil +} + + type StreamFormatter struct { json bool used bool From fd89a1c59f66d03ee2e809f4eb9d574af2d2e14d Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 12 Jul 2013 15:09:20 +0000 Subject: [PATCH 70/80] add timestamp and change untagged -> untag Upstream-commit: b8d52ec2669332988a972bff3b5f5d2e9d526b33 Component: engine --- components/engine/server.go | 5 +++-- components/engine/utils/utils.go | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index 2499d64397..8ba90c69e3 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -19,6 +19,7 @@ import ( "runtime" "strings" "sync" + "time" ) func (srv *Server) DockerVersion() APIVersion { @@ -953,7 +954,7 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro } if tagDeleted { imgs = append(imgs, APIRmi{Untagged: img.ShortID()}) - srv.SendEvent("untagged", img.ShortID()) + srv.SendEvent("untag", img.ShortID()) } if len(srv.runtime.repositories.ByID()[img.ID]) == 0 { if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil { @@ -1180,7 +1181,7 @@ func NewServer(flGraphPath string, autoRestart, enableCors bool, dns ListOpts) ( func (srv *Server) SendEvent(action, id string) { for _, c := range srv.events { - c <- utils.JSONMessage{Status: action, ID: id} + c <- utils.JSONMessage{Status: action, ID: id, Time: time.Now().Unix()} } } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 1523835f99..acb015becd 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -612,9 +612,13 @@ type JSONMessage struct { Progress string `json:"progress,omitempty"` Error string `json:"error,omitempty"` ID string `json:"id,omitempty"` + Time int64 `json:"time,omitempty"` } func (jm *JSONMessage) Display(out io.Writer) (error) { + if jm.Time != 0 { + fmt.Fprintf(out, "[%s] ", time.Unix(jm.Time, 0)) + } if jm.Progress != "" { fmt.Fprintf(out, "%s %s\r", jm.Status, jm.Progress) } else if jm.Error != "" { From 012e9440352ecf0138ba0c7b5e8b8e8bbec44fc5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 12 Jul 2013 16:29:23 +0000 Subject: [PATCH 71/80] add since for polling, rename some vars Upstream-commit: 2e4d4c9f60d0ad92ab0cc84c56c060678222c4db Component: engine --- components/engine/api.go | 51 ++++++++++++++++++++++++++++------- components/engine/commands.go | 10 +++++-- components/engine/server.go | 33 +++++++++++++---------- 3 files changed, 68 insertions(+), 26 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index 9d0348b608..0a7f5744b1 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -218,24 +218,55 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques } func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - events := make(chan utils.JSONMessage) - srv.Lock() - srv.events[r.RemoteAddr] = events - srv.Unlock() - w.Header().Set("Content-Type", "application/json") - wf := utils.NewWriteFlusher(w) - for { - event := <-events + sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) (bool, error) { b, err := json.Marshal(event) if err != nil { - continue + return true, nil } _, err = wf.Write(b) if err != nil { utils.Debugf("%s", err) srv.Lock() - delete(srv.events, r.RemoteAddr) + delete(srv.listeners, r.RemoteAddr) srv.Unlock() + return false, err + } + return false, nil + } + + if err := parseForm(r); err != nil { + return err + } + listener := make(chan utils.JSONMessage) + srv.Lock() + srv.listeners[r.RemoteAddr] = listener + srv.Unlock() + since, err := strconv.ParseInt(r.Form.Get("since"), 10, 0) + if err != nil { + since = 0 + } + w.Header().Set("Content-Type", "application/json") + wf := utils.NewWriteFlusher(w) + if since != 0 { + for _, event := range srv.events { + if event.Time >= since { + skip, err := sendEvent(wf, &event) + if skip { + continue + } + if err != nil { + return err + } + } + } + } + for { + event := <-listener + skip, err := sendEvent(wf, &event) + if skip { + continue + } + if err != nil { return err } } diff --git a/components/engine/commands.go b/components/engine/commands.go index 12647feead..2ab107bb77 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1058,7 +1058,8 @@ func (cli *DockerCli) CmdCommit(args ...string) error { } func (cli *DockerCli) CmdEvents(args ...string) error { - cmd := Subcmd("events", "", "Get real time events from the server") + cmd := Subcmd("events", "[OPTIONS]", "Get real time events from the server") + since := cmd.String("since", "", "Show events previously created (used for polling).") if err := cmd.Parse(args); err != nil { return nil } @@ -1068,7 +1069,12 @@ func (cli *DockerCli) CmdEvents(args ...string) error { return nil } - if err := cli.stream("GET", "/events", nil, cli.out); err != nil { + v := url.Values{} + if *since != "" { + v.Set("since", *since) + } + + if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out); err != nil { return err } return nil diff --git a/components/engine/server.go b/components/engine/server.go index 8ba90c69e3..7efb850882 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -35,7 +35,7 @@ func (srv *Server) ContainerKill(name string) error { if err := container.Kill(); err != nil { return fmt.Errorf("Error killing container %s: %s", name, err) } - srv.SendEvent("kill", name) + srv.LogEvent("kill", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -54,7 +54,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { if _, err := io.Copy(out, data); err != nil { return err } - srv.SendEvent("export", name) + srv.LogEvent("export", name) return nil } return fmt.Errorf("No such container: %s", name) @@ -814,7 +814,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) { } return "", err } - srv.SendEvent("create", container.ShortID()) + srv.LogEvent("create", container.ShortID()) return container.ShortID(), nil } @@ -823,7 +823,7 @@ func (srv *Server) ContainerRestart(name string, t int) error { if err := container.Restart(t); err != nil { return fmt.Errorf("Error restarting container %s: %s", name, err) } - srv.SendEvent("restart", name) + srv.LogEvent("restart", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -843,7 +843,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error { if err := srv.runtime.Destroy(container); err != nil { return fmt.Errorf("Error destroying container %s: %s", name, err) } - srv.SendEvent("destroy", name) + srv.LogEvent("destroy", name) if removeVolume { // Retrieve all volumes from all remaining containers @@ -910,7 +910,7 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error { return err } *imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)}) - srv.SendEvent("delete", utils.TruncateID(id)) + srv.LogEvent("delete", utils.TruncateID(id)) return nil } return nil @@ -954,7 +954,7 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro } if tagDeleted { imgs = append(imgs, APIRmi{Untagged: img.ShortID()}) - srv.SendEvent("untag", img.ShortID()) + srv.LogEvent("untag", img.ShortID()) } if len(srv.runtime.repositories.ByID()[img.ID]) == 0 { if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil { @@ -1027,7 +1027,7 @@ func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error { if err := container.Start(hostConfig); err != nil { return fmt.Errorf("Error starting container %s: %s", name, err) } - srv.SendEvent("start", name) + srv.LogEvent("start", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -1039,7 +1039,7 @@ func (srv *Server) ContainerStop(name string, t int) error { if err := container.Stop(t); err != nil { return fmt.Errorf("Error stopping container %s: %s", name, err) } - srv.SendEvent("stop", name) + srv.LogEvent("stop", name) } else { return fmt.Errorf("No such container: %s", name) } @@ -1173,15 +1173,19 @@ func NewServer(flGraphPath string, autoRestart, enableCors bool, dns ListOpts) ( enableCors: enableCors, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), - events: make(map[string]chan utils.JSONMessage), + events: make([]utils.JSONMessage, 0, 64), //only keeps the 64 last events + listeners: make(map[string]chan utils.JSONMessage), } runtime.srv = srv return srv, nil } -func (srv *Server) SendEvent(action, id string) { - for _, c := range srv.events { - c <- utils.JSONMessage{Status: action, ID: id, Time: time.Now().Unix()} +func (srv *Server) LogEvent(action, id string) { + now := time.Now().Unix() + jm := utils.JSONMessage{Status: action, ID: id, Time: now} + srv.events = append(srv.events, jm) + for _, c := range srv.listeners { + c <- jm } } @@ -1191,5 +1195,6 @@ type Server struct { enableCors bool pullingPool map[string]struct{} pushingPool map[string]struct{} - events map[string]chan utils.JSONMessage + events []utils.JSONMessage + listeners map[string]chan utils.JSONMessage } From 05d166e113cf82f6e67f7b0efeb00212a4f6597f Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 17 Jul 2013 15:44:06 +0200 Subject: [PATCH 72/80] add docs Upstream-commit: ec559c02b8dd70692821b3dc7f497d6fccaa88ad Component: engine --- .../docs/sources/api/docker_remote_api.rst | 4 +++ .../sources/api/docker_remote_api_v1.3.rst | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/components/engine/docs/sources/api/docker_remote_api.rst b/components/engine/docs/sources/api/docker_remote_api.rst index f8a1381c53..792d43f501 100644 --- a/components/engine/docs/sources/api/docker_remote_api.rst +++ b/components/engine/docs/sources/api/docker_remote_api.rst @@ -44,6 +44,10 @@ What's new **New!** List the processes running inside a container. +.. http:get:: /events: + + **New!** Monitor docker's events via streaming or via polling + Builder (/build): - Simplify the upload of the build context diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index 273ec2e98d..69f480e453 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst @@ -1059,6 +1059,36 @@ Create a new image from a container's changes :statuscode 500: server error +Monitor Docker's events +*********************** + +.. http:get:: /events + + Get events from docker, either in real time via streaming, or via polling (using `since`) + + **Example request**: + + .. sourcecode:: http + + POST /events?since=1374067924 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"create","id":"dfdf82bd3881","time":1374067924} + {"status":"start","id":"dfdf82bd3881","time":1374067924} + {"status":"stop","id":"dfdf82bd3881","time":1374067966} + {"status":"destroy","id":"dfdf82bd3881","time":1374067970} + + :query since: timestamp used for polling + :statuscode 200: no error + :statuscode 500: server error + + 3. Going further ================ From 35c9a9d01e288176a1302a294bb1d0a6a76fda5b Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 17 Jul 2013 13:56:09 +0000 Subject: [PATCH 73/80] getEvents a bit simpler Upstream-commit: 8b3519c5f7540b99d19ed2c3163aabf9897dd5a4 Component: engine --- components/engine/api.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index 0a7f5744b1..dd3e8eed0d 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -218,20 +218,21 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques } func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) (bool, error) { + sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) (error) { b, err := json.Marshal(event) if err != nil { - return true, nil + return fmt.Errorf("JSON error") } _, err = wf.Write(b) if err != nil { + // On error, evict the listener utils.Debugf("%s", err) srv.Lock() delete(srv.listeners, r.RemoteAddr) srv.Unlock() - return false, err + return err } - return false, nil + return nil } if err := parseForm(r); err != nil { @@ -248,10 +249,11 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ w.Header().Set("Content-Type", "application/json") wf := utils.NewWriteFlusher(w) if since != 0 { + // If since, send previous events that happened after the timestamp for _, event := range srv.events { if event.Time >= since { - skip, err := sendEvent(wf, &event) - if skip { + err := sendEvent(wf, &event) + if err != nil && err.Error() == "JSON error" { continue } if err != nil { @@ -262,8 +264,8 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } for { event := <-listener - skip, err := sendEvent(wf, &event) - if skip { + err := sendEvent(wf, &event) + if err != nil && err.Error() == "JSON error" { continue } if err != nil { From ae5b19f00259a1ce4b1d33643e557640be5bd3cb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 14:35:14 +0000 Subject: [PATCH 74/80] use non-blocking channel to prevent dead-lock and add test for server Upstream-commit: 040c3b50d0a56baf98bd1ec14ad7d59c55a4ab31 Component: engine --- components/engine/server.go | 5 +++- components/engine/server_test.go | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/components/engine/server.go b/components/engine/server.go index 7efb850882..8eff17a947 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -1185,7 +1185,10 @@ func (srv *Server) LogEvent(action, id string) { jm := utils.JSONMessage{Status: action, ID: id, Time: now} srv.events = append(srv.events, jm) for _, c := range srv.listeners { - c <- jm + select { // non blocking channel + case c <- jm: + default: + } } } diff --git a/components/engine/server_test.go b/components/engine/server_test.go index 05a286aaa8..8612b3fcea 100644 --- a/components/engine/server_test.go +++ b/components/engine/server_test.go @@ -1,7 +1,9 @@ package docker import ( + "github.com/dotcloud/docker/utils" "testing" + "time" ) func TestContainerTagImageDelete(t *testing.T) { @@ -163,3 +165,41 @@ func TestRunWithTooLowMemoryLimit(t *testing.T) { } } + +func TestLogEvent(t *testing.T) { + runtime := mkRuntime(t) + srv := &Server{ + runtime: runtime, + events: make([]utils.JSONMessage, 0, 64), + listeners: make(map[string]chan utils.JSONMessage), + } + + srv.LogEvent("fakeaction", "fakeid") + + listener := make(chan utils.JSONMessage) + srv.Lock() + srv.listeners["test"] = listener + srv.Unlock() + + srv.LogEvent("fakeaction2", "fakeid") + + if len(srv.events) != 2 { + t.Fatalf("Expected 2 events, found %d", len(srv.events)) + } + go func() { + time.Sleep(200 * time.Millisecond) + srv.LogEvent("fakeaction3", "fakeid") + time.Sleep(200 * time.Millisecond) + srv.LogEvent("fakeaction4", "fakeid") + }() + + setTimeout(t, "Listening for events timed out", 2*time.Second, func() { + for i := 2; i < 4; i++ { + event := <-listener + if event != srv.events[i] { + t.Fatalf("Event received it different than expected") + } + } + }) + +} From 211bbde153caf194c9c48898a3b815a886a1be50 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 14:54:58 +0000 Subject: [PATCH 75/80] Add tests for the api Upstream-commit: ed7a4236b32f3f711c183dff8b70fbef17bae2d7 Component: engine --- components/engine/api_test.go | 38 ++++++++++++++++++++++++++++++ components/engine/commands_test.go | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/components/engine/api_test.go b/components/engine/api_test.go index 17ada96eab..13731dbf9e 100644 --- a/components/engine/api_test.go +++ b/components/engine/api_test.go @@ -89,6 +89,44 @@ func TestGetInfo(t *testing.T) { } } +func TestGetEvents(t *testing.T) { + runtime := mkRuntime(t) + srv := &Server{ + runtime: runtime, + events: make([]utils.JSONMessage, 0, 64), + listeners: make(map[string]chan utils.JSONMessage), + } + + srv.LogEvent("fakeaction", "fakeid") + srv.LogEvent("fakeaction2", "fakeid") + + req, err := http.NewRequest("GET", "/events?since=1", nil) + if err != nil { + t.Fatal(err) + } + + r := httptest.NewRecorder() + setTimeout(t, "", 500*time.Millisecond, func() { + if err := getEvents(srv, APIVERSION, r, req, nil); err != nil { + t.Fatal(err) + } + }) + + dec := json.NewDecoder(r.Body) + for i := 0; i < 2; i++ { + var jm utils.JSONMessage + if err := dec.Decode(&jm); err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + if jm != srv.events[i] { + t.Fatalf("Event received it different than expected") + } + } + +} + func TestGetImagesJSON(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) diff --git a/components/engine/commands_test.go b/components/engine/commands_test.go index 233c6337d4..030fb29f95 100644 --- a/components/engine/commands_test.go +++ b/components/engine/commands_test.go @@ -38,7 +38,7 @@ func setTimeout(t *testing.T, msg string, d time.Duration, f func()) { f() c <- false }() - if <-c { + if <-c && msg != "" { t.Fatal(msg) } } From 056ee7cfd5b6a8f0e395263cc735a943c9d11a7e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 14:31:42 +0000 Subject: [PATCH 76/80] add die event Upstream-commit: a41384ad7312a21fd8fe429637c8d6b5c883fa2a Component: engine --- components/engine/container.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/engine/container.go b/components/engine/container.go index f4a3c762ab..1e7bfed299 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -789,7 +789,9 @@ func (container *Container) monitor() { } } utils.Debugf("Process finished") - + if container.runtime != nil && container.runtime.srv != nil { + container.runtime.srv.LogEvent("die", container.ShortID()) + } exitCode := -1 if container.cmd != nil { exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() From f64e80d22bc7277b49ed6ef9c74c235a29b4bd31 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 23 Jul 2013 19:55:38 +0000 Subject: [PATCH 77/80] update AUTHORS Upstream-commit: 7aba68cd548d69e10e710029ca143b32fd291585 Component: engine --- components/engine/AUTHORS | 1 + components/engine/runtime_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/components/engine/AUTHORS b/components/engine/AUTHORS index 7506cd885c..86d03f6e12 100644 --- a/components/engine/AUTHORS +++ b/components/engine/AUTHORS @@ -76,6 +76,7 @@ Shawn Siefkas Silas Sewell Solomon Hykes Sridhar Ratnakumar +Stefan Praszalowicz Thatcher Peskens Thomas Bikeev Thomas Hansen diff --git a/components/engine/runtime_test.go b/components/engine/runtime_test.go index 66d92c8100..807097404d 100644 --- a/components/engine/runtime_test.go +++ b/components/engine/runtime_test.go @@ -17,12 +17,12 @@ import ( ) const ( - unitTestImageName = "docker-test-image" - unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 - unitTestNetworkBridge = "testdockbr0" - unitTestStoreBase = "/var/lib/docker/unit-tests" - testDaemonAddr = "127.0.0.1:4270" - testDaemonProto = "tcp" + unitTestImageName = "docker-test-image" + unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 + unitTestNetworkBridge = "testdockbr0" + unitTestStoreBase = "/var/lib/docker/unit-tests" + testDaemonAddr = "127.0.0.1:4270" + testDaemonProto = "tcp" ) var globalRuntime *Runtime From 0da0fa300a8b0f3253d7ad1ca8809cc784f77313 Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Tue, 23 Jul 2013 17:05:13 -0400 Subject: [PATCH 78/80] Rename: VersionChecker->VersionInfo. Upstream-commit: ede1e6d4754f3cffeac72f5d760fe4d87c5ae570 Component: engine --- components/engine/registry/registry.go | 16 ++++++++-------- components/engine/runtime_test.go | 12 ++++++------ components/engine/server.go | 22 +++++++++++----------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/components/engine/registry/registry.go b/components/engine/registry/registry.go index 6ba80cbea5..e6f4f592e2 100644 --- a/components/engine/registry/registry.go +++ b/components/engine/registry/registry.go @@ -98,9 +98,9 @@ func ResolveRepositoryName(reposName string) (string, string, error) { return endpoint, reposName, err } -// VersionChecker is used to model entities which has a version. +// VersionInfo is used to model entities which has a version. // It is basically a tupple with name and version. -type VersionChecker interface { +type VersionInfo interface { Name() string Version() string } @@ -114,7 +114,7 @@ func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) { // Set the user agent field in the header based on the versions provided // in NewRegistry() and extra. -func (r *Registry) setUserAgent(req *http.Request, extra ...VersionChecker) { +func (r *Registry) setUserAgent(req *http.Request, extra ...VersionInfo) { if len(r.baseVersions)+len(extra) == 0 { return } @@ -569,11 +569,11 @@ type ImgData struct { type Registry struct { client *http.Client authConfig *auth.AuthConfig - baseVersions []VersionChecker + baseVersions []VersionInfo baseVersionsStr string } -func validVersion(version VersionChecker) bool { +func validVersion(version VersionInfo) bool { stopChars := " \t\r\n/" if strings.ContainsAny(version.Name(), stopChars) { return false @@ -586,11 +586,11 @@ func validVersion(version VersionChecker) bool { // Convert versions to a string and append the string to the string base. // -// Each VersionChecker will be converted to a string in the format of +// Each VersionInfo will be converted to a string in the format of // "product/version", where the "product" is get from the Name() method, while // version is get from the Version() method. Several pieces of verson information // will be concatinated and separated by space. -func appendVersions(base string, versions ...VersionChecker) string { +func appendVersions(base string, versions ...VersionInfo) string { if len(versions) == 0 { return base } @@ -612,7 +612,7 @@ func appendVersions(base string, versions ...VersionChecker) string { return buf.String() } -func NewRegistry(root string, authConfig *auth.AuthConfig, baseVersions ...VersionChecker) (r *Registry, err error) { +func NewRegistry(root string, authConfig *auth.AuthConfig, baseVersions ...VersionInfo) (r *Registry, err error) { httpTransport := &http.Transport{ DisableKeepAlives: true, Proxy: http.ProxyFromEnvironment, diff --git a/components/engine/runtime_test.go b/components/engine/runtime_test.go index 6b94e5ce2c..74ca09cc8d 100644 --- a/components/engine/runtime_test.go +++ b/components/engine/runtime_test.go @@ -18,12 +18,12 @@ import ( ) const ( - unitTestImageName = "docker-test-image" - unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 - unitTestNetworkBridge = "testdockbr0" - unitTestStoreBase = "/var/lib/docker/unit-tests" - testDaemonAddr = "127.0.0.1:4270" - testDaemonProto = "tcp" + unitTestImageName = "docker-test-image" + unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 + unitTestNetworkBridge = "testdockbr0" + unitTestStoreBase = "/var/lib/docker/unit-tests" + testDaemonAddr = "127.0.0.1:4270" + testDaemonProto = "tcp" ) var globalRuntime *Runtime diff --git a/components/engine/server.go b/components/engine/server.go index 925e4e3386..62c0243820 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -26,21 +26,21 @@ func (srv *Server) DockerVersion() APIVersion { } } -// plainVersionChecker is a simple implementation of -// the interface VersionChecker, which is used +// simpleVersionInfo is a simple implementation of +// the interface VersionInfo, which is used // to provide version information for some product, // component, etc. It stores the product name and the version // in string and returns them on calls to Name() and Version(). -type plainVersionChecker struct { +type simpleVersionInfo struct { name string version string } -func (v *plainVersionChecker) Name() string { +func (v *simpleVersionInfo) Name() string { return v.name } -func (v *plainVersionChecker) Version() string { +func (v *simpleVersionInfo) Version() string { return v.version } @@ -48,20 +48,20 @@ func (v *plainVersionChecker) Version() string { // docker, go, git-commit (of the docker) and the host's kernel. // // Such information will be used on call to NewRegistry(). -func (srv *Server) versionCheckers() []registry.VersionChecker { +func (srv *Server) versionCheckers() []registry.VersionInfo { v := srv.DockerVersion() - ret := make([]registry.VersionChecker, 0, 4) - ret = append(ret, &plainVersionChecker{"docker", v.Version}) + ret := make([]registry.VersionInfo, 0, 4) + ret = append(ret, &simpleVersionInfo{"docker", v.Version}) if len(v.GoVersion) > 0 { - ret = append(ret, &plainVersionChecker{"go", v.GoVersion}) + ret = append(ret, &simpleVersionInfo{"go", v.GoVersion}) } if len(v.GitCommit) > 0 { - ret = append(ret, &plainVersionChecker{"git-commit", v.GitCommit}) + ret = append(ret, &simpleVersionInfo{"git-commit", v.GitCommit}) } kernelVersion, err := utils.GetKernelVersion() if err == nil { - ret = append(ret, &plainVersionChecker{"kernel", kernelVersion.String()}) + ret = append(ret, &simpleVersionInfo{"kernel", kernelVersion.String()}) } return ret From e919f6ad2f5da5ffc51cd6d835c6ed0e6e887810 Mon Sep 17 00:00:00 2001 From: Nan Monnand Deng Date: Tue, 23 Jul 2013 17:17:31 -0400 Subject: [PATCH 79/80] versionCheckers()->versionInfos(). Upstream-commit: 1ae54707a0ee1f690a7dca17d83b5417e83704c3 Component: engine --- components/engine/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index 62c0243820..b706ceda12 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -48,7 +48,7 @@ func (v *simpleVersionInfo) Version() string { // docker, go, git-commit (of the docker) and the host's kernel. // // Such information will be used on call to NewRegistry(). -func (srv *Server) versionCheckers() []registry.VersionInfo { +func (srv *Server) versionInfos() []registry.VersionInfo { v := srv.DockerVersion() ret := make([]registry.VersionInfo, 0, 4) ret = append(ret, &simpleVersionInfo{"docker", v.Version}) @@ -96,7 +96,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { } func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { - r, err := registry.NewRegistry(srv.runtime.root, nil, srv.versionCheckers()...) + r, err := registry.NewRegistry(srv.runtime.root, nil, srv.versionInfos()...) if err != nil { return nil, err } @@ -511,7 +511,7 @@ func (srv *Server) poolRemove(kind, key string) error { } func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error { - r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.versionCheckers()...) + r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.versionInfos()...) if err != nil { return err } @@ -728,7 +728,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo out = utils.NewWriteFlusher(out) img, err := srv.runtime.graph.Get(localName) - r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.versionCheckers()...) + r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.versionInfos()...) if err2 != nil { return err2 } From f910d92eadbca2cbbfe5ba0a9c1b96125e0ac940 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 24 Jul 2013 12:28:22 +0000 Subject: [PATCH 80/80] fix tests Upstream-commit: f4b41e1a6c2c5d531451bf2feeb3877e03eb8c1c Component: engine --- components/engine/api.go | 2 +- components/engine/auth/auth.go | 1 + components/engine/auth/auth_test.go | 14 ++++++++------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index 94c1a3b8d2..834c41a68c 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -179,7 +179,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques } func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) (error) { + sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) error { b, err := json.Marshal(event) if err != nil { return fmt.Errorf("JSON error") diff --git a/components/engine/auth/auth.go b/components/engine/auth/auth.go index 8f4d09fb8f..bffed49807 100644 --- a/components/engine/auth/auth.go +++ b/components/engine/auth/auth.go @@ -102,6 +102,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) { if err != nil { return nil, err } + authConfig.Auth = "" configFile.Configs[k] = authConfig } } diff --git a/components/engine/auth/auth_test.go b/components/engine/auth/auth_test.go index d036de7364..458a505ea2 100644 --- a/components/engine/auth/auth_test.go +++ b/components/engine/auth/auth_test.go @@ -11,7 +11,9 @@ import ( func TestEncodeAuth(t *testing.T) { newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"} authStr := encodeAuth(newAuthConfig) - decAuthConfig, err := decodeAuth(authStr) + decAuthConfig := &AuthConfig{} + var err error + decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr) if err != nil { t.Fatal(err) } @@ -29,8 +31,8 @@ func TestEncodeAuth(t *testing.T) { func TestLogin(t *testing.T) { os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com") defer os.Setenv("DOCKER_INDEX_URL", "") - authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp") - status, err := Login(authConfig, false) + authConfig := &AuthConfig{Username: "unittester", Password: "surlautrerivejetattendrai", Email: "noise+unittester@dotcloud.com"} + status, err := Login(authConfig) if err != nil { t.Fatal(err) } @@ -49,8 +51,8 @@ func TestCreateAccount(t *testing.T) { } token := hex.EncodeToString(tokenBuffer)[:12] username := "ut" + token - authConfig := NewAuthConfig(username, "test42", "docker-ut+"+token+"@example.com", "/tmp") - status, err := Login(authConfig, false) + authConfig := &AuthConfig{Username: username, Password: "test42", Email: "docker-ut+"+token+"@example.com"} + status, err := Login(authConfig) if err != nil { t.Fatal(err) } @@ -60,7 +62,7 @@ func TestCreateAccount(t *testing.T) { t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status) } - status, err = Login(authConfig, false) + status, err = Login(authConfig) if err == nil { t.Fatalf("Expected error but found nil instead") }