func (b *Provider) DestroyVolume(vol volume.Volume) error { zvol, err := b.owns(vol) if err != nil { return err } if vol.IsSnapshot() { if err := syscall.Unmount(vol.Location(), 0); err != nil { return err } os.Remove(vol.Location()) } if err := zvol.dataset.Destroy(zfs.DestroyForceUmount); err != nil { for i := 0; i < 5 && err != nil && IsDatasetBusyError(err); i++ { // sometimes zfs will claim to be busy as if files are still open even when all container processes are dead. // usually this goes away, so retry a few times. time.Sleep(1 * time.Second) err = zvol.dataset.Destroy(zfs.DestroyForceUmount) } if err != nil { return err } } os.Remove(zvol.basemount) delete(b.volumes, vol.Info().ID) return nil }
func (p *Provider) ForkVolume(vol volume.Volume) (volume.Volume, error) { zvol, err := p.owns(vol) if err != nil { return nil, err } if !vol.IsSnapshot() { return nil, fmt.Errorf("can only fork a snapshot") } id := random.UUID() info := &volume.Info{ID: id, Type: vol.Info().Type} v2 := &zfsVolume{ info: info, provider: zvol.provider, basemount: p.mountPath(info), } cloneID := fmt.Sprintf("%s/%s", zvol.provider.dataset.Name, id) v2.dataset, err = zvol.dataset.Clone(cloneID, map[string]string{ "mountpoint": v2.basemount, }) if err != nil { return nil, fmt.Errorf("could not fork volume: %s", err) } p.volumes[id] = v2 return v2, nil }
func (b *Provider) SendSnapshot(vol volume.Volume, haves []json.RawMessage, output io.Writer) error { zvol, err := b.owns(vol) if err != nil { return err } if !vol.IsSnapshot() { return fmt.Errorf("can only send a snapshot") } // zfs recv can only really accept snapshots that apply to the current tip var latestRemote string if haves != nil && len(haves) > 0 { have := &zfsHaves{} if err := json.Unmarshal(haves[len(haves)-1], have); err == nil { latestRemote = have.SnapID } } // look for intersection of existing snapshots on this volume; if so do incremental parentName := strings.SplitN(zvol.dataset.Name, "@", 2)[0] parentDataset, err := zfs.GetDataset(parentName) if err != nil { return err } snapshots, err := parentDataset.Snapshots() if err != nil { return err } // we can fly incremental iff the latest snap on the remote is available here useIncremental := false if latestRemote != "" { for _, snap := range snapshots { if strings.SplitN(snap.Name, "@", 2)[1] == latestRemote { useIncremental = true break } } } // at last, send: if useIncremental { sendCmd := exec.Command("zfs", "send", "-i", latestRemote, zvol.dataset.Name) sendCmd.Stdout = output return sendCmd.Run() } return zvol.dataset.SendSnapshot(output) }