backup 2.4.5.1 → 3.0.0.build.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.
Files changed (168) hide show
  1. data/.gitignore +2 -0
  2. data/.infinity_test +7 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +17 -0
  5. data/Gemfile.lock +88 -0
  6. data/LICENSE.md +24 -0
  7. data/README.md +189 -75
  8. data/backup.gemspec +41 -0
  9. data/bin/backup +161 -90
  10. data/lib/backup.rb +133 -117
  11. data/lib/backup/archive.rb +54 -0
  12. data/lib/backup/cli.rb +50 -0
  13. data/lib/backup/compressor/base.rb +17 -0
  14. data/lib/backup/compressor/gzip.rb +61 -0
  15. data/lib/backup/configuration/base.rb +7 -67
  16. data/lib/backup/configuration/compressor/base.rb +10 -0
  17. data/lib/backup/configuration/compressor/gzip.rb +23 -0
  18. data/lib/backup/configuration/database/base.rb +18 -0
  19. data/lib/backup/configuration/database/mongodb.rb +37 -0
  20. data/lib/backup/configuration/database/mysql.rb +37 -0
  21. data/lib/backup/configuration/database/postgresql.rb +37 -0
  22. data/lib/backup/configuration/database/redis.rb +35 -0
  23. data/lib/backup/configuration/encryptor/base.rb +10 -0
  24. data/lib/backup/configuration/encryptor/gpg.rb +17 -0
  25. data/lib/backup/configuration/encryptor/open_ssl.rb +26 -0
  26. data/lib/backup/configuration/helpers.rb +47 -17
  27. data/lib/backup/configuration/notifier/base.rb +39 -0
  28. data/lib/backup/configuration/notifier/mail.rb +52 -0
  29. data/lib/backup/configuration/storage/base.rb +18 -0
  30. data/lib/backup/configuration/storage/cloudfiles.rb +21 -0
  31. data/lib/backup/configuration/storage/dropbox.rb +25 -0
  32. data/lib/backup/configuration/storage/ftp.rb +25 -0
  33. data/lib/backup/configuration/storage/rsync.rb +25 -0
  34. data/lib/backup/configuration/storage/s3.rb +25 -0
  35. data/lib/backup/configuration/storage/scp.rb +25 -0
  36. data/lib/backup/configuration/storage/sftp.rb +25 -0
  37. data/lib/backup/database/base.rb +33 -0
  38. data/lib/backup/database/mongodb.rb +137 -0
  39. data/lib/backup/database/mysql.rb +104 -0
  40. data/lib/backup/database/postgresql.rb +111 -0
  41. data/lib/backup/database/redis.rb +105 -0
  42. data/lib/backup/encryptor/base.rb +17 -0
  43. data/lib/backup/encryptor/gpg.rb +78 -0
  44. data/lib/backup/encryptor/open_ssl.rb +67 -0
  45. data/lib/backup/finder.rb +39 -0
  46. data/lib/backup/logger.rb +80 -0
  47. data/lib/backup/model.rb +249 -0
  48. data/lib/backup/notifier/base.rb +29 -0
  49. data/lib/backup/notifier/binder.rb +32 -0
  50. data/lib/backup/notifier/mail.rb +141 -0
  51. data/lib/backup/notifier/templates/notify_failure.erb +31 -0
  52. data/lib/backup/notifier/templates/notify_success.erb +16 -0
  53. data/lib/backup/storage/base.rb +60 -3
  54. data/lib/backup/storage/cloudfiles.rb +85 -6
  55. data/lib/backup/storage/dropbox.rb +74 -4
  56. data/lib/backup/storage/ftp.rb +103 -27
  57. data/lib/backup/storage/object.rb +45 -0
  58. data/lib/backup/storage/rsync.rb +100 -0
  59. data/lib/backup/storage/s3.rb +100 -7
  60. data/lib/backup/storage/scp.rb +94 -19
  61. data/lib/backup/storage/sftp.rb +94 -19
  62. data/lib/backup/version.rb +70 -1
  63. data/lib/templates/archive +4 -0
  64. data/lib/templates/compressor/gzip +4 -0
  65. data/lib/templates/database/mongodb +10 -0
  66. data/lib/templates/database/mysql +11 -0
  67. data/lib/templates/database/postgresql +11 -0
  68. data/lib/templates/database/redis +10 -0
  69. data/lib/templates/encryptor/gpg +9 -0
  70. data/lib/templates/encryptor/openssl +5 -0
  71. data/lib/templates/notifier/mail +14 -0
  72. data/lib/templates/readme +15 -0
  73. data/lib/templates/storage/cloudfiles +7 -0
  74. data/lib/templates/storage/dropbox +8 -0
  75. data/lib/templates/storage/ftp +8 -0
  76. data/lib/templates/storage/rsync +7 -0
  77. data/lib/templates/storage/s3 +8 -0
  78. data/lib/templates/storage/scp +8 -0
  79. data/lib/templates/storage/sftp +8 -0
  80. data/spec/archive_spec.rb +53 -0
  81. data/spec/backup_spec.rb +11 -0
  82. data/spec/compressor/gzip_spec.rb +59 -0
  83. data/spec/configuration/base_spec.rb +35 -0
  84. data/spec/configuration/compressor/gzip_spec.rb +28 -0
  85. data/spec/configuration/database/base_spec.rb +16 -0
  86. data/spec/configuration/database/mongodb_spec.rb +30 -0
  87. data/spec/configuration/database/mysql_spec.rb +32 -0
  88. data/spec/configuration/database/postgresql_spec.rb +32 -0
  89. data/spec/configuration/database/redis_spec.rb +30 -0
  90. data/spec/configuration/encryptor/gpg_spec.rb +25 -0
  91. data/spec/configuration/encryptor/open_ssl_spec.rb +31 -0
  92. data/spec/configuration/notifier/mail_spec.rb +32 -0
  93. data/spec/configuration/storage/cloudfiles_spec.rb +34 -0
  94. data/spec/configuration/storage/dropbox_spec.rb +40 -0
  95. data/spec/configuration/storage/ftp_spec.rb +40 -0
  96. data/spec/configuration/storage/rsync_spec.rb +37 -0
  97. data/spec/configuration/storage/s3_spec.rb +37 -0
  98. data/spec/configuration/storage/scp_spec.rb +40 -0
  99. data/spec/configuration/storage/sftp_spec.rb +40 -0
  100. data/spec/database/base_spec.rb +30 -0
  101. data/spec/database/mongodb_spec.rb +144 -0
  102. data/spec/database/mysql_spec.rb +150 -0
  103. data/spec/database/postgresql_spec.rb +164 -0
  104. data/spec/database/redis_spec.rb +122 -0
  105. data/spec/encryptor/gpg_spec.rb +57 -0
  106. data/spec/encryptor/open_ssl_spec.rb +102 -0
  107. data/spec/logger_spec.rb +37 -0
  108. data/spec/model_spec.rb +236 -0
  109. data/spec/notifier/mail_spec.rb +97 -0
  110. data/spec/spec_helper.rb +21 -0
  111. data/spec/storage/base_spec.rb +33 -0
  112. data/spec/storage/cloudfiles_spec.rb +102 -0
  113. data/spec/storage/dropbox_spec.rb +89 -0
  114. data/spec/storage/ftp_spec.rb +133 -0
  115. data/spec/storage/object_spec.rb +74 -0
  116. data/spec/storage/rsync_spec.rb +115 -0
  117. data/spec/storage/s3_spec.rb +110 -0
  118. data/spec/storage/scp_spec.rb +129 -0
  119. data/spec/storage/sftp_spec.rb +125 -0
  120. data/spec/version_spec.rb +32 -0
  121. metadata +139 -123
  122. data/CHANGELOG +0 -131
  123. data/LICENSE +0 -20
  124. data/generators/backup/backup_generator.rb +0 -69
  125. data/generators/backup/templates/backup.rake +0 -56
  126. data/generators/backup/templates/backup.rb +0 -253
  127. data/generators/backup/templates/create_backup_tables.rb +0 -18
  128. data/generators/backup_update/backup_update_generator.rb +0 -50
  129. data/generators/backup_update/templates/migrations/update_backup_tables.rb +0 -27
  130. data/lib/backup/adapters/archive.rb +0 -34
  131. data/lib/backup/adapters/base.rb +0 -167
  132. data/lib/backup/adapters/custom.rb +0 -41
  133. data/lib/backup/adapters/mongo_db.rb +0 -139
  134. data/lib/backup/adapters/mysql.rb +0 -60
  135. data/lib/backup/adapters/postgresql.rb +0 -60
  136. data/lib/backup/adapters/sqlite.rb +0 -25
  137. data/lib/backup/command_helper.rb +0 -14
  138. data/lib/backup/configuration/adapter.rb +0 -21
  139. data/lib/backup/configuration/adapter_options.rb +0 -8
  140. data/lib/backup/configuration/attributes.rb +0 -19
  141. data/lib/backup/configuration/mail.rb +0 -20
  142. data/lib/backup/configuration/smtp.rb +0 -8
  143. data/lib/backup/configuration/storage.rb +0 -8
  144. data/lib/backup/connection/cloudfiles.rb +0 -75
  145. data/lib/backup/connection/dropbox.rb +0 -63
  146. data/lib/backup/connection/s3.rb +0 -88
  147. data/lib/backup/core_ext/object.rb +0 -5
  148. data/lib/backup/environment/base.rb +0 -12
  149. data/lib/backup/environment/rails_configuration.rb +0 -15
  150. data/lib/backup/environment/unix_configuration.rb +0 -109
  151. data/lib/backup/mail/base.rb +0 -97
  152. data/lib/backup/mail/mail.txt +0 -7
  153. data/lib/backup/record/base.rb +0 -65
  154. data/lib/backup/record/cloudfiles.rb +0 -28
  155. data/lib/backup/record/dropbox.rb +0 -27
  156. data/lib/backup/record/ftp.rb +0 -39
  157. data/lib/backup/record/local.rb +0 -26
  158. data/lib/backup/record/s3.rb +0 -25
  159. data/lib/backup/record/scp.rb +0 -33
  160. data/lib/backup/record/sftp.rb +0 -38
  161. data/lib/backup/storage/local.rb +0 -22
  162. data/lib/generators/backup/USAGE +0 -10
  163. data/lib/generators/backup/backup_generator.rb +0 -47
  164. data/lib/generators/backup/templates/backup.rake +0 -56
  165. data/lib/generators/backup/templates/backup.rb +0 -236
  166. data/lib/generators/backup/templates/create_backup_tables.rb +0 -18
  167. data/setup/backup.rb +0 -257
  168. data/setup/backup.sqlite3 +0 -0
