snapsync 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a591ba7179ca6238815f6aa99b747ce66468010
4
- data.tar.gz: e8cdd470db1ed3e0118c8a6c4804c548f62fec96
3
+ metadata.gz: 6537d4331f2602702b307130fda024bdab894efa
4
+ data.tar.gz: ee06446c1c840e41217616528ec5ac10e29db0cb
5
5
  SHA512:
6
- metadata.gz: c2f58dd6c19a0cdca23cf65f6564a151ac126eee30af7e84bcfc7120be42f83460912f01ec8fcfdee627ca17f6df6d47d23e2418802450605c1be9a739d7f841
7
- data.tar.gz: f85021d8c1b5375b50b7f4b1e4bb89706d45e6ebc19f26c4ff69177ae6fc4755f6bb71822a64ac2621126c53295948cec608bcdb43c4096f54e3f4091c84fb9d
6
+ metadata.gz: a128cafd4224ebac10c98d333e90c3fee57812d19e9c57f5460b626d7008c302e28f7b3730d1b0535ae3f8230d80578f1020a0d0f926071ddd5de2d861526c63
7
+ data.tar.gz: 32cb9bb06f3340f10f7b2087ee4eb63a8f4411f7c421155420c7b1b264b03f6118e25fb93c05d95d7b2cd1e7bc307fa36b03b6801347ba0b5536e8effb241f47
data/Rakefile CHANGED
@@ -1,9 +1,10 @@
1
+ require "bundler/gem_tasks"
1
2
  require "rake/testtask"
2
3
 
3
4
  Rake::TestTask.new(:test) do |t|
4
5
  t.libs << "test"
5
6
  t.libs << "lib"
6
- t.test_files = FileList['test/**/*_test.rb']
7
+ t.test_files = FileList['test/**/test_*.rb']
7
8
  end
8
9
 
9
10
  task :default => :test
data/install.sh CHANGED
@@ -10,8 +10,9 @@ GEMFILE
10
10
  bundler install --standalone --binstubs
11
11
  if test -d /opt/snapsync; then
12
12
  sudo rm -rf /opt/snapsync
13
- sudo cp -r . /opt/snapsync
14
13
  fi
14
+ sudo cp -r . /opt/snapsync
15
+ sudo chmod go+rX /opt/snapsync
15
16
 
16
17
  if test -d /lib/systemd/system; then
17
18
  snapsync_gem=`bundler show snapsync`
@@ -22,4 +23,3 @@ if test -d /lib/systemd/system; then
22
23
  fi
23
24
 
24
25
  rm -rf $target
25
-
@@ -52,6 +52,11 @@ module Snapsync
52
52
  end
53
53
  end
54
54
  end
55
+
56
+ def partition_of(dir)
57
+ partitions = PartitionsMonitor.new
58
+ PartitionsMonitor.new.partition_of(dir)
59
+ end
55
60
  end
56
61
 
57
62
  desc 'sync CONFIG DIR', 'synchronizes the snapper configuration CONFIG with the snapsync target DIR'
@@ -91,8 +96,27 @@ module Snapsync
91
96
  end
92
97
  end
93
98
 
94
- desc 'init NAME DIR [POLICY]', 'creates a synchronization target, optionally specifying the synchronization and cleanup policy'
99
+ no_commands do
100
+ def normalize_policy(args)
101
+ policy =
102
+ if args.empty?
103
+ ['default', Array.new]
104
+ elsif args.size == 1
105
+ args + [Array.new]
106
+ else
107
+ [args.shift, args]
108
+ end
109
+
110
+ LocalTarget.parse_policy(*policy)
111
+ return *policy
112
+ end
113
+ end
114
+
115
+ desc 'init [NAME] DIR [POLICY]', 'creates a synchronization target, optionally adding it to the auto-sync targets and specifying the synchronization and cleanup policies'
95
116
  long_desc <<-EOD
117
+ NAME must be provided if DIR is to be added to the auto-sync targets (which
118
+ is the default).
119
+
96
120
  By default, the default policy is used. To change this, provide additional
