diff --git a/main.go b/main.go index 734cd52..35c1a76 100644 --- a/main.go +++ b/main.go @@ -57,11 +57,6 @@ type ofsDriver struct { defMountSnapshots bool } -type snapshotRule struct { - count int - period time.Duration -} - var version = "1.0" var objfsVersion = "7.2" @@ -169,8 +164,27 @@ func (d *ofsDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) { func umount(v *ofsVolume, rt *ofsVolumeRt) error { log.WithFields(log.Fields{"name": v.Volume.Name}).Info("Unmount ObjectiveFS Volume") if !rt.mounted { - return fmt.Errorf("Volume is not mounted") + return fmt.Errorf("volume is not mounted") } + + if v.MountSnapshots { + // Snapshots must be unmounted first + // Note: We do NOT remove the mount directory as other distributed mounts may be using them + snapshotsPath := path.Join(v.Volume.Mountpoint, snapshotsDirectory) + if existingMounts, err := getMountedSnaphots(snapshotsPath, true); err == nil { + log.WithFields(log.Fields{"name": v.Volume.Name, "existingMounts": existingMounts}).Trace("Existing snapshot mounts") + for i, path := range existingMounts { + if err := exec.Command("umount", path).Run(); err != nil { + log.WithFields(log.Fields{"name": v.Volume.Name, "snapshot": i}).Warn("Failed to unmount a snapshot on unmount.") + } else { + log.WithFields(log.Fields{"name": v.Volume.Name, "snapshot": i}).Trace("Snapshot unmounted on unmount.") + } + } + } else { + log.WithFields(log.Fields{"name": v.Volume.Name, "Error": err}).Warn("Cannot determine existing snapshot mounts on unmount") + } + } + if err := exec.Command("umount", v.Volume.Mountpoint).Run(); err != nil { return err } @@ -330,44 +344,56 @@ func (d *ofsDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) if existingSnapshotsB, err := applyEnv(exec.Command("/sbin/mount.objectivefs", "list", "-sz", v.Fs), v.Env).Output(); err == nil { if existingSnapshots, err := parseExistingSnapshots(string(existingSnapshotsB), v.Fs); err == nil { log.WithFields(log.Fields{"name": r.Name, "existingSnapshots": existingSnapshots}).Trace("Existing snapshots") - if existingMounts, err := getMountedSnaphots(snapshotsPath); err == nil { + if existingMounts, err := getMountedSnaphots(snapshotsPath, true); err == nil { log.WithFields(log.Fields{"name": r.Name, "existingMounts": existingMounts}).Trace("Existing snapshot mounts") + if existingMountDirs, err := getMountedSnaphots(snapshotsPath, false); err == nil { + log.WithFields(log.Fields{"name": r.Name, "existingMountDirs": existingMounts}).Trace("Existing snapshot mount dirs") - // Remove mounts of expired snapshots - for i, path := range setMinus(existingMounts, expectedSnapshots) { - if err := exec.Command("umount", path).Run(); err != nil { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to unmount an expired snapshot.") - } else { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Debug("Snapshot unmounted.") - } - if err := os.Remove(path); err != nil { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to remove directory of an expired snapshot.") - } - } - - // Destroy old snapshots - for i, name := range setMinus(existingSnapshots, expectedSnapshots) { - expectedOutput := "Snapshot '" + name + "' destroyed." - if output, err := applyEnv(exec.Command("/sbin/mount.objectivefs", "destroy", name, "-f"), v.Env).Output(); err != nil || strings.TrimSpace(string(output)) != expectedOutput { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to destroy an expired snapshot.") - } else { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Debug("Snapshot destroyed.") - } - } - - // Add new mounts - for i, name := range setMinus(setIntersect(existingSnapshots, expectedSnapshots), existingMounts) { - dest := filepath.Join(snapshotsPath, i) - if err := os.Mkdir(dest, os.ModePerm); err == nil || os.IsExist(err) { - // Note: There is a missing "mount" argument so the mount continues running in a background process - if err := applyEnv(exec.Command("/sbin/mount.objectivefs", name, dest), v.Env).Run(); err != nil { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to mount a new snapshot.") - } else { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Debug("Snapshot mounted.") + // Remove left-over dirs of expired snapshots (ie. appears during downtime) + for i, path := range setMinus(setMinus(existingMountDirs, expectedSnapshots), existingMounts) { + if err := os.Remove(path); err != nil { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to remove directory of an off-line expired snapshot.") } - } else { - log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to create directory for a snapshot.") } + + // Remove mounts of expired snapshots + for i, path := range setMinus(existingMounts, expectedSnapshots) { + if err := exec.Command("umount", path).Run(); err != nil { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to unmount an expired snapshot.") + } else { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Debug("Snapshot unmounted.") + } + if err := os.Remove(path); err != nil { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to remove directory of an expired snapshot.") + } + } + + // Destroy old snapshots + for i, name := range setMinus(existingSnapshots, expectedSnapshots) { + expectedOutput := "Snapshot '" + name + "' destroyed." + if output, err := applyEnv(exec.Command("/sbin/mount.objectivefs", "destroy", name, "-f"), v.Env).Output(); err != nil || strings.TrimSpace(string(output)) != expectedOutput { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to destroy an expired snapshot.") + } else { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Debug("Snapshot destroyed.") + } + } + + // Add new mounts + for i, name := range setMinus(setIntersect(existingSnapshots, expectedSnapshots), existingMounts) { + dest := filepath.Join(snapshotsPath, i) + if err := os.Mkdir(dest, os.ModePerm); err == nil || os.IsExist(err) { + // Note: There is a missing "mount" argument so the mount continues running in a background process + if err := applyEnv(exec.Command("/sbin/mount.objectivefs", name, dest), v.Env).Run(); err != nil { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to mount a new snapshot.") + } else { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Debug("Snapshot mounted.") + } + } else { + log.WithFields(log.Fields{"name": r.Name, "snapshot": i}).Warn("Failed to create directory for a snapshot.") + } + } + } else { + log.WithFields(log.Fields{"name": r.Name, "Error": err}).Warn("Cannot determine existing snapshot mount dirs") } } else { log.WithFields(log.Fields{"name": r.Name, "Error": err}).Warn("Cannot determine existing snapshot mounts") @@ -629,7 +655,7 @@ func parseExistingSnapshots(data string, expectedPrefix string) (map[string]stri return result, nil } -func getMountedSnaphots(baseDir string) (map[string]string, error) { +func getMountedSnaphots(baseDir string, checkMount bool) (map[string]string, error) { if entries, err := os.ReadDir(baseDir); err != nil { return nil, err } else { @@ -637,7 +663,7 @@ func getMountedSnaphots(baseDir string) (map[string]string, error) { for _, i := range entries { if i.IsDir() { iPath := filepath.Join(baseDir, i.Name()) - if isObjFs, err := isObjectiveFsMount(iPath); err == nil && isObjFs { + if isObjFs, err := isObjectiveFsMount(iPath); !checkMount || (err == nil && isObjFs) { result[i.Name()] = iPath } }