Ver Fonte

Start moving the docker builder into the server

Guillaume J. Charmes há 12 anos atrás
pai
commit
0f135ad7f3
4 ficheiros alterados com 157 adições e 24 exclusões
  1. 79 1
      api.go
  2. 22 13
      builder_client.go
  3. 48 2
      commands.go
  4. 8 8
      server.go

+ 79 - 1
api.go

@@ -1,6 +1,7 @@
 package docker
 package docker
 
 
 import (
 import (
+	"bytes"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/auth"
 	"github.com/dotcloud/docker/auth"
@@ -9,6 +10,7 @@ import (
 	"io"
 	"io"
 	"log"
 	"log"
 	"net/http"
 	"net/http"
+	"os"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
@@ -31,6 +33,13 @@ func parseForm(r *http.Request) error {
 	return nil
 	return nil
 }
 }
 
 
+func parseMultipartForm(r *http.Request) error {
+	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
+		return err
+	}
+	return nil
+}
+
 func httpError(w http.ResponseWriter, err error) {
 func httpError(w http.ResponseWriter, err error) {
 	if strings.HasPrefix(err.Error(), "No such") {
 	if strings.HasPrefix(err.Error(), "No such") {
 		http.Error(w, err.Error(), http.StatusNotFound)
 		http.Error(w, err.Error(), http.StatusNotFound)
@@ -329,9 +338,15 @@ func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars
 	}
 	}
 	name := vars["name"]
 	name := vars["name"]
 
 
-	if err := srv.ImageInsert(name, url, path, w); err != nil {
+	imgId, err := srv.ImageInsert(name, url, path, w)
+	if err != nil {
 		return err
 		return err
 	}
 	}
+	b, err := json.Marshal(&ApiId{Id: imgId})
+	if err != nil {
+		return err
+	}
+	writeJson(w, b)
 	return nil
 	return nil
 }
 }
 
 
@@ -585,6 +600,68 @@ func postImagesGetCache(srv *Server, w http.ResponseWriter, r *http.Request, var
 	return nil
 	return nil
 }
 }
 
 