97
121
  arguments as would be expected by the policy subcommand. Run snapsync help
98
122
  policy for more information
@@ -103,13 +127,52 @@ policy for more information
103
127
  desc: "if true (the default), add the newly created target to auto-sync"
104
128
  option :automount, type: :boolean, default: true,
105
129
  desc: 'whether the supporting partition should be auto-mounted by snapsync when needed or not (the default is yes). Only useful if --no-auto has not been provided on the command line.'
106
- def init(name, dir, *policy)
130
+ option :config_file, default: '/etc/snapsync.conf',
131
+ desc: 'the configuration file that should be updated'
132
+ def init(*args)
133
+ if options[:auto] && !options[:all]
134
+ raise ArgumentError, "cannot use --auto without --all"
135
+ end
136
+
137
+ if options[:auto]
138
+ if args.size < 2
139
+ self.class.handle_argument_error(self.class.all_commands['init'], nil, args, 2)
140
+ end
141
+ name, dir, *policy = *args
142
+ else
143
+ if args.size < 1
144
+ self.class.handle_argument_error(self.class.all_commands['init'], nil, args, 1)
145
+ end
146
+ dir, *policy = *args
147
+ end
107
148
  dir = Pathname.new(dir)
108
149
 
109
- if policy.empty?
110
- policy = ['default', Array.new]
111
- elsif policy.size == 1
112
- policy << Array.new
150
+ # Parse the policy option early to avoid breaking later
151
+ begin
152
+ policy = normalize_policy(policy)
153
+ rescue Exception
154
+ # Try to see if the user forgot to add the NAME option or added
155
+ # the name option but should not have
156
+ if options[:auto]
157
+ valid_policy = begin normalize_policy(args[1..-1])
158
+ true
159
+ rescue InvalidConfiguration
160
+ false
161
+ end
162
+ if valid_policy
163
+ raise ArgumentError, "--auto is set but it seems that you did not provide a name"
164
+ end
165
+ else
166
+ valid_policy = begin normalize_policy(args[2..-1])
167
+ true
168
+ rescue InvalidConfiguration
169
+ false
170
+ end
171
+ if valid_policy
172
+ raise ArgumentError, "--auto is not set but it seems that you provided a name"
173
+ end
174
+ end
175
+ raise ArgumentError, "invalid policy #{policy}"
113
176
  end
114
177
 
115
178
  dirs = Array.new
@@ -134,31 +197,39 @@ policy for more information
134
197
  end
135
198
  end
136
199
 
137
- if options[:auto]
138
- if !options[:all]
139
- Snapsync.warn "cannot use --auto without --all"
140
- else
141
- auto_add(name, dir)
142
- end
200
+ # We check that both options are set together for some added safety,
201
+ # but it's checked at the top of the method
202
+ if options[:auto] && options[:all]
203
+ auto_add(name, dir)
143
204
  end
144
205
  end
145
206
 
146
207
  desc 'auto-add NAME DIR', "add DIR to the set of targets for auto-sync"
147
208
  option :automount, type: :boolean, default: true,
148
209
  desc: 'whether the supporting partition should be auto-mounted by snapsync when needed or not (the default is yes)'
210
+ option :config_file, default: '/etc/snapsync.conf',
211
+ desc: 'the configuration file that should be updated'
149
212
  def auto_add(name, dir)
150
- partitions = PartitionsMonitor.new
151
- uuid, relative = partitions.partition_of(Pathname.new(dir))
152
-
153
- conf_path = Pathname.new('/etc/snapsync.conf')
213
+ uuid, relative = partition_of(Pathname.new(dir))
214
+ conf_path = Pathname.new(options[:config_file])
154
215
 
155
216
  autosync = AutoSync.new
156
- autosync.load_config(conf_path)
217
+ if conf_path.exist?
218
+ autosync.load_config(conf_path)
219
+ end
157
220
  exists = autosync.each_autosync_target.find do |t|
158
221
  t.partition_uuid == uuid && t.path.cleanpath == relative.cleanpath
