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.
@@ -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, :timeout
9
+ attr_accessor :password, :check, :start_timeout, :stop_timeout
10
10
 
11
11
  def initialize(options = {})
12
- @password = options[:password] || abort("ERROR: You must provide a password.")
13
- @check = options[:check] || abort("ERROR: You must a check interval.")
14
- @timout = options[:timeout] || abort("ERROR: You must provide a timeout.")
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 = 120
36
+ termlimit = stop_timeout
38
37
  loop {
39
38
  break if not mysql_running()
40
- sleep(sleeptime)
41
- termslept = termslept + sleeptime
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(sleeptime)
52
+ sleep(check)
54
53
  end
55
54
  break if not mysql_running()
56
- slept = slept + sleeptime
57
- if slept > sleeplimit
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 |timeout|
138
- options[:timeout] = timeout.to_i
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
- prerelease: false
4
+ hash: 961916000
5
+ prerelease: true
5
6
  segments:
6
7
  - 1
7
- - 3
8
- - 1
9
- version: 1.3.1
8
+ - 4
9
+ - 5
10
+ - pre
11
+ version: 1.4.5.pre
10
12
  platform: ruby
11
- authors:
12
- - Engine Yard Engineers
13
+ authors: []
14
+
13
15
  autorequire:
14
16
  bindir: bin
15
17
  cert_chain: []
16
18
 
17
- date: 2010-05-06 00:00:00 -07:00
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
- - 3
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: "Server side components for Engine Yard's cloud "
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
- - bin/eybackup
121
- - bin/ey-snapshots
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: http://github.com/engineyard
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
- - 0
146
- version: "0"
175
+ - 1
176
+ - 3
177
+ - 1
178
+ version: 1.3.1
147
179
  requirements: []
148
180
 
149
- rubyforge_project: http://gemcutter.org
150
- rubygems_version: 1.3.6
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
@@ -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