ey_cloud_server 1.3.1 → 1.4.5.pre
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.
- data/bin/binary_log_purge +253 -0
- data/bin/clear_binlogs_from_slave +7 -0
- data/bin/ey-snapshots +6 -1
- data/bin/eybackup +3 -2
- data/lib/ey-flex.rb +11 -3
- data/lib/ey-flex/bucket_minder.rb +91 -16
- data/lib/ey-flex/snapshot_minder.rb +39 -31
- data/lib/ey-flex/version.rb +1 -1
- data/lib/ey_backup.rb +96 -0
- data/lib/ey_backup/backup_set.rb +128 -0
- data/lib/ey_backup/base.rb +18 -0
- data/lib/ey_backup/cli.rb +89 -0
- data/lib/ey_backup/dumper.rb +47 -0
- data/lib/ey_backup/engine.rb +51 -0
- data/lib/ey_backup/engines/mysql_engine.rb +63 -0
- data/lib/ey_backup/engines/postgresql_engine.rb +57 -0
- data/lib/ey_backup/loader.rb +55 -0
- data/lib/ey_backup/logger.rb +36 -0
- data/lib/ey_backup/processors/gpg_encryptor.rb +30 -0
- data/lib/ey_backup/processors/gzipper.rb +53 -0
- data/lib/ey_backup/processors/splitter.rb +96 -0
- data/lib/ey_backup/spawner.rb +54 -0
- data/lib/ey_cloud_server/mysql_start.rb +21 -14
- metadata +71 -39
- data/lib/ey-flex/backups.rb +0 -233
- data/lib/ey-flex/mysql_database.rb +0 -23
- data/lib/ey-flex/postgresql_database.rb +0 -14
@@ -0,0 +1,54 @@
|
|
1
|
+
module EY
|
2
|
+
module Backup
|
3
|
+
module Spawner
|
4
|
+
extend Forwardable
|
5
|
+
extend self
|
6
|
+
|
7
|
+
CHUNK_SIZE = 4096
|
8
|
+
|
9
|
+
def_delegator EY::Backup, :logger
|
10
|
+
|
11
|
+
def spawn(command, stdout = nil, stdin = nil, stderr = logger.stderr)
|
12
|
+
ioify(stdout, stdin, stderr) do |o, i, e|
|
13
|
+
ios = {:stderr => e}
|
14
|
+
ios[:stdout] = o if o
|
15
|
+
ios[:stdin] = i if i
|
16
|
+
result = Open4.spawn([command], ios)
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(command)
|
22
|
+
pid, *_ = Open4.popen4(command)
|
23
|
+
Process::waitpid2(pid)
|
24
|
+
end
|
25
|
+
|
26
|
+
def popen(command, stdout, stdin = nil, stderr = logger.stderr)
|
27
|
+
ioify(stdout, stdin, stderr) do |o, i, e|
|
28
|
+
Open4.popen4(command) do |pid, stdin, stdout, stderr|
|
29
|
+
while chunk = i.read(CHUNK_SIZE)
|
30
|
+
stdin << chunk
|
31
|
+
end
|
32
|
+
|
33
|
+
stdin.flush
|
34
|
+
stdin.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ioify(stdout, stdin, stderr, &block)
|
40
|
+
if String === stdin
|
41
|
+
File.open(stdin, 'r') do |f|
|
42
|
+
ioify(stdout, f, stderr, &block)
|
43
|
+
end
|
44
|
+
elsif String === stdout
|
45
|
+
File.open(stdout, 'w') do |f|
|
46
|
+
ioify(f, stdin, stderr, &block)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
yield stdout, stdin, stderr
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -6,12 +6,13 @@ module EY
|
|
6
6
|
new(options).run
|
7
7
|
end
|
8
8
|
|
9
|
-
attr_accessor :password, :check, :
|
9
|
+
attr_accessor :password, :check, :start_timeout, :stop_timeout
|
10
10
|
|
11
11
|
def initialize(options = {})
|
12
|
-
@password
|
13
|
-
@check
|
14
|
-
@
|
12
|
+
@password = options[:password] || abort("ERROR: You must provide a password.")
|
13
|
+
@check = options[:check] || abort("ERROR: You must a check interval.")
|
14
|
+
@start_timeout = options[:start_timeout] || abort("ERROR: You must provide a start timeout.")
|
15
|
+
@stop_timeout = options[:stop_timeout] || 1200
|
15
16
|
|
16
17
|
abort("ERROR: You must be root.") unless Process.euid == 0
|
17
18
|
end
|
@@ -26,19 +27,17 @@ module EY
|
|
26
27
|
# test to make sure mysql is running
|
27
28
|
if mysql_running()
|
28
29
|
log_mysql_event("MySQL did start and is working through crash recovery")
|
29
|
-
sleeptime = check
|
30
30
|
slept = 0
|
31
|
-
sleeplimit = timeout
|
32
31
|
loop {
|
33
32
|
if system("mysqladmin -p#{password} status") # mysql is accessible
|
34
33
|
log_mysql_event("MySQL completed crash recovery, performing clean restart")
|
35
34
|
system('killall -TERM mysqld') # safe shutdown of mysql
|
36
35
|
termslept = 0
|
37
|
-
termlimit =
|
36
|
+
termlimit = stop_timeout
|
38
37
|
loop {
|
39
38
|
break if not mysql_running()
|
40
|
-
sleep(
|
41
|
-
termslept = termslept +
|
39
|
+
sleep(check)
|
40
|
+
termslept = termslept + check
|
42
41
|
if termslept > termlimit
|
43
42
|
log_mysql_event("MySQL did not shut down cleanly within the time limit, killing")
|
44
43
|
system('killall -9 mysqld')
|
@@ -50,11 +49,11 @@ module EY
|
|
50
49
|
end
|
51
50
|
else
|
52
51
|
log_mysql_event("MySQL has been starting for #{slept/60} minutes and is still attempting to start") if slept % 120 == 0
|
53
|
-
sleep(
|
52
|
+
sleep(check)
|
54
53
|
end
|
55
54
|
break if not mysql_running()
|
56
|
-
slept = slept +
|
57
|
-
if slept >
|
55
|
+
slept = slept + check
|
56
|
+
if slept > start_timeout
|
58
57
|
log_mysql_event("MySQL was not able to start after #{slept/60} minutes and was forcibly killed")
|
59
58
|
system("killall -9 mysqld")
|
60
59
|
end
|
@@ -134,8 +133,16 @@ module EY
|
|
134
133
|
options[:check] = check.to_i
|
135
134
|
end
|
136
135
|
|
137
|
-
opts.on("-t", "--timeout NUMBER", "Maximum wait time in seconds.") do |
|
138
|
-
options[:
|
136
|
+
opts.on("-t", "--timeout NUMBER", "Maximum wait time in seconds. (DEPRECATED)") do |start_timeout|
|
137
|
+
options[:start_timeout] = start_timeout.to_i
|
138
|
+
end
|
139
|
+
|
140
|
+
opts.on("-a", "--start NUMBER", "Startup timeout in seconds.") do |start_timeout|
|
141
|
+
options[:start_timeout] = start_timeout.to_i
|
142
|
+
end
|
143
|
+
|
144
|
+
opts.on("-o", "--stop NUMBER", "Graceful termination timeout in seconds.") do |stop_timeout|
|
145
|
+
options[:stop_timeout] = stop_timeout.to_i
|
139
146
|
end
|
140
147
|
end
|
141
148
|
|
metadata
CHANGED
@@ -1,128 +1,154 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ey_cloud_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 961916000
|
5
|
+
prerelease: true
|
5
6
|
segments:
|
6
7
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
8
|
+
- 4
|
9
|
+
- 5
|
10
|
+
- pre
|
11
|
+
version: 1.4.5.pre
|
10
12
|
platform: ruby
|
11
|
-
authors:
|
12
|
-
|
13
|
+
authors: []
|
14
|
+
|
13
15
|
autorequire:
|
14
16
|
bindir: bin
|
15
17
|
cert_chain: []
|
16
18
|
|
17
|
-
date: 2010-05
|
19
|
+
date: 2010-10-05 00:00:00 -07:00
|
18
20
|
default_executable:
|
19
21
|
dependencies:
|
20
22
|
- !ruby/object:Gem::Dependency
|
21
|
-
name: json
|
22
|
-
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
24
25
|
requirements:
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
27
29
|
segments:
|
28
30
|
- 0
|
29
31
|
version: "0"
|
30
32
|
type: :runtime
|
33
|
+
name: json
|
34
|
+
prerelease: false
|
31
35
|
version_requirements: *id001
|
32
36
|
- !ruby/object:Gem::Dependency
|
33
|
-
name: right_aws
|
34
|
-
prerelease: false
|
35
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
36
39
|
requirements:
|
37
40
|
- - ">="
|
38
41
|
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
39
43
|
segments:
|
40
44
|
- 0
|
41
45
|
version: "0"
|
42
46
|
type: :runtime
|
47
|
+
name: right_aws
|
48
|
+
prerelease: false
|
43
49
|
version_requirements: *id002
|
44
50
|
- !ruby/object:Gem::Dependency
|
45
|
-
name: open4
|
46
|
-
prerelease: false
|
47
51
|
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
48
53
|
requirements:
|
49
54
|
- - "="
|
50
55
|
- !ruby/object:Gem::Version
|
56
|
+
hash: 55
|
51
57
|
segments:
|
52
58
|
- 0
|
53
59
|
- 9
|
54
60
|
- 6
|
55
61
|
version: 0.9.6
|
56
62
|
type: :runtime
|
63
|
+
name: open4
|
64
|
+
prerelease: false
|
57
65
|
version_requirements: *id003
|
58
66
|
- !ruby/object:Gem::Dependency
|
59
|
-
name: aws-s3
|
60
|
-
prerelease: false
|
61
67
|
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
62
69
|
requirements:
|
63
70
|
- - ">="
|
64
71
|
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
65
73
|
segments:
|
66
74
|
- 0
|
67
75
|
version: "0"
|
68
76
|
type: :runtime
|
77
|
+
name: aws-s3
|
78
|
+
prerelease: false
|
69
79
|
version_requirements: *id004
|
70
80
|
- !ruby/object:Gem::Dependency
|
71
|
-
name: rest-client
|
72
|
-
prerelease: false
|
73
81
|
requirement: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
74
83
|
requirements:
|
75
84
|
- - ">="
|
76
85
|
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
77
87
|
segments:
|
78
88
|
- 0
|
79
89
|
version: "0"
|
80
90
|
type: :runtime
|
91
|
+
name: rest-client
|
92
|
+
prerelease: false
|
81
93
|
version_requirements: *id005
|
82
94
|
- !ruby/object:Gem::Dependency
|
83
|
-
name: ey_stonith
|
84
|
-
prerelease: false
|
85
95
|
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
86
97
|
requirements:
|
87
|
-
- -
|
98
|
+
- - ">="
|
88
99
|
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
89
101
|
segments:
|
90
102
|
- 0
|
91
|
-
|
92
|
-
- 0
|
93
|
-
version: 0.3.0
|
103
|
+
version: "0"
|
94
104
|
type: :runtime
|
105
|
+
name: mysql
|
106
|
+
prerelease: false
|
95
107
|
version_requirements: *id006
|
96
|
-
description:
|
108
|
+
description: Server side components for Engine Yard's cloud
|
97
109
|
email:
|
98
|
-
- ninja@engineyard.com
|
99
110
|
executables:
|
100
111
|
- eybackup
|
101
112
|
- ey-snapshots
|
102
113
|
- ey-agent
|
103
114
|
- mysql_start
|
115
|
+
- binary_log_purge
|
116
|
+
- clear_binlogs_from_slave
|
104
117
|
extensions: []
|
105
118
|
|
106
119
|
extra_rdoc_files: []
|
107
120
|
|
108
121
|
files:
|
109
|
-
- lib/ey-flex/backups.rb
|
110
122
|
- lib/ey-flex/big-brother.rb
|
111
123
|
- lib/ey-flex/bucket_minder.rb
|
112
|
-
- lib/ey-flex/ey-api.rb
|
113
|
-
- lib/ey-flex/mysql_database.rb
|
114
|
-
- lib/ey-flex/postgresql_database.rb
|
115
|
-
- lib/ey-flex/snapshot_minder.rb
|
116
124
|
- lib/ey-flex/version.rb
|
117
|
-
- lib/ey-flex.rb
|
125
|
+
- lib/ey-flex/snapshot_minder.rb
|
126
|
+
- lib/ey-flex/ey-api.rb
|
127
|
+
- lib/ey_backup.rb
|
118
128
|
- lib/ey_cloud_server/mysql_start.rb
|
119
129
|
- lib/ey_cloud_server.rb
|
120
|
-
-
|
121
|
-
-
|
130
|
+
- lib/ey-flex.rb
|
131
|
+
- lib/ey_backup/processors/splitter.rb
|
132
|
+
- lib/ey_backup/processors/gzipper.rb
|
133
|
+
- lib/ey_backup/processors/gpg_encryptor.rb
|
134
|
+
- lib/ey_backup/engines/postgresql_engine.rb
|
135
|
+
- lib/ey_backup/engines/mysql_engine.rb
|
136
|
+
- lib/ey_backup/backup_set.rb
|
137
|
+
- lib/ey_backup/loader.rb
|
138
|
+
- lib/ey_backup/engine.rb
|
139
|
+
- lib/ey_backup/logger.rb
|
140
|
+
- lib/ey_backup/cli.rb
|
141
|
+
- lib/ey_backup/dumper.rb
|
142
|
+
- lib/ey_backup/spawner.rb
|
143
|
+
- lib/ey_backup/base.rb
|
144
|
+
- bin/clear_binlogs_from_slave
|
145
|
+
- bin/binary_log_purge
|
122
146
|
- bin/ey-agent
|
123
147
|
- bin/mysql_start
|
148
|
+
- bin/ey-snapshots
|
149
|
+
- bin/eybackup
|
124
150
|
has_rdoc: true
|
125
|
-
homepage:
|
151
|
+
homepage:
|
126
152
|
licenses: []
|
127
153
|
|
128
154
|
post_install_message:
|
@@ -131,23 +157,29 @@ rdoc_options: []
|
|
131
157
|
require_paths:
|
132
158
|
- lib
|
133
159
|
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
134
161
|
requirements:
|
135
162
|
- - ">="
|
136
163
|
- !ruby/object:Gem::Version
|
164
|
+
hash: 3
|
137
165
|
segments:
|
138
166
|
- 0
|
139
167
|
version: "0"
|
140
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
141
170
|
requirements:
|
142
|
-
- - "
|
171
|
+
- - ">"
|
143
172
|
- !ruby/object:Gem::Version
|
173
|
+
hash: 25
|
144
174
|
segments:
|
145
|
-
-
|
146
|
-
|
175
|
+
- 1
|
176
|
+
- 3
|
177
|
+
- 1
|
178
|
+
version: 1.3.1
|
147
179
|
requirements: []
|
148
180
|
|
149
|
-
rubyforge_project:
|
150
|
-
rubygems_version: 1.3.
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 1.3.7
|
151
183
|
signing_key:
|
152
184
|
specification_version: 3
|
153
185
|
summary: Server side components for Engine Yard's cloud
|
data/lib/ey-flex/backups.rb
DELETED
@@ -1,233 +0,0 @@
|
|
1
|
-
module AWS::S3
|
2
|
-
class S3Object
|
3
|
-
def <=>(other)
|
4
|
-
DateTime.parse(self.about['last-modified']) <=> DateTime.parse(other.about['last-modified'])
|
5
|
-
end
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
module EY::Flex
|
10
|
-
class DatabaseEngine
|
11
|
-
def self.register_as(name)
|
12
|
-
EY::Flex::Backups::ENGINES[name] = self
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(backups)
|
16
|
-
@backups = backups
|
17
|
-
end
|
18
|
-
|
19
|
-
def dump_database(name)
|
20
|
-
raise "Implement #dump_database in #{self.class}"
|
21
|
-
end
|
22
|
-
|
23
|
-
def dbuser
|
24
|
-
@backups.config[:dbuser]
|
25
|
-
end
|
26
|
-
|
27
|
-
def dbpass
|
28
|
-
@backups.config[:dbpass]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class Backups
|
33
|
-
class BackupNotFound < EY::Flex::Error; end
|
34
|
-
|
35
|
-
def self.run(args)
|
36
|
-
options = {:command => :new_backup}
|
37
|
-
|
38
|
-
# Build a parser for the command line arguments
|
39
|
-
opts = OptionParser.new do |opts|
|
40
|
-
opts.version = "0.0.1"
|
41
|
-
|
42
|
-
opts.banner = "Usage: eybackup [-flag] [argument]"
|
43
|
-
opts.define_head "eybackup: backing up your shit since way back when..."
|
44
|
-
opts.separator '*'*80
|
45
|
-
|
46
|
-
opts.on("-l", "--list-backup DATABASE", "List mysql backups for DATABASE") do |db|
|
47
|
-
options[:db] = (db || 'all')
|
48
|
-
options[:command] = :list
|
49
|
-
end
|
50
|
-
|
51
|
-
opts.on("-n", "--new-backup", "Create new mysql backup") do
|
52
|
-
options[:command] = :new_backup
|
53
|
-
end
|
54
|
-
|
55
|
-
opts.on("-c", "--config CONFIG", "Use config file.") do |config|
|
56
|
-
options[:config] = config
|
57
|
-
end
|
58
|
-
|
59
|
-
opts.on("-d", "--download BACKUP_INDEX", "download the backup specified by index. Run eybackup -l to get the index.") do |index|
|
60
|
-
options[:command] = :download
|
61
|
-
options[:index] = index
|
62
|
-
end
|
63
|
-
|
64
|
-
opts.on("-e", "--engine DATABASE_ENGINE", "The database engine. ex: mysql, postgres.") do |engine|
|
65
|
-
options[:engine] = engine
|
66
|
-
end
|
67
|
-
|
68
|
-
opts.on("-r", "--restore BACKUP_INDEX", "Download and apply the backup specified by index WARNING! will overwrite the current db with the backup. Run eybackup -l to get the index.") do |index|
|
69
|
-
options[:command] = :restore
|
70
|
-
options[:index] = index
|
71
|
-
end
|
72
|
-
|
73
|
-
opts.on("-q", "--quiet", "Supress output to STDOUT") do
|
74
|
-
options[:quiet] = true
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
opts.parse!(args)
|
80
|
-
|
81
|
-
options[:engine] ||= 'mysql'
|
82
|
-
options[:config] ||= "/etc/.#{options[:engine]}.backups.yml"
|
83
|
-
|
84
|
-
eyb = new(options)
|
85
|
-
|
86
|
-
case options[:command]
|
87
|
-
when :list
|
88
|
-
eyb.list options[:db]
|
89
|
-
when :new_backup
|
90
|
-
eyb.new_backup
|
91
|
-
when :download
|
92
|
-
eyb.download(options[:index])
|
93
|
-
when :restore
|
94
|
-
eyb.restore(options[:index])
|
95
|
-
end
|
96
|
-
eyb.cleanup
|
97
|
-
rescue EY::Flex::Error => e
|
98
|
-
abort e.message
|
99
|
-
end
|
100
|
-
|
101
|
-
ENGINES = {}
|
102
|
-
|
103
|
-
def initialize(options = {})
|
104
|
-
@quiet = options[:quiet]
|
105
|
-
engine_klass = ENGINES[options[:engine]] || raise("Invalid database engine: #{options[:engine].inspect}")
|
106
|
-
@engine = engine_klass.new(self)
|
107
|
-
|
108
|
-
load_config(options[:config])
|
109
|
-
|
110
|
-
AWS::S3::Base.establish_connection!(
|
111
|
-
:access_key_id => config[:aws_secret_id],
|
112
|
-
:secret_access_key => config[:aws_secret_key]
|
113
|
-
)
|
114
|
-
@databases = config[:databases]
|
115
|
-
@keep = config[:keep]
|
116
|
-
@bucket = "ey-backup-#{Digest::SHA1.hexdigest(config[:aws_secret_id])[0..11]}"
|
117
|
-
@tmpname = "#{Time.now.strftime("%Y-%m-%dT%H:%M:%S").gsub(/:/, '-')}.sql.gz"
|
118
|
-
@env = config[:env]
|
119
|
-
FileUtils.mkdir_p '/mnt/backups'
|
120
|
-
FileUtils.mkdir_p '/mnt/tmp'
|
121
|
-
begin
|
122
|
-
AWS::S3::Bucket.find(@bucket)
|
123
|
-
rescue AWS::S3::NoSuchBucket
|
124
|
-
AWS::S3::Bucket.create(@bucket)
|
125
|
-
end
|
126
|
-
|
127
|
-
FileUtils.mkdir_p self.backup_dir
|
128
|
-
end
|
129
|
-
attr_reader :config
|
130
|
-
|
131
|
-
def load_config(filename)
|
132
|
-
if File.exist?(filename)
|
133
|
-
@config = YAML::load(File.read(filename))
|
134
|
-
else
|
135
|
-
abort "You need to have a backup file at #{filename}"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def new_backup
|
140
|
-
@databases.each do |db|
|
141
|
-
backup_database(db)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def say(msg, newline = true)
|
146
|
-
return if @quiet
|
147
|
-
print("#{msg}#{"\n" if newline}")
|
148
|
-
end
|
149
|
-
|
150
|
-
def backup_database(database)
|
151
|
-
File.open("#{self.backup_dir}/#{database}.#{@tmpname}", "w") do |f|
|
152
|
-
say "doing database: #{database}"
|
153
|
-
@engine.dump_database(database, f)
|
154
|
-
end
|
155
|
-
|
156
|
-
File.open("#{self.backup_dir}/#{database}.#{@tmpname}") do |f|
|
157
|
-
path = "#{@env}.#{database}/#{database}.#{@tmpname}"
|
158
|
-
AWS::S3::S3Object.store(path, f, @bucket, :access => :private)
|
159
|
-
say "successful backup: #{database}.#{@tmpname}"
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def download(index)
|
164
|
-
idx, db = index.split(":")
|
165
|
-
raise Error, "You didn't specify a database name: e.g. 1:rails_production" unless db
|
166
|
-
|
167
|
-
if obj = list(db)[idx.to_i]
|
168
|
-
filename = normalize_name(obj)
|
169
|
-
say "downloading: #{filename}"
|
170
|
-
File.open(filename, 'wb') do |f|
|
171
|
-
say ".", false
|
172
|
-
obj.value {|chunk| f.write chunk }
|
173
|
-
end
|
174
|
-
say "finished"
|
175
|
-
[db, filename]
|
176
|
-
else
|
177
|
-
raise BackupNotFound, "No backup found for database #{db.inspect}: requested index: #{idx}"
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def restore(index)
|
182
|
-
db, filename = download(index)
|
183
|
-
File.open(filename) do |f|
|
184
|
-
@engine.restore_database(db, f)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
def cleanup
|
189
|
-
begin
|
190
|
-
list('all')[0...-(@keep*@databases.size)].each do |o|
|
191
|
-
say "deleting: #{o.key}"
|
192
|
-
o.delete
|
193
|
-
end
|
194
|
-
rescue AWS::S3::S3Exception, AWS::S3::Error
|
195
|
-
nil # see bucket_minder cleanup note regarding S3 consistency
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def normalize_name(obj)
|
200
|
-
obj.key.gsub(/^.*?\//, '')
|
201
|
-
end
|
202
|
-
|
203
|
-
def find_obj(name)
|
204
|
-
AWS::S3::S3Object.find name, @bucket
|
205
|
-
end
|
206
|
-
|
207
|
-
def list(database='all')
|
208
|
-
say "Listing database backups for #{database}"
|
209
|
-
backups = []
|
210
|
-
if database == 'all'
|
211
|
-
@databases.each do |db|
|
212
|
-
backups << AWS::S3::Bucket.objects(@bucket, :prefix => "#{@env}.#{db}")
|
213
|
-
end
|
214
|
-
backups = backups.flatten.sort
|
215
|
-
else
|
216
|
-
backups = AWS::S3::Bucket.objects(@bucket, :prefix => "#{@env}.#{database}").sort
|
217
|
-
end
|
218
|
-
|
219
|
-
say "#{backups.size} backup(s) found"
|
220
|
-
|
221
|
-
backups.each_with_index do |b,i|
|
222
|
-
say "#{i}:#{database} #{normalize_name(b)}"
|
223
|
-
end
|
224
|
-
backups
|
225
|
-
end
|
226
|
-
|
227
|
-
protected
|
228
|
-
def backup_dir
|
229
|
-
"/mnt/tmp"
|
230
|
-
end
|
231
|
-
|
232
|
-
end
|
233
|
-
end
|