cfbackup 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -1,6 +1,8 @@
1
- pkg
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
2
4
  rdoc
5
+ pkg
3
6
  tmp
4
7
  test/tmp
5
8
  test/tmp/data
6
- .DS_Store
data/CHANGELOG.markdown CHANGED
@@ -1,6 +1,20 @@
1
1
  CFBackup ChangeLog
2
2
  ==================
3
3
 
4
+ 0.8.0 2010-01-20
5
+ -----------------
6
+ * Code documented
7
+ * Gem deployed on gemcutter
8
+ * Cloudfiles API dependency updated to 1.4.4
9
+ * Basic exception handling and retry mechanisms
10
+ * New --max_retries option
11
+ * New --ignore_errors option
12
+ * File push errors can now be ignored
13
+ * New --error_log option
14
+ * File push errors can be written to a log for later processing
15
+
16
+ This release sponsored by Shaun Brazier.
17
+
4
18
  0.7.1 2009-05-19
5
19
  -----------------
6
20
  * Version bump and re-release of 0.6.1 because the published gem by GitHub was marked as 0.7.0.
data/README.markdown CHANGED
@@ -16,18 +16,18 @@ Features
16
16
  Requirements
17
17
  --------------
18
18
 
19
- * ruby-cloudfiles
19
+ * cloudfiles >= 1.4.4
20
20
 
21
21
  Notes:
22
22
  * If you install CFBackup as a gem, all of the dependencies _should_ automatically be installed for you.
23
- * Ubuntu Users: The Ubuntu rubygems package will installs executables outside your normal PATH. You will
24
- need to update it or create a symlink to access cfbackup from anywhere. See the wiki for more information.reating a symlink.
23
+ * Ubuntu Users: The Ubuntu rubygems package will install executables outside your normal PATH. You will
24
+ need to update it or create a symlink to access cfbackup from anywhere. See the wiki for more information.
25
25
 
26
26
  Install
27
27
  -----------
28
28
 
29
- * gem sources -a http://gems.github.com
30
- * sudo gem install jmstacey-cfbackup
29
+ * gem sources -a http://gemcutter.org
30
+ * sudo gem install cfbackup
31
31
 
32
32
  Configuration
33
33
  -----------
@@ -41,7 +41,7 @@ CFBackup will look in the following places (in order) for the configuration file
41
41
 
42
42
  The configuration file can be overridden at any time with the --config_file option
43
43
 
44
- Configuration
44
+ Usage
45
45
  -----------
46
46
 
47
47
  Usage: cfbackup.rb --action push|pull|delete options --container CONTAINER
@@ -54,6 +54,9 @@ Configuration
54
54
  --version Show current version
55
55
  --config_file PATH Use specified config file, rather than the default
56
56
  --local_net Use unmetered connection in DFW1 (only applicable to Slicehost or Mosso Cloud Server customers)
57
+ --max_retries COUNT Change the number of times to retry an operation before giving up
58
+ --ignore_errors Ignore file operation errors (push only) and continue processing other files
59
+ --error_log FILEPATH Create an error log at the given filepath containing a listing of failed push operations
57
60
 
58
61
  The wiki has usage examples and some sample automation scripts can be found in the example_scripts directory.
59
62
 
data/Rakefile CHANGED
@@ -2,28 +2,29 @@ require 'rake'
2
2
 
3
3
  $LOAD_PATH.unshift('lib')
4
4
 
