diff --git a/runtime.go b/runtime.go index 4ea95a8b51..578d374b38 100644 --- a/runtime.go +++ b/runtime.go @@ -134,7 +134,7 @@ func (runtime *Runtime) containerRoot(id string) string { // Load reads the contents of a container from disk and registers // it with Register. // This is typically done at startup. -func (runtime *Runtime) Load(id string) (*Container, error) { +func (runtime *Runtime) load(id string) (*Container, error) { container := &Container{root: runtime.containerRoot(id)} if err := container.FromDisk(); err != nil { return nil, err @@ -145,9 +145,6 @@ func (runtime *Runtime) Load(id string) (*Container, error) { if container.State.Running { container.State.Ghost = true } - if err := runtime.Register(container); err != nil { - return nil, err - } return container, nil } @@ -289,9 +286,14 @@ func (runtime *Runtime) restore() error { if err != nil { return err } + + deviceSet := runtime.deviceSet + containers := []*Container{} + containersToMigrate := []*Container{} + for i, v := range dir { id := v.Name() - container, err := runtime.Load(id) + container, err := runtime.load(id) if i%21 == 0 && os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" { fmt.Printf("\b%c", wheel[i%4]) } @@ -300,10 +302,93 @@ func (runtime *Runtime) restore() error { continue } utils.Debugf("Loaded container %v", container.ID) + containers = append(containers, container) + + if !deviceSet.HasDevice(container.ID) { + containersToMigrate = append(containersToMigrate, container) + } + } + + // Migrate AUFS containers to device mapper + if len(containersToMigrate) > 0 { + if err := migrateToDeviceMapper(runtime, containersToMigrate); err != nil { + return err + } + } + + for _, container := range containers { + if err := runtime.Register(container); err != nil { + utils.Debugf("Failed to register container %s: %s", container.ID, err) + continue + } } if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" { fmt.Printf("\bdone.\n") } + + return nil +} + +func migrateToDeviceMapper(runtime *Runtime, containers []*Container) error { + var ( + image *Image + contents []os.FileInfo + err error + ) + + fmt.Printf("Migrating %d containers to new storage backend\n", len(containers)) + for _, container := range containers { + if container.State.Running { + fmt.Printf("WARNING - Cannot migrate %s because the container is running. Please stop the container and relaunch the daemon!") + continue + } + + fmt.Printf("Migrating %s\n", container.ID) + + if contents, err = ioutil.ReadDir(container.rwPath()); err != nil { + if !os.IsNotExist(err) { + fmt.Printf("Error reading rw dir %s\n", err) + } + continue + } + + if len(contents) == 0 { + fmt.Printf("Skipping migration of %s because rw layer contains no changes\n") + continue + } + + if image, err = runtime.graph.Get(container.Image); err != nil { + fmt.Printf("Failed to fetch image %s\n", err) + continue + } + + unmount := func() { + if err = image.Unmount(runtime, container.RootfsPath(), container.ID); err != nil { + fmt.Printf("Failed to unmount image %s\n", err) + } + } + + if err = image.Mount(runtime, container.RootfsPath(), container.rwPath(), container.ID); err != nil { + fmt.Printf("Failed to mount image %s\n", err) + continue + } + + if err = image.applyLayer(container.rwPath(), container.RootfsPath()); err != nil { + fmt.Printf("Failed to apply layer in storage backend %s\n", err) + unmount() + continue + } + + unmount() + + if err = os.RemoveAll(container.rwPath()); err != nil { + fmt.Printf("Failed to remove rw layer %s\n", err) + } + + fmt.Printf("Successful migration for %s\n", container.ID) + } + fmt.Printf("Migration complete\n") + return nil } diff --git a/runtime_test.go b/runtime_test.go index ba1947f4ae..e4c631daf6 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -154,7 +154,7 @@ func init() { deviceset := devmapper.NewDeviceSetDM(unitTestStoreDevicesBase) // Create a device, which triggers the initiation of the base FS // This avoids other tests doing this and timing out - deviceset.AddDevice("init","") + deviceset.AddDevice("init", "") // Make it our Store root if runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, deviceset, false); err != nil {