From 75d2244023cd39af17976c56225da6b5158ff68f Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 10:51:47 -0700 Subject: [PATCH 01/20] Update docker build UI --- commands.go | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/commands.go b/commands.go index e34c40f8cc..501dea6043 100644 --- a/commands.go +++ b/commands.go @@ -17,6 +17,7 @@ import ( "net/url" "os" "os/signal" + "path" "path/filepath" "reflect" "strconv" @@ -130,17 +131,12 @@ func (cli *DockerCli) CmdInsert(args ...string) error { } func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := Subcmd("build", "[OPTIONS] [CONTEXT]", "Build an image from a Dockerfile") - fileName := cmd.String("f", "Dockerfile", "Use `file` as Dockerfile. Can be '-' for stdin") + cmd := Subcmd("build", "[CONTEXT]", "Build an image from a Dockerfile") if err := cmd.Parse(args); err != nil { return nil } - var ( - file io.ReadCloser - multipartBody io.Reader - err error - ) + var multipartBody io.Reader // Init the needed component for the Multipart buff := bytes.NewBuffer([]byte{}) @@ -148,17 +144,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error { w := multipart.NewWriter(buff) boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n") - // Create a FormFile multipart for the Dockerfile - if *fileName == "-" { - file = os.Stdin - } else { - file, err = os.Open(*fileName) - if err != nil { - return err - } - defer file.Close() + dockerfile := "Dockerfile" + + if cmd.Arg(0) != "" { + dockerfile = path.Join(cmd.Arg(0), dockerfile) } - if wField, err := w.CreateFormFile("Dockerfile", *fileName); err != nil { + + // Create a FormFile multipart for the Dockerfile + file, err := os.Open(dockerfile) + if err != nil { + return err + } + defer file.Close() + if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { return err } else { io.Copy(wField, file) From 6cbc7757b2b4d068a98287754da552028fd62212 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 10:53:24 -0700 Subject: [PATCH 02/20] Fix issue with unknown instruction within docker build --- buildfile.go | 1 + 1 file changed, 1 insertion(+) diff --git a/buildfile.go b/buildfile.go index 23f2f47172..b9ca5696d5 100644 --- a/buildfile.go +++ b/buildfile.go @@ -337,6 +337,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:])) if !exists { fmt.Fprintf(b.out, "Skipping unknown instruction %s\n", strings.ToUpper(instruction)) + continue } ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface() if ret != nil { From 33ea1483d5b89559e1fe9a9556e3ea757f673e16 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 11:43:29 -0700 Subject: [PATCH 03/20] Allow docker build from stdin --- commands.go | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/commands.go b/commands.go index 501dea6043..8e8e17df81 100644 --- a/commands.go +++ b/commands.go @@ -131,12 +131,16 @@ func (cli *DockerCli) CmdInsert(args ...string) error { } func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := Subcmd("build", "[CONTEXT]", "Build an image from a Dockerfile") + cmd := Subcmd("build", "[CONTEXT|-]", "Build an image from a Dockerfile") if err := cmd.Parse(args); err != nil { return nil } - var multipartBody io.Reader + var ( + multipartBody io.Reader + file io.ReadCloser + contextPath string + ) // Init the needed component for the Multipart buff := bytes.NewBuffer([]byte{}) @@ -146,16 +150,23 @@ func (cli *DockerCli) CmdBuild(args ...string) error { dockerfile := "Dockerfile" - if cmd.Arg(0) != "" { + if cmd.Arg(0) != "" && cmd.Arg(0) != "-" { + contextPath = cmd.Arg(0) dockerfile = path.Join(cmd.Arg(0), dockerfile) } + if cmd.Arg(0) != "-" { + f, err := os.Open(dockerfile) + if err != nil { + return err + } + defer f.Close() + file = f + } else { + contextPath = cmd.Arg(1) + file = os.Stdin + } // Create a FormFile multipart for the Dockerfile - file, err := os.Open(dockerfile) - if err != nil { - return err - } - defer file.Close() if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { return err } else { @@ -166,14 +177,14 @@ func (cli *DockerCli) CmdBuild(args ...string) error { compression := Bzip2 // Create a FormFile multipart for the context if needed - if cmd.Arg(0) != "" { + if contextPath != "" { // FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? - context, err := Tar(cmd.Arg(0), compression) + context, err := Tar(contextPath, compression) if err != nil { return err } // NOTE: Do this in case '.' or '..' is input - absPath, err := filepath.Abs(cmd.Arg(0)) + absPath, err := filepath.Abs(contextPath) if err != nil { return err } @@ -193,7 +204,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { return err } req.Header.Set("Content-Type", w.FormDataContentType()) - if cmd.Arg(0) != "" { + if contextPath != "" { req.Header.Set("X-Docker-Context-Compression", compression.Flag()) fmt.Println("Uploading Context...") } From d97fff60a9275324290bb01b921c9b6b0967a4fd Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 11:50:49 -0700 Subject: [PATCH 04/20] Update docker build docs --- docs/sources/commandline/command/build.rst | 32 ++++++++++++++++++++-- docs/sources/use/builder.rst | 10 +++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/sources/commandline/command/build.rst b/docs/sources/commandline/command/build.rst index a8d2093a5b..0cb6463e4c 100644 --- a/docs/sources/commandline/command/build.rst +++ b/docs/sources/commandline/command/build.rst @@ -8,6 +8,32 @@ :: - Usage: docker build - - Example: cat Dockerfile | docker build - - Build a new image from the Dockerfile passed via stdin + Usage: docker build [CONTEXT|-] + Build a new image from a Dockerfile + +Examples +-------- + +.. code-block:: bash + + docker build + +This will take the local Dockerfile without context + +.. code-block:: bash + + docker build - + +This will read a Dockerfile form Stdin without context + +.. code-block:: bash + + docker build . + +This will take the local Dockerfile and set the current directory as context + +.. code-block:: bash + + docker build - . + +This will read a Dockerfile from Stdin and set the current directory as context diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index 4e53ed4a79..abd5b9ecb1 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -125,8 +125,14 @@ curl was installed within the image. .. note:: The path must include the file name. -.. note:: - This instruction has temporarily disabled +2.8 ADD +------- + + ``ADD `` + +The `ADD` instruction will insert the files from the `` path of the context into `` path +of the container. +The context must be set in order to use this instruction. (see examples) 3. Dockerfile Examples ====================== From 92939569ab06d13cd179d6e998ef9576d3921bb6 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 11:53:44 -0700 Subject: [PATCH 05/20] Update build doc --- docs/sources/commandline/command/build.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sources/commandline/command/build.rst b/docs/sources/commandline/command/build.rst index 0cb6463e4c..f7df8cbbc3 100644 --- a/docs/sources/commandline/command/build.rst +++ b/docs/sources/commandline/command/build.rst @@ -2,9 +2,9 @@ :description: Build a new image from the Dockerfile passed via stdin :keywords: build, docker, container, documentation -======================================================== -``build`` -- Build a container from Dockerfile via stdin -======================================================== +================================================ +``build`` -- Build a container from a Dockerfile +================================================ :: From ae0d555022411025607acce43ea1929bafd7174a Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 14:37:03 -0700 Subject: [PATCH 06/20] Fix autorun issue within docker build --- buildfile.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/buildfile.go b/buildfile.go index b9ca5696d5..0dd9bc049c 100644 --- a/buildfile.go +++ b/buildfile.go @@ -95,10 +95,12 @@ func (b *buildFile) CmdRun(args string) error { return err } - cmd, env := b.config.Cmd, b.config.Env + cmd := b.config.Cmd b.config.Cmd = nil MergeConfig(b.config, config) + utils.Debugf("Commang to be executed: %v", b.config.Cmd) + if cache, err := b.srv.ImageGetCached(b.image, config); err != nil { return err } else if cache != nil { @@ -111,8 +113,11 @@ func (b *buildFile) CmdRun(args string) error { if err != nil { return err } - b.config.Cmd, b.config.Env = cmd, env - return b.commit(cid) + if err := b.commit(cid, cmd); err != nil { + return err + } + b.config.Cmd = cmd + return nil } func (b *buildFile) CmdEnv(args string) error { @@ -153,6 +158,7 @@ func (b *buildFile) CmdExpose(args string) error { } func (b *buildFile) CmdInsert(args string) error { + if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to insert") } @@ -169,6 +175,7 @@ func (b *buildFile) CmdInsert(args string) error { } defer file.Body.Close() + cmd := b.config.Cmd b.config.Cmd = []string{"echo", "INSERT", sourceUrl, "in", destPath} cid, err := b.run() if err != nil { @@ -184,7 +191,7 @@ func (b *buildFile) CmdInsert(args string) error { return err } - return b.commit(cid) + return b.commit(cid, cmd) } func (b *buildFile) CmdAdd(args string) error { @@ -198,6 +205,7 @@ func (b *buildFile) CmdAdd(args string) error { orig := strings.Trim(tmp[0], " ") dest := strings.Trim(tmp[1], " ") + cmd := b.config.Cmd b.config.Cmd = []string{"echo", "PUSH", orig, "in", dest} cid, err := b.run() if err != nil { @@ -236,7 +244,7 @@ func (b *buildFile) CmdAdd(args string) error { } } - return b.commit(cid) + return b.commit(cid, cmd) } func (b *buildFile) run() (string, error) { @@ -265,7 +273,8 @@ func (b *buildFile) run() (string, error) { return c.Id, nil } -func (b *buildFile) commit(id string) error { +// Commit the container with the autorun command +func (b *buildFile) commit(id string, autoCmd []string) error { if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to commit") } @@ -286,8 +295,11 @@ func (b *buildFile) commit(id string) error { return fmt.Errorf("An error occured while creating the container") } + // Note: Actually copy the struct + autoConfig := *b.config + autoConfig.Cmd = autoCmd // Commit the container - image, err := b.builder.Commit(container, "", "", "", b.maintainer, nil) + image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig) if err != nil { return err } @@ -347,7 +359,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { fmt.Fprintf(b.out, "===> %v\n", b.image) } if b.needCommit { - if err := b.commit(""); err != nil { + if err := b.commit("", nil); err != nil { return "", err } } From b6165daa775bbf20451ecbe570ab0fb85ad35128 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 15:03:00 -0700 Subject: [PATCH 07/20] Create a layer for each operation (expose, cmd, etc) --- buildfile.go | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/buildfile.go b/buildfile.go index 0dd9bc049c..05dafe53e1 100644 --- a/buildfile.go +++ b/buildfile.go @@ -32,8 +32,6 @@ type buildFile struct { tmpContainers map[string]struct{} tmpImages map[string]struct{} - needCommit bool - out io.Writer } @@ -81,9 +79,8 @@ func (b *buildFile) CmdFrom(name string) error { } func (b *buildFile) CmdMaintainer(name string) error { - b.needCommit = true b.maintainer = name - return nil + return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name)) } func (b *buildFile) CmdRun(args string) error { @@ -113,7 +110,7 @@ func (b *buildFile) CmdRun(args string) error { if err != nil { return err } - if err := b.commit(cid, cmd); err != nil { + if err := b.commit(cid, cmd, "run"); err != nil { return err } b.config.Cmd = cmd @@ -121,7 +118,6 @@ func (b *buildFile) CmdRun(args string) error { } func (b *buildFile) CmdEnv(args string) error { - b.needCommit = true tmp := strings.SplitN(args, " ", 2) if len(tmp) != 2 { return fmt.Errorf("Invalid ENV format") @@ -136,29 +132,25 @@ func (b *buildFile) CmdEnv(args string) error { } } b.config.Env = append(b.config.Env, key+"="+value) - return nil + return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s=%s", key, value)) } func (b *buildFile) CmdCmd(args string) error { - b.needCommit = true var cmd []string if err := json.Unmarshal([]byte(args), &cmd); err != nil { utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err) - b.config.Cmd = []string{"/bin/sh", "-c", args} - } else { - b.config.Cmd = cmd + cmd = []string{"/bin/sh", "-c", args} } - return nil + return b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)) } func (b *buildFile) CmdExpose(args string) error { ports := strings.Split(args, " ") b.config.PortSpecs = append(ports, b.config.PortSpecs...) - return nil + return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports)) } func (b *buildFile) CmdInsert(args string) error { - if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to insert") } @@ -176,7 +168,7 @@ func (b *buildFile) CmdInsert(args string) error { defer file.Body.Close() cmd := b.config.Cmd - b.config.Cmd = []string{"echo", "INSERT", sourceUrl, "in", destPath} + b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) INSERT %s in %s", sourceUrl, destPath)} cid, err := b.run() if err != nil { return err @@ -190,8 +182,7 @@ func (b *buildFile) CmdInsert(args string) error { if err := container.Inject(file.Body, destPath); err != nil { return err } - - return b.commit(cid, cmd) + return b.commit(cid, cmd, fmt.Sprintf("INSERT %s in %s", sourceUrl, destPath)) } func (b *buildFile) CmdAdd(args string) error { @@ -206,7 +197,7 @@ func (b *buildFile) CmdAdd(args string) error { dest := strings.Trim(tmp[1], " ") cmd := b.config.Cmd - b.config.Cmd = []string{"echo", "PUSH", orig, "in", dest} + b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)} cid, err := b.run() if err != nil { return err @@ -243,8 +234,7 @@ func (b *buildFile) CmdAdd(args string) error { return err } } - - return b.commit(cid, cmd) + return b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)) } func (b *buildFile) run() (string, error) { @@ -274,20 +264,18 @@ func (b *buildFile) run() (string, error) { } // Commit the container with the autorun command -func (b *buildFile) commit(id string, autoCmd []string) error { +func (b *buildFile) commit(id string, autoCmd []string, comment string) error { if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to commit") } b.config.Image = b.image if id == "" { - cmd := b.config.Cmd - b.config.Cmd = []string{"true"} + b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} if cid, err := b.run(); err != nil { return err } else { id = cid } - b.config.Cmd = cmd } container := b.runtime.Get(id) @@ -305,7 +293,6 @@ func (b *buildFile) commit(id string, autoCmd []string) error { } b.tmpImages[image.Id] = struct{}{} b.image = image.Id - b.needCommit = false return nil } @@ -358,11 +345,6 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { fmt.Fprintf(b.out, "===> %v\n", b.image) } - if b.needCommit { - if err := b.commit("", nil); err != nil { - return "", err - } - } if b.image != "" { // The build is successful, keep the temporary containers and images for i := range b.tmpImages { From 560a74af1562de007ba487e102fd00a2f658e6a0 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 16:10:11 -0700 Subject: [PATCH 08/20] Fix cache miss issue within docker build --- buildfile.go | 23 ++++++++++++++++++++--- server.go | 19 ++----------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/buildfile.go b/buildfile.go index 05dafe53e1..60a975c8de 100644 --- a/buildfile.go +++ b/buildfile.go @@ -98,12 +98,14 @@ func (b *buildFile) CmdRun(args string) error { utils.Debugf("Commang to be executed: %v", b.config.Cmd) - if cache, err := b.srv.ImageGetCached(b.image, config); err != nil { + if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { return err } else if cache != nil { - utils.Debugf("Use cached version") + utils.Debugf("[BUILDER] Use cached version") b.image = cache.Id return nil + } else { + utils.Debugf("[BUILDER] Cache miss") } cid, err := b.run() @@ -182,7 +184,11 @@ func (b *buildFile) CmdInsert(args string) error { if err := container.Inject(file.Body, destPath); err != nil { return err } - return b.commit(cid, cmd, fmt.Sprintf("INSERT %s in %s", sourceUrl, destPath)) + if err := b.commit(cid, cmd, fmt.Sprintf("INSERT %s in %s", sourceUrl, destPath)); err != nil { + return err + } + b.config.Cmd = cmd + return nil } func (b *buildFile) CmdAdd(args string) error { @@ -271,6 +277,17 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { b.config.Image = b.image if id == "" { b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} + + if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { + return err + } else if cache != nil { + utils.Debugf("[BUILDER] Use cached version") + b.image = cache.Id + return nil + } else { + utils.Debugf("[BUILDER] Cache miss") + } + if cid, err := b.run(); err != nil { return err } else { diff --git a/server.go b/server.go index 0440b0a8a4..0125d2f0c1 100644 --- a/server.go +++ b/server.go @@ -720,28 +720,13 @@ func (srv *Server) ImageDelete(name string) error { } func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error) { - - // Retrieve all images - images, err := srv.runtime.graph.All() + byParent, err := srv.runtime.graph.ByParent() if err != nil { return nil, err } - // Store the tree in a map of map (map[parentId][childId]) - imageMap := make(map[string]map[string]struct{}) - for _, img := range images { - if _, exists := imageMap[img.Parent]; !exists { - imageMap[img.Parent] = make(map[string]struct{}) - } - imageMap[img.Parent][img.Id] = struct{}{} - } - // Loop on the children of the given image and check the config - for elem := range imageMap[imgId] { - img, err := srv.runtime.graph.Get(elem) - if err != nil { - return nil, err - } + for _, img := range byParent[imgId] { if CompareConfig(&img.ContainerConfig, config) { return img, nil } From 881fdc59edf39ba8d87b44b05db7fcd95661d083 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 17:04:46 -0700 Subject: [PATCH 09/20] Remove cache opti that cause wrong cache miss --- server.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index 0125d2f0c1..0440b0a8a4 100644 --- a/server.go +++ b/server.go @@ -720,13 +720,28 @@ func (srv *Server) ImageDelete(name string) error { } func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error) { - byParent, err := srv.runtime.graph.ByParent() + + // Retrieve all images + images, err := srv.runtime.graph.All() if err != nil { return nil, err } + // Store the tree in a map of map (map[parentId][childId]) + imageMap := make(map[string]map[string]struct{}) + for _, img := range images { + if _, exists := imageMap[img.Parent]; !exists { + imageMap[img.Parent] = make(map[string]struct{}) + } + imageMap[img.Parent][img.Id] = struct{}{} + } + // Loop on the children of the given image and check the config - for _, img := range byParent[imgId] { + for elem := range imageMap[imgId] { + img, err := srv.runtime.graph.Get(elem) + if err != nil { + return nil, err + } if CompareConfig(&img.ContainerConfig, config) { return img, nil } From faafbf211899bc28e7e21e76de051d87ef5b5cd2 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 17:58:05 -0700 Subject: [PATCH 10/20] Fix ADD behavior on single files --- buildfile.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/buildfile.go b/buildfile.go index 60a975c8de..fa19ab2392 100644 --- a/buildfile.go +++ b/buildfile.go @@ -213,13 +213,17 @@ func (b *buildFile) CmdAdd(args string) error { if container == nil { return fmt.Errorf("Error while creating the container (CmdAdd)") } - - if err := os.MkdirAll(path.Join(container.rwPath(), dest), 0700); err != nil { + if err := container.EnsureMounted(); err != nil { return err } + defer container.Unmount() origPath := path.Join(b.context, orig) - destPath := path.Join(container.rwPath(), dest) + destPath := path.Join(container.RootfsPath(), dest) + + if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil { + return err + } fi, err := os.Stat(origPath) if err != nil { From dcab408f6a047d5a4ccac0d3999d8c896f0a63dc Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 29 May 2013 18:14:50 -0700 Subject: [PATCH 11/20] Fixed phrasing, typos and formatting in 'docker build' --- buildfile.go | 6 +++--- commands.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/buildfile.go b/buildfile.go index fa19ab2392..805ebb4fbb 100644 --- a/buildfile.go +++ b/buildfile.go @@ -96,7 +96,7 @@ func (b *buildFile) CmdRun(args string) error { b.config.Cmd = nil MergeConfig(b.config, config) - utils.Debugf("Commang to be executed: %v", b.config.Cmd) + utils.Debugf("Command to be executed: %v", b.config.Cmd) if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { return err @@ -197,7 +197,7 @@ func (b *buildFile) CmdAdd(args string) error { } tmp := strings.SplitN(args, " ", 2) if len(tmp) != 2 { - return fmt.Errorf("Invalid INSERT format") + return fmt.Errorf("Invalid ADD format") } orig := strings.Trim(tmp[0], " ") dest := strings.Trim(tmp[1], " ") @@ -371,7 +371,7 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { for i := range b.tmpImages { delete(b.tmpImages, i) } - fmt.Fprintf(b.out, "Build success.\n Image id:\n%s\n", b.image) + fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image) return b.image, nil } for i := range b.tmpContainers { diff --git a/commands.go b/commands.go index 8e8e17df81..6cca487851 100644 --- a/commands.go +++ b/commands.go @@ -131,7 +131,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error { } func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := Subcmd("build", "[CONTEXT|-]", "Build an image from a Dockerfile") + cmd := Subcmd("build", "PATH | -", "Build a new container image from the source code at PATH") if err := cmd.Parse(args); err != nil { return nil } From fc864d2f0f2814be87984cefec66719c6d95d802 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 29 May 2013 18:18:57 -0700 Subject: [PATCH 12/20] Simplified syntax of 'docker build': 'docker build PATH | -' --- commands.go | 55 +++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/commands.go b/commands.go index 6cca487851..67b8e917f5 100644 --- a/commands.go +++ b/commands.go @@ -135,6 +135,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if err := cmd.Parse(args); err != nil { return nil } + if cmd.NArg() != 1 { + cmd.Usage() + return nil + } var ( multipartBody io.Reader @@ -148,43 +152,26 @@ func (cli *DockerCli) CmdBuild(args ...string) error { w := multipart.NewWriter(buff) boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n") - dockerfile := "Dockerfile" - - if cmd.Arg(0) != "" && cmd.Arg(0) != "-" { - contextPath = cmd.Arg(0) - dockerfile = path.Join(cmd.Arg(0), dockerfile) - } - if cmd.Arg(0) != "-" { - f, err := os.Open(dockerfile) - if err != nil { - return err - } - defer f.Close() - file = f - } else { - contextPath = cmd.Arg(1) - file = os.Stdin - } - - // Create a FormFile multipart for the Dockerfile - if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { - return err - } else { - io.Copy(wField, file) - } - multipartBody = io.MultiReader(multipartBody, boundary) - compression := Bzip2 - // Create a FormFile multipart for the context if needed - if contextPath != "" { + if cmd.Arg(0) == "-" { + file = os.Stdin + } else { + // Send Dockerfile from arg/Dockerfile (deprecate later) + if f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile")); err != nil { + return err + } else { + file = f + } + // Send context from arg + // Create a FormFile multipart for the context if needed // FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? - context, err := Tar(contextPath, compression) + context, err := Tar(cmd.Arg(0), compression) if err != nil { return err } // NOTE: Do this in case '.' or '..' is input - absPath, err := filepath.Abs(contextPath) + absPath, err := filepath.Abs(cmd.Arg(0)) if err != nil { return err } @@ -194,9 +181,15 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // FIXME: Find a way to have a progressbar for the upload too io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, "Caching Context %v/%v (%v)\r", false)) } - multipartBody = io.MultiReader(multipartBody, boundary) } + // Create a FormFile multipart for the Dockerfile + if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { + return err + } else { + io.Copy(wField, file) + } + multipartBody = io.MultiReader(multipartBody, boundary) // Send the multipart request with correct content-type req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), multipartBody) From 9a394041270d2a8ba648f215dacc186473140552 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 29 May 2013 18:55:00 -0700 Subject: [PATCH 13/20] Fix issue with mkdir within docker build --- buildfile.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/buildfile.go b/buildfile.go index 805ebb4fbb..b4c96e36d5 100644 --- a/buildfile.go +++ b/buildfile.go @@ -221,15 +221,15 @@ func (b *buildFile) CmdAdd(args string) error { origPath := path.Join(b.context, orig) destPath := path.Join(container.RootfsPath(), dest) - if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil { - return err - } - fi, err := os.Stat(origPath) if err != nil { return err } if fi.IsDir() { + if err := os.MkdirAll(destPath, 0700); err != nil { + return err + } + files, err := ioutil.ReadDir(path.Join(b.context, orig)) if err != nil { return err @@ -240,6 +240,9 @@ func (b *buildFile) CmdAdd(args string) error { } } } else { + if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil { + return err + } if err := utils.CopyDirectory(origPath, destPath); err != nil { return err } From 97247c5c733db355f7261ddad5223a00c33c635e Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 29 May 2013 21:57:36 -0700 Subject: [PATCH 14/20] 'docker build': remove INSERT command (should add support for remote sources in ADD instead) --- buildfile.go | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/buildfile.go b/buildfile.go index b4c96e36d5..a4ef2de0a9 100644 --- a/buildfile.go +++ b/buildfile.go @@ -152,45 +152,6 @@ func (b *buildFile) CmdExpose(args string) error { return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports)) } -func (b *buildFile) CmdInsert(args string) error { - if b.image == "" { - return fmt.Errorf("Please provide a source image with `from` prior to insert") - } - tmp := strings.SplitN(args, " ", 2) - if len(tmp) != 2 { - return fmt.Errorf("Invalid INSERT format") - } - sourceUrl := strings.Trim(tmp[0], " ") - destPath := strings.Trim(tmp[1], " ") - - file, err := utils.Download(sourceUrl, b.out) - if err != nil { - return err - } - defer file.Body.Close() - - cmd := b.config.Cmd - b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) INSERT %s in %s", sourceUrl, destPath)} - cid, err := b.run() - if err != nil { - return err - } - - container := b.runtime.Get(cid) - if container == nil { - return fmt.Errorf("An error occured while creating the container") - } - - if err := container.Inject(file.Body, destPath); err != nil { - return err - } - if err := b.commit(cid, cmd, fmt.Sprintf("INSERT %s in %s", sourceUrl, destPath)); err != nil { - return err - } - b.config.Cmd = cmd - return nil -} - func (b *buildFile) CmdAdd(args string) error { if b.context == "" { return fmt.Errorf("No context given. Impossible to use ADD") From 56431d3130f8a48cfb708509cbe39682f7fe841c Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 30 May 2013 12:08:21 -0700 Subject: [PATCH 15/20] Add -t to docker build in order to tag resulting image --- api.go | 11 ++++++++++- commands.go | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index 3c440d7331..621d9c82e1 100644 --- a/api.go +++ b/api.go @@ -650,6 +650,13 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ if err := r.ParseMultipartForm(4096); err != nil { return err } + remote := r.FormValue("t") + tag := "" + if strings.Contains(remote, ":") { + remoteParts := strings.Split(remote, ":") + tag = remoteParts[1] + remote = remoteParts[0] + } dockerfile, _, err := r.FormFile("Dockerfile") if err != nil { @@ -664,8 +671,10 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } b := NewBuildFile(srv, utils.NewWriteFlusher(w)) - if _, err := b.Build(dockerfile, context); err != nil { + if id, err := b.Build(dockerfile, context); err != nil { fmt.Fprintf(w, "Error build: %s\n", err) + } else if remote != "" { + srv.runtime.repositories.Set(remote, tag, id, false) } return nil } diff --git a/commands.go b/commands.go index 67b8e917f5..c24a48f5a0 100644 --- a/commands.go +++ b/commands.go @@ -132,6 +132,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error { func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "PATH | -", "Build a new container image from the source code at PATH") + tag := cmd.String("t", "", "Tag to be applied to the resulting image") if err := cmd.Parse(args); err != nil { return nil } @@ -191,8 +192,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error { } multipartBody = io.MultiReader(multipartBody, boundary) + v := &url.Values{} + v.Set("t", *tag) // Send the multipart request with correct content-type - req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), multipartBody) + req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody) if err != nil { return err } From a4e6025cc1103d1ad9c86fa5d75f832526d2a7b8 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 30 May 2013 12:10:54 -0700 Subject: [PATCH 16/20] Deprecate INSERT and COPY --- buildfile.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/buildfile.go b/buildfile.go index a4ef2de0a9..cf8f3e8e56 100644 --- a/buildfile.go +++ b/buildfile.go @@ -152,6 +152,14 @@ func (b *buildFile) CmdExpose(args string) error { return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports)) } +func (b *buildFile) CmdInsert(args string) error { + return fmt.Errorf("INSERT has been deprecated. Please use ADD instead") +} + +func (b *buildFile) CmdCopy(args string) error { + return fmt.Errorf("COPY has been deprecated. Please use ADD instead") +} + func (b *buildFile) CmdAdd(args string) error { if b.context == "" { return fmt.Errorf("No context given. Impossible to use ADD") From 6d2e3d2ec0dedc6c1429e87800e9f3d7e4ada095 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 30 May 2013 12:21:57 -0700 Subject: [PATCH 17/20] Fix issue with CMD instruction within docker build --- buildfile.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/buildfile.go b/buildfile.go index cf8f3e8e56..3081367719 100644 --- a/buildfile.go +++ b/buildfile.go @@ -143,7 +143,11 @@ func (b *buildFile) CmdCmd(args string) error { utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err) cmd = []string{"/bin/sh", "-c", args} } - return b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)) + if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil { + return err + } + b.config.Cmd = cmd + return nil } func (b *buildFile) CmdExpose(args string) error { @@ -216,7 +220,11 @@ func (b *buildFile) CmdAdd(args string) error { return err } } - return b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)) + if err := b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil { + return err + } + b.config.Cmd = cmd + return nil } func (b *buildFile) run() (string, error) { From 28d5b2c15acc6e088a0abd2099a00ba1180beb3e Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 30 May 2013 12:35:19 -0700 Subject: [PATCH 18/20] Update docker build docs --- commands.go | 4 ++-- docs/sources/commandline/command/build.rst | 21 +++++---------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/commands.go b/commands.go index c24a48f5a0..14491d31a1 100644 --- a/commands.go +++ b/commands.go @@ -131,8 +131,8 @@ func (cli *DockerCli) CmdInsert(args ...string) error { } func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := Subcmd("build", "PATH | -", "Build a new container image from the source code at PATH") - tag := cmd.String("t", "", "Tag to be applied to the resulting image") + cmd := Subcmd("build", "[OPTIONS] PATH | -", "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") if err := cmd.Parse(args); err != nil { return nil } diff --git a/docs/sources/commandline/command/build.rst b/docs/sources/commandline/command/build.rst index f7df8cbbc3..81120b22d2 100644 --- a/docs/sources/commandline/command/build.rst +++ b/docs/sources/commandline/command/build.rst @@ -8,32 +8,21 @@ :: - Usage: docker build [CONTEXT|-] - Build a new image from a Dockerfile + Usage: docker build [OPTIONS] PATH | - + Build a new container image from the source code at PATH + -t="": Tag to be applied to the resulting image in case of success. Examples -------- .. code-block:: bash - docker build + docker build . -This will take the local Dockerfile without context +This will take the local Dockerfile .. code-block:: bash docker build - This will read a Dockerfile form Stdin without context - -.. code-block:: bash - - docker build . - -This will take the local Dockerfile and set the current directory as context - -.. code-block:: bash - - docker build - . - -This will read a Dockerfile from Stdin and set the current directory as context From c903a6baf83b4aaa51d7198643e0eef9211c99ed Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 30 May 2013 15:52:09 -0700 Subject: [PATCH 19/20] * Builder: keep temporary images after a build fails, to allow caching --- buildfile.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/buildfile.go b/buildfile.go index 3081367719..81aea513db 100644 --- a/buildfile.go +++ b/buildfile.go @@ -347,10 +347,6 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { fmt.Fprintf(b.out, "===> %v\n", b.image) } if b.image != "" { - // The build is successful, keep the temporary containers and images - for i := range b.tmpImages { - delete(b.tmpImages, i) - } fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image) return b.image, nil } From caaea2e08f85438b7a6bee006e803f1c4351c445 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 30 May 2013 16:24:26 -0700 Subject: [PATCH 20/20] * Build: never remove temporary images and containers --- buildfile.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/buildfile.go b/buildfile.go index 81aea513db..d9a4b5eff6 100644 --- a/buildfile.go +++ b/buildfile.go @@ -298,8 +298,6 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { } func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { - defer b.clearTmp(b.tmpContainers, b.tmpImages) - if context != nil { name, err := ioutil.TempDir("/tmp", "docker-build") if err != nil { @@ -350,9 +348,6 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) { fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image) return b.image, nil } - for i := range b.tmpContainers { - delete(b.tmpContainers, i) - } return "", fmt.Errorf("An error occured during the build\n") }