mirror of
https://github.com/rclone/rclone.git
synced 2025-02-23 22:12:08 +01:00
poll for Google Drive changes when mounted
This commit is contained in:
parent
5455d34f8c
commit
a2e3af0523
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -48,6 +48,9 @@ func NewFS(f fs.Fs) *FS {
|
|||||||
if readOnly {
|
if readOnly {
|
||||||
fsys.FS.ReadOnly()
|
fsys.FS.ReadOnly()
|
||||||
}
|
}
|
||||||
|
if pollInterval > 0 {
|
||||||
|
fsys.FS.PollChanges(pollInterval)
|
||||||
|
}
|
||||||
return fsys
|
return fsys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ var (
|
|||||||
debugFUSE = false
|
debugFUSE = false
|
||||||
noSeek = false
|
noSeek = false
|
||||||
dirCacheTime = 5 * 60 * time.Second
|
dirCacheTime = 5 * 60 * time.Second
|
||||||
|
pollInterval = time.Minute
|
||||||
// mount options
|
// mount options
|
||||||
readOnly = false
|
readOnly = false
|
||||||
allowNonEmpty = false
|
allowNonEmpty = false
|
||||||
@ -58,6 +59,7 @@ func init() {
|
|||||||
commandDefintion.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
|
commandDefintion.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
|
||||||
commandDefintion.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
|
commandDefintion.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
|
||||||
commandDefintion.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
|
commandDefintion.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
|
||||||
|
commandDefintion.Flags().DurationVarP(&pollInterval, "poll-interval", "", pollInterval, "Time to wait between polling for changes. Must be smaller than dir-cache-time. Only on supported remotes. Set to 0 to disable.")
|
||||||
// mount options
|
// mount options
|
||||||
commandDefintion.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
|
commandDefintion.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
|
||||||
commandDefintion.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")
|
commandDefintion.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")
|
||||||
|
@ -39,6 +39,9 @@ func NewFS(f fs.Fs) *FS {
|
|||||||
if readOnly {
|
if readOnly {
|
||||||
fsys.FS.ReadOnly()
|
fsys.FS.ReadOnly()
|
||||||
}
|
}
|
||||||
|
if pollInterval > 0 {
|
||||||
|
fsys.FS.PollChanges(pollInterval)
|
||||||
|
}
|
||||||
return fsys
|
return fsys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ var (
|
|||||||
debugFUSE = false
|
debugFUSE = false
|
||||||
noSeek = false
|
noSeek = false
|
||||||
dirCacheTime = 5 * 60 * time.Second
|
dirCacheTime = 5 * 60 * time.Second
|
||||||
|
pollInterval = time.Minute
|
||||||
// mount options
|
// mount options
|
||||||
readOnly = false
|
readOnly = false
|
||||||
allowNonEmpty = false
|
allowNonEmpty = false
|
||||||
@ -54,6 +55,7 @@ func init() {
|
|||||||
commandDefintion.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
|
commandDefintion.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
|
||||||
commandDefintion.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
|
commandDefintion.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
|
||||||
commandDefintion.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
|
commandDefintion.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
|
||||||
|
commandDefintion.Flags().DurationVarP(&pollInterval, "poll-interval", "", pollInterval, "Time to wait between polling for changes. Must be smaller than dir-cache-time. Only on supported remotes. Set to 0 to disable.")
|
||||||
// mount options
|
// mount options
|
||||||
commandDefintion.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
|
commandDefintion.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
|
||||||
commandDefintion.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")
|
commandDefintion.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")
|
||||||
|
@ -51,7 +51,20 @@ func NewFS(f fs.Fs) *FS {
|
|||||||
fsys := &FS{
|
fsys := &FS{
|
||||||
f: f,
|
f: f,
|
||||||
}
|
}
|
||||||
|
|
||||||
fsys.root = newDir(fsys, f, fsDir)
|
fsys.root = newDir(fsys, f, fsDir)
|
||||||
|
|
||||||
|
return fsys
|
||||||
|
}
|
||||||
|
|
||||||
|
// PollChanges will poll the remote every pollInterval for changes if the remote
|
||||||
|
// supports it. If a non-polling option is used, the given time interval can be
|
||||||
|
// ignored
|
||||||
|
func (fsys *FS) PollChanges(pollInterval time.Duration) *FS {
|
||||||
|
doDirChangeNotify := fsys.f.Features().DirChangeNotify
|
||||||
|
if doDirChangeNotify != nil {
|
||||||
|
doDirChangeNotify(fsys.root.ForgetPath, pollInterval)
|
||||||
|
}
|
||||||
return fsys
|
return fsys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ func TestFsMove2(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove2(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove2(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull2(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull2(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision2(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision2(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify2(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString2(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString2(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs2(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs2(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote2(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote2(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -45,6 +45,7 @@ func TestFsMove3(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove3(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove3(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull3(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull3(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision3(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision3(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify3(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString3(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString3(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs3(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs3(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote3(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote3(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -45,6 +45,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
102
drive/drive.go
102
drive/drive.go
@ -13,6 +13,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -889,6 +890,106 @@ func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DirChangeNotify polls for changes from the remote and hands the path to the
|
||||||
|
// given function. Only changes that can be resolved to a path through the
|
||||||
|
// DirCache will handled.
|
||||||
|
//
|
||||||
|
// Automatically restarts itself in case of unexpected behaviour of the remote.
|
||||||
|
//
|
||||||
|
// Close the returned channel to stop being notified.
|
||||||
|
func (f *Fs) DirChangeNotify(notifyFunc func(string), pollInterval time.Duration) chan bool {
|
||||||
|
quit := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
for {
|
||||||
|
f.dirchangeNotifyRunner(notifyFunc, pollInterval)
|
||||||
|
fs.Debugf(f, "Notify listener service ran into issues, restarting shortly.")
|
||||||
|
time.Sleep(pollInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return quit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fs) dirchangeNotifyRunner(notifyFunc func(string), pollInterval time.Duration) {
|
||||||
|
var err error
|
||||||
|
var changeList *drive.ChangeList
|
||||||
|
var pageToken string
|
||||||
|
var largestChangeID int64
|
||||||
|
|
||||||
|
var startPageToken *drive.StartPageToken
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
startPageToken, err = f.svc.Changes.GetStartPageToken().Do()
|
||||||
|
return shouldRetry(err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fs.Debugf(f, "Failed to get StartPageToken: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pageToken = startPageToken.StartPageToken
|
||||||
|
|
||||||
|
for {
|
||||||
|
fs.Debugf(f, "Checking for changes on remote")
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
changesCall := f.svc.Changes.List().PageToken(pageToken).Fields(googleapi.Field("nextPageToken,largestChangeId,newStartPageToken,items(fileId,file/parents(id))"))
|
||||||
|
if largestChangeID != 0 {
|
||||||
|
changesCall = changesCall.StartChangeId(largestChangeID)
|
||||||
|
}
|
||||||
|
if *driveListChunk > 0 {
|
||||||
|
changesCall = changesCall.MaxResults(*driveListChunk)
|
||||||
|
}
|
||||||
|
changeList, err = changesCall.Do()
|
||||||
|
return shouldRetry(err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fs.Debugf(f, "Failed to get Changes: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pathsToClear := make([]string, 0)
|
||||||
|
for _, change := range changeList.Items {
|
||||||
|
if path, ok := f.dirCache.GetInv(change.FileId); ok {
|
||||||
|
pathsToClear = append(pathsToClear, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if change.File != nil {
|
||||||
|
for _, parent := range change.File.Parents {
|
||||||
|
if path, ok := f.dirCache.GetInv(parent.Id); ok {
|
||||||
|
pathsToClear = append(pathsToClear, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastNotifiedPath := ""
|
||||||
|
sort.Strings(pathsToClear)
|
||||||
|
for _, path := range pathsToClear {
|
||||||
|
if lastNotifiedPath != "" && (path == lastNotifiedPath || strings.HasPrefix(path+"/", lastNotifiedPath)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lastNotifiedPath = path
|
||||||
|
notifyFunc(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if changeList.LargestChangeId != 0 {
|
||||||
|
largestChangeID = changeList.LargestChangeId
|
||||||
|
}
|
||||||
|
if changeList.NewStartPageToken != "" {
|
||||||
|
pageToken = changeList.NewStartPageToken
|
||||||
|
fs.Debugf(f, "All changes were processed. Waiting for more.")
|
||||||
|
time.Sleep(pollInterval)
|
||||||
|
} else if changeList.NextPageToken != "" {
|
||||||
|
pageToken = changeList.NextPageToken
|
||||||
|
fs.Debugf(f, "There are more changes pending, checking now.")
|
||||||
|
} else {
|
||||||
|
fs.Debugf(f, "Did not get any page token, something went wrong! %+v", changeList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DirCacheFlush resets the directory cache - used in testing as an
|
// DirCacheFlush resets the directory cache - used in testing as an
|
||||||
// optional interface
|
// optional interface
|
||||||
func (f *Fs) DirCacheFlush() {
|
func (f *Fs) DirCacheFlush() {
|
||||||
@ -1181,6 +1282,7 @@ var (
|
|||||||
_ fs.Mover = (*Fs)(nil)
|
_ fs.Mover = (*Fs)(nil)
|
||||||
_ fs.DirMover = (*Fs)(nil)
|
_ fs.DirMover = (*Fs)(nil)
|
||||||
_ fs.DirCacheFlusher = (*Fs)(nil)
|
_ fs.DirCacheFlusher = (*Fs)(nil)
|
||||||
|
_ fs.DirChangeNotifier = (*Fs)(nil)
|
||||||
_ fs.PutUncheckeder = (*Fs)(nil)
|
_ fs.PutUncheckeder = (*Fs)(nil)
|
||||||
_ fs.Object = (*Object)(nil)
|
_ fs.Object = (*Object)(nil)
|
||||||
_ fs.MimeTyper = &Object{}
|
_ fs.MimeTyper = &Object{}
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
19
fs/fs.go
19
fs/fs.go
@ -267,6 +267,11 @@ type Features struct {
|
|||||||
// If destination exists then return fs.ErrorDirExists
|
// If destination exists then return fs.ErrorDirExists
|
||||||
DirMove func(src Fs, srcRemote, dstRemote string) error
|
DirMove func(src Fs, srcRemote, dstRemote string) error
|
||||||
|
|
||||||
|
// DirChangeNotify calls the passed function with a path
|
||||||
|
// of a directory that has had changes. If the implementation
|
||||||
|
// uses polling, it should adhere to the given interval.
|
||||||
|
DirChangeNotify func(func(string), time.Duration) chan bool
|
||||||
|
|
||||||
// UnWrap returns the Fs that this Fs is wrapping
|
// UnWrap returns the Fs that this Fs is wrapping
|
||||||
UnWrap func() Fs
|
UnWrap func() Fs
|
||||||
|
|
||||||
@ -307,6 +312,9 @@ func (ft *Features) Fill(f Fs) *Features {
|
|||||||
if do, ok := f.(DirMover); ok {
|
if do, ok := f.(DirMover); ok {
|
||||||
ft.DirMove = do.DirMove
|
ft.DirMove = do.DirMove
|
||||||
}
|
}
|
||||||
|
if do, ok := f.(DirChangeNotifier); ok {
|
||||||
|
ft.DirChangeNotify = do.DirChangeNotify
|
||||||
|
}
|
||||||
if do, ok := f.(UnWrapper); ok {
|
if do, ok := f.(UnWrapper); ok {
|
||||||
ft.UnWrap = do.UnWrap
|
ft.UnWrap = do.UnWrap
|
||||||
}
|
}
|
||||||
@ -346,6 +354,9 @@ func (ft *Features) Mask(f Fs) *Features {
|
|||||||
if mask.DirMove == nil {
|
if mask.DirMove == nil {
|
||||||
ft.DirMove = nil
|
ft.DirMove = nil
|
||||||
}
|
}
|
||||||
|
if mask.DirChangeNotify == nil {
|
||||||
|
ft.DirChangeNotify = nil
|
||||||
|
}
|
||||||
// if mask.UnWrap == nil {
|
// if mask.UnWrap == nil {
|
||||||
// ft.UnWrap = nil
|
// ft.UnWrap = nil
|
||||||
// }
|
// }
|
||||||
@ -424,6 +435,14 @@ type DirMover interface {
|
|||||||
DirMove(src Fs, srcRemote, dstRemote string) error
|
DirMove(src Fs, srcRemote, dstRemote string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DirChangeNotifier is an optional interface for Fs
|
||||||
|
type DirChangeNotifier interface {
|
||||||
|
// DirChangeNotify calls the passed function with a path
|
||||||
|
// of a directory that has had changes. If the implementation
|
||||||
|
// uses polling, it should adhere to the given interval.
|
||||||
|
DirChangeNotify(func(string), time.Duration) chan bool
|
||||||
|
}
|
||||||
|
|
||||||
// UnWrapper is an optional interfaces for Fs
|
// UnWrapper is an optional interfaces for Fs
|
||||||
type UnWrapper interface {
|
type UnWrapper interface {
|
||||||
// UnWrap returns the Fs that this Fs is wrapping
|
// UnWrap returns the Fs that this Fs is wrapping
|
||||||
|
@ -577,6 +577,36 @@ func TestFsPrecision(t *testing.T) {
|
|||||||
// FIXME check expected precision
|
// FIXME check expected precision
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFsDirChangeNotify tests that changes to directories are properly
|
||||||
|
// propagated
|
||||||
|
//
|
||||||
|
// go test -v -remote TestDrive: -run '^Test(Setup|Init|FsDirChangeNotify)$' -verbose
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) {
|
||||||
|
skipIfNotOk(t)
|
||||||
|
|
||||||
|
// Check have DirChangeNotify
|
||||||
|
doDirChangeNotify := remote.Features().DirChangeNotify
|
||||||
|
if doDirChangeNotify == nil {
|
||||||
|
t.Skip("FS has no DirChangeNotify interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := fs.Mkdir(remote, "dir")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
changes := []string{}
|
||||||
|
quitChannel := doDirChangeNotify(func(x string) {
|
||||||
|
changes = append(changes, x)
|
||||||
|
}, time.Second)
|
||||||
|
defer func() { close(quitChannel) }()
|
||||||
|
|
||||||
|
err = fs.Mkdir(remote, "dir/subdir")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"dir"}, changes)
|
||||||
|
}
|
||||||
|
|
||||||
// TestObjectString tests the Object String method
|
// TestObjectString tests the Object String method
|
||||||
func TestObjectString(t *testing.T) {
|
func TestObjectString(t *testing.T) {
|
||||||
skipIfNotOk(t)
|
skipIfNotOk(t)
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
@ -44,6 +44,7 @@ func TestFsMove(t *testing.T) { fstests.TestFsMove(t) }
|
|||||||
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
func TestFsDirMove(t *testing.T) { fstests.TestFsDirMove(t) }
|
||||||
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
func TestFsRmdirFull(t *testing.T) { fstests.TestFsRmdirFull(t) }
|
||||||
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
func TestFsPrecision(t *testing.T) { fstests.TestFsPrecision(t) }
|
||||||
|
func TestFsDirChangeNotify(t *testing.T) { fstests.TestFsDirChangeNotify(t) }
|
||||||
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
func TestObjectString(t *testing.T) { fstests.TestObjectString(t) }
|
||||||
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
func TestObjectFs(t *testing.T) { fstests.TestObjectFs(t) }
|
||||||
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
func TestObjectRemote(t *testing.T) { fstests.TestObjectRemote(t) }
|
||||||
|
Loading…
Reference in New Issue
Block a user