ey_cloud_server 1.1.1.pre7 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/ey-snapshots +1 -2
- data/lib/ey-flex.rb +2 -0
- data/lib/ey-flex/backups.rb +28 -21
- data/lib/ey-flex/ey-api.rb +22 -0
- data/lib/ey-flex/snapshot_minder.rb +30 -20
- data/lib/ey-flex/version.rb +1 -1
- metadata +62 -50
- data/LICENSE +0 -20
- data/README.rdoc +0 -25
data/bin/ey-snapshots
CHANGED
data/lib/ey-flex.rb
CHANGED
@@ -7,6 +7,7 @@ require 'fileutils'
|
|
7
7
|
require 'json/ext'
|
8
8
|
require 'right_aws'
|
9
9
|
require 'open-uri'
|
10
|
+
require 'rest_client'
|
10
11
|
require 'dbi'
|
11
12
|
require 'zlib'
|
12
13
|
require 'stringio'
|
@@ -26,6 +27,7 @@ end
|
|
26
27
|
require lib_dir + '/big-brother'
|
27
28
|
require lib_dir + '/backups'
|
28
29
|
require lib_dir + '/bucket_minder'
|
30
|
+
require lib_dir + '/ey-api'
|
29
31
|
require lib_dir + '/mysql_database'
|
30
32
|
require lib_dir + '/postgresql_database'
|
31
33
|
require lib_dir + '/snapshot_minder'
|
data/lib/ey-flex/backups.rb
CHANGED
@@ -70,6 +70,10 @@ module EY::Flex
|
|
70
70
|
options[:index] = index
|
71
71
|
end
|
72
72
|
|
73
|
+
opts.on("-q", "--quiet", "Supress output to STDOUT") do
|
74
|
+
options[:quiet] = true
|
75
|
+
end
|
76
|
+
|
73
77
|
end
|
74
78
|
|
75
79
|
opts.parse!(args)
|
@@ -81,7 +85,7 @@ module EY::Flex
|
|
81
85
|
|
82
86
|
case options[:command]
|
83
87
|
when :list
|
84
|
-
eyb.list options[:db]
|
88
|
+
eyb.list options[:db]
|
85
89
|
when :new_backup
|
86
90
|
eyb.new_backup
|
87
91
|
when :download
|
@@ -91,13 +95,13 @@ module EY::Flex
|
|
91
95
|
end
|
92
96
|
eyb.cleanup
|
93
97
|
rescue EY::Flex::Error => e
|
94
|
-
|
95
|
-
exit 1
|
98
|
+
abort e.message
|
96
99
|
end
|
97
100
|
|
98
101
|
ENGINES = {}
|
99
102
|
|
100
103
|
def initialize(options = {})
|
104
|
+
@quiet = options[:quiet]
|
101
105
|
engine_klass = ENGINES[options[:engine]] || raise("Invalid database engine: #{options[:engine].inspect}")
|
102
106
|
@engine = engine_klass.new(self)
|
103
107
|
|
@@ -128,8 +132,7 @@ module EY::Flex
|
|
128
132
|
if File.exist?(filename)
|
129
133
|
@config = YAML::load(File.read(filename))
|
130
134
|
else
|
131
|
-
|
132
|
-
exit 1
|
135
|
+
abort "You need to have a backup file at #{filename}"
|
133
136
|
end
|
134
137
|
end
|
135
138
|
|
@@ -139,16 +142,21 @@ module EY::Flex
|
|
139
142
|
end
|
140
143
|
end
|
141
144
|
|
145
|
+
def say(msg, newline = true)
|
146
|
+
return if @quiet
|
147
|
+
print("#{msg}#{"\n" if newline}")
|
148
|
+
end
|
149
|
+
|
142
150
|
def backup_database(database)
|
143
151
|
File.open("#{self.backup_dir}/#{database}.#{@tmpname}", "w") do |f|
|
144
|
-
|
152
|
+
say "doing database: #{database}"
|
145
153
|
@engine.dump_database(database, f)
|
146
154
|
end
|
147
155
|
|
148
156
|
File.open("#{self.backup_dir}/#{database}.#{@tmpname}") do |f|
|
149
157
|
path = "#{@env}.#{database}/#{database}.#{@tmpname}"
|
150
158
|
AWS::S3::S3Object.store(path, f, @bucket, :access => :private)
|
151
|
-
|
159
|
+
say "successful backup: #{database}.#{@tmpname}"
|
152
160
|
end
|
153
161
|
end
|
154
162
|
|
@@ -158,13 +166,12 @@ module EY::Flex
|
|
158
166
|
|
159
167
|
if obj = list(db)[idx.to_i]
|
160
168
|
filename = normalize_name(obj)
|
161
|
-
|
169
|
+
say "downloading: #{filename}"
|
162
170
|
File.open(filename, 'wb') do |f|
|
163
|
-
|
171
|
+
say ".", false
|
164
172
|
obj.value {|chunk| f.write chunk }
|
165
173
|
end
|
166
|
-
|
167
|
-
puts "finished"
|
174
|
+
say "finished"
|
168
175
|
[db, filename]
|
169
176
|
else
|
170
177
|
raise BackupNotFound, "No backup found for database #{db.inspect}: requested index: #{idx}"
|
@@ -180,8 +187,8 @@ module EY::Flex
|
|
180
187
|
|
181
188
|
def cleanup
|
182
189
|
begin
|
183
|
-
list('all'
|
184
|
-
|
190
|
+
list('all')[0...-(@keep*@databases.size)].each do |o|
|
191
|
+
say "deleting: #{o.key}"
|
185
192
|
o.delete
|
186
193
|
end
|
187
194
|
rescue AWS::S3::S3Exception, AWS::S3::Error
|
@@ -197,8 +204,8 @@ module EY::Flex
|
|
197
204
|
AWS::S3::S3Object.find name, @bucket
|
198
205
|
end
|
199
206
|
|
200
|
-
def list(database='all'
|
201
|
-
|
207
|
+
def list(database='all')
|
208
|
+
say "Listing database backups for #{database}"
|
202
209
|
backups = []
|
203
210
|
if database == 'all'
|
204
211
|
@databases.each do |db|
|
@@ -208,12 +215,12 @@ module EY::Flex
|
|
208
215
|
else
|
209
216
|
backups = AWS::S3::Bucket.objects(@bucket, :prefix => "#{@env}.#{database}").sort
|
210
217
|
end
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
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
|
217
224
|
backups
|
218
225
|
end
|
219
226
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EyApi
|
2
|
+
def call_api(path, opts={})
|
3
|
+
response = @rest["/api/#{path}"].post(@keys.merge(opts), {"Accept" => "application/json"})
|
4
|
+
JSON.parse(response.body)
|
5
|
+
rescue RestClient::RequestFailed => e
|
6
|
+
case e.http_code
|
7
|
+
when 503
|
8
|
+
sleep 10 # Nanite, save us...
|
9
|
+
retry
|
10
|
+
else
|
11
|
+
raise "API call to Engine Yard failed with status #{e.http_code}."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_envs
|
16
|
+
@_envs ||= call_api("environments")
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_json(instance_id)
|
20
|
+
call_api("json_for_instance", :instance_id => instance_id)
|
21
|
+
end
|
22
|
+
end
|
@@ -31,6 +31,9 @@ module EY
|
|
31
31
|
options[:command] = :snapshot_volumes
|
32
32
|
end
|
33
33
|
|
34
|
+
opts.on("-q", "--quiet", "Supress output to STDOUT") do
|
35
|
+
options[:quiet] = true
|
36
|
+
end
|
34
37
|
|
35
38
|
end
|
36
39
|
|
@@ -40,8 +43,7 @@ module EY
|
|
40
43
|
if File.exist?(config = File.expand_path(defaults[:config]))
|
41
44
|
ey = new(options = defaults.merge(YAML::load(File.read(config))).merge(options))
|
42
45
|
else
|
43
|
-
|
44
|
-
exit 1
|
46
|
+
abort "You need to have an /etc/.mysql.backups.yml file with your credentials in it to use this tool.\nOr point it at a yaml file with -c .mysql.backups.yml"
|
45
47
|
end
|
46
48
|
|
47
49
|
ey.send(options[:command])
|
@@ -52,9 +54,9 @@ module EY
|
|
52
54
|
@opts = opts
|
53
55
|
@instance_id = opts[:instance_id]
|
54
56
|
@db = Mysql.new('root', opts[:dbpass], opts[:lock_wait_timeout]) rescue nil
|
55
|
-
@ec2 = RightAws::Ec2.new(opts[:aws_secret_id], opts[:aws_secret_key])
|
57
|
+
@ec2 = RightAws::Ec2.new(opts[:aws_secret_id], opts[:aws_secret_key], :logger => Logger.new("/dev/null"))
|
56
58
|
get_instance_id
|
57
|
-
find_volume_ids
|
59
|
+
silence_stream($stderr) { find_volume_ids }
|
58
60
|
end
|
59
61
|
|
60
62
|
def find_volume_ids
|
@@ -68,7 +70,7 @@ module EY
|
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
71
|
-
|
73
|
+
say "Volume IDs are #{@volume_ids.inspect}"
|
72
74
|
@volume_ids
|
73
75
|
end
|
74
76
|
|
@@ -81,7 +83,7 @@ module EY
|
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
84
|
-
|
86
|
+
say "Snapshots #{@snapshot_ids.inspect}"
|
85
87
|
@snapshot_ids
|
86
88
|
end
|
87
89
|
|
@@ -93,7 +95,7 @@ module EY
|
|
93
95
|
snaps << snapshot
|
94
96
|
end
|
95
97
|
(snaps[keep..-1]||[]).each do |snapshot|
|
96
|
-
|
98
|
+
say "deleting snapshot of /#{mnt}: #{snapshot[:aws_id]}"
|
97
99
|
@ec2.delete_snapshot(snapshot[:aws_id])
|
98
100
|
end
|
99
101
|
end
|
@@ -109,7 +111,7 @@ module EY
|
|
109
111
|
snaps << create_snapshot(vid)
|
110
112
|
when :db
|
111
113
|
@db.flush_tables_with_read_lock
|
112
|
-
|
114
|
+
say "Read lock acquired. Writing master binlog info to #{@master_status_file} and syncing filesystem buffers."
|
113
115
|
@db.write_master_status
|
114
116
|
sync_filesystem_buffers
|
115
117
|
snaps << create_snapshot(vid)
|
@@ -125,8 +127,8 @@ module EY
|
|
125
127
|
open('http://169.254.169.254/latest/meta-data/instance-id') do |f|
|
126
128
|
@instance_id = f.gets
|
127
129
|
end
|
128
|
-
|
129
|
-
|
130
|
+
abort "Cannot find instance id!" unless @instance_id
|
131
|
+
say "Instance ID is #{@instance_id}"
|
130
132
|
@instance_id
|
131
133
|
end
|
132
134
|
|
@@ -137,10 +139,24 @@ module EY
|
|
137
139
|
|
138
140
|
def create_snapshot(volume_id)
|
139
141
|
snap = @ec2.create_snapshot(volume_id)
|
140
|
-
|
142
|
+
say "Created snapshot of #{volume_id} as #{snap[:aws_id]}"
|
141
143
|
snap
|
142
144
|
end
|
143
|
-
|
145
|
+
|
146
|
+
private
|
147
|
+
def say(msg, newline = true)
|
148
|
+
return if @opts[:quiet]
|
149
|
+
print("#{msg}#{"\n" if newline}")
|
150
|
+
end
|
151
|
+
|
152
|
+
def silence_stream(stream)
|
153
|
+
old_stream = stream.dup
|
154
|
+
stream.reopen("/dev/null")
|
155
|
+
stream.sync = true
|
156
|
+
yield
|
157
|
+
ensure
|
158
|
+
stream.reopen(old_stream)
|
159
|
+
end
|
144
160
|
end
|
145
161
|
|
146
162
|
class Mysql
|
@@ -166,8 +182,6 @@ module EY
|
|
166
182
|
end
|
167
183
|
|
168
184
|
def flush_tables_with_read_lock
|
169
|
-
puts("Requesting read lock for snapshot.")
|
170
|
-
|
171
185
|
pipe = IO.popen("mysql -u #{@username} -p#{@password}", 'w')
|
172
186
|
@read_lock_pid = pipe.pid
|
173
187
|
|
@@ -180,14 +194,13 @@ module EY
|
|
180
194
|
# after killing the process the mysql thread is still hanging out, need to kill it directly
|
181
195
|
kill_thread_cmd = "mysql -u #{@username} -p#{@password} -e'kill #{thread_id};'"
|
182
196
|
system(kill_thread_cmd)
|
183
|
-
|
197
|
+
abort "Read lock not acquired after #{@lock_wait_timeout} second timeout. Killed request and aborting backup."
|
184
198
|
end
|
185
199
|
|
186
200
|
true
|
187
201
|
end
|
188
202
|
|
189
203
|
def unlock_tables
|
190
|
-
puts("Unlocking tables")
|
191
204
|
# technically we don't actually have to do anything here since the spawned
|
192
205
|
# process that has the read lock will die with this one but it doesn't hurt
|
193
206
|
# to be safe
|
@@ -196,10 +209,7 @@ module EY
|
|
196
209
|
end
|
197
210
|
|
198
211
|
def disconnect
|
199
|
-
puts("Disconnecting from MySQL")
|
200
212
|
@dbh.disconnect
|
201
213
|
end
|
202
|
-
|
203
214
|
end
|
204
|
-
|
205
|
-
end
|
215
|
+
end
|
data/lib/ey-flex/version.rb
CHANGED
metadata
CHANGED
@@ -1,110 +1,124 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ey_cloud_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 1.1.1.pre7
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 1.2.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
|
-
-
|
12
|
+
- Engine Yard Engineers
|
14
13
|
autorequire:
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-27 00:00:00 -07:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
|
-
|
23
|
-
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
segments:
|
28
28
|
- 0
|
29
29
|
version: "0"
|
30
|
-
requirement: *id001
|
31
|
-
name: json
|
32
|
-
prerelease: false
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
30
|
type: :runtime
|
35
|
-
version_requirements:
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: right_aws
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
segments:
|
40
40
|
- 0
|
41
41
|
version: "0"
|
42
|
-
|
43
|
-
|
44
|
-
prerelease: false
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
45
44
|
- !ruby/object:Gem::Dependency
|
45
|
+
name: open4
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - "="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
- 9
|
54
|
+
- 6
|
55
|
+
version: 0.9.6
|
46
56
|
type: :runtime
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: *id003
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: aws-s3
|
60
|
+
prerelease: false
|
61
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
48
62
|
requirements:
|
49
63
|
- - ">="
|
50
64
|
- !ruby/object:Gem::Version
|
51
65
|
segments:
|
52
66
|
- 0
|
53
67
|
version: "0"
|
54
|
-
requirement: *id003
|
55
|
-
name: open4
|
56
|
-
prerelease: false
|
57
|
-
- !ruby/object:Gem::Dependency
|
58
68
|
type: :runtime
|
59
|
-
version_requirements:
|
69
|
+
version_requirements: *id004
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rest-client
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
60
74
|
requirements:
|
61
75
|
- - ">="
|
62
76
|
- !ruby/object:Gem::Version
|
63
77
|
segments:
|
64
78
|
- 0
|
65
79
|
version: "0"
|
66
|
-
requirement: *id004
|
67
|
-
name: aws-s3
|
68
|
-
prerelease: false
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
80
|
type: :runtime
|
71
|
-
version_requirements:
|
81
|
+
version_requirements: *id005
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: ey_stonith
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- -
|
87
|
+
- - ~>
|
74
88
|
- !ruby/object:Gem::Version
|
75
89
|
segments:
|
76
90
|
- 0
|
77
|
-
-
|
78
|
-
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
email: awsmdev@engineyard.com
|
91
|
+
- 2
|
92
|
+
- 0
|
93
|
+
version: 0.2.0
|
94
|
+
type: :runtime
|
95
|
+
version_requirements: *id006
|
96
|
+
description: "Server side components for Engine Yard's cloud "
|
97
|
+
email:
|
98
|
+
- ninja@engineyard.com
|
86
99
|
executables:
|
87
100
|
- eybackup
|
88
101
|
- ey-snapshots
|
89
102
|
- ey-agent
|
90
103
|
extensions: []
|
91
104
|
|
92
|
-
extra_rdoc_files:
|
93
|
-
|
94
|
-
- LICENSE
|
105
|
+
extra_rdoc_files: []
|
106
|
+
|
95
107
|
files:
|
96
|
-
- LICENSE
|
97
|
-
- README.rdoc
|
98
108
|
- lib/ey-flex/backups.rb
|
99
109
|
- lib/ey-flex/big-brother.rb
|
100
110
|
- lib/ey-flex/bucket_minder.rb
|
111
|
+
- lib/ey-flex/ey-api.rb
|
101
112
|
- lib/ey-flex/mysql_database.rb
|
102
113
|
- lib/ey-flex/postgresql_database.rb
|
103
114
|
- lib/ey-flex/snapshot_minder.rb
|
104
115
|
- lib/ey-flex/version.rb
|
105
116
|
- lib/ey-flex.rb
|
117
|
+
- bin/eybackup
|
118
|
+
- bin/ey-snapshots
|
119
|
+
- bin/ey-agent
|
106
120
|
has_rdoc: true
|
107
|
-
homepage: http://
|
121
|
+
homepage: http://github.com/engineyard
|
108
122
|
licenses: []
|
109
123
|
|
110
124
|
post_install_message:
|
@@ -121,16 +135,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
121
135
|
version: "0"
|
122
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
137
|
requirements:
|
124
|
-
- - "
|
138
|
+
- - ">="
|
125
139
|
- !ruby/object:Gem::Version
|
126
140
|
segments:
|
127
|
-
-
|
128
|
-
|
129
|
-
- 1
|
130
|
-
version: 1.3.1
|
141
|
+
- 0
|
142
|
+
version: "0"
|
131
143
|
requirements: []
|
132
144
|
|
133
|
-
rubyforge_project:
|
145
|
+
rubyforge_project: http://gemcutter.org
|
134
146
|
rubygems_version: 1.3.6
|
135
147
|
signing_key:
|
136
148
|
specification_version: 3
|
data/LICENSE
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
Copyright (c) 2009 Engine Yard Inc.
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
10
|
-
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
== ey
|
2
|
-
|
3
|
-
= Development
|
4
|
-
|
5
|
-
You need to have gem bundler install
|
6
|
-
|
7
|
-
gem install bundler
|
8
|
-
|
9
|
-
Then bundle everything up:
|
10
|
-
|
11
|
-
gem bundle -- --with-mysql_config
|
12
|
-
|
13
|
-
To run the specs, just jump into a "bundled" shell:
|
14
|
-
|
15
|
-
gem exec bash
|
16
|
-
|
17
|
-
Copy the example file to spec/config.yml:
|
18
|
-
|
19
|
-
Then you can run specs:
|
20
|
-
|
21
|
-
spec -c spec
|
22
|
-
|
23
|
-
Running the bin files are easy too:
|
24
|
-
|
25
|
-
bin/ey-recipes
|