ext_backup 5.0.0.beta.2.1

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. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +33 -0
  4. data/bin/backup +5 -0
  5. data/bin/docker_test +24 -0
  6. data/lib/backup.rb +140 -0
  7. data/lib/backup/archive.rb +169 -0
  8. data/lib/backup/binder.rb +18 -0
  9. data/lib/backup/cleaner.rb +112 -0
  10. data/lib/backup/cli.rb +370 -0
  11. data/lib/backup/cloud_io/base.rb +38 -0
  12. data/lib/backup/cloud_io/cloud_files.rb +296 -0
  13. data/lib/backup/cloud_io/s3.rb +253 -0
  14. data/lib/backup/compressor/base.rb +32 -0
  15. data/lib/backup/compressor/bzip2.rb +35 -0
  16. data/lib/backup/compressor/custom.rb +49 -0
  17. data/lib/backup/compressor/gzip.rb +73 -0
  18. data/lib/backup/config.rb +128 -0
  19. data/lib/backup/config/dsl.rb +102 -0
  20. data/lib/backup/config/helpers.rb +137 -0
  21. data/lib/backup/database/base.rb +86 -0
  22. data/lib/backup/database/mongodb.rb +186 -0
  23. data/lib/backup/database/mysql.rb +191 -0
  24. data/lib/backup/database/openldap.rb +93 -0
  25. data/lib/backup/database/postgresql.rb +132 -0
  26. data/lib/backup/database/redis.rb +176 -0
  27. data/lib/backup/database/riak.rb +79 -0
  28. data/lib/backup/database/sqlite.rb +55 -0
  29. data/lib/backup/encryptor/base.rb +27 -0
  30. data/lib/backup/encryptor/gpg.rb +737 -0
  31. data/lib/backup/encryptor/open_ssl.rb +74 -0
  32. data/lib/backup/errors.rb +53 -0
  33. data/lib/backup/logger.rb +197 -0
  34. data/lib/backup/logger/console.rb +48 -0
  35. data/lib/backup/logger/fog_adapter.rb +25 -0
  36. data/lib/backup/logger/logfile.rb +131 -0
  37. data/lib/backup/logger/syslog.rb +114 -0
  38. data/lib/backup/model.rb +472 -0
  39. data/lib/backup/notifier/base.rb +126 -0
  40. data/lib/backup/notifier/campfire.rb +61 -0
  41. data/lib/backup/notifier/command.rb +99 -0
  42. data/lib/backup/notifier/datadog.rb +104 -0
  43. data/lib/backup/notifier/flowdock.rb +99 -0
  44. data/lib/backup/notifier/hipchat.rb +116 -0
  45. data/lib/backup/notifier/http_post.rb +114 -0
  46. data/lib/backup/notifier/mail.rb +232 -0
  47. data/lib/backup/notifier/nagios.rb +65 -0
  48. data/lib/backup/notifier/pagerduty.rb +79 -0
  49. data/lib/backup/notifier/prowl.rb +68 -0
  50. data/lib/backup/notifier/pushover.rb +71 -0
  51. data/lib/backup/notifier/ses.rb +123 -0
  52. data/lib/backup/notifier/slack.rb +147 -0
  53. data/lib/backup/notifier/twitter.rb +55 -0
  54. data/lib/backup/notifier/zabbix.rb +60 -0
  55. data/lib/backup/package.rb +51 -0
  56. data/lib/backup/packager.rb +106 -0
  57. data/lib/backup/pipeline.rb +120 -0
  58. data/lib/backup/splitter.rb +73 -0
  59. data/lib/backup/storage/base.rb +66 -0
  60. data/lib/backup/storage/cloud_files.rb +156 -0
  61. data/lib/backup/storage/cycler.rb +70 -0
  62. data/lib/backup/storage/dropbox.rb +206 -0
  63. data/lib/backup/storage/ftp.rb +116 -0
  64. data/lib/backup/storage/local.rb +61 -0
  65. data/lib/backup/storage/qiniu.rb +65 -0
  66. data/lib/backup/storage/rsync.rb +246 -0
  67. data/lib/backup/storage/s3.rb +155 -0
  68. data/lib/backup/storage/scp.rb +65 -0
  69. data/lib/backup/storage/sftp.rb +80 -0
  70. data/lib/backup/syncer/base.rb +67 -0
  71. data/lib/backup/syncer/cloud/base.rb +176 -0
  72. data/lib/backup/syncer/cloud/cloud_files.rb +81 -0
  73. data/lib/backup/syncer/cloud/local_file.rb +97 -0
  74. data/lib/backup/syncer/cloud/s3.rb +109 -0
  75. data/lib/backup/syncer/rsync/base.rb +50 -0
  76. data/lib/backup/syncer/rsync/local.rb +27 -0
  77. data/lib/backup/syncer/rsync/pull.rb +47 -0
  78. data/lib/backup/syncer/rsync/push.rb +201 -0
  79. data/lib/backup/template.rb +41 -0
  80. data/lib/backup/utilities.rb +233 -0
  81. data/lib/backup/version.rb +3 -0
  82. data/lib/ext_backup.rb +5 -0
  83. data/lib/ext_backup/version.rb +5 -0
  84. data/templates/cli/archive +28 -0
  85. data/templates/cli/compressor/bzip2 +4 -0
  86. data/templates/cli/compressor/custom +7 -0
  87. data/templates/cli/compressor/gzip +4 -0
  88. data/templates/cli/config +123 -0
  89. data/templates/cli/databases/mongodb +15 -0
  90. data/templates/cli/databases/mysql +18 -0
  91. data/templates/cli/databases/openldap +24 -0
  92. data/templates/cli/databases/postgresql +16 -0
  93. data/templates/cli/databases/redis +16 -0
  94. data/templates/cli/databases/riak +17 -0
  95. data/templates/cli/databases/sqlite +11 -0
  96. data/templates/cli/encryptor/gpg +27 -0
  97. data/templates/cli/encryptor/openssl +9 -0
  98. data/templates/cli/model +26 -0
  99. data/templates/cli/notifier/zabbix +15 -0
  100. data/templates/cli/notifiers/campfire +12 -0
  101. data/templates/cli/notifiers/command +32 -0
  102. data/templates/cli/notifiers/datadog +57 -0
  103. data/templates/cli/notifiers/flowdock +16 -0
  104. data/templates/cli/notifiers/hipchat +16 -0
  105. data/templates/cli/notifiers/http_post +32 -0
  106. data/templates/cli/notifiers/mail +24 -0
  107. data/templates/cli/notifiers/nagios +13 -0
  108. data/templates/cli/notifiers/pagerduty +12 -0
  109. data/templates/cli/notifiers/prowl +11 -0
  110. data/templates/cli/notifiers/pushover +11 -0
  111. data/templates/cli/notifiers/ses +15 -0
  112. data/templates/cli/notifiers/slack +22 -0
  113. data/templates/cli/notifiers/twitter +13 -0
  114. data/templates/cli/splitter +7 -0
  115. data/templates/cli/storages/cloud_files +11 -0
  116. data/templates/cli/storages/dropbox +20 -0
  117. data/templates/cli/storages/ftp +13 -0
  118. data/templates/cli/storages/local +8 -0
  119. data/templates/cli/storages/qiniu +12 -0
  120. data/templates/cli/storages/rsync +17 -0
  121. data/templates/cli/storages/s3 +16 -0
  122. data/templates/cli/storages/scp +15 -0
  123. data/templates/cli/storages/sftp +15 -0
  124. data/templates/cli/syncers/cloud_files +22 -0
  125. data/templates/cli/syncers/rsync_local +20 -0
  126. data/templates/cli/syncers/rsync_pull +28 -0
  127. data/templates/cli/syncers/rsync_push +28 -0
  128. data/templates/cli/syncers/s3 +27 -0
  129. data/templates/general/links +3 -0
  130. data/templates/general/version.erb +2 -0
  131. data/templates/notifier/mail/failure.erb +16 -0
  132. data/templates/notifier/mail/success.erb +16 -0
  133. data/templates/notifier/mail/warning.erb +16 -0
  134. data/templates/storage/dropbox/authorization_url.erb +6 -0
  135. data/templates/storage/dropbox/authorized.erb +4 -0
  136. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  137. metadata +506 -0
