mirror of
				https://github.com/rclone/rclone.git
				synced 2025-10-30 23:17:59 +02:00 
			
		
		
		
	fs: Add --disable flag to disable optional features - fixes #1551
Eg to disable server side copy use `--disable copy`, to see a list of what you can disable, `--disable help`.
This commit is contained in:
		| @@ -365,6 +365,27 @@ connection to go through to a remote object storage system.  It is | ||||
|  | ||||
| Mode to run dedupe command in.  One of `interactive`, `skip`, `first`, `newest`, `oldest`, `rename`.  The default is `interactive`.  See the dedupe command for more information as to what these options mean. | ||||
|  | ||||
| ### --disable FEATURE,FEATURE,... ### | ||||
|  | ||||
| This disables a comma separated list of optional features. For example | ||||
| to disable server side move and server side copy use: | ||||
|  | ||||
|     --disable move,copy | ||||
|  | ||||
| The features can be put in in any case. | ||||
|  | ||||
| To see a list of which features can be disabled use: | ||||
|  | ||||
|     --disable help | ||||
|  | ||||
| See the overview [features](/overview/#features) and | ||||
| [optional features](/overview/#optional-features) to get an idea of | ||||
| which feature does what. | ||||
|  | ||||
| This flag can be useful for debugging and in exceptional circumstances | ||||
| (eg Google Drive limiting the total volume of Server Side Copies to | ||||
| 100GB/day). | ||||
|  | ||||
| ### -n, --dry-run ### | ||||
|  | ||||
| Do a trial run with no permanent changes.  Use this to see what rclone | ||||
|   | ||||
| @@ -275,9 +275,10 @@ limited to transferring about 2 files per second only.  Individual | ||||
| files may be transferred much faster at 100s of MBytes/s but lots of | ||||
| small files can take a long time. | ||||
|  | ||||
| Server side copies are also subject to a separate rate limit. If | ||||
| you see User rate limit exceeded errors, wait at least 24 hours and | ||||
| retry. | ||||
| Server side copies are also subject to a separate rate limit. If you | ||||
| see User rate limit exceeded errors, wait at least 24 hours and retry. | ||||
| You can disable server side copies with `--disable copy` to download | ||||
| and upload the files if you prefer. | ||||
|  | ||||
| ### Duplicated files ### | ||||
|  | ||||
|   | ||||
| @@ -100,6 +100,7 @@ var ( | ||||
| 	tpsLimit        = Float64P("tpslimit", "", 0, "Limit HTTP transactions per second to this.") | ||||
| 	tpsLimitBurst   = IntP("tpslimit-burst", "", 1, "Max burst of transactions for --tpslimit.") | ||||
| 	bindAddr        = StringP("bind", "", "", "Local address to bind to for outgoing connections, IPv4, IPv6 or name.") | ||||
| 	disableFeatures = StringP("disable", "", "", "Disable a comma separated list of features.  Use help to see a list.") | ||||
| 	logLevel        = LogLevelNotice | ||||
| 	statsLogLevel   = LogLevelInfo | ||||
| 	bwLimit         BwTimetable | ||||
| @@ -235,6 +236,7 @@ type ConfigInfo struct { | ||||
| 	TPSLimit           float64 | ||||
| 	TPSLimitBurst      int | ||||
| 	BindAddr           net.IP | ||||
| 	DisableFeatures    []string | ||||
| } | ||||
|  | ||||
| // Return the path to the configuration file | ||||
| @@ -410,6 +412,13 @@ func LoadConfig() { | ||||
| 		Config.BindAddr = addrs[0] | ||||
| 	} | ||||
|  | ||||
| 	if *disableFeatures != "" { | ||||
| 		if *disableFeatures == "help" { | ||||
| 			log.Fatalf("Possible backend features are: %s\n", strings.Join(new(Features).List(), ", ")) | ||||
| 		} | ||||
| 		Config.DisableFeatures = strings.Split(*disableFeatures, ",") | ||||
| 	} | ||||
|  | ||||
| 	// Load configuration file. | ||||
| 	var err error | ||||
| 	ConfigPath, err = filepath.Abs(*configFile) | ||||
|   | ||||
							
								
								
									
										46
									
								
								fs/fs.go
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								fs/fs.go
									
									
									
									
									
								
							| @@ -8,8 +8,10 @@ import ( | ||||
| 	"math" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/pkg/errors" | ||||
| @@ -347,6 +349,46 @@ type Features struct { | ||||
| 	ListR ListRFn | ||||
| } | ||||
|  | ||||
| // Disable nil's out the named feature.  If it isn't found then it | ||||
| // will log a message. | ||||
| func (ft *Features) Disable(name string) *Features { | ||||
| 	v := reflect.ValueOf(ft).Elem() | ||||
| 	vType := v.Type() | ||||
| 	for i := 0; i < v.NumField(); i++ { | ||||
| 		vName := vType.Field(i).Name | ||||
| 		field := v.Field(i) | ||||
| 		if strings.EqualFold(name, vName) { | ||||
| 			if !field.CanSet() { | ||||
| 				Errorf(nil, "Can't set Feature %q", name) | ||||
| 			} else { | ||||
| 				zero := reflect.Zero(field.Type()) | ||||
| 				field.Set(zero) | ||||
| 				Debugf(nil, "Reset feature %q", name) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return ft | ||||
| } | ||||
|  | ||||
| // List returns a slice of all the possible feature names | ||||
| func (ft *Features) List() (out []string) { | ||||
| 	v := reflect.ValueOf(ft).Elem() | ||||
| 	vType := v.Type() | ||||
| 	for i := 0; i < v.NumField(); i++ { | ||||
| 		out = append(out, vType.Field(i).Name) | ||||
| 	} | ||||
| 	return out | ||||
| } | ||||
|  | ||||
| // DisableList nil's out the comma separated list of named features. | ||||
| // If it isn't found then it will log a message. | ||||
| func (ft *Features) DisableList(list []string) *Features { | ||||
| 	for _, feature := range list { | ||||
| 		ft.Disable(strings.TrimSpace(feature)) | ||||
| 	} | ||||
| 	return ft | ||||
| } | ||||
|  | ||||
| // Fill fills in the function pointers in the Features struct from the | ||||
| // optional interfaces.  It returns the original updated Features | ||||
| // struct passed in. | ||||
| @@ -387,7 +429,7 @@ func (ft *Features) Fill(f Fs) *Features { | ||||
| 	if do, ok := f.(ListRer); ok { | ||||
| 		ft.ListR = do.ListR | ||||
| 	} | ||||
| 	return ft | ||||
| 	return ft.DisableList(Config.DisableFeatures) | ||||
| } | ||||
|  | ||||
| // Mask the Features with the Fs passed in | ||||
| @@ -438,7 +480,7 @@ func (ft *Features) Mask(f Fs) *Features { | ||||
| 	if mask.ListR == nil { | ||||
| 		ft.ListR = nil | ||||
| 	} | ||||
| 	return ft | ||||
| 	return ft.DisableList(Config.DisableFeatures) | ||||
| } | ||||
|  | ||||
| // Wrap makes a Copy of the features passed in, overriding the UnWrap | ||||
|   | ||||
							
								
								
									
										54
									
								
								fs/fs_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								fs/fs_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| package fs | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestFeaturesDisable(t *testing.T) { | ||||
| 	ft := new(Features) | ||||
| 	ft.Copy = func(src Object, remote string) (Object, error) { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	ft.CaseInsensitive = true | ||||
|  | ||||
| 	assert.NotNil(t, ft.Copy) | ||||
| 	assert.Nil(t, ft.Purge) | ||||
| 	ft.Disable("copy") | ||||
| 	assert.Nil(t, ft.Copy) | ||||
| 	assert.Nil(t, ft.Purge) | ||||
|  | ||||
| 	assert.True(t, ft.CaseInsensitive) | ||||
| 	assert.False(t, ft.DuplicateFiles) | ||||
| 	ft.Disable("caseinsensitive") | ||||
| 	assert.False(t, ft.CaseInsensitive) | ||||
| 	assert.False(t, ft.DuplicateFiles) | ||||
| } | ||||
|  | ||||
| func TestFeaturesList(t *testing.T) { | ||||
| 	ft := new(Features) | ||||
| 	names := strings.Join(ft.List(), ",") | ||||
| 	assert.True(t, strings.Contains(names, ",Copy,")) | ||||
| } | ||||
|  | ||||
| func TestFeaturesDisableList(t *testing.T) { | ||||
| 	ft := new(Features) | ||||
| 	ft.Copy = func(src Object, remote string) (Object, error) { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	ft.CaseInsensitive = true | ||||
|  | ||||
| 	assert.NotNil(t, ft.Copy) | ||||
| 	assert.Nil(t, ft.Purge) | ||||
| 	assert.True(t, ft.CaseInsensitive) | ||||
| 	assert.False(t, ft.DuplicateFiles) | ||||
|  | ||||
| 	ft.DisableList([]string{"copy", "caseinsensitive"}) | ||||
|  | ||||
| 	assert.Nil(t, ft.Copy) | ||||
| 	assert.Nil(t, ft.Purge) | ||||
| 	assert.False(t, ft.CaseInsensitive) | ||||
| 	assert.False(t, ft.DuplicateFiles) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user