VladTheEnterprising 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of VladTheEnterprising might be problematic. Click here for more details.
- data.tar.gz.sig +0 -0
- data/README.txt +50 -1
- data/Rakefile +2 -0
- data/lib/vlad/dba.rb +5 -8
- data/lib/vlad/dba/mysql.rb +162 -6
- data/lib/vlad/enterprising.rb +1 -1
- metadata +3 -3
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/README.txt
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
VladTheEnterprising
|
2
2
|
by Michael L. Welles
|
3
|
+
michael@bangstate.com
|
3
4
|
http://rubyforge.org/projects/vladenvironment/
|
5
|
+
Contributions by:
|
6
|
+
Alexander Rubin - arubin@mysql.com
|
4
7
|
|
5
8
|
== DESCRIPTION:
|
6
9
|
|
@@ -90,6 +93,52 @@ Currently consists of the following:
|
|
90
93
|
necessary stuff on those machines while still working as expected
|
91
94
|
on the ubuntu hosts in the subenvironment.
|
92
95
|
|
96
|
+
Migrations and Versioning
|
97
|
+
=========================
|
98
|
+
|
99
|
+
As of 1.8, schema migrations and versioning are supported. By
|
100
|
+
default the app will look under db/migrations/[schema name]/[version
|
101
|
+
no]-[text]/[upgrade.sql|rollback.sql]
|
102
|
+
|
103
|
+
So, for example. Lets say I have a the folowing files:
|
104
|
+
|
105
|
+
db/migrations/UserContent/1-version_column/rollback.sql
|
106
|
+
db/migrations/UserContent/1-version_column/upgrade.sql
|
107
|
+
db/migrations/UserContent/2-crnr_sp_get_asset_comments/rollback.sql
|
108
|
+
db/migrations/UserContent/2-crnr_sp_get_asset_comments/upgrade.sql
|
109
|
+
db/migrations/UserContent/3-crnr_sp_update_asset_taxonomy_properties/rollback.sql
|
110
|
+
db/migrations/UserContent/3-crnr_sp_update_asset_taxonomy_properties/upgrade.sql
|
111
|
+
|
112
|
+
And I run the following command:
|
113
|
+
|
114
|
+
"rake vlad:mysql:migrate TARGET=testing:user_content"
|
115
|
+
|
116
|
+
VladtheDBA will will perform the following steps on :db_master
|
117
|
+
in my testing:user_content subenvironment:
|
118
|
+
|
119
|
+
- For each database defined there, if database
|
120
|
+
doesn't have a "schema_info" table, vlad will create one.
|
121
|
+
|
122
|
+
- It will look for a directory for that schema under db/migrations.
|
123
|
+
|
124
|
+
- If it finds one (in this case, UserContent), it will perform the
|
125
|
+
upgrade.sql for each migration migration subdirectory in
|
126
|
+
numerical order, until the version in the schema info table
|
127
|
+
is up to date with the migrations.
|
128
|
+
|
129
|
+
Running a rollback is as follows:
|
130
|
+
|
131
|
+
"rake vlad:mysql:migrate TARGET=testing:user_content
|
132
|
+
DATABASE=UserContent VERSION=1"
|
133
|
+
|
134
|
+
Will cause vlad to decremant backwards through the migrations
|
135
|
+
running the DDL in rollback.sql, until it gets back to version 1.
|
136
|
+
|
137
|
+
The idea was a blatant ripoff of activerecord migrations in rails,
|
138
|
+
but just in a framework neutral way, and with the only assumption
|
139
|
+
being that there's ssh and the mysql client available on the
|
140
|
+
db_master.
|
141
|
+
|
93
142
|
* The Xen Master
|
94
143
|
|
95
144
|
Beginnings of a series of tasks for autmating the creation and
|
@@ -120,7 +169,7 @@ Currently consists of the following:
|
|
120
169
|
|
121
170
|
(The MIT License)
|
122
171
|
|
123
|
-
Copyright (c) 2007
|
172
|
+
Copyright (c) 2007
|
124
173
|
|
125
174
|
Permission is hereby granted, free of charge, to any person obtaining
|
126
175
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
data/lib/vlad/dba.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
require 'vlad/dba/mysql'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
2
|
+
unless defined? mysql_user
|
3
|
+
set(:mysql_user, 'root')
|
4
|
+
#if defined? mysql_root_password
|
5
|
+
#set(:mysql_password, mysql_root_password)
|
6
|
+
#end
|
10
7
|
end
|
data/lib/vlad/dba/mysql.rb
CHANGED
@@ -4,10 +4,23 @@ require 'highline/import'
|
|
4
4
|
require 'monitor'
|
5
5
|
require 'resolv'
|
6
6
|
require 'digest/md5'
|
7
|
-
|
7
|
+
require 'tempfile'
|
8
|
+
unless defined? mysql_user
|
9
|
+
set(:mysql_user, 'root')
|
10
|
+
#if defined? mysql_root_password
|
11
|
+
#set(:mysql_password, mysql_root_password)
|
12
|
+
#end
|
13
|
+
end
|
8
14
|
|
9
15
|
namespace :vlad do
|
10
16
|
namespace :mysql do
|
17
|
+
desc "Checks mysql user open files ulimit on db servers"
|
18
|
+
remote_task :check_ulimit_open_files, :roles => [:db_master, :db_slave] do
|
19
|
+
n = run("#{sudo} su - mysql -c 'ulimit -n' | tail -1 ")
|
20
|
+
puts "#{target_host} mysql user open file ulimit: #{n}"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
11
24
|
desc "Bootstraps mysql slave candiates, installs mysql, syncs them to the master, and starts them."
|
12
25
|
task :bootstrap_new_slaves => [
|
13
26
|
:assert_production_target_subset,
|
@@ -172,8 +185,8 @@ namespace :vlad do
|
|
172
185
|
"db_slave_#{os}.cnf.tmpl",
|
173
186
|
"#{name}.cnf.tmpl",
|
174
187
|
].each do |t|
|
175
|
-
tt = "files/mysql/templates/#{t}"
|
176
|
-
template_file = tt if File.exist?(tt)
|
188
|
+
tt = "files/mysql/templates/#{t}"
|
189
|
+
template_file = tt if File.exist?(tt)
|
177
190
|
end
|
178
191
|
template = File.read(template_file)
|
179
192
|
server_id = Resolv.getaddress(hostname) + "-" + hostname
|
@@ -238,6 +251,25 @@ namespace :vlad do
|
|
238
251
|
mysql_query "flush privileges; slave start;", "", :u => "root"
|
239
252
|
end
|
240
253
|
|
254
|
+
desc "dumps, compresses, and copys database(s) from db_master, will copy all databases unless one is specified in a DATABASE= arg"
|
255
|
+
remote_task :dump, :roles => :db_master do
|
256
|
+
database = ENV["DATABASE"] || "all_tables"
|
257
|
+
db_arg = ENV["DATABASE"] || "-A"
|
258
|
+
dumpfile = ENV["DUMPFILE"] || mysql_dumpfile
|
259
|
+
datfile = dumpfile.sub(/\.gz$/,"").sub(/-/, "-#{database}-")
|
260
|
+
dumpfile = datfile + ".gz"
|
261
|
+
run "mkdir -p `dirname #{datfile}`; exit 0"
|
262
|
+
run "#{mysqldump} --single-transaction -uroot -p'#{mysql_root_password}' #{db_arg} > #{datfile}"
|
263
|
+
`mkdir -p \`dirname #{datfile}\``
|
264
|
+
run "nice gzip #{datfile}"
|
265
|
+
cmd="ssh #{target_host} cat #{dumpfile} > #{dumpfile}"
|
266
|
+
`#{cmd}`
|
267
|
+
run "if [ -e #{dumpfile} ]; then rm #{dumpfile}; else exit 1; fi"
|
268
|
+
puts "Done: #{dumpfile}"
|
269
|
+
end
|
270
|
+
task :dump => [:ask_root_password]
|
271
|
+
|
272
|
+
desc "Creates a master-data dump from the target environment db_master, copies to dumps/"
|
241
273
|
remote_task :dump_master, :roles => :db_master do
|
242
274
|
unless ENV["DUMPFILE"]
|
243
275
|
datfile = mysql_dumpfile.sub(/\.gz$/,"")
|
@@ -246,20 +278,23 @@ namespace :vlad do
|
|
246
278
|
`mkdir -p \`dirname #{mysql_dumpfile}\``
|
247
279
|
run "nice gzip #{datfile}"
|
248
280
|
cmd="ssh #{target_host} cat #{mysql_dumpfile} > #{mysql_dumpfile}"
|
249
|
-
puts cmd
|
250
281
|
`#{cmd}`
|
251
282
|
run "if [ -e #{mysql_dumpfile} ]; then rm #{mysql_dumpfile}; else exit 1; fi"
|
252
283
|
end
|
253
284
|
end
|
285
|
+
task :dump_master => [:ask_root_password]
|
254
286
|
|
255
287
|
remote_task :slave_sync, :roles => :new_slave do
|
256
|
-
|
288
|
+
if ENV["DUMPFILE"]
|
289
|
+
set( :mysql_dumpfile, ENV["DUMPFILE"] )
|
290
|
+
end
|
257
291
|
run "#{mysql_path_bin}/mysql -vvf -u root -e \"slave stop\;\"; exit 0"
|
258
292
|
run "#{mysql_path_bin}/mysql -vvf -u root -e \"change master to master_host='#{mysql_master_host}', master_user='#{shortname}_repl', master_password='#{mysql_replication_password}';\""
|
259
293
|
run "mkdir -p `dirname #{mysql_dumpfile}`"
|
260
294
|
#puts "#{target_host}"
|
261
295
|
scp mysql_dumpfile, mysql_dumpfile
|
262
296
|
run "cat #{mysql_dumpfile} | gunzip - | #{mysql_path_bin}/mysql -uroot"
|
297
|
+
|
263
298
|
run "rm #{mysql_dumpfile}"
|
264
299
|
end
|
265
300
|
|
@@ -273,7 +308,6 @@ namespace :vlad do
|
|
273
308
|
|
274
309
|
def assert_agent_running
|
275
310
|
cmd = ssh #{target_host} "ps -ef | grep mysql | grep agent | grep -v grep"
|
276
|
-
puts cmd
|
277
311
|
output=`#{cmd}`
|
278
312
|
raise "DOWN" unless output.match(/mysql-service-agent/)
|
279
313
|
puts "#{target_host}: agent OK"
|
@@ -289,6 +323,7 @@ namespace :vlad do
|
|
289
323
|
end
|
290
324
|
task :check_slaves => [:ask_root_password]
|
291
325
|
|
326
|
+
|
292
327
|
desc "Checks mysql agent status on all slaves"
|
293
328
|
remote_task :check_restart_agents, :roles => [:db_slave, :db_master] do
|
294
329
|
cmd1 = "#{mysql_agent_init} start"
|
@@ -344,9 +379,130 @@ namespace :vlad do
|
|
344
379
|
`rm /tmp/my.cnf.#{target_host}; exit 0`
|
345
380
|
run "rm -f .my.cnf; exit 0"
|
346
381
|
end
|
382
|
+
|
383
|
+
desc "Executes query specified in QUERY='' argument on database specified in the DATABASE='' argument on the :db_master for the target environment and prints the results"
|
384
|
+
remote_task :query, :roles => :db_master do
|
385
|
+
raise "Please specify what QUERY to run against what database, i.e. QUERY='select foo from bar' DATABASE='Movies' TARGET='staging'" unless ENV['QUERY']
|
386
|
+
puts mysql_query(ENV['QUERY'],ENV['DATABASE'], :u => 'root', :p => mysql_root_password )
|
387
|
+
end
|
388
|
+
|
389
|
+
desc "Executes query specified in QUERY='' argument on database specified in the DATABASE='' argument on all the slaves for the target environment and prints the results"
|
390
|
+
remote_task :query_slaves, :roles => :db_slave do
|
391
|
+
raise "Please specify what QUERY to run against what database, i.e. QUERY='select foo from bar' DATABASE='Movies' TARGET='staging'" unless ENV['QUERY']
|
392
|
+
puts "#{target_host}: " + mysql_query(ENV['QUERY'],ENV['DATABASE'], :u => 'root', :p => mysql_root_password ).gsub("\n", "*****")
|
393
|
+
end
|
394
|
+
|
395
|
+
remote_task :schema_info, :roles => :db_master do
|
396
|
+
databases.select{ |db| db == "mysql" ? false : true }.each do |database|
|
397
|
+
begin
|
398
|
+
output = mysql_query("select version from schema_info limit 1",
|
399
|
+
database,
|
400
|
+
:u => 'root',
|
401
|
+
:p => mysql_root_password)
|
402
|
+
|
403
|
+
current_version = output.split(/\n/)[1].to_i
|
404
|
+
rescue
|
405
|
+
current_version = "unknown"
|
406
|
+
end
|
407
|
+
puts "#{target_host} schema info"
|
408
|
+
puts "#{database} version #{current_version}"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
task :schema_info => [:ask_root_password]
|
412
|
+
|
413
|
+
desc "Runs db migrations for current TARGET can be controlled with better granuarity with DATABASE and VERSION arguments"
|
414
|
+
remote_task :migrate, :roles => :db_master do
|
415
|
+
assert_database_if_version_given()
|
416
|
+
migration_dir ||= "db/migrations"
|
417
|
+
current_version = {}
|
418
|
+
targets = ENV['DATABASE']
|
419
|
+
targets ||= databases.select{ |db| db == "mysql" ? false : true }
|
420
|
+
targets.each do |database|
|
421
|
+
database_dir = File.join(migration_dir, database)
|
422
|
+
begin
|
423
|
+
output = mysql_query("select version from schema_info limit 1",
|
424
|
+
database,
|
425
|
+
:u => 'root',
|
426
|
+
:p => mysql_root_password)
|
427
|
+
current_version = output.split(/\n/)[1].to_i
|
428
|
+
rescue
|
429
|
+
current_version = 0
|
430
|
+
end
|
431
|
+
migrations = { }
|
432
|
+
dirs = Dir.new(database_dir).entries.select{|d| d.match(/^\d+/)}
|
433
|
+
dirs.each { |dir| migrations[dir.sub(/[^\d]*$/, "")] = dir }
|
434
|
+
target_version = ENV['VERSION'].to_i if ENV['VERSION']
|
435
|
+
target_version ||= migrations.keys.sort{|a,b| a.to_i <=> b.to_i }.last.to_i rescue 0
|
436
|
+
case
|
437
|
+
when current_version < target_version
|
438
|
+
have_migrations = lambda { current_version < target_version }
|
439
|
+
iterator = lambda { |v| v + 1}
|
440
|
+
action = "upgrade"
|
441
|
+
next_file = lambda {|v| File.join(database_dir, migrations[(v + 1 ).to_s], "#{action}.sql") rescue nil}
|
442
|
+
puts "Upgrading schema #{database} from version #{current_version} to version #{target_version} on #{target_host}"
|
443
|
+
when current_version > target_version
|
444
|
+
have_migrations = lambda { current_version > target_version }
|
445
|
+
iterator = lambda { |v| v - 1}
|
446
|
+
action = "rollback"
|
447
|
+
next_file = lambda { |v| File.join(database_dir, migrations[v.to_s], "#{action}.sql") }
|
448
|
+
puts "Downgrading #{database} from version #{current_version} to version #{target_version} on #{target_host}"
|
449
|
+
else
|
450
|
+
have_migrations = lambda{ false }
|
451
|
+
puts "#{database} already at version #{target_version} on #{target_host}"
|
452
|
+
end
|
453
|
+
|
454
|
+
while have_migrations.call() do
|
455
|
+
next_version = iterator.call(current_version)
|
456
|
+
migration_file = next_file.call(current_version)
|
457
|
+
if migration_file && File.exist?(migration_file)
|
458
|
+
sql = "start transaction;\n"
|
459
|
+
sql << "CREATE TABLE IF NOT EXISTS schema_info ( version INT default 0 );\n"
|
460
|
+
sql << File.read(migration_file)
|
461
|
+
sql << "\n"
|
462
|
+
sql << "update schema_info set version = #{next_version};\n"
|
463
|
+
sql << "commit;"
|
464
|
+
src = "/tmp/#{database}-version-#{File.basename(File.dirname(migration_file))}-#{File.basename(migration_file)}"
|
465
|
+
File.open(src, "w"){ |f| f.write(sql) }
|
466
|
+
scp src, src
|
467
|
+
if Rake.application.options.trace
|
468
|
+
puts "============================================\n"
|
469
|
+
puts sql
|
470
|
+
puts "============================================\n"
|
471
|
+
end
|
472
|
+
mysql_query("source #{src};", database, :u => 'root', :p => mysql_root_password)
|
473
|
+
puts "Executed: #{migration_file} on #{target_host}"
|
474
|
+
end
|
475
|
+
current_version = next_version
|
476
|
+
end
|
477
|
+
puts "Migration complete."
|
478
|
+
end
|
479
|
+
end
|
480
|
+
task :migrate => [:ask_root_password, :assert_single_master]
|
481
|
+
|
482
|
+
desc "restores a dump to current db_master from file set in mysql_dumpfile or in a DUMPFILE argument"
|
483
|
+
remote_task :restore, :roles => :db_master do
|
484
|
+
dump = ENV["DUMPFILE"] || dump
|
485
|
+
raise "Please specify a valid dumpfile to restore via :mysql_dumpfile in the config or use the DUMPFILE argument!" unless File.exist?(dump)
|
486
|
+
scp dump, dump
|
487
|
+
p = ""
|
488
|
+
unless mysql_root_password.nil? or mysql_root_password == ""
|
489
|
+
p = "-p'#{mysql_root_password}' "
|
490
|
+
end
|
491
|
+
if ENV['DATABASE']
|
492
|
+
mysql_query "DROP DATABASE IF EXISTS #{ENV['DATABASE']}", "", :u => 'root', :p => mysql_root_password
|
493
|
+
mysql_query "CREATE DATABASE IF NOT EXISTS #{ENV['DATABASE']}", "", :u => 'root', :p => mysql_root_password
|
494
|
+
end
|
495
|
+
run "cat #{dump} | gunzip - | #{mysql_path_bin}/mysql -uroot #{p} #{ENV['DATABASE']}"
|
496
|
+
end
|
497
|
+
task :restore => [:ask_root_password, :assert_single_master]
|
347
498
|
end
|
348
499
|
end
|
349
500
|
|
501
|
+
def assert_database_if_version_given
|
502
|
+
if ENV['VERSION']
|
503
|
+
raise "You must specify a database if you specify a target version! i.e. DATABASE=Movies VERSION=93" unless ENV['DATABASE']
|
504
|
+
end
|
505
|
+
end
|
350
506
|
dialect( :name => :default_mysql,
|
351
507
|
:assert => lambda{ true },
|
352
508
|
:set => {
|
data/lib/vlad/enterprising.rb
CHANGED
@@ -4,7 +4,7 @@ require 'vlad/environmentalist'
|
|
4
4
|
require 'vlad/xen_master'
|
5
5
|
require 'vlad/dba'
|
6
6
|
require 'vlad/webmaster/apache'
|
7
|
-
VLAD_ENTERPRISING_VERSION = '0.1.
|
7
|
+
VLAD_ENTERPRISING_VERSION = '0.1.8' unless defined? VLAD_ENTERPRISING_VERSION
|
8
8
|
|
9
9
|
|
10
10
|
# rsync the given files to <tt>target_host</tt>.
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: VladTheEnterprising
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.1.8
|
7
|
+
date: 2007-11-02 00:00:00 -04:00
|
8
8
|
summary: Adds 'enterprisey' features to Vlad The Deployer
|
9
9
|
require_paths:
|
10
10
|
- lib
|
metadata.gz.sig
CHANGED
Binary file
|