func (s *storageZfs) zfsDestroy(path string) error { mountpoint, err := s.zfsGet(path, "mountpoint") if err != nil { return err } if mountpoint != "none" && shared.IsMountPoint(mountpoint) { err := syscall.Unmount(mountpoint, syscall.MNT_DETACH) if err != nil { s.log.Error("umount failed", log.Ctx{"err": err}) return err } } // Due to open fds or kernel refs, this may fail for a bit, give it 10s output, err := tryExec( "zfs", "destroy", "-r", fmt.Sprintf("%s/%s", s.zfsPool, path)) if err != nil { s.log.Error("zfs destroy failed", log.Ctx{"output": string(output)}) return fmt.Errorf("Failed to destroy ZFS filesystem: %s", output) } return nil }
// Things we don't need to care about func (s *storageZfs) ContainerStart(container container) error { fs := fmt.Sprintf("containers/%s", container.Name()) // Just in case the container filesystem got unmounted if !shared.IsMountPoint(shared.VarPath(fs)) { s.zfsMount(fs) } return nil }
func (s *storageZfs) zfsDestroy(path string) error { mountpoint, err := s.zfsGet(path, "mountpoint") if err != nil { return err } if mountpoint != "none" && shared.IsMountPoint(mountpoint) { err := syscall.Unmount(mountpoint, syscall.MNT_DETACH) if err != nil { s.log.Error("umount failed", log.Ctx{"err": err}) return err } } // Due to open fds or kernel refs, this may fail for a bit, give it 10s var output []byte for i := 0; i < 20; i++ { output, err = exec.Command( "zfs", "destroy", "-r", fmt.Sprintf("%s/%s", s.zfsPool, path)).CombinedOutput() if err == nil { break } time.Sleep(500 * time.Millisecond) } if err != nil { s.log.Error("zfs destroy failed", log.Ctx{"output": string(output)}) return err } return nil }
func (s *storageZfs) MigrationSink(container container, snapshots []container, conn *websocket.Conn) error { zfsRecv := func(zfsName string) error { zfsFsName := fmt.Sprintf("%s/%s", s.zfsPool, zfsName) args := []string{"receive", "-F", "-u", zfsFsName} cmd := exec.Command("zfs", args...) stdin, err := cmd.StdinPipe() if err != nil { return err } stderr, err := cmd.StderrPipe() if err != nil { return err } if err := cmd.Start(); err != nil { return err } <-shared.WebsocketRecvStream(stdin, conn) output, err := ioutil.ReadAll(stderr) if err != nil { shared.Debugf("problem reading zfs recv stderr %s", "err", err) } err = cmd.Wait() if err != nil { shared.Log.Error("problem with zfs recv", "output", string(output)) } return err } /* In some versions of zfs we can write `zfs recv -F` to mounted * filesystems, and in some versions we can't. So, let's always unmount * this fs (it's empty anyway) before we zfs recv. N.B. that `zfs recv` * of a snapshot also needs tha actual fs that it has snapshotted * unmounted, so we do this before receiving anything. * * Further, `zfs unmount` doesn't actually unmount things right away, * so we ask /proc/self/mountinfo whether or not this path is mounted * before continuing so that we're sure the fs is actually unmounted * before doing a recv. */ zfsName := fmt.Sprintf("containers/%s", container.Name()) fsPath := shared.VarPath(fmt.Sprintf("containers/%s.zfs", container.Name())) for i := 0; i < 20; i++ { if shared.IsMountPoint(fsPath) || s.zfsMounted(zfsName) { if err := s.zfsUnmount(zfsName); err != nil { shared.Log.Error("zfs umount error for", "path", zfsName, "err", err) } } else { break } time.Sleep(500 * time.Millisecond) } for _, snap := range snapshots { fields := strings.SplitN(snap.Name(), shared.SnapshotDelimiter, 2) name := fmt.Sprintf("containers/%s@snapshot-%s", fields[0], fields[1]) if err := zfsRecv(name); err != nil { return err } err := os.MkdirAll(shared.VarPath(fmt.Sprintf("snapshots/%s", fields[0])), 0700) if err != nil { return err } err = os.Symlink("on-zfs", shared.VarPath(fmt.Sprintf("snapshots/%s/%s.zfs", fields[0], fields[1]))) if err != nil { return err } } /* finally, do the real container */ if err := zfsRecv(zfsName); err != nil { return err } /* Sometimes, zfs recv mounts this anyway, even if we pass -u * (https://forums.freebsd.org/threads/zfs-receive-u-shouldnt-mount-received-filesystem-right.36844/) * but sometimes it doesn't. Let's try to mount, but not complain about * failure. */ s.zfsMount(zfsName) return nil }