| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/dotcloud/docker/auth" |
| 11 | 11 |
"github.com/dotcloud/docker/registry" |
| 12 | 12 |
"github.com/dotcloud/docker/runconfig" |
| 13 |
+ "github.com/dotcloud/docker/runtime" |
|
| 13 | 14 |
"github.com/dotcloud/docker/utils" |
| 14 | 15 |
"io" |
| 15 | 16 |
"io/ioutil" |
| ... | ... |
@@ -34,7 +35,7 @@ type BuildFile interface {
|
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 | 36 |
type buildFile struct {
|
| 37 |
- runtime *Runtime |
|
| 37 |
+ runtime *runtime.Runtime |
|
| 38 | 38 |
srv *Server |
| 39 | 39 |
|
| 40 | 40 |
image string |
| ... | ... |
@@ -74,9 +75,9 @@ func (b *buildFile) clearTmp(containers map[string]struct{}) {
|
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 | 76 |
func (b *buildFile) CmdFrom(name string) error {
|
| 77 |
- image, err := b.runtime.repositories.LookupImage(name) |
|
| 77 |
+ image, err := b.runtime.Repositories().LookupImage(name) |
|
| 78 | 78 |
if err != nil {
|
| 79 |
- if b.runtime.graph.IsNotExist(err) {
|
|
| 79 |
+ if b.runtime.Graph().IsNotExist(err) {
|
|
| 80 | 80 |
remote, tag := utils.ParseRepositoryTag(name) |
| 81 | 81 |
pullRegistryAuth := b.authConfig |
| 82 | 82 |
if len(b.configFile.Configs) > 0 {
|
| ... | ... |
@@ -96,7 +97,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
| 96 | 96 |
if err := job.Run(); err != nil {
|
| 97 | 97 |
return err |
| 98 | 98 |
} |
| 99 |
- image, err = b.runtime.repositories.LookupImage(name) |
|
| 99 |
+ image, err = b.runtime.Repositories().LookupImage(name) |
|
| 100 | 100 |
if err != nil {
|
| 101 | 101 |
return err |
| 102 | 102 |
} |
| ... | ... |
@@ -110,7 +111,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
| 110 | 110 |
b.config = image.Config |
| 111 | 111 |
} |
| 112 | 112 |
if b.config.Env == nil || len(b.config.Env) == 0 {
|
| 113 |
- b.config.Env = append(b.config.Env, "HOME=/", "PATH="+defaultPathEnv) |
|
| 113 |
+ b.config.Env = append(b.config.Env, "HOME=/", "PATH="+runtime.DefaultPathEnv) |
|
| 114 | 114 |
} |
| 115 | 115 |
// Process ONBUILD triggers if they exist |
| 116 | 116 |
if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
|
| ... | ... |
@@ -371,7 +372,7 @@ func (b *buildFile) checkPathForAddition(orig string) error {
|
| 371 | 371 |
return nil |
| 372 | 372 |
} |
| 373 | 373 |
|
| 374 |
-func (b *buildFile) addContext(container *Container, orig, dest string, remote bool) error {
|
|
| 374 |
+func (b *buildFile) addContext(container *runtime.Container, orig, dest string, remote bool) error {
|
|
| 375 | 375 |
var ( |
| 376 | 376 |
origPath = path.Join(b.contextPath, orig) |
| 377 | 377 |
destPath = path.Join(container.BasefsPath(), dest) |
| ... | ... |
@@ -604,7 +605,7 @@ func (sf *StderrFormater) Write(buf []byte) (int, error) {
|
| 604 | 604 |
return len(buf), err |
| 605 | 605 |
} |
| 606 | 606 |
|
| 607 |
-func (b *buildFile) create() (*Container, error) {
|
|
| 607 |
+func (b *buildFile) create() (*runtime.Container, error) {
|
|
| 608 | 608 |
if b.image == "" {
|
| 609 | 609 |
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
| 610 | 610 |
} |
| ... | ... |
@@ -625,7 +626,7 @@ func (b *buildFile) create() (*Container, error) {
|
| 625 | 625 |
return c, nil |
| 626 | 626 |
} |
| 627 | 627 |
|
| 628 |
-func (b *buildFile) run(c *Container) error {
|
|
| 628 |
+func (b *buildFile) run(c *runtime.Container) error {
|
|
| 629 | 629 |
var errCh chan error |
| 630 | 630 |
|
| 631 | 631 |
if b.verbose {
|
| 632 | 632 |
deleted file mode 100644 |
| ... | ... |
@@ -1,1143 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "errors" |
|
| 6 |
- "fmt" |
|
| 7 |
- "github.com/dotcloud/docker/archive" |
|
| 8 |
- "github.com/dotcloud/docker/engine" |
|
| 9 |
- "github.com/dotcloud/docker/execdriver" |
|
| 10 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 11 |
- "github.com/dotcloud/docker/image" |
|
| 12 |
- "github.com/dotcloud/docker/links" |
|
| 13 |
- "github.com/dotcloud/docker/nat" |
|
| 14 |
- "github.com/dotcloud/docker/runconfig" |
|
| 15 |
- "github.com/dotcloud/docker/utils" |
|
| 16 |
- "io" |
|
| 17 |
- "io/ioutil" |
|
| 18 |
- "log" |
|
| 19 |
- "os" |
|
| 20 |
- "path" |
|
| 21 |
- "strings" |
|
| 22 |
- "sync" |
|
| 23 |
- "syscall" |
|
| 24 |
- "time" |
|
| 25 |
-) |
|
| 26 |
- |
|
| 27 |
-const defaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" |
|
| 28 |
- |
|
| 29 |
-var ( |
|
| 30 |
- ErrNotATTY = errors.New("The PTY is not a file")
|
|
| 31 |
- ErrNoTTY = errors.New("No PTY found")
|
|
| 32 |
- ErrContainerStart = errors.New("The container failed to start. Unknown error")
|
|
| 33 |
- ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
|
|
| 34 |
-) |
|
| 35 |
- |
|
| 36 |
-type Container struct {
|
|
| 37 |
- sync.Mutex |
|
| 38 |
- root string // Path to the "home" of the container, including metadata. |
|
| 39 |
- basefs string // Path to the graphdriver mountpoint |
|
| 40 |
- |
|
| 41 |
- ID string |
|
| 42 |
- |
|
| 43 |
- Created time.Time |
|
| 44 |
- |
|
| 45 |
- Path string |
|
| 46 |
- Args []string |
|
| 47 |
- |
|
| 48 |
- Config *runconfig.Config |
|
| 49 |
- State State |
|
| 50 |
- Image string |
|
| 51 |
- |
|
| 52 |
- NetworkSettings *NetworkSettings |
|
| 53 |
- |
|
| 54 |
- ResolvConfPath string |
|
| 55 |
- HostnamePath string |
|
| 56 |
- HostsPath string |
|
| 57 |
- Name string |
|
| 58 |
- Driver string |
|
| 59 |
- ExecDriver string |
|
| 60 |
- |
|
| 61 |
- command *execdriver.Command |
|
| 62 |
- stdout *utils.WriteBroadcaster |
|
| 63 |
- stderr *utils.WriteBroadcaster |
|
| 64 |
- stdin io.ReadCloser |
|
| 65 |
- stdinPipe io.WriteCloser |
|
| 66 |
- |
|
| 67 |
- runtime *Runtime |
|
| 68 |
- |
|
| 69 |
- waitLock chan struct{}
|
|
| 70 |
- Volumes map[string]string |
|
| 71 |
- // Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. |
|
| 72 |
- // Easier than migrating older container configs :) |
|
| 73 |
- VolumesRW map[string]bool |
|
| 74 |
- hostConfig *runconfig.HostConfig |
|
| 75 |
- |
|
| 76 |
- activeLinks map[string]*links.Link |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-// FIXME: move deprecated port stuff to nat to clean up the core. |
|
| 80 |
-type PortMapping map[string]string // Deprecated |
|
| 81 |
- |
|
| 82 |
-type NetworkSettings struct {
|
|
| 83 |
- IPAddress string |
|
| 84 |
- IPPrefixLen int |
|
| 85 |
- Gateway string |
|
| 86 |
- Bridge string |
|
| 87 |
- PortMapping map[string]PortMapping // Deprecated |
|
| 88 |
- Ports nat.PortMap |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
|
| 92 |
- var outs = engine.NewTable("", 0)
|
|
| 93 |
- for port, bindings := range settings.Ports {
|
|
| 94 |
- p, _ := nat.ParsePort(port.Port()) |
|
| 95 |
- if len(bindings) == 0 {
|
|
| 96 |
- out := &engine.Env{}
|
|
| 97 |
- out.SetInt("PublicPort", p)
|
|
| 98 |
- out.Set("Type", port.Proto())
|
|
| 99 |
- outs.Add(out) |
|
| 100 |
- continue |
|
| 101 |
- } |
|
| 102 |
- for _, binding := range bindings {
|
|
| 103 |
- out := &engine.Env{}
|
|
| 104 |
- h, _ := nat.ParsePort(binding.HostPort) |
|
| 105 |
- out.SetInt("PrivatePort", p)
|
|
| 106 |
- out.SetInt("PublicPort", h)
|
|
| 107 |
- out.Set("Type", port.Proto())
|
|
| 108 |
- out.Set("IP", binding.HostIp)
|
|
| 109 |
- outs.Add(out) |
|
| 110 |
- } |
|
| 111 |
- } |
|
| 112 |
- return outs |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-// Inject the io.Reader at the given path. Note: do not close the reader |
|
| 116 |
-func (container *Container) Inject(file io.Reader, pth string) error {
|
|
| 117 |
- if err := container.Mount(); err != nil {
|
|
| 118 |
- return fmt.Errorf("inject: error mounting container %s: %s", container.ID, err)
|
|
| 119 |
- } |
|
| 120 |
- defer container.Unmount() |
|
| 121 |
- |
|
| 122 |
- // Return error if path exists |
|
| 123 |
- destPath := path.Join(container.basefs, pth) |
|
| 124 |
- if _, err := os.Stat(destPath); err == nil {
|
|
| 125 |
- // Since err is nil, the path could be stat'd and it exists |
|
| 126 |
- return fmt.Errorf("%s exists", pth)
|
|
| 127 |
- } else if !os.IsNotExist(err) {
|
|
| 128 |
- // Expect err might be that the file doesn't exist, so |
|
| 129 |
- // if it's some other error, return that. |
|
| 130 |
- |
|
| 131 |
- return err |
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- // Make sure the directory exists |
|
| 135 |
- if err := os.MkdirAll(path.Join(container.basefs, path.Dir(pth)), 0755); err != nil {
|
|
| 136 |
- return err |
|
| 137 |
- } |
|
| 138 |
- |
|
| 139 |
- dest, err := os.Create(destPath) |
|
| 140 |
- if err != nil {
|
|
| 141 |
- return err |
|
| 142 |
- } |
|
| 143 |
- defer dest.Close() |
|
| 144 |
- |
|
| 145 |
- if _, err := io.Copy(dest, file); err != nil {
|
|
| 146 |
- return err |
|
| 147 |
- } |
|
| 148 |
- return nil |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-func (container *Container) When() time.Time {
|
|
| 152 |
- return container.Created |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-func (container *Container) FromDisk() error {
|
|
| 156 |
- data, err := ioutil.ReadFile(container.jsonPath()) |
|
| 157 |
- if err != nil {
|
|
| 158 |
- return err |
|
| 159 |
- } |
|
| 160 |
- // Load container settings |
|
| 161 |
- // udp broke compat of docker.PortMapping, but it's not used when loading a container, we can skip it |
|
| 162 |
- if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
|
|
| 163 |
- return err |
|
| 164 |
- } |
|
| 165 |
- return container.readHostConfig() |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-func (container *Container) ToDisk() (err error) {
|
|
| 169 |
- data, err := json.Marshal(container) |
|
| 170 |
- if err != nil {
|
|
| 171 |
- return |
|
| 172 |
- } |
|
| 173 |
- err = ioutil.WriteFile(container.jsonPath(), data, 0666) |
|
| 174 |
- if err != nil {
|
|
| 175 |
- return |
|
| 176 |
- } |
|
| 177 |
- return container.writeHostConfig() |
|
| 178 |
-} |
|
| 179 |
- |
|
| 180 |
-func (container *Container) readHostConfig() error {
|
|
| 181 |
- container.hostConfig = &runconfig.HostConfig{}
|
|
| 182 |
- // If the hostconfig file does not exist, do not read it. |
|
| 183 |
- // (We still have to initialize container.hostConfig, |
|
| 184 |
- // but that's OK, since we just did that above.) |
|
| 185 |
- _, err := os.Stat(container.hostConfigPath()) |
|
| 186 |
- if os.IsNotExist(err) {
|
|
| 187 |
- return nil |
|
| 188 |
- } |
|
| 189 |
- data, err := ioutil.ReadFile(container.hostConfigPath()) |
|
| 190 |
- if err != nil {
|
|
| 191 |
- return err |
|
| 192 |
- } |
|
| 193 |
- return json.Unmarshal(data, container.hostConfig) |
|
| 194 |
-} |
|
| 195 |
- |
|
| 196 |
-func (container *Container) writeHostConfig() (err error) {
|
|
| 197 |
- data, err := json.Marshal(container.hostConfig) |
|
| 198 |
- if err != nil {
|
|
| 199 |
- return |
|
| 200 |
- } |
|
| 201 |
- return ioutil.WriteFile(container.hostConfigPath(), data, 0666) |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 |
-func (container *Container) generateEnvConfig(env []string) error {
|
|
| 205 |
- data, err := json.Marshal(env) |
|
| 206 |
- if err != nil {
|
|
| 207 |
- return err |
|
| 208 |
- } |
|
| 209 |
- p, err := container.EnvConfigPath() |
|
| 210 |
- if err != nil {
|
|
| 211 |
- return err |
|
| 212 |
- } |
|
| 213 |
- ioutil.WriteFile(p, data, 0600) |
|
| 214 |
- return nil |
|
| 215 |
-} |
|
| 216 |
- |
|
| 217 |
-func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
|
| 218 |
- var cStdout, cStderr io.ReadCloser |
|
| 219 |
- |
|
| 220 |
- var nJobs int |
|
| 221 |
- errors := make(chan error, 3) |
|
| 222 |
- if stdin != nil && container.Config.OpenStdin {
|
|
| 223 |
- nJobs += 1 |
|
| 224 |
- if cStdin, err := container.StdinPipe(); err != nil {
|
|
| 225 |
- errors <- err |
|
| 226 |
- } else {
|
|
| 227 |
- go func() {
|
|
| 228 |
- utils.Debugf("attach: stdin: begin")
|
|
| 229 |
- defer utils.Debugf("attach: stdin: end")
|
|
| 230 |
- // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr |
|
| 231 |
- if container.Config.StdinOnce && !container.Config.Tty {
|
|
| 232 |
- defer cStdin.Close() |
|
| 233 |
- } else {
|
|
| 234 |
- defer func() {
|
|
| 235 |
- if cStdout != nil {
|
|
| 236 |
- cStdout.Close() |
|
| 237 |
- } |
|
| 238 |
- if cStderr != nil {
|
|
| 239 |
- cStderr.Close() |
|
| 240 |
- } |
|
| 241 |
- }() |
|
| 242 |
- } |
|
| 243 |
- if container.Config.Tty {
|
|
| 244 |
- _, err = utils.CopyEscapable(cStdin, stdin) |
|
| 245 |
- } else {
|
|
| 246 |
- _, err = io.Copy(cStdin, stdin) |
|
| 247 |
- } |
|
| 248 |
- if err == io.ErrClosedPipe {
|
|
| 249 |
- err = nil |
|
| 250 |
- } |
|
| 251 |
- if err != nil {
|
|
| 252 |
- utils.Errorf("attach: stdin: %s", err)
|
|
| 253 |
- } |
|
| 254 |
- errors <- err |
|
| 255 |
- }() |
|
| 256 |
- } |
|
| 257 |
- } |
|
| 258 |
- if stdout != nil {
|
|
| 259 |
- nJobs += 1 |
|
| 260 |
- if p, err := container.StdoutPipe(); err != nil {
|
|
| 261 |
- errors <- err |
|
| 262 |
- } else {
|
|
| 263 |
- cStdout = p |
|
| 264 |
- go func() {
|
|
| 265 |
- utils.Debugf("attach: stdout: begin")
|
|
| 266 |
- defer utils.Debugf("attach: stdout: end")
|
|
| 267 |
- // If we are in StdinOnce mode, then close stdin |
|
| 268 |
- if container.Config.StdinOnce && stdin != nil {
|
|
| 269 |
- defer stdin.Close() |
|
| 270 |
- } |
|
| 271 |
- if stdinCloser != nil {
|
|
| 272 |
- defer stdinCloser.Close() |
|
| 273 |
- } |
|
| 274 |
- _, err := io.Copy(stdout, cStdout) |
|
| 275 |
- if err == io.ErrClosedPipe {
|
|
| 276 |
- err = nil |
|
| 277 |
- } |
|
| 278 |
- if err != nil {
|
|
| 279 |
- utils.Errorf("attach: stdout: %s", err)
|
|
| 280 |
- } |
|
| 281 |
- errors <- err |
|
| 282 |
- }() |
|
| 283 |
- } |
|
| 284 |
- } else {
|
|
| 285 |
- go func() {
|
|
| 286 |
- if stdinCloser != nil {
|
|
| 287 |
- defer stdinCloser.Close() |
|
| 288 |
- } |
|
| 289 |
- if cStdout, err := container.StdoutPipe(); err != nil {
|
|
| 290 |
- utils.Errorf("attach: stdout pipe: %s", err)
|
|
| 291 |
- } else {
|
|
| 292 |
- io.Copy(&utils.NopWriter{}, cStdout)
|
|
| 293 |
- } |
|
| 294 |
- }() |
|
| 295 |
- } |
|
| 296 |
- if stderr != nil {
|
|
| 297 |
- nJobs += 1 |
|
| 298 |
- if p, err := container.StderrPipe(); err != nil {
|
|
| 299 |
- errors <- err |
|
| 300 |
- } else {
|
|
| 301 |
- cStderr = p |
|
| 302 |
- go func() {
|
|
| 303 |
- utils.Debugf("attach: stderr: begin")
|
|
| 304 |
- defer utils.Debugf("attach: stderr: end")
|
|
| 305 |
- // If we are in StdinOnce mode, then close stdin |
|
| 306 |
- if container.Config.StdinOnce && stdin != nil {
|
|
| 307 |
- defer stdin.Close() |
|
| 308 |
- } |
|
| 309 |
- if stdinCloser != nil {
|
|
| 310 |
- defer stdinCloser.Close() |
|
| 311 |
- } |
|
| 312 |
- _, err := io.Copy(stderr, cStderr) |
|
| 313 |
- if err == io.ErrClosedPipe {
|
|
| 314 |
- err = nil |
|
| 315 |
- } |
|
| 316 |
- if err != nil {
|
|
| 317 |
- utils.Errorf("attach: stderr: %s", err)
|
|
| 318 |
- } |
|
| 319 |
- errors <- err |
|
| 320 |
- }() |
|
| 321 |
- } |
|
| 322 |
- } else {
|
|
| 323 |
- go func() {
|
|
| 324 |
- if stdinCloser != nil {
|
|
| 325 |
- defer stdinCloser.Close() |
|
| 326 |
- } |
|
| 327 |
- |
|
| 328 |
- if cStderr, err := container.StderrPipe(); err != nil {
|
|
| 329 |
- utils.Errorf("attach: stdout pipe: %s", err)
|
|
| 330 |
- } else {
|
|
| 331 |
- io.Copy(&utils.NopWriter{}, cStderr)
|
|
| 332 |
- } |
|
| 333 |
- }() |
|
| 334 |
- } |
|
| 335 |
- |
|
| 336 |
- return utils.Go(func() error {
|
|
| 337 |
- defer func() {
|
|
| 338 |
- if cStdout != nil {
|
|
| 339 |
- cStdout.Close() |
|
| 340 |
- } |
|
| 341 |
- if cStderr != nil {
|
|
| 342 |
- cStderr.Close() |
|
| 343 |
- } |
|
| 344 |
- }() |
|
| 345 |
- |
|
| 346 |
- // FIXME: how to clean up the stdin goroutine without the unwanted side effect |
|
| 347 |
- // of closing the passed stdin? Add an intermediary io.Pipe? |
|
| 348 |
- for i := 0; i < nJobs; i += 1 {
|
|
| 349 |
- utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
|
|
| 350 |
- if err := <-errors; err != nil {
|
|
| 351 |
- utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
|
|
| 352 |
- return err |
|
| 353 |
- } |
|
| 354 |
- utils.Debugf("attach: job %d completed successfully", i+1)
|
|
| 355 |
- } |
|
| 356 |
- utils.Debugf("attach: all jobs completed successfully")
|
|
| 357 |
- return nil |
|
| 358 |
- }) |
|
| 359 |
-} |
|
| 360 |
- |
|
| 361 |
-func populateCommand(c *Container) {
|
|
| 362 |
- var ( |
|
| 363 |
- en *execdriver.Network |
|
| 364 |
- driverConfig []string |
|
| 365 |
- ) |
|
| 366 |
- |
|
| 367 |
- if !c.Config.NetworkDisabled {
|
|
| 368 |
- network := c.NetworkSettings |
|
| 369 |
- en = &execdriver.Network{
|
|
| 370 |
- Gateway: network.Gateway, |
|
| 371 |
- Bridge: network.Bridge, |
|
| 372 |
- IPAddress: network.IPAddress, |
|
| 373 |
- IPPrefixLen: network.IPPrefixLen, |
|
| 374 |
- Mtu: c.runtime.config.Mtu, |
|
| 375 |
- } |
|
| 376 |
- } |
|
| 377 |
- |
|
| 378 |
- if lxcConf := c.hostConfig.LxcConf; lxcConf != nil {
|
|
| 379 |
- for _, pair := range lxcConf {
|
|
| 380 |
- driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
|
|
| 381 |
- } |
|
| 382 |
- } |
|
| 383 |
- resources := &execdriver.Resources{
|
|
| 384 |
- Memory: c.Config.Memory, |
|
| 385 |
- MemorySwap: c.Config.MemorySwap, |
|
| 386 |
- CpuShares: c.Config.CpuShares, |
|
| 387 |
- } |
|
| 388 |
- c.command = &execdriver.Command{
|
|
| 389 |
- ID: c.ID, |
|
| 390 |
- Privileged: c.hostConfig.Privileged, |
|
| 391 |
- Rootfs: c.RootfsPath(), |
|
| 392 |
- InitPath: "/.dockerinit", |
|
| 393 |
- Entrypoint: c.Path, |
|
| 394 |
- Arguments: c.Args, |
|
| 395 |
- WorkingDir: c.Config.WorkingDir, |
|
| 396 |
- Network: en, |
|
| 397 |
- Tty: c.Config.Tty, |
|
| 398 |
- User: c.Config.User, |
|
| 399 |
- Config: driverConfig, |
|
| 400 |
- Resources: resources, |
|
| 401 |
- } |
|
| 402 |
- c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
|
| 403 |
-} |
|
| 404 |
- |
|
| 405 |
-func (container *Container) Start() (err error) {
|
|
| 406 |
- container.Lock() |
|
| 407 |
- defer container.Unlock() |
|
| 408 |
- |
|
| 409 |
- if container.State.IsRunning() {
|
|
| 410 |
- return fmt.Errorf("The container %s is already running.", container.ID)
|
|
| 411 |
- } |
|
| 412 |
- |
|
| 413 |
- defer func() {
|
|
| 414 |
- if err != nil {
|
|
| 415 |
- container.cleanup() |
|
| 416 |
- } |
|
| 417 |
- }() |
|
| 418 |
- |
|
| 419 |
- if err := container.Mount(); err != nil {
|
|
| 420 |
- return err |
|
| 421 |
- } |
|
| 422 |
- |
|
| 423 |
- if container.runtime.config.DisableNetwork {
|
|
| 424 |
- container.Config.NetworkDisabled = true |
|
| 425 |
- container.buildHostnameAndHostsFiles("127.0.1.1")
|
|
| 426 |
- } else {
|
|
| 427 |
- if err := container.allocateNetwork(); err != nil {
|
|
| 428 |
- return err |
|
| 429 |
- } |
|
| 430 |
- container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) |
|
| 431 |
- } |
|
| 432 |
- |
|
| 433 |
- // Make sure the config is compatible with the current kernel |
|
| 434 |
- if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit {
|
|
| 435 |
- log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
|
|
| 436 |
- container.Config.Memory = 0 |
|
| 437 |
- } |
|
| 438 |
- if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit {
|
|
| 439 |
- log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
|
| 440 |
- container.Config.MemorySwap = -1 |
|
| 441 |
- } |
|
| 442 |
- |
|
| 443 |
- if container.runtime.sysInfo.IPv4ForwardingDisabled {
|
|
| 444 |
- log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
|
|
| 445 |
- } |
|
| 446 |
- |
|
| 447 |
- if err := prepareVolumesForContainer(container); err != nil {
|
|
| 448 |
- return err |
|
| 449 |
- } |
|
| 450 |
- |
|
| 451 |
- // Setup environment |
|
| 452 |
- env := []string{
|
|
| 453 |
- "HOME=/", |
|
| 454 |
- "PATH=" + defaultPathEnv, |
|
| 455 |
- "HOSTNAME=" + container.Config.Hostname, |
|
| 456 |
- } |
|
| 457 |
- |
|
| 458 |
- if container.Config.Tty {
|
|
| 459 |
- env = append(env, "TERM=xterm") |
|
| 460 |
- } |
|
| 461 |
- |
|
| 462 |
- // Init any links between the parent and children |
|
| 463 |
- runtime := container.runtime |
|
| 464 |
- |
|
| 465 |
- children, err := runtime.Children(container.Name) |
|
| 466 |
- if err != nil {
|
|
| 467 |
- return err |
|
| 468 |
- } |
|
| 469 |
- |
|
| 470 |
- if len(children) > 0 {
|
|
| 471 |
- container.activeLinks = make(map[string]*links.Link, len(children)) |
|
| 472 |
- |
|
| 473 |
- // If we encounter an error make sure that we rollback any network |
|
| 474 |
- // config and ip table changes |
|
| 475 |
- rollback := func() {
|
|
| 476 |
- for _, link := range container.activeLinks {
|
|
| 477 |
- link.Disable() |
|
| 478 |
- } |
|
| 479 |
- container.activeLinks = nil |
|
| 480 |
- } |
|
| 481 |
- |
|
| 482 |
- for linkAlias, child := range children {
|
|
| 483 |
- if !child.State.IsRunning() {
|
|
| 484 |
- return fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
|
|
| 485 |
- } |
|
| 486 |
- |
|
| 487 |
- link, err := links.NewLink( |
|
| 488 |
- container.NetworkSettings.IPAddress, |
|
| 489 |
- child.NetworkSettings.IPAddress, |
|
| 490 |
- linkAlias, |
|
| 491 |
- child.Config.Env, |
|
| 492 |
- child.Config.ExposedPorts, |
|
| 493 |
- runtime.eng) |
|
| 494 |
- |
|
| 495 |
- if err != nil {
|
|
| 496 |
- rollback() |
|
| 497 |
- return err |
|
| 498 |
- } |
|
| 499 |
- |
|
| 500 |
- container.activeLinks[link.Alias()] = link |
|
| 501 |
- if err := link.Enable(); err != nil {
|
|
| 502 |
- rollback() |
|
| 503 |
- return err |
|
| 504 |
- } |
|
| 505 |
- |
|
| 506 |
- for _, envVar := range link.ToEnv() {
|
|
| 507 |
- env = append(env, envVar) |
|
| 508 |
- } |
|
| 509 |
- } |
|
| 510 |
- } |
|
| 511 |
- |
|
| 512 |
- // because the env on the container can override certain default values |
|
| 513 |
- // we need to replace the 'env' keys where they match and append anything |
|
| 514 |
- // else. |
|
| 515 |
- env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) |
|
| 516 |
- if err := container.generateEnvConfig(env); err != nil {
|
|
| 517 |
- return err |
|
| 518 |
- } |
|
| 519 |
- |
|
| 520 |
- if container.Config.WorkingDir != "" {
|
|
| 521 |
- container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) |
|
| 522 |
- if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil {
|
|
| 523 |
- return nil |
|
| 524 |
- } |
|
| 525 |
- } |
|
| 526 |
- |
|
| 527 |
- envPath, err := container.EnvConfigPath() |
|
| 528 |
- if err != nil {
|
|
| 529 |
- return err |
|
| 530 |
- } |
|
| 531 |
- |
|
| 532 |
- if err := mountVolumesForContainer(container, envPath); err != nil {
|
|
| 533 |
- return err |
|
| 534 |
- } |
|
| 535 |
- |
|
| 536 |
- populateCommand(container) |
|
| 537 |
- container.command.Env = env |
|
| 538 |
- |
|
| 539 |
- // Setup logging of stdout and stderr to disk |
|
| 540 |
- if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
|
| 541 |
- return err |
|
| 542 |
- } |
|
| 543 |
- if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
|
|
| 544 |
- return err |
|
| 545 |
- } |
|
| 546 |
- container.waitLock = make(chan struct{})
|
|
| 547 |
- |
|
| 548 |
- callbackLock := make(chan struct{})
|
|
| 549 |
- callback := func(command *execdriver.Command) {
|
|
| 550 |
- container.State.SetRunning(command.Pid()) |
|
| 551 |
- if command.Tty {
|
|
| 552 |
- // The callback is called after the process Start() |
|
| 553 |
- // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace |
|
| 554 |
- // which we close here. |
|
| 555 |
- if c, ok := command.Stdout.(io.Closer); ok {
|
|
| 556 |
- c.Close() |
|
| 557 |
- } |
|
| 558 |
- } |
|
| 559 |
- if err := container.ToDisk(); err != nil {
|
|
| 560 |
- utils.Debugf("%s", err)
|
|
| 561 |
- } |
|
| 562 |
- close(callbackLock) |
|
| 563 |
- } |
|
| 564 |
- |
|
| 565 |
- // We use a callback here instead of a goroutine and an chan for |
|
| 566 |
- // syncronization purposes |
|
| 567 |
- cErr := utils.Go(func() error { return container.monitor(callback) })
|
|
| 568 |
- |
|
| 569 |
- // Start should not return until the process is actually running |
|
| 570 |
- select {
|
|
| 571 |
- case <-callbackLock: |
|
| 572 |
- case err := <-cErr: |
|
| 573 |
- return err |
|
| 574 |
- } |
|
| 575 |
- return nil |
|
| 576 |
-} |
|
| 577 |
- |
|
| 578 |
-func (container *Container) Run() error {
|
|
| 579 |
- if err := container.Start(); err != nil {
|
|
| 580 |
- return err |
|
| 581 |
- } |
|
| 582 |
- container.Wait() |
|
| 583 |
- return nil |
|
| 584 |
-} |
|
| 585 |
- |
|
| 586 |
-func (container *Container) Output() (output []byte, err error) {
|
|
| 587 |
- pipe, err := container.StdoutPipe() |
|
| 588 |
- if err != nil {
|
|
| 589 |
- return nil, err |
|
| 590 |
- } |
|
| 591 |
- defer pipe.Close() |
|
| 592 |
- if err := container.Start(); err != nil {
|
|
| 593 |
- return nil, err |
|
| 594 |
- } |
|
| 595 |
- output, err = ioutil.ReadAll(pipe) |
|
| 596 |
- container.Wait() |
|
| 597 |
- return output, err |
|
| 598 |
-} |
|
| 599 |
- |
|
| 600 |
-// Container.StdinPipe returns a WriteCloser which can be used to feed data |
|
| 601 |
-// to the standard input of the container's active process. |
|
| 602 |
-// Container.StdoutPipe and Container.StderrPipe each return a ReadCloser |
|
| 603 |
-// which can be used to retrieve the standard output (and error) generated |
|
| 604 |
-// by the container's active process. The output (and error) are actually |
|
| 605 |
-// copied and delivered to all StdoutPipe and StderrPipe consumers, using |
|
| 606 |
-// a kind of "broadcaster". |
|
| 607 |
- |
|
| 608 |
-func (container *Container) StdinPipe() (io.WriteCloser, error) {
|
|
| 609 |
- return container.stdinPipe, nil |
|
| 610 |
-} |
|
| 611 |
- |
|
| 612 |
-func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
|
| 613 |
- reader, writer := io.Pipe() |
|
| 614 |
- container.stdout.AddWriter(writer, "") |
|
| 615 |
- return utils.NewBufReader(reader), nil |
|
| 616 |
-} |
|
| 617 |
- |
|
| 618 |
-func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
|
| 619 |
- reader, writer := io.Pipe() |
|
| 620 |
- container.stderr.AddWriter(writer, "") |
|
| 621 |
- return utils.NewBufReader(reader), nil |
|
| 622 |
-} |
|
| 623 |
- |
|
| 624 |
-func (container *Container) buildHostnameAndHostsFiles(IP string) {
|
|
| 625 |
- container.HostnamePath = path.Join(container.root, "hostname") |
|
| 626 |
- ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) |
|
| 627 |
- |
|
| 628 |
- hostsContent := []byte(` |
|
| 629 |
-127.0.0.1 localhost |
|
| 630 |
-::1 localhost ip6-localhost ip6-loopback |
|
| 631 |
-fe00::0 ip6-localnet |
|
| 632 |
-ff00::0 ip6-mcastprefix |
|
| 633 |
-ff02::1 ip6-allnodes |
|
| 634 |
-ff02::2 ip6-allrouters |
|
| 635 |
-`) |
|
| 636 |
- |
|
| 637 |
- container.HostsPath = path.Join(container.root, "hosts") |
|
| 638 |
- |
|
| 639 |
- if container.Config.Domainname != "" {
|
|
| 640 |
- hostsContent = append([]byte(fmt.Sprintf("%s\t%s.%s %s\n", IP, container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
|
|
| 641 |
- } else if !container.Config.NetworkDisabled {
|
|
| 642 |
- hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
|
|
| 643 |
- } |
|
| 644 |
- |
|
| 645 |
- ioutil.WriteFile(container.HostsPath, hostsContent, 0644) |
|
| 646 |
-} |
|
| 647 |
- |
|
| 648 |
-func (container *Container) allocateNetwork() error {
|
|
| 649 |
- if container.Config.NetworkDisabled {
|
|
| 650 |
- return nil |
|
| 651 |
- } |
|
| 652 |
- |
|
| 653 |
- var ( |
|
| 654 |
- env *engine.Env |
|
| 655 |
- err error |
|
| 656 |
- eng = container.runtime.eng |
|
| 657 |
- ) |
|
| 658 |
- |
|
| 659 |
- if container.State.IsGhost() {
|
|
| 660 |
- if container.runtime.config.DisableNetwork {
|
|
| 661 |
- env = &engine.Env{}
|
|
| 662 |
- } else {
|
|
| 663 |
- currentIP := container.NetworkSettings.IPAddress |
|
| 664 |
- |
|
| 665 |
- job := eng.Job("allocate_interface", container.ID)
|
|
| 666 |
- if currentIP != "" {
|
|
| 667 |
- job.Setenv("RequestIP", currentIP)
|
|
| 668 |
- } |
|
| 669 |
- |
|
| 670 |
- env, err = job.Stdout.AddEnv() |
|
| 671 |
- if err != nil {
|
|
| 672 |
- return err |
|
| 673 |
- } |
|
| 674 |
- |
|
| 675 |
- if err := job.Run(); err != nil {
|
|
| 676 |
- return err |
|
| 677 |
- } |
|
| 678 |
- } |
|
| 679 |
- } else {
|
|
| 680 |
- job := eng.Job("allocate_interface", container.ID)
|
|
| 681 |
- env, err = job.Stdout.AddEnv() |
|
| 682 |
- if err != nil {
|
|
| 683 |
- return err |
|
| 684 |
- } |
|
| 685 |
- if err := job.Run(); err != nil {
|
|
| 686 |
- return err |
|
| 687 |
- } |
|
| 688 |
- } |
|
| 689 |
- |
|
| 690 |
- if container.Config.PortSpecs != nil {
|
|
| 691 |
- utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
|
|
| 692 |
- if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
|
|
| 693 |
- return err |
|
| 694 |
- } |
|
| 695 |
- container.Config.PortSpecs = nil |
|
| 696 |
- if err := container.writeHostConfig(); err != nil {
|
|
| 697 |
- return err |
|
| 698 |
- } |
|
| 699 |
- } |
|
| 700 |
- |
|
| 701 |
- var ( |
|
| 702 |
- portSpecs = make(nat.PortSet) |
|
| 703 |
- bindings = make(nat.PortMap) |
|
| 704 |
- ) |
|
| 705 |
- |
|
| 706 |
- if !container.State.IsGhost() {
|
|
| 707 |
- if container.Config.ExposedPorts != nil {
|
|
| 708 |
- portSpecs = container.Config.ExposedPorts |
|
| 709 |
- } |
|
| 710 |
- if container.hostConfig.PortBindings != nil {
|
|
| 711 |
- bindings = container.hostConfig.PortBindings |
|
| 712 |
- } |
|
| 713 |
- } else {
|
|
| 714 |
- if container.NetworkSettings.Ports != nil {
|
|
| 715 |
- for port, binding := range container.NetworkSettings.Ports {
|
|
| 716 |
- portSpecs[port] = struct{}{}
|
|
| 717 |
- bindings[port] = binding |
|
| 718 |
- } |
|
| 719 |
- } |
|
| 720 |
- } |
|
| 721 |
- |
|
| 722 |
- container.NetworkSettings.PortMapping = nil |
|
| 723 |
- |
|
| 724 |
- for port := range portSpecs {
|
|
| 725 |
- binding := bindings[port] |
|
| 726 |
- if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
|
| 727 |
- binding = append(binding, nat.PortBinding{})
|
|
| 728 |
- } |
|
| 729 |
- |
|
| 730 |
- for i := 0; i < len(binding); i++ {
|
|
| 731 |
- b := binding[i] |
|
| 732 |
- |
|
| 733 |
- portJob := eng.Job("allocate_port", container.ID)
|
|
| 734 |
- portJob.Setenv("HostIP", b.HostIp)
|
|
| 735 |
- portJob.Setenv("HostPort", b.HostPort)
|
|
| 736 |
- portJob.Setenv("Proto", port.Proto())
|
|
| 737 |
- portJob.Setenv("ContainerPort", port.Port())
|
|
| 738 |
- |
|
| 739 |
- portEnv, err := portJob.Stdout.AddEnv() |
|
| 740 |
- if err != nil {
|
|
| 741 |
- return err |
|
| 742 |
- } |
|
| 743 |
- if err := portJob.Run(); err != nil {
|
|
| 744 |
- eng.Job("release_interface", container.ID).Run()
|
|
| 745 |
- return err |
|
| 746 |
- } |
|
| 747 |
- b.HostIp = portEnv.Get("HostIP")
|
|
| 748 |
- b.HostPort = portEnv.Get("HostPort")
|
|
| 749 |
- |
|
| 750 |
- binding[i] = b |
|
| 751 |
- } |
|
| 752 |
- bindings[port] = binding |
|
| 753 |
- } |
|
| 754 |
- container.writeHostConfig() |
|
| 755 |
- |
|
| 756 |
- container.NetworkSettings.Ports = bindings |
|
| 757 |
- |
|
| 758 |
- container.NetworkSettings.Bridge = env.Get("Bridge")
|
|
| 759 |
- container.NetworkSettings.IPAddress = env.Get("IP")
|
|
| 760 |
- container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
|
|
| 761 |
- container.NetworkSettings.Gateway = env.Get("Gateway")
|
|
| 762 |
- |
|
| 763 |
- return nil |
|
| 764 |
-} |
|
| 765 |
- |
|
| 766 |
-func (container *Container) releaseNetwork() {
|
|
| 767 |
- if container.Config.NetworkDisabled {
|
|
| 768 |
- return |
|
| 769 |
- } |
|
| 770 |
- eng := container.runtime.eng |
|
| 771 |
- |
|
| 772 |
- eng.Job("release_interface", container.ID).Run()
|
|
| 773 |
- container.NetworkSettings = &NetworkSettings{}
|
|
| 774 |
-} |
|
| 775 |
- |
|
| 776 |
-func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|
| 777 |
- var ( |
|
| 778 |
- err error |
|
| 779 |
- exitCode int |
|
| 780 |
- ) |
|
| 781 |
- |
|
| 782 |
- pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin) |
|
| 783 |
- exitCode, err = container.runtime.Run(container, pipes, callback) |
|
| 784 |
- if err != nil {
|
|
| 785 |
- utils.Errorf("Error running container: %s", err)
|
|
| 786 |
- } |
|
| 787 |
- |
|
| 788 |
- if container.runtime.srv.IsRunning() {
|
|
| 789 |
- container.State.SetStopped(exitCode) |
|
| 790 |
- |
|
| 791 |
- // FIXME: there is a race condition here which causes this to fail during the unit tests. |
|
| 792 |
- // If another goroutine was waiting for Wait() to return before removing the container's root |
|
| 793 |
- // from the filesystem... At this point it may already have done so. |
|
| 794 |
- // This is because State.setStopped() has already been called, and has caused Wait() |
|
| 795 |
- // to return. |
|
| 796 |
- // FIXME: why are we serializing running state to disk in the first place? |
|
| 797 |
- //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
|
| 798 |
- if err := container.ToDisk(); err != nil {
|
|
| 799 |
- utils.Errorf("Error dumping container state to disk: %s\n", err)
|
|
| 800 |
- } |
|
| 801 |
- } |
|
| 802 |
- |
|
| 803 |
- // Cleanup |
|
| 804 |
- container.cleanup() |
|
| 805 |
- |
|
| 806 |
- // Re-create a brand new stdin pipe once the container exited |
|
| 807 |
- if container.Config.OpenStdin {
|
|
| 808 |
- container.stdin, container.stdinPipe = io.Pipe() |
|
| 809 |
- } |
|
| 810 |
- |
|
| 811 |
- if container.runtime != nil && container.runtime.srv != nil {
|
|
| 812 |
- container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
|
| 813 |
- } |
|
| 814 |
- |
|
| 815 |
- close(container.waitLock) |
|
| 816 |
- |
|
| 817 |
- return err |
|
| 818 |
-} |
|
| 819 |
- |
|
| 820 |
-func (container *Container) cleanup() {
|
|
| 821 |
- container.releaseNetwork() |
|
| 822 |
- |
|
| 823 |
- // Disable all active links |
|
| 824 |
- if container.activeLinks != nil {
|
|
| 825 |
- for _, link := range container.activeLinks {
|
|
| 826 |
- link.Disable() |
|
| 827 |
- } |
|
| 828 |
- } |
|
| 829 |
- if container.Config.OpenStdin {
|
|
| 830 |
- if err := container.stdin.Close(); err != nil {
|
|
| 831 |
- utils.Errorf("%s: Error close stdin: %s", container.ID, err)
|
|
| 832 |
- } |
|
| 833 |
- } |
|
| 834 |
- if err := container.stdout.CloseWriters(); err != nil {
|
|
| 835 |
- utils.Errorf("%s: Error close stdout: %s", container.ID, err)
|
|
| 836 |
- } |
|
| 837 |
- if err := container.stderr.CloseWriters(); err != nil {
|
|
| 838 |
- utils.Errorf("%s: Error close stderr: %s", container.ID, err)
|
|
| 839 |
- } |
|
| 840 |
- if container.command != nil && container.command.Terminal != nil {
|
|
| 841 |
- if err := container.command.Terminal.Close(); err != nil {
|
|
| 842 |
- utils.Errorf("%s: Error closing terminal: %s", container.ID, err)
|
|
| 843 |
- } |
|
| 844 |
- } |
|
| 845 |
- |
|
| 846 |
- unmountVolumesForContainer(container) |
|
| 847 |
- |
|
| 848 |
- if err := container.Unmount(); err != nil {
|
|
| 849 |
- log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
|
|
| 850 |
- } |
|
| 851 |
-} |
|
| 852 |
- |
|
| 853 |
-func (container *Container) kill(sig int) error {
|
|
| 854 |
- container.Lock() |
|
| 855 |
- defer container.Unlock() |
|
| 856 |
- |
|
| 857 |
- if !container.State.IsRunning() {
|
|
| 858 |
- return nil |
|
| 859 |
- } |
|
| 860 |
- return container.runtime.Kill(container, sig) |
|
| 861 |
-} |
|
| 862 |
- |
|
| 863 |
-func (container *Container) Kill() error {
|
|
| 864 |
- if !container.State.IsRunning() {
|
|
| 865 |
- return nil |
|
| 866 |
- } |
|
| 867 |
- |
|
| 868 |
- // 1. Send SIGKILL |
|
| 869 |
- if err := container.kill(9); err != nil {
|
|
| 870 |
- return err |
|
| 871 |
- } |
|
| 872 |
- |
|
| 873 |
- // 2. Wait for the process to die, in last resort, try to kill the process directly |
|
| 874 |
- if err := container.WaitTimeout(10 * time.Second); err != nil {
|
|
| 875 |
- if container.command == nil {
|
|
| 876 |
- return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID))
|
|
| 877 |
- } |
|
| 878 |
- log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID))
|
|
| 879 |
- if err := container.runtime.Kill(container, 9); err != nil {
|
|
| 880 |
- return err |
|
| 881 |
- } |
|
| 882 |
- } |
|
| 883 |
- |
|
| 884 |
- container.Wait() |
|
| 885 |
- return nil |
|
| 886 |
-} |
|
| 887 |
- |
|
| 888 |
-func (container *Container) Stop(seconds int) error {
|
|
| 889 |
- if !container.State.IsRunning() {
|
|
| 890 |
- return nil |
|
| 891 |
- } |
|
| 892 |
- |
|
| 893 |
- // 1. Send a SIGTERM |
|
| 894 |
- if err := container.kill(15); err != nil {
|
|
| 895 |
- utils.Debugf("Error sending kill SIGTERM: %s", err)
|
|
| 896 |
- log.Print("Failed to send SIGTERM to the process, force killing")
|
|
| 897 |
- if err := container.kill(9); err != nil {
|
|
| 898 |
- return err |
|
| 899 |
- } |
|
| 900 |
- } |
|
| 901 |
- |
|
| 902 |
- // 2. Wait for the process to exit on its own |
|
| 903 |
- if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
|
|
| 904 |
- log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
|
|
| 905 |
- // 3. If it doesn't, then send SIGKILL |
|
| 906 |
- if err := container.Kill(); err != nil {
|
|
| 907 |
- return err |
|
| 908 |
- } |
|
| 909 |
- } |
|
| 910 |
- return nil |
|
| 911 |
-} |
|
| 912 |
- |
|
| 913 |
-func (container *Container) Restart(seconds int) error {
|
|
| 914 |
- // Avoid unnecessarily unmounting and then directly mounting |
|
| 915 |
- // the container when the container stops and then starts |
|
| 916 |
- // again |
|
| 917 |
- if err := container.Mount(); err == nil {
|
|
| 918 |
- defer container.Unmount() |
|
| 919 |
- } |
|
| 920 |
- |
|
| 921 |
- if err := container.Stop(seconds); err != nil {
|
|
| 922 |
- return err |
|
| 923 |
- } |
|
| 924 |
- return container.Start() |
|
| 925 |
-} |
|
| 926 |
- |
|
| 927 |
-// Wait blocks until the container stops running, then returns its exit code. |
|
| 928 |
-func (container *Container) Wait() int {
|
|
| 929 |
- <-container.waitLock |
|
| 930 |
- return container.State.GetExitCode() |
|
| 931 |
-} |
|
| 932 |
- |
|
| 933 |
-func (container *Container) Resize(h, w int) error {
|
|
| 934 |
- return container.command.Terminal.Resize(h, w) |
|
| 935 |
-} |
|
| 936 |
- |
|
| 937 |
-func (container *Container) ExportRw() (archive.Archive, error) {
|
|
| 938 |
- if err := container.Mount(); err != nil {
|
|
| 939 |
- return nil, err |
|
| 940 |
- } |
|
| 941 |
- if container.runtime == nil {
|
|
| 942 |
- return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
|
|
| 943 |
- } |
|
| 944 |
- archive, err := container.runtime.Diff(container) |
|
| 945 |
- if err != nil {
|
|
| 946 |
- container.Unmount() |
|
| 947 |
- return nil, err |
|
| 948 |
- } |
|
| 949 |
- return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 950 |
- err := archive.Close() |
|
| 951 |
- container.Unmount() |
|
| 952 |
- return err |
|
| 953 |
- }), nil |
|
| 954 |
-} |
|
| 955 |
- |
|
| 956 |
-func (container *Container) Export() (archive.Archive, error) {
|
|
| 957 |
- if err := container.Mount(); err != nil {
|
|
| 958 |
- return nil, err |
|
| 959 |
- } |
|
| 960 |
- |
|
| 961 |
- archive, err := archive.Tar(container.basefs, archive.Uncompressed) |
|
| 962 |
- if err != nil {
|
|
| 963 |
- container.Unmount() |
|
| 964 |
- return nil, err |
|
| 965 |
- } |
|
| 966 |
- return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 967 |
- err := archive.Close() |
|
| 968 |
- container.Unmount() |
|
| 969 |
- return err |
|
| 970 |
- }), nil |
|
| 971 |
-} |
|
| 972 |
- |
|
| 973 |
-func (container *Container) WaitTimeout(timeout time.Duration) error {
|
|
| 974 |
- done := make(chan bool) |
|
| 975 |
- go func() {
|
|
| 976 |
- container.Wait() |
|
| 977 |
- done <- true |
|
| 978 |
- }() |
|
| 979 |
- |
|
| 980 |
- select {
|
|
| 981 |
- case <-time.After(timeout): |
|
| 982 |
- return fmt.Errorf("Timed Out")
|
|
| 983 |
- case <-done: |
|
| 984 |
- return nil |
|
| 985 |
- } |
|
| 986 |
-} |
|
| 987 |
- |
|
| 988 |
-func (container *Container) Mount() error {
|
|
| 989 |
- return container.runtime.Mount(container) |
|
| 990 |
-} |
|
| 991 |
- |
|
| 992 |
-func (container *Container) Changes() ([]archive.Change, error) {
|
|
| 993 |
- return container.runtime.Changes(container) |
|
| 994 |
-} |
|
| 995 |
- |
|
| 996 |
-func (container *Container) GetImage() (*image.Image, error) {
|
|
| 997 |
- if container.runtime == nil {
|
|
| 998 |
- return nil, fmt.Errorf("Can't get image of unregistered container")
|
|
| 999 |
- } |
|
| 1000 |
- return container.runtime.graph.Get(container.Image) |
|
| 1001 |
-} |
|
| 1002 |
- |
|
| 1003 |
-func (container *Container) Unmount() error {
|
|
| 1004 |
- return container.runtime.Unmount(container) |
|
| 1005 |
-} |
|
| 1006 |
- |
|
| 1007 |
-func (container *Container) logPath(name string) string {
|
|
| 1008 |
- return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
|
|
| 1009 |
-} |
|
| 1010 |
- |
|
| 1011 |
-func (container *Container) ReadLog(name string) (io.Reader, error) {
|
|
| 1012 |
- return os.Open(container.logPath(name)) |
|
| 1013 |
-} |
|
| 1014 |
- |
|
| 1015 |
-func (container *Container) hostConfigPath() string {
|
|
| 1016 |
- return path.Join(container.root, "hostconfig.json") |
|
| 1017 |
-} |
|
| 1018 |
- |
|
| 1019 |
-func (container *Container) jsonPath() string {
|
|
| 1020 |
- return path.Join(container.root, "config.json") |
|
| 1021 |
-} |
|
| 1022 |
- |
|
| 1023 |
-func (container *Container) EnvConfigPath() (string, error) {
|
|
| 1024 |
- p := path.Join(container.root, "config.env") |
|
| 1025 |
- if _, err := os.Stat(p); err != nil {
|
|
| 1026 |
- if os.IsNotExist(err) {
|
|
| 1027 |
- f, err := os.Create(p) |
|
| 1028 |
- if err != nil {
|
|
| 1029 |
- return "", err |
|
| 1030 |
- } |
|
| 1031 |
- f.Close() |
|
| 1032 |
- } else {
|
|
| 1033 |
- return "", err |
|
| 1034 |
- } |
|
| 1035 |
- } |
|
| 1036 |
- return p, nil |
|
| 1037 |
-} |
|
| 1038 |
- |
|
| 1039 |
-// This method must be exported to be used from the lxc template |
|
| 1040 |
-// This directory is only usable when the container is running |
|
| 1041 |
-func (container *Container) RootfsPath() string {
|
|
| 1042 |
- return path.Join(container.root, "root") |
|
| 1043 |
-} |
|
| 1044 |
- |
|
| 1045 |
-// This is the stand-alone version of the root fs, without any additional mounts. |
|
| 1046 |
-// This directory is usable whenever the container is mounted (and not unmounted) |
|
| 1047 |
-func (container *Container) BasefsPath() string {
|
|
| 1048 |
- return container.basefs |
|
| 1049 |
-} |
|
| 1050 |
- |
|
| 1051 |
-func validateID(id string) error {
|
|
| 1052 |
- if id == "" {
|
|
| 1053 |
- return fmt.Errorf("Invalid empty id")
|
|
| 1054 |
- } |
|
| 1055 |
- return nil |
|
| 1056 |
-} |
|
| 1057 |
- |
|
| 1058 |
-// GetSize, return real size, virtual size |
|
| 1059 |
-func (container *Container) GetSize() (int64, int64) {
|
|
| 1060 |
- var ( |
|
| 1061 |
- sizeRw, sizeRootfs int64 |
|
| 1062 |
- err error |
|
| 1063 |
- driver = container.runtime.driver |
|
| 1064 |
- ) |
|
| 1065 |
- |
|
| 1066 |
- if err := container.Mount(); err != nil {
|
|
| 1067 |
- utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
|
|
| 1068 |
- return sizeRw, sizeRootfs |
|
| 1069 |
- } |
|
| 1070 |
- defer container.Unmount() |
|
| 1071 |
- |
|
| 1072 |
- if differ, ok := container.runtime.driver.(graphdriver.Differ); ok {
|
|
| 1073 |
- sizeRw, err = differ.DiffSize(container.ID) |
|
| 1074 |
- if err != nil {
|
|
| 1075 |
- utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
|
|
| 1076 |
- // FIXME: GetSize should return an error. Not changing it now in case |
|
| 1077 |
- // there is a side-effect. |
|
| 1078 |
- sizeRw = -1 |
|
| 1079 |
- } |
|
| 1080 |
- } else {
|
|
| 1081 |
- changes, _ := container.Changes() |
|
| 1082 |
- if changes != nil {
|
|
| 1083 |
- sizeRw = archive.ChangesSize(container.basefs, changes) |
|
| 1084 |
- } else {
|
|
| 1085 |
- sizeRw = -1 |
|
| 1086 |
- } |
|
| 1087 |
- } |
|
| 1088 |
- |
|
| 1089 |
- if _, err = os.Stat(container.basefs); err != nil {
|
|
| 1090 |
- if sizeRootfs, err = utils.TreeSize(container.basefs); err != nil {
|
|
| 1091 |
- sizeRootfs = -1 |
|
| 1092 |
- } |
|
| 1093 |
- } |
|
| 1094 |
- return sizeRw, sizeRootfs |
|
| 1095 |
-} |
|
| 1096 |
- |
|
| 1097 |
-func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
|
| 1098 |
- if err := container.Mount(); err != nil {
|
|
| 1099 |
- return nil, err |
|
| 1100 |
- } |
|
| 1101 |
- var filter []string |
|
| 1102 |
- basePath := path.Join(container.basefs, resource) |
|
| 1103 |
- stat, err := os.Stat(basePath) |
|
| 1104 |
- if err != nil {
|
|
| 1105 |
- container.Unmount() |
|
| 1106 |
- return nil, err |
|
| 1107 |
- } |
|
| 1108 |
- if !stat.IsDir() {
|
|
| 1109 |
- d, f := path.Split(basePath) |
|
| 1110 |
- basePath = d |
|
| 1111 |
- filter = []string{f}
|
|
| 1112 |
- } else {
|
|
| 1113 |
- filter = []string{path.Base(basePath)}
|
|
| 1114 |
- basePath = path.Dir(basePath) |
|
| 1115 |
- } |
|
| 1116 |
- |
|
| 1117 |
- archive, err := archive.TarFilter(basePath, &archive.TarOptions{
|
|
| 1118 |
- Compression: archive.Uncompressed, |
|
| 1119 |
- Includes: filter, |
|
| 1120 |
- }) |
|
| 1121 |
- if err != nil {
|
|
| 1122 |
- return nil, err |
|
| 1123 |
- } |
|
| 1124 |
- return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 1125 |
- err := archive.Close() |
|
| 1126 |
- container.Unmount() |
|
| 1127 |
- return err |
|
| 1128 |
- }), nil |
|
| 1129 |
-} |
|
| 1130 |
- |
|
| 1131 |
-// Returns true if the container exposes a certain port |
|
| 1132 |
-func (container *Container) Exposes(p nat.Port) bool {
|
|
| 1133 |
- _, exists := container.Config.ExposedPorts[p] |
|
| 1134 |
- return exists |
|
| 1135 |
-} |
|
| 1136 |
- |
|
| 1137 |
-func (container *Container) GetPtyMaster() (*os.File, error) {
|
|
| 1138 |
- ttyConsole, ok := container.command.Terminal.(execdriver.TtyTerminal) |
|
| 1139 |
- if !ok {
|
|
| 1140 |
- return nil, ErrNoTTY |
|
| 1141 |
- } |
|
| 1142 |
- return ttyConsole.Master(), nil |
|
| 1143 |
-} |
| 1144 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,145 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/nat" |
|
| 5 |
- "testing" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
|
| 9 |
- ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
|
|
| 10 |
- if err != nil {
|
|
| 11 |
- t.Fatal(err) |
|
| 12 |
- } |
|
| 13 |
- if len(ports) != 1 {
|
|
| 14 |
- t.Logf("Expected 1 got %d", len(ports))
|
|
| 15 |
- t.FailNow() |
|
| 16 |
- } |
|
| 17 |
- if len(bindings) != 1 {
|
|
| 18 |
- t.Logf("Expected 1 got %d", len(bindings))
|
|
| 19 |
- t.FailNow() |
|
| 20 |
- } |
|
| 21 |
- for k := range ports {
|
|
| 22 |
- if k.Proto() != "tcp" {
|
|
| 23 |
- t.Logf("Expected tcp got %s", k.Proto())
|
|
| 24 |
- t.Fail() |
|
| 25 |
- } |
|
| 26 |
- if k.Port() != "80" {
|
|
| 27 |
- t.Logf("Expected 80 got %s", k.Port())
|
|
| 28 |
- t.Fail() |
|
| 29 |
- } |
|
| 30 |
- b, exists := bindings[k] |
|
| 31 |
- if !exists {
|
|
| 32 |
- t.Log("Binding does not exist")
|
|
| 33 |
- t.FailNow() |
|
| 34 |
- } |
|
| 35 |
- if len(b) != 1 {
|
|
| 36 |
- t.Logf("Expected 1 got %d", len(b))
|
|
| 37 |
- t.FailNow() |
|
| 38 |
- } |
|
| 39 |
- s := b[0] |
|
| 40 |
- if s.HostPort != "" {
|
|
| 41 |
- t.Logf("Expected \"\" got %s", s.HostPort)
|
|
| 42 |
- t.Fail() |
|
| 43 |
- } |
|
| 44 |
- if s.HostIp != "192.168.1.100" {
|
|
| 45 |
- t.Fail() |
|
| 46 |
- } |
|
| 47 |
- } |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func TestParseNetworkOptsPublic(t *testing.T) {
|
|
| 51 |
- ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:8080:80"})
|
|
| 52 |
- if err != nil {
|
|
| 53 |
- t.Fatal(err) |
|
| 54 |
- } |
|
| 55 |
- if len(ports) != 1 {
|
|
| 56 |
- t.Logf("Expected 1 got %d", len(ports))
|
|
| 57 |
- t.FailNow() |
|
| 58 |
- } |
|
| 59 |
- if len(bindings) != 1 {
|
|
| 60 |
- t.Logf("Expected 1 got %d", len(bindings))
|
|
| 61 |
- t.FailNow() |
|
| 62 |
- } |
|
| 63 |
- for k := range ports {
|
|
| 64 |
- if k.Proto() != "tcp" {
|
|
| 65 |
- t.Logf("Expected tcp got %s", k.Proto())
|
|
| 66 |
- t.Fail() |
|
| 67 |
- } |
|
| 68 |
- if k.Port() != "80" {
|
|
| 69 |
- t.Logf("Expected 80 got %s", k.Port())
|
|
| 70 |
- t.Fail() |
|
| 71 |
- } |
|
| 72 |
- b, exists := bindings[k] |
|
| 73 |
- if !exists {
|
|
| 74 |
- t.Log("Binding does not exist")
|
|
| 75 |
- t.FailNow() |
|
| 76 |
- } |
|
| 77 |
- if len(b) != 1 {
|
|
| 78 |
- t.Logf("Expected 1 got %d", len(b))
|
|
| 79 |
- t.FailNow() |
|
| 80 |
- } |
|
| 81 |
- s := b[0] |
|
| 82 |
- if s.HostPort != "8080" {
|
|
| 83 |
- t.Logf("Expected 8080 got %s", s.HostPort)
|
|
| 84 |
- t.Fail() |
|
| 85 |
- } |
|
| 86 |
- if s.HostIp != "192.168.1.100" {
|
|
| 87 |
- t.Fail() |
|
| 88 |
- } |
|
| 89 |
- } |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func TestParseNetworkOptsUdp(t *testing.T) {
|
|
| 93 |
- ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
|
| 94 |
- if err != nil {
|
|
| 95 |
- t.Fatal(err) |
|
| 96 |
- } |
|
| 97 |
- if len(ports) != 1 {
|
|
| 98 |
- t.Logf("Expected 1 got %d", len(ports))
|
|
| 99 |
- t.FailNow() |
|
| 100 |
- } |
|
| 101 |
- if len(bindings) != 1 {
|
|
| 102 |
- t.Logf("Expected 1 got %d", len(bindings))
|
|
| 103 |
- t.FailNow() |
|
| 104 |
- } |
|
| 105 |
- for k := range ports {
|
|
| 106 |
- if k.Proto() != "udp" {
|
|
| 107 |
- t.Logf("Expected udp got %s", k.Proto())
|
|
| 108 |
- t.Fail() |
|
| 109 |
- } |
|
| 110 |
- if k.Port() != "6000" {
|
|
| 111 |
- t.Logf("Expected 6000 got %s", k.Port())
|
|
| 112 |
- t.Fail() |
|
| 113 |
- } |
|
| 114 |
- b, exists := bindings[k] |
|
| 115 |
- if !exists {
|
|
| 116 |
- t.Log("Binding does not exist")
|
|
| 117 |
- t.FailNow() |
|
| 118 |
- } |
|
| 119 |
- if len(b) != 1 {
|
|
| 120 |
- t.Logf("Expected 1 got %d", len(b))
|
|
| 121 |
- t.FailNow() |
|
| 122 |
- } |
|
| 123 |
- s := b[0] |
|
| 124 |
- if s.HostPort != "" {
|
|
| 125 |
- t.Logf("Expected \"\" got %s", s.HostPort)
|
|
| 126 |
- t.Fail() |
|
| 127 |
- } |
|
| 128 |
- if s.HostIp != "192.168.1.100" {
|
|
| 129 |
- t.Fail() |
|
| 130 |
- } |
|
| 131 |
- } |
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 |
-func TestGetFullName(t *testing.T) {
|
|
| 135 |
- name, err := getFullName("testing")
|
|
| 136 |
- if err != nil {
|
|
| 137 |
- t.Fatal(err) |
|
| 138 |
- } |
|
| 139 |
- if name != "/testing" {
|
|
| 140 |
- t.Fatalf("Expected /testing got %s", name)
|
|
| 141 |
- } |
|
| 142 |
- if _, err := getFullName(""); err == nil {
|
|
| 143 |
- t.Fatal("Error should not be nil")
|
|
| 144 |
- } |
|
| 145 |
-} |
| ... | ... |
@@ -5,12 +5,12 @@ import ( |
| 5 | 5 |
"bytes" |
| 6 | 6 |
"encoding/json" |
| 7 | 7 |
"fmt" |
| 8 |
- "github.com/dotcloud/docker" |
|
| 9 | 8 |
"github.com/dotcloud/docker/api" |
| 10 | 9 |
"github.com/dotcloud/docker/dockerversion" |
| 11 | 10 |
"github.com/dotcloud/docker/engine" |
| 12 | 11 |
"github.com/dotcloud/docker/image" |
| 13 | 12 |
"github.com/dotcloud/docker/runconfig" |
| 13 |
+ "github.com/dotcloud/docker/runtime" |
|
| 14 | 14 |
"github.com/dotcloud/docker/utils" |
| 15 | 15 |
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" |
| 16 | 16 |
"io" |
| ... | ... |
@@ -600,7 +600,7 @@ func TestGetContainersByName(t *testing.T) {
|
| 600 | 600 |
t.Fatal(err) |
| 601 | 601 |
} |
| 602 | 602 |
assertHttpNotError(r, t) |
| 603 |
- outContainer := &docker.Container{}
|
|
| 603 |
+ outContainer := &runtime.Container{}
|
|
| 604 | 604 |
if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
|
| 605 | 605 |
t.Fatal(err) |
| 606 | 606 |
} |
| ... | ... |
@@ -3,11 +3,11 @@ package docker |
| 3 | 3 |
import ( |
| 4 | 4 |
"bufio" |
| 5 | 5 |
"fmt" |
| 6 |
- "github.com/dotcloud/docker" |
|
| 7 | 6 |
"github.com/dotcloud/docker/api" |
| 8 | 7 |
"github.com/dotcloud/docker/engine" |
| 9 | 8 |
"github.com/dotcloud/docker/image" |
| 10 | 9 |
"github.com/dotcloud/docker/pkg/term" |
| 10 |
+ "github.com/dotcloud/docker/runtime" |
|
| 11 | 11 |
"github.com/dotcloud/docker/utils" |
| 12 | 12 |
"io" |
| 13 | 13 |
"io/ioutil" |
| ... | ... |
@@ -36,7 +36,7 @@ func closeWrap(args ...io.Closer) error {
|
| 36 | 36 |
return nil |
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 |
-func setRaw(t *testing.T, c *docker.Container) *term.State {
|
|
| 39 |
+func setRaw(t *testing.T, c *runtime.Container) *term.State {
|
|
| 40 | 40 |
pty, err := c.GetPtyMaster() |
| 41 | 41 |
if err != nil {
|
| 42 | 42 |
t.Fatal(err) |
| ... | ... |
@@ -48,7 +48,7 @@ func setRaw(t *testing.T, c *docker.Container) *term.State {
|
| 48 | 48 |
return state |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
-func unsetRaw(t *testing.T, c *docker.Container, state *term.State) {
|
|
| 51 |
+func unsetRaw(t *testing.T, c *runtime.Container, state *term.State) {
|
|
| 52 | 52 |
pty, err := c.GetPtyMaster() |
| 53 | 53 |
if err != nil {
|
| 54 | 54 |
t.Fatal(err) |
| ... | ... |
@@ -56,8 +56,8 @@ func unsetRaw(t *testing.T, c *docker.Container, state *term.State) {
|
| 56 | 56 |
term.RestoreTerminal(pty.Fd(), state) |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
-func waitContainerStart(t *testing.T, timeout time.Duration) *docker.Container {
|
|
| 60 |
- var container *docker.Container |
|
| 59 |
+func waitContainerStart(t *testing.T, timeout time.Duration) *runtime.Container {
|
|
| 60 |
+ var container *runtime.Container |
|
| 61 | 61 |
|
| 62 | 62 |
setTimeout(t, "Waiting for the container to be started timed out", timeout, func() {
|
| 63 | 63 |
for {
|
| ... | ... |
@@ -3,11 +3,11 @@ package docker |
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 | 5 |
"fmt" |
| 6 |
- "github.com/dotcloud/docker" |
|
| 7 | 6 |
"github.com/dotcloud/docker/engine" |
| 8 | 7 |
"github.com/dotcloud/docker/image" |
| 9 | 8 |
"github.com/dotcloud/docker/nat" |
| 10 | 9 |
"github.com/dotcloud/docker/runconfig" |
| 10 |
+ "github.com/dotcloud/docker/runtime" |
|
| 11 | 11 |
"github.com/dotcloud/docker/sysinit" |
| 12 | 12 |
"github.com/dotcloud/docker/utils" |
| 13 | 13 |
"io" |
| ... | ... |
@@ -16,7 +16,7 @@ import ( |
| 16 | 16 |
"net/url" |
| 17 | 17 |
"os" |
| 18 | 18 |
"path/filepath" |
| 19 |
- "runtime" |
|
| 19 |
+ goruntime "runtime" |
|
| 20 | 20 |
"strconv" |
| 21 | 21 |
"strings" |
| 22 | 22 |
"syscall" |
| ... | ... |
@@ -36,14 +36,14 @@ const ( |
| 36 | 36 |
|
| 37 | 37 |
var ( |
| 38 | 38 |
// FIXME: globalRuntime is deprecated by globalEngine. All tests should be converted. |
| 39 |
- globalRuntime *docker.Runtime |
|
| 39 |
+ globalRuntime *runtime.Runtime |
|
| 40 | 40 |
globalEngine *engine.Engine |
| 41 | 41 |
startFds int |
| 42 | 42 |
startGoroutines int |
| 43 | 43 |
) |
| 44 | 44 |
|
| 45 | 45 |
// FIXME: nuke() is deprecated by Runtime.Nuke() |
| 46 |
-func nuke(runtime *docker.Runtime) error {
|
|
| 46 |
+func nuke(runtime *runtime.Runtime) error {
|
|
| 47 | 47 |
return runtime.Nuke() |
| 48 | 48 |
} |
| 49 | 49 |
|
| ... | ... |
@@ -120,7 +120,7 @@ func init() {
|
| 120 | 120 |
|
| 121 | 121 |
// Create the "global runtime" with a long-running daemon for integration tests |
| 122 | 122 |
spawnGlobalDaemon() |
| 123 |
- startFds, startGoroutines = utils.GetTotalUsedFds(), runtime.NumGoroutine() |
|
| 123 |
+ startFds, startGoroutines = utils.GetTotalUsedFds(), goruntime.NumGoroutine() |
|
| 124 | 124 |
} |
| 125 | 125 |
|
| 126 | 126 |
func setupBaseImage() {
|
| ... | ... |
@@ -173,7 +173,7 @@ func spawnGlobalDaemon() {
|
| 173 | 173 |
|
| 174 | 174 |
// FIXME: test that ImagePull(json=true) send correct json output |
| 175 | 175 |
|
| 176 |
-func GetTestImage(runtime *docker.Runtime) *image.Image {
|
|
| 176 |
+func GetTestImage(runtime *runtime.Runtime) *image.Image {
|
|
| 177 | 177 |
imgs, err := runtime.Graph().Map() |
| 178 | 178 |
if err != nil {
|
| 179 | 179 |
log.Fatalf("Unable to get the test image: %s", err)
|
| ... | ... |
@@ -357,7 +357,7 @@ func TestGet(t *testing.T) {
|
| 357 | 357 |
|
| 358 | 358 |
} |
| 359 | 359 |
|
| 360 |
-func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *docker.Container, string) {
|
|
| 360 |
+func startEchoServerContainer(t *testing.T, proto string) (*runtime.Runtime, *runtime.Container, string) {
|
|
| 361 | 361 |
var ( |
| 362 | 362 |
err error |
| 363 | 363 |
id string |
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
"github.com/dotcloud/docker/builtins" |
| 19 | 19 |
"github.com/dotcloud/docker/engine" |
| 20 | 20 |
"github.com/dotcloud/docker/runconfig" |
| 21 |
+ "github.com/dotcloud/docker/runtime" |
|
| 21 | 22 |
"github.com/dotcloud/docker/utils" |
| 22 | 23 |
) |
| 23 | 24 |
|
| ... | ... |
@@ -27,7 +28,7 @@ import ( |
| 27 | 27 |
|
| 28 | 28 |
// Create a temporary runtime suitable for unit testing. |
| 29 | 29 |
// Call t.Fatal() at the first error. |
| 30 |
-func mkRuntime(f utils.Fataler) *docker.Runtime {
|
|
| 30 |
+func mkRuntime(f utils.Fataler) *runtime.Runtime {
|
|
| 31 | 31 |
eng := newTestEngine(f, false, "") |
| 32 | 32 |
return mkRuntimeFromEngine(eng, f) |
| 33 | 33 |
// FIXME: |
| ... | ... |
@@ -139,7 +140,7 @@ func assertHttpError(r *httptest.ResponseRecorder, t utils.Fataler) {
|
| 139 | 139 |
} |
| 140 | 140 |
} |
| 141 | 141 |
|
| 142 |
-func getContainer(eng *engine.Engine, id string, t utils.Fataler) *docker.Container {
|
|
| 142 |
+func getContainer(eng *engine.Engine, id string, t utils.Fataler) *runtime.Container {
|
|
| 143 | 143 |
runtime := mkRuntimeFromEngine(eng, t) |
| 144 | 144 |
c := runtime.Get(id) |
| 145 | 145 |
if c == nil {
|
| ... | ... |
@@ -160,14 +161,14 @@ func mkServerFromEngine(eng *engine.Engine, t utils.Fataler) *docker.Server {
|
| 160 | 160 |
return srv |
| 161 | 161 |
} |
| 162 | 162 |
|
| 163 |
-func mkRuntimeFromEngine(eng *engine.Engine, t utils.Fataler) *docker.Runtime {
|
|
| 163 |
+func mkRuntimeFromEngine(eng *engine.Engine, t utils.Fataler) *runtime.Runtime {
|
|
| 164 | 164 |
iRuntime := eng.Hack_GetGlobalVar("httpapi.runtime")
|
| 165 | 165 |
if iRuntime == nil {
|
| 166 | 166 |
panic("Legacy runtime field not set in engine")
|
| 167 | 167 |
} |
| 168 |
- runtime, ok := iRuntime.(*docker.Runtime) |
|
| 168 |
+ runtime, ok := iRuntime.(*runtime.Runtime) |
|
| 169 | 169 |
if !ok {
|
| 170 |
- panic("Legacy runtime field in engine does not cast to *docker.Runtime")
|
|
| 170 |
+ panic("Legacy runtime field in engine does not cast to *runtime.Runtime")
|
|
| 171 | 171 |
} |
| 172 | 172 |
return runtime |
| 173 | 173 |
} |
| ... | ... |
@@ -249,7 +250,7 @@ func readFile(src string, t *testing.T) (content string) {
|
| 249 | 249 |
// dynamically replaced by the current test image. |
| 250 | 250 |
// The caller is responsible for destroying the container. |
| 251 | 251 |
// Call t.Fatal() at the first error. |
| 252 |
-func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *runconfig.HostConfig, error) {
|
|
| 252 |
+func mkContainer(r *runtime.Runtime, args []string, t *testing.T) (*runtime.Container, *runconfig.HostConfig, error) {
|
|
| 253 | 253 |
config, hc, _, err := runconfig.Parse(args, nil) |
| 254 | 254 |
defer func() {
|
| 255 | 255 |
if err != nil && t != nil {
|
| ... | ... |
@@ -280,7 +281,7 @@ func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Contai |
| 280 | 280 |
// and return its standard output as a string. |
| 281 | 281 |
// The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image.
|
| 282 | 282 |
// If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally. |
| 283 |
-func runContainer(eng *engine.Engine, r *docker.Runtime, args []string, t *testing.T) (output string, err error) {
|
|
| 283 |
+func runContainer(eng *engine.Engine, r *runtime.Runtime, args []string, t *testing.T) (output string, err error) {
|
|
| 284 | 284 |
defer func() {
|
| 285 | 285 |
if err != nil && t != nil {
|
| 286 | 286 |
t.Fatal(err) |
| 287 | 287 |
deleted file mode 100644 |
| ... | ... |
@@ -1,918 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "container/list" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/archive" |
|
| 7 |
- "github.com/dotcloud/docker/daemonconfig" |
|
| 8 |
- "github.com/dotcloud/docker/dockerversion" |
|
| 9 |
- "github.com/dotcloud/docker/engine" |
|
| 10 |
- "github.com/dotcloud/docker/execdriver" |
|
| 11 |
- "github.com/dotcloud/docker/execdriver/lxc" |
|
| 12 |
- "github.com/dotcloud/docker/execdriver/native" |
|
| 13 |
- "github.com/dotcloud/docker/graph" |
|
| 14 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 15 |
- "github.com/dotcloud/docker/graphdriver/aufs" |
|
| 16 |
- _ "github.com/dotcloud/docker/graphdriver/btrfs" |
|
| 17 |
- _ "github.com/dotcloud/docker/graphdriver/devmapper" |
|
| 18 |
- _ "github.com/dotcloud/docker/graphdriver/vfs" |
|
| 19 |
- "github.com/dotcloud/docker/image" |
|
| 20 |
- _ "github.com/dotcloud/docker/networkdriver/lxc" |
|
| 21 |
- "github.com/dotcloud/docker/networkdriver/portallocator" |
|
| 22 |
- "github.com/dotcloud/docker/pkg/graphdb" |
|
| 23 |
- "github.com/dotcloud/docker/pkg/sysinfo" |
|
| 24 |
- "github.com/dotcloud/docker/runconfig" |
|
| 25 |
- "github.com/dotcloud/docker/utils" |
|
| 26 |
- "io" |
|
| 27 |
- "io/ioutil" |
|
| 28 |
- "os" |
|
| 29 |
- "path" |
|
| 30 |
- "regexp" |
|
| 31 |
- "sort" |
|
| 32 |
- "strings" |
|
| 33 |
- "sync" |
|
| 34 |
- "time" |
|
| 35 |
-) |
|
| 36 |
- |
|
| 37 |
-// Set the max depth to the aufs default that most |
|
| 38 |
-// kernels are compiled with |
|
| 39 |
-// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk |
|
| 40 |
-const MaxImageDepth = 127 |
|
| 41 |
- |
|
| 42 |
-var ( |
|
| 43 |
- defaultDns = []string{"8.8.8.8", "8.8.4.4"}
|
|
| 44 |
- validContainerNameChars = `[a-zA-Z0-9_.-]` |
|
| 45 |
- validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`) |
|
| 46 |
-) |
|
| 47 |
- |
|
| 48 |
-type Runtime struct {
|
|
| 49 |
- repository string |
|
| 50 |
- sysInitPath string |
|
| 51 |
- containers *list.List |
|
| 52 |
- graph *graph.Graph |
|
| 53 |
- repositories *graph.TagStore |
|
| 54 |
- idIndex *utils.TruncIndex |
|
| 55 |
- sysInfo *sysinfo.SysInfo |
|
| 56 |
- volumes *graph.Graph |
|
| 57 |
- srv *Server |
|
| 58 |
- eng *engine.Engine |
|
| 59 |
- config *daemonconfig.Config |
|
| 60 |
- containerGraph *graphdb.Database |
|
| 61 |
- driver graphdriver.Driver |
|
| 62 |
- execDriver execdriver.Driver |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-// List returns an array of all containers registered in the runtime. |
|
| 66 |
-func (runtime *Runtime) List() []*Container {
|
|
| 67 |
- containers := new(History) |
|
| 68 |
- for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
|
| 69 |
- containers.Add(e.Value.(*Container)) |
|
| 70 |
- } |
|
| 71 |
- return *containers |
|
| 72 |
-} |
|
| 73 |
- |
|
| 74 |
-func (runtime *Runtime) getContainerElement(id string) *list.Element {
|
|
| 75 |
- for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
|
| 76 |
- container := e.Value.(*Container) |
|
| 77 |
- if container.ID == id {
|
|
| 78 |
- return e |
|
| 79 |
- } |
|
| 80 |
- } |
|
| 81 |
- return nil |
|
| 82 |
-} |
|
| 83 |
- |
|
| 84 |
-// Get looks for a container by the specified ID or name, and returns it. |
|
| 85 |
-// If the container is not found, or if an error occurs, nil is returned. |
|
| 86 |
-func (runtime *Runtime) Get(name string) *Container {
|
|
| 87 |
- if c, _ := runtime.GetByName(name); c != nil {
|
|
| 88 |
- return c |
|
| 89 |
- } |
|
| 90 |
- |
|
| 91 |
- id, err := runtime.idIndex.Get(name) |
|
| 92 |
- if err != nil {
|
|
| 93 |
- return nil |
|
| 94 |
- } |
|
| 95 |
- |
|
| 96 |
- e := runtime.getContainerElement(id) |
|
| 97 |
- if e == nil {
|
|
| 98 |
- return nil |
|
| 99 |
- } |
|
| 100 |
- return e.Value.(*Container) |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-// Exists returns a true if a container of the specified ID or name exists, |
|
| 104 |
-// false otherwise. |
|
| 105 |
-func (runtime *Runtime) Exists(id string) bool {
|
|
| 106 |
- return runtime.Get(id) != nil |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-func (runtime *Runtime) containerRoot(id string) string {
|
|
| 110 |
- return path.Join(runtime.repository, id) |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-// Load reads the contents of a container from disk |
|
| 114 |
-// This is typically done at startup. |
|
| 115 |
-func (runtime *Runtime) load(id string) (*Container, error) {
|
|
| 116 |
- container := &Container{root: runtime.containerRoot(id)}
|
|
| 117 |
- if err := container.FromDisk(); err != nil {
|
|
| 118 |
- return nil, err |
|
| 119 |
- } |
|
| 120 |
- if container.ID != id {
|
|
| 121 |
- return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
|
| 122 |
- } |
|
| 123 |
- if container.State.IsRunning() {
|
|
| 124 |
- container.State.SetGhost(true) |
|
| 125 |
- } |
|
| 126 |
- return container, nil |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-// Register makes a container object usable by the runtime as <container.ID> |
|
| 130 |
-func (runtime *Runtime) Register(container *Container) error {
|
|
| 131 |
- if container.runtime != nil || runtime.Exists(container.ID) {
|
|
| 132 |
- return fmt.Errorf("Container is already loaded")
|
|
| 133 |
- } |
|
| 134 |
- if err := validateID(container.ID); err != nil {
|
|
| 135 |
- return err |
|
| 136 |
- } |
|
| 137 |
- if err := runtime.ensureName(container); err != nil {
|
|
| 138 |
- return err |
|
| 139 |
- } |
|
| 140 |
- |
|
| 141 |
- container.runtime = runtime |
|
| 142 |
- |
|
| 143 |
- // Attach to stdout and stderr |
|
| 144 |
- container.stderr = utils.NewWriteBroadcaster() |
|
| 145 |
- container.stdout = utils.NewWriteBroadcaster() |
|
| 146 |
- // Attach to stdin |
|
| 147 |
- if container.Config.OpenStdin {
|
|
| 148 |
- container.stdin, container.stdinPipe = io.Pipe() |
|
| 149 |
- } else {
|
|
| 150 |
- container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
| 151 |
- } |
|
| 152 |
- // done |
|
| 153 |
- runtime.containers.PushBack(container) |
|
| 154 |
- runtime.idIndex.Add(container.ID) |
|
| 155 |
- |
|
| 156 |
- // FIXME: if the container is supposed to be running but is not, auto restart it? |
|
| 157 |
- // if so, then we need to restart monitor and init a new lock |
|
| 158 |
- // If the container is supposed to be running, make sure of it |
|
| 159 |
- if container.State.IsRunning() {
|
|
| 160 |
- if container.State.IsGhost() {
|
|
| 161 |
- utils.Debugf("killing ghost %s", container.ID)
|
|
| 162 |
- |
|
| 163 |
- existingPid := container.State.Pid |
|
| 164 |
- container.State.SetGhost(false) |
|
| 165 |
- container.State.SetStopped(0) |
|
| 166 |
- |
|
| 167 |
- if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
|
|
| 168 |
- lxc.KillLxc(container.ID, 9) |
|
| 169 |
- } else {
|
|
| 170 |
- command := &execdriver.Command{
|
|
| 171 |
- ID: container.ID, |
|
| 172 |
- } |
|
| 173 |
- command.Process = &os.Process{Pid: existingPid}
|
|
| 174 |
- runtime.execDriver.Kill(command, 9) |
|
| 175 |
- } |
|
| 176 |
- // ensure that the filesystem is also unmounted |
|
| 177 |
- unmountVolumesForContainer(container) |
|
| 178 |
- if err := container.Unmount(); err != nil {
|
|
| 179 |
- utils.Debugf("ghost unmount error %s", err)
|
|
| 180 |
- } |
|
| 181 |
- } |
|
| 182 |
- |
|
| 183 |
- info := runtime.execDriver.Info(container.ID) |
|
| 184 |
- if !info.IsRunning() {
|
|
| 185 |
- utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
|
|
| 186 |
- if runtime.config.AutoRestart {
|
|
| 187 |
- utils.Debugf("Restarting")
|
|
| 188 |
- unmountVolumesForContainer(container) |
|
| 189 |
- if err := container.Unmount(); err != nil {
|
|
| 190 |
- utils.Debugf("restart unmount error %s", err)
|
|
| 191 |
- } |
|
| 192 |
- |
|
| 193 |
- container.State.SetGhost(false) |
|
| 194 |
- container.State.SetStopped(0) |
|
| 195 |
- if err := container.Start(); err != nil {
|
|
| 196 |
- return err |
|
| 197 |
- } |
|
| 198 |
- } else {
|
|
| 199 |
- utils.Debugf("Marking as stopped")
|
|
| 200 |
- container.State.SetStopped(-127) |
|
| 201 |
- if err := container.ToDisk(); err != nil {
|
|
| 202 |
- return err |
|
| 203 |
- } |
|
| 204 |
- } |
|
| 205 |
- } |
|
| 206 |
- } else {
|
|
| 207 |
- // When the container is not running, we still initialize the waitLock |
|
| 208 |
- // chan and close it. Receiving on nil chan blocks whereas receiving on a |
|
| 209 |
- // closed chan does not. In this case we do not want to block. |
|
| 210 |
- container.waitLock = make(chan struct{})
|
|
| 211 |
- close(container.waitLock) |
|
| 212 |
- } |
|
| 213 |
- return nil |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-func (runtime *Runtime) ensureName(container *Container) error {
|
|
| 217 |
- if container.Name == "" {
|
|
| 218 |
- name, err := generateRandomName(runtime) |
|
| 219 |
- if err != nil {
|
|
| 220 |
- name = utils.TruncateID(container.ID) |
|
| 221 |
- } |
|
| 222 |
- container.Name = name |
|
| 223 |
- |
|
| 224 |
- if err := container.ToDisk(); err != nil {
|
|
| 225 |
- utils.Debugf("Error saving container name %s", err)
|
|
| 226 |
- } |
|
| 227 |
- if !runtime.containerGraph.Exists(name) {
|
|
| 228 |
- if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
|
|
| 229 |
- utils.Debugf("Setting default id - %s", err)
|
|
| 230 |
- } |
|
| 231 |
- } |
|
| 232 |
- } |
|
| 233 |
- return nil |
|
| 234 |
-} |
|
| 235 |
- |
|
| 236 |
-func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
|
|
| 237 |
- log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) |
|
| 238 |
- if err != nil {
|
|
| 239 |
- return err |
|
| 240 |
- } |
|
| 241 |
- src.AddWriter(log, stream) |
|
| 242 |
- return nil |
|
| 243 |
-} |
|
| 244 |
- |
|
| 245 |
-// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem. |
|
| 246 |
-func (runtime *Runtime) Destroy(container *Container) error {
|
|
| 247 |
- if container == nil {
|
|
| 248 |
- return fmt.Errorf("The given container is <nil>")
|
|
| 249 |
- } |
|
| 250 |
- |
|
| 251 |
- element := runtime.getContainerElement(container.ID) |
|
| 252 |
- if element == nil {
|
|
| 253 |
- return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
|
| 254 |
- } |
|
| 255 |
- |
|
| 256 |
- if err := container.Stop(3); err != nil {
|
|
| 257 |
- return err |
|
| 258 |
- } |
|
| 259 |
- |
|
| 260 |
- if err := runtime.driver.Remove(container.ID); err != nil {
|
|
| 261 |
- return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
|
|
| 262 |
- } |
|
| 263 |
- |
|
| 264 |
- initID := fmt.Sprintf("%s-init", container.ID)
|
|
| 265 |
- if err := runtime.driver.Remove(initID); err != nil {
|
|
| 266 |
- return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", runtime.driver, initID, err)
|
|
| 267 |
- } |
|
| 268 |
- |
|
| 269 |
- if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
|
|
| 270 |
- utils.Debugf("Unable to remove container from link graph: %s", err)
|
|
| 271 |
- } |
|
| 272 |
- |
|
| 273 |
- // Deregister the container before removing its directory, to avoid race conditions |
|
| 274 |
- runtime.idIndex.Delete(container.ID) |
|
| 275 |
- runtime.containers.Remove(element) |
|
| 276 |
- if err := os.RemoveAll(container.root); err != nil {
|
|
| 277 |
- return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
|
| 278 |
- } |
|
| 279 |
- return nil |
|
| 280 |
-} |
|
| 281 |
- |
|
| 282 |
-func (runtime *Runtime) restore() error {
|
|
| 283 |
- if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 284 |
- fmt.Printf("Loading containers: ")
|
|
| 285 |
- } |
|
| 286 |
- dir, err := ioutil.ReadDir(runtime.repository) |
|
| 287 |
- if err != nil {
|
|
| 288 |
- return err |
|
| 289 |
- } |
|
| 290 |
- containers := make(map[string]*Container) |
|
| 291 |
- currentDriver := runtime.driver.String() |
|
| 292 |
- |
|
| 293 |
- for _, v := range dir {
|
|
| 294 |
- id := v.Name() |
|
| 295 |
- container, err := runtime.load(id) |
|
| 296 |
- if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 297 |
- fmt.Print(".")
|
|
| 298 |
- } |
|
| 299 |
- if err != nil {
|
|
| 300 |
- utils.Errorf("Failed to load container %v: %v", id, err)
|
|
| 301 |
- continue |
|
| 302 |
- } |
|
| 303 |
- |
|
| 304 |
- // Ignore the container if it does not support the current driver being used by the graph |
|
| 305 |
- if container.Driver == "" && currentDriver == "aufs" || container.Driver == currentDriver {
|
|
| 306 |
- utils.Debugf("Loaded container %v", container.ID)
|
|
| 307 |
- containers[container.ID] = container |
|
| 308 |
- } else {
|
|
| 309 |
- utils.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID)
|
|
| 310 |
- } |
|
| 311 |
- } |
|
| 312 |
- |
|
| 313 |
- register := func(container *Container) {
|
|
| 314 |
- if err := runtime.Register(container); err != nil {
|
|
| 315 |
- utils.Debugf("Failed to register container %s: %s", container.ID, err)
|
|
| 316 |
- } |
|
| 317 |
- } |
|
| 318 |
- |
|
| 319 |
- if entities := runtime.containerGraph.List("/", -1); entities != nil {
|
|
| 320 |
- for _, p := range entities.Paths() {
|
|
| 321 |
- if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 322 |
- fmt.Print(".")
|
|
| 323 |
- } |
|
| 324 |
- e := entities[p] |
|
| 325 |
- if container, ok := containers[e.ID()]; ok {
|
|
| 326 |
- register(container) |
|
| 327 |
- delete(containers, e.ID()) |
|
| 328 |
- } |
|
| 329 |
- } |
|
| 330 |
- } |
|
| 331 |
- |
|
| 332 |
- // Any containers that are left over do not exist in the graph |
|
| 333 |
- for _, container := range containers {
|
|
| 334 |
- // Try to set the default name for a container if it exists prior to links |
|
| 335 |
- container.Name, err = generateRandomName(runtime) |
|
| 336 |
- if err != nil {
|
|
| 337 |
- container.Name = utils.TruncateID(container.ID) |
|
| 338 |
- } |
|
| 339 |
- |
|
| 340 |
- if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil {
|
|
| 341 |
- utils.Debugf("Setting default id - %s", err)
|
|
| 342 |
- } |
|
| 343 |
- register(container) |
|
| 344 |
- } |
|
| 345 |
- |
|
| 346 |
- if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 347 |
- fmt.Printf(": done.\n")
|
|
| 348 |
- } |
|
| 349 |
- |
|
| 350 |
- return nil |
|
| 351 |
-} |
|
| 352 |
- |
|
| 353 |
-// Create creates a new container from the given configuration with a given name. |
|
| 354 |
-func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
|
|
| 355 |
- // Lookup image |
|
| 356 |
- img, err := runtime.repositories.LookupImage(config.Image) |
|
| 357 |
- if err != nil {
|
|
| 358 |
- return nil, nil, err |
|
| 359 |
- } |
|
| 360 |
- |
|
| 361 |
- // We add 2 layers to the depth because the container's rw and |
|
| 362 |
- // init layer add to the restriction |
|
| 363 |
- depth, err := img.Depth() |
|
| 364 |
- if err != nil {
|
|
| 365 |
- return nil, nil, err |
|
| 366 |
- } |
|
| 367 |
- |
|
| 368 |
- if depth+2 >= MaxImageDepth {
|
|
| 369 |
- return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
|
|
| 370 |
- } |
|
| 371 |
- |
|
| 372 |
- checkDeprecatedExpose := func(config *runconfig.Config) bool {
|
|
| 373 |
- if config != nil {
|
|
| 374 |
- if config.PortSpecs != nil {
|
|
| 375 |
- for _, p := range config.PortSpecs {
|
|
| 376 |
- if strings.Contains(p, ":") {
|
|
| 377 |
- return true |
|
| 378 |
- } |
|
| 379 |
- } |
|
| 380 |
- } |
|
| 381 |
- } |
|
| 382 |
- return false |
|
| 383 |
- } |
|
| 384 |
- |
|
| 385 |
- warnings := []string{}
|
|
| 386 |
- if checkDeprecatedExpose(img.Config) || checkDeprecatedExpose(config) {
|
|
| 387 |
- warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.") |
|
| 388 |
- } |
|
| 389 |
- |
|
| 390 |
- if img.Config != nil {
|
|
| 391 |
- if err := runconfig.Merge(config, img.Config); err != nil {
|
|
| 392 |
- return nil, nil, err |
|
| 393 |
- } |
|
| 394 |
- } |
|
| 395 |
- |
|
| 396 |
- if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 {
|
|
| 397 |
- return nil, nil, fmt.Errorf("No command specified")
|
|
| 398 |
- } |
|
| 399 |
- |
|
| 400 |
- // Generate id |
|
| 401 |
- id := utils.GenerateRandomID() |
|
| 402 |
- |
|
| 403 |
- if name == "" {
|
|
| 404 |
- name, err = generateRandomName(runtime) |
|
| 405 |
- if err != nil {
|
|
| 406 |
- name = utils.TruncateID(id) |
|
| 407 |
- } |
|
| 408 |
- } else {
|
|
| 409 |
- if !validContainerNamePattern.MatchString(name) {
|
|
| 410 |
- return nil, nil, fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars)
|
|
| 411 |
- } |
|
| 412 |
- } |
|
| 413 |
- |
|
| 414 |
- if name[0] != '/' {
|
|
| 415 |
- name = "/" + name |
|
| 416 |
- } |
|
| 417 |
- |
|
| 418 |
- // Set the enitity in the graph using the default name specified |
|
| 419 |
- if _, err := runtime.containerGraph.Set(name, id); err != nil {
|
|
| 420 |
- if !graphdb.IsNonUniqueNameError(err) {
|
|
| 421 |
- return nil, nil, err |
|
| 422 |
- } |
|
| 423 |
- |
|
| 424 |
- conflictingContainer, err := runtime.GetByName(name) |
|
| 425 |
- if err != nil {
|
|
| 426 |
- if strings.Contains(err.Error(), "Could not find entity") {
|
|
| 427 |
- return nil, nil, err |
|
| 428 |
- } |
|
| 429 |
- |
|
| 430 |
- // Remove name and continue starting the container |
|
| 431 |
- if err := runtime.containerGraph.Delete(name); err != nil {
|
|
| 432 |
- return nil, nil, err |
|
| 433 |
- } |
|
| 434 |
- } else {
|
|
| 435 |
- nameAsKnownByUser := strings.TrimPrefix(name, "/") |
|
| 436 |
- return nil, nil, fmt.Errorf( |
|
| 437 |
- "Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", nameAsKnownByUser, |
|
| 438 |
- utils.TruncateID(conflictingContainer.ID), nameAsKnownByUser) |
|
| 439 |
- } |
|
| 440 |
- } |
|
| 441 |
- |
|
| 442 |
- // Generate default hostname |
|
| 443 |
- // FIXME: the lxc template no longer needs to set a default hostname |
|
| 444 |
- if config.Hostname == "" {
|
|
| 445 |
- config.Hostname = id[:12] |
|
| 446 |
- } |
|
| 447 |
- |
|
| 448 |
- var args []string |
|
| 449 |
- var entrypoint string |
|
| 450 |
- |
|
| 451 |
- if len(config.Entrypoint) != 0 {
|
|
| 452 |
- entrypoint = config.Entrypoint[0] |
|
| 453 |
- args = append(config.Entrypoint[1:], config.Cmd...) |
|
| 454 |
- } else {
|
|
| 455 |
- entrypoint = config.Cmd[0] |
|
| 456 |
- args = config.Cmd[1:] |
|
| 457 |
- } |
|
| 458 |
- |
|
| 459 |
- container := &Container{
|
|
| 460 |
- // FIXME: we should generate the ID here instead of receiving it as an argument |
|
| 461 |
- ID: id, |
|
| 462 |
- Created: time.Now().UTC(), |
|
| 463 |
- Path: entrypoint, |
|
| 464 |
- Args: args, //FIXME: de-duplicate from config |
|
| 465 |
- Config: config, |
|
| 466 |
- hostConfig: &runconfig.HostConfig{},
|
|
| 467 |
- Image: img.ID, // Always use the resolved image id |
|
| 468 |
- NetworkSettings: &NetworkSettings{},
|
|
| 469 |
- Name: name, |
|
| 470 |
- Driver: runtime.driver.String(), |
|
| 471 |
- ExecDriver: runtime.execDriver.Name(), |
|
| 472 |
- } |
|
| 473 |
- container.root = runtime.containerRoot(container.ID) |
|
| 474 |
- // Step 1: create the container directory. |
|
| 475 |
- // This doubles as a barrier to avoid race conditions. |
|
| 476 |
- if err := os.Mkdir(container.root, 0700); err != nil {
|
|
| 477 |
- return nil, nil, err |
|
| 478 |
- } |
|
| 479 |
- |
|
| 480 |
- initID := fmt.Sprintf("%s-init", container.ID)
|
|
| 481 |
- if err := runtime.driver.Create(initID, img.ID); err != nil {
|
|
| 482 |
- return nil, nil, err |
|
| 483 |
- } |
|
| 484 |
- initPath, err := runtime.driver.Get(initID) |
|
| 485 |
- if err != nil {
|
|
| 486 |
- return nil, nil, err |
|
| 487 |
- } |
|
| 488 |
- defer runtime.driver.Put(initID) |
|
| 489 |
- |
|
| 490 |
- if err := graph.SetupInitLayer(initPath); err != nil {
|
|
| 491 |
- return nil, nil, err |
|
| 492 |
- } |
|
| 493 |
- |
|
| 494 |
- if err := runtime.driver.Create(container.ID, initID); err != nil {
|
|
| 495 |
- return nil, nil, err |
|
| 496 |
- } |
|
| 497 |
- resolvConf, err := utils.GetResolvConf() |
|
| 498 |
- if err != nil {
|
|
| 499 |
- return nil, nil, err |
|
| 500 |
- } |
|
| 501 |
- |
|
| 502 |
- if len(config.Dns) == 0 && len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
|
| 503 |
- //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns |
|
| 504 |
- runtime.config.Dns = defaultDns |
|
| 505 |
- } |
|
| 506 |
- |
|
| 507 |
- // If custom dns exists, then create a resolv.conf for the container |
|
| 508 |
- if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 {
|
|
| 509 |
- var dns []string |
|
| 510 |
- if len(config.Dns) > 0 {
|
|
| 511 |
- dns = config.Dns |
|
| 512 |
- } else {
|
|
| 513 |
- dns = runtime.config.Dns |
|
| 514 |
- } |
|
| 515 |
- container.ResolvConfPath = path.Join(container.root, "resolv.conf") |
|
| 516 |
- f, err := os.Create(container.ResolvConfPath) |
|
| 517 |
- if err != nil {
|
|
| 518 |
- return nil, nil, err |
|
| 519 |
- } |
|
| 520 |
- defer f.Close() |
|
| 521 |
- for _, dns := range dns {
|
|
| 522 |
- if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
|
|
| 523 |
- return nil, nil, err |
|
| 524 |
- } |
|
| 525 |
- } |
|
| 526 |
- } else {
|
|
| 527 |
- container.ResolvConfPath = "/etc/resolv.conf" |
|
| 528 |
- } |
|
| 529 |
- |
|
| 530 |
- // Step 2: save the container json |
|
| 531 |
- if err := container.ToDisk(); err != nil {
|
|
| 532 |
- return nil, nil, err |
|
| 533 |
- } |
|
| 534 |
- |
|
| 535 |
- // Step 3: register the container |
|
| 536 |
- if err := runtime.Register(container); err != nil {
|
|
| 537 |
- return nil, nil, err |
|
| 538 |
- } |
|
| 539 |
- return container, warnings, nil |
|
| 540 |
-} |
|
| 541 |
- |
|
| 542 |
-// Commit creates a new filesystem image from the current state of a container. |
|
| 543 |
-// The image can optionally be tagged into a repository |
|
| 544 |
-func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
|
|
| 545 |
- // FIXME: freeze the container before copying it to avoid data corruption? |
|
| 546 |
- // FIXME: this shouldn't be in commands. |
|
| 547 |
- if err := container.Mount(); err != nil {
|
|
| 548 |
- return nil, err |
|
| 549 |
- } |
|
| 550 |
- defer container.Unmount() |
|
| 551 |
- |
|
| 552 |
- rwTar, err := container.ExportRw() |
|
| 553 |
- if err != nil {
|
|
| 554 |
- return nil, err |
|
| 555 |
- } |
|
| 556 |
- defer rwTar.Close() |
|
| 557 |
- |
|
| 558 |
- // Create a new image from the container's base layers + a new layer from container changes |
|
| 559 |
- var ( |
|
| 560 |
- containerID, containerImage string |
|
| 561 |
- containerConfig *runconfig.Config |
|
| 562 |
- ) |
|
| 563 |
- if container != nil {
|
|
| 564 |
- containerID = container.ID |
|
| 565 |
- containerImage = container.Image |
|
| 566 |
- containerConfig = container.Config |
|
| 567 |
- } |
|
| 568 |
- img, err := runtime.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config) |
|
| 569 |
- if err != nil {
|
|
| 570 |
- return nil, err |
|
| 571 |
- } |
|
| 572 |
- // Register the image if needed |
|
| 573 |
- if repository != "" {
|
|
| 574 |
- if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil {
|
|
| 575 |
- return img, err |
|
| 576 |
- } |
|
| 577 |
- } |
|
| 578 |
- return img, nil |
|
| 579 |
-} |
|
| 580 |
- |
|
| 581 |
-func getFullName(name string) (string, error) {
|
|
| 582 |
- if name == "" {
|
|
| 583 |
- return "", fmt.Errorf("Container name cannot be empty")
|
|
| 584 |
- } |
|
| 585 |
- if name[0] != '/' {
|
|
| 586 |
- name = "/" + name |
|
| 587 |
- } |
|
| 588 |
- return name, nil |
|
| 589 |
-} |
|
| 590 |
- |
|
| 591 |
-func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
|
| 592 |
- fullName, err := getFullName(name) |
|
| 593 |
- if err != nil {
|
|
| 594 |
- return nil, err |
|
| 595 |
- } |
|
| 596 |
- entity := runtime.containerGraph.Get(fullName) |
|
| 597 |
- if entity == nil {
|
|
| 598 |
- return nil, fmt.Errorf("Could not find entity for %s", name)
|
|
| 599 |
- } |
|
| 600 |
- e := runtime.getContainerElement(entity.ID()) |
|
| 601 |
- if e == nil {
|
|
| 602 |
- return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
|
| 603 |
- } |
|
| 604 |
- return e.Value.(*Container), nil |
|
| 605 |
-} |
|
| 606 |
- |
|
| 607 |
-func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
|
|
| 608 |
- name, err := getFullName(name) |
|
| 609 |
- if err != nil {
|
|
| 610 |
- return nil, err |
|
| 611 |
- } |
|
| 612 |
- children := make(map[string]*Container) |
|
| 613 |
- |
|
| 614 |
- err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
|
| 615 |
- c := runtime.Get(e.ID()) |
|
| 616 |
- if c == nil {
|
|
| 617 |
- return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
|
| 618 |
- } |
|
| 619 |
- children[p] = c |
|
| 620 |
- return nil |
|
| 621 |
- }, 0) |
|
| 622 |
- |
|
| 623 |
- if err != nil {
|
|
| 624 |
- return nil, err |
|
| 625 |
- } |
|
| 626 |
- return children, nil |
|
| 627 |
-} |
|
| 628 |
- |
|
| 629 |
-func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error {
|
|
| 630 |
- fullName := path.Join(parent.Name, alias) |
|
| 631 |
- if !runtime.containerGraph.Exists(fullName) {
|
|
| 632 |
- _, err := runtime.containerGraph.Set(fullName, child.ID) |
|
| 633 |
- return err |
|
| 634 |
- } |
|
| 635 |
- return nil |
|
| 636 |
-} |
|
| 637 |
- |
|
| 638 |
-// FIXME: harmonize with NewGraph() |
|
| 639 |
-func NewRuntime(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
|
| 640 |
- runtime, err := NewRuntimeFromDirectory(config, eng) |
|
| 641 |
- if err != nil {
|
|
| 642 |
- return nil, err |
|
| 643 |
- } |
|
| 644 |
- return runtime, nil |
|
| 645 |
-} |
|
| 646 |
- |
|
| 647 |
-func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
|
| 648 |
- |
|
| 649 |
- // Set the default driver |
|
| 650 |
- graphdriver.DefaultDriver = config.GraphDriver |
|
| 651 |
- |
|
| 652 |
- // Load storage driver |
|
| 653 |
- driver, err := graphdriver.New(config.Root) |
|
| 654 |
- if err != nil {
|
|
| 655 |
- return nil, err |
|
| 656 |
- } |
|
| 657 |
- utils.Debugf("Using graph driver %s", driver)
|
|
| 658 |
- |
|
| 659 |
- runtimeRepo := path.Join(config.Root, "containers") |
|
| 660 |
- |
|
| 661 |
- if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
|
| 662 |
- return nil, err |
|
| 663 |
- } |
|
| 664 |
- |
|
| 665 |
- if ad, ok := driver.(*aufs.Driver); ok {
|
|
| 666 |
- utils.Debugf("Migrating existing containers")
|
|
| 667 |
- if err := ad.Migrate(config.Root, graph.SetupInitLayer); err != nil {
|
|
| 668 |
- return nil, err |
|
| 669 |
- } |
|
| 670 |
- } |
|
| 671 |
- |
|
| 672 |
- utils.Debugf("Creating images graph")
|
|
| 673 |
- g, err := graph.NewGraph(path.Join(config.Root, "graph"), driver) |
|
| 674 |
- if err != nil {
|
|
| 675 |
- return nil, err |
|
| 676 |
- } |
|
| 677 |
- |
|
| 678 |
- // We don't want to use a complex driver like aufs or devmapper |
|
| 679 |
- // for volumes, just a plain filesystem |
|
| 680 |
- volumesDriver, err := graphdriver.GetDriver("vfs", config.Root)
|
|
| 681 |
- if err != nil {
|
|
| 682 |
- return nil, err |
|
| 683 |
- } |
|
| 684 |
- utils.Debugf("Creating volumes graph")
|
|
| 685 |
- volumes, err := graph.NewGraph(path.Join(config.Root, "volumes"), volumesDriver) |
|
| 686 |
- if err != nil {
|
|
| 687 |
- return nil, err |
|
| 688 |
- } |
|
| 689 |
- utils.Debugf("Creating repository list")
|
|
| 690 |
- repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g) |
|
| 691 |
- if err != nil {
|
|
| 692 |
- return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
|
|
| 693 |
- } |
|
| 694 |
- |
|
| 695 |
- if !config.DisableNetwork {
|
|
| 696 |
- job := eng.Job("init_networkdriver")
|
|
| 697 |
- |
|
| 698 |
- job.SetenvBool("EnableIptables", config.EnableIptables)
|
|
| 699 |
- job.SetenvBool("InterContainerCommunication", config.InterContainerCommunication)
|
|
| 700 |
- job.SetenvBool("EnableIpForward", config.EnableIpForward)
|
|
| 701 |
- job.Setenv("BridgeIface", config.BridgeIface)
|
|
| 702 |
- job.Setenv("BridgeIP", config.BridgeIP)
|
|
| 703 |
- job.Setenv("DefaultBindingIP", config.DefaultIp.String())
|
|
| 704 |
- |
|
| 705 |
- if err := job.Run(); err != nil {
|
|
| 706 |
- return nil, err |
|
| 707 |
- } |
|
| 708 |
- } |
|
| 709 |
- |
|
| 710 |
- graphdbPath := path.Join(config.Root, "linkgraph.db") |
|
| 711 |
- graph, err := graphdb.NewSqliteConn(graphdbPath) |
|
| 712 |
- if err != nil {
|
|
| 713 |
- return nil, err |
|
| 714 |
- } |
|
| 715 |
- |
|
| 716 |
- localCopy := path.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION))
|
|
| 717 |
- sysInitPath := utils.DockerInitPath(localCopy) |
|
| 718 |
- if sysInitPath == "" {
|
|
| 719 |
- return nil, fmt.Errorf("Could not locate dockerinit: This usually means docker was built incorrectly. See http://docs.docker.io/en/latest/contributing/devenvironment for official build instructions.")
|
|
| 720 |
- } |
|
| 721 |
- |
|
| 722 |
- if sysInitPath != localCopy {
|
|
| 723 |
- // When we find a suitable dockerinit binary (even if it's our local binary), we copy it into config.Root at localCopy for future use (so that the original can go away without that being a problem, for example during a package upgrade). |
|
| 724 |
- if err := os.Mkdir(path.Dir(localCopy), 0700); err != nil && !os.IsExist(err) {
|
|
| 725 |
- return nil, err |
|
| 726 |
- } |
|
| 727 |
- if _, err := utils.CopyFile(sysInitPath, localCopy); err != nil {
|
|
| 728 |
- return nil, err |
|
| 729 |
- } |
|
| 730 |
- if err := os.Chmod(localCopy, 0700); err != nil {
|
|
| 731 |
- return nil, err |
|
| 732 |
- } |
|
| 733 |
- sysInitPath = localCopy |
|
| 734 |
- } |
|
| 735 |
- |
|
| 736 |
- var ( |
|
| 737 |
- ed execdriver.Driver |
|
| 738 |
- sysInfo = sysinfo.New(false) |
|
| 739 |
- ) |
|
| 740 |
- |
|
| 741 |
- switch config.ExecDriver {
|
|
| 742 |
- case "lxc": |
|
| 743 |
- // we want to five the lxc driver the full docker root because it needs |
|
| 744 |
- // to access and write config and template files in /var/lib/docker/containers/* |
|
| 745 |
- // to be backwards compatible |
|
| 746 |
- ed, err = lxc.NewDriver(config.Root, sysInfo.AppArmor) |
|
| 747 |
- case "native": |
|
| 748 |
- ed, err = native.NewDriver(path.Join(config.Root, "execdriver", "native")) |
|
| 749 |
- default: |
|
| 750 |
- return nil, fmt.Errorf("unknown exec driver %s", config.ExecDriver)
|
|
| 751 |
- } |
|
| 752 |
- if err != nil {
|
|
| 753 |
- return nil, err |
|
| 754 |
- } |
|
| 755 |
- |
|
| 756 |
- runtime := &Runtime{
|
|
| 757 |
- repository: runtimeRepo, |
|
| 758 |
- containers: list.New(), |
|
| 759 |
- graph: g, |
|
| 760 |
- repositories: repositories, |
|
| 761 |
- idIndex: utils.NewTruncIndex(), |
|
| 762 |
- sysInfo: sysInfo, |
|
| 763 |
- volumes: volumes, |
|
| 764 |
- config: config, |
|
| 765 |
- containerGraph: graph, |
|
| 766 |
- driver: driver, |
|
| 767 |
- sysInitPath: sysInitPath, |
|
| 768 |
- execDriver: ed, |
|
| 769 |
- eng: eng, |
|
| 770 |
- } |
|
| 771 |
- |
|
| 772 |
- if err := runtime.restore(); err != nil {
|
|
| 773 |
- return nil, err |
|
| 774 |
- } |
|
| 775 |
- return runtime, nil |
|
| 776 |
-} |
|
| 777 |
- |
|
| 778 |
-func (runtime *Runtime) Close() error {
|
|
| 779 |
- errorsStrings := []string{}
|
|
| 780 |
- if err := portallocator.ReleaseAll(); err != nil {
|
|
| 781 |
- utils.Errorf("portallocator.ReleaseAll(): %s", err)
|
|
| 782 |
- errorsStrings = append(errorsStrings, err.Error()) |
|
| 783 |
- } |
|
| 784 |
- if err := runtime.driver.Cleanup(); err != nil {
|
|
| 785 |
- utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
|
|
| 786 |
- errorsStrings = append(errorsStrings, err.Error()) |
|
| 787 |
- } |
|
| 788 |
- if err := runtime.containerGraph.Close(); err != nil {
|
|
| 789 |
- utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
|
|
| 790 |
- errorsStrings = append(errorsStrings, err.Error()) |
|
| 791 |
- } |
|
| 792 |
- if len(errorsStrings) > 0 {
|
|
| 793 |
- return fmt.Errorf("%s", strings.Join(errorsStrings, ", "))
|
|
| 794 |
- } |
|
| 795 |
- return nil |
|
| 796 |
-} |
|
| 797 |
- |
|
| 798 |
-func (runtime *Runtime) Mount(container *Container) error {
|
|
| 799 |
- dir, err := runtime.driver.Get(container.ID) |
|
| 800 |
- if err != nil {
|
|
| 801 |
- return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
|
|
| 802 |
- } |
|
| 803 |
- if container.basefs == "" {
|
|
| 804 |
- container.basefs = dir |
|
| 805 |
- } else if container.basefs != dir {
|
|
| 806 |
- return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
|
|
| 807 |
- runtime.driver, container.ID, container.basefs, dir) |
|
| 808 |
- } |
|
| 809 |
- return nil |
|
| 810 |
-} |
|
| 811 |
- |
|
| 812 |
-func (runtime *Runtime) Unmount(container *Container) error {
|
|
| 813 |
- runtime.driver.Put(container.ID) |
|
| 814 |
- return nil |
|
| 815 |
-} |
|
| 816 |
- |
|
| 817 |
-func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
|
|
| 818 |
- if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
|
| 819 |
- return differ.Changes(container.ID) |
|
| 820 |
- } |
|
| 821 |
- cDir, err := runtime.driver.Get(container.ID) |
|
| 822 |
- if err != nil {
|
|
| 823 |
- return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
|
| 824 |
- } |
|
| 825 |
- defer runtime.driver.Put(container.ID) |
|
| 826 |
- initDir, err := runtime.driver.Get(container.ID + "-init") |
|
| 827 |
- if err != nil {
|
|
| 828 |
- return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
|
| 829 |
- } |
|
| 830 |
- defer runtime.driver.Put(container.ID + "-init") |
|
| 831 |
- return archive.ChangesDirs(cDir, initDir) |
|
| 832 |
-} |
|
| 833 |
- |
|
| 834 |
-func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
|
| 835 |
- if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
|
| 836 |
- return differ.Diff(container.ID) |
|
| 837 |
- } |
|
| 838 |
- |
|
| 839 |
- changes, err := runtime.Changes(container) |
|
| 840 |
- if err != nil {
|
|
| 841 |
- return nil, err |
|
| 842 |
- } |
|
| 843 |
- |
|
| 844 |
- cDir, err := runtime.driver.Get(container.ID) |
|
| 845 |
- if err != nil {
|
|
| 846 |
- return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
|
| 847 |
- } |
|
| 848 |
- |
|
| 849 |
- archive, err := archive.ExportChanges(cDir, changes) |
|
| 850 |
- if err != nil {
|
|
| 851 |
- return nil, err |
|
| 852 |
- } |
|
| 853 |
- return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 854 |
- err := archive.Close() |
|
| 855 |
- runtime.driver.Put(container.ID) |
|
| 856 |
- return err |
|
| 857 |
- }), nil |
|
| 858 |
-} |
|
| 859 |
- |
|
| 860 |
-func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 861 |
- return runtime.execDriver.Run(c.command, pipes, startCallback) |
|
| 862 |
-} |
|
| 863 |
- |
|
| 864 |
-func (runtime *Runtime) Kill(c *Container, sig int) error {
|
|
| 865 |
- return runtime.execDriver.Kill(c.command, sig) |
|
| 866 |
-} |
|
| 867 |
- |
|
| 868 |
-// Nuke kills all containers then removes all content |
|
| 869 |
-// from the content root, including images, volumes and |
|
| 870 |
-// container filesystems. |
|
| 871 |
-// Again: this will remove your entire docker runtime! |
|
| 872 |
-func (runtime *Runtime) Nuke() error {
|
|
| 873 |
- var wg sync.WaitGroup |
|
| 874 |
- for _, container := range runtime.List() {
|
|
| 875 |
- wg.Add(1) |
|
| 876 |
- go func(c *Container) {
|
|
| 877 |
- c.Kill() |
|
| 878 |
- wg.Done() |
|
| 879 |
- }(container) |
|
| 880 |
- } |
|
| 881 |
- wg.Wait() |
|
| 882 |
- runtime.Close() |
|
| 883 |
- |
|
| 884 |
- return os.RemoveAll(runtime.config.Root) |
|
| 885 |
-} |
|
| 886 |
- |
|
| 887 |
-// FIXME: this is a convenience function for integration tests |
|
| 888 |
-// which need direct access to runtime.graph. |
|
| 889 |
-// Once the tests switch to using engine and jobs, this method |
|
| 890 |
-// can go away. |
|
| 891 |
-func (runtime *Runtime) Graph() *graph.Graph {
|
|
| 892 |
- return runtime.graph |
|
| 893 |
-} |
|
| 894 |
- |
|
| 895 |
-// History is a convenience type for storing a list of containers, |
|
| 896 |
-// ordered by creation date. |
|
| 897 |
-type History []*Container |
|
| 898 |
- |
|
| 899 |
-func (history *History) Len() int {
|
|
| 900 |
- return len(*history) |
|
| 901 |
-} |
|
| 902 |
- |
|
| 903 |
-func (history *History) Less(i, j int) bool {
|
|
| 904 |
- containers := *history |
|
| 905 |
- return containers[j].When().Before(containers[i].When()) |
|
| 906 |
-} |
|
| 907 |
- |
|
| 908 |
-func (history *History) Swap(i, j int) {
|
|
| 909 |
- containers := *history |
|
| 910 |
- tmp := containers[i] |
|
| 911 |
- containers[i] = containers[j] |
|
| 912 |
- containers[j] = tmp |
|
| 913 |
-} |
|
| 914 |
- |
|
| 915 |
-func (history *History) Add(container *Container) {
|
|
| 916 |
- *history = append(*history, container) |
|
| 917 |
- sort.Sort(history) |
|
| 918 |
-} |
| 919 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1161 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/archive" |
|
| 7 |
+ "github.com/dotcloud/docker/engine" |
|
| 8 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 9 |
+ "github.com/dotcloud/docker/graphdriver" |
|
| 10 |
+ "github.com/dotcloud/docker/image" |
|
| 11 |
+ "github.com/dotcloud/docker/links" |
|
| 12 |
+ "github.com/dotcloud/docker/nat" |
|
| 13 |
+ "github.com/dotcloud/docker/runconfig" |
|
| 14 |
+ "github.com/dotcloud/docker/utils" |
|
| 15 |
+ "io" |
|
| 16 |
+ "io/ioutil" |
|
| 17 |
+ "log" |
|
| 18 |
+ "os" |
|
| 19 |
+ "path" |
|
| 20 |
+ "strings" |
|
| 21 |
+ "sync" |
|
| 22 |
+ "syscall" |
|
| 23 |
+ "time" |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" |
|
| 27 |
+ |
|
| 28 |
+var ( |
|
| 29 |
+ ErrNotATTY = errors.New("The PTY is not a file")
|
|
| 30 |
+ ErrNoTTY = errors.New("No PTY found")
|
|
| 31 |
+ ErrContainerStart = errors.New("The container failed to start. Unknown error")
|
|
| 32 |
+ ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
|
|
| 33 |
+) |
|
| 34 |
+ |
|
| 35 |
+type Container struct {
|
|
| 36 |
+ sync.Mutex |
|
| 37 |
+ root string // Path to the "home" of the container, including metadata. |
|
| 38 |
+ basefs string // Path to the graphdriver mountpoint |
|
| 39 |
+ |
|
| 40 |
+ ID string |
|
| 41 |
+ |
|
| 42 |
+ Created time.Time |
|
| 43 |
+ |
|
| 44 |
+ Path string |
|
| 45 |
+ Args []string |
|
| 46 |
+ |
|
| 47 |
+ Config *runconfig.Config |
|
| 48 |
+ State State |
|
| 49 |
+ Image string |
|
| 50 |
+ |
|
| 51 |
+ NetworkSettings *NetworkSettings |
|
| 52 |
+ |
|
| 53 |
+ ResolvConfPath string |
|
| 54 |
+ HostnamePath string |
|
| 55 |
+ HostsPath string |
|
| 56 |
+ Name string |
|
| 57 |
+ Driver string |
|
| 58 |
+ ExecDriver string |
|
| 59 |
+ |
|
| 60 |
+ command *execdriver.Command |
|
| 61 |
+ stdout *utils.WriteBroadcaster |
|
| 62 |
+ stderr *utils.WriteBroadcaster |
|
| 63 |
+ stdin io.ReadCloser |
|
| 64 |
+ stdinPipe io.WriteCloser |
|
| 65 |
+ |
|
| 66 |
+ runtime *Runtime |
|
| 67 |
+ |
|
| 68 |
+ waitLock chan struct{}
|
|
| 69 |
+ Volumes map[string]string |
|
| 70 |
+ // Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. |
|
| 71 |
+ // Easier than migrating older container configs :) |
|
| 72 |
+ VolumesRW map[string]bool |
|
| 73 |
+ hostConfig *runconfig.HostConfig |
|
| 74 |
+ |
|
| 75 |
+ activeLinks map[string]*links.Link |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// FIXME: move deprecated port stuff to nat to clean up the core. |
|
| 79 |
+type PortMapping map[string]string // Deprecated |
|
| 80 |
+ |
|
| 81 |
+type NetworkSettings struct {
|
|
| 82 |
+ IPAddress string |
|
| 83 |
+ IPPrefixLen int |
|
| 84 |
+ Gateway string |
|
| 85 |
+ Bridge string |
|
| 86 |
+ PortMapping map[string]PortMapping // Deprecated |
|
| 87 |
+ Ports nat.PortMap |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
|
| 91 |
+ var outs = engine.NewTable("", 0)
|
|
| 92 |
+ for port, bindings := range settings.Ports {
|
|
| 93 |
+ p, _ := nat.ParsePort(port.Port()) |
|
| 94 |
+ if len(bindings) == 0 {
|
|
| 95 |
+ out := &engine.Env{}
|
|
| 96 |
+ out.SetInt("PublicPort", p)
|
|
| 97 |
+ out.Set("Type", port.Proto())
|
|
| 98 |
+ outs.Add(out) |
|
| 99 |
+ continue |
|
| 100 |
+ } |
|
| 101 |
+ for _, binding := range bindings {
|
|
| 102 |
+ out := &engine.Env{}
|
|
| 103 |
+ h, _ := nat.ParsePort(binding.HostPort) |
|
| 104 |
+ out.SetInt("PrivatePort", p)
|
|
| 105 |
+ out.SetInt("PublicPort", h)
|
|
| 106 |
+ out.Set("Type", port.Proto())
|
|
| 107 |
+ out.Set("IP", binding.HostIp)
|
|
| 108 |
+ outs.Add(out) |
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ return outs |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+// Inject the io.Reader at the given path. Note: do not close the reader |
|
| 115 |
+func (container *Container) Inject(file io.Reader, pth string) error {
|
|
| 116 |
+ if err := container.Mount(); err != nil {
|
|
| 117 |
+ return fmt.Errorf("inject: error mounting container %s: %s", container.ID, err)
|
|
| 118 |
+ } |
|
| 119 |
+ defer container.Unmount() |
|
| 120 |
+ |
|
| 121 |
+ // Return error if path exists |
|
| 122 |
+ destPath := path.Join(container.basefs, pth) |
|
| 123 |
+ if _, err := os.Stat(destPath); err == nil {
|
|
| 124 |
+ // Since err is nil, the path could be stat'd and it exists |
|
| 125 |
+ return fmt.Errorf("%s exists", pth)
|
|
| 126 |
+ } else if !os.IsNotExist(err) {
|
|
| 127 |
+ // Expect err might be that the file doesn't exist, so |
|
| 128 |
+ // if it's some other error, return that. |
|
| 129 |
+ |
|
| 130 |
+ return err |
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ // Make sure the directory exists |
|
| 134 |
+ if err := os.MkdirAll(path.Join(container.basefs, path.Dir(pth)), 0755); err != nil {
|
|
| 135 |
+ return err |
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ dest, err := os.Create(destPath) |
|
| 139 |
+ if err != nil {
|
|
| 140 |
+ return err |
|
| 141 |
+ } |
|
| 142 |
+ defer dest.Close() |
|
| 143 |
+ |
|
| 144 |
+ if _, err := io.Copy(dest, file); err != nil {
|
|
| 145 |
+ return err |
|
| 146 |
+ } |
|
| 147 |
+ return nil |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+func (container *Container) When() time.Time {
|
|
| 151 |
+ return container.Created |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func (container *Container) FromDisk() error {
|
|
| 155 |
+ data, err := ioutil.ReadFile(container.jsonPath()) |
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ return err |
|
| 158 |
+ } |
|
| 159 |
+ // Load container settings |
|
| 160 |
+ // udp broke compat of docker.PortMapping, but it's not used when loading a container, we can skip it |
|
| 161 |
+ if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
|
|
| 162 |
+ return err |
|
| 163 |
+ } |
|
| 164 |
+ return container.readHostConfig() |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+func (container *Container) ToDisk() (err error) {
|
|
| 168 |
+ data, err := json.Marshal(container) |
|
| 169 |
+ if err != nil {
|
|
| 170 |
+ return |
|
| 171 |
+ } |
|
| 172 |
+ err = ioutil.WriteFile(container.jsonPath(), data, 0666) |
|
| 173 |
+ if err != nil {
|
|
| 174 |
+ return |
|
| 175 |
+ } |
|
| 176 |
+ return container.WriteHostConfig() |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+func (container *Container) readHostConfig() error {
|
|
| 180 |
+ container.hostConfig = &runconfig.HostConfig{}
|
|
| 181 |
+ // If the hostconfig file does not exist, do not read it. |
|
| 182 |
+ // (We still have to initialize container.hostConfig, |
|
| 183 |
+ // but that's OK, since we just did that above.) |
|
| 184 |
+ _, err := os.Stat(container.hostConfigPath()) |
|
| 185 |
+ if os.IsNotExist(err) {
|
|
| 186 |
+ return nil |
|
| 187 |
+ } |
|
| 188 |
+ data, err := ioutil.ReadFile(container.hostConfigPath()) |
|
| 189 |
+ if err != nil {
|
|
| 190 |
+ return err |
|
| 191 |
+ } |
|
| 192 |
+ return json.Unmarshal(data, container.hostConfig) |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 195 |
+func (container *Container) WriteHostConfig() (err error) {
|
|
| 196 |
+ data, err := json.Marshal(container.hostConfig) |
|
| 197 |
+ if err != nil {
|
|
| 198 |
+ return |
|
| 199 |
+ } |
|
| 200 |
+ return ioutil.WriteFile(container.hostConfigPath(), data, 0666) |
|
| 201 |
+} |
|
| 202 |
+ |
|
| 203 |
+func (container *Container) generateEnvConfig(env []string) error {
|
|
| 204 |
+ data, err := json.Marshal(env) |
|
| 205 |
+ if err != nil {
|
|
| 206 |
+ return err |
|
| 207 |
+ } |
|
| 208 |
+ p, err := container.EnvConfigPath() |
|
| 209 |
+ if err != nil {
|
|
| 210 |
+ return err |
|
| 211 |
+ } |
|
| 212 |
+ ioutil.WriteFile(p, data, 0600) |
|
| 213 |
+ return nil |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 216 |
+func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
|
| 217 |
+ var cStdout, cStderr io.ReadCloser |
|
| 218 |
+ |
|
| 219 |
+ var nJobs int |
|
| 220 |
+ errors := make(chan error, 3) |
|
| 221 |
+ if stdin != nil && container.Config.OpenStdin {
|
|
| 222 |
+ nJobs += 1 |
|
| 223 |
+ if cStdin, err := container.StdinPipe(); err != nil {
|
|
| 224 |
+ errors <- err |
|
| 225 |
+ } else {
|
|
| 226 |
+ go func() {
|
|
| 227 |
+ utils.Debugf("attach: stdin: begin")
|
|
| 228 |
+ defer utils.Debugf("attach: stdin: end")
|
|
| 229 |
+ // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr |
|
| 230 |
+ if container.Config.StdinOnce && !container.Config.Tty {
|
|
| 231 |
+ defer cStdin.Close() |
|
| 232 |
+ } else {
|
|
| 233 |
+ defer func() {
|
|
| 234 |
+ if cStdout != nil {
|
|
| 235 |
+ cStdout.Close() |
|
| 236 |
+ } |
|
| 237 |
+ if cStderr != nil {
|
|
| 238 |
+ cStderr.Close() |
|
| 239 |
+ } |
|
| 240 |
+ }() |
|
| 241 |
+ } |
|
| 242 |
+ if container.Config.Tty {
|
|
| 243 |
+ _, err = utils.CopyEscapable(cStdin, stdin) |
|
| 244 |
+ } else {
|
|
| 245 |
+ _, err = io.Copy(cStdin, stdin) |
|
| 246 |
+ } |
|
| 247 |
+ if err == io.ErrClosedPipe {
|
|
| 248 |
+ err = nil |
|
| 249 |
+ } |
|
| 250 |
+ if err != nil {
|
|
| 251 |
+ utils.Errorf("attach: stdin: %s", err)
|
|
| 252 |
+ } |
|
| 253 |
+ errors <- err |
|
| 254 |
+ }() |
|
| 255 |
+ } |
|
| 256 |
+ } |
|
| 257 |
+ if stdout != nil {
|
|
| 258 |
+ nJobs += 1 |
|
| 259 |
+ if p, err := container.StdoutPipe(); err != nil {
|
|
| 260 |
+ errors <- err |
|
| 261 |
+ } else {
|
|
| 262 |
+ cStdout = p |
|
| 263 |
+ go func() {
|
|
| 264 |
+ utils.Debugf("attach: stdout: begin")
|
|
| 265 |
+ defer utils.Debugf("attach: stdout: end")
|
|
| 266 |
+ // If we are in StdinOnce mode, then close stdin |
|
| 267 |
+ if container.Config.StdinOnce && stdin != nil {
|
|
| 268 |
+ defer stdin.Close() |
|
| 269 |
+ } |
|
| 270 |
+ if stdinCloser != nil {
|
|
| 271 |
+ defer stdinCloser.Close() |
|
| 272 |
+ } |
|
| 273 |
+ _, err := io.Copy(stdout, cStdout) |
|
| 274 |
+ if err == io.ErrClosedPipe {
|
|
| 275 |
+ err = nil |
|
| 276 |
+ } |
|
| 277 |
+ if err != nil {
|
|
| 278 |
+ utils.Errorf("attach: stdout: %s", err)
|
|
| 279 |
+ } |
|
| 280 |
+ errors <- err |
|
| 281 |
+ }() |
|
| 282 |
+ } |
|
| 283 |
+ } else {
|
|
| 284 |
+ go func() {
|
|
| 285 |
+ if stdinCloser != nil {
|
|
| 286 |
+ defer stdinCloser.Close() |
|
| 287 |
+ } |
|
| 288 |
+ if cStdout, err := container.StdoutPipe(); err != nil {
|
|
| 289 |
+ utils.Errorf("attach: stdout pipe: %s", err)
|
|
| 290 |
+ } else {
|
|
| 291 |
+ io.Copy(&utils.NopWriter{}, cStdout)
|
|
| 292 |
+ } |
|
| 293 |
+ }() |
|
| 294 |
+ } |
|
| 295 |
+ if stderr != nil {
|
|
| 296 |
+ nJobs += 1 |
|
| 297 |
+ if p, err := container.StderrPipe(); err != nil {
|
|
| 298 |
+ errors <- err |
|
| 299 |
+ } else {
|
|
| 300 |
+ cStderr = p |
|
| 301 |
+ go func() {
|
|
| 302 |
+ utils.Debugf("attach: stderr: begin")
|
|
| 303 |
+ defer utils.Debugf("attach: stderr: end")
|
|
| 304 |
+ // If we are in StdinOnce mode, then close stdin |
|
| 305 |
+ if container.Config.StdinOnce && stdin != nil {
|
|
| 306 |
+ defer stdin.Close() |
|
| 307 |
+ } |
|
| 308 |
+ if stdinCloser != nil {
|
|
| 309 |
+ defer stdinCloser.Close() |
|
| 310 |
+ } |
|
| 311 |
+ _, err := io.Copy(stderr, cStderr) |
|
| 312 |
+ if err == io.ErrClosedPipe {
|
|
| 313 |
+ err = nil |
|
| 314 |
+ } |
|
| 315 |
+ if err != nil {
|
|
| 316 |
+ utils.Errorf("attach: stderr: %s", err)
|
|
| 317 |
+ } |
|
| 318 |
+ errors <- err |
|
| 319 |
+ }() |
|
| 320 |
+ } |
|
| 321 |
+ } else {
|
|
| 322 |
+ go func() {
|
|
| 323 |
+ if stdinCloser != nil {
|
|
| 324 |
+ defer stdinCloser.Close() |
|
| 325 |
+ } |
|
| 326 |
+ |
|
| 327 |
+ if cStderr, err := container.StderrPipe(); err != nil {
|
|
| 328 |
+ utils.Errorf("attach: stdout pipe: %s", err)
|
|
| 329 |
+ } else {
|
|
| 330 |
+ io.Copy(&utils.NopWriter{}, cStderr)
|
|
| 331 |
+ } |
|
| 332 |
+ }() |
|
| 333 |
+ } |
|
| 334 |
+ |
|
| 335 |
+ return utils.Go(func() error {
|
|
| 336 |
+ defer func() {
|
|
| 337 |
+ if cStdout != nil {
|
|
| 338 |
+ cStdout.Close() |
|
| 339 |
+ } |
|
| 340 |
+ if cStderr != nil {
|
|
| 341 |
+ cStderr.Close() |
|
| 342 |
+ } |
|
| 343 |
+ }() |
|
| 344 |
+ |
|
| 345 |
+ // FIXME: how to clean up the stdin goroutine without the unwanted side effect |
|
| 346 |
+ // of closing the passed stdin? Add an intermediary io.Pipe? |
|
| 347 |
+ for i := 0; i < nJobs; i += 1 {
|
|
| 348 |
+ utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
|
|
| 349 |
+ if err := <-errors; err != nil {
|
|
| 350 |
+ utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
|
|
| 351 |
+ return err |
|
| 352 |
+ } |
|
| 353 |
+ utils.Debugf("attach: job %d completed successfully", i+1)
|
|
| 354 |
+ } |
|
| 355 |
+ utils.Debugf("attach: all jobs completed successfully")
|
|
| 356 |
+ return nil |
|
| 357 |
+ }) |
|
| 358 |
+} |
|
| 359 |
+ |
|
| 360 |
+func populateCommand(c *Container) {
|
|
| 361 |
+ var ( |
|
| 362 |
+ en *execdriver.Network |
|
| 363 |
+ driverConfig []string |
|
| 364 |
+ ) |
|
| 365 |
+ |
|
| 366 |
+ if !c.Config.NetworkDisabled {
|
|
| 367 |
+ network := c.NetworkSettings |
|
| 368 |
+ en = &execdriver.Network{
|
|
| 369 |
+ Gateway: network.Gateway, |
|
| 370 |
+ Bridge: network.Bridge, |
|
| 371 |
+ IPAddress: network.IPAddress, |
|
| 372 |
+ IPPrefixLen: network.IPPrefixLen, |
|
| 373 |
+ Mtu: c.runtime.config.Mtu, |
|
| 374 |
+ } |
|
| 375 |
+ } |
|
| 376 |
+ |
|
| 377 |
+ if lxcConf := c.hostConfig.LxcConf; lxcConf != nil {
|
|
| 378 |
+ for _, pair := range lxcConf {
|
|
| 379 |
+ driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
|
|
| 380 |
+ } |
|
| 381 |
+ } |
|
| 382 |
+ resources := &execdriver.Resources{
|
|
| 383 |
+ Memory: c.Config.Memory, |
|
| 384 |
+ MemorySwap: c.Config.MemorySwap, |
|
| 385 |
+ CpuShares: c.Config.CpuShares, |
|
| 386 |
+ } |
|
| 387 |
+ c.command = &execdriver.Command{
|
|
| 388 |
+ ID: c.ID, |
|
| 389 |
+ Privileged: c.hostConfig.Privileged, |
|
| 390 |
+ Rootfs: c.RootfsPath(), |
|
| 391 |
+ InitPath: "/.dockerinit", |
|
| 392 |
+ Entrypoint: c.Path, |
|
| 393 |
+ Arguments: c.Args, |
|
| 394 |
+ WorkingDir: c.Config.WorkingDir, |
|
| 395 |
+ Network: en, |
|
| 396 |
+ Tty: c.Config.Tty, |
|
| 397 |
+ User: c.Config.User, |
|
| 398 |
+ Config: driverConfig, |
|
| 399 |
+ Resources: resources, |
|
| 400 |
+ } |
|
| 401 |
+ c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
|
| 402 |
+} |
|
| 403 |
+ |
|
| 404 |
+func (container *Container) Start() (err error) {
|
|
| 405 |
+ container.Lock() |
|
| 406 |
+ defer container.Unlock() |
|
| 407 |
+ |
|
| 408 |
+ if container.State.IsRunning() {
|
|
| 409 |
+ return fmt.Errorf("The container %s is already running.", container.ID)
|
|
| 410 |
+ } |
|
| 411 |
+ |
|
| 412 |
+ defer func() {
|
|
| 413 |
+ if err != nil {
|
|
| 414 |
+ container.cleanup() |
|
| 415 |
+ } |
|
| 416 |
+ }() |
|
| 417 |
+ |
|
| 418 |
+ if err := container.Mount(); err != nil {
|
|
| 419 |
+ return err |
|
| 420 |
+ } |
|
| 421 |
+ |
|
| 422 |
+ if container.runtime.config.DisableNetwork {
|
|
| 423 |
+ container.Config.NetworkDisabled = true |
|
| 424 |
+ container.buildHostnameAndHostsFiles("127.0.1.1")
|
|
| 425 |
+ } else {
|
|
| 426 |
+ if err := container.allocateNetwork(); err != nil {
|
|
| 427 |
+ return err |
|
| 428 |
+ } |
|
| 429 |
+ container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) |
|
| 430 |
+ } |
|
| 431 |
+ |
|
| 432 |
+ // Make sure the config is compatible with the current kernel |
|
| 433 |
+ if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit {
|
|
| 434 |
+ log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
|
|
| 435 |
+ container.Config.Memory = 0 |
|
| 436 |
+ } |
|
| 437 |
+ if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit {
|
|
| 438 |
+ log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
|
| 439 |
+ container.Config.MemorySwap = -1 |
|
| 440 |
+ } |
|
| 441 |
+ |
|
| 442 |
+ if container.runtime.sysInfo.IPv4ForwardingDisabled {
|
|
| 443 |
+ log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
|
|
| 444 |
+ } |
|
| 445 |
+ |
|
| 446 |
+ if err := prepareVolumesForContainer(container); err != nil {
|
|
| 447 |
+ return err |
|
| 448 |
+ } |
|
| 449 |
+ |
|
| 450 |
+ // Setup environment |
|
| 451 |
+ env := []string{
|
|
| 452 |
+ "HOME=/", |
|
| 453 |
+ "PATH=" + DefaultPathEnv, |
|
| 454 |
+ "HOSTNAME=" + container.Config.Hostname, |
|
| 455 |
+ } |
|
| 456 |
+ |
|
| 457 |
+ if container.Config.Tty {
|
|
| 458 |
+ env = append(env, "TERM=xterm") |
|
| 459 |
+ } |
|
| 460 |
+ |
|
| 461 |
+ // Init any links between the parent and children |
|
| 462 |
+ runtime := container.runtime |
|
| 463 |
+ |
|
| 464 |
+ children, err := runtime.Children(container.Name) |
|
| 465 |
+ if err != nil {
|
|
| 466 |
+ return err |
|
| 467 |
+ } |
|
| 468 |
+ |
|
| 469 |
+ if len(children) > 0 {
|
|
| 470 |
+ container.activeLinks = make(map[string]*links.Link, len(children)) |
|
| 471 |
+ |
|
| 472 |
+ // If we encounter an error make sure that we rollback any network |
|
| 473 |
+ // config and ip table changes |
|
| 474 |
+ rollback := func() {
|
|
| 475 |
+ for _, link := range container.activeLinks {
|
|
| 476 |
+ link.Disable() |
|
| 477 |
+ } |
|
| 478 |
+ container.activeLinks = nil |
|
| 479 |
+ } |
|
| 480 |
+ |
|
| 481 |
+ for linkAlias, child := range children {
|
|
| 482 |
+ if !child.State.IsRunning() {
|
|
| 483 |
+ return fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
|
|
| 484 |
+ } |
|
| 485 |
+ |
|
| 486 |
+ link, err := links.NewLink( |
|
| 487 |
+ container.NetworkSettings.IPAddress, |
|
| 488 |
+ child.NetworkSettings.IPAddress, |
|
| 489 |
+ linkAlias, |
|
| 490 |
+ child.Config.Env, |
|
| 491 |
+ child.Config.ExposedPorts, |
|
| 492 |
+ runtime.eng) |
|
| 493 |
+ |
|
| 494 |
+ if err != nil {
|
|
| 495 |
+ rollback() |
|
| 496 |
+ return err |
|
| 497 |
+ } |
|
| 498 |
+ |
|
| 499 |
+ container.activeLinks[link.Alias()] = link |
|
| 500 |
+ if err := link.Enable(); err != nil {
|
|
| 501 |
+ rollback() |
|
| 502 |
+ return err |
|
| 503 |
+ } |
|
| 504 |
+ |
|
| 505 |
+ for _, envVar := range link.ToEnv() {
|
|
| 506 |
+ env = append(env, envVar) |
|
| 507 |
+ } |
|
| 508 |
+ } |
|
| 509 |
+ } |
|
| 510 |
+ |
|
| 511 |
+ // because the env on the container can override certain default values |
|
| 512 |
+ // we need to replace the 'env' keys where they match and append anything |
|
| 513 |
+ // else. |
|
| 514 |
+ env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env) |
|
| 515 |
+ if err := container.generateEnvConfig(env); err != nil {
|
|
| 516 |
+ return err |
|
| 517 |
+ } |
|
| 518 |
+ |
|
| 519 |
+ if container.Config.WorkingDir != "" {
|
|
| 520 |
+ container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) |
|
| 521 |
+ if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil {
|
|
| 522 |
+ return nil |
|
| 523 |
+ } |
|
| 524 |
+ } |
|
| 525 |
+ |
|
| 526 |
+ envPath, err := container.EnvConfigPath() |
|
| 527 |
+ if err != nil {
|
|
| 528 |
+ return err |
|
| 529 |
+ } |
|
| 530 |
+ |
|
| 531 |
+ if err := mountVolumesForContainer(container, envPath); err != nil {
|
|
| 532 |
+ return err |
|
| 533 |
+ } |
|
| 534 |
+ |
|
| 535 |
+ populateCommand(container) |
|
| 536 |
+ container.command.Env = env |
|
| 537 |
+ |
|
| 538 |
+ // Setup logging of stdout and stderr to disk |
|
| 539 |
+ if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
|
| 540 |
+ return err |
|
| 541 |
+ } |
|
| 542 |
+ if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
|
|
| 543 |
+ return err |
|
| 544 |
+ } |
|
| 545 |
+ container.waitLock = make(chan struct{})
|
|
| 546 |
+ |
|
| 547 |
+ callbackLock := make(chan struct{})
|
|
| 548 |
+ callback := func(command *execdriver.Command) {
|
|
| 549 |
+ container.State.SetRunning(command.Pid()) |
|
| 550 |
+ if command.Tty {
|
|
| 551 |
+ // The callback is called after the process Start() |
|
| 552 |
+ // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace |
|
| 553 |
+ // which we close here. |
|
| 554 |
+ if c, ok := command.Stdout.(io.Closer); ok {
|
|
| 555 |
+ c.Close() |
|
| 556 |
+ } |
|
| 557 |
+ } |
|
| 558 |
+ if err := container.ToDisk(); err != nil {
|
|
| 559 |
+ utils.Debugf("%s", err)
|
|
| 560 |
+ } |
|
| 561 |
+ close(callbackLock) |
|
| 562 |
+ } |
|
| 563 |
+ |
|
| 564 |
+ // We use a callback here instead of a goroutine and an chan for |
|
| 565 |
+ // syncronization purposes |
|
| 566 |
+ cErr := utils.Go(func() error { return container.monitor(callback) })
|
|
| 567 |
+ |
|
| 568 |
+ // Start should not return until the process is actually running |
|
| 569 |
+ select {
|
|
| 570 |
+ case <-callbackLock: |
|
| 571 |
+ case err := <-cErr: |
|
| 572 |
+ return err |
|
| 573 |
+ } |
|
| 574 |
+ return nil |
|
| 575 |
+} |
|
| 576 |
+ |
|
| 577 |
+func (container *Container) Run() error {
|
|
| 578 |
+ if err := container.Start(); err != nil {
|
|
| 579 |
+ return err |
|
| 580 |
+ } |
|
| 581 |
+ container.Wait() |
|
| 582 |
+ return nil |
|
| 583 |
+} |
|
| 584 |
+ |
|
| 585 |
+func (container *Container) Output() (output []byte, err error) {
|
|
| 586 |
+ pipe, err := container.StdoutPipe() |
|
| 587 |
+ if err != nil {
|
|
| 588 |
+ return nil, err |
|
| 589 |
+ } |
|
| 590 |
+ defer pipe.Close() |
|
| 591 |
+ if err := container.Start(); err != nil {
|
|
| 592 |
+ return nil, err |
|
| 593 |
+ } |
|
| 594 |
+ output, err = ioutil.ReadAll(pipe) |
|
| 595 |
+ container.Wait() |
|
| 596 |
+ return output, err |
|
| 597 |
+} |
|
| 598 |
+ |
|
| 599 |
+// Container.StdinPipe returns a WriteCloser which can be used to feed data |
|
| 600 |
+// to the standard input of the container's active process. |
|
| 601 |
+// Container.StdoutPipe and Container.StderrPipe each return a ReadCloser |
|
| 602 |
+// which can be used to retrieve the standard output (and error) generated |
|
| 603 |
+// by the container's active process. The output (and error) are actually |
|
| 604 |
+// copied and delivered to all StdoutPipe and StderrPipe consumers, using |
|
| 605 |
+// a kind of "broadcaster". |
|
| 606 |
+ |
|
| 607 |
+func (container *Container) StdinPipe() (io.WriteCloser, error) {
|
|
| 608 |
+ return container.stdinPipe, nil |
|
| 609 |
+} |
|
| 610 |
+ |
|
| 611 |
+func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
|
| 612 |
+ reader, writer := io.Pipe() |
|
| 613 |
+ container.stdout.AddWriter(writer, "") |
|
| 614 |
+ return utils.NewBufReader(reader), nil |
|
| 615 |
+} |
|
| 616 |
+ |
|
| 617 |
+func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
|
| 618 |
+ reader, writer := io.Pipe() |
|
| 619 |
+ container.stderr.AddWriter(writer, "") |
|
| 620 |
+ return utils.NewBufReader(reader), nil |
|
| 621 |
+} |
|
| 622 |
+ |
|
| 623 |
+func (container *Container) buildHostnameAndHostsFiles(IP string) {
|
|
| 624 |
+ container.HostnamePath = path.Join(container.root, "hostname") |
|
| 625 |
+ ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) |
|
| 626 |
+ |
|
| 627 |
+ hostsContent := []byte(` |
|
| 628 |
+127.0.0.1 localhost |
|
| 629 |
+::1 localhost ip6-localhost ip6-loopback |
|
| 630 |
+fe00::0 ip6-localnet |
|
| 631 |
+ff00::0 ip6-mcastprefix |
|
| 632 |
+ff02::1 ip6-allnodes |
|
| 633 |
+ff02::2 ip6-allrouters |
|
| 634 |
+`) |
|
| 635 |
+ |
|
| 636 |
+ container.HostsPath = path.Join(container.root, "hosts") |
|
| 637 |
+ |
|
| 638 |
+ if container.Config.Domainname != "" {
|
|
| 639 |
+ hostsContent = append([]byte(fmt.Sprintf("%s\t%s.%s %s\n", IP, container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
|
|
| 640 |
+ } else if !container.Config.NetworkDisabled {
|
|
| 641 |
+ hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
|
|
| 642 |
+ } |
|
| 643 |
+ |
|
| 644 |
+ ioutil.WriteFile(container.HostsPath, hostsContent, 0644) |
|
| 645 |
+} |
|
| 646 |
+ |
|
| 647 |
+func (container *Container) allocateNetwork() error {
|
|
| 648 |
+ if container.Config.NetworkDisabled {
|
|
| 649 |
+ return nil |
|
| 650 |
+ } |
|
| 651 |
+ |
|
| 652 |
+ var ( |
|
| 653 |
+ env *engine.Env |
|
| 654 |
+ err error |
|
| 655 |
+ eng = container.runtime.eng |
|
| 656 |
+ ) |
|
| 657 |
+ |
|
| 658 |
+ if container.State.IsGhost() {
|
|
| 659 |
+ if container.runtime.config.DisableNetwork {
|
|
| 660 |
+ env = &engine.Env{}
|
|
| 661 |
+ } else {
|
|
| 662 |
+ currentIP := container.NetworkSettings.IPAddress |
|
| 663 |
+ |
|
| 664 |
+ job := eng.Job("allocate_interface", container.ID)
|
|
| 665 |
+ if currentIP != "" {
|
|
| 666 |
+ job.Setenv("RequestIP", currentIP)
|
|
| 667 |
+ } |
|
| 668 |
+ |
|
| 669 |
+ env, err = job.Stdout.AddEnv() |
|
| 670 |
+ if err != nil {
|
|
| 671 |
+ return err |
|
| 672 |
+ } |
|
| 673 |
+ |
|
| 674 |
+ if err := job.Run(); err != nil {
|
|
| 675 |
+ return err |
|
| 676 |
+ } |
|
| 677 |
+ } |
|
| 678 |
+ } else {
|
|
| 679 |
+ job := eng.Job("allocate_interface", container.ID)
|
|
| 680 |
+ env, err = job.Stdout.AddEnv() |
|
| 681 |
+ if err != nil {
|
|
| 682 |
+ return err |
|
| 683 |
+ } |
|
| 684 |
+ if err := job.Run(); err != nil {
|
|
| 685 |
+ return err |
|
| 686 |
+ } |
|
| 687 |
+ } |
|
| 688 |
+ |
|
| 689 |
+ if container.Config.PortSpecs != nil {
|
|
| 690 |
+ utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
|
|
| 691 |
+ if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
|
|
| 692 |
+ return err |
|
| 693 |
+ } |
|
| 694 |
+ container.Config.PortSpecs = nil |
|
| 695 |
+ if err := container.WriteHostConfig(); err != nil {
|
|
| 696 |
+ return err |
|
| 697 |
+ } |
|
| 698 |
+ } |
|
| 699 |
+ |
|
| 700 |
+ var ( |
|
| 701 |
+ portSpecs = make(nat.PortSet) |
|
| 702 |
+ bindings = make(nat.PortMap) |
|
| 703 |
+ ) |
|
| 704 |
+ |
|
| 705 |
+ if !container.State.IsGhost() {
|
|
| 706 |
+ if container.Config.ExposedPorts != nil {
|
|
| 707 |
+ portSpecs = container.Config.ExposedPorts |
|
| 708 |
+ } |
|
| 709 |
+ if container.hostConfig.PortBindings != nil {
|
|
| 710 |
+ bindings = container.hostConfig.PortBindings |
|
| 711 |
+ } |
|
| 712 |
+ } else {
|
|
| 713 |
+ if container.NetworkSettings.Ports != nil {
|
|
| 714 |
+ for port, binding := range container.NetworkSettings.Ports {
|
|
| 715 |
+ portSpecs[port] = struct{}{}
|
|
| 716 |
+ bindings[port] = binding |
|
| 717 |
+ } |
|
| 718 |
+ } |
|
| 719 |
+ } |
|
| 720 |
+ |
|
| 721 |
+ container.NetworkSettings.PortMapping = nil |
|
| 722 |
+ |
|
| 723 |
+ for port := range portSpecs {
|
|
| 724 |
+ binding := bindings[port] |
|
| 725 |
+ if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
|
| 726 |
+ binding = append(binding, nat.PortBinding{})
|
|
| 727 |
+ } |
|
| 728 |
+ |
|
| 729 |
+ for i := 0; i < len(binding); i++ {
|
|
| 730 |
+ b := binding[i] |
|
| 731 |
+ |
|
| 732 |
+ portJob := eng.Job("allocate_port", container.ID)
|
|
| 733 |
+ portJob.Setenv("HostIP", b.HostIp)
|
|
| 734 |
+ portJob.Setenv("HostPort", b.HostPort)
|
|
| 735 |
+ portJob.Setenv("Proto", port.Proto())
|
|
| 736 |
+ portJob.Setenv("ContainerPort", port.Port())
|
|
| 737 |
+ |
|
| 738 |
+ portEnv, err := portJob.Stdout.AddEnv() |
|
| 739 |
+ if err != nil {
|
|
| 740 |
+ return err |
|
| 741 |
+ } |
|
| 742 |
+ if err := portJob.Run(); err != nil {
|
|
| 743 |
+ eng.Job("release_interface", container.ID).Run()
|
|
| 744 |
+ return err |
|
| 745 |
+ } |
|
| 746 |
+ b.HostIp = portEnv.Get("HostIP")
|
|
| 747 |
+ b.HostPort = portEnv.Get("HostPort")
|
|
| 748 |
+ |
|
| 749 |
+ binding[i] = b |
|
| 750 |
+ } |
|
| 751 |
+ bindings[port] = binding |
|
| 752 |
+ } |
|
| 753 |
+ container.WriteHostConfig() |
|
| 754 |
+ |
|
| 755 |
+ container.NetworkSettings.Ports = bindings |
|
| 756 |
+ |
|
| 757 |
+ container.NetworkSettings.Bridge = env.Get("Bridge")
|
|
| 758 |
+ container.NetworkSettings.IPAddress = env.Get("IP")
|
|
| 759 |
+ container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
|
|
| 760 |
+ container.NetworkSettings.Gateway = env.Get("Gateway")
|
|
| 761 |
+ |
|
| 762 |
+ return nil |
|
| 763 |
+} |
|
| 764 |
+ |
|
| 765 |
+func (container *Container) releaseNetwork() {
|
|
| 766 |
+ if container.Config.NetworkDisabled {
|
|
| 767 |
+ return |
|
| 768 |
+ } |
|
| 769 |
+ eng := container.runtime.eng |
|
| 770 |
+ |
|
| 771 |
+ eng.Job("release_interface", container.ID).Run()
|
|
| 772 |
+ container.NetworkSettings = &NetworkSettings{}
|
|
| 773 |
+} |
|
| 774 |
+ |
|
| 775 |
+func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|
| 776 |
+ var ( |
|
| 777 |
+ err error |
|
| 778 |
+ exitCode int |
|
| 779 |
+ ) |
|
| 780 |
+ |
|
| 781 |
+ pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin) |
|
| 782 |
+ exitCode, err = container.runtime.Run(container, pipes, callback) |
|
| 783 |
+ if err != nil {
|
|
| 784 |
+ utils.Errorf("Error running container: %s", err)
|
|
| 785 |
+ } |
|
| 786 |
+ |
|
| 787 |
+ if container.runtime.srv.IsRunning() {
|
|
| 788 |
+ container.State.SetStopped(exitCode) |
|
| 789 |
+ |
|
| 790 |
+ // FIXME: there is a race condition here which causes this to fail during the unit tests. |
|
| 791 |
+ // If another goroutine was waiting for Wait() to return before removing the container's root |
|
| 792 |
+ // from the filesystem... At this point it may already have done so. |
|
| 793 |
+ // This is because State.setStopped() has already been called, and has caused Wait() |
|
| 794 |
+ // to return. |
|
| 795 |
+ // FIXME: why are we serializing running state to disk in the first place? |
|
| 796 |
+ //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
|
| 797 |
+ if err := container.ToDisk(); err != nil {
|
|
| 798 |
+ utils.Errorf("Error dumping container state to disk: %s\n", err)
|
|
| 799 |
+ } |
|
| 800 |
+ } |
|
| 801 |
+ |
|
| 802 |
+ // Cleanup |
|
| 803 |
+ container.cleanup() |
|
| 804 |
+ |
|
| 805 |
+ // Re-create a brand new stdin pipe once the container exited |
|
| 806 |
+ if container.Config.OpenStdin {
|
|
| 807 |
+ container.stdin, container.stdinPipe = io.Pipe() |
|
| 808 |
+ } |
|
| 809 |
+ |
|
| 810 |
+ if container.runtime != nil && container.runtime.srv != nil {
|
|
| 811 |
+ container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
|
| 812 |
+ } |
|
| 813 |
+ |
|
| 814 |
+ close(container.waitLock) |
|
| 815 |
+ |
|
| 816 |
+ return err |
|
| 817 |
+} |
|
| 818 |
+ |
|
| 819 |
+func (container *Container) cleanup() {
|
|
| 820 |
+ container.releaseNetwork() |
|
| 821 |
+ |
|
| 822 |
+ // Disable all active links |
|
| 823 |
+ if container.activeLinks != nil {
|
|
| 824 |
+ for _, link := range container.activeLinks {
|
|
| 825 |
+ link.Disable() |
|
| 826 |
+ } |
|
| 827 |
+ } |
|
| 828 |
+ if container.Config.OpenStdin {
|
|
| 829 |
+ if err := container.stdin.Close(); err != nil {
|
|
| 830 |
+ utils.Errorf("%s: Error close stdin: %s", container.ID, err)
|
|
| 831 |
+ } |
|
| 832 |
+ } |
|
| 833 |
+ if err := container.stdout.CloseWriters(); err != nil {
|
|
| 834 |
+ utils.Errorf("%s: Error close stdout: %s", container.ID, err)
|
|
| 835 |
+ } |
|
| 836 |
+ if err := container.stderr.CloseWriters(); err != nil {
|
|
| 837 |
+ utils.Errorf("%s: Error close stderr: %s", container.ID, err)
|
|
| 838 |
+ } |
|
| 839 |
+ if container.command != nil && container.command.Terminal != nil {
|
|
| 840 |
+ if err := container.command.Terminal.Close(); err != nil {
|
|
| 841 |
+ utils.Errorf("%s: Error closing terminal: %s", container.ID, err)
|
|
| 842 |
+ } |
|
| 843 |
+ } |
|
| 844 |
+ |
|
| 845 |
+ unmountVolumesForContainer(container) |
|
| 846 |
+ |
|
| 847 |
+ if err := container.Unmount(); err != nil {
|
|
| 848 |
+ log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
|
|
| 849 |
+ } |
|
| 850 |
+} |
|
| 851 |
+ |
|
| 852 |
+func (container *Container) KillSig(sig int) error {
|
|
| 853 |
+ container.Lock() |
|
| 854 |
+ defer container.Unlock() |
|
| 855 |
+ |
|
| 856 |
+ if !container.State.IsRunning() {
|
|
| 857 |
+ return nil |
|
| 858 |
+ } |
|
| 859 |
+ return container.runtime.Kill(container, sig) |
|
| 860 |
+} |
|
| 861 |
+ |
|
| 862 |
+func (container *Container) Kill() error {
|
|
| 863 |
+ if !container.State.IsRunning() {
|
|
| 864 |
+ return nil |
|
| 865 |
+ } |
|
| 866 |
+ |
|
| 867 |
+ // 1. Send SIGKILL |
|
| 868 |
+ if err := container.KillSig(9); err != nil {
|
|
| 869 |
+ return err |
|
| 870 |
+ } |
|
| 871 |
+ |
|
| 872 |
+ // 2. Wait for the process to die, in last resort, try to kill the process directly |
|
| 873 |
+ if err := container.WaitTimeout(10 * time.Second); err != nil {
|
|
| 874 |
+ if container.command == nil {
|
|
| 875 |
+ return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID))
|
|
| 876 |
+ } |
|
| 877 |
+ log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID))
|
|
| 878 |
+ if err := container.runtime.Kill(container, 9); err != nil {
|
|
| 879 |
+ return err |
|
| 880 |
+ } |
|
| 881 |
+ } |
|
| 882 |
+ |
|
| 883 |
+ container.Wait() |
|
| 884 |
+ return nil |
|
| 885 |
+} |
|
| 886 |
+ |
|
| 887 |
+func (container *Container) Stop(seconds int) error {
|
|
| 888 |
+ if !container.State.IsRunning() {
|
|
| 889 |
+ return nil |
|
| 890 |
+ } |
|
| 891 |
+ |
|
| 892 |
+ // 1. Send a SIGTERM |
|
| 893 |
+ if err := container.KillSig(15); err != nil {
|
|
| 894 |
+ utils.Debugf("Error sending kill SIGTERM: %s", err)
|
|
| 895 |
+ log.Print("Failed to send SIGTERM to the process, force killing")
|
|
| 896 |
+ if err := container.KillSig(9); err != nil {
|
|
| 897 |
+ return err |
|
| 898 |
+ } |
|
| 899 |
+ } |
|
| 900 |
+ |
|
| 901 |
+ // 2. Wait for the process to exit on its own |
|
| 902 |
+ if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
|
|
| 903 |
+ log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
|
|
| 904 |
+ // 3. If it doesn't, then send SIGKILL |
|
| 905 |
+ if err := container.Kill(); err != nil {
|
|
| 906 |
+ return err |
|
| 907 |
+ } |
|
| 908 |
+ } |
|
| 909 |
+ return nil |
|
| 910 |
+} |
|
| 911 |
+ |
|
| 912 |
+func (container *Container) Restart(seconds int) error {
|
|
| 913 |
+ // Avoid unnecessarily unmounting and then directly mounting |
|
| 914 |
+ // the container when the container stops and then starts |
|
| 915 |
+ // again |
|
| 916 |
+ if err := container.Mount(); err == nil {
|
|
| 917 |
+ defer container.Unmount() |
|
| 918 |
+ } |
|
| 919 |
+ |
|
| 920 |
+ if err := container.Stop(seconds); err != nil {
|
|
| 921 |
+ return err |
|
| 922 |
+ } |
|
| 923 |
+ return container.Start() |
|
| 924 |
+} |
|
| 925 |
+ |
|
| 926 |
+// Wait blocks until the container stops running, then returns its exit code. |
|
| 927 |
+func (container *Container) Wait() int {
|
|
| 928 |
+ <-container.waitLock |
|
| 929 |
+ return container.State.GetExitCode() |
|
| 930 |
+} |
|
| 931 |
+ |
|
| 932 |
+func (container *Container) Resize(h, w int) error {
|
|
| 933 |
+ return container.command.Terminal.Resize(h, w) |
|
| 934 |
+} |
|
| 935 |
+ |
|
| 936 |
+func (container *Container) ExportRw() (archive.Archive, error) {
|
|
| 937 |
+ if err := container.Mount(); err != nil {
|
|
| 938 |
+ return nil, err |
|
| 939 |
+ } |
|
| 940 |
+ if container.runtime == nil {
|
|
| 941 |
+ return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
|
|
| 942 |
+ } |
|
| 943 |
+ archive, err := container.runtime.Diff(container) |
|
| 944 |
+ if err != nil {
|
|
| 945 |
+ container.Unmount() |
|
| 946 |
+ return nil, err |
|
| 947 |
+ } |
|
| 948 |
+ return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 949 |
+ err := archive.Close() |
|
| 950 |
+ container.Unmount() |
|
| 951 |
+ return err |
|
| 952 |
+ }), nil |
|
| 953 |
+} |
|
| 954 |
+ |
|
| 955 |
+func (container *Container) Export() (archive.Archive, error) {
|
|
| 956 |
+ if err := container.Mount(); err != nil {
|
|
| 957 |
+ return nil, err |
|
| 958 |
+ } |
|
| 959 |
+ |
|
| 960 |
+ archive, err := archive.Tar(container.basefs, archive.Uncompressed) |
|
| 961 |
+ if err != nil {
|
|
| 962 |
+ container.Unmount() |
|
| 963 |
+ return nil, err |
|
| 964 |
+ } |
|
| 965 |
+ return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 966 |
+ err := archive.Close() |
|
| 967 |
+ container.Unmount() |
|
| 968 |
+ return err |
|
| 969 |
+ }), nil |
|
| 970 |
+} |
|
| 971 |
+ |
|
| 972 |
+func (container *Container) WaitTimeout(timeout time.Duration) error {
|
|
| 973 |
+ done := make(chan bool) |
|
| 974 |
+ go func() {
|
|
| 975 |
+ container.Wait() |
|
| 976 |
+ done <- true |
|
| 977 |
+ }() |
|
| 978 |
+ |
|
| 979 |
+ select {
|
|
| 980 |
+ case <-time.After(timeout): |
|
| 981 |
+ return fmt.Errorf("Timed Out")
|
|
| 982 |
+ case <-done: |
|
| 983 |
+ return nil |
|
| 984 |
+ } |
|
| 985 |
+} |
|
| 986 |
+ |
|
| 987 |
+func (container *Container) Mount() error {
|
|
| 988 |
+ return container.runtime.Mount(container) |
|
| 989 |
+} |
|
| 990 |
+ |
|
| 991 |
+func (container *Container) Changes() ([]archive.Change, error) {
|
|
| 992 |
+ return container.runtime.Changes(container) |
|
| 993 |
+} |
|
| 994 |
+ |
|
| 995 |
+func (container *Container) GetImage() (*image.Image, error) {
|
|
| 996 |
+ if container.runtime == nil {
|
|
| 997 |
+ return nil, fmt.Errorf("Can't get image of unregistered container")
|
|
| 998 |
+ } |
|
| 999 |
+ return container.runtime.graph.Get(container.Image) |
|
| 1000 |
+} |
|
| 1001 |
+ |
|
| 1002 |
+func (container *Container) Unmount() error {
|
|
| 1003 |
+ return container.runtime.Unmount(container) |
|
| 1004 |
+} |
|
| 1005 |
+ |
|
| 1006 |
+func (container *Container) logPath(name string) string {
|
|
| 1007 |
+ return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
|
|
| 1008 |
+} |
|
| 1009 |
+ |
|
| 1010 |
+func (container *Container) ReadLog(name string) (io.Reader, error) {
|
|
| 1011 |
+ return os.Open(container.logPath(name)) |
|
| 1012 |
+} |
|
| 1013 |
+ |
|
| 1014 |
+func (container *Container) hostConfigPath() string {
|
|
| 1015 |
+ return path.Join(container.root, "hostconfig.json") |
|
| 1016 |
+} |
|
| 1017 |
+ |
|
| 1018 |
+func (container *Container) jsonPath() string {
|
|
| 1019 |
+ return path.Join(container.root, "config.json") |
|
| 1020 |
+} |
|
| 1021 |
+ |
|
| 1022 |
+func (container *Container) EnvConfigPath() (string, error) {
|
|
| 1023 |
+ p := path.Join(container.root, "config.env") |
|
| 1024 |
+ if _, err := os.Stat(p); err != nil {
|
|
| 1025 |
+ if os.IsNotExist(err) {
|
|
| 1026 |
+ f, err := os.Create(p) |
|
| 1027 |
+ if err != nil {
|
|
| 1028 |
+ return "", err |
|
| 1029 |
+ } |
|
| 1030 |
+ f.Close() |
|
| 1031 |
+ } else {
|
|
| 1032 |
+ return "", err |
|
| 1033 |
+ } |
|
| 1034 |
+ } |
|
| 1035 |
+ return p, nil |
|
| 1036 |
+} |
|
| 1037 |
+ |
|
| 1038 |
+// This method must be exported to be used from the lxc template |
|
| 1039 |
+// This directory is only usable when the container is running |
|
| 1040 |
+func (container *Container) RootfsPath() string {
|
|
| 1041 |
+ return path.Join(container.root, "root") |
|
| 1042 |
+} |
|
| 1043 |
+ |
|
| 1044 |
+// This is the stand-alone version of the root fs, without any additional mounts. |
|
| 1045 |
+// This directory is usable whenever the container is mounted (and not unmounted) |
|
| 1046 |
+func (container *Container) BasefsPath() string {
|
|
| 1047 |
+ return container.basefs |
|
| 1048 |
+} |
|
| 1049 |
+ |
|
| 1050 |
+func validateID(id string) error {
|
|
| 1051 |
+ if id == "" {
|
|
| 1052 |
+ return fmt.Errorf("Invalid empty id")
|
|
| 1053 |
+ } |
|
| 1054 |
+ return nil |
|
| 1055 |
+} |
|
| 1056 |
+ |
|
| 1057 |
+// GetSize, return real size, virtual size |
|
| 1058 |
+func (container *Container) GetSize() (int64, int64) {
|
|
| 1059 |
+ var ( |
|
| 1060 |
+ sizeRw, sizeRootfs int64 |
|
| 1061 |
+ err error |
|
| 1062 |
+ driver = container.runtime.driver |
|
| 1063 |
+ ) |
|
| 1064 |
+ |
|
| 1065 |
+ if err := container.Mount(); err != nil {
|
|
| 1066 |
+ utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
|
|
| 1067 |
+ return sizeRw, sizeRootfs |
|
| 1068 |
+ } |
|
| 1069 |
+ defer container.Unmount() |
|
| 1070 |
+ |
|
| 1071 |
+ if differ, ok := container.runtime.driver.(graphdriver.Differ); ok {
|
|
| 1072 |
+ sizeRw, err = differ.DiffSize(container.ID) |
|
| 1073 |
+ if err != nil {
|
|
| 1074 |
+ utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
|
|
| 1075 |
+ // FIXME: GetSize should return an error. Not changing it now in case |
|
| 1076 |
+ // there is a side-effect. |
|
| 1077 |
+ sizeRw = -1 |
|
| 1078 |
+ } |
|
| 1079 |
+ } else {
|
|
| 1080 |
+ changes, _ := container.Changes() |
|
| 1081 |
+ if changes != nil {
|
|
| 1082 |
+ sizeRw = archive.ChangesSize(container.basefs, changes) |
|
| 1083 |
+ } else {
|
|
| 1084 |
+ sizeRw = -1 |
|
| 1085 |
+ } |
|
| 1086 |
+ } |
|
| 1087 |
+ |
|
| 1088 |
+ if _, err = os.Stat(container.basefs); err != nil {
|
|
| 1089 |
+ if sizeRootfs, err = utils.TreeSize(container.basefs); err != nil {
|
|
| 1090 |
+ sizeRootfs = -1 |
|
| 1091 |
+ } |
|
| 1092 |
+ } |
|
| 1093 |
+ return sizeRw, sizeRootfs |
|
| 1094 |
+} |
|
| 1095 |
+ |
|
| 1096 |
+func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
|
| 1097 |
+ if err := container.Mount(); err != nil {
|
|
| 1098 |
+ return nil, err |
|
| 1099 |
+ } |
|
| 1100 |
+ var filter []string |
|
| 1101 |
+ basePath := path.Join(container.basefs, resource) |
|
| 1102 |
+ stat, err := os.Stat(basePath) |
|
| 1103 |
+ if err != nil {
|
|
| 1104 |
+ container.Unmount() |
|
| 1105 |
+ return nil, err |
|
| 1106 |
+ } |
|
| 1107 |
+ if !stat.IsDir() {
|
|
| 1108 |
+ d, f := path.Split(basePath) |
|
| 1109 |
+ basePath = d |
|
| 1110 |
+ filter = []string{f}
|
|
| 1111 |
+ } else {
|
|
| 1112 |
+ filter = []string{path.Base(basePath)}
|
|
| 1113 |
+ basePath = path.Dir(basePath) |
|
| 1114 |
+ } |
|
| 1115 |
+ |
|
| 1116 |
+ archive, err := archive.TarFilter(basePath, &archive.TarOptions{
|
|
| 1117 |
+ Compression: archive.Uncompressed, |
|
| 1118 |
+ Includes: filter, |
|
| 1119 |
+ }) |
|
| 1120 |
+ if err != nil {
|
|
| 1121 |
+ return nil, err |
|
| 1122 |
+ } |
|
| 1123 |
+ return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 1124 |
+ err := archive.Close() |
|
| 1125 |
+ container.Unmount() |
|
| 1126 |
+ return err |
|
| 1127 |
+ }), nil |
|
| 1128 |
+} |
|
| 1129 |
+ |
|
| 1130 |
+// Returns true if the container exposes a certain port |
|
| 1131 |
+func (container *Container) Exposes(p nat.Port) bool {
|
|
| 1132 |
+ _, exists := container.Config.ExposedPorts[p] |
|
| 1133 |
+ return exists |
|
| 1134 |
+} |
|
| 1135 |
+ |
|
| 1136 |
+func (container *Container) GetPtyMaster() (*os.File, error) {
|
|
| 1137 |
+ ttyConsole, ok := container.command.Terminal.(execdriver.TtyTerminal) |
|
| 1138 |
+ if !ok {
|
|
| 1139 |
+ return nil, ErrNoTTY |
|
| 1140 |
+ } |
|
| 1141 |
+ return ttyConsole.Master(), nil |
|
| 1142 |
+} |
|
| 1143 |
+ |
|
| 1144 |
+func (container *Container) HostConfig() *runconfig.HostConfig {
|
|
| 1145 |
+ return container.hostConfig |
|
| 1146 |
+} |
|
| 1147 |
+ |
|
| 1148 |
+func (container *Container) SetHostConfig(hostConfig *runconfig.HostConfig) {
|
|
| 1149 |
+ container.hostConfig = hostConfig |
|
| 1150 |
+} |
|
| 1151 |
+ |
|
| 1152 |
+func (container *Container) DisableLink(name string) {
|
|
| 1153 |
+ if container.activeLinks != nil {
|
|
| 1154 |
+ if link, exists := container.activeLinks[name]; exists {
|
|
| 1155 |
+ link.Disable() |
|
| 1156 |
+ } else {
|
|
| 1157 |
+ utils.Debugf("Could not find active link for %s", name)
|
|
| 1158 |
+ } |
|
| 1159 |
+ } |
|
| 1160 |
+} |
| 0 | 1161 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,145 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/nat" |
|
| 4 |
+ "testing" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
|
| 8 |
+ ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
|
|
| 9 |
+ if err != nil {
|
|
| 10 |
+ t.Fatal(err) |
|
| 11 |
+ } |
|
| 12 |
+ if len(ports) != 1 {
|
|
| 13 |
+ t.Logf("Expected 1 got %d", len(ports))
|
|
| 14 |
+ t.FailNow() |
|
| 15 |
+ } |
|
| 16 |
+ if len(bindings) != 1 {
|
|
| 17 |
+ t.Logf("Expected 1 got %d", len(bindings))
|
|
| 18 |
+ t.FailNow() |
|
| 19 |
+ } |
|
| 20 |
+ for k := range ports {
|
|
| 21 |
+ if k.Proto() != "tcp" {
|
|
| 22 |
+ t.Logf("Expected tcp got %s", k.Proto())
|
|
| 23 |
+ t.Fail() |
|
| 24 |
+ } |
|
| 25 |
+ if k.Port() != "80" {
|
|
| 26 |
+ t.Logf("Expected 80 got %s", k.Port())
|
|
| 27 |
+ t.Fail() |
|
| 28 |
+ } |
|
| 29 |
+ b, exists := bindings[k] |
|
| 30 |
+ if !exists {
|
|
| 31 |
+ t.Log("Binding does not exist")
|
|
| 32 |
+ t.FailNow() |
|
| 33 |
+ } |
|
| 34 |
+ if len(b) != 1 {
|
|
| 35 |
+ t.Logf("Expected 1 got %d", len(b))
|
|
| 36 |
+ t.FailNow() |
|
| 37 |
+ } |
|
| 38 |
+ s := b[0] |
|
| 39 |
+ if s.HostPort != "" {
|
|
| 40 |
+ t.Logf("Expected \"\" got %s", s.HostPort)
|
|
| 41 |
+ t.Fail() |
|
| 42 |
+ } |
|
| 43 |
+ if s.HostIp != "192.168.1.100" {
|
|
| 44 |
+ t.Fail() |
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func TestParseNetworkOptsPublic(t *testing.T) {
|
|
| 50 |
+ ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:8080:80"})
|
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ t.Fatal(err) |
|
| 53 |
+ } |
|
| 54 |
+ if len(ports) != 1 {
|
|
| 55 |
+ t.Logf("Expected 1 got %d", len(ports))
|
|
| 56 |
+ t.FailNow() |
|
| 57 |
+ } |
|
| 58 |
+ if len(bindings) != 1 {
|
|
| 59 |
+ t.Logf("Expected 1 got %d", len(bindings))
|
|
| 60 |
+ t.FailNow() |
|
| 61 |
+ } |
|
| 62 |
+ for k := range ports {
|
|
| 63 |
+ if k.Proto() != "tcp" {
|
|
| 64 |
+ t.Logf("Expected tcp got %s", k.Proto())
|
|
| 65 |
+ t.Fail() |
|
| 66 |
+ } |
|
| 67 |
+ if k.Port() != "80" {
|
|
| 68 |
+ t.Logf("Expected 80 got %s", k.Port())
|
|
| 69 |
+ t.Fail() |
|
| 70 |
+ } |
|
| 71 |
+ b, exists := bindings[k] |
|
| 72 |
+ if !exists {
|
|
| 73 |
+ t.Log("Binding does not exist")
|
|
| 74 |
+ t.FailNow() |
|
| 75 |
+ } |
|
| 76 |
+ if len(b) != 1 {
|
|
| 77 |
+ t.Logf("Expected 1 got %d", len(b))
|
|
| 78 |
+ t.FailNow() |
|
| 79 |
+ } |
|
| 80 |
+ s := b[0] |
|
| 81 |
+ if s.HostPort != "8080" {
|
|
| 82 |
+ t.Logf("Expected 8080 got %s", s.HostPort)
|
|
| 83 |
+ t.Fail() |
|
| 84 |
+ } |
|
| 85 |
+ if s.HostIp != "192.168.1.100" {
|
|
| 86 |
+ t.Fail() |
|
| 87 |
+ } |
|
| 88 |
+ } |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func TestParseNetworkOptsUdp(t *testing.T) {
|
|
| 92 |
+ ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
|
| 93 |
+ if err != nil {
|
|
| 94 |
+ t.Fatal(err) |
|
| 95 |
+ } |
|
| 96 |
+ if len(ports) != 1 {
|
|
| 97 |
+ t.Logf("Expected 1 got %d", len(ports))
|
|
| 98 |
+ t.FailNow() |
|
| 99 |
+ } |
|
| 100 |
+ if len(bindings) != 1 {
|
|
| 101 |
+ t.Logf("Expected 1 got %d", len(bindings))
|
|
| 102 |
+ t.FailNow() |
|
| 103 |
+ } |
|
| 104 |
+ for k := range ports {
|
|
| 105 |
+ if k.Proto() != "udp" {
|
|
| 106 |
+ t.Logf("Expected udp got %s", k.Proto())
|
|
| 107 |
+ t.Fail() |
|
| 108 |
+ } |
|
| 109 |
+ if k.Port() != "6000" {
|
|
| 110 |
+ t.Logf("Expected 6000 got %s", k.Port())
|
|
| 111 |
+ t.Fail() |
|
| 112 |
+ } |
|
| 113 |
+ b, exists := bindings[k] |
|
| 114 |
+ if !exists {
|
|
| 115 |
+ t.Log("Binding does not exist")
|
|
| 116 |
+ t.FailNow() |
|
| 117 |
+ } |
|
| 118 |
+ if len(b) != 1 {
|
|
| 119 |
+ t.Logf("Expected 1 got %d", len(b))
|
|
| 120 |
+ t.FailNow() |
|
| 121 |
+ } |
|
| 122 |
+ s := b[0] |
|
| 123 |
+ if s.HostPort != "" {
|
|
| 124 |
+ t.Logf("Expected \"\" got %s", s.HostPort)
|
|
| 125 |
+ t.Fail() |
|
| 126 |
+ } |
|
| 127 |
+ if s.HostIp != "192.168.1.100" {
|
|
| 128 |
+ t.Fail() |
|
| 129 |
+ } |
|
| 130 |
+ } |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+func TestGetFullName(t *testing.T) {
|
|
| 134 |
+ name, err := GetFullContainerName("testing")
|
|
| 135 |
+ if err != nil {
|
|
| 136 |
+ t.Fatal(err) |
|
| 137 |
+ } |
|
| 138 |
+ if name != "/testing" {
|
|
| 139 |
+ t.Fatalf("Expected /testing got %s", name)
|
|
| 140 |
+ } |
|
| 141 |
+ if _, err := GetFullContainerName(""); err == nil {
|
|
| 142 |
+ t.Fatal("Error should not be nil")
|
|
| 143 |
+ } |
|
| 144 |
+} |
| 0 | 145 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,953 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "container/list" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/archive" |
|
| 6 |
+ "github.com/dotcloud/docker/daemonconfig" |
|
| 7 |
+ "github.com/dotcloud/docker/dockerversion" |
|
| 8 |
+ "github.com/dotcloud/docker/engine" |
|
| 9 |
+ "github.com/dotcloud/docker/execdriver" |
|
| 10 |
+ "github.com/dotcloud/docker/execdriver/lxc" |
|
| 11 |
+ "github.com/dotcloud/docker/execdriver/native" |
|
| 12 |
+ "github.com/dotcloud/docker/graph" |
|
| 13 |
+ "github.com/dotcloud/docker/graphdriver" |
|
| 14 |
+ "github.com/dotcloud/docker/graphdriver/aufs" |
|
| 15 |
+ _ "github.com/dotcloud/docker/graphdriver/btrfs" |
|
| 16 |
+ _ "github.com/dotcloud/docker/graphdriver/devmapper" |
|
| 17 |
+ _ "github.com/dotcloud/docker/graphdriver/vfs" |
|
| 18 |
+ "github.com/dotcloud/docker/image" |
|
| 19 |
+ _ "github.com/dotcloud/docker/networkdriver/lxc" |
|
| 20 |
+ "github.com/dotcloud/docker/networkdriver/portallocator" |
|
| 21 |
+ "github.com/dotcloud/docker/pkg/graphdb" |
|
| 22 |
+ "github.com/dotcloud/docker/pkg/sysinfo" |
|
| 23 |
+ "github.com/dotcloud/docker/runconfig" |
|
| 24 |
+ "github.com/dotcloud/docker/utils" |
|
| 25 |
+ "io" |
|
| 26 |
+ "io/ioutil" |
|
| 27 |
+ "os" |
|
| 28 |
+ "path" |
|
| 29 |
+ "regexp" |
|
| 30 |
+ "sort" |
|
| 31 |
+ "strings" |
|
| 32 |
+ "sync" |
|
| 33 |
+ "time" |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+// Set the max depth to the aufs default that most |
|
| 37 |
+// kernels are compiled with |
|
| 38 |
+// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk |
|
| 39 |
+const MaxImageDepth = 127 |
|
| 40 |
+ |
|
| 41 |
+var ( |
|
| 42 |
+ DefaultDns = []string{"8.8.8.8", "8.8.4.4"}
|
|
| 43 |
+ validContainerNameChars = `[a-zA-Z0-9_.-]` |
|
| 44 |
+ validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`) |
|
| 45 |
+) |
|
| 46 |
+ |
|
| 47 |
+type Runtime struct {
|
|
| 48 |
+ repository string |
|
| 49 |
+ sysInitPath string |
|
| 50 |
+ containers *list.List |
|
| 51 |
+ graph *graph.Graph |
|
| 52 |
+ repositories *graph.TagStore |
|
| 53 |
+ idIndex *utils.TruncIndex |
|
| 54 |
+ sysInfo *sysinfo.SysInfo |
|
| 55 |
+ volumes *graph.Graph |
|
| 56 |
+ srv Server |
|
| 57 |
+ eng *engine.Engine |
|
| 58 |
+ config *daemonconfig.Config |
|
| 59 |
+ containerGraph *graphdb.Database |
|
| 60 |
+ driver graphdriver.Driver |
|
| 61 |
+ execDriver execdriver.Driver |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// List returns an array of all containers registered in the runtime. |
|
| 65 |
+func (runtime *Runtime) List() []*Container {
|
|
| 66 |
+ containers := new(History) |
|
| 67 |
+ for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
|
| 68 |
+ containers.Add(e.Value.(*Container)) |
|
| 69 |
+ } |
|
| 70 |
+ return *containers |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func (runtime *Runtime) getContainerElement(id string) *list.Element {
|
|
| 74 |
+ for e := runtime.containers.Front(); e != nil; e = e.Next() {
|
|
| 75 |
+ container := e.Value.(*Container) |
|
| 76 |
+ if container.ID == id {
|
|
| 77 |
+ return e |
|
| 78 |
+ } |
|
| 79 |
+ } |
|
| 80 |
+ return nil |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// Get looks for a container by the specified ID or name, and returns it. |
|
| 84 |
+// If the container is not found, or if an error occurs, nil is returned. |
|
| 85 |
+func (runtime *Runtime) Get(name string) *Container {
|
|
| 86 |
+ if c, _ := runtime.GetByName(name); c != nil {
|
|
| 87 |
+ return c |
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ id, err := runtime.idIndex.Get(name) |
|
| 91 |
+ if err != nil {
|
|
| 92 |
+ return nil |
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ e := runtime.getContainerElement(id) |
|
| 96 |
+ if e == nil {
|
|
| 97 |
+ return nil |
|
| 98 |
+ } |
|
| 99 |
+ return e.Value.(*Container) |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+// Exists returns a true if a container of the specified ID or name exists, |
|
| 103 |
+// false otherwise. |
|
| 104 |
+func (runtime *Runtime) Exists(id string) bool {
|
|
| 105 |
+ return runtime.Get(id) != nil |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+func (runtime *Runtime) containerRoot(id string) string {
|
|
| 109 |
+ return path.Join(runtime.repository, id) |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// Load reads the contents of a container from disk |
|
| 113 |
+// This is typically done at startup. |
|
| 114 |
+func (runtime *Runtime) load(id string) (*Container, error) {
|
|
| 115 |
+ container := &Container{root: runtime.containerRoot(id)}
|
|
| 116 |
+ if err := container.FromDisk(); err != nil {
|
|
| 117 |
+ return nil, err |
|
| 118 |
+ } |
|
| 119 |
+ if container.ID != id {
|
|
| 120 |
+ return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
|
|
| 121 |
+ } |
|
| 122 |
+ if container.State.IsRunning() {
|
|
| 123 |
+ container.State.SetGhost(true) |
|
| 124 |
+ } |
|
| 125 |
+ return container, nil |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// Register makes a container object usable by the runtime as <container.ID> |
|
| 129 |
+func (runtime *Runtime) Register(container *Container) error {
|
|
| 130 |
+ if container.runtime != nil || runtime.Exists(container.ID) {
|
|
| 131 |
+ return fmt.Errorf("Container is already loaded")
|
|
| 132 |
+ } |
|
| 133 |
+ if err := validateID(container.ID); err != nil {
|
|
| 134 |
+ return err |
|
| 135 |
+ } |
|
| 136 |
+ if err := runtime.ensureName(container); err != nil {
|
|
| 137 |
+ return err |
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ container.runtime = runtime |
|
| 141 |
+ |
|
| 142 |
+ // Attach to stdout and stderr |
|
| 143 |
+ container.stderr = utils.NewWriteBroadcaster() |
|
| 144 |
+ container.stdout = utils.NewWriteBroadcaster() |
|
| 145 |
+ // Attach to stdin |
|
| 146 |
+ if container.Config.OpenStdin {
|
|
| 147 |
+ container.stdin, container.stdinPipe = io.Pipe() |
|
| 148 |
+ } else {
|
|
| 149 |
+ container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
| 150 |
+ } |
|
| 151 |
+ // done |
|
| 152 |
+ runtime.containers.PushBack(container) |
|
| 153 |
+ runtime.idIndex.Add(container.ID) |
|
| 154 |
+ |
|
| 155 |
+ // FIXME: if the container is supposed to be running but is not, auto restart it? |
|
| 156 |
+ // if so, then we need to restart monitor and init a new lock |
|
| 157 |
+ // If the container is supposed to be running, make sure of it |
|
| 158 |
+ if container.State.IsRunning() {
|
|
| 159 |
+ if container.State.IsGhost() {
|
|
| 160 |
+ utils.Debugf("killing ghost %s", container.ID)
|
|
| 161 |
+ |
|
| 162 |
+ existingPid := container.State.Pid |
|
| 163 |
+ container.State.SetGhost(false) |
|
| 164 |
+ container.State.SetStopped(0) |
|
| 165 |
+ |
|
| 166 |
+ if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
|
|
| 167 |
+ lxc.KillLxc(container.ID, 9) |
|
| 168 |
+ } else {
|
|
| 169 |
+ command := &execdriver.Command{
|
|
| 170 |
+ ID: container.ID, |
|
| 171 |
+ } |
|
| 172 |
+ command.Process = &os.Process{Pid: existingPid}
|
|
| 173 |
+ runtime.execDriver.Kill(command, 9) |
|
| 174 |
+ } |
|
| 175 |
+ // ensure that the filesystem is also unmounted |
|
| 176 |
+ unmountVolumesForContainer(container) |
|
| 177 |
+ if err := container.Unmount(); err != nil {
|
|
| 178 |
+ utils.Debugf("ghost unmount error %s", err)
|
|
| 179 |
+ } |
|
| 180 |
+ } |
|
| 181 |
+ |
|
| 182 |
+ info := runtime.execDriver.Info(container.ID) |
|
| 183 |
+ if !info.IsRunning() {
|
|
| 184 |
+ utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
|
|
| 185 |
+ if runtime.config.AutoRestart {
|
|
| 186 |
+ utils.Debugf("Restarting")
|
|
| 187 |
+ unmountVolumesForContainer(container) |
|
| 188 |
+ if err := container.Unmount(); err != nil {
|
|
| 189 |
+ utils.Debugf("restart unmount error %s", err)
|
|
| 190 |
+ } |
|
| 191 |
+ |
|
| 192 |
+ container.State.SetGhost(false) |
|
| 193 |
+ container.State.SetStopped(0) |
|
| 194 |
+ if err := container.Start(); err != nil {
|
|
| 195 |
+ return err |
|
| 196 |
+ } |
|
| 197 |
+ } else {
|
|
| 198 |
+ utils.Debugf("Marking as stopped")
|
|
| 199 |
+ container.State.SetStopped(-127) |
|
| 200 |
+ if err := container.ToDisk(); err != nil {
|
|
| 201 |
+ return err |
|
| 202 |
+ } |
|
| 203 |
+ } |
|
| 204 |
+ } |
|
| 205 |
+ } else {
|
|
| 206 |
+ // When the container is not running, we still initialize the waitLock |
|
| 207 |
+ // chan and close it. Receiving on nil chan blocks whereas receiving on a |
|
| 208 |
+ // closed chan does not. In this case we do not want to block. |
|
| 209 |
+ container.waitLock = make(chan struct{})
|
|
| 210 |
+ close(container.waitLock) |
|
| 211 |
+ } |
|
| 212 |
+ return nil |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+func (runtime *Runtime) ensureName(container *Container) error {
|
|
| 216 |
+ if container.Name == "" {
|
|
| 217 |
+ name, err := generateRandomName(runtime) |
|
| 218 |
+ if err != nil {
|
|
| 219 |
+ name = utils.TruncateID(container.ID) |
|
| 220 |
+ } |
|
| 221 |
+ container.Name = name |
|
| 222 |
+ |
|
| 223 |
+ if err := container.ToDisk(); err != nil {
|
|
| 224 |
+ utils.Debugf("Error saving container name %s", err)
|
|
| 225 |
+ } |
|
| 226 |
+ if !runtime.containerGraph.Exists(name) {
|
|
| 227 |
+ if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
|
|
| 228 |
+ utils.Debugf("Setting default id - %s", err)
|
|
| 229 |
+ } |
|
| 230 |
+ } |
|
| 231 |
+ } |
|
| 232 |
+ return nil |
|
| 233 |
+} |
|
| 234 |
+ |
|
| 235 |
+func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
|
|
| 236 |
+ log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) |
|
| 237 |
+ if err != nil {
|
|
| 238 |
+ return err |
|
| 239 |
+ } |
|
| 240 |
+ src.AddWriter(log, stream) |
|
| 241 |
+ return nil |
|
| 242 |
+} |
|
| 243 |
+ |
|
| 244 |
+// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem. |
|
| 245 |
+func (runtime *Runtime) Destroy(container *Container) error {
|
|
| 246 |
+ if container == nil {
|
|
| 247 |
+ return fmt.Errorf("The given container is <nil>")
|
|
| 248 |
+ } |
|
| 249 |
+ |
|
| 250 |
+ element := runtime.getContainerElement(container.ID) |
|
| 251 |
+ if element == nil {
|
|
| 252 |
+ return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
|
|
| 253 |
+ } |
|
| 254 |
+ |
|
| 255 |
+ if err := container.Stop(3); err != nil {
|
|
| 256 |
+ return err |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ if err := runtime.driver.Remove(container.ID); err != nil {
|
|
| 260 |
+ return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
|
|
| 261 |
+ } |
|
| 262 |
+ |
|
| 263 |
+ initID := fmt.Sprintf("%s-init", container.ID)
|
|
| 264 |
+ if err := runtime.driver.Remove(initID); err != nil {
|
|
| 265 |
+ return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", runtime.driver, initID, err)
|
|
| 266 |
+ } |
|
| 267 |
+ |
|
| 268 |
+ if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
|
|
| 269 |
+ utils.Debugf("Unable to remove container from link graph: %s", err)
|
|
| 270 |
+ } |
|
| 271 |
+ |
|
| 272 |
+ // Deregister the container before removing its directory, to avoid race conditions |
|
| 273 |
+ runtime.idIndex.Delete(container.ID) |
|
| 274 |
+ runtime.containers.Remove(element) |
|
| 275 |
+ if err := os.RemoveAll(container.root); err != nil {
|
|
| 276 |
+ return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
|
|
| 277 |
+ } |
|
| 278 |
+ return nil |
|
| 279 |
+} |
|
| 280 |
+ |
|
| 281 |
+func (runtime *Runtime) restore() error {
|
|
| 282 |
+ if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 283 |
+ fmt.Printf("Loading containers: ")
|
|
| 284 |
+ } |
|
| 285 |
+ dir, err := ioutil.ReadDir(runtime.repository) |
|
| 286 |
+ if err != nil {
|
|
| 287 |
+ return err |
|
| 288 |
+ } |
|
| 289 |
+ containers := make(map[string]*Container) |
|
| 290 |
+ currentDriver := runtime.driver.String() |
|
| 291 |
+ |
|
| 292 |
+ for _, v := range dir {
|
|
| 293 |
+ id := v.Name() |
|
| 294 |
+ container, err := runtime.load(id) |
|
| 295 |
+ if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 296 |
+ fmt.Print(".")
|
|
| 297 |
+ } |
|
| 298 |
+ if err != nil {
|
|
| 299 |
+ utils.Errorf("Failed to load container %v: %v", id, err)
|
|
| 300 |
+ continue |
|
| 301 |
+ } |
|
| 302 |
+ |
|
| 303 |
+ // Ignore the container if it does not support the current driver being used by the graph |
|
| 304 |
+ if container.Driver == "" && currentDriver == "aufs" || container.Driver == currentDriver {
|
|
| 305 |
+ utils.Debugf("Loaded container %v", container.ID)
|
|
| 306 |
+ containers[container.ID] = container |
|
| 307 |
+ } else {
|
|
| 308 |
+ utils.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID)
|
|
| 309 |
+ } |
|
| 310 |
+ } |
|
| 311 |
+ |
|
| 312 |
+ register := func(container *Container) {
|
|
| 313 |
+ if err := runtime.Register(container); err != nil {
|
|
| 314 |
+ utils.Debugf("Failed to register container %s: %s", container.ID, err)
|
|
| 315 |
+ } |
|
| 316 |
+ } |
|
| 317 |
+ |
|
| 318 |
+ if entities := runtime.containerGraph.List("/", -1); entities != nil {
|
|
| 319 |
+ for _, p := range entities.Paths() {
|
|
| 320 |
+ if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 321 |
+ fmt.Print(".")
|
|
| 322 |
+ } |
|
| 323 |
+ e := entities[p] |
|
| 324 |
+ if container, ok := containers[e.ID()]; ok {
|
|
| 325 |
+ register(container) |
|
| 326 |
+ delete(containers, e.ID()) |
|
| 327 |
+ } |
|
| 328 |
+ } |
|
| 329 |
+ } |
|
| 330 |
+ |
|
| 331 |
+ // Any containers that are left over do not exist in the graph |
|
| 332 |
+ for _, container := range containers {
|
|
| 333 |
+ // Try to set the default name for a container if it exists prior to links |
|
| 334 |
+ container.Name, err = generateRandomName(runtime) |
|
| 335 |
+ if err != nil {
|
|
| 336 |
+ container.Name = utils.TruncateID(container.ID) |
|
| 337 |
+ } |
|
| 338 |
+ |
|
| 339 |
+ if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil {
|
|
| 340 |
+ utils.Debugf("Setting default id - %s", err)
|
|
| 341 |
+ } |
|
| 342 |
+ register(container) |
|
| 343 |
+ } |
|
| 344 |
+ |
|
| 345 |
+ if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
|
|
| 346 |
+ fmt.Printf(": done.\n")
|
|
| 347 |
+ } |
|
| 348 |
+ |
|
| 349 |
+ return nil |
|
| 350 |
+} |
|
| 351 |
+ |
|
| 352 |
+// Create creates a new container from the given configuration with a given name. |
|
| 353 |
+func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
|
|
| 354 |
+ // Lookup image |
|
| 355 |
+ img, err := runtime.repositories.LookupImage(config.Image) |
|
| 356 |
+ if err != nil {
|
|
| 357 |
+ return nil, nil, err |
|
| 358 |
+ } |
|
| 359 |
+ |
|
| 360 |
+ // We add 2 layers to the depth because the container's rw and |
|
| 361 |
+ // init layer add to the restriction |
|
| 362 |
+ depth, err := img.Depth() |
|
| 363 |
+ if err != nil {
|
|
| 364 |
+ return nil, nil, err |
|
| 365 |
+ } |
|
| 366 |
+ |
|
| 367 |
+ if depth+2 >= MaxImageDepth {
|
|
| 368 |
+ return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
|
|
| 369 |
+ } |
|
| 370 |
+ |
|
| 371 |
+ checkDeprecatedExpose := func(config *runconfig.Config) bool {
|
|
| 372 |
+ if config != nil {
|
|
| 373 |
+ if config.PortSpecs != nil {
|
|
| 374 |
+ for _, p := range config.PortSpecs {
|
|
| 375 |
+ if strings.Contains(p, ":") {
|
|
| 376 |
+ return true |
|
| 377 |
+ } |
|
| 378 |
+ } |
|
| 379 |
+ } |
|
| 380 |
+ } |
|
| 381 |
+ return false |
|
| 382 |
+ } |
|
| 383 |
+ |
|
| 384 |
+ warnings := []string{}
|
|
| 385 |
+ if checkDeprecatedExpose(img.Config) || checkDeprecatedExpose(config) {
|
|
| 386 |
+ warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.") |
|
| 387 |
+ } |
|
| 388 |
+ |
|
| 389 |
+ if img.Config != nil {
|
|
| 390 |
+ if err := runconfig.Merge(config, img.Config); err != nil {
|
|
| 391 |
+ return nil, nil, err |
|
| 392 |
+ } |
|
| 393 |
+ } |
|
| 394 |
+ |
|
| 395 |
+ if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 {
|
|
| 396 |
+ return nil, nil, fmt.Errorf("No command specified")
|
|
| 397 |
+ } |
|
| 398 |
+ |
|
| 399 |
+ // Generate id |
|
| 400 |
+ id := utils.GenerateRandomID() |
|
| 401 |
+ |
|
| 402 |
+ if name == "" {
|
|
| 403 |
+ name, err = generateRandomName(runtime) |
|
| 404 |
+ if err != nil {
|
|
| 405 |
+ name = utils.TruncateID(id) |
|
| 406 |
+ } |
|
| 407 |
+ } else {
|
|
| 408 |
+ if !validContainerNamePattern.MatchString(name) {
|
|
| 409 |
+ return nil, nil, fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars)
|
|
| 410 |
+ } |
|
| 411 |
+ } |
|
| 412 |
+ |
|
| 413 |
+ if name[0] != '/' {
|
|
| 414 |
+ name = "/" + name |
|
| 415 |
+ } |
|
| 416 |
+ |
|
| 417 |
+ // Set the enitity in the graph using the default name specified |
|
| 418 |
+ if _, err := runtime.containerGraph.Set(name, id); err != nil {
|
|
| 419 |
+ if !graphdb.IsNonUniqueNameError(err) {
|
|
| 420 |
+ return nil, nil, err |
|
| 421 |
+ } |
|
| 422 |
+ |
|
| 423 |
+ conflictingContainer, err := runtime.GetByName(name) |
|
| 424 |
+ if err != nil {
|
|
| 425 |
+ if strings.Contains(err.Error(), "Could not find entity") {
|
|
| 426 |
+ return nil, nil, err |
|
| 427 |
+ } |
|
| 428 |
+ |
|
| 429 |
+ // Remove name and continue starting the container |
|
| 430 |
+ if err := runtime.containerGraph.Delete(name); err != nil {
|
|
| 431 |
+ return nil, nil, err |
|
| 432 |
+ } |
|
| 433 |
+ } else {
|
|
| 434 |
+ nameAsKnownByUser := strings.TrimPrefix(name, "/") |
|
| 435 |
+ return nil, nil, fmt.Errorf( |
|
| 436 |
+ "Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", nameAsKnownByUser, |
|
| 437 |
+ utils.TruncateID(conflictingContainer.ID), nameAsKnownByUser) |
|
| 438 |
+ } |
|
| 439 |
+ } |
|
| 440 |
+ |
|
| 441 |
+ // Generate default hostname |
|
| 442 |
+ // FIXME: the lxc template no longer needs to set a default hostname |
|
| 443 |
+ if config.Hostname == "" {
|
|
| 444 |
+ config.Hostname = id[:12] |
|
| 445 |
+ } |
|
| 446 |
+ |
|
| 447 |
+ var args []string |
|
| 448 |
+ var entrypoint string |
|
| 449 |
+ |
|
| 450 |
+ if len(config.Entrypoint) != 0 {
|
|
| 451 |
+ entrypoint = config.Entrypoint[0] |
|
| 452 |
+ args = append(config.Entrypoint[1:], config.Cmd...) |
|
| 453 |
+ } else {
|
|
| 454 |
+ entrypoint = config.Cmd[0] |
|
| 455 |
+ args = config.Cmd[1:] |
|
| 456 |
+ } |
|
| 457 |
+ |
|
| 458 |
+ container := &Container{
|
|
| 459 |
+ // FIXME: we should generate the ID here instead of receiving it as an argument |
|
| 460 |
+ ID: id, |
|
| 461 |
+ Created: time.Now().UTC(), |
|
| 462 |
+ Path: entrypoint, |
|
| 463 |
+ Args: args, //FIXME: de-duplicate from config |
|
| 464 |
+ Config: config, |
|
| 465 |
+ hostConfig: &runconfig.HostConfig{},
|
|
| 466 |
+ Image: img.ID, // Always use the resolved image id |
|
| 467 |
+ NetworkSettings: &NetworkSettings{},
|
|
| 468 |
+ Name: name, |
|
| 469 |
+ Driver: runtime.driver.String(), |
|
| 470 |
+ ExecDriver: runtime.execDriver.Name(), |
|
| 471 |
+ } |
|
| 472 |
+ container.root = runtime.containerRoot(container.ID) |
|
| 473 |
+ // Step 1: create the container directory. |
|
| 474 |
+ // This doubles as a barrier to avoid race conditions. |
|
| 475 |
+ if err := os.Mkdir(container.root, 0700); err != nil {
|
|
| 476 |
+ return nil, nil, err |
|
| 477 |
+ } |
|
| 478 |
+ |
|
| 479 |
+ initID := fmt.Sprintf("%s-init", container.ID)
|
|
| 480 |
+ if err := runtime.driver.Create(initID, img.ID); err != nil {
|
|
| 481 |
+ return nil, nil, err |
|
| 482 |
+ } |
|
| 483 |
+ initPath, err := runtime.driver.Get(initID) |
|
| 484 |
+ if err != nil {
|
|
| 485 |
+ return nil, nil, err |
|
| 486 |
+ } |
|
| 487 |
+ defer runtime.driver.Put(initID) |
|
| 488 |
+ |
|
| 489 |
+ if err := graph.SetupInitLayer(initPath); err != nil {
|
|
| 490 |
+ return nil, nil, err |
|
| 491 |
+ } |
|
| 492 |
+ |
|
| 493 |
+ if err := runtime.driver.Create(container.ID, initID); err != nil {
|
|
| 494 |
+ return nil, nil, err |
|
| 495 |
+ } |
|
| 496 |
+ resolvConf, err := utils.GetResolvConf() |
|
| 497 |
+ if err != nil {
|
|
| 498 |
+ return nil, nil, err |
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ if len(config.Dns) == 0 && len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
|
| 502 |
+ runtime.config.Dns = DefaultDns |
|
| 503 |
+ } |
|
| 504 |
+ |
|
| 505 |
+ // If custom dns exists, then create a resolv.conf for the container |
|
| 506 |
+ if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 {
|
|
| 507 |
+ var dns []string |
|
| 508 |
+ if len(config.Dns) > 0 {
|
|
| 509 |
+ dns = config.Dns |
|
| 510 |
+ } else {
|
|
| 511 |
+ dns = runtime.config.Dns |
|
| 512 |
+ } |
|
| 513 |
+ container.ResolvConfPath = path.Join(container.root, "resolv.conf") |
|
| 514 |
+ f, err := os.Create(container.ResolvConfPath) |
|
| 515 |
+ if err != nil {
|
|
| 516 |
+ return nil, nil, err |
|
| 517 |
+ } |
|
| 518 |
+ defer f.Close() |
|
| 519 |
+ for _, dns := range dns {
|
|
| 520 |
+ if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
|
|
| 521 |
+ return nil, nil, err |
|
| 522 |
+ } |
|
| 523 |
+ } |
|
| 524 |
+ } else {
|
|
| 525 |
+ container.ResolvConfPath = "/etc/resolv.conf" |
|
| 526 |
+ } |
|
| 527 |
+ |
|
| 528 |
+ // Step 2: save the container json |
|
| 529 |
+ if err := container.ToDisk(); err != nil {
|
|
| 530 |
+ return nil, nil, err |
|
| 531 |
+ } |
|
| 532 |
+ |
|
| 533 |
+ // Step 3: register the container |
|
| 534 |
+ if err := runtime.Register(container); err != nil {
|
|
| 535 |
+ return nil, nil, err |
|
| 536 |
+ } |
|
| 537 |
+ return container, warnings, nil |
|
| 538 |
+} |
|
| 539 |
+ |
|
| 540 |
+// Commit creates a new filesystem image from the current state of a container. |
|
| 541 |
+// The image can optionally be tagged into a repository |
|
| 542 |
+func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
|
|
| 543 |
+ // FIXME: freeze the container before copying it to avoid data corruption? |
|
| 544 |
+ // FIXME: this shouldn't be in commands. |
|
| 545 |
+ if err := container.Mount(); err != nil {
|
|
| 546 |
+ return nil, err |
|
| 547 |
+ } |
|
| 548 |
+ defer container.Unmount() |
|
| 549 |
+ |
|
| 550 |
+ rwTar, err := container.ExportRw() |
|
| 551 |
+ if err != nil {
|
|
| 552 |
+ return nil, err |
|
| 553 |
+ } |
|
| 554 |
+ defer rwTar.Close() |
|
| 555 |
+ |
|
| 556 |
+ // Create a new image from the container's base layers + a new layer from container changes |
|
| 557 |
+ var ( |
|
| 558 |
+ containerID, containerImage string |
|
| 559 |
+ containerConfig *runconfig.Config |
|
| 560 |
+ ) |
|
| 561 |
+ if container != nil {
|
|
| 562 |
+ containerID = container.ID |
|
| 563 |
+ containerImage = container.Image |
|
| 564 |
+ containerConfig = container.Config |
|
| 565 |
+ } |
|
| 566 |
+ img, err := runtime.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config) |
|
| 567 |
+ if err != nil {
|
|
| 568 |
+ return nil, err |
|
| 569 |
+ } |
|
| 570 |
+ // Register the image if needed |
|
| 571 |
+ if repository != "" {
|
|
| 572 |
+ if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil {
|
|
| 573 |
+ return img, err |
|
| 574 |
+ } |
|
| 575 |
+ } |
|
| 576 |
+ return img, nil |
|
| 577 |
+} |
|
| 578 |
+ |
|
| 579 |
+func GetFullContainerName(name string) (string, error) {
|
|
| 580 |
+ if name == "" {
|
|
| 581 |
+ return "", fmt.Errorf("Container name cannot be empty")
|
|
| 582 |
+ } |
|
| 583 |
+ if name[0] != '/' {
|
|
| 584 |
+ name = "/" + name |
|
| 585 |
+ } |
|
| 586 |
+ return name, nil |
|
| 587 |
+} |
|
| 588 |
+ |
|
| 589 |
+func (runtime *Runtime) GetByName(name string) (*Container, error) {
|
|
| 590 |
+ fullName, err := GetFullContainerName(name) |
|
| 591 |
+ if err != nil {
|
|
| 592 |
+ return nil, err |
|
| 593 |
+ } |
|
| 594 |
+ entity := runtime.containerGraph.Get(fullName) |
|
| 595 |
+ if entity == nil {
|
|
| 596 |
+ return nil, fmt.Errorf("Could not find entity for %s", name)
|
|
| 597 |
+ } |
|
| 598 |
+ e := runtime.getContainerElement(entity.ID()) |
|
| 599 |
+ if e == nil {
|
|
| 600 |
+ return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
|
| 601 |
+ } |
|
| 602 |
+ return e.Value.(*Container), nil |
|
| 603 |
+} |
|
| 604 |
+ |
|
| 605 |
+func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
|
|
| 606 |
+ name, err := GetFullContainerName(name) |
|
| 607 |
+ if err != nil {
|
|
| 608 |
+ return nil, err |
|
| 609 |
+ } |
|
| 610 |
+ children := make(map[string]*Container) |
|
| 611 |
+ |
|
| 612 |
+ err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
|
| 613 |
+ c := runtime.Get(e.ID()) |
|
| 614 |
+ if c == nil {
|
|
| 615 |
+ return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
|
| 616 |
+ } |
|
| 617 |
+ children[p] = c |
|
| 618 |
+ return nil |
|
| 619 |
+ }, 0) |
|
| 620 |
+ |
|
| 621 |
+ if err != nil {
|
|
| 622 |
+ return nil, err |
|
| 623 |
+ } |
|
| 624 |
+ return children, nil |
|
| 625 |
+} |
|
| 626 |
+ |
|
| 627 |
+func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error {
|
|
| 628 |
+ fullName := path.Join(parent.Name, alias) |
|
| 629 |
+ if !runtime.containerGraph.Exists(fullName) {
|
|
| 630 |
+ _, err := runtime.containerGraph.Set(fullName, child.ID) |
|
| 631 |
+ return err |
|
| 632 |
+ } |
|
| 633 |
+ return nil |
|
| 634 |
+} |
|
| 635 |
+ |
|
| 636 |
+// FIXME: harmonize with NewGraph() |
|
| 637 |
+func NewRuntime(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
|
| 638 |
+ runtime, err := NewRuntimeFromDirectory(config, eng) |
|
| 639 |
+ if err != nil {
|
|
| 640 |
+ return nil, err |
|
| 641 |
+ } |
|
| 642 |
+ return runtime, nil |
|
| 643 |
+} |
|
| 644 |
+ |
|
| 645 |
+func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
|
|
| 646 |
+ |
|
| 647 |
+ // Set the default driver |
|
| 648 |
+ graphdriver.DefaultDriver = config.GraphDriver |
|
| 649 |
+ |
|
| 650 |
+ // Load storage driver |
|
| 651 |
+ driver, err := graphdriver.New(config.Root) |
|
| 652 |
+ if err != nil {
|
|
| 653 |
+ return nil, err |
|
| 654 |
+ } |
|
| 655 |
+ utils.Debugf("Using graph driver %s", driver)
|
|
| 656 |
+ |
|
| 657 |
+ runtimeRepo := path.Join(config.Root, "containers") |
|
| 658 |
+ |
|
| 659 |
+ if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
|
| 660 |
+ return nil, err |
|
| 661 |
+ } |
|
| 662 |
+ |
|
| 663 |
+ if ad, ok := driver.(*aufs.Driver); ok {
|
|
| 664 |
+ utils.Debugf("Migrating existing containers")
|
|
| 665 |
+ if err := ad.Migrate(config.Root, graph.SetupInitLayer); err != nil {
|
|
| 666 |
+ return nil, err |
|
| 667 |
+ } |
|
| 668 |
+ } |
|
| 669 |
+ |
|
| 670 |
+ utils.Debugf("Creating images graph")
|
|
| 671 |
+ g, err := graph.NewGraph(path.Join(config.Root, "graph"), driver) |
|
| 672 |
+ if err != nil {
|
|
| 673 |
+ return nil, err |
|
| 674 |
+ } |
|
| 675 |
+ |
|
| 676 |
+ // We don't want to use a complex driver like aufs or devmapper |
|
| 677 |
+ // for volumes, just a plain filesystem |
|
| 678 |
+ volumesDriver, err := graphdriver.GetDriver("vfs", config.Root)
|
|
| 679 |
+ if err != nil {
|
|
| 680 |
+ return nil, err |
|
| 681 |
+ } |
|
| 682 |
+ utils.Debugf("Creating volumes graph")
|
|
| 683 |
+ volumes, err := graph.NewGraph(path.Join(config.Root, "volumes"), volumesDriver) |
|
| 684 |
+ if err != nil {
|
|
| 685 |
+ return nil, err |
|
| 686 |
+ } |
|
| 687 |
+ utils.Debugf("Creating repository list")
|
|
| 688 |
+ repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g) |
|
| 689 |
+ if err != nil {
|
|
| 690 |
+ return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
|
|
| 691 |
+ } |
|
| 692 |
+ |
|
| 693 |
+ if !config.DisableNetwork {
|
|
| 694 |
+ job := eng.Job("init_networkdriver")
|
|
| 695 |
+ |
|
| 696 |
+ job.SetenvBool("EnableIptables", config.EnableIptables)
|
|
| 697 |
+ job.SetenvBool("InterContainerCommunication", config.InterContainerCommunication)
|
|
| 698 |
+ job.SetenvBool("EnableIpForward", config.EnableIpForward)
|
|
| 699 |
+ job.Setenv("BridgeIface", config.BridgeIface)
|
|
| 700 |
+ job.Setenv("BridgeIP", config.BridgeIP)
|
|
| 701 |
+ job.Setenv("DefaultBindingIP", config.DefaultIp.String())
|
|
| 702 |
+ |
|
| 703 |
+ if err := job.Run(); err != nil {
|
|
| 704 |
+ return nil, err |
|
| 705 |
+ } |
|
| 706 |
+ } |
|
| 707 |
+ |
|
| 708 |
+ graphdbPath := path.Join(config.Root, "linkgraph.db") |
|
| 709 |
+ graph, err := graphdb.NewSqliteConn(graphdbPath) |
|
| 710 |
+ if err != nil {
|
|
| 711 |
+ return nil, err |
|
| 712 |
+ } |
|
| 713 |
+ |
|
| 714 |
+ localCopy := path.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION))
|
|
| 715 |
+ sysInitPath := utils.DockerInitPath(localCopy) |
|
| 716 |
+ if sysInitPath == "" {
|
|
| 717 |
+ return nil, fmt.Errorf("Could not locate dockerinit: This usually means docker was built incorrectly. See http://docs.docker.io/en/latest/contributing/devenvironment for official build instructions.")
|
|
| 718 |
+ } |
|
| 719 |
+ |
|
| 720 |
+ if sysInitPath != localCopy {
|
|
| 721 |
+ // When we find a suitable dockerinit binary (even if it's our local binary), we copy it into config.Root at localCopy for future use (so that the original can go away without that being a problem, for example during a package upgrade). |
|
| 722 |
+ if err := os.Mkdir(path.Dir(localCopy), 0700); err != nil && !os.IsExist(err) {
|
|
| 723 |
+ return nil, err |
|
| 724 |
+ } |
|
| 725 |
+ if _, err := utils.CopyFile(sysInitPath, localCopy); err != nil {
|
|
| 726 |
+ return nil, err |
|
| 727 |
+ } |
|
| 728 |
+ if err := os.Chmod(localCopy, 0700); err != nil {
|
|
| 729 |
+ return nil, err |
|
| 730 |
+ } |
|
| 731 |
+ sysInitPath = localCopy |
|
| 732 |
+ } |
|
| 733 |
+ |
|
| 734 |
+ var ( |
|
| 735 |
+ ed execdriver.Driver |
|
| 736 |
+ sysInfo = sysinfo.New(false) |
|
| 737 |
+ ) |
|
| 738 |
+ |
|
| 739 |
+ switch config.ExecDriver {
|
|
| 740 |
+ case "lxc": |
|
| 741 |
+ // we want to five the lxc driver the full docker root because it needs |
|
| 742 |
+ // to access and write config and template files in /var/lib/docker/containers/* |
|
| 743 |
+ // to be backwards compatible |
|
| 744 |
+ ed, err = lxc.NewDriver(config.Root, sysInfo.AppArmor) |
|
| 745 |
+ case "native": |
|
| 746 |
+ ed, err = native.NewDriver(path.Join(config.Root, "execdriver", "native")) |
|
| 747 |
+ default: |
|
| 748 |
+ return nil, fmt.Errorf("unknown exec driver %s", config.ExecDriver)
|
|
| 749 |
+ } |
|
| 750 |
+ if err != nil {
|
|
| 751 |
+ return nil, err |
|
| 752 |
+ } |
|
| 753 |
+ |
|
| 754 |
+ runtime := &Runtime{
|
|
| 755 |
+ repository: runtimeRepo, |
|
| 756 |
+ containers: list.New(), |
|
| 757 |
+ graph: g, |
|
| 758 |
+ repositories: repositories, |
|
| 759 |
+ idIndex: utils.NewTruncIndex(), |
|
| 760 |
+ sysInfo: sysInfo, |
|
| 761 |
+ volumes: volumes, |
|
| 762 |
+ config: config, |
|
| 763 |
+ containerGraph: graph, |
|
| 764 |
+ driver: driver, |
|
| 765 |
+ sysInitPath: sysInitPath, |
|
| 766 |
+ execDriver: ed, |
|
| 767 |
+ eng: eng, |
|
| 768 |
+ } |
|
| 769 |
+ |
|
| 770 |
+ if err := runtime.restore(); err != nil {
|
|
| 771 |
+ return nil, err |
|
| 772 |
+ } |
|
| 773 |
+ return runtime, nil |
|
| 774 |
+} |
|
| 775 |
+ |
|
| 776 |
+func (runtime *Runtime) Close() error {
|
|
| 777 |
+ errorsStrings := []string{}
|
|
| 778 |
+ if err := portallocator.ReleaseAll(); err != nil {
|
|
| 779 |
+ utils.Errorf("portallocator.ReleaseAll(): %s", err)
|
|
| 780 |
+ errorsStrings = append(errorsStrings, err.Error()) |
|
| 781 |
+ } |
|
| 782 |
+ if err := runtime.driver.Cleanup(); err != nil {
|
|
| 783 |
+ utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
|
|
| 784 |
+ errorsStrings = append(errorsStrings, err.Error()) |
|
| 785 |
+ } |
|
| 786 |
+ if err := runtime.containerGraph.Close(); err != nil {
|
|
| 787 |
+ utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
|
|
| 788 |
+ errorsStrings = append(errorsStrings, err.Error()) |
|
| 789 |
+ } |
|
| 790 |
+ if len(errorsStrings) > 0 {
|
|
| 791 |
+ return fmt.Errorf("%s", strings.Join(errorsStrings, ", "))
|
|
| 792 |
+ } |
|
| 793 |
+ return nil |
|
| 794 |
+} |
|
| 795 |
+ |
|
| 796 |
+func (runtime *Runtime) Mount(container *Container) error {
|
|
| 797 |
+ dir, err := runtime.driver.Get(container.ID) |
|
| 798 |
+ if err != nil {
|
|
| 799 |
+ return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
|
|
| 800 |
+ } |
|
| 801 |
+ if container.basefs == "" {
|
|
| 802 |
+ container.basefs = dir |
|
| 803 |
+ } else if container.basefs != dir {
|
|
| 804 |
+ return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
|
|
| 805 |
+ runtime.driver, container.ID, container.basefs, dir) |
|
| 806 |
+ } |
|
| 807 |
+ return nil |
|
| 808 |
+} |
|
| 809 |
+ |
|
| 810 |
+func (runtime *Runtime) Unmount(container *Container) error {
|
|
| 811 |
+ runtime.driver.Put(container.ID) |
|
| 812 |
+ return nil |
|
| 813 |
+} |
|
| 814 |
+ |
|
| 815 |
+func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
|
|
| 816 |
+ if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
|
| 817 |
+ return differ.Changes(container.ID) |
|
| 818 |
+ } |
|
| 819 |
+ cDir, err := runtime.driver.Get(container.ID) |
|
| 820 |
+ if err != nil {
|
|
| 821 |
+ return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
|
| 822 |
+ } |
|
| 823 |
+ defer runtime.driver.Put(container.ID) |
|
| 824 |
+ initDir, err := runtime.driver.Get(container.ID + "-init") |
|
| 825 |
+ if err != nil {
|
|
| 826 |
+ return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
|
| 827 |
+ } |
|
| 828 |
+ defer runtime.driver.Put(container.ID + "-init") |
|
| 829 |
+ return archive.ChangesDirs(cDir, initDir) |
|
| 830 |
+} |
|
| 831 |
+ |
|
| 832 |
+func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
|
|
| 833 |
+ if differ, ok := runtime.driver.(graphdriver.Differ); ok {
|
|
| 834 |
+ return differ.Diff(container.ID) |
|
| 835 |
+ } |
|
| 836 |
+ |
|
| 837 |
+ changes, err := runtime.Changes(container) |
|
| 838 |
+ if err != nil {
|
|
| 839 |
+ return nil, err |
|
| 840 |
+ } |
|
| 841 |
+ |
|
| 842 |
+ cDir, err := runtime.driver.Get(container.ID) |
|
| 843 |
+ if err != nil {
|
|
| 844 |
+ return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
|
|
| 845 |
+ } |
|
| 846 |
+ |
|
| 847 |
+ archive, err := archive.ExportChanges(cDir, changes) |
|
| 848 |
+ if err != nil {
|
|
| 849 |
+ return nil, err |
|
| 850 |
+ } |
|
| 851 |
+ return utils.NewReadCloserWrapper(archive, func() error {
|
|
| 852 |
+ err := archive.Close() |
|
| 853 |
+ runtime.driver.Put(container.ID) |
|
| 854 |
+ return err |
|
| 855 |
+ }), nil |
|
| 856 |
+} |
|
| 857 |
+ |
|
| 858 |
+func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 859 |
+ return runtime.execDriver.Run(c.command, pipes, startCallback) |
|
| 860 |
+} |
|
| 861 |
+ |
|
| 862 |
+func (runtime *Runtime) Kill(c *Container, sig int) error {
|
|
| 863 |
+ return runtime.execDriver.Kill(c.command, sig) |
|
| 864 |
+} |
|
| 865 |
+ |
|
| 866 |
+// Nuke kills all containers then removes all content |
|
| 867 |
+// from the content root, including images, volumes and |
|
| 868 |
+// container filesystems. |
|
| 869 |
+// Again: this will remove your entire docker runtime! |
|
| 870 |
+func (runtime *Runtime) Nuke() error {
|
|
| 871 |
+ var wg sync.WaitGroup |
|
| 872 |
+ for _, container := range runtime.List() {
|
|
| 873 |
+ wg.Add(1) |
|
| 874 |
+ go func(c *Container) {
|
|
| 875 |
+ c.Kill() |
|
| 876 |
+ wg.Done() |
|
| 877 |
+ }(container) |
|
| 878 |
+ } |
|
| 879 |
+ wg.Wait() |
|
| 880 |
+ runtime.Close() |
|
| 881 |
+ |
|
| 882 |
+ return os.RemoveAll(runtime.config.Root) |
|
| 883 |
+} |
|
| 884 |
+ |
|
| 885 |
+// FIXME: this is a convenience function for integration tests |
|
| 886 |
+// which need direct access to runtime.graph. |
|
| 887 |
+// Once the tests switch to using engine and jobs, this method |
|
| 888 |
+// can go away. |
|
| 889 |
+func (runtime *Runtime) Graph() *graph.Graph {
|
|
| 890 |
+ return runtime.graph |
|
| 891 |
+} |
|
| 892 |
+ |
|
| 893 |
+func (runtime *Runtime) Repositories() *graph.TagStore {
|
|
| 894 |
+ return runtime.repositories |
|
| 895 |
+} |
|
| 896 |
+ |
|
| 897 |
+func (runtime *Runtime) Config() *daemonconfig.Config {
|
|
| 898 |
+ return runtime.config |
|
| 899 |
+} |
|
| 900 |
+ |
|
| 901 |
+func (runtime *Runtime) SystemConfig() *sysinfo.SysInfo {
|
|
| 902 |
+ return runtime.sysInfo |
|
| 903 |
+} |
|
| 904 |
+ |
|
| 905 |
+func (runtime *Runtime) SystemInitPath() string {
|
|
| 906 |
+ return runtime.sysInitPath |
|
| 907 |
+} |
|
| 908 |
+ |
|
| 909 |
+func (runtime *Runtime) GraphDriver() graphdriver.Driver {
|
|
| 910 |
+ return runtime.driver |
|
| 911 |
+} |
|
| 912 |
+ |
|
| 913 |
+func (runtime *Runtime) ExecutionDriver() execdriver.Driver {
|
|
| 914 |
+ return runtime.execDriver |
|
| 915 |
+} |
|
| 916 |
+ |
|
| 917 |
+func (runtime *Runtime) Volumes() *graph.Graph {
|
|
| 918 |
+ return runtime.volumes |
|
| 919 |
+} |
|
| 920 |
+ |
|
| 921 |
+func (runtime *Runtime) ContainerGraph() *graphdb.Database {
|
|
| 922 |
+ return runtime.containerGraph |
|
| 923 |
+} |
|
| 924 |
+ |
|
| 925 |
+func (runtime *Runtime) SetServer(server Server) {
|
|
| 926 |
+ runtime.srv = server |
|
| 927 |
+} |
|
| 928 |
+ |
|
| 929 |
+// History is a convenience type for storing a list of containers, |
|
| 930 |
+// ordered by creation date. |
|
| 931 |
+type History []*Container |
|
| 932 |
+ |
|
| 933 |
+func (history *History) Len() int {
|
|
| 934 |
+ return len(*history) |
|
| 935 |
+} |
|
| 936 |
+ |
|
| 937 |
+func (history *History) Less(i, j int) bool {
|
|
| 938 |
+ containers := *history |
|
| 939 |
+ return containers[j].When().Before(containers[i].When()) |
|
| 940 |
+} |
|
| 941 |
+ |
|
| 942 |
+func (history *History) Swap(i, j int) {
|
|
| 943 |
+ containers := *history |
|
| 944 |
+ tmp := containers[i] |
|
| 945 |
+ containers[i] = containers[j] |
|
| 946 |
+ containers[j] = tmp |
|
| 947 |
+} |
|
| 948 |
+ |
|
| 949 |
+func (history *History) Add(container *Container) {
|
|
| 950 |
+ *history = append(*history, container) |
|
| 951 |
+ sort.Sort(history) |
|
| 952 |
+} |
| 0 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import "sort" |
|
| 3 |
+ |
|
| 4 |
+type containerSorter struct {
|
|
| 5 |
+ containers []*Container |
|
| 6 |
+ by func(i, j *Container) bool |
|
| 7 |
+} |
|
| 8 |
+ |
|
| 9 |
+func (s *containerSorter) Len() int {
|
|
| 10 |
+ return len(s.containers) |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func (s *containerSorter) Swap(i, j int) {
|
|
| 14 |
+ s.containers[i], s.containers[j] = s.containers[j], s.containers[i] |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func (s *containerSorter) Less(i, j int) bool {
|
|
| 18 |
+ return s.by(s.containers[i], s.containers[j]) |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func sortContainers(containers []*Container, predicate func(i, j *Container) bool) {
|
|
| 22 |
+ s := &containerSorter{containers, predicate}
|
|
| 23 |
+ sort.Sort(s) |
|
| 24 |
+} |
| 0 | 25 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,81 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/utils" |
|
| 5 |
+ "sync" |
|
| 6 |
+ "time" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+type State struct {
|
|
| 10 |
+ sync.RWMutex |
|
| 11 |
+ Running bool |
|
| 12 |
+ Pid int |
|
| 13 |
+ ExitCode int |
|
| 14 |
+ StartedAt time.Time |
|
| 15 |
+ FinishedAt time.Time |
|
| 16 |
+ Ghost bool |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// String returns a human-readable description of the state |
|
| 20 |
+func (s *State) String() string {
|
|
| 21 |
+ s.RLock() |
|
| 22 |
+ defer s.RUnlock() |
|
| 23 |
+ |
|
| 24 |
+ if s.Running {
|
|
| 25 |
+ if s.Ghost {
|
|
| 26 |
+ return fmt.Sprintf("Ghost")
|
|
| 27 |
+ } |
|
| 28 |
+ return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
|
| 29 |
+ } |
|
| 30 |
+ return fmt.Sprintf("Exit %d", s.ExitCode)
|
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+func (s *State) IsRunning() bool {
|
|
| 34 |
+ s.RLock() |
|
| 35 |
+ defer s.RUnlock() |
|
| 36 |
+ |
|
| 37 |
+ return s.Running |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func (s *State) IsGhost() bool {
|
|
| 41 |
+ s.RLock() |
|
| 42 |
+ defer s.RUnlock() |
|
| 43 |
+ |
|
| 44 |
+ return s.Ghost |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (s *State) GetExitCode() int {
|
|
| 48 |
+ s.RLock() |
|
| 49 |
+ defer s.RUnlock() |
|
| 50 |
+ |
|
| 51 |
+ return s.ExitCode |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func (s *State) SetGhost(val bool) {
|
|
| 55 |
+ s.Lock() |
|
| 56 |
+ defer s.Unlock() |
|
| 57 |
+ |
|
| 58 |
+ s.Ghost = val |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func (s *State) SetRunning(pid int) {
|
|
| 62 |
+ s.Lock() |
|
| 63 |
+ defer s.Unlock() |
|
| 64 |
+ |
|
| 65 |
+ s.Running = true |
|
| 66 |
+ s.Ghost = false |
|
| 67 |
+ s.ExitCode = 0 |
|
| 68 |
+ s.Pid = pid |
|
| 69 |
+ s.StartedAt = time.Now().UTC() |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func (s *State) SetStopped(exitCode int) {
|
|
| 73 |
+ s.Lock() |
|
| 74 |
+ defer s.Unlock() |
|
| 75 |
+ |
|
| 76 |
+ s.Running = false |
|
| 77 |
+ s.Pid = 0 |
|
| 78 |
+ s.FinishedAt = time.Now().UTC() |
|
| 79 |
+ s.ExitCode = exitCode |
|
| 80 |
+} |
| 0 | 81 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,44 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/nat" |
|
| 4 |
+ "github.com/dotcloud/docker/pkg/namesgenerator" |
|
| 5 |
+ "github.com/dotcloud/docker/runconfig" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
|
|
| 9 |
+ if config.PortSpecs != nil {
|
|
| 10 |
+ ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs) |
|
| 11 |
+ if err != nil {
|
|
| 12 |
+ return err |
|
| 13 |
+ } |
|
| 14 |
+ config.PortSpecs = nil |
|
| 15 |
+ if len(bindings) > 0 {
|
|
| 16 |
+ if hostConfig == nil {
|
|
| 17 |
+ hostConfig = &runconfig.HostConfig{}
|
|
| 18 |
+ } |
|
| 19 |
+ hostConfig.PortBindings = bindings |
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ if config.ExposedPorts == nil {
|
|
| 23 |
+ config.ExposedPorts = make(nat.PortSet, len(ports)) |
|
| 24 |
+ } |
|
| 25 |
+ for k, v := range ports {
|
|
| 26 |
+ config.ExposedPorts[k] = v |
|
| 27 |
+ } |
|
| 28 |
+ } |
|
| 29 |
+ return nil |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+type checker struct {
|
|
| 33 |
+ runtime *Runtime |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func (c *checker) Exists(name string) bool {
|
|
| 37 |
+ return c.runtime.containerGraph.Exists("/" + name)
|
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+// Generate a random and unique name |
|
| 41 |
+func generateRandomName(runtime *Runtime) (string, error) {
|
|
| 42 |
+ return namesgenerator.GenerateRandomName(&checker{runtime})
|
|
| 43 |
+} |
| 0 | 44 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,332 @@ |
| 0 |
+package runtime |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/archive" |
|
| 5 |
+ "github.com/dotcloud/docker/pkg/mount" |
|
| 6 |
+ "github.com/dotcloud/docker/utils" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "log" |
|
| 9 |
+ "os" |
|
| 10 |
+ "path/filepath" |
|
| 11 |
+ "strings" |
|
| 12 |
+ "syscall" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+type BindMap struct {
|
|
| 16 |
+ SrcPath string |
|
| 17 |
+ DstPath string |
|
| 18 |
+ Mode string |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func prepareVolumesForContainer(container *Container) error {
|
|
| 22 |
+ if container.Volumes == nil || len(container.Volumes) == 0 {
|
|
| 23 |
+ container.Volumes = make(map[string]string) |
|
| 24 |
+ container.VolumesRW = make(map[string]bool) |
|
| 25 |
+ if err := applyVolumesFrom(container); err != nil {
|
|
| 26 |
+ return err |
|
| 27 |
+ } |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ if err := createVolumes(container); err != nil {
|
|
| 31 |
+ return err |
|
| 32 |
+ } |
|
| 33 |
+ return nil |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func mountVolumesForContainer(container *Container, envPath string) error {
|
|
| 37 |
+ // Setup the root fs as a bind mount of the base fs |
|
| 38 |
+ var ( |
|
| 39 |
+ root = container.RootfsPath() |
|
| 40 |
+ runtime = container.runtime |
|
| 41 |
+ ) |
|
| 42 |
+ if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) {
|
|
| 43 |
+ return nil |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ // Create a bind mount of the base fs as a place where we can add mounts |
|
| 47 |
+ // without affecting the ability to access the base fs |
|
| 48 |
+ if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ // Make sure the root fs is private so the mounts here don't propagate to basefs |
|
| 53 |
+ if err := mount.ForceMount(root, root, "none", "private"); err != nil {
|
|
| 54 |
+ return err |
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ // Mount docker specific files into the containers root fs |
|
| 58 |
+ if err := mount.Mount(runtime.sysInitPath, filepath.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil {
|
|
| 59 |
+ return err |
|
| 60 |
+ } |
|
| 61 |
+ if err := mount.Mount(envPath, filepath.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil {
|
|
| 62 |
+ return err |
|
| 63 |
+ } |
|
| 64 |
+ if err := mount.Mount(container.ResolvConfPath, filepath.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil {
|
|
| 65 |
+ return err |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ if container.HostnamePath != "" && container.HostsPath != "" {
|
|
| 69 |
+ if err := mount.Mount(container.HostnamePath, filepath.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil {
|
|
| 70 |
+ return err |
|
| 71 |
+ } |
|
| 72 |
+ if err := mount.Mount(container.HostsPath, filepath.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil {
|
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ // Mount user specified volumes |
|
| 78 |
+ for r, v := range container.Volumes {
|
|
| 79 |
+ mountAs := "ro" |
|
| 80 |
+ if container.VolumesRW[r] {
|
|
| 81 |
+ mountAs = "rw" |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ r = filepath.Join(root, r) |
|
| 85 |
+ if p, err := utils.FollowSymlinkInScope(r, root); err != nil {
|
|
| 86 |
+ return err |
|
| 87 |
+ } else {
|
|
| 88 |
+ r = p |
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ if err := mount.Mount(v, r, "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
|
|
| 92 |
+ return err |
|
| 93 |
+ } |
|
| 94 |
+ } |
|
| 95 |
+ return nil |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func unmountVolumesForContainer(container *Container) {
|
|
| 99 |
+ var ( |
|
| 100 |
+ root = container.RootfsPath() |
|
| 101 |
+ mounts = []string{
|
|
| 102 |
+ root, |
|
| 103 |
+ filepath.Join(root, "/.dockerinit"), |
|
| 104 |
+ filepath.Join(root, "/.dockerenv"), |
|
| 105 |
+ filepath.Join(root, "/etc/resolv.conf"), |
|
| 106 |
+ } |
|
| 107 |
+ ) |
|
| 108 |
+ |
|
| 109 |
+ if container.HostnamePath != "" && container.HostsPath != "" {
|
|
| 110 |
+ mounts = append(mounts, filepath.Join(root, "/etc/hostname"), filepath.Join(root, "/etc/hosts")) |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ for r := range container.Volumes {
|
|
| 114 |
+ mounts = append(mounts, filepath.Join(root, r)) |
|
| 115 |
+ } |
|
| 116 |
+ |
|
| 117 |
+ for i := len(mounts) - 1; i >= 0; i-- {
|
|
| 118 |
+ if lastError := mount.Unmount(mounts[i]); lastError != nil {
|
|
| 119 |
+ log.Printf("Failed to umount %v: %v", mounts[i], lastError)
|
|
| 120 |
+ } |
|
| 121 |
+ } |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func applyVolumesFrom(container *Container) error {
|
|
| 125 |
+ if container.Config.VolumesFrom != "" {
|
|
| 126 |
+ for _, containerSpec := range strings.Split(container.Config.VolumesFrom, ",") {
|
|
| 127 |
+ var ( |
|
| 128 |
+ mountRW = true |
|
| 129 |
+ specParts = strings.SplitN(containerSpec, ":", 2) |
|
| 130 |
+ ) |
|
| 131 |
+ |
|
| 132 |
+ switch len(specParts) {
|
|
| 133 |
+ case 0: |
|
| 134 |
+ return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
|
|
| 135 |
+ case 2: |
|
| 136 |
+ switch specParts[1] {
|
|
| 137 |
+ case "ro": |
|
| 138 |
+ mountRW = false |
|
| 139 |
+ case "rw": // mountRW is already true |
|
| 140 |
+ default: |
|
| 141 |
+ return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
|
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ c := container.runtime.Get(specParts[0]) |
|
| 146 |
+ if c == nil {
|
|
| 147 |
+ return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
|
|
| 148 |
+ } |
|
| 149 |
+ |
|
| 150 |
+ for volPath, id := range c.Volumes {
|
|
| 151 |
+ if _, exists := container.Volumes[volPath]; exists {
|
|
| 152 |
+ continue |
|
| 153 |
+ } |
|
| 154 |
+ if err := os.MkdirAll(filepath.Join(container.basefs, volPath), 0755); err != nil {
|
|
| 155 |
+ return err |
|
| 156 |
+ } |
|
| 157 |
+ container.Volumes[volPath] = id |
|
| 158 |
+ if isRW, exists := c.VolumesRW[volPath]; exists {
|
|
| 159 |
+ container.VolumesRW[volPath] = isRW && mountRW |
|
| 160 |
+ } |
|
| 161 |
+ } |
|
| 162 |
+ |
|
| 163 |
+ } |
|
| 164 |
+ } |
|
| 165 |
+ return nil |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+func getBindMap(container *Container) (map[string]BindMap, error) {
|
|
| 169 |
+ var ( |
|
| 170 |
+ // Create the requested bind mounts |
|
| 171 |
+ binds = make(map[string]BindMap) |
|
| 172 |
+ // Define illegal container destinations |
|
| 173 |
+ illegalDsts = []string{"/", "."}
|
|
| 174 |
+ ) |
|
| 175 |
+ |
|
| 176 |
+ for _, bind := range container.hostConfig.Binds {
|
|
| 177 |
+ // FIXME: factorize bind parsing in parseBind |
|
| 178 |
+ var ( |
|
| 179 |
+ src, dst, mode string |
|
| 180 |
+ arr = strings.Split(bind, ":") |
|
| 181 |
+ ) |
|
| 182 |
+ |
|
| 183 |
+ if len(arr) == 2 {
|
|
| 184 |
+ src = arr[0] |
|
| 185 |
+ dst = arr[1] |
|
| 186 |
+ mode = "rw" |
|
| 187 |
+ } else if len(arr) == 3 {
|
|
| 188 |
+ src = arr[0] |
|
| 189 |
+ dst = arr[1] |
|
| 190 |
+ mode = arr[2] |
|
| 191 |
+ } else {
|
|
| 192 |
+ return nil, fmt.Errorf("Invalid bind specification: %s", bind)
|
|
| 193 |
+ } |
|
| 194 |
+ |
|
| 195 |
+ // Bail if trying to mount to an illegal destination |
|
| 196 |
+ for _, illegal := range illegalDsts {
|
|
| 197 |
+ if dst == illegal {
|
|
| 198 |
+ return nil, fmt.Errorf("Illegal bind destination: %s", dst)
|
|
| 199 |
+ } |
|
| 200 |
+ } |
|
| 201 |
+ |
|
| 202 |
+ bindMap := BindMap{
|
|
| 203 |
+ SrcPath: src, |
|
| 204 |
+ DstPath: dst, |
|
| 205 |
+ Mode: mode, |
|
| 206 |
+ } |
|
| 207 |
+ binds[filepath.Clean(dst)] = bindMap |
|
| 208 |
+ } |
|
| 209 |
+ return binds, nil |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 212 |
+func createVolumes(container *Container) error {
|
|
| 213 |
+ binds, err := getBindMap(container) |
|
| 214 |
+ if err != nil {
|
|
| 215 |
+ return err |
|
| 216 |
+ } |
|
| 217 |
+ |
|
| 218 |
+ volumesDriver := container.runtime.volumes.Driver() |
|
| 219 |
+ // Create the requested volumes if they don't exist |
|
| 220 |
+ for volPath := range container.Config.Volumes {
|
|
| 221 |
+ volPath = filepath.Clean(volPath) |
|
| 222 |
+ volIsDir := true |
|
| 223 |
+ // Skip existing volumes |
|
| 224 |
+ if _, exists := container.Volumes[volPath]; exists {
|
|
| 225 |
+ continue |
|
| 226 |
+ } |
|
| 227 |
+ var srcPath string |
|
| 228 |
+ var isBindMount bool |
|
| 229 |
+ srcRW := false |
|
| 230 |
+ // If an external bind is defined for this volume, use that as a source |
|
| 231 |
+ if bindMap, exists := binds[volPath]; exists {
|
|
| 232 |
+ isBindMount = true |
|
| 233 |
+ srcPath = bindMap.SrcPath |
|
| 234 |
+ if strings.ToLower(bindMap.Mode) == "rw" {
|
|
| 235 |
+ srcRW = true |
|
| 236 |
+ } |
|
| 237 |
+ if stat, err := os.Stat(bindMap.SrcPath); err != nil {
|
|
| 238 |
+ return err |
|
| 239 |
+ } else {
|
|
| 240 |
+ volIsDir = stat.IsDir() |
|
| 241 |
+ } |
|
| 242 |
+ // Otherwise create an directory in $ROOT/volumes/ and use that |
|
| 243 |
+ } else {
|
|
| 244 |
+ |
|
| 245 |
+ // Do not pass a container as the parameter for the volume creation. |
|
| 246 |
+ // The graph driver using the container's information ( Image ) to |
|
| 247 |
+ // create the parent. |
|
| 248 |
+ c, err := container.runtime.volumes.Create(nil, "", "", "", "", nil, nil) |
|
| 249 |
+ if err != nil {
|
|
| 250 |
+ return err |
|
| 251 |
+ } |
|
| 252 |
+ srcPath, err = volumesDriver.Get(c.ID) |
|
| 253 |
+ if err != nil {
|
|
| 254 |
+ return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
|
|
| 255 |
+ } |
|
| 256 |
+ srcRW = true // RW by default |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ if p, err := filepath.EvalSymlinks(srcPath); err != nil {
|
|
| 260 |
+ return err |
|
| 261 |
+ } else {
|
|
| 262 |
+ srcPath = p |
|
| 263 |
+ } |
|
| 264 |
+ |
|
| 265 |
+ container.Volumes[volPath] = srcPath |
|
| 266 |
+ container.VolumesRW[volPath] = srcRW |
|
| 267 |
+ |
|
| 268 |
+ // Create the mountpoint |
|
| 269 |
+ volPath = filepath.Join(container.basefs, volPath) |
|
| 270 |
+ rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs) |
|
| 271 |
+ if err != nil {
|
|
| 272 |
+ return err |
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 275 |
+ if _, err := os.Stat(rootVolPath); err != nil {
|
|
| 276 |
+ if os.IsNotExist(err) {
|
|
| 277 |
+ if volIsDir {
|
|
| 278 |
+ if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
|
| 279 |
+ return err |
|
| 280 |
+ } |
|
| 281 |
+ } else {
|
|
| 282 |
+ if err := os.MkdirAll(filepath.Dir(rootVolPath), 0755); err != nil {
|
|
| 283 |
+ return err |
|
| 284 |
+ } |
|
| 285 |
+ if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil {
|
|
| 286 |
+ return err |
|
| 287 |
+ } else {
|
|
| 288 |
+ f.Close() |
|
| 289 |
+ } |
|
| 290 |
+ } |
|
| 291 |
+ } |
|
| 292 |
+ } |
|
| 293 |
+ |
|
| 294 |
+ // Do not copy or change permissions if we are mounting from the host |
|
| 295 |
+ if srcRW && !isBindMount {
|
|
| 296 |
+ volList, err := ioutil.ReadDir(rootVolPath) |
|
| 297 |
+ if err != nil {
|
|
| 298 |
+ return err |
|
| 299 |
+ } |
|
| 300 |
+ if len(volList) > 0 {
|
|
| 301 |
+ srcList, err := ioutil.ReadDir(srcPath) |
|
| 302 |
+ if err != nil {
|
|
| 303 |
+ return err |
|
| 304 |
+ } |
|
| 305 |
+ if len(srcList) == 0 {
|
|
| 306 |
+ // If the source volume is empty copy files from the root into the volume |
|
| 307 |
+ if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 308 |
+ return err |
|
| 309 |
+ } |
|
| 310 |
+ |
|
| 311 |
+ var stat syscall.Stat_t |
|
| 312 |
+ if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
|
| 313 |
+ return err |
|
| 314 |
+ } |
|
| 315 |
+ var srcStat syscall.Stat_t |
|
| 316 |
+ if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
|
| 317 |
+ return err |
|
| 318 |
+ } |
|
| 319 |
+ // Change the source volume's ownership if it differs from the root |
|
| 320 |
+ // files that were just copied |
|
| 321 |
+ if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
|
| 322 |
+ if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
|
| 323 |
+ return err |
|
| 324 |
+ } |
|
| 325 |
+ } |
|
| 326 |
+ } |
|
| 327 |
+ } |
|
| 328 |
+ } |
|
| 329 |
+ } |
|
| 330 |
+ return nil |
|
| 331 |
+} |
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"github.com/dotcloud/docker/pkg/graphdb" |
| 14 | 14 |
"github.com/dotcloud/docker/registry" |
| 15 | 15 |
"github.com/dotcloud/docker/runconfig" |
| 16 |
+ "github.com/dotcloud/docker/runtime" |
|
| 16 | 17 |
"github.com/dotcloud/docker/utils" |
| 17 | 18 |
"io" |
| 18 | 19 |
"io/ioutil" |
| ... | ... |
@@ -24,7 +25,7 @@ import ( |
| 24 | 24 |
"os/signal" |
| 25 | 25 |
"path" |
| 26 | 26 |
"path/filepath" |
| 27 |
- "runtime" |
|
| 27 |
+ goruntime "runtime" |
|
| 28 | 28 |
"strconv" |
| 29 | 29 |
"strings" |
| 30 | 30 |
"sync" |
| ... | ... |
@@ -41,9 +42,9 @@ func InitServer(job *engine.Job) engine.Status {
|
| 41 | 41 |
if err != nil {
|
| 42 | 42 |
return job.Error(err) |
| 43 | 43 |
} |
| 44 |
- if srv.runtime.config.Pidfile != "" {
|
|
| 44 |
+ if srv.runtime.Config().Pidfile != "" {
|
|
| 45 | 45 |
job.Logf("Creating pidfile")
|
| 46 |
- if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil {
|
|
| 46 |
+ if err := utils.CreatePidFile(srv.runtime.Config().Pidfile); err != nil {
|
|
| 47 | 47 |
// FIXME: do we need fatal here instead of returning a job error? |
| 48 | 48 |
log.Fatal(err) |
| 49 | 49 |
} |
| ... | ... |
@@ -54,7 +55,7 @@ func InitServer(job *engine.Job) engine.Status {
|
| 54 | 54 |
go func() {
|
| 55 | 55 |
sig := <-c |
| 56 | 56 |
log.Printf("Received signal '%v', exiting\n", sig)
|
| 57 |
- utils.RemovePidFile(srv.runtime.config.Pidfile) |
|
| 57 |
+ utils.RemovePidFile(srv.runtime.Config().Pidfile) |
|
| 58 | 58 |
srv.Close() |
| 59 | 59 |
os.Exit(0) |
| 60 | 60 |
}() |
| ... | ... |
@@ -181,10 +182,10 @@ func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
|
| 181 | 181 |
if err := container.Kill(); err != nil {
|
| 182 | 182 |
return job.Errorf("Cannot kill container %s: %s", name, err)
|
| 183 | 183 |
} |
| 184 |
- srv.LogEvent("kill", container.ID, srv.runtime.repositories.ImageName(container.Image))
|
|
| 184 |
+ srv.LogEvent("kill", container.ID, srv.runtime.Repositories().ImageName(container.Image))
|
|
| 185 | 185 |
} else {
|
| 186 | 186 |
// Otherwise, just send the requested signal |
| 187 |
- if err := container.kill(int(sig)); err != nil {
|
|
| 187 |
+ if err := container.KillSig(int(sig)); err != nil {
|
|
| 188 | 188 |
return job.Errorf("Cannot kill container %s: %s", name, err)
|
| 189 | 189 |
} |
| 190 | 190 |
// FIXME: Add event for signals |
| ... | ... |
@@ -293,7 +294,7 @@ func (srv *Server) ContainerExport(job *engine.Job) engine.Status {
|
| 293 | 293 |
return job.Errorf("%s: %s", name, err)
|
| 294 | 294 |
} |
| 295 | 295 |
// FIXME: factor job-specific LogEvent to engine.Job.Run() |
| 296 |
- srv.LogEvent("export", container.ID, srv.runtime.repositories.ImageName(container.Image))
|
|
| 296 |
+ srv.LogEvent("export", container.ID, srv.runtime.Repositories().ImageName(container.Image))
|
|
| 297 | 297 |
return engine.StatusOK |
| 298 | 298 |
} |
| 299 | 299 |
return job.Errorf("No such container: %s", name)
|
| ... | ... |
@@ -318,7 +319,7 @@ func (srv *Server) ImageExport(job *engine.Job) engine.Status {
|
| 318 | 318 |
|
| 319 | 319 |
utils.Debugf("Serializing %s", name)
|
| 320 | 320 |
|
| 321 |
- rootRepo, err := srv.runtime.repositories.Get(name) |
|
| 321 |
+ rootRepo, err := srv.runtime.Repositories().Get(name) |
|
| 322 | 322 |
if err != nil {
|
| 323 | 323 |
return job.Error(err) |
| 324 | 324 |
} |
| ... | ... |
@@ -494,7 +495,7 @@ func (srv *Server) Build(job *engine.Job) engine.Status {
|
| 494 | 494 |
return job.Error(err) |
| 495 | 495 |
} |
| 496 | 496 |
if repoName != "" {
|
| 497 |
- srv.runtime.repositories.Set(repoName, tag, id, false) |
|
| 497 |
+ srv.runtime.Repositories().Set(repoName, tag, id, false) |
|
| 498 | 498 |
} |
| 499 | 499 |
return engine.StatusOK |
| 500 | 500 |
} |
| ... | ... |
@@ -555,7 +556,7 @@ func (srv *Server) ImageLoad(job *engine.Job) engine.Status {
|
| 555 | 555 |
|
| 556 | 556 |
for imageName, tagMap := range repositories {
|
| 557 | 557 |
for tag, address := range tagMap {
|
| 558 |
- if err := srv.runtime.repositories.Set(imageName, tag, address, true); err != nil {
|
|
| 558 |
+ if err := srv.runtime.Repositories().Set(imageName, tag, address, true); err != nil {
|
|
| 559 | 559 |
return job.Error(err) |
| 560 | 560 |
} |
| 561 | 561 |
} |
| ... | ... |
@@ -588,13 +589,13 @@ func (srv *Server) recursiveLoad(address, tmpImageDir string) error {
|
| 588 | 588 |
return err |
| 589 | 589 |
} |
| 590 | 590 |
if img.Parent != "" {
|
| 591 |
- if !srv.runtime.graph.Exists(img.Parent) {
|
|
| 591 |
+ if !srv.runtime.Graph().Exists(img.Parent) {
|
|
| 592 | 592 |
if err := srv.recursiveLoad(img.Parent, tmpImageDir); err != nil {
|
| 593 | 593 |
return err |
| 594 | 594 |
} |
| 595 | 595 |
} |
| 596 | 596 |
} |
| 597 |
- if err := srv.runtime.graph.Register(imageJson, layer, img); err != nil {
|
|
| 597 |
+ if err := srv.runtime.Graph().Register(imageJson, layer, img); err != nil {
|
|
| 598 | 598 |
return err |
| 599 | 599 |
} |
| 600 | 600 |
} |
| ... | ... |
@@ -650,7 +651,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
|
| 650 | 650 |
sf := utils.NewStreamFormatter(job.GetenvBool("json"))
|
| 651 | 651 |
|
| 652 | 652 |
out := utils.NewWriteFlusher(job.Stdout) |
| 653 |
- img, err := srv.runtime.repositories.LookupImage(name) |
|
| 653 |
+ img, err := srv.runtime.Repositories().LookupImage(name) |
|
| 654 | 654 |
if err != nil {
|
| 655 | 655 |
return job.Error(err) |
| 656 | 656 |
} |
| ... | ... |
@@ -661,7 +662,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
|
| 661 | 661 |
} |
| 662 | 662 |
defer file.Body.Close() |
| 663 | 663 |
|
| 664 |
- config, _, _, err := runconfig.Parse([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
|
|
| 664 |
+ config, _, _, err := runconfig.Parse([]string{img.ID, "echo", "insert", url, path}, srv.runtime.SystemConfig())
|
|
| 665 | 665 |
if err != nil {
|
| 666 | 666 |
return job.Error(err) |
| 667 | 667 |
} |
| ... | ... |
@@ -685,7 +686,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
|
| 685 | 685 |
} |
| 686 | 686 |
|
| 687 | 687 |
func (srv *Server) ImagesViz(job *engine.Job) engine.Status {
|
| 688 |
- images, _ := srv.runtime.graph.Map() |
|
| 688 |
+ images, _ := srv.runtime.Graph().Map() |
|
| 689 | 689 |
if images == nil {
|
| 690 | 690 |
return engine.StatusOK |
| 691 | 691 |
} |
| ... | ... |
@@ -709,7 +710,7 @@ func (srv *Server) ImagesViz(job *engine.Job) engine.Status {
|
| 709 | 709 |
|
| 710 | 710 |
reporefs := make(map[string][]string) |
| 711 | 711 |
|
| 712 |
- for name, repository := range srv.runtime.repositories.Repositories {
|
|
| 712 |
+ for name, repository := range srv.runtime.Repositories().Repositories {
|
|
| 713 | 713 |
for tag, id := range repository {
|
| 714 | 714 |
reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag))
|
| 715 | 715 |
} |
| ... | ... |
@@ -728,22 +729,22 @@ func (srv *Server) Images(job *engine.Job) engine.Status {
|
| 728 | 728 |
err error |
| 729 | 729 |
) |
| 730 | 730 |
if job.GetenvBool("all") {
|
| 731 |
- allImages, err = srv.runtime.graph.Map() |
|
| 731 |
+ allImages, err = srv.runtime.Graph().Map() |
|
| 732 | 732 |
} else {
|
| 733 |
- allImages, err = srv.runtime.graph.Heads() |
|
| 733 |
+ allImages, err = srv.runtime.Graph().Heads() |
|
| 734 | 734 |
} |
| 735 | 735 |
if err != nil {
|
| 736 | 736 |
return job.Error(err) |
| 737 | 737 |
} |
| 738 | 738 |
lookup := make(map[string]*engine.Env) |
| 739 |
- for name, repository := range srv.runtime.repositories.Repositories {
|
|
| 739 |
+ for name, repository := range srv.runtime.Repositories().Repositories {
|
|
| 740 | 740 |
if job.Getenv("filter") != "" {
|
| 741 | 741 |
if match, _ := path.Match(job.Getenv("filter"), name); !match {
|
| 742 | 742 |
continue |
| 743 | 743 |
} |
| 744 | 744 |
} |
| 745 | 745 |
for tag, id := range repository {
|
| 746 |
- image, err := srv.runtime.graph.Get(id) |
|
| 746 |
+ image, err := srv.runtime.Graph().Get(id) |
|
| 747 | 747 |
if err != nil {
|
| 748 | 748 |
log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
|
| 749 | 749 |
continue |
| ... | ... |
@@ -793,7 +794,7 @@ func (srv *Server) Images(job *engine.Job) engine.Status {
|
| 793 | 793 |
} |
| 794 | 794 |
|
| 795 | 795 |
func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
|
| 796 |
- images, _ := srv.runtime.graph.Map() |
|
| 796 |
+ images, _ := srv.runtime.Graph().Map() |
|
| 797 | 797 |
var imgcount int |
| 798 | 798 |
if images == nil {
|
| 799 | 799 |
imgcount = 0 |
| ... | ... |
@@ -809,21 +810,21 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
|
| 809 | 809 |
initPath := utils.DockerInitPath("")
|
| 810 | 810 |
if initPath == "" {
|
| 811 | 811 |
// if that fails, we'll just return the path from the runtime |
| 812 |
- initPath = srv.runtime.sysInitPath |
|
| 812 |
+ initPath = srv.runtime.SystemInitPath() |
|
| 813 | 813 |
} |
| 814 | 814 |
|
| 815 | 815 |
v := &engine.Env{}
|
| 816 | 816 |
v.SetInt("Containers", len(srv.runtime.List()))
|
| 817 | 817 |
v.SetInt("Images", imgcount)
|
| 818 |
- v.Set("Driver", srv.runtime.driver.String())
|
|
| 819 |
- v.SetJson("DriverStatus", srv.runtime.driver.Status())
|
|
| 820 |
- v.SetBool("MemoryLimit", srv.runtime.sysInfo.MemoryLimit)
|
|
| 821 |
- v.SetBool("SwapLimit", srv.runtime.sysInfo.SwapLimit)
|
|
| 822 |
- v.SetBool("IPv4Forwarding", !srv.runtime.sysInfo.IPv4ForwardingDisabled)
|
|
| 818 |
+ v.Set("Driver", srv.runtime.GraphDriver().String())
|
|
| 819 |
+ v.SetJson("DriverStatus", srv.runtime.GraphDriver().Status())
|
|
| 820 |
+ v.SetBool("MemoryLimit", srv.runtime.SystemConfig().MemoryLimit)
|
|
| 821 |
+ v.SetBool("SwapLimit", srv.runtime.SystemConfig().SwapLimit)
|
|
| 822 |
+ v.SetBool("IPv4Forwarding", !srv.runtime.SystemConfig().IPv4ForwardingDisabled)
|
|
| 823 | 823 |
v.SetBool("Debug", os.Getenv("DEBUG") != "")
|
| 824 | 824 |
v.SetInt("NFd", utils.GetTotalUsedFds())
|
| 825 |
- v.SetInt("NGoroutines", runtime.NumGoroutine())
|
|
| 826 |
- v.Set("ExecutionDriver", srv.runtime.execDriver.Name())
|
|
| 825 |
+ v.SetInt("NGoroutines", goruntime.NumGoroutine())
|
|
| 826 |
+ v.Set("ExecutionDriver", srv.runtime.ExecutionDriver().Name())
|
|
| 827 | 827 |
v.SetInt("NEventsListener", len(srv.listeners))
|
| 828 | 828 |
v.Set("KernelVersion", kernelVersion)
|
| 829 | 829 |
v.Set("IndexServerAddress", auth.IndexServerAddress())
|
| ... | ... |
@@ -840,13 +841,13 @@ func (srv *Server) ImageHistory(job *engine.Job) engine.Status {
|
| 840 | 840 |
return job.Errorf("Usage: %s IMAGE", job.Name)
|
| 841 | 841 |
} |
| 842 | 842 |
name := job.Args[0] |
| 843 |
- foundImage, err := srv.runtime.repositories.LookupImage(name) |
|
| 843 |
+ foundImage, err := srv.runtime.Repositories().LookupImage(name) |
|
| 844 | 844 |
if err != nil {
|
| 845 | 845 |
return job.Error(err) |
| 846 | 846 |
} |
| 847 | 847 |
|
| 848 | 848 |
lookupMap := make(map[string][]string) |
| 849 |
- for name, repository := range srv.runtime.repositories.Repositories {
|
|
| 849 |
+ for name, repository := range srv.runtime.Repositories().Repositories {
|
|
| 850 | 850 |
for tag, id := range repository {
|
| 851 | 851 |
// If the ID already has a reverse lookup, do not update it unless for "latest" |
| 852 | 852 |
if _, exists := lookupMap[id]; !exists {
|
| ... | ... |
@@ -891,7 +892,7 @@ func (srv *Server) ContainerTop(job *engine.Job) engine.Status {
|
| 891 | 891 |
if !container.State.IsRunning() {
|
| 892 | 892 |
return job.Errorf("Container %s is not running", name)
|
| 893 | 893 |
} |
| 894 |
- pids, err := srv.runtime.execDriver.GetPidsForContainer(container.ID) |
|
| 894 |
+ pids, err := srv.runtime.ExecutionDriver().GetPidsForContainer(container.ID) |
|
| 895 | 895 |
if err != nil {
|
| 896 | 896 |
return job.Error(err) |
| 897 | 897 |
} |
| ... | ... |
@@ -984,7 +985,7 @@ func (srv *Server) Containers(job *engine.Job) engine.Status {
|
| 984 | 984 |
outs := engine.NewTable("Created", 0)
|
| 985 | 985 |
|
| 986 | 986 |
names := map[string][]string{}
|
| 987 |
- srv.runtime.containerGraph.Walk("/", func(p string, e *graphdb.Entity) error {
|
|
| 987 |
+ srv.runtime.ContainerGraph().Walk("/", func(p string, e *graphdb.Entity) error {
|
|
| 988 | 988 |
names[e.ID()] = append(names[e.ID()], p) |
| 989 | 989 |
return nil |
| 990 | 990 |
}, -1) |
| ... | ... |
@@ -1009,7 +1010,7 @@ func (srv *Server) Containers(job *engine.Job) engine.Status {
|
| 1009 | 1009 |
out := &engine.Env{}
|
| 1010 | 1010 |
out.Set("Id", container.ID)
|
| 1011 | 1011 |
out.SetList("Names", names[container.ID])
|
| 1012 |
- out.Set("Image", srv.runtime.repositories.ImageName(container.Image))
|
|
| 1012 |
+ out.Set("Image", srv.runtime.Repositories().ImageName(container.Image))
|
|
| 1013 | 1013 |
if len(container.Args) > 0 {
|
| 1014 | 1014 |
out.Set("Command", fmt.Sprintf("\"%s %s\"", container.Path, strings.Join(container.Args, " ")))
|
| 1015 | 1015 |
} else {
|
| ... | ... |
@@ -1067,7 +1068,7 @@ func (srv *Server) ImageTag(job *engine.Job) engine.Status {
|
| 1067 | 1067 |
if len(job.Args) == 3 {
|
| 1068 | 1068 |
tag = job.Args[2] |
| 1069 | 1069 |
} |
| 1070 |
- if err := srv.runtime.repositories.Set(job.Args[1], tag, job.Args[0], job.GetenvBool("force")); err != nil {
|
|
| 1070 |
+ if err := srv.runtime.Repositories().Set(job.Args[1], tag, job.Args[0], job.GetenvBool("force")); err != nil {
|
|
| 1071 | 1071 |
return job.Error(err) |
| 1072 | 1072 |
} |
| 1073 | 1073 |
return engine.StatusOK |
| ... | ... |
@@ -1092,7 +1093,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin |
| 1092 | 1092 |
} |
| 1093 | 1093 |
defer srv.poolRemove("pull", "layer:"+id)
|
| 1094 | 1094 |
|
| 1095 |
- if !srv.runtime.graph.Exists(id) {
|
|
| 1095 |
+ if !srv.runtime.Graph().Exists(id) {
|
|
| 1096 | 1096 |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling metadata", nil)) |
| 1097 | 1097 |
imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token) |
| 1098 | 1098 |
if err != nil {
|
| ... | ... |
@@ -1114,7 +1115,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin |
| 1114 | 1114 |
return err |
| 1115 | 1115 |
} |
| 1116 | 1116 |
defer layer.Close() |
| 1117 |
- if err := srv.runtime.graph.Register(imgJSON, utils.ProgressReader(layer, imgSize, out, sf, false, utils.TruncateID(id), "Downloading"), img); err != nil {
|
|
| 1117 |
+ if err := srv.runtime.Graph().Register(imgJSON, utils.ProgressReader(layer, imgSize, out, sf, false, utils.TruncateID(id), "Downloading"), img); err != nil {
|
|
| 1118 | 1118 |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error downloading dependent layers", nil)) |
| 1119 | 1119 |
return err |
| 1120 | 1120 |
} |
| ... | ... |
@@ -1249,11 +1250,11 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, localName |
| 1249 | 1249 |
if askedTag != "" && tag != askedTag {
|
| 1250 | 1250 |
continue |
| 1251 | 1251 |
} |
| 1252 |
- if err := srv.runtime.repositories.Set(localName, tag, id, true); err != nil {
|
|
| 1252 |
+ if err := srv.runtime.Repositories().Set(localName, tag, id, true); err != nil {
|
|
| 1253 | 1253 |
return err |
| 1254 | 1254 |
} |
| 1255 | 1255 |
} |
| 1256 |
- if err := srv.runtime.repositories.Save(); err != nil {
|
|
| 1256 |
+ if err := srv.runtime.Repositories().Save(); err != nil {
|
|
| 1257 | 1257 |
return err |
| 1258 | 1258 |
} |
| 1259 | 1259 |
|
| ... | ... |
@@ -1374,7 +1375,7 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri |
| 1374 | 1374 |
|
| 1375 | 1375 |
tagsByImage[id] = append(tagsByImage[id], tag) |
| 1376 | 1376 |
|
| 1377 |
- for img, err := srv.runtime.graph.Get(id); img != nil; img, err = img.GetParent() {
|
|
| 1377 |
+ for img, err := srv.runtime.Graph().Get(id); img != nil; img, err = img.GetParent() {
|
|
| 1378 | 1378 |
if err != nil {
|
| 1379 | 1379 |
return nil, nil, err |
| 1380 | 1380 |
} |
| ... | ... |
@@ -1481,7 +1482,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName |
| 1481 | 1481 |
|
| 1482 | 1482 |
func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID, ep string, token []string, sf *utils.StreamFormatter) (checksum string, err error) {
|
| 1483 | 1483 |
out = utils.NewWriteFlusher(out) |
| 1484 |
- jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgID, "json")) |
|
| 1484 |
+ jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.Graph().Root, imgID, "json")) |
|
| 1485 | 1485 |
if err != nil {
|
| 1486 | 1486 |
return "", fmt.Errorf("Cannot retrieve the path for {%s}: %s", imgID, err)
|
| 1487 | 1487 |
} |
| ... | ... |
@@ -1500,7 +1501,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID, |
| 1500 | 1500 |
return "", err |
| 1501 | 1501 |
} |
| 1502 | 1502 |
|
| 1503 |
- layerData, err := srv.runtime.graph.TempLayerArchive(imgID, archive.Uncompressed, sf, out) |
|
| 1503 |
+ layerData, err := srv.runtime.Graph().TempLayerArchive(imgID, archive.Uncompressed, sf, out) |
|
| 1504 | 1504 |
if err != nil {
|
| 1505 | 1505 |
return "", fmt.Errorf("Failed to generate layer archive: %s", err)
|
| 1506 | 1506 |
} |
| ... | ... |
@@ -1552,17 +1553,17 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
|
| 1552 | 1552 |
return job.Error(err) |
| 1553 | 1553 |
} |
| 1554 | 1554 |
|
| 1555 |
- img, err := srv.runtime.graph.Get(localName) |
|
| 1555 |
+ img, err := srv.runtime.Graph().Get(localName) |
|
| 1556 | 1556 |
r, err2 := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), endpoint) |
| 1557 | 1557 |
if err2 != nil {
|
| 1558 | 1558 |
return job.Error(err2) |
| 1559 | 1559 |
} |
| 1560 | 1560 |
|
| 1561 | 1561 |
if err != nil {
|
| 1562 |
- reposLen := len(srv.runtime.repositories.Repositories[localName]) |
|
| 1562 |
+ reposLen := len(srv.runtime.Repositories().Repositories[localName]) |
|
| 1563 | 1563 |
job.Stdout.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", localName, reposLen))
|
| 1564 | 1564 |
// If it fails, try to get the repository |
| 1565 |
- if localRepo, exists := srv.runtime.repositories.Repositories[localName]; exists {
|
|
| 1565 |
+ if localRepo, exists := srv.runtime.Repositories().Repositories[localName]; exists {
|
|
| 1566 | 1566 |
if err := srv.pushRepository(r, job.Stdout, localName, remoteName, localRepo, sf); err != nil {
|
| 1567 | 1567 |
return job.Error(err) |
| 1568 | 1568 |
} |
| ... | ... |
@@ -1618,13 +1619,13 @@ func (srv *Server) ImageImport(job *engine.Job) engine.Status {
|
| 1618 | 1618 |
defer progressReader.Close() |
| 1619 | 1619 |
archive = progressReader |
| 1620 | 1620 |
} |
| 1621 |
- img, err := srv.runtime.graph.Create(archive, "", "", "Imported from "+src, "", nil, nil) |
|
| 1621 |
+ img, err := srv.runtime.Graph().Create(archive, "", "", "Imported from "+src, "", nil, nil) |
|
| 1622 | 1622 |
if err != nil {
|
| 1623 | 1623 |
return job.Error(err) |
| 1624 | 1624 |
} |
| 1625 | 1625 |
// Optionally register the image at REPO/TAG |
| 1626 | 1626 |
if repo != "" {
|
| 1627 |
- if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil {
|
|
| 1627 |
+ if err := srv.runtime.Repositories().Set(repo, tag, img.ID, true); err != nil {
|
|
| 1628 | 1628 |
return job.Error(err) |
| 1629 | 1629 |
} |
| 1630 | 1630 |
} |
| ... | ... |
@@ -1643,11 +1644,11 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
|
| 1643 | 1643 |
if config.Memory != 0 && config.Memory < 524288 {
|
| 1644 | 1644 |
return job.Errorf("Minimum memory limit allowed is 512k")
|
| 1645 | 1645 |
} |
| 1646 |
- if config.Memory > 0 && !srv.runtime.sysInfo.MemoryLimit {
|
|
| 1646 |
+ if config.Memory > 0 && !srv.runtime.SystemConfig().MemoryLimit {
|
|
| 1647 | 1647 |
job.Errorf("Your kernel does not support memory limit capabilities. Limitation discarded.\n")
|
| 1648 | 1648 |
config.Memory = 0 |
| 1649 | 1649 |
} |
| 1650 |
- if config.Memory > 0 && !srv.runtime.sysInfo.SwapLimit {
|
|
| 1650 |
+ if config.Memory > 0 && !srv.runtime.SystemConfig().SwapLimit {
|
|
| 1651 | 1651 |
job.Errorf("Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
| 1652 | 1652 |
config.MemorySwap = -1 |
| 1653 | 1653 |
} |
| ... | ... |
@@ -1655,14 +1656,14 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
|
| 1655 | 1655 |
if err != nil {
|
| 1656 | 1656 |
return job.Error(err) |
| 1657 | 1657 |
} |
| 1658 |
- if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
|
| 1659 |
- job.Errorf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", defaultDns)
|
|
| 1660 |
- config.Dns = defaultDns |
|
| 1658 |
+ if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Config().Dns) == 0 && utils.CheckLocalDns(resolvConf) {
|
|
| 1659 |
+ job.Errorf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", runtime.DefaultDns)
|
|
| 1660 |
+ config.Dns = runtime.DefaultDns |
|
| 1661 | 1661 |
} |
| 1662 | 1662 |
|
| 1663 | 1663 |
container, buildWarnings, err := srv.runtime.Create(config, name) |
| 1664 | 1664 |
if err != nil {
|
| 1665 |
- if srv.runtime.graph.IsNotExist(err) {
|
|
| 1665 |
+ if srv.runtime.Graph().IsNotExist(err) {
|
|
| 1666 | 1666 |
_, tag := utils.ParseRepositoryTag(config.Image) |
| 1667 | 1667 |
if tag == "" {
|
| 1668 | 1668 |
tag = graph.DEFAULTTAG |
| ... | ... |
@@ -1671,10 +1672,10 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
|
| 1671 | 1671 |
} |
| 1672 | 1672 |
return job.Error(err) |
| 1673 | 1673 |
} |
| 1674 |
- if !container.Config.NetworkDisabled && srv.runtime.sysInfo.IPv4ForwardingDisabled {
|
|
| 1674 |
+ if !container.Config.NetworkDisabled && srv.runtime.SystemConfig().IPv4ForwardingDisabled {
|
|
| 1675 | 1675 |
job.Errorf("IPv4 forwarding is disabled.\n")
|
| 1676 | 1676 |
} |
| 1677 |
- srv.LogEvent("create", container.ID, srv.runtime.repositories.ImageName(container.Image))
|
|
| 1677 |
+ srv.LogEvent("create", container.ID, srv.runtime.Repositories().ImageName(container.Image))
|
|
| 1678 | 1678 |
// FIXME: this is necessary because runtime.Create might return a nil container |
| 1679 | 1679 |
// with a non-nil error. This should not happen! Once it's fixed we |
| 1680 | 1680 |
// can remove this workaround. |
| ... | ... |
@@ -1702,7 +1703,7 @@ func (srv *Server) ContainerRestart(job *engine.Job) engine.Status {
|
| 1702 | 1702 |
if err := container.Restart(int(t)); err != nil {
|
| 1703 | 1703 |
return job.Errorf("Cannot restart container %s: %s\n", name, err)
|
| 1704 | 1704 |
} |
| 1705 |
- srv.LogEvent("restart", container.ID, srv.runtime.repositories.ImageName(container.Image))
|
|
| 1705 |
+ srv.LogEvent("restart", container.ID, srv.runtime.Repositories().ImageName(container.Image))
|
|
| 1706 | 1706 |
} else {
|
| 1707 | 1707 |
return job.Errorf("No such container: %s\n", name)
|
| 1708 | 1708 |
} |
| ... | ... |
@@ -1724,7 +1725,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1724 | 1724 |
if container == nil {
|
| 1725 | 1725 |
return job.Errorf("No such link: %s", name)
|
| 1726 | 1726 |
} |
| 1727 |
- name, err := getFullName(name) |
|
| 1727 |
+ name, err := runtime.GetFullContainerName(name) |
|
| 1728 | 1728 |
if err != nil {
|
| 1729 | 1729 |
job.Error(err) |
| 1730 | 1730 |
} |
| ... | ... |
@@ -1732,21 +1733,17 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1732 | 1732 |
if parent == "/" {
|
| 1733 | 1733 |
return job.Errorf("Conflict, cannot remove the default name of the container")
|
| 1734 | 1734 |
} |
| 1735 |
- pe := srv.runtime.containerGraph.Get(parent) |
|
| 1735 |
+ pe := srv.runtime.ContainerGraph().Get(parent) |
|
| 1736 | 1736 |
if pe == nil {
|
| 1737 | 1737 |
return job.Errorf("Cannot get parent %s for name %s", parent, name)
|
| 1738 | 1738 |
} |
| 1739 | 1739 |
parentContainer := srv.runtime.Get(pe.ID()) |
| 1740 | 1740 |
|
| 1741 |
- if parentContainer != nil && parentContainer.activeLinks != nil {
|
|
| 1742 |
- if link, exists := parentContainer.activeLinks[n]; exists {
|
|
| 1743 |
- link.Disable() |
|
| 1744 |
- } else {
|
|
| 1745 |
- utils.Debugf("Could not find active link for %s", name)
|
|
| 1746 |
- } |
|
| 1741 |
+ if parentContainer != nil {
|
|
| 1742 |
+ parentContainer.DisableLink(n) |
|
| 1747 | 1743 |
} |
| 1748 | 1744 |
|
| 1749 |
- if err := srv.runtime.containerGraph.Delete(name); err != nil {
|
|
| 1745 |
+ if err := srv.runtime.ContainerGraph().Delete(name); err != nil {
|
|
| 1750 | 1746 |
return job.Error(err) |
| 1751 | 1747 |
} |
| 1752 | 1748 |
return engine.StatusOK |
| ... | ... |
@@ -1765,13 +1762,13 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1765 | 1765 |
if err := srv.runtime.Destroy(container); err != nil {
|
| 1766 | 1766 |
return job.Errorf("Cannot destroy container %s: %s", name, err)
|
| 1767 | 1767 |
} |
| 1768 |
- srv.LogEvent("destroy", container.ID, srv.runtime.repositories.ImageName(container.Image))
|
|
| 1768 |
+ srv.LogEvent("destroy", container.ID, srv.runtime.Repositories().ImageName(container.Image))
|
|
| 1769 | 1769 |
|
| 1770 | 1770 |
if removeVolume {
|
| 1771 | 1771 |
var ( |
| 1772 | 1772 |
volumes = make(map[string]struct{})
|
| 1773 | 1773 |
binds = make(map[string]struct{})
|
| 1774 |
- usedVolumes = make(map[string]*Container) |
|
| 1774 |
+ usedVolumes = make(map[string]*runtime.Container) |
|
| 1775 | 1775 |
) |
| 1776 | 1776 |
|
| 1777 | 1777 |
// the volume id is always the base of the path |
| ... | ... |
@@ -1780,7 +1777,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1780 | 1780 |
} |
| 1781 | 1781 |
|
| 1782 | 1782 |
// populate bind map so that they can be skipped and not removed |
| 1783 |
- for _, bind := range container.hostConfig.Binds {
|
|
| 1783 |
+ for _, bind := range container.HostConfig().Binds {
|
|
| 1784 | 1784 |
source := strings.Split(bind, ":")[0] |
| 1785 | 1785 |
// TODO: refactor all volume stuff, all of it |
| 1786 | 1786 |
// this is very important that we eval the link |
| ... | ... |
@@ -1819,7 +1816,7 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
| 1819 | 1819 |
log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID)
|
| 1820 | 1820 |
continue |
| 1821 | 1821 |
} |
| 1822 |
- if err := srv.runtime.volumes.Delete(volumeId); err != nil {
|
|
| 1822 |
+ if err := srv.runtime.Volumes().Delete(volumeId); err != nil {
|
|
| 1823 | 1823 |
return job.Errorf("Error calling volumes.Delete(%q): %v", volumeId, err)
|
| 1824 | 1824 |
} |
| 1825 | 1825 |
} |
| ... | ... |
@@ -1841,9 +1838,9 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force boo |
| 1841 | 1841 |
tag = graph.DEFAULTTAG |
| 1842 | 1842 |
} |
| 1843 | 1843 |
|
| 1844 |
- img, err := srv.runtime.repositories.LookupImage(name) |
|
| 1844 |
+ img, err := srv.runtime.Repositories().LookupImage(name) |
|
| 1845 | 1845 |
if err != nil {
|
| 1846 |
- if r, _ := srv.runtime.repositories.Get(repoName); r != nil {
|
|
| 1846 |
+ if r, _ := srv.runtime.Repositories().Get(repoName); r != nil {
|
|
| 1847 | 1847 |
return fmt.Errorf("No such image: %s:%s", repoName, tag)
|
| 1848 | 1848 |
} |
| 1849 | 1849 |
return fmt.Errorf("No such image: %s", name)
|
| ... | ... |
@@ -1854,14 +1851,14 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force boo |
| 1854 | 1854 |
tag = "" |
| 1855 | 1855 |
} |
| 1856 | 1856 |
|
| 1857 |
- byParents, err := srv.runtime.graph.ByParent() |
|
| 1857 |
+ byParents, err := srv.runtime.Graph().ByParent() |
|
| 1858 | 1858 |
if err != nil {
|
| 1859 | 1859 |
return err |
| 1860 | 1860 |
} |
| 1861 | 1861 |
|
| 1862 | 1862 |
//If delete by id, see if the id belong only to one repository |
| 1863 | 1863 |
if repoName == "" {
|
| 1864 |
- for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] {
|
|
| 1864 |
+ for _, repoAndTag := range srv.runtime.Repositories().ByID()[img.ID] {
|
|
| 1865 | 1865 |
parsedRepo, parsedTag := utils.ParseRepositoryTag(repoAndTag) |
| 1866 | 1866 |
if repoName == "" || repoName == parsedRepo {
|
| 1867 | 1867 |
repoName = parsedRepo |
| ... | ... |
@@ -1884,7 +1881,7 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force boo |
| 1884 | 1884 |
|
| 1885 | 1885 |
//Untag the current image |
| 1886 | 1886 |
for _, tag := range tags {
|
| 1887 |
- tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag) |
|
| 1887 |
+ tagDeleted, err := srv.runtime.Repositories().Delete(repoName, tag) |
|
| 1888 | 1888 |
if err != nil {
|
| 1889 | 1889 |
return err |
| 1890 | 1890 |
} |
| ... | ... |
@@ -1895,16 +1892,16 @@ func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force boo |
| 1895 | 1895 |
srv.LogEvent("untag", img.ID, "")
|
| 1896 | 1896 |
} |
| 1897 | 1897 |
} |
| 1898 |
- tags = srv.runtime.repositories.ByID()[img.ID] |
|
| 1898 |
+ tags = srv.runtime.Repositories().ByID()[img.ID] |
|
| 1899 | 1899 |
if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
|
| 1900 | 1900 |
if len(byParents[img.ID]) == 0 {
|
| 1901 | 1901 |
if err := srv.canDeleteImage(img.ID); err != nil {
|
| 1902 | 1902 |
return err |
| 1903 | 1903 |
} |
| 1904 |
- if err := srv.runtime.repositories.DeleteAll(img.ID); err != nil {
|
|
| 1904 |
+ if err := srv.runtime.Repositories().DeleteAll(img.ID); err != nil {
|
|
| 1905 | 1905 |
return err |
| 1906 | 1906 |
} |
| 1907 |
- if err := srv.runtime.graph.Delete(img.ID); err != nil {
|
|
| 1907 |
+ if err := srv.runtime.Graph().Delete(img.ID); err != nil {
|
|
| 1908 | 1908 |
return err |
| 1909 | 1909 |
} |
| 1910 | 1910 |
out := &engine.Env{}
|
| ... | ... |
@@ -1943,7 +1940,7 @@ func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
|
| 1943 | 1943 |
|
| 1944 | 1944 |
func (srv *Server) canDeleteImage(imgID string) error {
|
| 1945 | 1945 |
for _, container := range srv.runtime.List() {
|
| 1946 |
- parent, err := srv.runtime.repositories.LookupImage(container.Image) |
|
| 1946 |
+ parent, err := srv.runtime.Repositories().LookupImage(container.Image) |
|
| 1947 | 1947 |
if err != nil {
|
| 1948 | 1948 |
return err |
| 1949 | 1949 |
} |
| ... | ... |
@@ -1963,7 +1960,7 @@ func (srv *Server) canDeleteImage(imgID string) error {
|
| 1963 | 1963 |
func (srv *Server) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
|
| 1964 | 1964 |
|
| 1965 | 1965 |
// Retrieve all images |
| 1966 |
- images, err := srv.runtime.graph.Map() |
|
| 1966 |
+ images, err := srv.runtime.Graph().Map() |
|
| 1967 | 1967 |
if err != nil {
|
| 1968 | 1968 |
return nil, err |
| 1969 | 1969 |
} |
| ... | ... |
@@ -1980,7 +1977,7 @@ func (srv *Server) ImageGetCached(imgID string, config *runconfig.Config) (*imag |
| 1980 | 1980 |
// Loop on the children of the given image and check the config |
| 1981 | 1981 |
var match *image.Image |
| 1982 | 1982 |
for elem := range imageMap[imgID] {
|
| 1983 |
- img, err := srv.runtime.graph.Get(elem) |
|
| 1983 |
+ img, err := srv.runtime.Graph().Get(elem) |
|
| 1984 | 1984 |
if err != nil {
|
| 1985 | 1985 |
return nil, err |
| 1986 | 1986 |
} |
| ... | ... |
@@ -1993,7 +1990,7 @@ func (srv *Server) ImageGetCached(imgID string, config *runconfig.Config) (*imag |
| 1993 | 1993 |
return match, nil |
| 1994 | 1994 |
} |
| 1995 | 1995 |
|
| 1996 |
-func (srv *Server) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
|
|
| 1996 |
+func (srv *Server) RegisterLinks(container *runtime.Container, hostConfig *runconfig.HostConfig) error {
|
|
| 1997 | 1997 |
runtime := srv.runtime |
| 1998 | 1998 |
|
| 1999 | 1999 |
if hostConfig != nil && hostConfig.Links != nil {
|
| ... | ... |
@@ -2017,7 +2014,7 @@ func (srv *Server) RegisterLinks(container *Container, hostConfig *runconfig.Hos |
| 2017 | 2017 |
// After we load all the links into the runtime |
| 2018 | 2018 |
// set them to nil on the hostconfig |
| 2019 | 2019 |
hostConfig.Links = nil |
| 2020 |
- if err := container.writeHostConfig(); err != nil {
|
|
| 2020 |
+ if err := container.WriteHostConfig(); err != nil {
|
|
| 2021 | 2021 |
return err |
| 2022 | 2022 |
} |
| 2023 | 2023 |
} |
| ... | ... |
@@ -2065,13 +2062,13 @@ func (srv *Server) ContainerStart(job *engine.Job) engine.Status {
|
| 2065 | 2065 |
if err := srv.RegisterLinks(container, hostConfig); err != nil {
|
| 2066 | 2066 |
return job.Error(err) |
| 2067 | 2067 |
} |
| 2068 |
- container.hostConfig = hostConfig |
|
| 2068 |
+ container.SetHostConfig(hostConfig) |
|
| 2069 | 2069 |
container.ToDisk() |
| 2070 | 2070 |
} |
| 2071 | 2071 |
if err := container.Start(); err != nil {
|
| 2072 | 2072 |
return job.Errorf("Cannot start container %s: %s", name, err)
|
| 2073 | 2073 |
} |
| 2074 |
- srv.LogEvent("start", container.ID, runtime.repositories.ImageName(container.Image))
|
|
| 2074 |
+ srv.LogEvent("start", container.ID, runtime.Repositories().ImageName(container.Image))
|
|
| 2075 | 2075 |
|
| 2076 | 2076 |
return engine.StatusOK |
| 2077 | 2077 |
} |
| ... | ... |
@@ -2091,7 +2088,7 @@ func (srv *Server) ContainerStop(job *engine.Job) engine.Status {
|
| 2091 | 2091 |
if err := container.Stop(int(t)); err != nil {
|
| 2092 | 2092 |
return job.Errorf("Cannot stop container %s: %s\n", name, err)
|
| 2093 | 2093 |
} |
| 2094 |
- srv.LogEvent("stop", container.ID, srv.runtime.repositories.ImageName(container.Image))
|
|
| 2094 |
+ srv.LogEvent("stop", container.ID, srv.runtime.Repositories().ImageName(container.Image))
|
|
| 2095 | 2095 |
} else {
|
| 2096 | 2096 |
return job.Errorf("No such container: %s\n", name)
|
| 2097 | 2097 |
} |
| ... | ... |
@@ -2237,7 +2234,7 @@ func (srv *Server) ContainerAttach(job *engine.Job) engine.Status {
|
| 2237 | 2237 |
return engine.StatusOK |
| 2238 | 2238 |
} |
| 2239 | 2239 |
|
| 2240 |
-func (srv *Server) ContainerInspect(name string) (*Container, error) {
|
|
| 2240 |
+func (srv *Server) ContainerInspect(name string) (*runtime.Container, error) {
|
|
| 2241 | 2241 |
if container := srv.runtime.Get(name); container != nil {
|
| 2242 | 2242 |
return container, nil |
| 2243 | 2243 |
} |
| ... | ... |
@@ -2245,7 +2242,7 @@ func (srv *Server) ContainerInspect(name string) (*Container, error) {
|
| 2245 | 2245 |
} |
| 2246 | 2246 |
|
| 2247 | 2247 |
func (srv *Server) ImageInspect(name string) (*image.Image, error) {
|
| 2248 |
- if image, err := srv.runtime.repositories.LookupImage(name); err == nil && image != nil {
|
|
| 2248 |
+ if image, err := srv.runtime.Repositories().LookupImage(name); err == nil && image != nil {
|
|
| 2249 | 2249 |
return image, nil |
| 2250 | 2250 |
} |
| 2251 | 2251 |
return nil, fmt.Errorf("No such image: %s", name)
|
| ... | ... |
@@ -2280,9 +2277,9 @@ func (srv *Server) JobInspect(job *engine.Job) engine.Status {
|
| 2280 | 2280 |
return job.Error(errContainer) |
| 2281 | 2281 |
} |
| 2282 | 2282 |
object = &struct {
|
| 2283 |
- *Container |
|
| 2283 |
+ *runtime.Container |
|
| 2284 | 2284 |
HostConfig *runconfig.HostConfig |
| 2285 |
- }{container, container.hostConfig}
|
|
| 2285 |
+ }{container, container.HostConfig()}
|
|
| 2286 | 2286 |
default: |
| 2287 | 2287 |
return job.Errorf("Unknown kind: %s", kind)
|
| 2288 | 2288 |
} |
| ... | ... |
@@ -2322,7 +2319,7 @@ func (srv *Server) ContainerCopy(job *engine.Job) engine.Status {
|
| 2322 | 2322 |
} |
| 2323 | 2323 |
|
| 2324 | 2324 |
func NewServer(eng *engine.Engine, config *daemonconfig.Config) (*Server, error) {
|
| 2325 |
- runtime, err := NewRuntime(config, eng) |
|
| 2325 |
+ runtime, err := runtime.NewRuntime(config, eng) |
|
| 2326 | 2326 |
if err != nil {
|
| 2327 | 2327 |
return nil, err |
| 2328 | 2328 |
} |
| ... | ... |
@@ -2335,7 +2332,7 @@ func NewServer(eng *engine.Engine, config *daemonconfig.Config) (*Server, error) |
| 2335 | 2335 |
listeners: make(map[string]chan utils.JSONMessage), |
| 2336 | 2336 |
running: true, |
| 2337 | 2337 |
} |
| 2338 |
- runtime.srv = srv |
|
| 2338 |
+ runtime.SetServer(srv) |
|
| 2339 | 2339 |
return srv, nil |
| 2340 | 2340 |
} |
| 2341 | 2341 |
|
| ... | ... |
@@ -2403,7 +2400,7 @@ func (srv *Server) Close() error {
|
| 2403 | 2403 |
|
| 2404 | 2404 |
type Server struct {
|
| 2405 | 2405 |
sync.RWMutex |
| 2406 |
- runtime *Runtime |
|
| 2406 |
+ runtime *runtime.Runtime |
|
| 2407 | 2407 |
pullingPool map[string]chan struct{}
|
| 2408 | 2408 |
pushingPool map[string]chan struct{}
|
| 2409 | 2409 |
events []utils.JSONMessage |
| 2410 | 2410 |
deleted file mode 100644 |
| ... | ... |
@@ -1,25 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import "sort" |
|
| 4 |
- |
|
| 5 |
-type containerSorter struct {
|
|
| 6 |
- containers []*Container |
|
| 7 |
- by func(i, j *Container) bool |
|
| 8 |
-} |
|
| 9 |
- |
|
| 10 |
-func (s *containerSorter) Len() int {
|
|
| 11 |
- return len(s.containers) |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func (s *containerSorter) Swap(i, j int) {
|
|
| 15 |
- s.containers[i], s.containers[j] = s.containers[j], s.containers[i] |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-func (s *containerSorter) Less(i, j int) bool {
|
|
| 19 |
- return s.by(s.containers[i], s.containers[j]) |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-func sortContainers(containers []*Container, predicate func(i, j *Container) bool) {
|
|
| 23 |
- s := &containerSorter{containers, predicate}
|
|
| 24 |
- sort.Sort(s) |
|
| 25 |
-} |
| 26 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,81 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/utils" |
|
| 6 |
- "sync" |
|
| 7 |
- "time" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-type State struct {
|
|
| 11 |
- sync.RWMutex |
|
| 12 |
- Running bool |
|
| 13 |
- Pid int |
|
| 14 |
- ExitCode int |
|
| 15 |
- StartedAt time.Time |
|
| 16 |
- FinishedAt time.Time |
|
| 17 |
- Ghost bool |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-// String returns a human-readable description of the state |
|
| 21 |
-func (s *State) String() string {
|
|
| 22 |
- s.RLock() |
|
| 23 |
- defer s.RUnlock() |
|
| 24 |
- |
|
| 25 |
- if s.Running {
|
|
| 26 |
- if s.Ghost {
|
|
| 27 |
- return fmt.Sprintf("Ghost")
|
|
| 28 |
- } |
|
| 29 |
- return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
|
| 30 |
- } |
|
| 31 |
- return fmt.Sprintf("Exit %d", s.ExitCode)
|
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-func (s *State) IsRunning() bool {
|
|
| 35 |
- s.RLock() |
|
| 36 |
- defer s.RUnlock() |
|
| 37 |
- |
|
| 38 |
- return s.Running |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-func (s *State) IsGhost() bool {
|
|
| 42 |
- s.RLock() |
|
| 43 |
- defer s.RUnlock() |
|
| 44 |
- |
|
| 45 |
- return s.Ghost |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func (s *State) GetExitCode() int {
|
|
| 49 |
- s.RLock() |
|
| 50 |
- defer s.RUnlock() |
|
| 51 |
- |
|
| 52 |
- return s.ExitCode |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-func (s *State) SetGhost(val bool) {
|
|
| 56 |
- s.Lock() |
|
| 57 |
- defer s.Unlock() |
|
| 58 |
- |
|
| 59 |
- s.Ghost = val |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-func (s *State) SetRunning(pid int) {
|
|
| 63 |
- s.Lock() |
|
| 64 |
- defer s.Unlock() |
|
| 65 |
- |
|
| 66 |
- s.Running = true |
|
| 67 |
- s.Ghost = false |
|
| 68 |
- s.ExitCode = 0 |
|
| 69 |
- s.Pid = pid |
|
| 70 |
- s.StartedAt = time.Now().UTC() |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-func (s *State) SetStopped(exitCode int) {
|
|
| 74 |
- s.Lock() |
|
| 75 |
- defer s.Unlock() |
|
| 76 |
- |
|
| 77 |
- s.Running = false |
|
| 78 |
- s.Pid = 0 |
|
| 79 |
- s.FinishedAt = time.Now().UTC() |
|
| 80 |
- s.ExitCode = exitCode |
|
| 81 |
-} |
| ... | ... |
@@ -2,9 +2,6 @@ package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/dotcloud/docker/archive" |
| 5 |
- "github.com/dotcloud/docker/nat" |
|
| 6 |
- "github.com/dotcloud/docker/pkg/namesgenerator" |
|
| 7 |
- "github.com/dotcloud/docker/runconfig" |
|
| 8 | 5 |
"github.com/dotcloud/docker/utils" |
| 9 | 6 |
) |
| 10 | 7 |
|
| ... | ... |
@@ -12,45 +9,8 @@ type Change struct {
|
| 12 | 12 |
archive.Change |
| 13 | 13 |
} |
| 14 | 14 |
|
| 15 |
-func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
|
|
| 16 |
- if config.PortSpecs != nil {
|
|
| 17 |
- ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs) |
|
| 18 |
- if err != nil {
|
|
| 19 |
- return err |
|
| 20 |
- } |
|
| 21 |
- config.PortSpecs = nil |
|
| 22 |
- if len(bindings) > 0 {
|
|
| 23 |
- if hostConfig == nil {
|
|
| 24 |
- hostConfig = &runconfig.HostConfig{}
|
|
| 25 |
- } |
|
| 26 |
- hostConfig.PortBindings = bindings |
|
| 27 |
- } |
|
| 28 |
- |
|
| 29 |
- if config.ExposedPorts == nil {
|
|
| 30 |
- config.ExposedPorts = make(nat.PortSet, len(ports)) |
|
| 31 |
- } |
|
| 32 |
- for k, v := range ports {
|
|
| 33 |
- config.ExposedPorts[k] = v |
|
| 34 |
- } |
|
| 35 |
- } |
|
| 36 |
- return nil |
|
| 37 |
-} |
|
| 38 |
- |
|
| 39 | 15 |
// Links come in the format of |
| 40 | 16 |
// name:alias |
| 41 | 17 |
func parseLink(rawLink string) (map[string]string, error) {
|
| 42 | 18 |
return utils.PartParser("name:alias", rawLink)
|
| 43 | 19 |
} |
| 44 |
- |
|
| 45 |
-type checker struct {
|
|
| 46 |
- runtime *Runtime |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-func (c *checker) Exists(name string) bool {
|
|
| 50 |
- return c.runtime.containerGraph.Exists("/" + name)
|
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// Generate a random and unique name |
|
| 54 |
-func generateRandomName(runtime *Runtime) (string, error) {
|
|
| 55 |
- return namesgenerator.GenerateRandomName(&checker{runtime})
|
|
| 56 |
-} |
| 57 | 20 |
deleted file mode 100644 |
| ... | ... |
@@ -1,332 +0,0 @@ |
| 1 |
-package docker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/archive" |
|
| 6 |
- "github.com/dotcloud/docker/pkg/mount" |
|
| 7 |
- "github.com/dotcloud/docker/utils" |
|
| 8 |
- "io/ioutil" |
|
| 9 |
- "log" |
|
| 10 |
- "os" |
|
| 11 |
- "path/filepath" |
|
| 12 |
- "strings" |
|
| 13 |
- "syscall" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-type BindMap struct {
|
|
| 17 |
- SrcPath string |
|
| 18 |
- DstPath string |
|
| 19 |
- Mode string |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-func prepareVolumesForContainer(container *Container) error {
|
|
| 23 |
- if container.Volumes == nil || len(container.Volumes) == 0 {
|
|
| 24 |
- container.Volumes = make(map[string]string) |
|
| 25 |
- container.VolumesRW = make(map[string]bool) |
|
| 26 |
- if err := applyVolumesFrom(container); err != nil {
|
|
| 27 |
- return err |
|
| 28 |
- } |
|
| 29 |
- } |
|
| 30 |
- |
|
| 31 |
- if err := createVolumes(container); err != nil {
|
|
| 32 |
- return err |
|
| 33 |
- } |
|
| 34 |
- return nil |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-func mountVolumesForContainer(container *Container, envPath string) error {
|
|
| 38 |
- // Setup the root fs as a bind mount of the base fs |
|
| 39 |
- var ( |
|
| 40 |
- root = container.RootfsPath() |
|
| 41 |
- runtime = container.runtime |
|
| 42 |
- ) |
|
| 43 |
- if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) {
|
|
| 44 |
- return nil |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- // Create a bind mount of the base fs as a place where we can add mounts |
|
| 48 |
- // without affecting the ability to access the base fs |
|
| 49 |
- if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil {
|
|
| 50 |
- return err |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- // Make sure the root fs is private so the mounts here don't propagate to basefs |
|
| 54 |
- if err := mount.ForceMount(root, root, "none", "private"); err != nil {
|
|
| 55 |
- return err |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- // Mount docker specific files into the containers root fs |
|
| 59 |
- if err := mount.Mount(runtime.sysInitPath, filepath.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil {
|
|
| 60 |
- return err |
|
| 61 |
- } |
|
| 62 |
- if err := mount.Mount(envPath, filepath.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil {
|
|
| 63 |
- return err |
|
| 64 |
- } |
|
| 65 |
- if err := mount.Mount(container.ResolvConfPath, filepath.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil {
|
|
| 66 |
- return err |
|
| 67 |
- } |
|
| 68 |
- |
|
| 69 |
- if container.HostnamePath != "" && container.HostsPath != "" {
|
|
| 70 |
- if err := mount.Mount(container.HostnamePath, filepath.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil {
|
|
| 71 |
- return err |
|
| 72 |
- } |
|
| 73 |
- if err := mount.Mount(container.HostsPath, filepath.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil {
|
|
| 74 |
- return err |
|
| 75 |
- } |
|
| 76 |
- } |
|
| 77 |
- |
|
| 78 |
- // Mount user specified volumes |
|
| 79 |
- for r, v := range container.Volumes {
|
|
| 80 |
- mountAs := "ro" |
|
| 81 |
- if container.VolumesRW[r] {
|
|
| 82 |
- mountAs = "rw" |
|
| 83 |
- } |
|
| 84 |
- |
|
| 85 |
- r = filepath.Join(root, r) |
|
| 86 |
- if p, err := utils.FollowSymlinkInScope(r, root); err != nil {
|
|
| 87 |
- return err |
|
| 88 |
- } else {
|
|
| 89 |
- r = p |
|
| 90 |
- } |
|
| 91 |
- |
|
| 92 |
- if err := mount.Mount(v, r, "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
|
|
| 93 |
- return err |
|
| 94 |
- } |
|
| 95 |
- } |
|
| 96 |
- return nil |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-func unmountVolumesForContainer(container *Container) {
|
|
| 100 |
- var ( |
|
| 101 |
- root = container.RootfsPath() |
|
| 102 |
- mounts = []string{
|
|
| 103 |
- root, |
|
| 104 |
- filepath.Join(root, "/.dockerinit"), |
|
| 105 |
- filepath.Join(root, "/.dockerenv"), |
|
| 106 |
- filepath.Join(root, "/etc/resolv.conf"), |
|
| 107 |
- } |
|
| 108 |
- ) |
|
| 109 |
- |
|
| 110 |
- if container.HostnamePath != "" && container.HostsPath != "" {
|
|
| 111 |
- mounts = append(mounts, filepath.Join(root, "/etc/hostname"), filepath.Join(root, "/etc/hosts")) |
|
| 112 |
- } |
|
| 113 |
- |
|
| 114 |
- for r := range container.Volumes {
|
|
| 115 |
- mounts = append(mounts, filepath.Join(root, r)) |
|
| 116 |
- } |
|
| 117 |
- |
|
| 118 |
- for i := len(mounts) - 1; i >= 0; i-- {
|
|
| 119 |
- if lastError := mount.Unmount(mounts[i]); lastError != nil {
|
|
| 120 |
- log.Printf("Failed to umount %v: %v", mounts[i], lastError)
|
|
| 121 |
- } |
|
| 122 |
- } |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-func applyVolumesFrom(container *Container) error {
|
|
| 126 |
- if container.Config.VolumesFrom != "" {
|
|
| 127 |
- for _, containerSpec := range strings.Split(container.Config.VolumesFrom, ",") {
|
|
| 128 |
- var ( |
|
| 129 |
- mountRW = true |
|
| 130 |
- specParts = strings.SplitN(containerSpec, ":", 2) |
|
| 131 |
- ) |
|
| 132 |
- |
|
| 133 |
- switch len(specParts) {
|
|
| 134 |
- case 0: |
|
| 135 |
- return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
|
|
| 136 |
- case 2: |
|
| 137 |
- switch specParts[1] {
|
|
| 138 |
- case "ro": |
|
| 139 |
- mountRW = false |
|
| 140 |
- case "rw": // mountRW is already true |
|
| 141 |
- default: |
|
| 142 |
- return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
|
|
| 143 |
- } |
|
| 144 |
- } |
|
| 145 |
- |
|
| 146 |
- c := container.runtime.Get(specParts[0]) |
|
| 147 |
- if c == nil {
|
|
| 148 |
- return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
|
|
| 149 |
- } |
|
| 150 |
- |
|
| 151 |
- for volPath, id := range c.Volumes {
|
|
| 152 |
- if _, exists := container.Volumes[volPath]; exists {
|
|
| 153 |
- continue |
|
| 154 |
- } |
|
| 155 |
- if err := os.MkdirAll(filepath.Join(container.basefs, volPath), 0755); err != nil {
|
|
| 156 |
- return err |
|
| 157 |
- } |
|
| 158 |
- container.Volumes[volPath] = id |
|
| 159 |
- if isRW, exists := c.VolumesRW[volPath]; exists {
|
|
| 160 |
- container.VolumesRW[volPath] = isRW && mountRW |
|
| 161 |
- } |
|
| 162 |
- } |
|
| 163 |
- |
|
| 164 |
- } |
|
| 165 |
- } |
|
| 166 |
- return nil |
|
| 167 |
-} |
|
| 168 |
- |
|
| 169 |
-func getBindMap(container *Container) (map[string]BindMap, error) {
|
|
| 170 |
- var ( |
|
| 171 |
- // Create the requested bind mounts |
|
| 172 |
- binds = make(map[string]BindMap) |
|
| 173 |
- // Define illegal container destinations |
|
| 174 |
- illegalDsts = []string{"/", "."}
|
|
| 175 |
- ) |
|
| 176 |
- |
|
| 177 |
- for _, bind := range container.hostConfig.Binds {
|
|
| 178 |
- // FIXME: factorize bind parsing in parseBind |
|
| 179 |
- var ( |
|
| 180 |
- src, dst, mode string |
|
| 181 |
- arr = strings.Split(bind, ":") |
|
| 182 |
- ) |
|
| 183 |
- |
|
| 184 |
- if len(arr) == 2 {
|
|
| 185 |
- src = arr[0] |
|
| 186 |
- dst = arr[1] |
|
| 187 |
- mode = "rw" |
|
| 188 |
- } else if len(arr) == 3 {
|
|
| 189 |
- src = arr[0] |
|
| 190 |
- dst = arr[1] |
|
| 191 |
- mode = arr[2] |
|
| 192 |
- } else {
|
|
| 193 |
- return nil, fmt.Errorf("Invalid bind specification: %s", bind)
|
|
| 194 |
- } |
|
| 195 |
- |
|
| 196 |
- // Bail if trying to mount to an illegal destination |
|
| 197 |
- for _, illegal := range illegalDsts {
|
|
| 198 |
- if dst == illegal {
|
|
| 199 |
- return nil, fmt.Errorf("Illegal bind destination: %s", dst)
|
|
| 200 |
- } |
|
| 201 |
- } |
|
| 202 |
- |
|
| 203 |
- bindMap := BindMap{
|
|
| 204 |
- SrcPath: src, |
|
| 205 |
- DstPath: dst, |
|
| 206 |
- Mode: mode, |
|
| 207 |
- } |
|
| 208 |
- binds[filepath.Clean(dst)] = bindMap |
|
| 209 |
- } |
|
| 210 |
- return binds, nil |
|
| 211 |
-} |
|
| 212 |
- |
|
| 213 |
-func createVolumes(container *Container) error {
|
|
| 214 |
- binds, err := getBindMap(container) |
|
| 215 |
- if err != nil {
|
|
| 216 |
- return err |
|
| 217 |
- } |
|
| 218 |
- |
|
| 219 |
- volumesDriver := container.runtime.volumes.Driver() |
|
| 220 |
- // Create the requested volumes if they don't exist |
|
| 221 |
- for volPath := range container.Config.Volumes {
|
|
| 222 |
- volPath = filepath.Clean(volPath) |
|
| 223 |
- volIsDir := true |
|
| 224 |
- // Skip existing volumes |
|
| 225 |
- if _, exists := container.Volumes[volPath]; exists {
|
|
| 226 |
- continue |
|
| 227 |
- } |
|
| 228 |
- var srcPath string |
|
| 229 |
- var isBindMount bool |
|
| 230 |
- srcRW := false |
|
| 231 |
- // If an external bind is defined for this volume, use that as a source |
|
| 232 |
- if bindMap, exists := binds[volPath]; exists {
|
|
| 233 |
- isBindMount = true |
|
| 234 |
- srcPath = bindMap.SrcPath |
|
| 235 |
- if strings.ToLower(bindMap.Mode) == "rw" {
|
|
| 236 |
- srcRW = true |
|
| 237 |
- } |
|
| 238 |
- if stat, err := os.Stat(bindMap.SrcPath); err != nil {
|
|
| 239 |
- return err |
|
| 240 |
- } else {
|
|
| 241 |
- volIsDir = stat.IsDir() |
|
| 242 |
- } |
|
| 243 |
- // Otherwise create an directory in $ROOT/volumes/ and use that |
|
| 244 |
- } else {
|
|
| 245 |
- |
|
| 246 |
- // Do not pass a container as the parameter for the volume creation. |
|
| 247 |
- // The graph driver using the container's information ( Image ) to |
|
| 248 |
- // create the parent. |
|
| 249 |
- c, err := container.runtime.volumes.Create(nil, "", "", "", "", nil, nil) |
|
| 250 |
- if err != nil {
|
|
| 251 |
- return err |
|
| 252 |
- } |
|
| 253 |
- srcPath, err = volumesDriver.Get(c.ID) |
|
| 254 |
- if err != nil {
|
|
| 255 |
- return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
|
|
| 256 |
- } |
|
| 257 |
- srcRW = true // RW by default |
|
| 258 |
- } |
|
| 259 |
- |
|
| 260 |
- if p, err := filepath.EvalSymlinks(srcPath); err != nil {
|
|
| 261 |
- return err |
|
| 262 |
- } else {
|
|
| 263 |
- srcPath = p |
|
| 264 |
- } |
|
| 265 |
- |
|
| 266 |
- container.Volumes[volPath] = srcPath |
|
| 267 |
- container.VolumesRW[volPath] = srcRW |
|
| 268 |
- |
|
| 269 |
- // Create the mountpoint |
|
| 270 |
- volPath = filepath.Join(container.basefs, volPath) |
|
| 271 |
- rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs) |
|
| 272 |
- if err != nil {
|
|
| 273 |
- return err |
|
| 274 |
- } |
|
| 275 |
- |
|
| 276 |
- if _, err := os.Stat(rootVolPath); err != nil {
|
|
| 277 |
- if os.IsNotExist(err) {
|
|
| 278 |
- if volIsDir {
|
|
| 279 |
- if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
|
| 280 |
- return err |
|
| 281 |
- } |
|
| 282 |
- } else {
|
|
| 283 |
- if err := os.MkdirAll(filepath.Dir(rootVolPath), 0755); err != nil {
|
|
| 284 |
- return err |
|
| 285 |
- } |
|
| 286 |
- if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil {
|
|
| 287 |
- return err |
|
| 288 |
- } else {
|
|
| 289 |
- f.Close() |
|
| 290 |
- } |
|
| 291 |
- } |
|
| 292 |
- } |
|
| 293 |
- } |
|
| 294 |
- |
|
| 295 |
- // Do not copy or change permissions if we are mounting from the host |
|
| 296 |
- if srcRW && !isBindMount {
|
|
| 297 |
- volList, err := ioutil.ReadDir(rootVolPath) |
|
| 298 |
- if err != nil {
|
|
| 299 |
- return err |
|
| 300 |
- } |
|
| 301 |
- if len(volList) > 0 {
|
|
| 302 |
- srcList, err := ioutil.ReadDir(srcPath) |
|
| 303 |
- if err != nil {
|
|
| 304 |
- return err |
|
| 305 |
- } |
|
| 306 |
- if len(srcList) == 0 {
|
|
| 307 |
- // If the source volume is empty copy files from the root into the volume |
|
| 308 |
- if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 309 |
- return err |
|
| 310 |
- } |
|
| 311 |
- |
|
| 312 |
- var stat syscall.Stat_t |
|
| 313 |
- if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
|
| 314 |
- return err |
|
| 315 |
- } |
|
| 316 |
- var srcStat syscall.Stat_t |
|
| 317 |
- if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
|
| 318 |
- return err |
|
| 319 |
- } |
|
| 320 |
- // Change the source volume's ownership if it differs from the root |
|
| 321 |
- // files that were just copied |
|
| 322 |
- if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
|
| 323 |
- if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
|
| 324 |
- return err |
|
| 325 |
- } |
|
| 326 |
- } |
|
| 327 |
- } |
|
| 328 |
- } |
|
| 329 |
- } |
|
| 330 |
- } |
|
| 331 |
- return nil |
|
| 332 |
-} |