+func Upload(w http.ResponseWriter, req *http.Request) {
+
+	mr, err := req.MultipartReader()
+	if err != nil {
+		return
+	}
+	length := req.ContentLength
+	for {
+
+		part, err := mr.NextPart()
+		if err == io.EOF {
+			break
+		}
+		var read int64
+		var p float32
+		for {
+			buffer := make([]byte, 100000)
+			cBytes, err := part.Read(buffer)
+			if err == io.EOF {
+				break
+			}
+			read = read + int64(cBytes)
+			//fmt.Printf("read: %v \n",read )
+			p = float32(read) / float32(length) * 100
+			fmt.Printf("progress: %v \n", p)
+			os.Stdout.Write(buffer)
+		}
+	}
+}
+
+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)
+		return err
+	}
+
+	mpr, err := r.MultipartReader()
+	if err != nil {
+		return err
+	}
+
+	p, err := mpr.NextPart()
+	if err != nil {
+		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 {
+		return err
+	}
+	return nil
+}
+
 func ListenAndServe(addr string, srv *Server, logging bool) error {
 func ListenAndServe(addr string, srv *Server, logging bool) error {
 	r := mux.NewRouter()
 	r := mux.NewRouter()
 	log.Printf("Listening for HTTP on %s\n", addr)
 	log.Printf("Listening for HTTP on %s\n", addr)
@@ -607,6 +684,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
 		"POST": {
 		"POST": {
 			"/auth":                         postAuth,
 			"/auth":                         postAuth,
 			"/commit":                       postCommit,
 			"/commit":                       postCommit,
+			"/build":                        postBuild,
 			"/images/create":                postImagesCreate,
 			"/images/create":                postImagesCreate,
 			"/images/{name:.*}/insert":      postImagesInsert,
 			"/images/{name:.*}/insert":      postImagesInsert,
 			"/images/{name:.*}/push":        postImagesPush,
 			"/images/{name:.*}/push":        postImagesPush,

+ 22 - 13
builder_client.go

@@ -12,12 +12,6 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-type BuilderClient interface {
-	Build(io.Reader) (string, error)
-	CmdFrom(string) error
-	CmdRun(string) error
-}
-
 type builderClient struct {
 type builderClient struct {
 	cli *DockerCli
 	cli *DockerCli
 
 
@@ -164,8 +158,23 @@ func (b *builderClient) CmdExpose(args string) error {
 }
 }
 
 
 func (b *builderClient) CmdInsert(args string) error {
 func (b *builderClient) CmdInsert(args string) error {
-	// FIXME: Reimplement this once the remove_hijack branch gets merged.
-	// We need to retrieve the resulting Id
+	// tmp := strings.SplitN(args, "\t ", 2)
+	// sourceUrl, destPath := tmp[0], tmp[1]
+
+	// v := url.Values{}
+	// v.Set("url", sourceUrl)
+	// v.Set("path", destPath)
+	// body, _, err := b.cli.call("POST", "/images/insert?"+v.Encode(), nil)
+	// if err != nil {
+	// 	return err
+	// }
+
+	// apiId := &ApiId{}
+	// if err := json.Unmarshal(body, apiId); err != nil {
+	// 	return err
+	// }
+
+	// FIXME: Reimplement this, we need to retrieve the resulting Id
 	return fmt.Errorf("INSERT not implemented")
 	return fmt.Errorf("INSERT not implemented")
 }
 }
 
 
@@ -269,18 +278,18 @@ func (b *builderClient) Build(dockerfile io.Reader) (string, error) {
 		instruction := strings.ToLower(strings.Trim(tmp[0], " "))
 		instruction := strings.ToLower(strings.Trim(tmp[0], " "))
 		arguments := strings.Trim(tmp[1], " ")
 		arguments := strings.Trim(tmp[1], " ")
 
 
-		fmt.Printf("%s %s (%s)\n", strings.ToUpper(instruction), arguments, b.image)
+		fmt.Fprintf(os.Stderr, "%s %s (%s)\n", strings.ToUpper(instruction), arguments, b.image)
 
 
 		method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
 		method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
 		if !exists {
 		if !exists {
-			fmt.Printf("Skipping unknown instruction %s\n", strings.ToUpper(instruction))
+			fmt.Fprintf(os.Stderr, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
 		}
 		}
 		ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
 		ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
 		if ret != nil {
 		if ret != nil {
 			return "", ret.(error)
 			return "", ret.(error)
 		}
 		}
 
 
-		fmt.Printf("===> %v\n", b.image)
+		fmt.Fprintf(os.Stderr, "===> %v\n", b.image)
 	}
 	}
 	if b.needCommit {
 	if b.needCommit {
 		if err := b.commit(""); err != nil {
 		if err := b.commit(""); err != nil {
@@ -295,13 +304,13 @@ func (b *builderClient) Build(dockerfile io.Reader) (string, error) {
 		for i := range b.tmpContainers {
 		for i := range b.tmpContainers {
 			delete(b.tmpContainers, i)
 			delete(b.tmpContainers, i)
 		}
 		}
-		fmt.Printf("Build finished. image id: %s\n", b.image)
+		fmt.Fprintf(os.Stderr, "Build finished. image id: %s\n", b.image)
 		return b.image, nil
 		return b.image, nil
 	}
 	}
 	return "", fmt.Errorf("An error occured during the build\n")
 	return "", fmt.Errorf("An error occured during the build\n")
 }
 }
 
 
-func NewBuilderClient(addr string, port int) BuilderClient {
+func NewBuilderClient(addr string, port int) BuildFile {
 	return &builderClient{
 	return &builderClient{
 		cli:           NewDockerCli(addr, port),
 		cli:           NewDockerCli(addr, port),
 		config:        &Config{},
 		config:        &Config{},

+ 48 - 2
commands.go

@@ -10,6 +10,7 @@ import (
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
+	"mime/multipart"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"net/http/httputil"
 	"net/http/httputil"
@@ -104,14 +105,59 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
 	v.Set("url", cmd.Arg(1))
 	v.Set("url", cmd.Arg(1))
 	v.Set("path", cmd.Arg(2))
 	v.Set("path", cmd.Arg(2))
 
 
-	err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, os.Stdout)
-	if err != nil {
+	if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, os.Stdout); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 func (cli *DockerCli) CmdBuild(args ...string) error {
 func (cli *DockerCli) CmdBuild(args ...string) error {
+
+	buff := bytes.NewBuffer([]byte{})
+
+	w := multipart.NewWriter(buff)
+
+	dockerfile, err := w.CreateFormFile("Dockerfile", "Dockerfile")
+	if err != nil {
+		return err
+	}
+	file, err := os.Open("Dockerfile")
+	if err != nil {
+		return err
+	}
+	dockerfile.Write([]byte(w.Boundary() + "\r\n"))
+	if _, err := io.Copy(dockerfile, file); err != nil {
+		return err
+	}
+	dockerfile.Write([]byte("\r\n" + w.Boundary()))
+
+	// 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)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		return fmt.Errorf("error: %s", body)
+	}
+
+	if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (cli *DockerCli) CmdBuildClient(args ...string) error {
 	cmd := Subcmd("build", "-|Dockerfile", "Build an image from Dockerfile or via stdin")
 	cmd := Subcmd("build", "-|Dockerfile", "Build an image from Dockerfile or via stdin")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil

+ 8 - 8
server.go

@@ -67,40 +67,40 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
 	return outs, nil
 	return outs, nil
 }
 }
 
 
-func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error {
+func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, error) {
 	out = utils.NewWriteFlusher(out)
 	out = utils.NewWriteFlusher(out)
 	img, err := srv.runtime.repositories.LookupImage(name)
 	img, err := srv.runtime.repositories.LookupImage(name)
 	if err != nil {
 	if err != nil {
-		return err
+		return "", err
 	}
 	}
 
 
 	file, err := utils.Download(url, out)
 	file, err := utils.Download(url, out)
 	if err != nil {
 	if err != nil {
-		return err
+		return "", err
 	}
 	}
 	defer file.Body.Close()
 	defer file.Body.Close()
 
 
 	config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities)
 	config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities)
 	if err != nil {
 	if err != nil {
-		return err
+		return "", err
 	}
 	}
 
 
 	b := NewBuilder(srv.runtime)
 	b := NewBuilder(srv.runtime)
 	c, err := b.Create(config)
 	c, err := b.Create(config)
 	if err != nil {
 	if err != nil {
-		return err
+		return "", err
 	}
 	}
 
 
 	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil {
 	if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil {
-		return err
+		return "", err
 	}
 	}
 	// FIXME: Handle custom repo, tag comment, author
 	// FIXME: Handle custom repo, tag comment, author
 	img, err = b.Commit(c, "", "", img.Comment, img.Author, nil)
 	img, err = b.Commit(c, "", "", img.Comment, img.Author, nil)
 	if err != nil {
 	if err != nil {
-		return err
+		return "", err
 	}
 	}
 	fmt.Fprintf(out, "%s\n", img.Id)
 	fmt.Fprintf(out, "%s\n", img.Id)
-	return nil
+	return img.ShortId(), nil
 }
 }
 
 
 func (srv *Server) ImagesViz(out io.Writer) error {
 func (srv *Server) ImagesViz(out io.Writer) error {