From 4ab3a310b2ca324a6676baea9865003e36634e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Tue, 3 Jan 2012 15:59:43 +0100 Subject: [PATCH 01/10] moved syncer/rsync.rb to syncer/rsync/push.rb --- Gemfile.lock | 4 +- lib/backup.rb | 33 +++- lib/backup/configuration/helpers.rb | 7 +- lib/backup/configuration/syncer/rsync.rb | 45 ------ lib/backup/configuration/syncer/rsync/push.rb | 47 ++++++ lib/backup/syncer/rsync.rb | 152 ----------------- lib/backup/syncer/rsync/push.rb | 153 ++++++++++++++++++ .../{rsync_spec.rb => rsync/push_spec.rb} | 12 +- .../{rsync_spec.rb => rsync/push_spec.rb} | 18 +-- 9 files changed, 250 insertions(+), 221 deletions(-) delete mode 100644 lib/backup/configuration/syncer/rsync.rb create mode 100644 lib/backup/configuration/syncer/rsync/push.rb delete mode 100644 lib/backup/syncer/rsync.rb create mode 100644 lib/backup/syncer/rsync/push.rb rename spec/configuration/syncer/{rsync_spec.rb => rsync/push_spec.rb} (75%) rename spec/syncer/{rsync_spec.rb => rsync/push_spec.rb} (90%) diff --git a/Gemfile.lock b/Gemfile.lock index 57d53d281..3aaac89d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - backup (3.0.19) + backup (3.0.20) POpen4 (~> 0.1.4) thor (~> 0.14.6) @@ -72,7 +72,7 @@ GEM net-ssh (2.1.4) nokogiri (1.5.0) oauth (0.4.5) - open4 (1.2.0) + open4 (1.3.0) polyglot (0.3.2) prowler (1.3.1) rack (1.3.2) diff --git a/lib/backup.rb b/lib/backup.rb index 642c51fe7..b1b9a67d5 100644 --- a/lib/backup.rb +++ b/lib/backup.rb @@ -36,7 +36,7 @@ module Backup STORAGES = ['S3', 'CloudFiles', 'Ninefold', 'Dropbox', 'FTP', 'SFTP', 'SCP', 'RSync', 'Local'] COMPRESSORS = ['Gzip', 'Bzip2', 'Pbzip2', 'Lzma'] ENCRYPTORS = ['OpenSSL', 'GPG'] - SYNCERS = ['RSync', 'S3'] + SYNCERS = ['S3', 'Rsync' => ['Push']] NOTIFIERS = ['Mail', 'Twitter', 'Campfire', 'Presently', 'Prowl', 'Hipchat'] ## @@ -105,8 +105,10 @@ module Storage # Autoload Backup syncer files module Syncer autoload :Base, File.join(SYNCER_PATH, 'base') - autoload :RSync, File.join(SYNCER_PATH, 'rsync') autoload :S3, File.join(SYNCER_PATH, 's3') + module RSync + autoload :Push, File.join(SYNCER_PATH, 'rsync', 'push') + end end ## @@ -195,8 +197,10 @@ module Storage end module Syncer - autoload :RSync, File.join(CONFIGURATION_PATH, 'syncer', 'rsync') autoload :S3, File.join(CONFIGURATION_PATH, 'syncer', 's3') + module RSync + autoload :Push, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'push') + end end module Database @@ -209,12 +213,31 @@ module Database end end +private + + def self.create_empty_class(class_name, scope = Backup::Finder) + scope.const_set(class_name, Class.new) unless scope.const_defined?(class_name) + end + + def self.get_or_create_empty_module(module_name) + if Backup::Finder.const_defined?(module_name) + return Backup::Finder.const_get(module_name) + else + return Backup::Finder.const_set(module_name, Module.new) + end + end + ## # Dynamically defines all the available database, storage, compressor, encryptor and notifier # classes inside Backup::Finder to improve the DSL for the configuration file (DATABASES + STORAGES + COMPRESSORS + ENCRYPTORS + NOTIFIERS + SYNCERS).each do |constant| - unless Backup::Finder.const_defined?(constant) - Backup::Finder.const_set(constant, Class.new) + if constant.is_a?(Hash) + constant.each do |module_name, class_names| + mod = get_or_create_empty_module(module_name) + class_names.each{ |class_name| create_empty_class(class_name, mod) } + end + else + create_empty_class(constant) end end end diff --git a/lib/backup/configuration/helpers.rb b/lib/backup/configuration/helpers.rb index 16ba65a7a..524770a68 100644 --- a/lib/backup/configuration/helpers.rb +++ b/lib/backup/configuration/helpers.rb @@ -9,8 +9,11 @@ module Helpers # configuration for these methods, if they respond then they will # assign the object's attribute(s) to that particular global configuration's attribute def load_defaults! - c = self.class.name.split('::') - configuration = Backup::Configuration.const_get(c[1]).const_get(c[2]) + module_names = self.class.name.split('::')[1..-1] + configuration = Backup::Configuration + module_names.each do |module_name| + configuration = configuration.const_get(module_name) + end getter_methods.each do |attribute| if configuration.respond_to?(attribute) diff --git a/lib/backup/configuration/syncer/rsync.rb b/lib/backup/configuration/syncer/rsync.rb deleted file mode 100644 index e3d68dec7..000000000 --- a/lib/backup/configuration/syncer/rsync.rb +++ /dev/null @@ -1,45 +0,0 @@ -# encoding: utf-8 - -module Backup - module Configuration - module Syncer - class RSync < Base - class << self - - ## - # Server credentials - attr_accessor :username, :password - - ## - # Server IP Address and SSH port - attr_accessor :ip - - ## - # The SSH port to connect to - attr_accessor :port - - ## - # Directories to sync - attr_accessor :directories - - ## - # Path to store the synced files/directories to - attr_accessor :path - - ## - # Flag for mirroring the files/directories - attr_accessor :mirror - - ## - # Flag for compressing (only compresses for the transfer) - attr_accessor :compress - - ## - # Additional options for the rsync cli - attr_accessor :additional_options - - end - end - end - end -end diff --git a/lib/backup/configuration/syncer/rsync/push.rb b/lib/backup/configuration/syncer/rsync/push.rb new file mode 100644 index 000000000..9657f14f3 --- /dev/null +++ b/lib/backup/configuration/syncer/rsync/push.rb @@ -0,0 +1,47 @@ +# encoding: utf-8 + +module Backup + module Configuration + module Syncer + module RSync + class Push < Configuration::Base + class << self + + ## + # Server credentials + attr_accessor :username, :password + + ## + # Server IP Address and SSH port + attr_accessor :ip + + ## + # The SSH port to connect to + attr_accessor :port + + ## + # Directories to sync + attr_accessor :directories + + ## + # Path to store the synced files/directories to + attr_accessor :path + + ## + # Flag for mirroring the files/directories + attr_accessor :mirror + + ## + # Flag for compressing (only compresses for the transfer) + attr_accessor :compress + + ## + # Additional options for the rsync cli + attr_accessor :additional_options + + end + end + end + end + end +end diff --git a/lib/backup/syncer/rsync.rb b/lib/backup/syncer/rsync.rb deleted file mode 100644 index 069a9482d..000000000 --- a/lib/backup/syncer/rsync.rb +++ /dev/null @@ -1,152 +0,0 @@ -# encoding: utf-8 - -## -# Require the tempfile Ruby library when Backup::Syncer::RSync is loaded -require 'tempfile' - -module Backup - module Syncer - class RSync < Base - - ## - # Server credentials - attr_accessor :username, :password - - ## - # Server IP Address and SSH port - attr_accessor :ip - - ## - # The SSH port to connect to - attr_writer :port - - ## - # Directories to sync - attr_writer :directories - - ## - # Path to store the synced files/directories to - attr_accessor :path - - ## - # Flag for mirroring the files/directories - attr_writer :mirror - - ## - # Flag for compressing (only compresses for the transfer) - attr_writer :compress - - ## - # Additional options for the rsync cli - attr_accessor :additional_options - - ## - # Instantiates a new RSync Syncer object and sets the default configuration - # specified in the Backup::Configuration::Syncer::RSync. Then it sets the object - # defaults if particular properties weren't set. Finally it'll evaluate the users - # configuration file and overwrite anything that's been defined - def initialize(&block) - load_defaults! - - @directories = Array.new - @additional_options ||= Array.new - @path ||= 'backups' - @port ||= 22 - @mirror ||= false - @compress ||= false - - instance_eval(&block) if block_given? - write_password_file! - - @path = path.sub(/^\~\//, '') - end - - ## - # Performs the RSync operation - # debug options: -vhP - def perform! - Logger.message("#{ self.class } started syncing #{ directories }.") - Logger.silent( - run("#{ utility(:rsync) } #{ options } #{ directories } '#{ username }@#{ ip }:#{ path }'") - ) - - remove_password_file! - end - - ## - # Returns all the specified Rsync options, concatenated, ready for the CLI - def options - ([archive, mirror, compress, port, password] + additional_options).compact.join("\s") - end - - ## - # Returns Rsync syntax for enabling mirroring - def mirror - '--delete' if @mirror - end - - ## - # Returns Rsync syntax for compressing the file transfers - def compress - '--compress' if @compress - end - - ## - # Returns Rsync syntax for invoking "archive" mode - def archive - '--archive' - end - - ## - # Returns Rsync syntax for defining a port to connect to - def port - "-e 'ssh -p #{@port}'" - end - - ## - # Returns Rsync syntax for setting a password (via a file) - def password - "--password-file='#{@password_file.path}'" unless @password.nil? - end - - ## - # If no block has been provided, it'll return the array of @directories. - # If a block has been provided, it'll evaluate it and add the defined paths to the @directories - def directories(&block) - unless block_given? - return @directories.map do |directory| - "'#{directory}'" - end.join("\s") - end - instance_eval(&block) - end - - ## - # Adds a path to the @directories array - def add(path) - @directories << path - end - - private - - ## - # Writes the provided password to a temporary file so that - # the rsync utility can read the password from this file - def write_password_file! - unless @password.nil? - @password_file = Tempfile.new('backup-rsync-password') - @password_file.write(@password) - @password_file.close - end - end - - ## - # Removes the previously created @password_file - # (temporary file containing the password) - def remove_password_file! - @password_file.unlink unless @password.nil? - end - - end - end -end diff --git a/lib/backup/syncer/rsync/push.rb b/lib/backup/syncer/rsync/push.rb new file mode 100644 index 000000000..fdb7edcc3 --- /dev/null +++ b/lib/backup/syncer/rsync/push.rb @@ -0,0 +1,153 @@ +# encoding: utf-8 + +## +# Require the tempfile Ruby library when Backup::Syncer::RSync is loaded +require 'tempfile' + +module Backup + module Syncer + module RSync + class Push < Syncer::Base + + ## + # Server credentials + attr_accessor :username, :password + + ## + # Server IP Address and SSH port + attr_accessor :ip + + ## + # The SSH port to connect to + attr_writer :port + + ## + # Directories to sync + attr_writer :directories + + ## + # Path to store the synced files/directories to + attr_accessor :path + + ## + # Flag for mirroring the files/directories + attr_writer :mirror + + ## + # Flag for compressing (only compresses for the transfer) + attr_writer :compress + + ## + # Additional options for the rsync cli + attr_accessor :additional_options + + ## + # Instantiates a new RSync Syncer object and sets the default configuration + # specified in the Backup::Configuration::Syncer::RSync. Then it sets the object + # defaults if particular properties weren't set. Finally it'll evaluate the users + # configuration file and overwrite anything that's been defined + def initialize(&block) + load_defaults! + + @directories = Array.new + @additional_options ||= Array.new + @path ||= 'backups' + @port ||= 22 + @mirror ||= false + @compress ||= false + + instance_eval(&block) if block_given? + write_password_file! + + @path = path.sub(/^\~\//, '') + end + + ## + # Performs the RSync operation + # debug options: -vhP + def perform! + Logger.message("#{ self.class } started syncing #{ directories }.") + Logger.silent( + run("#{ utility(:rsync) } #{ options } #{ directories } '#{ username }@#{ ip }:#{ path }'") + ) + + remove_password_file! + end + + ## + # Returns all the specified Rsync options, concatenated, ready for the CLI + def options + ([archive, mirror, compress, port, password] + additional_options).compact.join("\s") + end + + ## + # Returns Rsync syntax for enabling mirroring + def mirror + '--delete' if @mirror + end + + ## + # Returns Rsync syntax for compressing the file transfers + def compress + '--compress' if @compress + end + + ## + # Returns Rsync syntax for invoking "archive" mode + def archive + '--archive' + end + + ## + # Returns Rsync syntax for defining a port to connect to + def port + "-e 'ssh -p #{@port}'" + end + + ## + # Returns Rsync syntax for setting a password (via a file) + def password + "--password-file='#{@password_file.path}'" unless @password.nil? + end + + ## + # If no block has been provided, it'll return the array of @directories. + # If a block has been provided, it'll evaluate it and add the defined paths to the @directories + def directories(&block) + unless block_given? + return @directories.map do |directory| + "'#{directory}'" + end.join("\s") + end + instance_eval(&block) + end + + ## + # Adds a path to the @directories array + def add(path) + @directories << path + end + + private + + ## + # Writes the provided password to a temporary file so that + # the rsync utility can read the password from this file + def write_password_file! + unless @password.nil? + @password_file = Tempfile.new('backup-rsync-password') + @password_file.write(@password) + @password_file.close + end + end + + ## + # Removes the previously created @password_file + # (temporary file containing the password) + def remove_password_file! + @password_file.unlink unless @password.nil? + end + end + end + end +end diff --git a/spec/configuration/syncer/rsync_spec.rb b/spec/configuration/syncer/rsync/push_spec.rb similarity index 75% rename from spec/configuration/syncer/rsync_spec.rb rename to spec/configuration/syncer/rsync/push_spec.rb index bae8c4be9..3bb460ffd 100644 --- a/spec/configuration/syncer/rsync_spec.rb +++ b/spec/configuration/syncer/rsync/push_spec.rb @@ -1,10 +1,10 @@ # encoding: utf-8 -require File.expand_path('../../../spec_helper.rb', __FILE__) +require File.expand_path('../../../../spec_helper.rb', __FILE__) -describe Backup::Configuration::Syncer::RSync do +describe Backup::Configuration::Syncer::RSync::Push do before do - Backup::Configuration::Syncer::RSync.defaults do |rsync| + Backup::Configuration::Syncer::RSync::Push.defaults do |rsync| rsync.username = 'my_username' rsync.password = 'my_password' rsync.ip = '123.45.678.90' @@ -17,7 +17,7 @@ end it 'should set the default rsync configuration' do - rsync = Backup::Configuration::Syncer::RSync + rsync = Backup::Configuration::Syncer::RSync::Push rsync.username.should == 'my_username' rsync.password.should == 'my_password' rsync.ip.should == '123.45.678.90' @@ -30,9 +30,9 @@ describe '#clear_defaults!' do it 'should clear all the defaults, resetting them to nil' do - Backup::Configuration::Syncer::RSync.clear_defaults! + Backup::Configuration::Syncer::RSync::Push.clear_defaults! - rsync = Backup::Configuration::Syncer::RSync + rsync = Backup::Configuration::Syncer::RSync::Push rsync.username.should == nil rsync.password.should == nil rsync.ip.should == nil diff --git a/spec/syncer/rsync_spec.rb b/spec/syncer/rsync/push_spec.rb similarity index 90% rename from spec/syncer/rsync_spec.rb rename to spec/syncer/rsync/push_spec.rb index 4ba276a9f..8dad3bbdd 100644 --- a/spec/syncer/rsync_spec.rb +++ b/spec/syncer/rsync/push_spec.rb @@ -1,11 +1,11 @@ # encoding: utf-8 -require File.expand_path('../../spec_helper.rb', __FILE__) +require File.expand_path('../../../spec_helper.rb', __FILE__) -describe Backup::Syncer::RSync do +describe Backup::Syncer::RSync::Push do let(:rsync) do - Backup::Syncer::RSync.new do |rsync| + Backup::Syncer::RSync::Push.new do |rsync| rsync.username = 'my_username' rsync.password = 'my_password' rsync.ip = '123.45.678.90' @@ -23,7 +23,7 @@ end before do - Backup::Configuration::Syncer::RSync.clear_defaults! + Backup::Configuration::Syncer::RSync::Push.clear_defaults! end it 'should have defined the configuration properly' do @@ -39,14 +39,14 @@ end it 'should use the defaults if a particular attribute has not been defined' do - Backup::Configuration::Syncer::RSync.defaults do |rsync| + Backup::Configuration::Syncer::RSync::Push.defaults do |rsync| rsync.username = 'my_default_username' rsync.password = 'my_default_password' rsync.path = '~/backups' rsync.mirror = false end - rsync = Backup::Syncer::RSync.new do |rsync| + rsync = Backup::Syncer::RSync::Push.new do |rsync| rsync.password = 'my_password' rsync.ip = '123.45.678.90' rsync.compress = false @@ -63,7 +63,7 @@ end it 'should have its own defaults' do - rsync = Backup::Syncer::RSync.new + rsync = Backup::Syncer::RSync::Push.new rsync.port.should == "-e 'ssh -p 22'" rsync.path.should == 'backups' rsync.compress.should == nil @@ -173,7 +173,7 @@ describe '#perform' do it 'should invoke the rsync command to transfer the files and directories' do - Backup::Logger.expects(:message).with("Backup::Syncer::RSync started syncing '/some/random/directory' '/another/random/directory'.") + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Push started syncing '/some/random/directory' '/another/random/directory'.") rsync.expects(:utility).with(:rsync).returns(:rsync) rsync.expects(:remove_password_file!) rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' --password-file='#{rsync.instance_variable_get('@password_file').path}' " + @@ -182,7 +182,7 @@ end it 'should not pass in the --password-file option' do - Backup::Logger.expects(:message).with("Backup::Syncer::RSync started syncing '/some/random/directory' '/another/random/directory'.") + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Push started syncing '/some/random/directory' '/another/random/directory'.") rsync.password = nil rsync.expects(:utility).with(:rsync).returns(:rsync) rsync.expects(:remove_password_file!) From cb744f7154e7e4114ba258e7f4802215bb092b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Tue, 3 Jan 2012 19:02:27 +0100 Subject: [PATCH 02/10] add rsync/pull --- lib/backup.rb | 4 +- lib/backup/configuration/syncer/rsync/pull.rb | 12 ++ lib/backup/syncer/rsync/pull.rb | 27 +++ spec/configuration/syncer/rsync/pull_spec.rb | 46 ++++ spec/configuration/syncer/rsync/push_spec.rb | 10 +- spec/syncer/rsync/pull_spec.rb | 204 ++++++++++++++++++ 6 files changed, 297 insertions(+), 6 deletions(-) create mode 100644 lib/backup/configuration/syncer/rsync/pull.rb create mode 100644 lib/backup/syncer/rsync/pull.rb create mode 100644 spec/configuration/syncer/rsync/pull_spec.rb create mode 100644 spec/syncer/rsync/pull_spec.rb diff --git a/lib/backup.rb b/lib/backup.rb index b1b9a67d5..f3e278670 100644 --- a/lib/backup.rb +++ b/lib/backup.rb @@ -36,7 +36,7 @@ module Backup STORAGES = ['S3', 'CloudFiles', 'Ninefold', 'Dropbox', 'FTP', 'SFTP', 'SCP', 'RSync', 'Local'] COMPRESSORS = ['Gzip', 'Bzip2', 'Pbzip2', 'Lzma'] ENCRYPTORS = ['OpenSSL', 'GPG'] - SYNCERS = ['S3', 'Rsync' => ['Push']] + SYNCERS = ['S3', 'Rsync' => ['Push', 'Pull']] NOTIFIERS = ['Mail', 'Twitter', 'Campfire', 'Presently', 'Prowl', 'Hipchat'] ## @@ -108,6 +108,7 @@ module Syncer autoload :S3, File.join(SYNCER_PATH, 's3') module RSync autoload :Push, File.join(SYNCER_PATH, 'rsync', 'push') + autoload :Pull, File.join(SYNCER_PATH, 'rsync', 'pull') end end @@ -200,6 +201,7 @@ module Syncer autoload :S3, File.join(CONFIGURATION_PATH, 'syncer', 's3') module RSync autoload :Push, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'push') + autoload :Pull, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'pull') end end diff --git a/lib/backup/configuration/syncer/rsync/pull.rb b/lib/backup/configuration/syncer/rsync/pull.rb new file mode 100644 index 000000000..77defb413 --- /dev/null +++ b/lib/backup/configuration/syncer/rsync/pull.rb @@ -0,0 +1,12 @@ +# encoding: utf-8 + +module Backup + module Configuration + module Syncer + module RSync + class Pull < Push + end + end + end + end +end diff --git a/lib/backup/syncer/rsync/pull.rb b/lib/backup/syncer/rsync/pull.rb new file mode 100644 index 000000000..51c3cf8ee --- /dev/null +++ b/lib/backup/syncer/rsync/pull.rb @@ -0,0 +1,27 @@ +# encoding: utf-8 + +## +# Require the tempfile Ruby library when Backup::Syncer::RSync is loaded +require 'tempfile' + +module Backup + module Syncer + module RSync + class Pull < Push + + ## + # Performs the RSync operation + # debug options: -vhP + def perform! + @directories.each do |directory| + Logger.message("#{ self.class } started syncing '#{ directory }'.") + Logger.silent( + run("#{ utility(:rsync) } #{ options } '#{ path }' '#{ username }@#{ ip }:#{ directory }'") + ) + end + remove_password_file! + end + end + end + end +end diff --git a/spec/configuration/syncer/rsync/pull_spec.rb b/spec/configuration/syncer/rsync/pull_spec.rb new file mode 100644 index 000000000..5f3f300a2 --- /dev/null +++ b/spec/configuration/syncer/rsync/pull_spec.rb @@ -0,0 +1,46 @@ +# encoding: utf-8 + +require File.expand_path('../../../../spec_helper.rb', __FILE__) + +describe Backup::Configuration::Syncer::RSync::Pull do + before do + Backup::Configuration::Syncer::RSync::Pull.defaults do |rsync| + rsync.username = 'my_username' + rsync.password = 'my_password' + rsync.ip = '123.45.678.90' + rsync.port = 22 + rsync.path = '~/backups/' + rsync.mirror = true + rsync.compress = true + rsync.additional_options = [] + end + end + + it 'should set the default rsync configuration' do + rsync = Backup::Configuration::Syncer::RSync::Pull + rsync.username.should == 'my_username' + rsync.password.should == 'my_password' + rsync.ip.should == '123.45.678.90' + rsync.port.should == 22 + rsync.path.should == '~/backups/' + rsync.mirror.should == true + rsync.compress.should == true + rsync.additional_options.should == [] + end + + describe '#clear_defaults!' do + it 'should clear all the defaults, resetting them to nil' do + Backup::Configuration::Syncer::RSync::Pull.clear_defaults! + + rsync = Backup::Configuration::Syncer::RSync::Pull + rsync.username.should == nil + rsync.password.should == nil + rsync.ip.should == nil + rsync.port.should == nil + rsync.path.should == nil + rsync.mirror.should == nil + rsync.compress.should == nil + rsync.additional_options.should == nil + end + end +end diff --git a/spec/configuration/syncer/rsync/push_spec.rb b/spec/configuration/syncer/rsync/push_spec.rb index 3bb460ffd..5f3f300a2 100644 --- a/spec/configuration/syncer/rsync/push_spec.rb +++ b/spec/configuration/syncer/rsync/push_spec.rb @@ -2,9 +2,9 @@ require File.expand_path('../../../../spec_helper.rb', __FILE__) -describe Backup::Configuration::Syncer::RSync::Push do +describe Backup::Configuration::Syncer::RSync::Pull do before do - Backup::Configuration::Syncer::RSync::Push.defaults do |rsync| + Backup::Configuration::Syncer::RSync::Pull.defaults do |rsync| rsync.username = 'my_username' rsync.password = 'my_password' rsync.ip = '123.45.678.90' @@ -17,7 +17,7 @@ end it 'should set the default rsync configuration' do - rsync = Backup::Configuration::Syncer::RSync::Push + rsync = Backup::Configuration::Syncer::RSync::Pull rsync.username.should == 'my_username' rsync.password.should == 'my_password' rsync.ip.should == '123.45.678.90' @@ -30,9 +30,9 @@ describe '#clear_defaults!' do it 'should clear all the defaults, resetting them to nil' do - Backup::Configuration::Syncer::RSync::Push.clear_defaults! + Backup::Configuration::Syncer::RSync::Pull.clear_defaults! - rsync = Backup::Configuration::Syncer::RSync::Push + rsync = Backup::Configuration::Syncer::RSync::Pull rsync.username.should == nil rsync.password.should == nil rsync.ip.should == nil diff --git a/spec/syncer/rsync/pull_spec.rb b/spec/syncer/rsync/pull_spec.rb new file mode 100644 index 000000000..0c077e0c3 --- /dev/null +++ b/spec/syncer/rsync/pull_spec.rb @@ -0,0 +1,204 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper.rb', __FILE__) + +describe Backup::Syncer::RSync::Pull do + + let(:rsync) do + Backup::Syncer::RSync::Pull.new do |rsync| + rsync.username = 'my_username' + rsync.password = 'my_password' + rsync.ip = '123.45.678.90' + rsync.port = 22 + rsync.path = '~/backups/' + rsync.mirror = true + rsync.compress = true + rsync.additional_options = [] + + rsync.directories do |directory| + directory.add "/some/random/directory" + directory.add "/another/random/directory" + end + end + end + + before do + Backup::Configuration::Syncer::RSync::Pull.clear_defaults! + end + + it 'should have defined the configuration properly' do + rsync.username.should == 'my_username' + rsync.password.should =~ /backup-rsync-password/ + rsync.ip.should == '123.45.678.90' + rsync.port.should == "-e 'ssh -p 22'" + rsync.path.should == 'backups/' + rsync.mirror.should == "--delete" + rsync.compress.should == "--compress" + + File.read(rsync.instance_variable_get('@password_file').path).should == 'my_password' + end + + it 'should use the defaults if a particular attribute has not been defined' do + Backup::Configuration::Syncer::RSync::Pull.defaults do |rsync| + rsync.username = 'my_default_username' + rsync.password = 'my_default_password' + rsync.path = '~/backups' + rsync.mirror = false + end + + rsync = Backup::Syncer::RSync::Pull.new do |rsync| + rsync.password = 'my_password' + rsync.ip = '123.45.678.90' + rsync.compress = false + end + + rsync.username.should == 'my_default_username' + rsync.password.should =~ /backup-rsync-password/ + rsync.ip.should == '123.45.678.90' + rsync.port.should == "-e 'ssh -p 22'" + rsync.mirror.should == nil + rsync.compress.should == nil + + File.read(rsync.instance_variable_get('@password_file').path).should == 'my_password' + end + + it 'should have its own defaults' do + rsync = Backup::Syncer::RSync::Pull.new + rsync.port.should == "-e 'ssh -p 22'" + rsync.path.should == 'backups' + rsync.compress.should == nil + rsync.mirror.should == nil + rsync.directories.should == '' + rsync.additional_options.should == [] + end + + describe '#mirror' do + context 'when true' do + it do + rsync.mirror = true + rsync.mirror.should == '--delete' + end + end + + context 'when nil/false' do + it do + rsync.mirror = nil + rsync.mirror.should == nil + end + + it do + rsync.mirror = false + rsync.mirror.should == nil + end + end + end + + describe '#compress' do + context 'when true' do + it do + rsync.compress = true + rsync.compress.should == '--compress' + end + end + + context 'when nil/false' do + it do + rsync.compress = nil + rsync.compress.should == nil + end + + it do + rsync.compress = false + rsync.compress.should == nil + end + end + end + + describe '#archive' do + it do + rsync.archive.should == '--archive' + end + end + + describe '#port' do + it do + rsync.port.should == "-e 'ssh -p 22'" + end + end + + describe '#directories' do + context 'when its empty' do + it do + rsync.directories = [] + rsync.directories.should == '' + end + end + + context 'when it has items' do + it do + rsync.directories = ['directory1', 'directory1/directory2', 'directory1/directory2/directory3'] + rsync.directories.should == "'directory1' 'directory1/directory2' 'directory1/directory2/directory3'" + end + end + end + + describe '#options' do + it do + rsync.options.should == "--archive --delete --compress -e 'ssh -p 22' " + + "--password-file='#{rsync.instance_variable_get('@password_file').path}'" + end + end + + describe '#password' do + before do + rsync.stubs(:utility).with(:rsync).returns(:rsync) + rsync.stubs(:run) + end + + it do + rsync.password = 'my_password' + rsync.expects(:remove_password_file!) + + rsync.perform! + end + + it do + rsync.password = nil + rsync.expects(:remove_password_file!) + + rsync.perform! + end + end + + describe '#perform' do + + it 'should invoke the rsync command to transfer the files and directories' do + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Pull started syncing '/some/random/directory'.") + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Pull started syncing '/another/random/directory'.") + + rsync.expects(:utility).with(:rsync).returns(:rsync).twice + rsync.expects(:remove_password_file!) + rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' --password-file='#{rsync.instance_variable_get('@password_file').path}' " + + "'backups/' 'my_username@123.45.678.90:/some/random/directory'") + rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' --password-file='#{rsync.instance_variable_get('@password_file').path}' " + + "'backups/' 'my_username@123.45.678.90:/another/random/directory'") + rsync.perform! + end + + it 'should not pass in the --password-file option' do + rsync.password = nil + + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Pull started syncing '/some/random/directory'.") + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Pull started syncing '/another/random/directory'.") + + rsync.expects(:utility).with(:rsync).returns(:rsync).twice + rsync.expects(:remove_password_file!) + rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' " + + "'backups/' 'my_username@123.45.678.90:/some/random/directory'") + rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' " + + "'backups/' 'my_username@123.45.678.90:/another/random/directory'") + rsync.perform! + end + end + +end From 504400d154be68fe4da0601b613183c9ad205ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 12:11:11 +0100 Subject: [PATCH 03/10] working local rsyncer --- lib/backup.rb | 12 +- .../configuration/syncer/rsync/local.rb | 31 +++++ lib/backup/syncer/rsync/local.rb | 94 ++++++++++++++ spec/configuration/syncer/rsync/local_spec.rb | 31 +++++ spec/syncer/rsync/local_spec.rb | 121 ++++++++++++++++++ 5 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 lib/backup/configuration/syncer/rsync/local.rb create mode 100644 lib/backup/syncer/rsync/local.rb create mode 100644 spec/configuration/syncer/rsync/local_spec.rb create mode 100644 spec/syncer/rsync/local_spec.rb diff --git a/lib/backup.rb b/lib/backup.rb index f3e278670..3586f1b4e 100644 --- a/lib/backup.rb +++ b/lib/backup.rb @@ -36,7 +36,7 @@ module Backup STORAGES = ['S3', 'CloudFiles', 'Ninefold', 'Dropbox', 'FTP', 'SFTP', 'SCP', 'RSync', 'Local'] COMPRESSORS = ['Gzip', 'Bzip2', 'Pbzip2', 'Lzma'] ENCRYPTORS = ['OpenSSL', 'GPG'] - SYNCERS = ['S3', 'Rsync' => ['Push', 'Pull']] + SYNCERS = ['S3', 'Rsync' => ['Push', 'Pull', 'Local']] NOTIFIERS = ['Mail', 'Twitter', 'Campfire', 'Presently', 'Prowl', 'Hipchat'] ## @@ -107,8 +107,9 @@ module Syncer autoload :Base, File.join(SYNCER_PATH, 'base') autoload :S3, File.join(SYNCER_PATH, 's3') module RSync - autoload :Push, File.join(SYNCER_PATH, 'rsync', 'push') - autoload :Pull, File.join(SYNCER_PATH, 'rsync', 'pull') + autoload :Push, File.join(SYNCER_PATH, 'rsync', 'push') + autoload :Pull, File.join(SYNCER_PATH, 'rsync', 'pull') + autoload :Local, File.join(SYNCER_PATH, 'rsync', 'local') end end @@ -200,8 +201,9 @@ module Storage module Syncer autoload :S3, File.join(CONFIGURATION_PATH, 'syncer', 's3') module RSync - autoload :Push, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'push') - autoload :Pull, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'pull') + autoload :Push, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'push') + autoload :Pull, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'pull') + autoload :Local, File.join(CONFIGURATION_PATH, 'syncer', 'rsync', 'local') end end diff --git a/lib/backup/configuration/syncer/rsync/local.rb b/lib/backup/configuration/syncer/rsync/local.rb new file mode 100644 index 000000000..1685de86d --- /dev/null +++ b/lib/backup/configuration/syncer/rsync/local.rb @@ -0,0 +1,31 @@ +# encoding: utf-8 + +module Backup + module Configuration + module Syncer + module RSync + class Local < Configuration::Base + class << self + + ## + # Directories to sync + attr_accessor :directories + + ## + # Path to store the synced files/directories to + attr_accessor :path + + ## + # Flag for mirroring the files/directories + attr_accessor :mirror + + ## + # Additional options for the rsync cli + attr_accessor :additional_options + + end + end + end + end + end +end diff --git a/lib/backup/syncer/rsync/local.rb b/lib/backup/syncer/rsync/local.rb new file mode 100644 index 000000000..dfbc4acc6 --- /dev/null +++ b/lib/backup/syncer/rsync/local.rb @@ -0,0 +1,94 @@ +# encoding: utf-8 + +## +# Require the tempfile Ruby library when Backup::Syncer::RSync is loaded +require 'tempfile' + +module Backup + module Syncer + module RSync + class Local < Syncer::Base + + ## + # Directories to sync + attr_writer :directories + + ## + # Path to store the synced files/directories to + attr_accessor :path + + ## + # Flag for mirroring the files/directories + attr_writer :mirror + + ## + # Additional options for the rsync cli + attr_accessor :additional_options + + ## + # Instantiates a new RSync Syncer object and sets the default configuration + # specified in the Backup::Configuration::Syncer::RSync. Then it sets the object + # defaults if particular properties weren't set. Finally it'll evaluate the users + # configuration file and overwrite anything that's been defined + def initialize(&block) + load_defaults! + + @directories = Array.new + @additional_options ||= Array.new + @path ||= 'backups' + @mirror ||= false + + instance_eval(&block) if block_given? + + @path = path.sub(/^\~\//, '') + end + + ## + # Performs the RSync operation + # debug options: -vhP + def perform! + Logger.message("#{ self.class } started syncing #{ directories }.") + Logger.silent( + run("#{ utility(:rsync) } #{ options } #{ directories } '#{ path }'") + ) + end + + ## + # Returns all the specified Rsync options, concatenated, ready for the CLI + def options + ([archive, mirror] + additional_options).compact.join("\s") + end + + ## + # Returns Rsync syntax for enabling mirroring + def mirror + '--delete' if @mirror + end + + ## + # Returns Rsync syntax for invoking "archive" mode + def archive + '--archive' + end + + ## + # If no block has been provided, it'll return the array of @directories. + # If a block has been provided, it'll evaluate it and add the defined paths to the @directories + def directories(&block) + unless block_given? + return @directories.map do |directory| + "'#{directory}'" + end.join("\s") + end + instance_eval(&block) + end + + ## + # Adds a path to the @directories array + def add(path) + @directories << path + end + end + end + end +end diff --git a/spec/configuration/syncer/rsync/local_spec.rb b/spec/configuration/syncer/rsync/local_spec.rb new file mode 100644 index 000000000..59895ed01 --- /dev/null +++ b/spec/configuration/syncer/rsync/local_spec.rb @@ -0,0 +1,31 @@ +# encoding: utf-8 + +require File.expand_path('../../../../spec_helper.rb', __FILE__) + +describe Backup::Configuration::Syncer::RSync::Local do + before do + Backup::Configuration::Syncer::RSync::Local.defaults do |rsync| + rsync.path = '~/backups/' + rsync.mirror = true + rsync.additional_options = [] + end + end + + it 'should set the default rsync configuration' do + rsync = Backup::Configuration::Syncer::RSync::Local + rsync.path.should == '~/backups/' + rsync.mirror.should == true + rsync.additional_options.should == [] + end + + describe '#clear_defaults!' do + it 'should clear all the defaults, resetting them to nil' do + Backup::Configuration::Syncer::RSync::Local.clear_defaults! + + rsync = Backup::Configuration::Syncer::RSync::Local + rsync.path.should == nil + rsync.mirror.should == nil + rsync.additional_options.should == nil + end + end +end diff --git a/spec/syncer/rsync/local_spec.rb b/spec/syncer/rsync/local_spec.rb new file mode 100644 index 000000000..dfc76bd17 --- /dev/null +++ b/spec/syncer/rsync/local_spec.rb @@ -0,0 +1,121 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper.rb', __FILE__) + +describe Backup::Syncer::RSync::Local do + + let(:rsync) do + Backup::Syncer::RSync::Local.new do |rsync| + rsync.path = '~/backups/' + rsync.mirror = true + rsync.additional_options = [] + + rsync.directories do |directory| + directory.add "/some/random/directory" + directory.add "/another/random/directory" + end + end + end + + before do + Backup::Configuration::Syncer::RSync::Local.clear_defaults! + end + + it 'should have defined the configuration properly' do + rsync.path.should == 'backups/' + rsync.mirror.should == "--delete" + end + + it 'should use the defaults if a particular attribute has not been defined' do + Backup::Configuration::Syncer::RSync::Local.defaults do |rsync| + rsync.mirror = false + end + + rsync = Backup::Syncer::RSync::Local.new do |rsync| + rsync.directories do |directory| + directory.add "/some/random/directory" + directory.add "/another/random/directory" + end + end + + rsync.mirror.should == nil + rsync.directories.should == "'/some/random/directory' '/another/random/directory'" + end + + it 'should have its own defaults' do + rsync = Backup::Syncer::RSync::Local.new + rsync.path.should == 'backups' + rsync.mirror.should == nil + rsync.directories.should == '' + rsync.additional_options.should == [] + end + + describe '#mirror' do + context 'when true' do + it do + rsync.mirror = true + rsync.mirror.should == '--delete' + end + end + + context 'when nil/false' do + it do + rsync.mirror = nil + rsync.mirror.should == nil + end + + it do + rsync.mirror = false + rsync.mirror.should == nil + end + end + end + + describe '#archive' do + it do + rsync.archive.should == '--archive' + end + end + + describe '#directories' do + context 'when its empty' do + it do + rsync.directories = [] + rsync.directories.should == '' + end + end + + context 'when it has items' do + it do + rsync.directories = ['directory1', 'directory1/directory2', 'directory1/directory2/directory3'] + rsync.directories.should == "'directory1' 'directory1/directory2' 'directory1/directory2/directory3'" + end + end + end + + describe '#options' do + it do + rsync.options.should == "--archive --delete" + end + end + + describe '#perform' do + + it 'should invoke the rsync command to transfer the files and directories' do + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Local started syncing '/some/random/directory' '/another/random/directory'.") + rsync.expects(:utility).with(:rsync).returns(:rsync) + rsync.expects(:run).with("rsync --archive --delete " + + "'/some/random/directory' '/another/random/directory' 'backups/'") + rsync.perform! + end + + it 'should not pass in the --password-file option' do + Backup::Logger.expects(:message).with("Backup::Syncer::RSync::Local started syncing '/some/random/directory' '/another/random/directory'.") + rsync.expects(:utility).with(:rsync).returns(:rsync) + rsync.expects(:run).with("rsync --archive --delete " + + "'/some/random/directory' '/another/random/directory' 'backups/'") + rsync.perform! + end + end + +end From bbac371323d3194d987b5664e4563b2b6c4ccbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 12:18:43 +0100 Subject: [PATCH 04/10] refactorized rsync/pull to rely on rsync/local for options and methods --- lib/backup/configuration/syncer/rsync/push.rb | 18 +------ lib/backup/syncer/rsync/push.rb | 48 +------------------ 2 files changed, 2 insertions(+), 64 deletions(-) diff --git a/lib/backup/configuration/syncer/rsync/push.rb b/lib/backup/configuration/syncer/rsync/push.rb index 9657f14f3..ebd2abfbe 100644 --- a/lib/backup/configuration/syncer/rsync/push.rb +++ b/lib/backup/configuration/syncer/rsync/push.rb @@ -4,7 +4,7 @@ module Backup module Configuration module Syncer module RSync - class Push < Configuration::Base + class Push < Local class << self ## @@ -19,26 +19,10 @@ class << self # The SSH port to connect to attr_accessor :port - ## - # Directories to sync - attr_accessor :directories - - ## - # Path to store the synced files/directories to - attr_accessor :path - - ## - # Flag for mirroring the files/directories - attr_accessor :mirror - ## # Flag for compressing (only compresses for the transfer) attr_accessor :compress - ## - # Additional options for the rsync cli - attr_accessor :additional_options - end end end diff --git a/lib/backup/syncer/rsync/push.rb b/lib/backup/syncer/rsync/push.rb index fdb7edcc3..c15326fc8 100644 --- a/lib/backup/syncer/rsync/push.rb +++ b/lib/backup/syncer/rsync/push.rb @@ -7,7 +7,7 @@ module Backup module Syncer module RSync - class Push < Syncer::Base + class Push < Local ## # Server credentials @@ -21,26 +21,10 @@ class Push < Syncer::Base # The SSH port to connect to attr_writer :port - ## - # Directories to sync - attr_writer :directories - - ## - # Path to store the synced files/directories to - attr_accessor :path - - ## - # Flag for mirroring the files/directories - attr_writer :mirror - ## # Flag for compressing (only compresses for the transfer) attr_writer :compress - ## - # Additional options for the rsync cli - attr_accessor :additional_options - ## # Instantiates a new RSync Syncer object and sets the default configuration # specified in the Backup::Configuration::Syncer::RSync. Then it sets the object @@ -80,24 +64,12 @@ def options ([archive, mirror, compress, port, password] + additional_options).compact.join("\s") end - ## - # Returns Rsync syntax for enabling mirroring - def mirror - '--delete' if @mirror - end - ## # Returns Rsync syntax for compressing the file transfers def compress '--compress' if @compress end - ## - # Returns Rsync syntax for invoking "archive" mode - def archive - '--archive' - end - ## # Returns Rsync syntax for defining a port to connect to def port @@ -110,24 +82,6 @@ def password "--password-file='#{@password_file.path}'" unless @password.nil? end - ## - # If no block has been provided, it'll return the array of @directories. - # If a block has been provided, it'll evaluate it and add the defined paths to the @directories - def directories(&block) - unless block_given? - return @directories.map do |directory| - "'#{directory}'" - end.join("\s") - end - instance_eval(&block) - end - - ## - # Adds a path to the @directories array - def add(path) - @directories << path - end - private ## From 56dc6cb86366f90064aa16ea5d5c5ef9f5a53d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 13:57:41 +0100 Subject: [PATCH 05/10] add missing specs for Model.sync_with --- spec/model_spec.rb | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/spec/model_spec.rb b/spec/model_spec.rb index 036d13171..00e29c5d8 100644 --- a/spec/model_spec.rb +++ b/spec/model_spec.rb @@ -30,7 +30,12 @@ def initialize(&block); end class Backup::Notifier::TestMail def initialize(&block); end end - + class Backup::Syncer::TestS3 + def initialize(&block); end + end + class Backup::Syncer::TestRSync + def initialize(&block); end + end end let(:model) { Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') {} } @@ -189,6 +194,26 @@ def initialize(&block); end end end + describe '#sync_with' do + it 'should add a syncer to the array of syncers to use' do + model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do + sync_with('TestRSync') + end + + model.syncers.count.should == 1 + end + + it 'should add a Syncer to the array of syncers to use' do + model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do + sync_with('TestS3') + sync_with('TestRSync') + end + + model.syncers.count.should == 2 + end + end + + describe '#notify_by' do it 'should add a notifier to the array of notifiers to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do From 1ac97178b20c8b7e06debd91b5f836e7be70b8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 14:22:01 +0100 Subject: [PATCH 06/10] modify Model so it acceptes deeply-nested objects - i.e Backup::Syncer::RSync::Local --- lib/backup/model.rb | 52 +++++++++++++++++++-------------------------- spec/model_spec.rb | 6 +++--- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/lib/backup/model.rb b/lib/backup/model.rb index 7f88390a3..6e11ff692 100644 --- a/lib/backup/model.rb +++ b/lib/backup/model.rb @@ -125,61 +125,49 @@ def initialize(trigger, label, &block) # Adds a database to the array of databases # to dump during the backup process def database(database, &block) - @databases << Backup::Database.const_get( - last_constant(database) - ).new(&block) + @databases << get_class_from_scope(Backup::Database, database).new(&block) end ## # Adds an archive to the array of archives # to store during the backup process - def archive(name, &block) - @archives << Backup::Archive.new(name, &block) + def archive(archive_name, &block) + @archives << Backup::Archive.new(archive_name, &block) end ## # Adds an encryptor to the array of encryptors # to use during the backup process - def encrypt_with(name, &block) - @encryptors << Backup::Encryptor.const_get( - last_constant(name) - ).new(&block) + def encrypt_with(encryptor_name, &block) + @encryptors << get_class_from_scope(Backup::Encryptor, encryptor_name).new(&block) end ## # Adds a compressor to the array of compressors # to use during the backup process - def compress_with(name, &block) - @compressors << Backup::Compressor.const_get( - last_constant(name) - ).new(&block) + def compress_with(compressor_name, &block) + @compressors << get_class_from_scope(Backup::Compressor, compressor_name).new(&block) end ## # Adds a notifier to the array of notifiers # to use during the backup process - def notify_by(name, &block) - @notifiers << Backup::Notifier.const_get( - last_constant(name) - ).new(&block) + def notify_by(notifier_name, &block) + @notifiers << get_class_from_scope(Backup::Notifier, notifier_name).new(&block) end ## # Adds a storage method to the array of storage # methods to use during the backup process - def store_with(storage, storage_id = nil, &block) - @storages << Backup::Storage.const_get( - last_constant(storage) - ).new(storage_id, &block) + def store_with(storage_name, storage_id = nil, &block) + @storages << get_class_from_scope(Backup::Storage, storage_name).new(storage_id, &block) end ## # Adds a syncer method to the array of syncer # methods to use during the backup process - def sync_with(syncer, &block) - @syncers << Backup::Syncer.const_get( - last_constant(syncer) - ).new(&block) + def sync_with(syncer_name, &block) + @syncers << get_class_from_scope(Backup::Syncer, syncer_name).new(&block) end ## @@ -323,11 +311,15 @@ def procedure_instance_variables end ## - # Returns the string representation of the last value of a nested constant - # example: last_constant(Backup::Model::MySQL) becomes and returns "MySQL" - def last_constant(constant) - constant.to_s.split("::").last + # Returns the class/model specified by a string inside a scope. + # For example, given the scope Backup::Model and the name "MySQL", + # it returns the class Backup::Model::MySQL. It accepts deeply nested class/modules + def get_class_from_scope(scope, name) + klass = scope + name.split('::').each do |name_chunk| + klass = klass.const_get(name_chunk) + end + klass end - end end diff --git a/spec/model_spec.rb b/spec/model_spec.rb index 00e29c5d8..1b5c9dd23 100644 --- a/spec/model_spec.rb +++ b/spec/model_spec.rb @@ -33,7 +33,7 @@ def initialize(&block); end class Backup::Syncer::TestS3 def initialize(&block); end end - class Backup::Syncer::TestRSync + class Backup::Syncer::RSync::TestLocal def initialize(&block); end end end @@ -197,7 +197,7 @@ def initialize(&block); end describe '#sync_with' do it 'should add a syncer to the array of syncers to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do - sync_with('TestRSync') + sync_with('RSync::TestLocal') end model.syncers.count.should == 1 @@ -206,7 +206,7 @@ def initialize(&block); end it 'should add a Syncer to the array of syncers to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do sync_with('TestS3') - sync_with('TestRSync') + sync_with('RSync::TestLocal') end model.syncers.count.should == 2 From 2376660b5de6726be1d362b95bdb411bbf59fe7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 15:31:59 +0100 Subject: [PATCH 07/10] model accepts descriptors and proper classes. Added specs for that (they were missing) --- lib/backup/model.rb | 23 ++++++++++++----------- spec/model_spec.rb | 26 +++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/lib/backup/model.rb b/lib/backup/model.rb index 6e11ff692..40624fcab 100644 --- a/lib/backup/model.rb +++ b/lib/backup/model.rb @@ -138,15 +138,15 @@ def archive(archive_name, &block) ## # Adds an encryptor to the array of encryptors # to use during the backup process - def encrypt_with(encryptor_name, &block) - @encryptors << get_class_from_scope(Backup::Encryptor, encryptor_name).new(&block) + def encrypt_with(encryptor, &block) + @encryptors << get_class_from_scope(Backup::Encryptor, encryptor).new(&block) end ## # Adds a compressor to the array of compressors # to use during the backup process - def compress_with(compressor_name, &block) - @compressors << get_class_from_scope(Backup::Compressor, compressor_name).new(&block) + def compress_with(compressor, &block) + @compressors << get_class_from_scope(Backup::Compressor, compressor).new(&block) end ## @@ -159,15 +159,15 @@ def notify_by(notifier_name, &block) ## # Adds a storage method to the array of storage # methods to use during the backup process - def store_with(storage_name, storage_id = nil, &block) - @storages << get_class_from_scope(Backup::Storage, storage_name).new(storage_id, &block) + def store_with(storage, storage_id = nil, &block) + @storages << get_class_from_scope(Backup::Storage, storage).new(storage_id, &block) end ## # Adds a syncer method to the array of syncer # methods to use during the backup process - def sync_with(syncer_name, &block) - @syncers << get_class_from_scope(Backup::Syncer, syncer_name).new(&block) + def sync_with(syncer, &block) + @syncers << get_class_from_scope(Backup::Syncer, syncer).new(&block) end ## @@ -314,10 +314,11 @@ def procedure_instance_variables # Returns the class/model specified by a string inside a scope. # For example, given the scope Backup::Model and the name "MySQL", # it returns the class Backup::Model::MySQL. It accepts deeply nested class/modules - def get_class_from_scope(scope, name) + def get_class_from_scope(scope, constant) + return constant if constant.is_a? Class klass = scope - name.split('::').each do |name_chunk| - klass = klass.const_get(name_chunk) + constant.split('::').each do |chunk| + klass = klass.const_get(chunk) end klass end diff --git a/spec/model_spec.rb b/spec/model_spec.rb index 1b5c9dd23..4009323d0 100644 --- a/spec/model_spec.rb +++ b/spec/model_spec.rb @@ -165,6 +165,15 @@ def initialize(&block); end model.compressors.count.should == 1 end + it 'should accept compressor classes in addition to names' do + model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do + compress_with(Backup::Compressor::TestGzip) + end + + model.compressors.count.should == 1 + end + + it 'should add a compressor to the array of compressors to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do compress_with('TestGzip') @@ -184,6 +193,14 @@ def initialize(&block); end model.encryptors.count.should == 1 end + it 'should accept encryptor classes in addition to names' do + model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do + encrypt_with(Backup::Encryptor::TestOpenSSL) + end + + model.encryptors.count.should == 1 + end + it 'should add a encryptor to the array of encryptors to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do encrypt_with('TestOpenSSL') @@ -203,6 +220,14 @@ def initialize(&block); end model.syncers.count.should == 1 end + it 'should accept sync classes in addition to names' do + model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do + sync_with(Backup::Syncer::RSync::TestLocal) + end + + model.syncers.count.should == 1 + end + it 'should add a Syncer to the array of syncers to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do sync_with('TestS3') @@ -213,7 +238,6 @@ def initialize(&block); end end end - describe '#notify_by' do it 'should add a notifier to the array of notifiers to use' do model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do From de5b323e2ee50cd88cb5fc8d97c9bd3d3c6c3472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 15:33:13 +0100 Subject: [PATCH 08/10] switch src & dest in Syncer::RSync::Pull --- lib/backup/syncer/rsync/pull.rb | 2 +- spec/syncer/rsync/pull_spec.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/backup/syncer/rsync/pull.rb b/lib/backup/syncer/rsync/pull.rb index 51c3cf8ee..d532292f0 100644 --- a/lib/backup/syncer/rsync/pull.rb +++ b/lib/backup/syncer/rsync/pull.rb @@ -16,7 +16,7 @@ def perform! @directories.each do |directory| Logger.message("#{ self.class } started syncing '#{ directory }'.") Logger.silent( - run("#{ utility(:rsync) } #{ options } '#{ path }' '#{ username }@#{ ip }:#{ directory }'") + run("#{ utility(:rsync) } #{ options } '#{ username }@#{ ip }:#{ directory }' '#{ path }'") ) end remove_password_file! diff --git a/spec/syncer/rsync/pull_spec.rb b/spec/syncer/rsync/pull_spec.rb index 0c077e0c3..60a26ea09 100644 --- a/spec/syncer/rsync/pull_spec.rb +++ b/spec/syncer/rsync/pull_spec.rb @@ -179,9 +179,9 @@ rsync.expects(:utility).with(:rsync).returns(:rsync).twice rsync.expects(:remove_password_file!) rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' --password-file='#{rsync.instance_variable_get('@password_file').path}' " + - "'backups/' 'my_username@123.45.678.90:/some/random/directory'") + "'my_username@123.45.678.90:/some/random/directory' 'backups/'") rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' --password-file='#{rsync.instance_variable_get('@password_file').path}' " + - "'backups/' 'my_username@123.45.678.90:/another/random/directory'") + "'my_username@123.45.678.90:/another/random/directory' 'backups/'") rsync.perform! end @@ -194,9 +194,9 @@ rsync.expects(:utility).with(:rsync).returns(:rsync).twice rsync.expects(:remove_password_file!) rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' " + - "'backups/' 'my_username@123.45.678.90:/some/random/directory'") + "'my_username@123.45.678.90:/some/random/directory' 'backups/'") rsync.expects(:run).with("rsync --archive --delete --compress -e 'ssh -p 22' " + - "'backups/' 'my_username@123.45.678.90:/another/random/directory'") + "'my_username@123.45.678.90:/another/random/directory' 'backups/'") rsync.perform! end end From 06b3cace7134c9ea0a01600982e4671786f39bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 16:26:50 +0100 Subject: [PATCH 09/10] updated templates --- templates/cli/utility/syncer/rsync_local | 12 ++++++++++++ .../cli/utility/syncer/{rsync => rsync_pull} | 4 ++-- templates/cli/utility/syncer/rsync_push | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 templates/cli/utility/syncer/rsync_local rename templates/cli/utility/syncer/{rsync => rsync_pull} (86%) create mode 100644 templates/cli/utility/syncer/rsync_push diff --git a/templates/cli/utility/syncer/rsync_local b/templates/cli/utility/syncer/rsync_local new file mode 100644 index 000000000..dd50039bf --- /dev/null +++ b/templates/cli/utility/syncer/rsync_local @@ -0,0 +1,12 @@ + ## + # RSync::Local [Syncer] + # + sync_with RSync do |rsync| + rsync.path = "~/backups/" + rsync.mirror = true + + rsync.directories do |directory| + directory.add "/var/apps/my_app/public/uploads" + directory.add "/var/apps/my_app/logs" + end + end diff --git a/templates/cli/utility/syncer/rsync b/templates/cli/utility/syncer/rsync_pull similarity index 86% rename from templates/cli/utility/syncer/rsync rename to templates/cli/utility/syncer/rsync_pull index 06ec2e0a2..3800f7bcf 100644 --- a/templates/cli/utility/syncer/rsync +++ b/templates/cli/utility/syncer/rsync_pull @@ -1,7 +1,7 @@ ## - # RSync [Syncer] + # RSync::Pull [Syncer] # - sync_with RSync do |rsync| + sync_with RSync::Pull do |rsync| rsync.ip = "123.45.678.90" rsync.port = 22 rsync.username = "my_username" diff --git a/templates/cli/utility/syncer/rsync_push b/templates/cli/utility/syncer/rsync_push new file mode 100644 index 000000000..ed30909b4 --- /dev/null +++ b/templates/cli/utility/syncer/rsync_push @@ -0,0 +1,17 @@ + ## + # RSync::Push [Syncer] + # + sync_with RSync::Push do |rsync| + rsync.ip = "123.45.678.90" + rsync.port = 22 + rsync.username = "my_username" + rsync.password = "my_password" + rsync.path = "~/backups/" + rsync.mirror = true + rsync.compress = true + + rsync.directories do |directory| + directory.add "/var/apps/my_app/public/uploads" + directory.add "/var/apps/my_app/logs" + end + end From d9e453f51d249797c6a8f7cadfb0af14bbe497e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20Cota?= Date: Wed, 4 Jan 2012 16:29:48 +0100 Subject: [PATCH 10/10] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6aae94c94..a13ca2741 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Storage Features Syncers ------- -- RSync +- RSync (Push, Pull and Local) - Amazon Simple Storage Service (S3) [Syncer Wiki Page](https://github.com/meskyanichi/backup/wiki/Syncers)