@@ -1,38 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Net::FTP library/gem when the Backup::Storage::FTP class is loaded
1
5
  require 'net/ftp'
2
6
 
3
7
  module Backup
4
8
  module Storage
5
- class FTP < Backup::Storage::Base
6
-
7
- attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
8
-
9
- # Stores the backup file on the remote server using FTP
10
- def initialize(adapter)
11
- %w(ip user password path).each do |method|
12
- send("#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
9
+ class FTP < Base
10
+
11
+ ##
12
+ # Server credentials
13
+ attr_accessor :username, :password
14
+
15
+ ##
16
+ # Server IP Address and FTP port
17
+ attr_accessor :ip, :port
18
+
19
+ ##
20
+ # Path to store backups to
21
+ attr_accessor :path
22
+
23
+ ##
24
+ # Creates a new instance of the FTP storage object
25
+ # First it sets the defaults (if any exist) and then evaluates
26
+ # the configuration block which may overwrite these defaults
27
+ def initialize(&block)
28
+ load_defaults!
29
+
30
+ @port ||= 21
31
+ @path ||= 'backups'
32
+
33
+ instance_eval(&block) if block_given?
34
+
35
+ @time = TIME
36
+ @path = path.sub(/^\~\//, '')
37
+ end
38
+
39
+ ##
40
+ # This is the remote path to where the backup files will be stored
41
+ def remote_path
42
+ File.join(path, TRIGGER)
43
+ end
44
+
45
+ ##
46
+ # Performs the backup transfer
47
+ def perform!
48
+ transfer!
49
+ cycle!
50
+ end
51
+
52
+ private
53
+
54
+ ##
55
+ # Establishes a connection to the remote server and returns the Net::FTP object.
56
+ # Not doing any instance variable caching because this object gets persisted in YAML
57
+ # format to a file and will issues. This, however has no impact on performance since it only
58
+ # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
59
+ # background anyway so even if it were a bit slower it shouldn't matter.
60
+ #
61
+ # Note *
62
+ # Since the FTP port is defined as a constant in the Net::FTP class, and might be required
63
+ # to change by the user, we dynamically remove and re-add the constant with the provided port value
64
+ def connection
65
+ if defined? Net::FTP::FTP_PORT
66
+ Net::FTP.send(:remove_const, :FTP_PORT)
67
+ end; Net::FTP.send(:const_set, :FTP_PORT, port)
68
+
69
+ Net::FTP.new(ip, username, password)
70
+ end
71
+
72
+ ##
73
+ # Transfers the archived file to the specified remote server
74
+ def transfer!
75
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
76
+ create_remote_directories!
77
+ connection.put(
78
+ File.join(local_path, local_file),
79
+ File.join(remote_path, remote_file)
80
+ )
81
+ end
82
+
83
+ ##
84
+ # Removes the transferred archive file from the server
85
+ def remove!
86
+ begin
87
+ connection.delete(
88
+ File.join(remote_path, remote_file)
89
+ )
90
+ rescue Net::FTPPermError
91
+ Logger.warn "Could not remove file \"#{ File.join(remote_path, remote_file) }\"."
13
92
  end
14
-
15
- final_file = adapter.final_file
16
- tmp_path = adapter.tmp_path
17
-
18
- Net::FTP.open(ip, user, password) do |ftp|
19
- begin
20
- ftp.chdir(path)
21
- rescue
22
- puts "Could not find or access \"#{path}\" on \"#{ip}\", please ensure this directory exists and is accessible by the user \"#{user}\"."
23
- exit
24
- end
25
-
93
+ end
94
+
95
+ ##
96
+ # Creates (if they don't exist yet) all the directories on the remote
97
+ # server in order to upload the backup file. Net::FTP does not support
98
+ # paths to directories that don't yet exist when creating new directories.
99
+ # Instead, we split the parts up in to an array (for each '/') and loop through
100
+ # that to create the directories one by one. Net::FTP raises an exception when
101
+ # the directory it's trying ot create already exists, so we have rescue it
102
+ def create_remote_directories!
103
+ path_parts = Array.new
104
+ remote_path.split('/').each do |path_part|
105
+ path_parts << path_part
26
106
  begin
27
- puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
28
- ftp.putbinaryfile(File.join(tmp_path, final_file).gsub('\ ', ' '), File.join(path, final_file))
29
- rescue
30
- puts "Could not save file to backup server. Is the \"#{path}\" directory writable?"
31
- exit
32
- end
107
+ connection.mkdir(path_parts.join('/'))
108
+ rescue Net::FTPPermError; end
33
109
  end
34
110
  end
35
-
111
+
36
112
  end
37
113
  end
38
114
  end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Storage
5
+ class Object
6
+
7
+ ##
8
+ # Holds the type attribute
9
+ attr_accessor :storage_file
10
+
11
+ ##
12
+ # Instantiates a new Backup::Storage::Object and stores the
13
+ # full path to the storage file (yaml) in the @storage_file attribute
14
+ def initialize(type)
15
+ @storage_file = File.join(DATA_PATH, TRIGGER, "#{type}.yml")
16
+ end
17
+
18
+ ##
19
+ # Tries to load an existing YAML file and returns an
20
+ # array of storage objects. If no file exists, an empty
21
+ # array gets returned
22
+ #
23
+ # If a file is loaded it'll sort the array of objects by @time
24
+ # descending. The newest backup storage object comes in Backup::Storage::Object.load[0]
25
+ # and the oldest in Backup::Storage::Object.load[-1]
26
+ def load
27
+ if File.exist?(storage_file)
28
+ YAML.load_file(storage_file).sort { |a,b| b.time <=> a.time }
29
+ else
30
+ []
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Takes the provided objects and converts it to YAML format.
36
+ # The YAML data gets written to the storage file
37
+ def write(objects)
38
+ File.open(storage_file, 'w') do |file|
39
+ file.write(objects.to_yaml)
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Net::SSH library when the Backup::Storage::RSync class is loaded
5
+ require 'net/ssh'
6
+
7
+ module Backup
8
+ module Storage
9
+ class RSync < Base
10
+ include Backup::CLI
11
+
12
+ ##
13
+ # Server credentials
14
+ attr_accessor :username, :password
15
+
16
+ ##
17
+ # Server IP Address and SSH port
18
+ attr_accessor :ip, :port
19
+
20
+ ##
21
+ # Path to store backups to
22
+ attr_accessor :path
23
+
24
+ ##
25
+ # Creates a new instance of the RSync storage object
26
+ # First it sets the defaults (if any exist) and then evaluates
27
+ # the configuration block which may overwrite these defaults
28
+ def initialize(&block)
29
+ load_defaults!
30
+
31
+ @port ||= 22
32
+ @path ||= 'backups'
33
+
34
+ instance_eval(&block) if block_given?
35
+
36
+ @time = TIME
37
+ @path = path.sub(/^\~\//, '')
38
+ end
39
+
40
+ ##
41
+ # This is the remote path to where the backup files will be stored
42
+ def remote_path
43
+ File.join(path, TRIGGER)
44
+ end
45
+
46
+ ##
47
+ # Performs the backup transfer
48
+ def perform!
49
+ transfer!
50
+ end
51
+
52
+ private
53
+
54
+ ##
55
+ # Establishes a connection to the remote server and returns the Net::SSH object.
56
+ # Not doing any instance variable caching because this object gets persisted in YAML
57
+ # format to a file and will issues. This, however has no impact on performance since it only
58
+ # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
59
+ # background anyway so even if it were a bit slower it shouldn't matter.
60
+ def connection
61
+ Net::SSH.start(ip, username, :password => password, :port => port)
62
+ end
63
+
64
+ ##
65
+ # Transfers the archived file to the specified remote server
66
+ def transfer!
67
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
68
+ create_remote_directories!
69
+ run("#{ utility(:rsync) } #{ options } '#{ File.join(local_path, local_file) }' '#{ username }@#{ ip }:#{ File.join(remote_path, remote_file[20..-1]) }'")
70
+ end
71
+
72
+ ##
73
+ # Removes the transferred archive file from the server
74
+ def remove!
75
+ response = connection.exec!("rm #{ File.join(remote_path, remote_file) }")
76
+ if response =~ /No such file or directory/
77
+ Logger.warn "Could not remove file \"#{ File.join(remote_path, remote_file) }\"."
78
+ end
79
+ end
80
+
81
+ ##
82
+ # Creates (if they don't exist yet) all the directories on the remote
83
+ # server in order to upload the backup file.
84
+ def create_remote_directories!
85
+ connection.exec!("mkdir -p '#{ remote_path }'")
86
+ end
87
+
88
+ ##
89
+ # RSync options
90
+ # -z = Compresses the bytes that will be transferred to reduce bandwidth usage
91
+ # -e = Allow the usage of SSH remote shell
92
+ # --port = the port to connect to through SSH
93
+ # -Phv = debug options
94
+ def options
95
+ "-z -e ssh --port='#{ port }'"
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -1,15 +1,108 @@
1
- require 'backup/connection/s3'
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Fog gem when the Backup::Storage::S3 class is loaded
5
+ require 'fog'
2
6
 
3
7
  module Backup
4
8
  module Storage
5
9
  class S3 < Base
6
-
7
- # Stores the backup file on the remote server using S3
8
- def initialize(adapter)
9
- s3 = Backup::Connection::S3.new(adapter)
10
- s3.store
10
+
11
+ ##
12
+ # Amazon Simple Storage Service (S3) Credentials
13
+ attr_accessor :access_key_id, :secret_access_key
14
+
15
+ ##
16
+ # Amazon S3 bucket name and path
17
+ attr_accessor :bucket, :path
18
+
19
+ ##
20
+ # Region of the specified S3 bucket
21
+ attr_accessor :region
22
+
23
+ ##
24
+ # Creates a new instance of the Amazon S3 storage object
25
+ # First it sets the defaults (if any exist) and then evaluates
26
+ # the configuration block which may overwrite these defaults
27
+ #
28
+ # Currently available regions:
29
+ # eu-west-1, us-east-1, ap-southeast-1, us-west-1
30
+ def initialize(&block)
31
+ load_defaults!
32
+
33
+ @path ||= 'backups'
34
+
35
+ instance_eval(&block) if block_given?
36
+
37
+ @path = path.sub(/^\//, '')
38
+ @time = TIME
39
+ end
40
+
41
+ ##
42
+ # This is the remote path to where the backup files will be stored
43
+ def remote_path
44
+ File.join(path, TRIGGER, '/')
45
+ end
46
+
47
+ ##
48
+ # This is the provider that Fog uses for the S3 Storage
49
+ def provider
50
+ 'AWS'
51
+ end
52
+
53
+ ##
54
+ # Performs the backup transfer
55
+ def perform!
56
+ transfer!
57
+ cycle!
58
+ end
59
+
60
+ private
61
+
62
+ ##
63
+ # Establishes a connection to Amazon S3 and returns the Fog object.
64
+ # Not doing any instance variable caching because this object gets persisted in YAML
65
+ # format to a file and will issues. This, however has no impact on performance since it only
66
+ # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
67
+ # background anyway so even if it were a bit slower it shouldn't matter.
68
+ def connection
69
+ Fog::Storage.new(
70
+ :provider => provider,
71
+ :aws_access_key_id => access_key_id,
72
+ :aws_secret_access_key => secret_access_key,
73
+ :region => region
74
+ )
11
75
  end
12
-
76
+
77
+ ##
78
+ # Transfers the archived file to the specified Amazon S3 bucket
79
+ def transfer!
80
+ begin
81
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
82
+ connection.sync_clock
83
+ connection.put_object(
84
+ bucket,
85
+ File.join(remote_path, remote_file),
86
+ File.read(File.join(local_path, local_file))
87
+ )
88
+ rescue Excon::Errors::SocketError
89
+ puts "\nAn error occurred while trying to transfer the backup."
90
+ puts "Make sure the bucket exists, and that you specified the correct bucket region.\n\n"
91
+ puts "The available regions are:\n\n"
92
+ puts %w[eu-west-1 us-east-1 ap-southeast-1 us-west-1].map{ |region| "\s\s* #{region}" }.join("\n")
93
+ exit
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Removes the transferred archive file from the Amazon S3 bucket
99
+ def remove!
100
+ begin
101
+ connection.sync_clock
102
+ connection.delete_object(bucket, File.join(remote_path, remote_file))
103
+ rescue Excon::Errors::SocketError; end
104
+ end
105
+
13
106
  end
14
107
  end
15
108
  end
@@ -1,30 +1,105 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Net::SSH and Net::SCP library/gems
5
+ # when the Backup::Storage::SCP class is loaded
6
+ require 'net/ssh'
1
7
  require 'net/scp'
2
8
 
3
9
  module Backup
4
10
  module Storage
5
11
  class SCP < Base
6
-
7
- attr_accessor :user, :password, :ip, :path, :tmp_path, :final_file
8
-
9
- # Stores the backup file on the remote server using SCP
10
- def initialize(adapter)
11
- %w(ip user password path).each do |method|
12
- send("#{method}=", adapter.procedure.get_storage_configuration.attributes[method])
13
- end
14
-
15
- final_file = adapter.final_file
16
- tmp_path = adapter.tmp_path
17
-
18
- Net::SSH.start(ip, user, :password => password) do |ssh|
19
- ssh.exec "mkdir -p #{path}"
12
+
13
+ ##
14
+ # Server credentials
15
+ attr_accessor :username, :password
16
+
17
+ ##
18
+ # Server IP Address and SCP port
19
+ attr_accessor :ip, :port
20
+
21
+ ##
22
+ # Path to store backups to
23
+ attr_accessor :path
24
+
25
+ ##
26
+ # Creates a new instance of the SCP storage object
27
+ # First it sets the defaults (if any exist) and then evaluates
28
+ # the configuration block which may overwrite these defaults
29
+ def initialize(&block)
30
+ load_defaults!
31
+
32
+ @port ||= 22
33
+ @path ||= 'backups'
34
+
35
+ instance_eval(&block) if block_given?
36
+
37
+ @time = TIME
38
+ @path = path.sub(/^\~\//, '')
39
+ end
40
+
41
+ ##
42
+ # This is the remote path to where the backup files will be stored
43
+ def remote_path
44
+ File.join(path, TRIGGER)
45
+ end
46
+
47
+ ##
48
+ # Performs the backup transfer
49
+ def perform!
50
+ transfer!
51
+ cycle!
52
+ end
53
+
54
+ private
55
+
56
+ ##
57
+ # Establishes a connection to the remote server and returns the Net::SCP object.
58
+ # Not doing any instance variable caching because this object gets persisted in YAML
59
+ # format to a file and will issues. This, however has no impact on performance since it only
60
+ # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
61
+ # background anyway so even if it were a bit slower it shouldn't matter.
62
+ #
63
+ # We will be using Net::SSH, and use Net::SCP through Net::SSH to transfer backups
64
+ def connection
65
+ Net::SSH.start(ip, username, :password => password, :port => port)
66
+ end
67
+
68
+ ##
69
+ # Transfers the archived file to the specified remote server
70
+ def transfer!
71
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
72
+ create_remote_directories!
73
+ connection.scp.upload!(
74
+ File.join(local_path, local_file),
75
+ File.join(remote_path, remote_file)
76
+ )
77
+ end
78
+
79
+ ##
80
+ # Removes the transferred archive file from the server
81
+ def remove!
82
+ response = connection.exec!("rm #{ File.join(remote_path, remote_file) }")
83
+ if response =~ /No such file or directory/
84
+ Logger.warn "Could not remove file \"#{ File.join(remote_path, remote_file) }\"."
20
85
  end
21
-
22
- puts "Storing \"#{final_file}\" to path \"#{path}\" on remote server (#{ip})."
23
- Net::SCP.start(ip, user, :password => password) do |scp|
24
- scp.upload! File.join(tmp_path, final_file).gsub('\ ', ' '), path
86
+ end
87
+
88
+ ##
89
+ # Creates (if they don't exist yet) all the directories on the remote
90
+ # server in order to upload the backup file. Net::SCP does not support
91
+ # paths to directories that don't yet exist when creating new directories.
92
+ # Instead, we split the parts up in to an array (for each '/') and loop through
93
+ # that to create the directories one by one. Net::SCP raises an exception when
94
+ # the directory it's trying ot create already exists, so we have rescue it
95
+ def create_remote_directories!
96
+ path_parts = Array.new
97
+ remote_path.split('/').each do |path_part|
98
+ path_parts << path_part
99
+ connection.exec!("mkdir '#{ path_parts.join('/') }'")
25
100
  end
26
101
  end
27
-
102
+
28
103
  end
29
104
  end
30
105
  end