ey_cloud_server 1.1.1.pre7 → 1.2.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.
- 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
|