cfbackup 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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