5
- begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |gem|
8
- gem.name = "cfbackup"
9
- gem.summary = "A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files."
10
- gem.description = "A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files."
11
- gem.email = "jon@jonsview.com"
12
- gem.homepage = "http://github.com/jmstacey/cfbackup"
13
- gem.authors = ["Jon Stacey"]
14
-
15
- # Dependencies
16
- gem.add_dependency('rackspace-cloudfiles', '>=1.3.0.3')
17
-
18
- # Include Files
19
- gem.files.include %w(lib/optcfbackup.rb)
20
-
21
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
- end
23
- rescue LoadError
24
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "cfbackup"
8
+ gem.summary = "A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files."
9
+ gem.description = "A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files."
10
+ gem.email = "jon@jonsview.com"
11
+ gem.homepage = "http://github.com/jmstacey/cfbackup"
12
+ gem.authors = ["Jon Stacey"]
13
+
14
+ # Dependencies
15
+ gem.add_dependency "cloudfiles", ">=1.4.4"
16
+ gem.add_dependency "gemcutter", ">= 0.1.0"
17
+
18
+ # Development Dependencies
19
+ gem.add_development_dependency "shoulda"
20
+ gem.add_development_dependency "mocha"
21
+
22
+ # Include Files
23
+ gem.files.include %w(lib/optcfbackup.rb)
25
24
  end
26
25
 
26
+ Jeweler::GemcutterTasks.new
27
+
27
28
  require 'rake/testtask'
28
29
  Rake::TestTask.new(:test) do |test|
29
30
  test.libs << 'lib' << 'test'
data/VERSION.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 7
4
- :patch: 1
3
+ :minor: 8
4
+ :patch: 0
5
+ :build:
data/bin/cfbackup CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ # This file will be installed in the bin directory, making cfbackup callable
4
+ # from anywhere on the command line.
5
+ #
6
+ # Instantiate a new CFBackup object and run.
7
+
2
8
  require 'rubygems'
3
9
  require 'cfbackup'
4
10
 
data/cfbackup.gemspec CHANGED
@@ -1,10 +1,15 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
1
6
  Gem::Specification.new do |s|
2
7
  s.name = %q{cfbackup}
3
- s.version = "0.7.1"
8
+ s.version = "0.8.0"
4
9
 
5
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
11
  s.authors = ["Jon Stacey"]
7
- s.date = %q{2009-05-19}
12
+ s.date = %q{2010-01-20}
8
13
  s.default_executable = %q{cfbackup}
9
14
  s.description = %q{A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files.}
10
15
  s.email = %q{jon@jonsview.com}
@@ -28,7 +33,6 @@ Gem::Specification.new do |s|
28
33
  "example_scripts/temp_directory.sh",
29
34
  "lib/cfbackup.rb",
30
35
  "lib/optcfbackup.rb",
31
- "lib/optcfbackup.rb",
32
36
  "temp/README",
33
37
  "test/cfbackup_test.rb",
34
38
  "test/cfconfig.yml",
@@ -40,11 +44,10 @@ Gem::Specification.new do |s|
40
44
  "test/data/folder_2/file2.txt",
41
45
  "test/test_helper.rb"
42
46
  ]
