| 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 |