diff --git a/apis/opts/mountpoint.go b/apis/opts/mountpoint.go index 4dbfcd86fe..f1fca35409 100644 --- a/apis/opts/mountpoint.go +++ b/apis/opts/mountpoint.go @@ -59,7 +59,6 @@ func ParseVolumesFrom(volume string) (string, string, error) { // ParseBindMode is used to parse the bind's mode. func ParseBindMode(mp *types.MountPoint, mode string) error { mp.RW = true - mp.CopyData = true defaultMode := 0 rwMode := 0 diff --git a/daemon/mgr/container.go b/daemon/mgr/container.go index 68cf737344..03ac71651b 100644 --- a/daemon/mgr/container.go +++ b/daemon/mgr/container.go @@ -1950,6 +1950,12 @@ func (mgr *ContainerManager) generateMountPoints(ctx context.Context, c *Contain return errors.Wrap(err, "failed to get mount point from volumes") } + // 5. populate the volumes + err = mgr.populateVolumes(ctx, c) + if err != nil { + return errors.Wrap(err, "failed to populate volumes") + } + return nil } @@ -1973,6 +1979,7 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont case 1: mp.Source = "" mp.Destination = parts[0] + mp.CopyData = true case 2: mp.Source = parts[0] mp.Destination = parts[1] @@ -1993,11 +2000,6 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont if mp.Source == "" { mp.Source = randomid.Generate() - - // Source is empty, anonymouse volume - if _, exist := c.Config.Volumes[mp.Destination]; !exist { - c.Config.Volumes[mp.Destination] = struct{}{} - } } err = opts.ParseBindMode(mp, mode) @@ -2037,6 +2039,7 @@ func (mgr *ContainerManager) getMountPointFromBinds(ctx context.Context, c *Cont mp.Named = false mp.Driver = "" } + mp.CopyData = true } if _, err = os.Stat(mp.Source); err != nil { @@ -2075,6 +2078,7 @@ func (mgr *ContainerManager) getMountPointFromVolumes(ctx context.Context, c *Co mp := new(types.MountPoint) mp.Name = name mp.Destination = dest + mp.CopyData = true mp.Source, mp.Driver, err = mgr.attachVolume(ctx, mp.Name, c) if err != nil { @@ -2104,10 +2108,6 @@ func (mgr *ContainerManager) getMountPointFromImage(ctx context.Context, c *Cont return errors.Wrapf(err, "failed to get image: %s", c.Image) } for dest := range image.Config.Volumes { - if _, exist := c.Config.Volumes[dest]; !exist { - c.Config.Volumes[dest] = struct{}{} - } - // check if volume has been created name := randomid.Generate() if _, exist := volumeSet[name]; exist { @@ -2122,6 +2122,7 @@ func (mgr *ContainerManager) getMountPointFromImage(ctx context.Context, c *Cont mp := new(types.MountPoint) mp.Name = name mp.Destination = dest + mp.CopyData = true mp.Source, mp.Driver, err = mgr.attachVolume(ctx, mp.Name, c) if err != nil { @@ -2172,6 +2173,7 @@ func (mgr *ContainerManager) getMountPointFromContainers(ctx context.Context, co Named: oldMountPoint.Named, RW: oldMountPoint.RW, Propagation: oldMountPoint.Propagation, + CopyData: false, } if _, exist := volumeSet[oldMountPoint.Name]; len(oldMountPoint.Name) > 0 && !exist { @@ -2182,7 +2184,6 @@ func (mgr *ContainerManager) getMountPointFromContainers(ctx context.Context, co return errors.Wrap(err, "failed to bind volume") } - container.Config.Volumes[mp.Destination] = struct{}{} volumeSet[mp.Name] = struct{}{} } @@ -2199,6 +2200,37 @@ func (mgr *ContainerManager) getMountPointFromContainers(ctx context.Context, co return nil } +func (mgr *ContainerManager) populateVolumes(ctx context.Context, c *Container) error { + err := mgr.Mount(ctx, c) + if err != nil { + return err + } + defer mgr.Unmount(ctx, c) + + for _, mnt := range c.Mounts { + if mnt.Driver == "tmpfs" { + continue + } + + if !mnt.CopyData { + continue + } + + logrus.Debugf("copying image data from %s:%s, to %s, path: %s", + c.ID, mnt.Destination, mnt.Name, mnt.Source) + + imagePath := path.Join(mgr.Store.Path(c.ID), "rootfs", mnt.Destination) + + err := copyImageContent(imagePath, mnt.Source) + if err != nil { + logrus.Errorf("failed to populate volume[name: %s, source: %s] err: %v", mnt.Name, mnt.Source, err) + return err + } + } + + return nil +} + func (mgr *ContainerManager) setMountPointDiskQuota(ctx context.Context, c *Container) error { if c.Config.DiskQuota == nil { if c.Config.QuotaID != "" && c.Config.QuotaID != "0" { @@ -2386,3 +2418,73 @@ func (mgr *ContainerManager) execProcessGC() { func (mgr *ContainerManager) NewSnapshotsSyncer(snapshotStore *SnapshotStore, duration time.Duration) *SnapshotsSyncer { return newSnapshotsSyncer(snapshotStore, mgr.Client, duration) } + +// Mount sets the container rootfs +func (mgr *ContainerManager) Mount(ctx context.Context, c *Container) error { + mounts, err := mgr.Client.GetMounts(ctx, c.ID) + if err != nil { + return err + } else if len(mounts) != 1 { + return fmt.Errorf("failed to get snapshot %s mounts: not equals 1", c.ID) + } + + mountPath := path.Join(mgr.Store.Path(c.ID), "rootfs") + + err = os.MkdirAll(mountPath, 0755) + if err != nil && !os.IsExist(err) { + return err + } + + return mounts[0].Mount(mountPath) +} + +// Unmount unsets the container rootfs +func (mgr *ContainerManager) Unmount(ctx context.Context, c *Container) error { + mountPath := path.Join(mgr.Store.Path(c.ID), "rootfs") + + err := mount.Unmount(mountPath, 0) + if err != nil { + return err + } + + return os.RemoveAll(mountPath) +} + +func copyImageContent(source, destination string) error { + volList, err := ioutil.ReadDir(source) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + + if len(volList) > 0 { + dstList, err := ioutil.ReadDir(destination) + if err != nil { + return err + } + if len(dstList) == 0 { + // TODO: refactor + + // If the source volume is empty, copies files from the root into the volume + commandLine := fmt.Sprintf("cp -r %s/* %s", source, destination) + + cmd := exec.Command("sh", "-c", commandLine) + out, err := cmd.CombinedOutput() + if err != nil { + logrus.Errorf("copyImageContent: %s, %v", string(out), err) + return err + } + } + } + return copyOwnership(source, destination) +} + +func copyOwnership(source, destination string) error { + fi, err := os.Stat(source) + if err != nil { + return err + } + return os.Chmod(destination, os.FileMode(fi.Mode())) +}