Browse code

Add build command

Guillaume J. Charmes authored on 2013/04/25 03:03:01
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,111 @@
0
+package docker
1
+
2
+import (
3
+	"bufio"
4
+	"fmt"
5
+	"io"
6
+	"strings"
7
+)
8
+
9
+type Builder struct {
10
+	runtime *Runtime
11
+}
12
+
13
+func NewBuilder(runtime *Runtime) *Builder {
14
+	return &Builder{
15
+		runtime: runtime,
16
+	}
17
+}
18
+
19
+func (builder *Builder) run(image *Image, cmd string) (*Container, error) {
20
+	// FIXME: pass a NopWriter instead of nil
21
+	config, err := ParseRun([]string{"-d", image.Id, "/bin/sh", "-c", cmd}, nil, builder.runtime.capabilities)
22
+	if config.Image == "" {
23
+		return nil, fmt.Errorf("Image not specified")
24
+	}
25
+	if len(config.Cmd) == 0 {
26
+		return nil, fmt.Errorf("Command not specified")
27
+	}
28
+	if config.Tty {
29
+		return nil, fmt.Errorf("The tty mode is not supported within the builder")
30
+	}
31
+
32
+	// Create new container
33
+	container, err := builder.runtime.Create(config)
34
+	if err != nil {
35
+		return nil, err
36
+	}
37
+	if err := container.Start(); err != nil {
38
+		return nil, err
39
+	}
40
+	return container, nil
41
+}
42
+
43
+func (builder *Builder) runCommit(image *Image, cmd string) (*Image, error) {
44
+	c, err := builder.run(image, cmd)
45
+	if err != nil {
46
+		return nil, err
47
+	}
48
+	if result := c.Wait(); result != 0 {
49
+		return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", cmd, result)
50
+	}
51
+	img, err := builder.runtime.Commit(c.Id, "", "", "", "")
52
+	if err != nil {
53
+		return nil, err
54
+	}
55
+	return img, nil
56
+}
57
+
58
+func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
59
+	var image, base *Image
60
+
61
+	file := bufio.NewReader(dockerfile)
62
+	for {
63
+		line, err := file.ReadString('\n')
64
+		if err != nil {
65
+			if err == io.EOF {
66
+				break
67
+			}
68
+			return err
69
+		}
70
+		line = strings.TrimSpace(line)
71
+		// Skip comments and empty line
72
+		if len(line) == 0 || line[0] == '#' {
73
+			continue
74
+		}
75
+		tmp := strings.SplitN(line, "	", 2)
76
+		if len(tmp) != 2 {
77
+			return fmt.Errorf("Invalid Dockerfile format")
78
+		}
79
+		switch tmp[0] {
80
+		case "from":
81
+			fmt.Fprintf(stdout, "FROM %s\n", tmp[1])
82
+			image, err = builder.runtime.repositories.LookupImage(tmp[1])
83
+			if err != nil {
84
+				return err
85
+			}
86
+			break
87
+		case "run":
88
+			fmt.Fprintf(stdout, "RUN %s\n", tmp[1])
89
+			if image == nil {
90
+				return fmt.Errorf("Please provide a source image with `from` prior to run")
91
+			}
92
+			base, err = builder.runCommit(image, tmp[1])
93
+			if err != nil {
94
+				return err
95
+			}
96
+			fmt.Fprintf(stdout, "===> %s\n", base.Id)
97
+			break
98
+		case "copy":
99
+			return fmt.Errorf("The copy operator has not yet been implemented")
100
+		default:
101
+			fmt.Fprintf(stdout, "Skipping unknown op %s\n", tmp[0])
102
+		}
103
+	}
104
+	if base != nil {
105
+		fmt.Fprintf(stdout, "Build finished. image id: %s\n", base.Id)
106
+	} else {
107
+		fmt.Fprintf(stdout, "An error occured during the build\n")
108
+	}
109
+	return nil
110
+}
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"log"
11 11
 	"net/http"
12 12
 	"net/url"
13
+	"os"
13 14
 	"runtime"
14 15
 	"strconv"
15 16
 	"strings"
... ...
@@ -33,6 +34,7 @@ func (srv *Server) Help() string {
33 33
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
34 34
 	for _, cmd := range [][]string{
35 35
 		{"attach", "Attach to a running container"},
36
+		{"build", "Build a container from Dockerfile"},
36 37
 		{"commit", "Create a new image from a container's changes"},
37 38
 		{"diff", "Inspect changes on a container's filesystem"},
38 39
 		{"export", "Stream the contents of a container as a tar archive"},
... ...
@@ -63,6 +65,32 @@ func (srv *Server) Help() string {
63 63
 	return help
64 64
 }
65 65
 
66
+func (srv *Server) CmdBuild(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
67
+	stdout.Flush()
68
+	cmd := rcli.Subcmd(stdout, "build", "[Dockerfile|-]", "Build a container from Dockerfile")
69
+	if err := cmd.Parse(args); err != nil {
70
+		return nil
71
+	}
72
+	dockerfile := cmd.Arg(0)
73
+	if dockerfile == "" {
74
+		dockerfile = "Dockerfile"
75
+	}
76
+
77
+	var file io.Reader
78
+
79
+	if dockerfile != "-" {
80
+		f, err := os.Open(dockerfile)
81
+		if err != nil {
82
+			return err
83
+		}
84
+		defer f.Close()
85
+		file = f
86
+	} else {
87
+		file = stdin
88
+	}
89
+	return NewBuilder(srv.runtime).Build(file, stdout)
90
+}
91
+
66 92
 // 'docker login': login / register a user to registry service.
67 93
 func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
68 94
 	// Read a line on raw terminal with support for simple backspace