@@ -0,0 +1,81 @@
1
+ require "backup/cloud_io/cloud_files"
2
+
3
+ module Backup
4
+ module Syncer
5
+ module Cloud
6
+ class CloudFiles < Base
7
+ class Error < Backup::Error; end
8
+
9
+ ##
10
+ # Rackspace CloudFiles Credentials
11
+ attr_accessor :username, :api_key
12
+
13
+ ##
14
+ # Rackspace CloudFiles Container
15
+ attr_accessor :container
16
+
17
+ ##
18
+ # Rackspace AuthURL (optional)
19
+ attr_accessor :auth_url
20
+
21
+ ##
22
+ # Rackspace Region (optional)
23
+ attr_accessor :region
24
+
25
+ ##
26
+ # Rackspace Service Net
27
+ # (LAN-based transfers to avoid charges and improve performance)
28
+ attr_accessor :servicenet
29
+
30
+ ##
31
+ # Additional options to pass along to fog.
32
+ # e.g. Fog::Storage.new({ :provider => 'Rackspace' }.merge(fog_options))
33
+ attr_accessor :fog_options
34
+
35
+ def initialize(syncer_id = nil)
36
+ super
37
+
38
+ @servicenet ||= false
39
+
40
+ check_configuration
41
+ end
42
+
43
+ private
44
+
45
+ def cloud_io
46
+ @cloud_io ||= CloudIO::CloudFiles.new(
47
+ username: username,
48
+ api_key: api_key,
49
+ auth_url: auth_url,
50
+ region: region,
51
+ servicenet: servicenet,
52
+ container: container,
53
+ max_retries: max_retries,
54
+ retry_waitsec: retry_waitsec,
55
+ # Syncer can not use SLOs.
56
+ segments_container: nil,
57
+ segment_size: 0,
58
+ fog_options: fog_options
59
+ )
60
+ end
61
+
62
+ def get_remote_files(remote_base)
63
+ hash = {}
64
+ cloud_io.objects(remote_base).each do |object|
65
+ relative_path = object.name.sub(remote_base + "/", "")
66
+ hash[relative_path] = object.hash
67
+ end
68
+ hash
69
+ end
70
+
71
+ def check_configuration
72
+ required = %w[username api_key container]
73
+ raise Error, <<-EOS if required.map { |name| send(name) }.any?(&:nil?)
74
+ Configuration Error
75
+ #{required.map { |name| "##{name}" }.join(", ")} are all required
76
+ EOS
77
+ end
78
+ end # class Cloudfiles < Base
79
+ end # module Cloud
80
+ end
81
+ end
@@ -0,0 +1,97 @@
1
+ require "digest/md5"
2
+
3
+ module Backup
4
+ module Syncer
5
+ module Cloud
6
+ class LocalFile
7
+ attr_reader :path
8
+ attr_accessor :md5
9
+
10
+ class << self
11
+ # Returns a Hash of LocalFile objects for each file within +dir+,
12
+ # except those matching any of the +excludes+.
13
+ # Hash keys are the file's path relative to +dir+.
14
+ def find(dir, excludes = [])
15
+ dir = File.expand_path(dir)
16
+ hash = {}
17
+ find_md5(dir, excludes).each do |file|
18
+ hash[file.path.sub(dir + "/", "")] = file
19
+ end
20
+ hash
21
+ end
22
+
23
+ # Return a new LocalFile object if it's valid.
24
+ # Otherwise, log a warning and return nil.
25
+ def new(*args)
26
+ file = super
27
+ if file.invalid?
28
+ Logger.warn("\s\s[skipping] #{file.path}\n" \
29
+ "\s\sPath Contains Invalid UTF-8 byte sequences")
30
+ file = nil
31
+ end
32
+ file
33
+ end
34
+
35
+ private
36
+
37
+ # Returns an Array of file paths and their md5 hashes.
38
+ def find_md5(dir, excludes)
39
+ found = []
40
+ (Dir.entries(dir) - %w[. ..]).map { |e| File.join(dir, e) }.each do |path|
41
+ if File.directory?(path)
42
+ unless exclude?(excludes, path)
43
+ found += find_md5(path, excludes)
44
+ end
45
+ elsif File.file?(path)
46
+ if file = new(path)
47
+ unless exclude?(excludes, file.path)
48
+ file.md5 = Digest::MD5.file(file.path).hexdigest
49
+ found << file
50
+ end
51
+ end
52
+ end
53
+ end
54
+ found
55
+ end
56
+
57
+ # Returns true if +path+ matches any of the +excludes+.
58
+ # Note this can not be called if +path+ includes invalid UTF-8.
59
+ def exclude?(excludes, path)
60
+ excludes.any? do |ex|
61
+ if ex.is_a?(String)
62
+ File.fnmatch?(ex, path)
63
+ elsif ex.is_a?(Regexp)
64
+ ex.match(path)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # If +path+ contains invalid UTF-8, it will be sanitized
71
+ # and the LocalFile object will be flagged as invalid.
72
+ # This is done so @file.path may be logged.
73
+ def initialize(path)
74
+ @path = sanitize(path)
75
+ end
76
+
77
+ def invalid?
78
+ !!@invalid
79
+ end
80
+
81
+ private
82
+
83
+ def sanitize(str)
84
+ str.each_char.map do |char|
85
+ begin
86
+ char.unpack("U")
87
+ char
88
+ rescue
89
+ @invalid = true
90
+ "\xEF\xBF\xBD" # => "\uFFFD"
91
+ end
92
+ end.join
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,109 @@
1
+ require "backup/cloud_io/s3"
2
+
3
+ module Backup
4
+ module Syncer
5
+ module Cloud
6
+ class S3 < Base
7
+ class Error < Backup::Error; end
8
+
9
+ ##
10
+ # Amazon Simple Storage Service (S3) Credentials
11
+ attr_accessor :access_key_id, :secret_access_key, :use_iam_profile
12
+
13
+ ##
14
+ # Amazon S3 bucket name
15
+ attr_accessor :bucket
16
+
17
+ ##
18
+ # Region of the specified S3 bucket
19
+ attr_accessor :region
20
+
21
+ ##
22
+ # Encryption algorithm to use for Amazon Server-Side Encryption
23
+ #
24
+ # Supported values:
25
+ #
26
+ # - :aes256
27
+ #
28
+ # Default: nil
29
+ attr_accessor :encryption
30
+
31
+ ##
32
+ # Storage class to use for the S3 objects uploaded
33
+ #
34
+ # Supported values:
35
+ #
36
+ # - :standard (default)
37
+ # - :reduced_redundancy
38
+ #
39
+ # Default: :standard
40
+ attr_accessor :storage_class
41
+
42
+ ##
43
+ # Additional options to pass along to fog.
44
+ # e.g. Fog::Storage.new({ :provider => 'AWS' }.merge(fog_options))
45
+ attr_accessor :fog_options
46
+
47
+ def initialize(syncer_id = nil)
48
+ super
49
+
50
+ @storage_class ||= :standard
51
+
52
+ check_configuration
53
+ end
54
+
55
+ private
56
+
57
+ def cloud_io
58
+ @cloud_io ||= CloudIO::S3.new(
59
+ access_key_id: access_key_id,
60
+ secret_access_key: secret_access_key,
61
+ use_iam_profile: use_iam_profile,
62
+ bucket: bucket,
63
+ region: region,
64
+ encryption: encryption,
65
+ storage_class: storage_class,
66
+ max_retries: max_retries,
67
+ retry_waitsec: retry_waitsec,
68
+ # Syncer can not use multipart upload.
69
+ chunk_size: 0,
70
+ fog_options: fog_options
71
+ )
72
+ end
73
+
74
+ def get_remote_files(remote_base)
75
+ hash = {}
76
+ cloud_io.objects(remote_base).each do |object|
77
+ relative_path = object.key.sub(remote_base + "/", "")
78
+ hash[relative_path] = object.etag
79
+ end
80
+ hash
81
+ end
82
+
83
+ def check_configuration
84
+ required =
85
+ if use_iam_profile
86
+ %w[bucket]
87
+ else
88
+ %w[access_key_id secret_access_key bucket]
89
+ end
90
+ raise Error, <<-EOS if required.map { |name| send(name) }.any?(&:nil?)
91
+ Configuration Error
92
+ #{required.map { |name| "##{name}" }.join(", ")} are all required
93
+ EOS
94
+
95
+ raise Error, <<-EOS if encryption && encryption.to_s.upcase != "AES256"
96
+ Configuration Error
97
+ #encryption must be :aes256 or nil
98
+ EOS
99
+
100
+ classes = ["STANDARD", "REDUCED_REDUNDANCY"]
101
+ raise Error, <<-EOS unless classes.include?(storage_class.to_s.upcase)
102
+ Configuration Error
103
+ #storage_class must be :standard or :reduced_redundancy
104
+ EOS
105
+ end
106
+ end # Class S3 < Base
107
+ end # module Cloud
108
+ end
109
+ end
@@ -0,0 +1,50 @@
1
+ module Backup
2
+ module Syncer
3
+ module RSync
4
+ class Base < Syncer::Base
5
+ ##
6
+ # Additional String or Array of options for the rsync cli
7
+ attr_accessor :additional_rsync_options
8
+ attr_accessor :archive
9
+
10
+ def initialize(syncer_id = nil, &block)
11
+ super
12
+ instance_eval(&block) if block_given?
13
+
14
+ @path ||= "~/backups"
15
+ @archive = @archive.nil? ? true : @archive
16
+ end
17
+
18
+ private
19
+
20
+ ##
21
+ # Common base command for Local/Push/Pull
22
+ def rsync_command
23
+ utility(:rsync) << archive_option << mirror_option << exclude_option <<
24
+ " #{Array(additional_rsync_options).join(" ")}".rstrip
25
+ end
26
+
27
+ def mirror_option
28
+ mirror ? " --delete" : ""
29
+ end
30
+
31
+ def archive_option
32
+ archive ? " --archive" : ""
33
+ end
34
+
35
+ def exclude_option
36
+ excludes.map { |pattern| " --exclude='#{pattern}'" }.join
37
+ end
38
+
39
+ ##
40
+ # Each path is expanded, since these refer to local paths and are
41
+ # being shell-quoted. This will also remove any trailing `/` from
42
+ # each path, as we don't want rsync's "trailing / on source directories"
43
+ # behavior. This method is used by RSync::Local and RSync::Push.
44
+ def paths_to_push
45
+ directories.map { |dir| "'#{File.expand_path(dir)}'" }.join(" ")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ module Backup
2
+ module Syncer
3
+ module RSync
4
+ class Local < Base
5
+ def perform!
6
+ log!(:started)
7
+
8
+ create_dest_path!
9
+ run("#{rsync_command} #{paths_to_push} '#{dest_path}'")
10
+
11
+ log!(:finished)
12
+ end
13
+
14
+ private
15
+
16
+ # Expand path, since this is local and shell-quoted.
17
+ def dest_path
18
+ @dest_path ||= File.expand_path(path)
19
+ end
20
+
21
+ def create_dest_path!
22
+ FileUtils.mkdir_p dest_path
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ module Backup
2
+ module Syncer
3
+ module RSync
4
+ class Pull < Push
5
+ def perform!
6
+ log!(:started)
7
+ write_password_file!
8
+
9
+ create_dest_path!
10
+ run("#{rsync_command} #{host_options}#{paths_to_pull} " \
11
+ "'#{dest_path}'")
12
+
13
+ log!(:finished)
14
+ ensure
15
+ remove_password_file!
16
+ end
17
+
18
+ private
19
+
20
+ ##
21
+ # Returns the syntax for pulling multiple paths from the remote host.
22
+ # e.g.
23
+ # rsync -a -e "ssh -p 22" host:'path1' :'path2' '/dest'
24
+ # rsync -a rsync_user@host::'modname/path1' ::'modname/path2' '/dest'
25
+ #
26
+ # Remove any preceeding '~/', since these paths are on the remote.
27
+ # Also remove any trailing `/`, since we don't want rsync's
28
+ # "trailing / on source directories" behavior.
29
+ def paths_to_pull
30
+ sep = mode == :ssh ? ":" : "::"
31
+ directories.map do |dir|
32
+ "#{sep}'#{dir.sub(/^~\//, "").sub(/\/$/, "")}'"
33
+ end.join(" ").sub(/^#{ sep }/, "")
34
+ end
35
+
36
+ # Expand path, since this is local and shell-quoted.
37
+ def dest_path
38
+ @dest_path ||= File.expand_path(path)
39
+ end
40
+
41
+ def create_dest_path!
42
+ FileUtils.mkdir_p dest_path
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,201 @@
1
+ module Backup
2
+ module Syncer
3
+ module RSync
4
+ class Push < Base
5
+ ##
6
+ # Mode of operation
7
+ #
8
+ # [:ssh (default)]
9
+ # Connects to the remote via SSH.
10
+ # Does not use an rsync daemon on the remote.
11
+ #
12
+ # [:ssh_daemon]
13
+ # Connects to the remote via SSH.
14
+ # Spawns a single-use daemon on the remote, which allows certain
15
+ # daemon features (like modules) to be used.
16
+ #
17
+ # [:rsync_daemon]
18
+ # Connects directly to an rsync daemon via TCP.
19
+ # Data transferred is not encrypted.
20
+ #
21
+ attr_accessor :mode
22
+
23
+ ##
24
+ # Server Address
25
+ attr_accessor :host
26
+
27
+ ##
28
+ # SSH or RSync port
29
+ #
30
+ # For `:ssh` or `:ssh_daemon` mode, this specifies the SSH port to use
31
+ # and defaults to 22.
32
+ #
33
+ # For `:rsync_daemon` mode, this specifies the TCP port to use
34
+ # and defaults to 873.
35
+ attr_accessor :port
36
+
37
+ ##
38
+ # SSH User
39
+ #
40
+ # If the user running the backup is not the same user that needs to
41
+ # authenticate with the remote server, specify the user here.
42
+ #
43
+ # The user must have SSH keys setup for passphrase-less access to the
44
+ # remote. If the SSH User does not have passphrase-less keys, or no
45
+ # default keys in their `~/.ssh` directory, you will need to use the
46
+ # `-i` option in `:additional_ssh_options` to specify the
47
+ # passphrase-less key to use.
48
+ #
49
+ # Used only for `:ssh` and `:ssh_daemon` modes.
50
+ attr_accessor :ssh_user
51
+
52
+ ##
53
+ # Additional SSH Options
54
+ #
55
+ # Used to supply a String or Array of options to be passed to the SSH
56
+ # command in `:ssh` and `:ssh_daemon` modes.
57
+ #
58
+ # For example, if you need to supply a specific SSH key for the `ssh_user`,
59
+ # you would set this to: "-i '/path/to/id_rsa'". Which would produce:
60
+ #
61
+ # rsync -e "ssh -p 22 -i '/path/to/id_rsa'"
62
+ #
63
+ # Arguments may be single-quoted, but should not contain any double-quotes.
64
+ #
65
+ # Used only for `:ssh` and `:ssh_daemon` modes.
66
+ attr_accessor :additional_ssh_options
67
+
68
+ ##
69
+ # RSync User
70
+ #
71
+ # If the user running the backup is not the same user that needs to
72
+ # authenticate with the rsync daemon, specify the user here.
73
+ #
74
+ # Used only for `:ssh_daemon` and `:rsync_daemon` modes.
75
+ attr_accessor :rsync_user
76
+
77
+ ##
78
+ # RSync Password
79
+ #
80
+ # If specified, Backup will write the password to a temporary file and
81
+ # use it with rsync's `--password-file` option for daemon authentication.
82
+ #
83
+ # Note that setting this will override `rsync_password_file`.
84
+ #
85
+ # Used only for `:ssh_daemon` and `:rsync_daemon` modes.
86
+ attr_accessor :rsync_password
87
+
88
+ ##
89
+ # RSync Password File
90
+ #
91
+ # If specified, this path will be passed to rsync's `--password-file`
92
+ # option for daemon authentication.
93
+ #
94
+ # Used only for `:ssh_daemon` and `:rsync_daemon` modes.
95
+ attr_accessor :rsync_password_file
96
+
97
+ ##
98
+ # Flag for compressing (only compresses for the transfer)
99
+ attr_accessor :compress
100
+
101
+ def initialize(syncer_id = nil)
102
+ super
103
+
104
+ @mode ||= :ssh
105
+ @port ||= mode == :rsync_daemon ? 873 : 22
106
+ @compress ||= false
107
+ end
108
+
109
+ def perform!
110
+ log!(:started)
111
+ write_password_file!
112
+
113
+ create_dest_path!
114
+ run("#{rsync_command} #{paths_to_push} " \
115
+ "#{host_options}'#{dest_path}'")
116
+
117
+ log!(:finished)
118
+ ensure
119
+ remove_password_file!
120
+ end
121
+
122
+ private
123
+
124
+ ##
125
+ # Remove any preceeding '~/', since this is on the remote,
126
+ # and remove any trailing `/`.
127
+ def dest_path
128
+ @dest_path ||= path.sub(/^~\//, "").sub(/\/$/, "")
129
+ end
130
+
131
+ ##
132
+ # Runs a 'mkdir -p' command on the remote to ensure the dest_path exists.
133
+ # This used because rsync will attempt to create the path, but will only
134
+ # call 'mkdir' without the '-p' option. This is only applicable in :ssh
135
+ # mode, and only used if the path would require this.
136
+ def create_dest_path!
137
+ return unless mode == :ssh && dest_path.index("/").to_i > 0
138
+
139
+ run "#{utility(:ssh)} #{ssh_transport_args} #{host} " +
140
+ %("mkdir -p '#{dest_path}'")
141
+ end
142
+
143
+ ##
144
+ # For Push, this will prepend the #dest_path.
145
+ # For Pull, this will prepend the first path in #paths_to_pull.
146
+ def host_options
147
+ if mode == :ssh
148
+ "#{host}:"
149
+ else
150
+ user = "#{rsync_user}@" if rsync_user
151
+ "#{user}#{host}::"
152
+ end
153
+ end
154
+
155
+ ##
156
+ # Common base command, plus options for Push/Pull
157
+ def rsync_command
158
+ super << compress_option << password_option << transport_options
159
+ end
160
+
161
+ def compress_option
162
+ compress ? " --compress" : ""
163
+ end
164
+
165
+ def password_option
166
+ return "" if mode == :ssh
167
+
168
+ path = @password_file ? @password_file.path : rsync_password_file
169
+ path ? " --password-file='#{File.expand_path(path)}'" : ""
170
+ end
171
+
172
+ def transport_options
173
+ if mode == :rsync_daemon
174
+ " --port #{port}"
175
+ else
176
+ %( -e "#{utility(:ssh)} #{ssh_transport_args}")
177
+ end
178
+ end
179
+
180
+ def ssh_transport_args
181
+ args = "-p #{port} "
182
+ args << "-l #{ssh_user} " if ssh_user
183
+ args << Array(additional_ssh_options).join(" ")
184
+ args.rstrip
185
+ end
186
+
187
+ def write_password_file!
188
+ return unless rsync_password && mode != :ssh
189
+
190
+ @password_file = Tempfile.new("backup-rsync-password")
191
+ @password_file.write(rsync_password)
192
+ @password_file.close
193
+ end
194
+
195
+ def remove_password_file!
196
+ @password_file.delete if @password_file
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end