Browse code

Moving runtime.Create to builder.Create

Guillaume J. Charmes authored on 2013/04/25 07:14:10
Showing 3 changed files
... ...
@@ -4,38 +4,80 @@ import (
4 4
 	"bufio"
5 5
 	"fmt"
6 6
 	"io"
7
+	"os"
8
+	"path"
7 9
 	"strings"
10
+	"time"
8 11
 )
9 12
 
10 13
 type Builder struct {
11
-	runtime *Runtime
14
+	runtime      *Runtime
15
+	repositories *TagStore
12 16
 }
13 17
 
14 18
 func NewBuilder(runtime *Runtime) *Builder {
15 19
 	return &Builder{
16
-		runtime: runtime,
20
+		runtime:      runtime,
21
+		repositories: runtime.repositories,
17 22
 	}
18 23
 }
19 24
 
20
-func (builder *Builder) Run(image *Image, cmd ...string) (*Container, error) {
21
-	// FIXME: pass a NopWriter instead of nil
22
-	config, err := ParseRun(append([]string{"-d", image.Id}, cmd...), nil, builder.runtime.capabilities)
23
-	if config.Image == "" {
24
-		return nil, fmt.Errorf("Image not specified")
25
+func (builder *Builder) Create(config *Config) (*Container, error) {
26
+	// Lookup image
27
+	img, err := builder.repositories.LookupImage(config.Image)
28
+	if err != nil {
29
+		return nil, err
25 30
 	}
26
-	if len(config.Cmd) == 0 {
27
-		return nil, fmt.Errorf("Command not specified")
31
+	// Generate id
32
+	id := GenerateId()
33
+	// Generate default hostname
34
+	// FIXME: the lxc template no longer needs to set a default hostname
35
+	if config.Hostname == "" {
36
+		config.Hostname = id[:12]
28 37
 	}
29
-	if config.Tty {
30
-		return nil, fmt.Errorf("The tty mode is not supported within the builder")
38
+
39
+	container := &Container{
40
+		// FIXME: we should generate the ID here instead of receiving it as an argument
41
+		Id:              id,
42
+		Created:         time.Now(),
43
+		Path:            config.Cmd[0],
44
+		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
45
+		Config:          config,
46
+		Image:           img.Id, // Always use the resolved image id
47
+		NetworkSettings: &NetworkSettings{},
48
+		// FIXME: do we need to store this in the container?
49
+		SysInitPath: sysInitPath,
50
+	}
51
+	container.root = builder.runtime.containerRoot(container.Id)
52
+	// Step 1: create the container directory.
53
+	// This doubles as a barrier to avoid race conditions.
54
+	if err := os.Mkdir(container.root, 0700); err != nil {
55
+		return nil, err
31 56
 	}
32 57
 
33
-	// Create new container
34
-	container, err := builder.runtime.Create(config)
35
-	if err != nil {
58
+	// If custom dns exists, then create a resolv.conf for the container
59
+	if len(config.Dns) > 0 {
60
+		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
61
+		f, err := os.Create(container.ResolvConfPath)
62
+		if err != nil {
63
+			return nil, err
64
+		}
65
+		defer f.Close()
66
+		for _, dns := range config.Dns {
67
+			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
68
+				return nil, err
69
+			}
70
+		}
71
+	} else {
72
+		container.ResolvConfPath = "/etc/resolv.conf"
73
+	}
74
+
75
+	// Step 2: save the container json
76
+	if err := container.ToDisk(); err != nil {
36 77
 		return nil, err
37 78
 	}
38
-	if err := container.Start(); err != nil {
79
+	// Step 3: register the container
80
+	if err := builder.runtime.Register(container); err != nil {
39 81
 		return nil, err
40 82
 	}
41 83
 	return container, nil
... ...
@@ -96,12 +138,19 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
96 96
 			if image == nil {
97 97
 				return fmt.Errorf("Please provide a source image with `from` prior to run")
98 98
 			}
99
+			config, err := ParseRun([]string{image.Id, "/bin/sh", "-c", tmp[1]}, nil, builder.runtime.capabilities)
100
+			if err != nil {
101
+				return err
102
+			}
99 103
 
100 104
 			// Create the container and start it
101
-			c, err := builder.Run(image, "/bin/sh", "-c", tmp[1])
105
+			c, err := builder.Create(config)
102 106
 			if err != nil {
103 107
 				return err
104 108
 			}
109
+			if err := c.Start(); err != nil {
110
+				return err
111
+			}
105 112
 			tmpContainers[c.Id] = struct{}{}
106 113
 
107 114
 			// Wait for it to finish
... ...
@@ -134,11 +183,24 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
134 134
 			}
135 135
 			defer file.Body.Close()
136 136
 
137
-			c, err := builder.Run(base, "echo", "insert", tmp2[0], tmp2[1])
137
+			config, err := ParseRun([]string{base.Id, "echo", "insert", tmp2[0], tmp2[1]}, nil, builder.runtime.capabilities)
138
+			if err != nil {
139
+				return err
140
+			}
141
+			c, err := builder.Create(config)
138 142
 			if err != nil {
139 143
 				return err
140 144
 			}
141 145
 
146
+			if err := c.Start(); err != nil {
147
+				return err
148
+			}
149
+
150
+			// Wait for echo to finish
151
+			if result := c.Wait(); result != 0 {
152
+				return fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", tmp[1], result)
153
+			}
154
+
142 155
 			if err := c.Inject(file.Body, tmp2[1]); err != nil {
143 156
 				return err
144 157
 			}
... ...
@@ -90,8 +90,13 @@ func (srv *Server) CmdInsert(stdin io.ReadCloser, stdout rcli.DockerConn, args .
90 90
 	}
91 91
 	defer file.Body.Close()
92 92
 
93
+	config, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, nil, srv.runtime.capabilities)
94
+	if err != nil {
95
+		return err
96
+	}
97
+
93 98
 	b := NewBuilder(srv.runtime)
94
-	c, err := b.Run(img, "echo", "insert", url, path)
99
+	c, err := b.Create(config)
95 100
 	if err != nil {
96 101
 		return err
97 102
 	}
... ...
@@ -1008,8 +1013,10 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
1008 1008
 	// or tell the client there is no options
1009 1009
 	stdout.Flush()
1010 1010
 
1011
+	b := NewBuilder(srv.runtime)
1012
+
1011 1013
 	// Create new container
1012
-	container, err := srv.runtime.Create(config)
1014
+	container, err := b.Create(config)
1013 1015
 	if err != nil {
1014 1016
 		// If container not found, try to pull it
1015 1017
 		if srv.runtime.graph.IsNotExist(err) {
... ...
@@ -1017,7 +1024,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
1017 1017
 			if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
1018 1018
 				return err
1019 1019
 			}
1020
-			if container, err = srv.runtime.Create(config); err != nil {
1020
+			if container, err = b.Create(config); err != nil {
1021 1021
 				return err
1022 1022
 			}
1023 1023
 		} else {
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	"path"
13 13
 	"sort"
14 14
 	"strings"
15
-	"time"
16 15
 )
17 16
 
18 17
 type Capabilities struct {
... ...
@@ -77,67 +76,6 @@ func (runtime *Runtime) containerRoot(id string) string {
77 77
 	return path.Join(runtime.repository, id)
78 78
 }
79 79
 
80
-func (runtime *Runtime) Create(config *Config) (*Container, error) {
81
-	// Lookup image
82
-	img, err := runtime.repositories.LookupImage(config.Image)
83
-	if err != nil {
84
-		return nil, err
85
-	}
86
-	// Generate id
87
-	id := GenerateId()
88
-	// Generate default hostname
89
-	// FIXME: the lxc template no longer needs to set a default hostname
90
-	if config.Hostname == "" {
91
-		config.Hostname = id[:12]
92
-	}
93
-
94
-	container := &Container{
95
-		// FIXME: we should generate the ID here instead of receiving it as an argument
96
-		Id:              id,
97
-		Created:         time.Now(),
98
-		Path:            config.Cmd[0],
99
-		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
100
-		Config:          config,
101
-		Image:           img.Id, // Always use the resolved image id
102
-		NetworkSettings: &NetworkSettings{},
103
-		// FIXME: do we need to store this in the container?
104
-		SysInitPath: sysInitPath,
105
-	}
106
-	container.root = runtime.containerRoot(container.Id)
107
-	// Step 1: create the container directory.
108
-	// This doubles as a barrier to avoid race conditions.
109
-	if err := os.Mkdir(container.root, 0700); err != nil {
110
-		return nil, err
111
-	}
112
-
113
-	// If custom dns exists, then create a resolv.conf for the container
114
-	if len(config.Dns) > 0 {
115
-		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
116
-		f, err := os.Create(container.ResolvConfPath)
117
-		if err != nil {
118
-			return nil, err
119
-		}
120
-		defer f.Close()
121
-		for _, dns := range config.Dns {
122
-			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
123
-				return nil, err
124
-			}
125
-		}
126
-	} else {
127
-		container.ResolvConfPath = "/etc/resolv.conf"
128
-	}
129
-
130
-	// Step 2: save the container json
131
-	if err := container.ToDisk(); err != nil {
132
-		return nil, err
133
-	}
134
-	// Step 3: register the container
135
-	if err := runtime.Register(container); err != nil {
136
-		return nil, err
137
-	}
138
-	return container, nil
139
-}
140
-
141 80
 func (runtime *Runtime) Load(id string) (*Container, error) {
142 81
 	container := &Container{root: runtime.containerRoot(id)}
143 82
 	if err := container.FromDisk(); err != nil {
... ...
@@ -314,13 +252,13 @@ func NewRuntime() (*Runtime, error) {
314 314
 		_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
315 315
 		runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
316 316
 		if !runtime.capabilities.MemoryLimit {
317
-		   	log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
317
+			log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
318 318
 		}
319 319
 
320 320
 		_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
321 321
 		runtime.capabilities.SwapLimit = err == nil
322 322
 		if !runtime.capabilities.SwapLimit {
323
-		   	log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
323
+			log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
324 324
 		}
325 325
 	}
326 326
 	return runtime, nil