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 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 FIX
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
@@ -4,6 +4,8 @@ require 'rubygems'
4
4
  require 'hoe'
5
5
  require 'vlad'
6
6
  require 'highline/import'
7
+ require './lib/nytd/dbslayer'
8
+
7
9
  require './lib/vlad/enterprising.rb'
8
10
  require './lib/nytd/builder'
9
11
  Hoe.new('VladTheEnterprising', VLAD_ENTERPRISING_VERSION) do |p|
data/lib/vlad/dba.rb CHANGED
@@ -1,10 +1,7 @@
1
1
  require 'vlad/dba/mysql'
2
- namespace :vlad do
3
- namespace :dba do
4
- task :assert_single_master do
5
- if Rake::RemoteTask.hosts_for(:db_master).size != 1
6
- raise "Error! This task demands one and only one host have the :db_master role assigned"
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
@@ -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
- mysql_dumpfile = ENV["DUMPFILE"] if ENV["DUMPFILE"]
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 => {
@@ -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' unless defined? VLAD_ENTERPRISING_VERSION
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.0
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
7
- date: 2007-10-17 00:00:00 -04:00
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