Browse Source

Implement Context within docker build (not yet in use)

Guillaume J. Charmes 12 years ago
parent
commit
d42c10aa09
3 changed files with 57 additions and 34 deletions
  1. 7 16
      api.go
  2. 1 1
      builder_client.go
  3. 49 17
      commands.go

+ 7 - 16
api.go

@@ -1,7 +1,6 @@
 package docker
 
 import (
-	"bytes"
 	"encoding/json"
 	"fmt"
 	"github.com/dotcloud/docker/auth"
@@ -631,32 +630,24 @@ func Upload(w http.ResponseWriter, req *http.Request) {
 }
 
 func postBuild(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-
-	Upload(w, r)
-
-	//	io.Copy(os.Stderr, r.Body)
-
-	if err := r.ParseMultipartForm(409699); err != nil {
-		utils.Debugf("----- %s\n", err)
+	if err := r.ParseMultipartForm(4096); err != nil {
 		return err
 	}
 
-	mpr, err := r.MultipartReader()
+	file, _, err := r.FormFile("Dockerfile")
 	if err != nil {
 		return err
 	}
 
-	p, err := mpr.NextPart()
+	context, _, err := r.FormFile("Context")
 	if err != nil {
-		return err
+		if err != http.ErrMissingFile {
+			return err
+		}
 	}
 
-	dockerfile := make([]byte, 4096)
-	p.Read(dockerfile)
-
-	utils.Debugf("Dockerfile >>>%s<<<\n", dockerfile)
 	b := NewBuildFile(srv, w)
-	if _, err := b.Build(bytes.NewReader(dockerfile)); err != nil {
+	if _, err := b.Build(file, context); err != nil {
 		return err
 	}
 	return nil

+ 1 - 1
builder_client.go

@@ -255,7 +255,7 @@ func (b *builderClient) commit(id string) error {
 	return nil
 }
 
-func (b *builderClient) Build(dockerfile io.Reader) (string, error) {
+func (b *builderClient) Build(dockerfile, context io.Reader) (string, error) {
 	defer b.clearTmp(b.tmpContainers, b.tmpImages)
 	file := bufio.NewReader(dockerfile)
 	for {

+ 49 - 17
commands.go

@@ -57,7 +57,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
 	for cmd, description := range map[string]string{
 		"attach":  "Attach to a running container",
-		"build":   "Build a container from Dockerfile or via stdin",
+		"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",
 		"export":  "Stream the contents of a container as a tar archive",
@@ -112,36 +112,67 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
 }
 
 func (cli *DockerCli) CmdBuild(args ...string) error {
+	cmd := Subcmd("build", "[OPTIONS]", "Build an image from a Dockerfile")
+	fileName := cmd.String("f", "Dockerfile", "Use file as Dockerfile. Can be '-' for stdin")
+	contextPath := cmd.String("c", "", "Use the specified directory as context for the build")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
 
-	buff := bytes.NewBuffer([]byte{})
+	var (
+		file          io.ReadCloser
+		multipartBody io.Reader
+		err           error
+	)
 
+	// Init the needed component for the Multipart
+	buff := bytes.NewBuffer([]byte{})
+	multipartBody = buff
 	w := multipart.NewWriter(buff)
+	boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n")
 
-	dockerfile, err := w.CreateFormFile("Dockerfile", "Dockerfile")
-	if err != nil {
-		return err
+	// 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()
 	}
-	file, err := os.Open("Dockerfile")
-	if err != nil {
+	if _, err := w.CreateFormFile("Dockerfile", *fileName); err != nil {
 		return err
 	}
-	dockerfile.Write([]byte(w.Boundary() + "\r\n"))
-	if _, err := io.Copy(dockerfile, file); err != nil {
+	multipartBody = io.MultiReader(multipartBody, file)
+
+	// Create a FormFile multipart for the context if needed
+	if *contextPath != "" {
+		// FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage?
+		context, err := Tar(*contextPath, Bzip2)
+		if err != nil {
+			return err
+		}
+		if _, err := w.CreateFormFile("Context", *contextPath+".tar.bz2"); err != nil {
+			return err
+		}
+		multipartBody = io.MultiReader(multipartBody, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, "Uploading Context %v/%v (%v)"))
+	}
+
+	// Send the multipart request with correct content-type
+	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), io.MultiReader(multipartBody, boundary))
+	if err != nil {
 		return err
 	}
-	dockerfile.Write([]byte("\r\n" + w.Boundary()))
+	req.Header.Set("Content-Type", w.FormDataContentType())
 
-	// req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), buff)
-	// if err != nil {
-	// 	return err
-	// }
-	// req.Header.Set("Content-Type", w.FormDataContentType())
-	resp, err := http.Post(fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, "/build"), w.FormDataContentType(), buff)
+	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 		return err
 	}
 	defer resp.Body.Close()
 
+	// Check for errors
 	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
 		body, err := ioutil.ReadAll(resp.Body)
 		if err != nil {
@@ -150,6 +181,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		return fmt.Errorf("error: %s", body)
 	}
 
+	// Output the result
 	if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
 		return err
 	}
@@ -180,7 +212,7 @@ func (cli *DockerCli) CmdBuildClient(args ...string) error {
 			return err
 		}
 	}
-	if _, err := NewBuilderClient("0.0.0.0", 4243).Build(file); err != nil {
+	if _, err := NewBuilderClient("0.0.0.0", 4243).Build(file, nil); err != nil {
 		return err
 	}
 	return nil