db_helper 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/db_helper.gemspec +27 -0
- data/lib/db_helper.rb +6 -0
- data/lib/db_helper/command.rb +8 -0
- data/lib/db_helper/mysql_interface.rb +49 -0
- data/lib/db_helper/railtie.rb +13 -0
- data/lib/db_helper/sandbox.rb +40 -0
- data/lib/db_helper/sandbox_manager.rb +43 -0
- data/lib/db_helper/sql_importer.rb +159 -0
- data/lib/db_helper/tasks.rb +230 -0
- data/lib/db_helper/version.rb +3 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f401f8ce01038c7b94346e597ffed0a3235bc7e1
|
4
|
+
data.tar.gz: a627c6d5dde59bec96506c78fffdfc56d7a96f3a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 478f1a496e29c43ce56ecda0f1c1bbd1966900b8fbcdefa191b1a650d0a43f18b09876018d182bf3cfcd3896c8b6ff4a8c009404b8bfdbf6d5188e88cb531ff5
|
7
|
+
data.tar.gz: 3d8d9fa0f99df7f649c8252e986b7054cd3472096b5d701e11d8d1b1ddf00d9f038d1be293c6ed9e9fe8549b42f726b25e3369017c3e291a4c0dd27c69882f4f
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Michael Noack
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# DbHelper
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'db_helper'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install db_helper
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/db_helper.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'db_helper/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "db_helper"
|
8
|
+
spec.version = DbHelper::VERSION
|
9
|
+
spec.authors = ["Michael Noack"]
|
10
|
+
spec.email = ["michael@noack.com.au"]
|
11
|
+
spec.description = "Database rake tasks for rails apps"
|
12
|
+
spec.summary = "Extracted rake tasks from large rails apps"
|
13
|
+
spec.homepage = "https://github.com/sealink/db_helper"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'input_reader'
|
22
|
+
spec.add_dependency 'activesupport'
|
23
|
+
spec.add_dependency 'sys-proctable'
|
24
|
+
spec.add_dependency 'parseconfig'
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
end
|
data/lib/db_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'active_support/core_ext/hash'
|
2
|
+
require 'db_helper/command'
|
3
|
+
include Command
|
4
|
+
|
5
|
+
class MysqlInterface
|
6
|
+
attr_accessor :options, :config
|
7
|
+
def initialize(config)
|
8
|
+
@config = config.symbolize_keys
|
9
|
+
@config[:username] ||= 'root'
|
10
|
+
@options = "--user=#{@config[:username]}"
|
11
|
+
[:password,:host,:port,:socket].each do |opt|
|
12
|
+
@options += " --#{opt}=#{@config[opt]}" unless @config[opt].blank?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def database
|
17
|
+
@config[:database]
|
18
|
+
end
|
19
|
+
|
20
|
+
def drop_and_create
|
21
|
+
puts "0. Dropping and creating database"
|
22
|
+
begin
|
23
|
+
command "mysqladmin #{@options} drop --force #{database}"
|
24
|
+
rescue
|
25
|
+
puts "database doesn't exist. Skipping drop"
|
26
|
+
end
|
27
|
+
command "mysqladmin #{@options} create #{database}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def import(file)
|
31
|
+
command "mysql #{@options} #{database} < #{file.gsub('.gz', '')}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def csv_import(csv_file_full_path, columns, csv_options = {})
|
35
|
+
csv_options['fields-terminated-by'] ||= "','"
|
36
|
+
csv_options['fields-enclosed-by'] ||= "'\"'"
|
37
|
+
csv_options['lines-terminated-by'] ||= "'\n'"
|
38
|
+
csv_options['ignore-lines'] = '1' # Ignore first line so it can be used to show columns
|
39
|
+
`mysqlimport #{@options} #{database} \
|
40
|
+
#{csv_options.map{|k,v| "--#{k}=#{v}"}.join(' ')} \
|
41
|
+
--columns='#{columns.join(',')}' \
|
42
|
+
--local \
|
43
|
+
#{csv_file_full_path}`
|
44
|
+
end
|
45
|
+
|
46
|
+
def admin
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'db_helper/command'
|
2
|
+
|
3
|
+
class Sandbox
|
4
|
+
def initialize(dir)
|
5
|
+
@dir = dir
|
6
|
+
end
|
7
|
+
|
8
|
+
def import(file)
|
9
|
+
if file.end_with?('tar.gz')
|
10
|
+
import_innobackup(file)
|
11
|
+
elsif file.end_with?('sql.gz')
|
12
|
+
import_sql(file)
|
13
|
+
else
|
14
|
+
raise "Unrecognised file format for #{file}."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def import_sql(file)
|
19
|
+
command "gunzip #{File.join(@dir, file)}"
|
20
|
+
command File.join(@dir, 'start')
|
21
|
+
db_name = file.gsub(/_[0-9]{4}.*/, '')
|
22
|
+
command "#{File.join(@dir, 'use')} #{db_name} < #{File.join(@dir, file.sub('.gz',''))}"
|
23
|
+
command "rm #{File.join(@dir, file.sub('.gz',''))}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def import_innobackup(file)
|
27
|
+
if File.exist?(data_new = File.join(@dir, 'data.new'))
|
28
|
+
command "rm -rf #{data_new}"
|
29
|
+
end
|
30
|
+
command File.join(@dir,'start')
|
31
|
+
command "mkdir #{data_new}"
|
32
|
+
command "mv #{File.join(@dir, file)} #{File.join(data_new, file)}"
|
33
|
+
command "cd #{data_new} && tar -xzf #{file}"
|
34
|
+
command "rm #{File.join(data_new, file)}"
|
35
|
+
command File.join(@dir,'stop')
|
36
|
+
command "rm -rf #{File.join(@dir,'data')}"
|
37
|
+
command "mv #{data_new} #{File.join(@dir,'data')}"
|
38
|
+
command File.join(@dir,'start')
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'sys/proctable'
|
2
|
+
require 'parseconfig'
|
3
|
+
|
4
|
+
include Sys
|
5
|
+
|
6
|
+
class SandboxManager
|
7
|
+
def initialize(dir)
|
8
|
+
@dir = dir
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def self.default_sandboxes_base_dir
|
13
|
+
configuration = Rails.configuration.database_configuration[Rails.env]
|
14
|
+
suffixes = Rails.configuration.database_configuration.keys
|
15
|
+
current_db = configuration['database']
|
16
|
+
|
17
|
+
while suffix = suffixes.pop
|
18
|
+
current_db = current_db.gsub("_#{suffix}",'')
|
19
|
+
end
|
20
|
+
|
21
|
+
sandbox_proc = Sys::ProcTable.ps.find { |p| p.comm == 'mysqld' && p.cwd.to_s.include?(current_db) }
|
22
|
+
|
23
|
+
if sandbox_proc
|
24
|
+
sandbox_proc.cwd.gsub(File.join('', current_db, 'data'), '')
|
25
|
+
else
|
26
|
+
File.join('', 'db')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def configurations
|
32
|
+
Dir[File.join(@dir,'**','my.sandbox.cnf')].map.with_object({}) do |config_file, hash|
|
33
|
+
config = ParseConfig.new(config_file)
|
34
|
+
sandbox_name = Pathname.new(config_file).split.first.split.last.to_s
|
35
|
+
hash[sandbox_name] = config
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def ports
|
41
|
+
configurations.map.with_object({}) { |(sandbox, conf), hash| hash[sandbox] = conf['client']['port']}
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'input_reader'
|
2
|
+
require 'db_helper/command'
|
3
|
+
require 'db_helper/mysql_interface'
|
4
|
+
|
5
|
+
class SqlImporter
|
6
|
+
attr_accessor :selected, :default
|
7
|
+
|
8
|
+
def initialize(selected, default)
|
9
|
+
@selected = selected
|
10
|
+
@default = default
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def backup
|
15
|
+
if selected['hot_backup_dir']
|
16
|
+
hot_backup(selected, default)
|
17
|
+
else
|
18
|
+
cold_backup(selected, default)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def hot_backup
|
24
|
+
sandbox_dir = selected['sandbox_dir']
|
25
|
+
#command "mkdir #{sandbox_dir}/data.new && cd #{sandbox_dir}/data.new"
|
26
|
+
#command "cp #{selected['hot_backup_dir']}/*.tar.gz #{sandbox_dir}/data.new/remote-hot-backup.tar.gz"
|
27
|
+
#command "cd #{sandbox_dir}/data.new && tar -xzf remote-hot-backup.tar.gz"
|
28
|
+
command "#{sandbox_dir}/stop"
|
29
|
+
command "rm -rf #{sandbox_dir}/data"
|
30
|
+
command "mv #{sandbox_dir}/data.new #{sandbox_dir}/data"
|
31
|
+
command "#{sandbox_dir}/start"
|
32
|
+
end
|
33
|
+
|
34
|
+
def cold_backup(selected, default)
|
35
|
+
src_db_config = default['db'].merge(selected['db'] || {})
|
36
|
+
|
37
|
+
selected['ssh'] ||= {}
|
38
|
+
ssh_host = selected['ssh']['host'] || selected['host'] || default['host']
|
39
|
+
ssh_port = selected['ssh']['port'] || default['ssh']['port']
|
40
|
+
ssh_user = selected['ssh']['user'] || default['ssh']['user']
|
41
|
+
app_root = selected['app_root'] || default['app_root']
|
42
|
+
|
43
|
+
app_db_config = selected['app_db_config'] || default['app_db_config']
|
44
|
+
|
45
|
+
excluded_tables = Array.wrap(selected.has_key?('excluded_tables') ? selected['excluded_tables'] : default['excluded_tables'])
|
46
|
+
included_tables = Array.wrap(selected.has_key?('included_tables') ? selected['included_tables'] : default['included_tables'])
|
47
|
+
|
48
|
+
app_root ||= "/home/#{ssh_user}/#{src_db_config['database'].to_s.gsub('_production', '')}/current"
|
49
|
+
|
50
|
+
db_config = selected
|
51
|
+
if app_db_config
|
52
|
+
yaml = command("ssh -p #{ssh_port} #{ssh_user}@#{ssh_host} \"cat #{app_root}/config/database.yml\"")
|
53
|
+
db_config = YAML::load(yaml)
|
54
|
+
app_db_config_hash = db_config[app_db_config]
|
55
|
+
|
56
|
+
if app_db_config_hash
|
57
|
+
src_db_config.merge!(app_db_config_hash)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
table_choices = []
|
62
|
+
table_choices << {:option => :all, :message => "All tables"}
|
63
|
+
table_choices << {:option => :exclude, :message => "All except #{excluded_tables.to_sentence}"} unless excluded_tables.empty?
|
64
|
+
table_choices << {:option => :include, :message => "Only #{included_tables.to_sentence}"} unless included_tables.empty?
|
65
|
+
table_choices << {:option => :custom, :message => "Custom select tables"}
|
66
|
+
|
67
|
+
table_choices.each.with_index do |tc,i|
|
68
|
+
puts "#{i + 1}. #{tc[:message]}"
|
69
|
+
end
|
70
|
+
|
71
|
+
table_choice = table_choices[get_int(:valid_values => (1..table_choices.count).to_a) - 1][:option]
|
72
|
+
case table_choice
|
73
|
+
when :all
|
74
|
+
included_tables, excluded_tables = [[],[]]
|
75
|
+
when :exclude
|
76
|
+
included_tables = []
|
77
|
+
when :include
|
78
|
+
excluded_tables = []
|
79
|
+
when :custom
|
80
|
+
included_tables, excluded_tables = [get_input(:prompt => "Which tables to select (comma separated)").split(',').map(&:strip), []]
|
81
|
+
end
|
82
|
+
|
83
|
+
time_string = DateTime.current.strftime('%Y%m%d-%H%M')
|
84
|
+
file_prefix = "#{src_db_config['database']}_#{time_string}"
|
85
|
+
data_file_name = "#{file_prefix}_data.sql"
|
86
|
+
structure_file_name = "#{file_prefix}_structure_for_empty_tables.sql" if included_tables.empty? && !excluded_tables.empty?
|
87
|
+
|
88
|
+
db_config = YAML::load(File.open(File.join(ENV['RAILS_ROOT'] || '.', File.join('config','database.yml'))))
|
89
|
+
db_config.delete_if { |config,db| db['database'].blank? }
|
90
|
+
|
91
|
+
puts "Destination database?"
|
92
|
+
db_config.each.with_index do |source_db,i|
|
93
|
+
puts "#{i + 1}. #{source_db.first} - (#{source_db.last['database']})"
|
94
|
+
end
|
95
|
+
puts "#{db_config.size + 1}. Original (#{src_db_config['database']})"
|
96
|
+
puts "#{db_config.size + 2}. Custom select database"
|
97
|
+
choice = get_int(:valid_values => (1..db_config.size + 2).to_a) - 1
|
98
|
+
destination_db_config = if choice < db_config.size
|
99
|
+
db_config.to_a[choice] && db_config.to_a[choice].last
|
100
|
+
else
|
101
|
+
puts "Database options:"
|
102
|
+
{
|
103
|
+
:database => choice == db_config.size ? src_db_config['database'] : get_input(:prompt => "Database: ", :allow_blank => false),
|
104
|
+
:username => get_input(:prompt => "Username: ", :allow_blank => true),
|
105
|
+
:password => get_input(:prompt => "Password: ", :allow_blank => true),
|
106
|
+
:port => get_input(:prompt => "Port: ", :allow_blank => true),
|
107
|
+
:host => get_input(:prompt => "Host: ", :allow_blank => true),
|
108
|
+
:socket => get_input(:prompt => "Socket: ", :allow_blank => true)
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
src_sql = MysqlInterface.new(src_db_config)
|
113
|
+
dst_sql = MysqlInterface.new(destination_db_config)
|
114
|
+
dst_sql.drop_and_create if included_tables.empty?
|
115
|
+
|
116
|
+
puts "1. Backing up database"
|
117
|
+
|
118
|
+
mysql_cmd = "mysqldump #{src_sql.options} #{src_sql.database}"
|
119
|
+
|
120
|
+
dump_data_cmd = "#{mysql_cmd} %s %s > %s" % [
|
121
|
+
included_tables.map{|t| " #{t}"}.join,
|
122
|
+
excluded_tables.map{|t| " --ignore-table=#{src_sql.database}.#{t}"}.join,
|
123
|
+
"/tmp/#{data_file_name}"
|
124
|
+
]
|
125
|
+
|
126
|
+
compress_data_cmd = "gzip '/tmp/#{data_file_name}'"
|
127
|
+
|
128
|
+
dump_structure_cmd = "#{mysql_cmd} --no-data --tables #{excluded_tables.join(" ")} > /tmp/#{structure_file_name}" if included_tables.empty? && !excluded_tables.empty?
|
129
|
+
|
130
|
+
[dump_data_cmd, dump_structure_cmd, compress_data_cmd].compact.each do |dump_cmd|
|
131
|
+
command "ssh -p #{ssh_port} #{ssh_user}@#{ssh_host} \"#{dump_cmd}\""
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
puts "2. Coping & Decompressing SQL Files..."
|
136
|
+
[data_file_name + ".gz", structure_file_name].compact.each_with_index do |f, index|
|
137
|
+
command "scp -P #{ssh_port} #{ssh_user}@#{ssh_host}:/tmp/#{f} #{f}"
|
138
|
+
command("ssh -p #{ssh_port} #{ssh_user}@#{ssh_host} \"rm /tmp/#{f}\"")
|
139
|
+
command "gunzip #{f}" if f.end_with?('.gz')
|
140
|
+
end
|
141
|
+
|
142
|
+
puts "3. Importing SQL files..."
|
143
|
+
[data_file_name + ".gz", structure_file_name].compact.each_with_index do |f, index|
|
144
|
+
dst_sql.import(f.gsub('.gz', ''))
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def get_input(options = {})
|
150
|
+
options[:prompt] ||= "Enter Choice: "
|
151
|
+
InputReader.get_input(options)
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def get_int(options = {})
|
156
|
+
options[:prompt] ||= "Enter Choice: "
|
157
|
+
InputReader.get_int(options)
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'input_reader'
|
2
|
+
require 'sys/proctable'
|
3
|
+
require 'db_helper/command'
|
4
|
+
require 'db_helper/sandbox'
|
5
|
+
require 'db_helper/sql_importer'
|
6
|
+
require 'db_helper/sandbox_manager'
|
7
|
+
|
8
|
+
include Command
|
9
|
+
include Sys
|
10
|
+
|
11
|
+
namespace :db do
|
12
|
+
desc "Copy and import a remote database to a local database"
|
13
|
+
task :import_from_remote do
|
14
|
+
begin
|
15
|
+
sources = YAML::load(File.open(File.join(ENV['RAILS_ROOT'] || '.', File.join('config','database_import.yml'))))
|
16
|
+
default = sources.delete('default') || {:db => {}}
|
17
|
+
|
18
|
+
puts "Select database?"
|
19
|
+
|
20
|
+
sources.values.each.with_index do |source,i|
|
21
|
+
puts "#{i + 1}. #{source['name']}"
|
22
|
+
end
|
23
|
+
|
24
|
+
num_selected = InputReader.get_int(prompt: 'Enter Choice: ', valid_values: (1..sources.count).to_a)
|
25
|
+
selected = sources.values[num_selected - 1]
|
26
|
+
|
27
|
+
importer = SqlImporter.new(selected, default)
|
28
|
+
importer.backup
|
29
|
+
|
30
|
+
post_execution_commands = Array.wrap(selected.has_key?('post_execution_commands') ? selected['post_execution_commands'] : default['post_execution_commands'])
|
31
|
+
|
32
|
+
if !post_execution_commands.empty?
|
33
|
+
puts "Post execution commands"
|
34
|
+
post_execution_commands.each.with_index { |c,i| puts "#{i + 1}. #{c}" }
|
35
|
+
if InputReader.get_boolean(:prompt => "Run post execution commands (careful what you do, the command strings will be evaluated and executed!) (y/n)?")
|
36
|
+
puts "Running post execution commands"
|
37
|
+
post_execution_commands.each do |c|
|
38
|
+
eval c
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
puts "Done"
|
45
|
+
#rescue Exception => e
|
46
|
+
# puts "Error: #{e.message}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO : Clean up this mess.
|
51
|
+
desc "Import remote database from hot backup into sandbox"
|
52
|
+
task :update_sandbox do
|
53
|
+
|
54
|
+
db_config, env = if defined?(Rails)
|
55
|
+
[Rails.configuration.database_configuration, Rails.env]
|
56
|
+
else
|
57
|
+
[YAML::load(File.read(ENV['YML'])), ENV['ENV']]
|
58
|
+
end
|
59
|
+
|
60
|
+
configuration = db_config[env]
|
61
|
+
suffixes = db_config.keys
|
62
|
+
default_db = configuration['database']
|
63
|
+
|
64
|
+
while suffix = suffixes.pop
|
65
|
+
default_db = default_db.gsub("_#{suffix}",'')
|
66
|
+
end
|
67
|
+
|
68
|
+
backup_db = default_db.gsub('_','-')
|
69
|
+
|
70
|
+
db = InputReader.get_string(prompt: "Database (#{backup_db}):", default_value: backup_db)
|
71
|
+
|
72
|
+
default_backup_dir = 'latest'
|
73
|
+
backup_dir = InputReader.get_string(prompt: "Backup directory (#{default_backup_dir}):", default_value: default_backup_dir)
|
74
|
+
backup_db = InputReader.get_string(prompt: "Backup database (#{db}):", default_value: db)
|
75
|
+
default_hot_backup_dir_base = File.join('','media','backup','mysql')
|
76
|
+
default_hot_backup_dir = File.join(default_hot_backup_dir_base, "#{backup_db}", "#{backup_dir}")
|
77
|
+
hot_backup_dir = InputReader.get_string(prompt: "Backup path (#{default_hot_backup_dir}):", default_value: default_hot_backup_dir)
|
78
|
+
raise "#{hot_backup_dir} is not a valid path" unless File.directory?(hot_backup_dir)
|
79
|
+
|
80
|
+
sandbox_proc = Sys::ProcTable.ps.find { |p| p.comm == 'mysqld' &&
|
81
|
+
( p.respond_to?(:cwd) ? p.cwd.to_s.include?(default_db) : p.cmdline.include?(default_db)
|
82
|
+
) }
|
83
|
+
|
84
|
+
default_sandbox_dir = if sandbox_proc
|
85
|
+
sandbox_proc.cwd.gsub(File.join('','data'),'')
|
86
|
+
else
|
87
|
+
File.join('','db',db)
|
88
|
+
end
|
89
|
+
|
90
|
+
sandbox_dir = InputReader.get_string(prompt: "Sandbox path (#{default_sandbox_dir}):", default_value: default_sandbox_dir)
|
91
|
+
raise "#{sandbox_dir} is not a valid path" unless File.directory?(sandbox_dir)
|
92
|
+
|
93
|
+
backup_files = Dir[File.join(hot_backup_dir,'*.gz')]
|
94
|
+
raise "No backup files found" if backup_files.empty?
|
95
|
+
backup_file_path = InputReader.select_item(backup_files, prompt: "Select backup file:")
|
96
|
+
|
97
|
+
command "rsync -avP #{File.join(backup_file_path)} #{sandbox_dir}"
|
98
|
+
|
99
|
+
backup_file = File.basename(backup_file_path)
|
100
|
+
|
101
|
+
sandbox = Sandbox.new(sandbox_dir)
|
102
|
+
sandbox.import(backup_file)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Given a set of database yml files in the config directory named following the pattern
|
107
|
+
# database.#{database_name}.yml (e.g.: database.sealink.yml), it will allow swapping the
|
108
|
+
# current database configuration with one of the specific database configurations
|
109
|
+
# and start the related sandbox server
|
110
|
+
desc "Swap database config files"
|
111
|
+
task :swap_config do
|
112
|
+
databases = []
|
113
|
+
config_dir = Dir.new(File.join(Rails.root,'config'))
|
114
|
+
config_dir.each do |f|
|
115
|
+
match = f.scan(/database\.(.*)\.yml/).presence
|
116
|
+
databases << match.flatten.first if match
|
117
|
+
end
|
118
|
+
database = InputReader.select_item(databases, :prompt => "Select database:")
|
119
|
+
selected_database_config_file = File.join(config_dir,"database.#{database}.yml")
|
120
|
+
database_config_file = File.join(config_dir,"database.yml")
|
121
|
+
command "cp #{selected_database_config_file} #{database_config_file}"
|
122
|
+
puts "Swapped database config file"
|
123
|
+
|
124
|
+
if InputReader.get_boolean(:prompt => "Start #{database} sandbox server? (Y/N):")
|
125
|
+
default_sandbox_base_dir = File.join('','home','projects','db')
|
126
|
+
sandbox_base_dir = InputReader.get_string(:prompt => "Sandbox base directory (#{default_sandbox_base_dir}):", :default_value => default_sandbox_base_dir)
|
127
|
+
default_sandbox_dir = File.join("#{sandbox_base_dir}","#{database}")
|
128
|
+
sandbox_dir = InputReader.get_string(:prompt => "Full sandbox directory (#{default_sandbox_dir}):", :default_value => default_sandbox_dir)
|
129
|
+
command "#{sandbox_dir}/start"
|
130
|
+
puts "Started sandbox server"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
desc 'Create sandbox'
|
136
|
+
task :create_sandbox do
|
137
|
+
default_sandboxes_base_dir = SandboxManager.default_sandboxes_base_dir
|
138
|
+
sandboxes_dir = InputReader.get_string(prompt: "Sandboxes path (#{default_sandboxes_base_dir}):", default_value: default_sandboxes_base_dir)
|
139
|
+
sm = SandboxManager.new(sandboxes_dir)
|
140
|
+
ports = sm.ports
|
141
|
+
sandboxes = ports.keys.sort_by { |sandbox| ports[sandbox] }
|
142
|
+
|
143
|
+
puts "Existing sandboxes:"
|
144
|
+
sandboxes.each.with_index do |sandbox, i|
|
145
|
+
puts "#{i + 1}. #{sandbox.foreground(:cyan)}: #{ports[sandbox].foreground(:green)}"
|
146
|
+
end
|
147
|
+
|
148
|
+
sandbox_name = InputReader.get_string(prompt: 'Sandbox name:')
|
149
|
+
|
150
|
+
used_ports = ports.values.map(&:to_i)
|
151
|
+
sandbox_port = nil
|
152
|
+
while sandbox_port.nil? do
|
153
|
+
suggested_ports = (used_ports.min..used_ports.max).to_a - used_ports
|
154
|
+
puts "Suggested free ports: #{suggested_ports.join(', ')}"
|
155
|
+
chosen_port = InputReader.get_int(prompt: 'Sandbox port:')
|
156
|
+
if used_ports.include?(chosen_port)
|
157
|
+
puts "Port #{chosen_port} is already taken!"
|
158
|
+
else
|
159
|
+
sandbox_port = chosen_port
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
command "cd #{sandboxes_dir} && ./install_sandbox #{sandbox_name} #{sandbox_port}"
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
desc 'Change sandbox port'
|
168
|
+
task :change_sandbox_port do
|
169
|
+
default_sandboxes_base_dir = SandboxManager.default_sandboxes_base_dir
|
170
|
+
sandboxes_dir = InputReader.get_string(prompt: "Sandboxes path (#{default_sandboxes_base_dir}):", default_value: default_sandboxes_base_dir)
|
171
|
+
sm = SandboxManager.new(sandboxes_dir)
|
172
|
+
|
173
|
+
change_port = true
|
174
|
+
while change_port do
|
175
|
+
ports = sm.ports
|
176
|
+
|
177
|
+
selection_proc = ->(sandbox) { "#{sandbox.foreground(:cyan)}: #{ports[sandbox].foreground(:green)}" }
|
178
|
+
sandboxes = ports.keys.sort_by { |sandbox| ports[sandbox] }
|
179
|
+
selected_sandbox = InputReader.select_item(sandboxes,
|
180
|
+
selection_attribute: selection_proc,
|
181
|
+
prompt: 'Which sandbox port would you like to change?')
|
182
|
+
path_to_sandbox = File.join(sandboxes_dir, selected_sandbox)
|
183
|
+
|
184
|
+
used_ports = ports.values.map(&:to_i)
|
185
|
+
new_port = nil
|
186
|
+
while new_port.nil? do
|
187
|
+
suggested_ports = (used_ports.min..used_ports.max).to_a - used_ports
|
188
|
+
puts "Suggested free ports: #{suggested_ports.join(', ')}"
|
189
|
+
chosen_port = InputReader.get_int(prompt: 'Enter new port:')
|
190
|
+
if used_ports.include?(chosen_port)
|
191
|
+
puts "Port #{chosen_port} is already taken!"
|
192
|
+
else
|
193
|
+
new_port = chosen_port
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
puts "Changing port for #{selected_sandbox} from #{ports[selected_sandbox]} to #{new_port}:"
|
198
|
+
command "cd #{sandboxes_dir} && sbtool -o port -s #{path_to_sandbox} --new_port=#{new_port}"
|
199
|
+
|
200
|
+
if InputReader.get_boolean(prompt: "Start #{selected_sandbox} sandbox? (y/n):")
|
201
|
+
command File.join(path_to_sandbox, 'start')
|
202
|
+
end
|
203
|
+
|
204
|
+
change_port = InputReader.get_boolean(prompt: 'Change another port (y/n)?')
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
desc 'Remove sandbox'
|
210
|
+
task :remove_sandbox do
|
211
|
+
default_sandboxes_base_dir = SandboxManager.default_sandboxes_base_dir
|
212
|
+
sandboxes_dir = InputReader.get_string(prompt: "Sandboxes path (#{default_sandboxes_base_dir}):", default_value: default_sandboxes_base_dir)
|
213
|
+
sm = SandboxManager.new(sandboxes_dir)
|
214
|
+
ports = sm.ports
|
215
|
+
selection_proc = ->(sandbox) { "#{sandbox.foreground(:cyan)}: #{ports[sandbox].foreground(:green)}" }
|
216
|
+
sandboxes = ports.keys.sort_by { |sandbox| ports[sandbox] }
|
217
|
+
selected_sandbox = InputReader.select_item(sandboxes,
|
218
|
+
selection_attribute: selection_proc,
|
219
|
+
prompt: 'Which sandbox would you like to delete?')
|
220
|
+
path_to_sandbox = File.join(sandboxes_dir, selected_sandbox)
|
221
|
+
|
222
|
+
if InputReader.get_boolean(prompt: "Are you sure you want do delete #{selected_sandbox} sandbox? (y/n):".foreground(:red))
|
223
|
+
puts "Deleting sandbox #{selected_sandbox}:"
|
224
|
+
command "cd #{sandboxes_dir} && sbtool -o delete -s #{path_to_sandbox}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
end
|
230
|
+
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: db_helper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Noack
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: input_reader
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sys-proctable
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: parseconfig
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Database rake tasks for rails apps
|
98
|
+
email:
|
99
|
+
- michael@noack.com.au
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".ruby-version"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- db_helper.gemspec
|
111
|
+
- lib/db_helper.rb
|
112
|
+
- lib/db_helper/command.rb
|
113
|
+
- lib/db_helper/mysql_interface.rb
|
114
|
+
- lib/db_helper/railtie.rb
|
115
|
+
- lib/db_helper/sandbox.rb
|
116
|
+
- lib/db_helper/sandbox_manager.rb
|
117
|
+
- lib/db_helper/sql_importer.rb
|
118
|
+
- lib/db_helper/tasks.rb
|
119
|
+
- lib/db_helper/version.rb
|
120
|
+
homepage: https://github.com/sealink/db_helper
|
121
|
+
licenses:
|
122
|
+
- MIT
|
123
|
+
metadata: {}
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 2.4.5
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: Extracted rake tasks from large rails apps
|
144
|
+
test_files: []
|