backup 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -5
- data/lib/backup/archive.rb +11 -1
- data/lib/backup/cli.rb +252 -141
- data/lib/backup/database/base.rb +50 -24
- data/lib/backup/database/mongodb.rb +101 -127
- data/lib/backup/database/mysql.rb +50 -76
- data/lib/backup/database/postgresql.rb +44 -70
- data/lib/backup/database/redis.rb +61 -66
- data/lib/backup/database/riak.rb +64 -43
- data/lib/backup/dependency.rb +1 -1
- data/lib/backup/model.rb +10 -7
- data/lib/backup/storage/cloudfiles.rb +2 -0
- data/lib/backup/storage/cycler.rb +2 -1
- data/lib/backup/storage/dropbox.rb +66 -5
- data/lib/backup/utilities.rb +29 -14
- data/lib/backup/version.rb +1 -1
- data/templates/cli/archive +2 -0
- data/templates/cli/database/mongodb +1 -4
- data/templates/cli/database/mysql +0 -3
- data/templates/cli/database/postgresql +0 -3
- data/templates/cli/database/redis +11 -9
- data/templates/cli/database/riak +11 -14
- metadata +2 -2
@@ -28,33 +28,16 @@ module Backup
|
|
28
28
|
# Additional "pg_dump" options
|
29
29
|
attr_accessor :additional_options
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
attr_accessor :pg_dump_utility
|
34
|
-
|
35
|
-
attr_deprecate :utility_path, :version => '3.0.21',
|
36
|
-
:message => 'Use PostgreSQL#pg_dump_utility instead.',
|
37
|
-
:action => lambda {|klass, val| klass.pg_dump_utility = val }
|
38
|
-
|
39
|
-
##
|
40
|
-
# Creates a new instance of the PostgreSQL adapter object
|
41
|
-
# Sets the PGPASSWORD environment variable to the password
|
42
|
-
# so it doesn't prompt and hang in the process
|
43
|
-
def initialize(model, &block)
|
44
|
-
super(model)
|
45
|
-
|
46
|
-
@skip_tables ||= Array.new
|
47
|
-
@only_tables ||= Array.new
|
48
|
-
@additional_options ||= Array.new
|
49
|
-
|
31
|
+
def initialize(model, database_id = nil, &block)
|
32
|
+
super
|
50
33
|
instance_eval(&block) if block_given?
|
51
|
-
|
52
|
-
@pg_dump_utility ||= utility(:pg_dump)
|
53
34
|
end
|
54
35
|
|
55
36
|
##
|
56
|
-
# Performs the
|
57
|
-
#
|
37
|
+
# Performs the mysqldump command and outputs the dump file
|
38
|
+
# in the +dump_path+ using +dump_filename+.
|
39
|
+
#
|
40
|
+
# <trigger>/databases/PostgreSQL[-<database_id>].sql[.gz]
|
58
41
|
def perform!
|
59
42
|
super
|
60
43
|
|
@@ -62,84 +45,75 @@ module Backup
|
|
62
45
|
dump_ext = 'sql'
|
63
46
|
|
64
47
|
pipeline << pgdump
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
48
|
+
|
49
|
+
model.compressor.compress_with do |command, ext|
|
50
|
+
pipeline << command
|
51
|
+
dump_ext << ext
|
52
|
+
end if model.compressor
|
71
53
|
|
72
54
|
pipeline << "#{ utility(:cat) } > " +
|
73
|
-
"'#{ File.join(
|
55
|
+
"'#{ File.join(dump_path, dump_filename) }.#{ dump_ext }'"
|
56
|
+
|
74
57
|
pipeline.run
|
75
58
|
if pipeline.success?
|
76
|
-
|
59
|
+
log!(:finished)
|
77
60
|
else
|
78
61
|
raise Errors::Database::PipelineError,
|
79
|
-
"#{ database_name } Dump Failed!\n" +
|
80
|
-
pipeline.error_messages
|
62
|
+
"#{ database_name } Dump Failed!\n" + pipeline.error_messages
|
81
63
|
end
|
82
64
|
end
|
83
65
|
|
84
|
-
##
|
85
|
-
# Builds the full pgdump string based on all attributes
|
86
66
|
def pgdump
|
87
|
-
"#{
|
88
|
-
"#{
|
67
|
+
"#{ password_option }" +
|
68
|
+
"#{ utility(:pg_dump) } #{ username_option } #{ connectivity_options } " +
|
89
69
|
"#{ user_options } #{ tables_to_dump } #{ tables_to_skip } #{ name }"
|
90
70
|
end
|
91
71
|
|
92
|
-
|
93
|
-
|
94
|
-
# to perform database dumping
|
95
|
-
def password_options
|
96
|
-
password.to_s.empty? ? '' : "PGPASSWORD='#{password}' "
|
72
|
+
def password_option
|
73
|
+
"PGPASSWORD='#{ password }' " if password
|
97
74
|
end
|
98
75
|
|
99
|
-
|
100
|
-
|
101
|
-
# to perform the database dumping process
|
102
|
-
def username_options
|
103
|
-
username.to_s.empty? ? '' : "--username='#{username}'"
|
76
|
+
def username_option
|
77
|
+
"--username='#{ username }'" if username
|
104
78
|
end
|
105
79
|
|
106
|
-
##
|
107
|
-
# Builds the PostgreSQL connectivity options syntax to connect the user
|
108
|
-
# to perform the database dumping process, socket gets gsub'd to host since
|
109
|
-
# that's the option PostgreSQL takes for socket connections as well. In case
|
110
|
-
# both the host and the socket are specified, the socket will take priority over the host
|
111
80
|
def connectivity_options
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
81
|
+
return "--host='#{ socket }'" if socket
|
82
|
+
|
83
|
+
opts = []
|
84
|
+
opts << "--host='#{ host }'" if host
|
85
|
+
opts << "--port='#{ port }'" if port
|
86
|
+
opts.join(' ')
|
116
87
|
end
|
117
88
|
|
118
|
-
##
|
119
|
-
# Builds a PostgreSQL compatible string for the additional options
|
120
|
-
# specified by the user
|
121
89
|
def user_options
|
122
|
-
additional_options.join(' ')
|
90
|
+
Array(additional_options).join(' ')
|
123
91
|
end
|
124
92
|
|
125
|
-
##
|
126
|
-
# Builds the PostgreSQL syntax for specifying which tables to dump
|
127
|
-
# during the dumping of the database
|
128
93
|
def tables_to_dump
|
129
|
-
only_tables.map do |table|
|
130
|
-
"--table='#{table}'"
|
94
|
+
Array(only_tables).map do |table|
|
95
|
+
"--table='#{ table }'"
|
131
96
|
end.join(' ')
|
132
97
|
end
|
133
98
|
|
134
|
-
##
|
135
|
-
# Builds the PostgreSQL syntax for specifying which tables to skip
|
136
|
-
# during the dumping of the database
|
137
99
|
def tables_to_skip
|
138
|
-
skip_tables.map do |table|
|
139
|
-
"--exclude-table='#{table}'"
|
100
|
+
Array(skip_tables).map do |table|
|
101
|
+
"--exclude-table='#{ table }'"
|
140
102
|
end.join(' ')
|
141
103
|
end
|
142
104
|
|
105
|
+
attr_deprecate :utility_path, :version => '3.0.21',
|
106
|
+
:message => 'Use Backup::Utilities.configure instead.',
|
107
|
+
:action => lambda {|klass, val|
|
108
|
+
Utilities.configure { pg_dump val }
|
109
|
+
}
|
110
|
+
|
111
|
+
attr_deprecate :pg_dump_utility, :version => '3.3.0',
|
112
|
+
:message => 'Use Backup::Utilities.configure instead.',
|
113
|
+
:action => lambda {|klass, val|
|
114
|
+
Utilities.configure { pg_dump val }
|
115
|
+
}
|
116
|
+
|
143
117
|
end
|
144
118
|
end
|
145
119
|
end
|
@@ -5,85 +5,76 @@ module Backup
|
|
5
5
|
class Redis < Base
|
6
6
|
|
7
7
|
##
|
8
|
-
# Name of
|
9
|
-
|
8
|
+
# Name of the redis dump file.
|
9
|
+
#
|
10
|
+
# This is set in `redis.conf` as `dbfilename`.
|
11
|
+
# This must be set to the name of that file without the `.rdb` extension.
|
12
|
+
# Default: 'dump'
|
13
|
+
attr_accessor :name
|
10
14
|
|
11
15
|
##
|
12
|
-
#
|
16
|
+
# Path to the redis dump file.
|
17
|
+
#
|
18
|
+
# This is set in `redis.conf` as `dir`.
|
19
|
+
attr_accessor :path
|
20
|
+
|
21
|
+
##
|
22
|
+
# Password for the redis-cli utility to perform the `SAVE` command
|
23
|
+
# if +invoke_save+ is set `true`.
|
13
24
|
attr_accessor :password
|
14
25
|
|
15
26
|
##
|
16
|
-
# Connectivity options
|
27
|
+
# Connectivity options for the +invoke_save+ option.
|
17
28
|
attr_accessor :host, :port, :socket
|
18
29
|
|
19
30
|
##
|
20
|
-
# Determines whether Backup should invoke the SAVE command through
|
21
|
-
# the
|
22
|
-
# copying
|
31
|
+
# Determines whether Backup should invoke the `SAVE` command through
|
32
|
+
# the `redis-cli` utility to persist the most recent data before
|
33
|
+
# copying the dump file specified by +path+ and +name+.
|
23
34
|
attr_accessor :invoke_save
|
24
35
|
|
25
36
|
##
|
26
37
|
# Additional "redis-cli" options
|
27
38
|
attr_accessor :additional_options
|
28
39
|
|
29
|
-
|
30
|
-
|
31
|
-
attr_accessor :redis_cli_utility
|
32
|
-
|
33
|
-
attr_deprecate :utility_path, :version => '3.0.21',
|
34
|
-
:message => 'Use Redis#redis_cli_utility instead.',
|
35
|
-
:action => lambda {|klass, val| klass.redis_cli_utility = val }
|
36
|
-
|
37
|
-
##
|
38
|
-
# Creates a new instance of the Redis database object
|
39
|
-
def initialize(model, &block)
|
40
|
-
super(model)
|
41
|
-
|
42
|
-
@additional_options ||= Array.new
|
43
|
-
|
40
|
+
def initialize(model, database_id = nil, &block)
|
41
|
+
super
|
44
42
|
instance_eval(&block) if block_given?
|
45
43
|
|
46
44
|
@name ||= 'dump'
|
47
|
-
@redis_cli_utility ||= utility('redis-cli')
|
48
45
|
end
|
49
46
|
|
50
47
|
##
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
48
|
+
# Copies and optionally compresses the Redis dump file to the
|
49
|
+
# +dump_path+ using the +dump_filename+.
|
50
|
+
#
|
51
|
+
# <trigger>/databases/Redis[-<database_id>].rdb[.gz]
|
52
|
+
#
|
53
|
+
# If +invoke_save+ is true, `redis-cli SAVE` will be invoked.
|
56
54
|
def perform!
|
57
55
|
super
|
58
56
|
|
59
57
|
invoke_save! if invoke_save
|
60
58
|
copy!
|
59
|
+
|
60
|
+
log!(:finished)
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
64
64
|
|
65
|
-
##
|
66
|
-
# Tells Redis to persist the current state of the
|
67
|
-
# in-memory database to the persisted dump file
|
68
65
|
def invoke_save!
|
69
|
-
|
70
|
-
|
71
|
-
unless response =~ /OK/
|
66
|
+
resp = run(redis_save_cmd)
|
67
|
+
unless resp =~ /OK$/
|
72
68
|
raise Errors::Database::Redis::CommandError, <<-EOS
|
73
69
|
Could not invoke the Redis SAVE command.
|
74
|
-
|
75
|
-
|
76
|
-
and the host/port/socket are correct.
|
77
|
-
|
78
|
-
Redis CLI response: #{ response }
|
70
|
+
Command was: #{ redis_save_cmd }
|
71
|
+
Response was: #{ resp }
|
79
72
|
EOS
|
80
73
|
end
|
81
74
|
end
|
82
75
|
|
83
|
-
##
|
84
|
-
# Performs the copy command to copy over the Redis dump file to the Backup archive
|
85
76
|
def copy!
|
86
|
-
src_path = File.join(path,
|
77
|
+
src_path = File.join(path, name + '.rdb')
|
87
78
|
unless File.exist?(src_path)
|
88
79
|
raise Errors::Database::Redis::NotFoundError, <<-EOS
|
89
80
|
Redis database dump not found
|
@@ -91,46 +82,50 @@ module Backup
|
|
91
82
|
EOS
|
92
83
|
end
|
93
84
|
|
94
|
-
dst_path = File.join(
|
95
|
-
if
|
96
|
-
|
97
|
-
run("#{ command } -c #{ src_path } > #{ dst_path + ext }")
|
85
|
+
dst_path = File.join(dump_path, dump_filename + '.rdb')
|
86
|
+
if model.compressor
|
87
|
+
model.compressor.compress_with do |command, ext|
|
88
|
+
run("#{ command } -c '#{ src_path }' > '#{ dst_path + ext }'")
|
98
89
|
end
|
99
90
|
else
|
100
91
|
FileUtils.cp(src_path, dst_path)
|
101
92
|
end
|
102
93
|
end
|
103
94
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
"#{ name }.rdb"
|
95
|
+
def redis_save_cmd
|
96
|
+
"#{ utility('redis-cli') } #{ password_option } " +
|
97
|
+
"#{ connectivity_options } #{ user_options } SAVE"
|
108
98
|
end
|
109
99
|
|
110
|
-
|
111
|
-
|
112
|
-
# to perform the database dumping process
|
113
|
-
def credential_options
|
114
|
-
password.to_s.empty? ? '' : "-a '#{password}'"
|
100
|
+
def password_option
|
101
|
+
"-a '#{ password }'" if password
|
115
102
|
end
|
116
103
|
|
117
|
-
##
|
118
|
-
# Builds the Redis connectivity options syntax to connect the user
|
119
|
-
# to perform the database dumping process
|
120
104
|
def connectivity_options
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
105
|
+
return "-s '#{ socket }'" if socket
|
106
|
+
|
107
|
+
opts = []
|
108
|
+
opts << "-h '#{ host }'" if host
|
109
|
+
opts << "-p '#{ port }'" if port
|
110
|
+
opts.join(' ')
|
125
111
|
end
|
126
112
|
|
127
|
-
##
|
128
|
-
# Builds a Redis compatible string for the
|
129
|
-
# additional options specified by the user
|
130
113
|
def user_options
|
131
|
-
|
114
|
+
Array(additional_options).join(' ')
|
132
115
|
end
|
133
116
|
|
117
|
+
attr_deprecate :utility_path, :version => '3.0.21',
|
118
|
+
:message => 'Use Backup::Utilities.configure instead.',
|
119
|
+
:action => lambda {|klass, val|
|
120
|
+
Utilities.configure { redis_cli val }
|
121
|
+
}
|
122
|
+
|
123
|
+
attr_deprecate :redis_cli_utility, :version => '3.3.0',
|
124
|
+
:message => 'Use Backup::Utilities.configure instead.',
|
125
|
+
:action => lambda {|klass, val|
|
126
|
+
Utilities.configure { redis_cli val }
|
127
|
+
}
|
128
|
+
|
134
129
|
end
|
135
130
|
end
|
136
131
|
end
|
data/lib/backup/database/riak.rb
CHANGED
@@ -4,77 +4,98 @@ module Backup
|
|
4
4
|
module Database
|
5
5
|
class Riak < Base
|
6
6
|
|
7
|
-
##
|
8
|
-
# Name is the name of the backup
|
9
|
-
attr_accessor :name
|
10
|
-
|
11
7
|
##
|
12
8
|
# Node is the node from which to perform the backup.
|
9
|
+
# Default: riak@127.0.0.1
|
13
10
|
attr_accessor :node
|
14
11
|
|
15
12
|
##
|
16
13
|
# Cookie is the Erlang cookie/shared secret used to connect to the node.
|
14
|
+
# Default: riak
|
17
15
|
attr_accessor :cookie
|
18
16
|
|
19
17
|
##
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
##
|
24
|
-
# Username for the riak instance (optional)
|
18
|
+
# Username for the riak instance
|
19
|
+
# Default: riak
|
25
20
|
attr_accessor :user
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
attr_accessor :group
|
30
|
-
|
31
|
-
attr_deprecate :utility_path, :version => '3.0.21',
|
32
|
-
:message => 'Use Riak#riak_admin_utility instead.',
|
33
|
-
:action => lambda {|klass, val| klass.riak_admin_utility = val }
|
34
|
-
|
35
|
-
##
|
36
|
-
# Creates a new instance of the Riak adapter object
|
37
|
-
def initialize(model, &block)
|
38
|
-
super(model)
|
39
|
-
|
22
|
+
def initialize(model, database_id = nil, &block)
|
23
|
+
super
|
40
24
|
instance_eval(&block) if block_given?
|
41
25
|
|
42
|
-
@
|
43
|
-
@
|
44
|
-
@
|
26
|
+
@node ||= 'riak@127.0.0.1'
|
27
|
+
@cookie ||= 'riak'
|
28
|
+
@user ||= 'riak'
|
45
29
|
end
|
46
30
|
|
47
31
|
##
|
48
|
-
# Performs the `riak-admin
|
49
|
-
# @dump_path based on the `name` and `node`.
|
32
|
+
# Performs the dump using `riak-admin backup`.
|
50
33
|
#
|
51
|
-
#
|
52
|
-
#
|
34
|
+
# This will be stored in the final backup package as
|
35
|
+
# <trigger>/databases/<dump_filename>-<node>[.gz]
|
53
36
|
def perform!
|
54
37
|
super
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
run("#{ riakadmin } #{ backup_file } node")
|
60
|
-
|
61
|
-
if @model.compressor
|
62
|
-
@model.compressor.compress_with do |command, ext|
|
63
|
-
backup_file << "-#{ node }"
|
64
|
-
run("#{ command } -c #{ backup_file } > #{ backup_file + ext }")
|
65
|
-
FileUtils.rm_f(backup_file)
|
66
|
-
end
|
38
|
+
|
39
|
+
dump_file = File.join(dump_path, dump_filename)
|
40
|
+
with_riak_owned_dump_path do
|
41
|
+
run("#{ riakadmin } backup #{ node } #{ cookie } '#{ dump_file }' node")
|
67
42
|
end
|
43
|
+
|
44
|
+
model.compressor.compress_with do |command, ext|
|
45
|
+
dump_file << "-#{ node }" # `riak-admin` appends `node` to the filename.
|
46
|
+
run("#{ command } -c '#{ dump_file }' > '#{ dump_file + ext }'")
|
47
|
+
FileUtils.rm_f(dump_file)
|
48
|
+
end if model.compressor
|
49
|
+
|
50
|
+
log!(:finished)
|
68
51
|
end
|
69
52
|
|
70
53
|
private
|
71
54
|
|
72
55
|
##
|
73
|
-
#
|
56
|
+
# The `riak-admin backup` command is run as the riak +user+,
|
57
|
+
# so +user+ must have write priviledges to the +dump_path+.
|
58
|
+
#
|
59
|
+
# Note that the riak +user+ must also have access to +dump_path+.
|
60
|
+
# This means Backup's +tmp_path+ can not be under the home directory of
|
61
|
+
# the user running Backup, since the absence of the execute bit on their
|
62
|
+
# home directory would deny +user+ access.
|
63
|
+
def with_riak_owned_dump_path
|
64
|
+
run("#{ utility(:sudo) } -n #{ utility(:chown) } " +
|
65
|
+
"#{ user } '#{ dump_path }'")
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
# reclaim ownership
|
69
|
+
run("#{ utility(:sudo) } -n #{ utility(:chown) } -R " +
|
70
|
+
"#{ Config.user } '#{ dump_path }'")
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# `riak-admin` must be run as the riak +user+.
|
75
|
+
# It will do this itself, but without `-n` and emits a message on STDERR.
|
74
76
|
def riakadmin
|
75
|
-
"#{
|
77
|
+
"#{ utility(:sudo) } -n -u #{ user } #{ utility('riak-admin') }"
|
76
78
|
end
|
77
79
|
|
80
|
+
attr_deprecate :utility_path, :version => '3.0.21',
|
81
|
+
:message => 'Use Backup::Utilities.configure instead.',
|
82
|
+
:action => lambda {|klass, val|
|
83
|
+
Utilities.configure { riak_admin val }
|
84
|
+
}
|
85
|
+
|
86
|
+
attr_deprecate :riak_admin_utility, :version => '3.3.0',
|
87
|
+
:message => 'Use Backup::Utilities.configure instead.',
|
88
|
+
:action => lambda {|klass, val|
|
89
|
+
Utilities.configure { riak_admin val }
|
90
|
+
}
|
91
|
+
|
92
|
+
attr_deprecate :name, :version => '3.3.0',
|
93
|
+
:message => "If you wish to add an identifier to the dump filename,\n" +
|
94
|
+
"use a +database_id+ when defining the database in your Model.\n" +
|
95
|
+
"e.g. database Riak, :my_id do |db|"
|
96
|
+
|
97
|
+
attr_deprecate :group, :version => '3.3.0'
|
98
|
+
|
78
99
|
end
|
79
100
|
end
|
80
101
|
end
|