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