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,191 @@
1
+ module Backup
2
+ module Database
3
+ class MySQL < Base
4
+ class Error < Backup::Error; end
5
+
6
+ ##
7
+ # Name of the database that needs to get dumped
8
+ # To dump all databases, set this to `:all` or leave blank.
9
+ attr_accessor :name
10
+
11
+ ##
12
+ # Credentials for the specified database
13
+ attr_accessor :username, :password
14
+
15
+ ##
16
+ # Connectivity options
17
+ attr_accessor :host, :port, :socket
18
+
19
+ ##
20
+ # Tables to skip while dumping the database
21
+ #
22
+ # If `name` is set to :all (or not specified), these must include
23
+ # a database name. e.g. 'name.table'.
24
+ # If `name` is given, these may simply be table names.
25
+ attr_accessor :skip_tables
26
+
27
+ ##
28
+ # Tables to dump. This in only valid if `name` is specified.
29
+ # If none are given, the entire database will be dumped.
30
+ attr_accessor :only_tables
31
+
32
+ ##
33
+ # Additional "mysqldump" or "innobackupex (backup creation)" options
34
+ attr_accessor :additional_options
35
+
36
+ ##
37
+ # Additional innobackupex log preparation phase ("apply-logs") options
38
+ attr_accessor :prepare_options
39
+
40
+ ##
41
+ # Default is :mysqldump (which is built in MySQL and generates
42
+ # a textual SQL file), but can be changed to :innobackupex, which
43
+ # has more feasible restore times for large databases.
44
+ # See: http://www.percona.com/doc/percona-xtrabackup/
45
+ attr_accessor :backup_engine
46
+
47
+ ##
48
+ # If true (which is the default behaviour), the backup will be prepared
49
+ # after it has been successfuly created. This option is only valid if
50
+ # :backup_engine is set to :innobackupex.
51
+ attr_accessor :prepare_backup
52
+
53
+ ##
54
+ # If set the backup engine command block is executed as the given user
55
+ attr_accessor :sudo_user
56
+
57
+ ##
58
+ # If set, do not suppress innobackupdb output (useful for debugging)
59
+ attr_accessor :verbose
60
+
61
+ def initialize(model, database_id = nil, &block)
62
+ super
63
+ instance_eval(&block) if block_given?
64
+
65
+ @name ||= :all
66
+ @backup_engine ||= :mysqldump
67
+ @prepare_backup = true if @prepare_backup.nil?
68
+ end
69
+
70
+ ##
71
+ # Performs the mysqldump or innobackupex command and outputs
72
+ # the dump file in the +dump_path+ using +dump_filename+.
73
+ #
74
+ # <trigger>/databases/MySQL[-<database_id>].[sql|tar][.gz]
75
+ def perform!
76
+ super
77
+
78
+ pipeline = Pipeline.new
79
+ dump_ext = sql_backup? ? "sql" : "tar"
80
+
81
+ pipeline << sudo_option(sql_backup? ? mysqldump : innobackupex)
82
+
83
+ if model.compressor
84
+ model.compressor.compress_with do |command, ext|
85
+ pipeline << command
86
+ dump_ext << ext
87
+ end
88
+ end
89
+
90
+ pipeline << "#{utility(:cat)} > " \
91
+ "'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
92
+
93
+ pipeline.run
94
+ if pipeline.success?
95
+ log!(:finished)
96
+ else
97
+ raise Error, "Dump Failed!\n#{pipeline.error_messages}"
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def mysqldump
104
+ "#{utility(:mysqldump)} #{user_options} #{credential_options} " \
105
+ "#{connectivity_options} #{name_option} " \
106
+ "#{tables_to_dump} #{tables_to_skip}"
107
+ end
108
+
109
+ def credential_options
110
+ opts = []
111
+ opts << "--user=#{Shellwords.escape(username)}" if username
112
+ opts << "--password=#{Shellwords.escape(password)}" if password
113
+ opts.join(" ")
114
+ end
115
+
116
+ def connectivity_options
117
+ return "--socket='#{socket}'" if socket
118
+
119
+ opts = []
120
+ opts << "--host='#{host}'" if host
121
+ opts << "--port='#{port}'" if port
122
+ opts.join(" ")
123
+ end
124
+
125
+ def user_options
126
+ Array(additional_options).join(" ")
127
+ end
128
+
129
+ def user_prepare_options
130
+ Array(prepare_options).join(" ")
131
+ end
132
+
133
+ def name_option
134
+ dump_all? ? "--all-databases" : name
135
+ end
136
+
137
+ def tables_to_dump
138
+ Array(only_tables).join(" ") unless dump_all?
139
+ end
140
+
141
+ def tables_to_skip
142
+ Array(skip_tables).map do |table|
143
+ table = dump_all? || table["."] ? table : "#{name}.#{table}"
144
+ "--ignore-table='#{table}'"
145
+ end.join(" ")
146
+ end
147
+
148
+ def dump_all?
149
+ name == :all
150
+ end
151
+
152
+ def sql_backup?
153
+ backup_engine.to_sym == :mysqldump
154
+ end
155
+
156
+ def innobackupex
157
+ # Creation phase
158
+ "#{utility(:innobackupex)} #{credential_options} " \
159
+ "#{connectivity_options} #{user_options} " \
160
+ "--no-timestamp #{temp_dir} #{quiet_option} && " +
161
+ innobackupex_prepare +
162
+ # Move files to tar-ed stream on stdout
163
+ "#{utility(:tar)} --remove-files -cf - " \
164
+ "-C #{File.dirname(temp_dir)} #{File.basename(temp_dir)}"
165
+ end
166
+
167
+ def innobackupex_prepare
168
+ return "" unless @prepare_backup
169
+ # Log applying phase (prepare for restore)
170
+ "#{utility(:innobackupex)} --apply-log #{temp_dir} " \
171
+ "#{user_prepare_options} #{quiet_option} && "
172
+ end
173
+
174
+ def sudo_option(command_block)
175
+ return command_block unless sudo_user
176
+
177
+ "sudo -s -u #{sudo_user} -- <<END_OF_SUDO\n" \
178
+ "#{command_block}\n" \
179
+ "END_OF_SUDO\n"
180
+ end
181
+
182
+ def quiet_option
183
+ verbose ? "" : " 2> /dev/null "
184
+ end
185
+
186
+ def temp_dir
187
+ File.join(dump_path, "#{dump_filename}.bkpdir")
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,93 @@
1
+ module Backup
2
+ module Database
3
+ class OpenLDAP < Base
4
+ class Error < Backup::Error; end
5
+
6
+ ##
7
+ # Name of the ldap backup
8
+ attr_accessor :name
9
+
10
+ ##
11
+ # run slapcat under sudo if needed
12
+ # make sure to set SUID on a file, to let you run the file with permissions of file owner
13
+ # eg. sudo chmod u+s /usr/sbin/slapcat
14
+ attr_accessor :use_sudo
15
+
16
+ ##
17
+ # Stores the location of the slapd.conf or slapcat confdir
18
+ attr_accessor :slapcat_conf
19
+
20
+ ##
21
+ # Additional slapcat options
22
+ attr_accessor :slapcat_args
23
+
24
+ ##
25
+ # Path to slapcat utility (optional)
26
+ attr_accessor :slapcat_utility
27
+
28
+ ##
29
+ # Takes the name of the archive and the configuration block
30
+ def initialize(model, database_id = nil, &block)
31
+ super
32
+ instance_eval(&block) if block_given?
33
+
34
+ @name ||= "ldap_backup"
35
+ @use_sudo ||= false
36
+ @slapcat_args ||= []
37
+ @slapcat_utility ||= utility(:slapcat)
38
+ @slapcat_conf ||= "/etc/ldap/slapd.d"
39
+ end
40
+
41
+ ##
42
+ # Performs the slapcat command and outputs the
43
+ # data to the specified path based on the 'trigger'
44
+ def perform!
45
+ super
46
+
47
+ pipeline = Pipeline.new
48
+ dump_ext = "ldif"
49
+
50
+ pipeline << slapcat
51
+ if @model.compressor
52
+ @model.compressor.compress_with do |command, ext|
53
+ pipeline << command
54
+ dump_ext << ext
55
+ end
56
+ end
57
+
58
+ pipeline << "#{utility(:cat)} > " \
59
+ "'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
60
+
61
+ pipeline.run
62
+ if pipeline.success?
63
+ log!(:finished)
64
+ else
65
+ raise Error, "Dump Failed!\n" + pipeline.error_messages
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ ##
72
+ # Builds the full slapcat string based on all attributes
73
+ def slapcat
74
+ command = "#{slapcat_utility} #{slapcat_conf_option} #{slapcat_conf} #{user_options}"
75
+ command.prepend("sudo ") if use_sudo
76
+ command
77
+ end
78
+
79
+ ##
80
+ # Uses different slapcat switch depending on confdir or conffile set
81
+ def slapcat_conf_option
82
+ @slapcat_conf.include?(".d") ? "-F" : "-f"
83
+ end
84
+
85
+ ##
86
+ # Builds a compatible string for the additional options
87
+ # specified by the user
88
+ def user_options
89
+ slapcat_args.join(" ")
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,132 @@
1
+ module Backup
2
+ module Database
3
+ class PostgreSQL < Base
4
+ class Error < Backup::Error; end
5
+
6
+ ##
7
+ # Name of the database that needs to get dumped.
8
+ # To dump all databases, set this to `:all` or leave blank.
9
+ # +username+ must be a PostgreSQL superuser to run `pg_dumpall`.
10
+ attr_accessor :name
11
+
12
+ ##
13
+ # Credentials for the specified database
14
+ attr_accessor :username, :password
15
+
16
+ ##
17
+ # If set the pg_dump(all) command is executed as the given user
18
+ attr_accessor :sudo_user
19
+
20
+ ##
21
+ # Connectivity options
22
+ attr_accessor :host, :port, :socket
23
+
24
+ ##
25
+ # Tables to skip while dumping the database.
26
+ # If `name` is set to :all (or not specified), these are ignored.
27
+ attr_accessor :skip_tables
28
+
29
+ ##
30
+ # Tables to dump. This in only valid if `name` is specified.
31
+ # If none are given, the entire database will be dumped.
32
+ attr_accessor :only_tables
33
+
34
+ ##
35
+ # Additional "pg_dump" or "pg_dumpall" options
36
+ attr_accessor :additional_options
37
+
38
+ def initialize(model, database_id = nil, &block)
39
+ super
40
+ instance_eval(&block) if block_given?
41
+
42
+ @name ||= :all
43
+ end
44
+
45
+ ##
46
+ # Performs the pgdump command and outputs the dump file
47
+ # in the +dump_path+ using +dump_filename+.
48
+ #
49
+ # <trigger>/databases/PostgreSQL[-<database_id>].sql[.gz]
50
+ def perform!
51
+ super
52
+
53
+ pipeline = Pipeline.new
54
+ dump_ext = "sql"
55
+
56
+ pipeline << (dump_all? ? pgdumpall : pgdump)
57
+
58
+ if model.compressor
59
+ model.compressor.compress_with do |command, ext|
60
+ pipeline << command
61
+ dump_ext << ext
62
+ end
63
+ end
64
+
65
+ pipeline << "#{utility(:cat)} > " \
66
+ "'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
67
+
68
+ pipeline.run
69
+ if pipeline.success?
70
+ log!(:finished)
71
+ else
72
+ raise Error, "Dump Failed!\n" + pipeline.error_messages
73
+ end
74
+ end
75
+
76
+ def pgdump
77
+ password_option.to_s +
78
+ sudo_option.to_s +
79
+ "#{utility(:pg_dump)} #{username_option} #{connectivity_options} " \
80
+ "#{user_options} #{tables_to_dump} #{tables_to_skip} #{name}"
81
+ end
82
+
83
+ def pgdumpall
84
+ password_option.to_s +
85
+ sudo_option.to_s +
86
+ "#{utility(:pg_dumpall)} #{username_option} " \
87
+ "#{connectivity_options} #{user_options}"
88
+ end
89
+
90
+ def password_option
91
+ "PGPASSWORD=#{Shellwords.escape(password)} " if password
92
+ end
93
+
94
+ def sudo_option
95
+ "#{utility(:sudo)} -n -H -u #{sudo_user} " if sudo_user
96
+ end
97
+
98
+ def username_option
99
+ "--username=#{Shellwords.escape(username)}" if username
100
+ end
101
+
102
+ def connectivity_options
103
+ return "--host='#{socket}'" if socket
104
+
105
+ opts = []
106
+ opts << "--host='#{host}'" if host
107
+ opts << "--port='#{port}'" if port
108
+ opts.join(" ")
109
+ end
110
+
111
+ def user_options
112
+ Array(additional_options).join(" ")
113
+ end
114
+
115
+ def tables_to_dump
116
+ Array(only_tables).map do |table|
117
+ "--table='#{table}'"
118
+ end.join(" ")
119
+ end
120
+
121
+ def tables_to_skip
122
+ Array(skip_tables).map do |table|
123
+ "--exclude-table='#{table}'"
124
+ end.join(" ")
125
+ end
126
+
127
+ def dump_all?
128
+ name == :all
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,176 @@
1
+ module Backup
2
+ module Database
3
+ class Redis < Base
4
+ class Error < Backup::Error; end
5
+
6
+ MODES = [:copy, :sync]
7
+
8
+ ##
9
+ # Mode of operation.
10
+ #
11
+ # [:copy]
12
+ # Copies the redis dump file specified by {#rdb_path}.
13
+ # This data will be current as of the last RDB Snapshot
14
+ # performed by the server (per your redis.conf settings).
15
+ # You may set {#invoke_save} to +true+ to have Backup issue
16
+ # a +SAVE+ command to update the dump file with the current
17
+ # data before performing the copy.
18
+ #
19
+ # [:sync]
20
+ # Performs a dump of your redis data using +redis-cli --rdb -+.
21
+ # Redis implements this internally using a +SYNC+ command.
22
+ # The operation is analogous to requesting a +BGSAVE+, then having the
23
+ # dump returned. This mode is capable of dumping data from a local or
24
+ # remote server. Requires Redis v2.6 or better.
25
+ #
26
+ # Defaults to +:copy+.
27
+ attr_accessor :mode
28
+
29
+ ##
30
+ # Full path to the redis dump file.
31
+ #
32
+ # Required when {#mode} is +:copy+.
33
+ attr_accessor :rdb_path
34
+
35
+ ##
36
+ # Perform a +SAVE+ command using the +redis-cli+ utility
37
+ # before copying the dump file specified by {#rdb_path}.
38
+ #
39
+ # Only valid when {#mode} is +:copy+.
40
+ attr_accessor :invoke_save
41
+
42
+ ##
43
+ # Connectivity options for the +redis-cli+ utility.
44
+ attr_accessor :host, :port, :socket
45
+
46
+ ##
47
+ # Password for the +redis-cli+ utility.
48
+ attr_accessor :password
49
+
50
+ ##
51
+ # Additional options for the +redis-cli+ utility.
52
+ attr_accessor :additional_options
53
+
54
+ def initialize(model, database_id = nil, &block)
55
+ super
56
+ instance_eval(&block) if block_given?
57
+
58
+ @mode ||= :copy
59
+
60
+ raise Error, "'#{mode}' is not a valid mode" unless MODES.include?(mode)
61
+
62
+ if mode == :copy && rdb_path.nil?
63
+ raise Error, "`rdb_path` must be set when `mode` is :copy"
64
+ end
65
+ end
66
+
67
+ ##
68
+ # Performs the dump based on {#mode} and stores the Redis dump file
69
+ # to the +dump_path+ using the +dump_filename+.
70
+ #
71
+ # <trigger>/databases/Redis[-<database_id>].rdb[.gz]
72
+ def perform!
73
+ super
74
+
75
+ case mode
76
+ when :sync
77
+ # messages output by `redis-cli --rdb` on $stderr
78
+ Logger.configure do
79
+ ignore_warning(/Transfer finished with success/)
80
+ ignore_warning(/SYNC sent to master/)
81
+ end
82
+ sync!
83
+ when :copy
84
+ save! if invoke_save
85
+ copy!
86
+ end
87
+
88
+ log!(:finished)
89
+ end
90
+
91
+ private
92
+
93
+ def sync!
94
+ pipeline = Pipeline.new
95
+ dump_ext = "rdb"
96
+
97
+ pipeline << "#{redis_cli_cmd} --rdb -"
98
+
99
+ if model.compressor
100
+ model.compressor.compress_with do |command, ext|
101
+ pipeline << command
102
+ dump_ext << ext
103
+ end
104
+ end
105
+
106
+ pipeline << "#{utility(:cat)} > " \
107
+ "'#{File.join(dump_path, dump_filename)}.#{dump_ext}'"
108
+
109
+ pipeline.run
110
+
111
+ unless pipeline.success?
112
+ raise Error, "Dump Failed!\n" + pipeline.error_messages
113
+ end
114
+ end
115
+
116
+ def save!
117
+ resp = run("#{redis_cli_cmd} SAVE")
118
+ unless resp =~ /OK$/
119
+ raise Error, <<-EOS
120
+ Failed to invoke the `SAVE` command
121
+ Response was: #{resp}
122
+ EOS
123
+ end
124
+ rescue Error
125
+ if resp =~ /save already in progress/
126
+ unless (attempts ||= "0").next! == "5"
127
+ sleep 5
128
+ retry
129
+ end
130
+ end
131
+ raise
132
+ end
133
+
134
+ def copy!
135
+ unless File.exist?(rdb_path)
136
+ raise Error, <<-EOS
137
+ Redis database dump not found
138
+ `rdb_path` was '#{rdb_path}'
139
+ EOS
140
+ end
141
+
142
+ dst_path = File.join(dump_path, dump_filename + ".rdb")
143
+ if model.compressor
144
+ model.compressor.compress_with do |command, ext|
145
+ run("#{command} -c '#{rdb_path}' > '#{dst_path + ext}'")
146
+ end
147
+ else
148
+ FileUtils.cp(rdb_path, dst_path)
149
+ end
150
+ end
151
+
152
+ def redis_cli_cmd
153
+ "#{utility("redis-cli")} #{password_option} " \
154
+ "#{connectivity_options} #{user_options}"
155
+ end
156
+
157
+ def password_option
158
+ return unless password
159
+ "-a '#{password}'"
160
+ end
161
+
162
+ def connectivity_options
163
+ return "-s '#{socket}'" if socket
164
+
165
+ opts = []
166
+ opts << "-h '#{host}'" if host
167
+ opts << "-p '#{port}'" if port
168
+ opts.join(" ")
169
+ end
170
+
171
+ def user_options
172
+ Array(additional_options).join(" ")
173
+ end
174
+ end
175
+ end
176
+ end