159
222
  end
160
223
  if exists
161
- if exists.automount == options[:automount]
224
+ if !exists.name
225
+ if (exists.automount ^ options[:automount]) && name
226
+ Snapsync.info "already exists without a name, setting the name to #{name}"
227
+ elsif name
228
+ Snapsync.info "already exists without a name and a different automount flag, setting the name to #{name} and updating the automount flag"
229
+ else
230
+ Snapsync.info "already exists with different automount flag, updating"
231
+ end
232
+ elsif exists.automount == options[:automount]
162
233
  Snapsync.info "already exists under the name #{exists.name}"
163
234
  else
164
235
  Snapsync.info "already exists under the name #{exists.name} but with a different automount flag, changing"
@@ -197,6 +268,8 @@ for 10 days). snapsync understands the following period names: year month day ho
197
268
  EOD
198
269
  def policy(dir, type, *options)
199
270
  handle_class_options
271
+ # Parse the policy early to avoid breaking later
272
+ LocalTarget.parse_policy(*policy)
200
273
  each_target(dir) do |_, target|
201
274
  target.change_policy(type, options)
202
275
  target.write_config
@@ -231,9 +304,7 @@ While it can easily be done manually, this command makes sure that the snapshots
231
304
  desc 'list [DIR]', 'list the snapshots present on DIR. If DIR is omitted, tries to access all targets defined as auto-sync targets'
232
305
  def list(dir = nil)
233
306
  handle_class_options
234
- matched_uuids = Set.new
235
307
  each_target(dir) do |_, target|
236
- matched_uuids << target.uuid
237
308
  puts "== #{target.dir}"
238
309
  puts "UUID: #{target.uuid}"
239
310
  puts "Enabled: #{target.enabled?}"
@@ -5,4 +5,6 @@ module Snapsync
5
5
  # Exception raised when a snapshot directory is given to {Snapshot} but
6
6
  # snapshot_dir/info.xml does not look like a valid snapper info file.
7
7
  class InvalidInfoFile < InvalidSnapshot; end
8
+ # Invalid configuration requested
9
+ class InvalidConfiguration < ArgumentError; end
8
10
  end
@@ -75,14 +75,17 @@ module Snapsync
75
75
  config['enabled'] = enabled?
76
76
  config['autoclean'] = autoclean?
77
77
 
78
- File.open(config_path, 'w') do |io|
79
- io.write YAML.dump(config)
78
+ config_path.open('w') do |io|
79
+ YAML.dump(config, io)
80
80
  end
81
81
  end
82
82
 
83
83
  def read_config
84
84
  begin
85
- raw_config = YAML.load(config_path.read)
85
+ if !(raw_config = YAML.load(config_path.read))
86
+ raise NoUUIDError, "empty configuration file found in #{config_path}"
87
+ end
88
+
86
89
  rescue Errno::ENOENT => e
87
90
  raise NoUUIDError, e.message, e.backtrace
88
91
  end
@@ -113,26 +116,51 @@ module Snapsync
113
116
  def config_path
114
117
  dir + "snapsync.config"
115
118
  end
116
-
117
- def change_policy(type, options)
119
+
120
+ # Parse a policy specification as provided on the command line or saved
121
+ # in the config file into sync and cleanup policy objects
122
+ #
123
+ # @param [String] type the policy type, either default, timeline or last
124
+ # @param [Array<String>] options to be passed to the #from_config method
125
+ # on the underlying policy classes
126
+ #
127
+ # @return [(#filter_snapshots,#filter_snapshots)] the sync policy
128
+ # and the cleanup policy. The cleanup policy might be nil
129
+ # @raise [InvalidConfiguration] if the policy type is unknown
130
+ # @see DefaultSyncPolicy TimelineSyncPolicy SyncLastPolicy
131
+ def self.parse_policy(type, options)
118
132
  case type
119
133
  when 'default'
120
134
  sync_policy = DefaultSyncPolicy
