alg-backup 3.0.10

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 (137) hide show
  1. data/.gitignore +2 -0
  2. data/.infinity_test +7 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +25 -0
  5. data/Gemfile.lock +101 -0
  6. data/LICENSE.md +24 -0
  7. data/README.md +276 -0
  8. data/backup.gemspec +39 -0
  9. data/bin/backup +260 -0
  10. data/lib/backup.rb +168 -0
  11. data/lib/backup/archive.rb +73 -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 +15 -0
  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 +54 -0
  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/notifier/twitter.rb +21 -0
  30. data/lib/backup/configuration/storage/base.rb +18 -0
  31. data/lib/backup/configuration/storage/cloudfiles.rb +21 -0
  32. data/lib/backup/configuration/storage/dropbox.rb +29 -0
  33. data/lib/backup/configuration/storage/ftp.rb +25 -0
  34. data/lib/backup/configuration/storage/rsync.rb +25 -0
  35. data/lib/backup/configuration/storage/s3.rb +25 -0
  36. data/lib/backup/configuration/storage/scp.rb +25 -0
  37. data/lib/backup/configuration/storage/sftp.rb +25 -0
  38. data/lib/backup/configuration/syncer/rsync.rb +45 -0
  39. data/lib/backup/configuration/syncer/s3.rb +33 -0
  40. data/lib/backup/database/base.rb +33 -0
  41. data/lib/backup/database/mongodb.rb +137 -0
  42. data/lib/backup/database/mysql.rb +104 -0
  43. data/lib/backup/database/postgresql.rb +111 -0
  44. data/lib/backup/database/redis.rb +105 -0
  45. data/lib/backup/dependency.rb +84 -0
  46. data/lib/backup/encryptor/base.rb +17 -0
  47. data/lib/backup/encryptor/gpg.rb +78 -0
  48. data/lib/backup/encryptor/open_ssl.rb +67 -0
  49. data/lib/backup/finder.rb +39 -0
  50. data/lib/backup/logger.rb +86 -0
  51. data/lib/backup/model.rb +272 -0
  52. data/lib/backup/notifier/base.rb +29 -0
  53. data/lib/backup/notifier/binder.rb +32 -0
  54. data/lib/backup/notifier/mail.rb +141 -0
  55. data/lib/backup/notifier/templates/notify_failure.erb +31 -0
  56. data/lib/backup/notifier/templates/notify_success.erb +16 -0
  57. data/lib/backup/notifier/twitter.rb +87 -0
  58. data/lib/backup/storage/base.rb +67 -0
  59. data/lib/backup/storage/cloudfiles.rb +95 -0
  60. data/lib/backup/storage/dropbox.rb +87 -0
  61. data/lib/backup/storage/ftp.rb +114 -0
  62. data/lib/backup/storage/object.rb +45 -0
  63. data/lib/backup/storage/rsync.rb +99 -0
  64. data/lib/backup/storage/s3.rb +108 -0
  65. data/lib/backup/storage/scp.rb +106 -0
  66. data/lib/backup/storage/sftp.rb +106 -0
  67. data/lib/backup/syncer/base.rb +10 -0
  68. data/lib/backup/syncer/rsync.rb +117 -0
  69. data/lib/backup/syncer/s3.rb +116 -0
  70. data/lib/backup/version.rb +43 -0
  71. data/lib/templates/archive +4 -0
  72. data/lib/templates/compressor/gzip +4 -0
  73. data/lib/templates/database/mongodb +10 -0
  74. data/lib/templates/database/mysql +11 -0
  75. data/lib/templates/database/postgresql +11 -0
  76. data/lib/templates/database/redis +10 -0
  77. data/lib/templates/encryptor/gpg +9 -0
  78. data/lib/templates/encryptor/openssl +5 -0
  79. data/lib/templates/notifier/mail +14 -0
  80. data/lib/templates/notifier/twitter +9 -0
  81. data/lib/templates/readme +15 -0
  82. data/lib/templates/storage/cloudfiles +7 -0
  83. data/lib/templates/storage/dropbox +9 -0
  84. data/lib/templates/storage/ftp +8 -0
  85. data/lib/templates/storage/rsync +7 -0
  86. data/lib/templates/storage/s3 +8 -0
  87. data/lib/templates/storage/scp +8 -0
  88. data/lib/templates/storage/sftp +8 -0
  89. data/lib/templates/syncer/rsync +14 -0
  90. data/lib/templates/syncer/s3 +12 -0
  91. data/spec/archive_spec.rb +90 -0
  92. data/spec/backup_spec.rb +11 -0
  93. data/spec/compressor/gzip_spec.rb +59 -0
  94. data/spec/configuration/base_spec.rb +35 -0
  95. data/spec/configuration/compressor/gzip_spec.rb +28 -0
  96. data/spec/configuration/database/base_spec.rb +16 -0
  97. data/spec/configuration/database/mongodb_spec.rb +30 -0
  98. data/spec/configuration/database/mysql_spec.rb +32 -0
  99. data/spec/configuration/database/postgresql_spec.rb +32 -0
  100. data/spec/configuration/database/redis_spec.rb +30 -0
  101. data/spec/configuration/encryptor/gpg_spec.rb +25 -0
  102. data/spec/configuration/encryptor/open_ssl_spec.rb +31 -0
  103. data/spec/configuration/notifier/mail_spec.rb +32 -0
  104. data/spec/configuration/storage/cloudfiles_spec.rb +34 -0
  105. data/spec/configuration/storage/dropbox_spec.rb +43 -0
  106. data/spec/configuration/storage/ftp_spec.rb +40 -0
  107. data/spec/configuration/storage/rsync_spec.rb +37 -0
  108. data/spec/configuration/storage/s3_spec.rb +37 -0
  109. data/spec/configuration/storage/scp_spec.rb +40 -0
  110. data/spec/configuration/storage/sftp_spec.rb +40 -0
  111. data/spec/configuration/syncer/rsync_spec.rb +46 -0
  112. data/spec/configuration/syncer/s3_spec.rb +43 -0
  113. data/spec/database/base_spec.rb +30 -0
  114. data/spec/database/mongodb_spec.rb +144 -0
  115. data/spec/database/mysql_spec.rb +150 -0
  116. data/spec/database/postgresql_spec.rb +164 -0
  117. data/spec/database/redis_spec.rb +122 -0
  118. data/spec/encryptor/gpg_spec.rb +57 -0
  119. data/spec/encryptor/open_ssl_spec.rb +102 -0
  120. data/spec/logger_spec.rb +46 -0
  121. data/spec/model_spec.rb +236 -0
  122. data/spec/notifier/mail_spec.rb +97 -0
  123. data/spec/notifier/twitter_spec.rb +86 -0
  124. data/spec/spec_helper.rb +21 -0
  125. data/spec/storage/base_spec.rb +33 -0
  126. data/spec/storage/cloudfiles_spec.rb +102 -0
  127. data/spec/storage/dropbox_spec.rb +105 -0
  128. data/spec/storage/ftp_spec.rb +133 -0
  129. data/spec/storage/object_spec.rb +74 -0
  130. data/spec/storage/rsync_spec.rb +115 -0
  131. data/spec/storage/s3_spec.rb +110 -0
  132. data/spec/storage/scp_spec.rb +129 -0
  133. data/spec/storage/sftp_spec.rb +125 -0
  134. data/spec/syncer/rsync_spec.rb +156 -0
  135. data/spec/syncer/s3_spec.rb +139 -0
  136. data/spec/version_spec.rb +21 -0
  137. metadata +217 -0
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Dropbox gem when the Backup::Storage::Dropbox class is loaded
5
+ Backup::Dependency.load('dropbox')
6
+
7
+ module Backup
8
+ module Storage
9
+ class Dropbox < Base
10
+
11
+ ##
12
+ # Dropbox user credentials
13
+ attr_accessor :email, :password
14
+
15
+ ##
16
+ # Dropbox API credentials
17
+ attr_accessor :api_key, :api_secret
18
+
19
+ ##
20
+ # Path to where the backups will be stored
21
+ attr_accessor :path
22
+
23
+ ##
24
+ # Dropbox connection timeout
25
+ attr_accessor :timeout
26
+
27
+ ##
28
+ # Creates a new instance of the Dropbox storage object
29
+ # First it sets the defaults (if any exist) and then evaluates
30
+ # the configuration block which may overwrite these defaults
31
+ def initialize(&block)
32
+ load_defaults!
33
+
34
+ @path ||= 'backups'
35
+
36
+ instance_eval(&block) if block_given?
37
+
38
+ @timeout ||= 300
39
+ @time = TIME
40
+ end
41
+
42
+ ##
43
+ # This is the remote path to where the backup files will be stored
44
+ def remote_path
45
+ File.join(path, TRIGGER)
46
+ end
47
+
48
+ ##
49
+ # Performs the backup transfer
50
+ def perform!
51
+ transfer!
52
+ cycle!
53
+ end
54
+
55
+ private
56
+
57
+ ##
58
+ # Establishes a connection to Dropbox and returns the Dropbox::Session object.
59
+ # Not doing any instance variable caching because this object gets persisted in YAML
60
+ # format to a file and will issues. This, however has no impact on performance since it only
61
+ # gets invoked once per object for a #transfer! and once for a remove! Backups run in the
62
+ # background anyway so even if it were a bit slower it shouldn't matter.
63
+ def connection
64
+ session = ::Dropbox::Session.new(api_key, api_secret)
65
+ session.mode = :dropbox
66
+ session.authorizing_user = email
67
+ session.authorizing_password = password
68
+ session.authorize!
69
+ session
70
+ end
71
+
72
+ ##
73
+ # Transfers the archived file to the specified Dropbox folder
74
+ def transfer!
75
+ Logger.message("#{ self.class } started transferring \"#{ remote_file }\".")
76
+ connection.upload(File.join(local_path, local_file), remote_path, :timeout => timeout)
77
+ end
78
+
79
+ ##
80
+ # Removes the transferred archive file from the Dropbox folder
81
+ def remove!
82
+ connection.delete(File.join(remote_path, remote_file))
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Net::FTP library/gem when the Backup::Storage::FTP class is loaded
5
+ require 'net/ftp'
6
+
7
+ module Backup
8
+ module Storage
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) }\"."
92
+ end
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
106
+ begin
107
+ connection.mkdir(path_parts.join('/'))
108
+ rescue Net::FTPPermError; end
109
+ end
110
+ end
111
+
112
+ end
113
+ end
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,99 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Net::SSH library when the Backup::Storage::RSync class is loaded
5
+ Backup::Dependency.load('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
+ # --port = the port to connect to through SSH
92
+ # -Phv = debug options
93
+ def options
94
+ "-z --port='#{ port }'"
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Only load the Fog gem when the Backup::Storage::S3 class is loaded
5
+ Backup::Dependency.load('fog')
6
+
7
+ module Backup
8
+ module Storage
9
+ class S3 < Base
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
+ )
75
+ end
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.open(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
+
106
+ end
107
+ end
108
+ end