Jelajahi Sumber

Moving runtime.Create to builder.Create

Guillaume J. Charmes 12 tahun lalu
induk
melakukan
f911ccc27b
1 mengubah file dengan 125 tambahan dan 18 penghapusan
  1. 125 18
      builder.go

+ 125 - 18
builder.go

@@ -4,38 +4,80 @@ import (
 	"bufio"
 	"fmt"
 	"io"
+	"os"
+	"path"
 	"strings"
+	"time"
 )
 
 type Builder struct {
-	runtime *Runtime
+	runtime      *Runtime
+	repositories *TagStore
 }
 
 func NewBuilder(runtime *Runtime) *Builder {
 	return &Builder{
-		runtime: runtime,
+		runtime:      runtime,
+		repositories: runtime.repositories,
 	}
 }
 
-func (builder *Builder) run(image *Image, cmd string) (*Container, error) {
-	// FIXME: pass a NopWriter instead of nil
-	config, err := ParseRun([]string{"-d", image.Id, "/bin/sh", "-c", cmd}, nil, builder.runtime.capabilities)
-	if config.Image == "" {
-		return nil, fmt.Errorf("Image not specified")
+func (builder *Builder) Create(config *Config) (*Container, error) {
+	// Lookup image
+	img, err := builder.repositories.LookupImage(config.Image)
+	if err != nil {
+		return nil, err
 	}
-	if len(config.Cmd) == 0 {
-		return nil, fmt.Errorf("Command not specified")
+	// Generate id
+	id := GenerateId()
+	// Generate default hostname
+	// FIXME: the lxc template no longer needs to set a default hostname
+	if config.Hostname == "" {
+		config.Hostname = id[:12]
 	}
-	if config.Tty {
-		return nil, fmt.Errorf("The tty mode is not supported within the builder")
+
+	container := &Container{
+		// FIXME: we should generate the ID here instead of receiving it as an argument
+		Id:              id,
+		Created:         time.Now(),
+		Path:            config.Cmd[0],
+		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
+		Config:          config,
+		Image:           img.Id, // Always use the resolved image id
+		NetworkSettings: &NetworkSettings{},
+		// FIXME: do we need to store this in the container?
+		SysInitPath: sysInitPath,
+	}
+	container.root = builder.runtime.containerRoot(container.Id)
+	// Step 1: create the container directory.
+	// This doubles as a barrier to avoid race conditions.
+	if err := os.Mkdir(container.root, 0700); err != nil {
+		return nil, err
 	}
 
-	// Create new container
-	container, err := builder.runtime.Create(config)
-	if err != nil {
+	// If custom dns exists, then create a resolv.conf for the container
+	if len(config.Dns) > 0 {
+		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
+		f, err := os.Create(container.ResolvConfPath)
+		if err != nil {
+			return nil, err
+		}
+		defer f.Close()
+		for _, dns := range config.Dns {
+			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
+				return nil, err
+			}
+		}
+	} else {
+		container.ResolvConfPath = "/etc/resolv.conf"
+	}
+
+	// Step 2: save the container json
+	if err := container.ToDisk(); err != nil {
 		return nil, err
 	}
-	if err := container.Start(); err != nil {
+	// Step 3: register the container
+	if err := builder.runtime.Register(container); err != nil {
 		return nil, err
 	}
 	return container, nil
@@ -110,14 +152,79 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
 			if image == nil {
 				return fmt.Errorf("Please provide a source image with `from` prior to run")
 			}
-			base, err = builder.runCommit(image, tmp[1])
+			config, err := ParseRun([]string{image.Id, "/bin/sh", "-c", tmp[1]}, nil, builder.runtime.capabilities)
 			if err != nil {
 				return err
 			}
-			fmt.Fprintf(stdout, "===> %s\n", base.Id)
+
+			// Create the container and start it
+			c, err := builder.Create(config)
+			if err != nil {
+				return err
+			}
+			if err := c.Start(); err != nil {
+				return err
+			}
+			tmpContainers[c.Id] = struct{}{}
+
+			// Wait for it to finish
+			if result := c.Wait(); result != 0 {
+				return fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", tmp[1], result)
+			}
+
+			// Commit the container
+			base, err = builder.Commit(c, "", "", "", "")
+			if err != nil {
+				return err
+			}
+			tmpImages[base.Id] = struct{}{}
+
+			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
 			break
 		case "copy":
-			return fmt.Errorf("The copy operator has not yet been implemented")
+			if image == nil {
+				return fmt.Errorf("Please provide a source image with `from` prior to copy")
+			}
+			tmp2 := strings.SplitN(tmp[1], " ", 2)
+			if len(tmp) != 2 {
+				return fmt.Errorf("Invalid COPY format")
+			}
+			fmt.Fprintf(stdout, "COPY %s to %s in %s\n", tmp2[0], tmp2[1], base.ShortId())
+
+			file, err := Download(tmp2[0], stdout)
+			if err != nil {
+				return err
+			}
+			defer file.Body.Close()
+
+			config, err := ParseRun([]string{base.Id, "echo", "insert", tmp2[0], tmp2[1]}, nil, builder.runtime.capabilities)
+			if err != nil {
+				return err
+			}
+			c, err := builder.Create(config)
+			if err != nil {
+				return err
+			}
+
+			if err := c.Start(); err != nil {
+				return err
+			}
+
+			// Wait for echo to finish
+			if result := c.Wait(); result != 0 {
+				return fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", tmp[1], result)
+			}
+
+			if err := c.Inject(file.Body, tmp2[1]); err != nil {
+				return err
+			}
+
+			base, err = builder.Commit(c, "", "", "", "")
+			if err != nil {
+				return err
+			}
+			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
+			break
 		default:
 			fmt.Fprintf(stdout, "Skipping unknown op %s\n", tmp[0])
 		}