43
- s.has_rdoc = true
44
47
  s.homepage = %q{http://github.com/jmstacey/cfbackup}
45
48
  s.rdoc_options = ["--charset=UTF-8"]
46
49
  s.require_paths = ["lib"]
47
- s.rubygems_version = %q{1.2.0}
50
+ s.rubygems_version = %q{1.3.5}
48
51
  s.summary = %q{A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files.}
49
52
  s.test_files = [
50
53
  "test/cfbackup_test.rb",
@@ -53,14 +56,24 @@ Gem::Specification.new do |s|
53
56
 
54
57
  if s.respond_to? :specification_version then
55
58
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
56
- s.specification_version = 2
59
+ s.specification_version = 3
57
60
 
58
- if current_version >= 3 then
59
- s.add_runtime_dependency(%q<rackspace-cloudfiles>, [">= 1.3.0.3"])
61
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
62
+ s.add_runtime_dependency(%q<cloudfiles>, [">= 1.4.4"])
63
+ s.add_runtime_dependency(%q<gemcutter>, [">= 0.1.0"])
64
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
65
+ s.add_development_dependency(%q<mocha>, [">= 0"])
60
66
  else
61
- s.add_dependency(%q<rackspace-cloudfiles>, [">= 1.3.0.3"])
67
+ s.add_dependency(%q<cloudfiles>, [">= 1.4.4"])
68
+ s.add_dependency(%q<gemcutter>, [">= 0.1.0"])
69
+ s.add_dependency(%q<shoulda>, [">= 0"])
70
+ s.add_dependency(%q<mocha>, [">= 0"])
62
71
  end
63
72
  else
64
- s.add_dependency(%q<rackspace-cloudfiles>, [">= 1.3.0.3"])
73
+ s.add_dependency(%q<cloudfiles>, [">= 1.4.4"])
74
+ s.add_dependency(%q<gemcutter>, [">= 0.1.0"])
75
+ s.add_dependency(%q<shoulda>, [">= 0"])
76
+ s.add_dependency(%q<mocha>, [">= 0"])
65
77
  end
66
78
  end
79
+
data/conf/cfconfig.yml CHANGED
@@ -1,2 +1,5 @@
1
+ # CFBackup configuration file
2
+ # Enter your Rackspace/Mosso Cloud Files API username and password.
3
+
1
4
  username: xxxxxxxxxxxxx
2
5
  api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -1,11 +1,13 @@
1
1
  #!/bin/sh
2
2
 
3
- # In this example, data is piped directly from the backup to a Cloud Files
4
- # container, bypassing the intermediate temp directory step
3
+ # piped.sh
5
4
  #
6
- # This is an example script that you could use in combination with CFBackup.
7
- # Modify it to suit your needs. Rename to file without an extension if you
8
- # are going to place in /etc/cron.daily.
5
+ # This script demonstrates how CFBackup could be used to peform
6
+ # automated backups. In this example, data is piped directly
7
+ # to a Cloud Files container, bypassing the need for a temporary
8
+ # holding directory.
9
+ #
10
+ # Modify it to suit your needs.
9
11
 
10
12
  CONTAINER=backups
11
13
  NOW=$(date +_%b_%d_%y)
@@ -1,13 +1,14 @@
1
1
  #!/bin/sh
2
2
 
3
- # In this example, backups are first created and placed in a temporary
4
- # holding directory. Then, the entire directory is uploaded to
5
- # Cloud Files. Finally, the temp directory is cleared in preparation
6
- # for the next backup.
3
+ # temp_directory.sh
7
4
  #
8
- # This is an example script that you could use in combination with CFBackup.
9
- # Modify it to suit your needs. Rename to file without an extension if you
10
- # are going to place in /etc/cron.daily.
5
+ # This script demonstrates how CFBackup could be used to peform
6
+ # automated backups. In this example, backups are first created
7
+ # and placed in a temporary holding directory. Then, the entire
8
+ # directory is uploaded to Cloud Files. Finally, the temp directory
9
+ # is cleared in preparation for the next scheduled backup.
10
+ #
11
+ # Modify it to suit your needs.
11
12
 
12
13
  cd ~/cfbackup/temp
13
14
  CONTAINER=backups
data/lib/cfbackup.rb CHANGED
@@ -22,6 +22,11 @@ require 'yaml'
22
22
 
23
23
  class CFBackup
24
24
 
25
+ # Implementation of initialize
26
+ #
27
+ # Prepares CFBackup object by calling options processor,
28
+ # loading configuration files, and preparing the connection
29
+ # to Mosso Cloud Files.
25
30
  def initialize(args)
26
31
  @opts = OptCFBackup.new(args)
27
32
 
@@ -47,9 +52,14 @@ class CFBackup
47
52
  show_error('Error: Unable to locate config file.') unless (@conf != nil)
48
53
 
49
54
  prep_connection
55
+ prep_error_log_file unless !@opts.options.error_log
50
56
 
51
57
  end # initialize()
52
58
 
59
+ # Run CFBackup.
60
+ #
61
+ # This method will call the appropriate method based on
62
+ # the action given when CFBackup was called.
53
63
  def run
54
64
 
55
65
  show_error() unless (@opts.options.container != "")
@@ -74,18 +84,38 @@ class CFBackup
74
84
 
75
85
  private
76
86
 
87
+ # Prepare the connection to Mosso Cloud Files.
88
+ #
89
+ # Will attempt connectiong via the local network if
90
+ # --local_net option was specified.
77
91
  def prep_connection
78
92
  # Establish connection
79
93
  show_verbose "Establishing connection...", false
80
- @cf = CloudFiles::Connection.new(@conf["username"], @conf["api_key"]);
81
- show_verbose " done."
82
94
 
83
- # Special option for Slicehost customers in DFW datacenter
84
- if @opts.options.local_net
85
- @cf.storagehost = 'snet-storage.clouddrive.com'
95
+ retry_count = 1
96
+ begin
97
+ @cf = CloudFiles::Connection.new(@conf["username"], @conf["api_key"], true, @opts.options.local_net)
98
+ rescue AuthenticationException => e
99
+ puts "Error: #{e.message}. Check your cfconfig.yml file."
100
+ Process.exit
101
+ rescue ConnectionException => e
102
+ if retry_count <= @opts.options.max_retries
103
+ puts "Error: #{e.message}. Retrying (#{retry_count}/#{@opts.options.max_retries.to_s}) in 15 seconds..."
104
+ retry_count = retry_count + 1
105
+ sleep 15
106
+ retry
107
+ else
108
+ puts "Error: #{e.message}. Giving up!"
109
+ Process.exit
110
+ end
86
111
  end
87
112
  end # prep_connection()
88
-
113
+
114
+ # Prepare the Cloud Files Container.
115
+ #
116
+ # Confirms the existence of the specified container
117
+ # and attempts to create it if possible. If container creation
118
+ # is disabled, an error will be thrown.
89
119
  def prep_container(create_container = true)
90
120
  # Check for the container. If it doesn't exist, create it if allowed
91
121
  if !@cf.container_exists?(@opts.options.container)
@@ -101,14 +131,50 @@ class CFBackup
101
131
  @container = @cf.container(@opts.options.container)
102
132
  end # prep_cnnection()
103
133
 
134
+ # Push piped data to the Cloud Files container.
135
+ #
136
+ # Pushes data piped from STDIN directly to the Cloud Files container.
104
137
  def push_piped_data
105
138
  prep_container
106
139
 
107
- puts "Warning: 5GB maximum filesize"
140
+ puts "Note: Rackspace enforces a 5GB maximum filesize."
108
141
  object = @container.create_object(@opts.options.remote_path, true)
109
142
  object.write
110
143
  end # push_piped_data()
111
144
 
145
+ # Push single file to the Cloud Files container.
146
+ def push_file(file)
147
+ if @opts.options.remote_path.to_s == ''
148
+ remote_path = file
149
+ else
150
+ remote_path = File.join(@opts.options.remote_path, file)
151
+ end
152
+
153
+ retry_count = 1
154
+ begin
155
+ object = @container.create_object(remote_path, true)
156
+ object.load_from_filename(file)
157
+ rescue Exception => e
158
+ if retry_count <= @opts.options.max_retries
159
+ puts "Error: #{e.message}. Retrying (#{retry_count}/#{@opts.options.max_retries.to_s})..."
160
+ retry_count = retry_count + 1
161
+ retry
162
+ else
163
+ write_error_to_log(file, e) unless !@opts.options.error_log
164
+ unless @opts.options.ignore_errors
165
+ puts "Error: #{e.message}. Giving up!"
166
+ Process.exit
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ # Push files to the Cloud Files container.
173
+ #
174
+ # Deterimes what files to upload and then sends them to the
175
+ # Cloud Files container one at a time. If the push is recursive
176
+ # pseudo directories will be used to mimic the filesystem layout
177
+ # in the Cloud Files container.
112
178
  def push_files
113
179
  prep_container
114
180
 
@@ -138,16 +204,7 @@ class CFBackup
138
204
  end
139
205
 
140
206
  show_verbose "(#{counter}/#{files.length}) Pushing file #{file}...", false
141
-
142
- if @opts.options.remote_path.to_s == ''
143
- remote_path = file
144
- else
145
- remote_path = File.join(@opts.options.remote_path, file)
146
- end
147
-
148
- object = @container.create_object(remote_path, true)
149
- object.load_from_filename(file)
150
-
207
+ push_file file
151
208
  show_verbose " done."
152
209
  counter += 1
153
210
  end # files.each
@@ -156,6 +213,12 @@ class CFBackup
156
213
 
157
214
  end # push_files()
158
215
 
216
+ # Pull files from Cloud Files container to local filesystem.
217
+ #
218
+ # This method will pull the given file or directory from
219
+ # the Cloud Files container to the local filesystem. If recursion
220
+ # is enabled, the pseudo directory structure will also be duplicated
221
+ # on the local system.
159
222
  def pull_files
160
223
  prep_container(false)
161
224
 
@@ -218,6 +281,10 @@ class CFBackup
218
281
  end
219
282
  end # pull_files()
220
283
 
284
+ # Delete a given file from a Cloud Files container.
285
+ #
286
+ # This method will delete a single file or directory
287
+ # from the Cloud Files container.
221
288
  def delete_files
222
289
  prep_container(false)
223
290
 
@@ -244,9 +311,15 @@ class CFBackup
244
311
 
245
312
  end # delete_files()
246
313
 
314
+ ################################################
315
+ # Helper methods #
316
+ ################################################
247
317
 
248
- # Helper methods below
249
-
318
+ # Determines if the given object is a file.
319
+ #
320
+ # If the object is a file, true is returned. Otherwise,
321
+ # if the object is a directory false is returned and a warning is
322
+ # emitted if the recursive option was not specified.
250
323
  def object_file?
251
324
 
252
325
  file = false
@@ -266,6 +339,10 @@ class CFBackup
266
339
  return file
267
340
  end
268
341
 
342
+ # Get an array of objects to process from the container.
343
+ #
344
+ # Queries the Cloud Files container to compile and return an array
345
+ # of objects that need to be processed.
269
346
  def get_objects_array
270
347
 
271
348
  file = object_file?
@@ -284,23 +361,44 @@ class CFBackup
284
361
  return objects
285
362
  end
286
363
 
287
- # Shows given message if verbose output is turned on
364
+ # Show given message if verbose output is turned on
365
+ #
366
+ # Used to display or hide messages based on the users verbosity
367
+ # preference.
288
368
  def show_verbose(message, line_break = true)
289
- unless !@opts.options.verbose
290
- if line_break
291
- puts message
292
- else
293
- print message
294
- end
295
- $stdout.flush
369
+ if @opts.options.verbose
370
+ line_break ? puts(message) : print(message)
296
371
  end
372
+ $stdout.flush
297
373
  end # show_verbose()
298
374
 
299
- # Show error message, banner and exit
375
+ # Show error message, banner, and exit program.
376
+ #
377
+ # This is considered a critical error and the application
378
+ # terminated after printing the given error message and the
379
+ # usage banner for reference.
300
380
  def show_error(message = '')
301
381
  puts message
302
382
  puts @opts.banner
303
383
  exit
304
384
  end # show_error()
305
385
 
386
+ # Prepare error log file for writing
387
+ #
388
+ # The log file is created if necessary and the current date and time are
389
+ # written to indicate a new set of operations. The log is never overwritten
390
+ # or truncated except by user.
391
+ def prep_error_log_file
392
+ header = "\n\nFile operations initiated #{Time.now}\n------------------"
393
+ File.open(@opts.options.error_log, 'a+') { |f| f.write(header) }
394
+ end
395
+
396
+ # Append given error message to the error log
397
+ #
398
+ # Log entries will be in the format "filepath:exception message"
399
+ def write_error_to_log(message, exception)
400
+ output = "#{message}:#{exception.message}"
401
+ File.open(@opts.options.error_log, 'a') {|f| f.write(output) }
402
+ end
403
+
306
404
  end # class CFBackup
data/lib/optcfbackup.rb CHANGED
@@ -1,30 +1,35 @@
1
+ # Handle option parsing for use in CFBackup.
2
+
1
3
  require 'optparse'
2
4
  require 'ostruct'
3
5
 
6
+ # Option parser class for CFBackup
4
7
  class OptCFBackup
5
8
 
6
- # Options structure
7
- attr_reader :options
8
-
9
- # Ussage message
10
- attr_reader :banner
9
+ attr_reader :options # Options structure
10
+ attr_reader :banner # Ussage message
11
11
 
12
+ # Implementation of initialize
13
+ #
12
14
  # Initializes object with command line arguments passed
13
15
  def initialize(args)
14
16
 
15
17
  @banner = "Usage: cfbackup.rb --action push|pull|delete options --container CONTAINER"
16
18
 
17
19
  @options = OpenStruct.new
18
- self.options.config = ["#{ENV['HOME']}/.cfconfig.yml", './cfconfig.yml', '/etc/cfconfig.yml']
19
- self.options.action = ''
20
- self.options.pipe_data = false
21
- self.options.show_ver = false
22
- self.options.recursive = false
23
- self.options.local_net = false
24
- self.options.container = ''
25
- self.options.local_path = ''
26
- self.options.remote_path = ''
27
- self.options.verbose = false;
20
+ self.options.config = ["#{ENV['HOME']}/.cfconfig.yml", './cfconfig.yml', '/etc/cfconfig.yml']
21
+ self.options.action = ''
22
+ self.options.pipe_data = false
23
+ self.options.show_ver = false
24
+ self.options.recursive = false
25
+ self.options.local_net = false
26
+ self.options.container = ''
27
+ self.options.local_path = ''
28
+ self.options.remote_path = ''
29
+ self.options.verbose = false
30
+ self.options.max_retries = 3
31
+ self.options.ignore_errors = false
32
+ self.options.error_log = false
28
33
 
29
34
  opts = OptionParser.new do |opts|
30
35
  opts.banner = self.banner
@@ -67,20 +72,31 @@ class OptCFBackup
67
72
  self.options.local_net = local_net
68
73
  end
69
74
 
75
+ opts.on("--max_retries COUNT", "Change the number of times to retry an operation before giving up.") do |config|
76
+ self.options.max_retries = max_retries
77
+ end
78
+
79
+ opts.on("--ignore_errors", "Ignore file operation errors (push only) and continue processing other files.") do |ignore_errors|
80
+ self.options.ignore_errors = ignore_errors
81
+ end
82
+
83
+ opts.on("--error_log FILEPATH", "Create an error log at the given filepath containing a listing of failed push operations.") do |error_log|
84
+ self.options.error_log = error_log
85
+ end
86
+
70
87
  end
71
88
 
72
- opts.parse!(args)
89
+ opts.parse!(args) # Parse arguments
73
90
 
74
91
  end # initialize()
75
92
 
76
93
  private
77
94
 
95
+ # Remove trailing slash from the remote path if present.
78
96
  def clean_remote_path
79
97
  if self.options.remote_path[0,1] == "/"
80
98
  self.options.remote_path.slice!(0)
81
99
  end
82
- # Follwoig won't work for piped data. Might result in "text.txt/"
83
- # self.options.remote_path = self.options.remote_path + "/" unless (self.options.remote_path[-1,1] == "/")
84
- end
100
+ end # clean_remote_path()
85
101
 
86
102
  end # class OptCFBackup
data/temp/README CHANGED
@@ -2,4 +2,4 @@ You could use this as a temporary holding directory
2
2
  in combination with the temp_directory.sh script
3
3
  example in the example_scripts directory.
4
4
 
5
- This file exists so that git doesn't delete this directory.
5
+ This file exists so that Git doesn't delete this directory.
@@ -1,8 +1,47 @@
1
+ # Contains CFBackup unit tests.
2
+
1
3
  require 'test_helper'
2
4
 
5
+ # CFBackup test class
3
6
  class CfbackupTest < Test::Unit::TestCase
4
- TEST_DIR = 'test/tmp'
7
+ TEST_DIR = 'test/tmp' # Test directory
8
+
9
+ # Test --error_log option
10
+ context "A backup with the --error_log option enabled" do
11
+ setup do
12
+ mock_ARGV = ['--action', 'push', '--local_path', 'test/data/data.txt', '--container', 'test', '--config_file', 'test/cfconfig.yml', '-v', '--error_log', 'error.log']
13
+ backup = CFBackup.new(mock_ARGV)
14
+ end
15
+
16
+ should "result in a log file being created" do
17
+ assert_equal true, File.exists?('error.log')
18
+ end
19
+
20
+ teardown do
21
+ File.delete('error.log')
22
+ end
23
+ end
5
24
 
25
+ # Test connections
26
+ context "A connection" do
27
+
28
+ context "that fails with a ConnectionException and the default number of retries" do
29
+ should "should be retried 3 times and then exit" do
30
+ CloudFiles::Connection.expects(:new).times(4).raises(ConnectionException)
31
+ # We expect 4 calls (1 initial + 3 retries)
32
+
33
+ assert_raises(SystemExit) do
34
+ mock_ARGV = ['--action', 'push', '--local_path', 'test/data/data.txt', '--container', 'test', '--config_file', 'test/cfconfig.yml', '-v']
35
+ CFBackup.new(mock_ARGV)
36
+ end
37
+ end
38
+ end # context "that fails with a ConnectionException..."
39
+
40
+ end
41
+
42
+ # Test uploading files.
43
+ # First a single file is uploaded, then a recursive directory
44
+ # push is performed.
6
45
  context "A backup" do
7
46
 
8
47
  context "with a single file push" do
@@ -26,9 +65,12 @@ class CfbackupTest < Test::Unit::TestCase
26
65
  assert @backup.run
27
66
  end
28
67
  end
29
-
68
+
30
69
  end
31
70
 
71
+ # Test restoring files.
72
+ # First attempts pulling a single file, then attempts a recursive
73
+ # directory pull. Cleans up afterwards.
32
74
  context "A restore" do
33
75
 
34
76
  context "with a single file pull" do
@@ -78,6 +120,9 @@ class CfbackupTest < Test::Unit::TestCase
78
120
 
79
121
  end
80
122
 
123
+ # Test deleting remote objects.
124
+ # First test deleting a single remote object representing a file,
125
+ # then delete a pseudo directory recursively.
81
126
  context "A deletion" do
82
127
 
83
128
  context "of a single file" do
data/test/cfconfig.yml CHANGED
@@ -1,2 +1,5 @@
1
+ # CFBackup configuration file
2
+ # Enter your Rackspace/Mosso Cloud Files API username and password.
3
+
1
4
  username: xxxxxxxxxxxxx
2
5
  api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
data/test/test_helper.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'mocha'
5
+ require 'cloudfiles'
4
6
 
5
7
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
8
  $LOAD_PATH.unshift(File.dirname(__FILE__))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfbackup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Stacey
@@ -9,18 +9,48 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-19 00:00:00 -05:00
12
+ date: 2010-01-20 00:00:00 -06:00
13
13
  default_executable: cfbackup
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: rackspace-cloudfiles
16
+ name: cloudfiles
17
17
  type: :runtime
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 1.3.0.3
23
+ version: 1.4.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: gemcutter
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: shoulda
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: mocha
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
24
54
  version:
25
55
  description: A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files.
26
56
  email: jon@jonsview.com
@@ -82,7 +112,7 @@ requirements: []
82
112
  rubyforge_project:
83
113
  rubygems_version: 1.3.5
84
114
  signing_key:
85
- specification_version: 2
115
+ specification_version: 3
86
116
  summary: A simple ruby program intended to serve as a useful tool for automated backups to Mosso Cloud Files.
87
117
  test_files:
88
118
  - test/cfbackup_test.rb