backup 3.2.0 → 3.3.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.
- 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
|