121
- cleanup = nil
135
+ cleanup = nil
122
136
  when 'timeline'
123
137
  sync_policy = TimelineSyncPolicy
124
- cleanup = TimelineSyncPolicy
138
+ cleanup = TimelineSyncPolicy
125
139
  when 'last'
126
140
  sync_policy = SyncLastPolicy
127
- cleanup = SyncLastPolicy
141
+ cleanup = SyncLastPolicy
128
142
  else
129
143
  raise InvalidConfiguration, "synchronization policy #{type} does not exist"
130
144
  end
131
- @sync_policy = sync_policy.from_config(options)
132
- @cleanup =
145
+ sync_policy = sync_policy.from_config(options)
146
+ cleanup =
133
147
  if cleanup
134
148
  Cleanup.new(cleanup.from_config(options))
135
149
  end
150
+ return sync_policy, cleanup
151
+ end
152
+
153
+ # Verifies that the given policy type and options are valid
154
+ def self.valid_policy?(type, options)
155
+ parse_policy(type, options)
156
+ true
157
+ rescue InvalidConfiguration
158
+ false
159
+ end
160
+
161
+ def change_policy(type, options)
162
+ @sync_policy, @cleanup =
163
+ self.class.parse_policy(type, options)
136
164
  end
137
165
 
138
166
  def delete(s, dry_run: false)
@@ -15,7 +15,7 @@ end
15
15
 
16
16
  require 'minitest/autorun'
17
17
  require 'snapsync'
18
- require 'flexmock/test_unit'
18
+ require 'flexmock/minitest'
19
19
  require 'minitest/spec'
20
20
 
21
21
  if ENV['TEST_ENABLE_PRY'] != '0'
@@ -19,7 +19,10 @@ module Snapsync
19
19
 
20
20
  def parse_config(config)
21
21
  config.each_slice(2) do |period, count|
22
- add(period.to_sym, Integer(count))
22
+ begin add(period.to_sym, Integer(count))
23
+ rescue ArgumentError
24
+ raise InvalidConfiguration, "invalid timeline period or count #{period} #{count}"
25
+ end
23
26
  end
24
27
  end
25
28
 
@@ -1,3 +1,3 @@
1
1
  module Snapsync
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -31,5 +31,6 @@ EOD
31
31
  spec.add_development_dependency "bundler", "~> 1.10"
32
32
  spec.add_development_dependency "rake", "~> 10.0"
33
33
  spec.add_development_dependency "minitest", "~> 5.0", ">= 5.7"
34
- spec.add_development_dependency "flexmock", "~> 1.3", ">= 1.3.3"
34
+ spec.add_development_dependency "flexmock", "~> 2.0", ">= 2.0"
35
+ spec.add_development_dependency "fakefs"
35
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snapsync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Joyeux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-03 00:00:00.000000000 Z
11
+ date: 2016-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logging
@@ -144,20 +144,34 @@ dependencies:
144
144
  requirements:
145
145
  - - "~>"
146
146
  - !ruby/object:Gem::Version
147
- version: '1.3'
147
+ version: '2.0'
148
148
  - - ">="
149
149
  - !ruby/object:Gem::Version
150
- version: 1.3.3
150
+ version: '2.0'
151
151
  type: :development
152
152
  prerelease: false
153
153
  version_requirements: !ruby/object:Gem::Requirement
154
154
  requirements:
155
155
  - - "~>"
156
156
  - !ruby/object:Gem::Version
157
- version: '1.3'
157
+ version: '2.0'
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '2.0'
161
+ - !ruby/object:Gem::Dependency
162
+ name: fakefs
163
+ requirement: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ type: :development
169
+ prerelease: false
170
+ version_requirements: !ruby/object:Gem::Requirement
171
+ requirements:
158
172
  - - ">="
159
173
  - !ruby/object:Gem::Version
160
- version: 1.3.3
174
+ version: '0'
161
175
  description: |
162
176
  Snapsync is a tool that automates transferring snapper snapshots to
163
177
  external media (USB drives ...) and managing these snapshots (e.g.