mysql-slaver 0.1.13 → 0.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.
- checksums.yaml +13 -5
- data/Gemfile +1 -1
- data/Gemfile.lock +23 -12
- data/README.md +1 -0
- data/Rakefile +4 -4
- data/bin/mysql_slaver-dev +6 -0
- data/lib/mysql_slaver/cli.rb +22 -18
- data/lib/mysql_slaver/db_copier.rb +6 -4
- data/lib/mysql_slaver/executor.rb +14 -3
- data/lib/mysql_slaver/master_changer.rb +2 -2
- data/lib/mysql_slaver/slaver.rb +25 -17
- data/lib/mysql_slaver/status_fetcher.rb +3 -3
- data/spec/mysql_slaver/db_copier_spec.rb +18 -8
- data/spec/mysql_slaver/executor_spec.rb +58 -2
- data/spec/mysql_slaver/master_changer_spec.rb +11 -11
- data/spec/mysql_slaver/slaver_spec.rb +44 -16
- data/spec/mysql_slaver/status_fetcher_spec.rb +7 -7
- data/spec/spec_helper.rb +2 -2
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YzdhZjFhOTkyMGFhMjk3NGNjMWRkZDRlOWZjNmZhZmQzNTA4MTVkMQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
N2IzYTBhODAwYzU2MjBmZGYwZDUxY2MwODg3YWVjNTk4MGYwM2E5Nw==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OWY0MjgyODk1ODk5YTc1ZmE4OTUxNTAwN2MwNGEwNDFiNjY4NTQ5NDhhZDY5
|
10
|
+
OGRkNGRhMGM4OGQ1NjIzODc0YzMwMjI1NmIyNWEwZDM3MmMxNjJiMTI2NjNk
|
11
|
+
MDdiNTI1NmQwODA2MzkyODM5ZWY3NjU5NzcyMTc3NDRlZDdhZmU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzQ3OWZmOWVmMTdjMmM5NzhhZTM4NDA3MWFjZmRjOWYzYTJlN2Y0YmJmNWMx
|
14
|
+
ODZlYTQ0MTFkMGY4ZTI1MjFiNDI3NDBhZjg2MDEzYWIwODlkODUyODAyMDQx
|
15
|
+
NDQ3NDMyMDhiMzM0ZWFmYzgxMDU1ZTJlYTFlZGQxODY3YmY0OTk=
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,34 +1,42 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
+
columnize (0.9.0)
|
5
|
+
debugger (1.6.8)
|
6
|
+
columnize (>= 0.3.1)
|
7
|
+
debugger-linecache (~> 1.2.0)
|
8
|
+
debugger-ruby_core_source (~> 1.3.5)
|
9
|
+
debugger-linecache (1.2.0)
|
10
|
+
debugger-ruby_core_source (1.3.8)
|
4
11
|
diff-lcs (1.2.5)
|
5
12
|
gem-this (0.3.7)
|
6
13
|
gemcutter (0.7.1)
|
7
|
-
json (1.8.
|
14
|
+
json (1.8.3)
|
8
15
|
rake (10.4.2)
|
9
16
|
rdoc (4.2.0)
|
10
17
|
json (~> 1.4)
|
11
18
|
rdoc-data (4.0.1)
|
12
19
|
rdoc (~> 4.0)
|
13
|
-
rspec (3.
|
14
|
-
rspec-core (~> 3.
|
15
|
-
rspec-expectations (~> 3.
|
16
|
-
rspec-mocks (~> 3.
|
17
|
-
rspec-core (3.2
|
18
|
-
rspec-support (~> 3.
|
19
|
-
rspec-expectations (3.
|
20
|
+
rspec (3.3.0)
|
21
|
+
rspec-core (~> 3.3.0)
|
22
|
+
rspec-expectations (~> 3.3.0)
|
23
|
+
rspec-mocks (~> 3.3.0)
|
24
|
+
rspec-core (3.3.2)
|
25
|
+
rspec-support (~> 3.3.0)
|
26
|
+
rspec-expectations (3.3.1)
|
20
27
|
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
-
rspec-support (~> 3.
|
22
|
-
rspec-mocks (3.2
|
28
|
+
rspec-support (~> 3.3.0)
|
29
|
+
rspec-mocks (3.3.2)
|
23
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
-
rspec-support (~> 3.
|
25
|
-
rspec-support (3.
|
31
|
+
rspec-support (~> 3.3.0)
|
32
|
+
rspec-support (3.3.0)
|
26
33
|
thor (0.19.1)
|
27
34
|
|
28
35
|
PLATFORMS
|
29
36
|
ruby
|
30
37
|
|
31
38
|
DEPENDENCIES
|
39
|
+
debugger
|
32
40
|
gem-this
|
33
41
|
gemcutter
|
34
42
|
rake
|
@@ -36,3 +44,6 @@ DEPENDENCIES
|
|
36
44
|
rdoc-data
|
37
45
|
rspec
|
38
46
|
thor
|
47
|
+
|
48
|
+
BUNDLED WITH
|
49
|
+
1.10.6
|
data/README.md
CHANGED
@@ -16,6 +16,7 @@ BLOGPOST
|
|
16
16
|
|
17
17
|
ASSUMPTIONS/PRE-REQUISITES
|
18
18
|
|
19
|
+
* ruby 1.9.3 or greater
|
19
20
|
* localhost is configured as a mysql replication slave
|
20
21
|
* the current localhost user can ssh to the db master
|
21
22
|
* any ssh config settings, other than a port number, required to access the master from localhost are set in a ~/.ssh/config file
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ RSpec::Core::RakeTask.new do |t|
|
|
9
9
|
end
|
10
10
|
|
11
11
|
|
12
|
-
task :
|
12
|
+
task default: ["spec"]
|
13
13
|
|
14
14
|
# This builds the actual gem. For details of what all these options
|
15
15
|
# mean, and other ones you can add, check the documentation here:
|
@@ -20,7 +20,7 @@ spec = Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
# Change these as appropriate
|
22
22
|
s.name = "mysql-slaver"
|
23
|
-
s.version = "0.
|
23
|
+
s.version = "0.2.0"
|
24
24
|
s.summary = "Setup mysql replication"
|
25
25
|
s.author = "David Salgado"
|
26
26
|
s.email = "david@digitalronin.com"
|
@@ -67,7 +67,7 @@ end
|
|
67
67
|
# - using bundler with a git source
|
68
68
|
# - building the gem without rake (i.e. gem build blah.gemspec)
|
69
69
|
# - maybe others?
|
70
|
-
task :
|
70
|
+
task package: :gemspec
|
71
71
|
|
72
72
|
# Generate documentation
|
73
73
|
RDoc::Task.new do |rd|
|
@@ -77,6 +77,6 @@ RDoc::Task.new do |rd|
|
|
77
77
|
end
|
78
78
|
|
79
79
|
desc 'Clear out RDoc and generated packages'
|
80
|
-
task :
|
80
|
+
task clean: [:clobber_rdoc, :clobber_package] do
|
81
81
|
rm "#{spec.name}.gemspec"
|
82
82
|
end
|
data/lib/mysql_slaver/cli.rb
CHANGED
@@ -2,27 +2,31 @@ module MysqlSlaver
|
|
2
2
|
class CLI < ::Thor
|
3
3
|
desc "enslave", "Start MySQL replication to this host from a master"
|
4
4
|
|
5
|
-
option :master_host, :
|
6
|
-
option :database, :
|
7
|
-
option :replication_user, :
|
8
|
-
option :replication_password, :
|
9
|
-
option :root_password,
|
10
|
-
option :port, :
|
11
|
-
option :ssh_port, :
|
12
|
-
option :sock,
|
13
|
-
option :no_copy, :
|
5
|
+
option :master_host, required: true, desc: "The server which will be the replication master, for this slave"
|
6
|
+
option :database, required: true, desc: "The database to copy from the master"
|
7
|
+
option :replication_user, required: true, desc: "DB user (on the master host), with replication permissions"
|
8
|
+
option :replication_password, required: true, desc: "DB password for the replication user"
|
9
|
+
option :root_password, desc: "Password for the mysql root user (on both master and slave)"
|
10
|
+
option :port, default: 3306, desc: "Mysql port"
|
11
|
+
option :ssh_port, default: 22, desc: "SSH port"
|
12
|
+
option :sock, desc: "Mysql socket file (if any)"
|
13
|
+
option :no_copy, type: :boolean, desc: "Do not copy data - just change master status"
|
14
|
+
option :tables, desc: "Space-separated list of tables to copy (defaults to entire database)"
|
15
|
+
option :dry_run, type: :boolean, desc: "Display commands without executing"
|
14
16
|
|
15
17
|
def enslave
|
16
18
|
MysqlSlaver::Slaver.new(
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
19
|
+
master_host: options[:master_host],
|
20
|
+
port: options[:port],
|
21
|
+
ssh_port: options[:ssh_port],
|
22
|
+
socket_file: options[:sock],
|
23
|
+
no_copy: options[:no_copy],
|
24
|
+
mysql_root_password: options[:root_password],
|
25
|
+
database: options[:database],
|
26
|
+
replication_user: options[:replication_user],
|
27
|
+
replication_password: options[:replication_password],
|
28
|
+
tables: options[:tables],
|
29
|
+
dry_run: options[:dry_run]
|
26
30
|
).enslave!
|
27
31
|
end
|
28
32
|
end
|
@@ -2,7 +2,7 @@ module MysqlSlaver
|
|
2
2
|
class DbCopier
|
3
3
|
include MysqlCommand
|
4
4
|
|
5
|
-
attr_accessor :master_host, :mysql_root_password, :database, :executor, :port, :socket_file
|
5
|
+
attr_accessor :master_host, :mysql_root_password, :database, :executor, :port, :socket_file, :tables
|
6
6
|
|
7
7
|
def initialize(params)
|
8
8
|
@master_host = params.fetch(:master_host)
|
@@ -10,7 +10,8 @@ module MysqlSlaver
|
|
10
10
|
@database = params.fetch(:database)
|
11
11
|
@port = params.fetch(:port, nil)
|
12
12
|
@socket_file = params.fetch(:socket_file, nil)
|
13
|
-
@executor = params.fetch(:executor) { Executor.new(:
|
13
|
+
@executor = params.fetch(:executor) { Executor.new(ssh_port: params[:ssh_port]) }
|
14
|
+
@tables = params.fetch(:tables, nil)
|
14
15
|
end
|
15
16
|
|
16
17
|
def copy!
|
@@ -26,8 +27,8 @@ module MysqlSlaver
|
|
26
27
|
|
27
28
|
def mysql_params
|
28
29
|
{
|
29
|
-
:
|
30
|
-
:socket_file
|
30
|
+
root_password: mysql_root_password,
|
31
|
+
socket_file: socket_file
|
31
32
|
}
|
32
33
|
end
|
33
34
|
|
@@ -36,6 +37,7 @@ module MysqlSlaver
|
|
36
37
|
rtn = %[mysqldump]
|
37
38
|
rtn << %[ -P #{port}] if port
|
38
39
|
rtn << %[ #{creds} -h #{master_host} --master-data --single-transaction --quick --skip-add-locks --skip-lock-tables --default-character-set=utf8 --compress #{database}]
|
40
|
+
rtn << %[ #{tables}] unless tables.to_s.empty?
|
39
41
|
rtn
|
40
42
|
end
|
41
43
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module MysqlSlaver
|
2
2
|
class Executor
|
3
|
-
attr_reader :ssh_port
|
3
|
+
attr_reader :ssh_port, :dry_run
|
4
4
|
|
5
5
|
include Logger
|
6
6
|
|
7
7
|
def initialize(params = {})
|
8
8
|
@ssh_port = params[:ssh_port]
|
9
|
+
@dry_run = params.fetch(:dry_run, false)
|
9
10
|
end
|
10
11
|
|
11
12
|
def ssh_command(cmd, host)
|
@@ -19,8 +20,18 @@ module MysqlSlaver
|
|
19
20
|
def execute(cmd)
|
20
21
|
string = cmd.is_a?(Array) ? cmd.join('; ') : cmd
|
21
22
|
log "CMD: #{string}"
|
22
|
-
|
23
|
-
|
23
|
+
if dry_run
|
24
|
+
"[DUMMY RESULT]"
|
25
|
+
else
|
26
|
+
result = `#{string}`
|
27
|
+
success? ? result : nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def success?
|
34
|
+
$?.success?
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
data/lib/mysql_slaver/slaver.rb
CHANGED
@@ -8,43 +8,51 @@ module MysqlSlaver
|
|
8
8
|
class Slaver
|
9
9
|
include Logger
|
10
10
|
|
11
|
-
attr_reader :status_fetcher, :data_copier, :master_changer, :no_copy
|
11
|
+
attr_reader :status_fetcher, :data_copier, :master_changer, :no_copy, :tables
|
12
12
|
|
13
13
|
def initialize(params)
|
14
14
|
mysql_root_password = params.fetch(:mysql_root_password, '')
|
15
15
|
port = params.fetch(:port, 3306)
|
16
16
|
ssh_port = params.fetch(:ssh_port, 22)
|
17
17
|
socket_file = params.fetch(:socket_file, nil)
|
18
|
+
tables = params.fetch(:tables, nil)
|
18
19
|
@no_copy = params.fetch(:no_copy, false)
|
19
20
|
|
21
|
+
dry_run = params.fetch(:dry_run, false)
|
22
|
+
executor = Executor.new(ssh_port: params[:ssh_port], dry_run: dry_run)
|
23
|
+
|
20
24
|
@status_fetcher = params.fetch(:status_fetcher) {
|
21
25
|
StatusFetcher.new(
|
22
|
-
:
|
23
|
-
:mysql_root_password
|
24
|
-
:socket_file
|
25
|
-
:ssh_port
|
26
|
+
master_host: params.fetch(:master_host),
|
27
|
+
mysql_root_password: mysql_root_password,
|
28
|
+
socket_file: socket_file,
|
29
|
+
ssh_port: ssh_port,
|
30
|
+
executor: executor
|
26
31
|
)
|
27
32
|
}
|
28
33
|
|
29
34
|
@data_copier = params.fetch(:data_copier) {
|
30
35
|
DbCopier.new(
|
31
|
-
:
|
32
|
-
:mysql_root_password
|
33
|
-
:
|
34
|
-
:port
|
35
|
-
:socket_file
|
36
|
-
:
|
36
|
+
master_host: params.fetch(:master_host),
|
37
|
+
mysql_root_password: mysql_root_password,
|
38
|
+
database: params.fetch(:database),
|
39
|
+
port: port,
|
40
|
+
socket_file: socket_file,
|
41
|
+
tables: tables,
|
42
|
+
ssh_port: ssh_port,
|
43
|
+
executor: executor
|
37
44
|
)
|
38
45
|
}
|
39
46
|
|
40
47
|
@master_changer = params.fetch(:master_changer) {
|
41
48
|
MasterChanger.new(
|
42
|
-
:
|
43
|
-
:mysql_root_password
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:port
|
47
|
-
:socket_file
|
49
|
+
master_host: params.fetch(:master_host),
|
50
|
+
mysql_root_password: mysql_root_password,
|
51
|
+
replication_user: params.fetch(:replication_user),
|
52
|
+
replication_password: params.fetch(:replication_password),
|
53
|
+
port: port,
|
54
|
+
socket_file: socket_file,
|
55
|
+
executor: executor
|
48
56
|
)
|
49
57
|
}
|
50
58
|
end
|
@@ -9,11 +9,11 @@ module MysqlSlaver
|
|
9
9
|
@master_host = params.fetch(:master_host)
|
10
10
|
@socket_file = params.fetch(:socket_file, nil)
|
11
11
|
@mysql_root_password = params.fetch(:mysql_root_password, '')
|
12
|
-
@executor = params.fetch(:executor) { Executor.new(:
|
12
|
+
@executor = params.fetch(:executor) { Executor.new(ssh_port: params[:ssh_port]) }
|
13
13
|
end
|
14
14
|
|
15
15
|
def status
|
16
|
-
params = {:
|
16
|
+
params = {root_password: mysql_root_password, socket_file: socket_file}
|
17
17
|
cmd = mysql_command("show master status\\G", params)
|
18
18
|
if data = executor.execute(executor.ssh_command(cmd, master_host))
|
19
19
|
rtn = parse data
|
@@ -37,7 +37,7 @@ module MysqlSlaver
|
|
37
37
|
position = $1
|
38
38
|
end
|
39
39
|
end
|
40
|
-
{:
|
40
|
+
{file: file, position: position}
|
41
41
|
end
|
42
42
|
|
43
43
|
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
require 'spec/spec_helper'
|
1
|
+
require './spec/spec_helper'
|
2
2
|
|
3
3
|
module MysqlSlaver
|
4
4
|
describe DbCopier do
|
5
|
-
let(:executor) { double(Executor, :
|
5
|
+
let(:executor) { double(Executor, execute: true, ssh_command: "dummy-ssh-command") }
|
6
6
|
|
7
7
|
let(:params) {
|
8
8
|
{
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:executor
|
9
|
+
master_host: 'my.db.host',
|
10
|
+
mysql_root_password: 'supersekrit',
|
11
|
+
database: 'myappdb',
|
12
|
+
executor: executor
|
13
13
|
}
|
14
14
|
}
|
15
15
|
subject(:copier) { described_class.new(params) }
|
@@ -37,7 +37,7 @@ module MysqlSlaver
|
|
37
37
|
end
|
38
38
|
|
39
39
|
context "with a non-standard mysql port" do
|
40
|
-
let(:params) { super().merge(:
|
40
|
+
let(:params) { super().merge(port: 3307) }
|
41
41
|
|
42
42
|
it "issues mysqldump over ssh" do
|
43
43
|
dump = "mysqldump -P 3307 -u root -p supersekrit -h my.db.host --master-data --single-transaction --quick --skip-add-locks --skip-lock-tables --default-character-set=utf8 --compress myappdb"
|
@@ -47,7 +47,7 @@ module MysqlSlaver
|
|
47
47
|
end
|
48
48
|
|
49
49
|
context "with a socket file" do
|
50
|
-
let(:params) { super().merge(:
|
50
|
+
let(:params) { super().merge(socket_file: "/tmp/mysql.sock") }
|
51
51
|
|
52
52
|
it "issues mysqldump over ssh" do
|
53
53
|
dump = "mysqldump -S /tmp/mysql.sock -u root -p supersekrit -h my.db.host --master-data --single-transaction --quick --skip-add-locks --skip-lock-tables --default-character-set=utf8 --compress myappdb"
|
@@ -62,5 +62,15 @@ module MysqlSlaver
|
|
62
62
|
end
|
63
63
|
|
64
64
|
end
|
65
|
+
|
66
|
+
context "with a list of tables" do
|
67
|
+
let(:params) { super().merge(tables: 'foo bar baz') }
|
68
|
+
|
69
|
+
it "lists tables in mysqldump command" do
|
70
|
+
dump = "mysqldump -u root -p supersekrit -h my.db.host --master-data --single-transaction --quick --skip-add-locks --skip-lock-tables --default-character-set=utf8 --compress myappdb foo bar baz"
|
71
|
+
expect(executor).to receive(:ssh_command).with(dump, 'my.db.host')
|
72
|
+
copier.copy!
|
73
|
+
end
|
74
|
+
end
|
65
75
|
end
|
66
76
|
end
|
@@ -1,12 +1,16 @@
|
|
1
|
-
require 'spec/spec_helper'
|
1
|
+
require './spec/spec_helper'
|
2
2
|
|
3
3
|
module MysqlSlaver
|
4
4
|
describe Executor do
|
5
5
|
let(:ssh_port) { nil }
|
6
|
-
let(:params) { {:
|
6
|
+
let(:params) { {ssh_port: ssh_port} }
|
7
7
|
|
8
8
|
subject(:executor) { described_class.new(params) }
|
9
9
|
|
10
|
+
before do
|
11
|
+
allow(executor).to receive(:log)
|
12
|
+
end
|
13
|
+
|
10
14
|
describe "#ssh_command" do
|
11
15
|
it "formats command" do
|
12
16
|
expect(executor.ssh_command("foo", "myhost")).to eq("ssh myhost 'foo'")
|
@@ -20,5 +24,57 @@ module MysqlSlaver
|
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
27
|
+
|
28
|
+
describe "#execute" do
|
29
|
+
let(:succeeded) { true }
|
30
|
+
|
31
|
+
before do
|
32
|
+
allow(executor).to receive(:success?).and_return(succeeded)
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when command succeeds" do
|
36
|
+
let(:succeeded) { true }
|
37
|
+
|
38
|
+
it "runs command in backticks" do
|
39
|
+
expect(executor).to receive(:`).with('ls')
|
40
|
+
executor.execute('ls')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns the command output" do
|
44
|
+
expect(executor).to receive(:`).with('ls').and_return("foo\nbar\nbaz")
|
45
|
+
expect(executor.execute('ls')).to eq("foo\nbar\nbaz")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when command fails" do
|
50
|
+
let(:succeeded) { false }
|
51
|
+
|
52
|
+
before do
|
53
|
+
allow(executor).to receive(:`)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "runs command in backticks" do
|
57
|
+
expect(executor).to receive(:`).with('ls')
|
58
|
+
executor.execute('ls')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns nil" do
|
62
|
+
expect(executor.execute('ls')).to be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "dry-run" do
|
67
|
+
let(:params) { super().merge(dry_run: true) }
|
68
|
+
|
69
|
+
it "doesn't run the command" do
|
70
|
+
expect(executor).to_not receive(:`)
|
71
|
+
executor.execute('ls')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns dummy result" do
|
75
|
+
expect(executor.execute('ls')).to eq("[DUMMY RESULT]")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
23
79
|
end
|
24
80
|
end
|
@@ -1,18 +1,18 @@
|
|
1
|
-
require 'spec/spec_helper'
|
1
|
+
require './spec/spec_helper'
|
2
2
|
|
3
3
|
module MysqlSlaver
|
4
4
|
describe MasterChanger do
|
5
|
-
let(:executor) { double(Executor, :
|
5
|
+
let(:executor) { double(Executor, execute: true) }
|
6
6
|
|
7
|
-
let(:status) { {:
|
7
|
+
let(:status) { {file: 'mysql-bin.001555', position: 18426246} }
|
8
8
|
let(:params) {
|
9
9
|
{
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:executor
|
10
|
+
master_host: 'my.db.host',
|
11
|
+
port: 3306,
|
12
|
+
mysql_root_password: 'supersekrit',
|
13
|
+
replication_user: 'repluser',
|
14
|
+
replication_password: 'replpassword',
|
15
|
+
executor: executor
|
16
16
|
}
|
17
17
|
}
|
18
18
|
subject(:changer) { described_class.new(params) }
|
@@ -26,7 +26,7 @@ module MysqlSlaver
|
|
26
26
|
end
|
27
27
|
|
28
28
|
context "with a non-standard mysql port" do
|
29
|
-
let(:params) { super().merge(:
|
29
|
+
let(:params) { super().merge(port: 3307) }
|
30
30
|
|
31
31
|
it "executes multi-part mysql command" do
|
32
32
|
change_cmd = %[mysql -u root -p supersekrit -e "stop slave; CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.001555', MASTER_LOG_POS=18426246, MASTER_HOST='my.db.host', MASTER_PORT=3307, MASTER_USER='repluser', MASTER_PASSWORD='replpassword'; start slave"]
|
@@ -36,7 +36,7 @@ module MysqlSlaver
|
|
36
36
|
end
|
37
37
|
|
38
38
|
context "with a mysql socket file" do
|
39
|
-
let(:params) { super().merge(:
|
39
|
+
let(:params) { super().merge(socket_file: "/tmp/mysql.sock") }
|
40
40
|
|
41
41
|
it "executes multi-part mysql command" do
|
42
42
|
change_cmd = %[mysql -S /tmp/mysql.sock -u root -p supersekrit -e "stop slave; CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.001555', MASTER_LOG_POS=18426246, MASTER_HOST='my.db.host', MASTER_PORT=3306, MASTER_USER='repluser', MASTER_PASSWORD='replpassword'; start slave"]
|
@@ -1,19 +1,19 @@
|
|
1
|
-
require 'spec/spec_helper'
|
1
|
+
require './spec/spec_helper'
|
2
2
|
|
3
3
|
module MysqlSlaver
|
4
4
|
describe Slaver do
|
5
5
|
subject(:slaver) { described_class.new(params) }
|
6
6
|
|
7
7
|
describe "#enslave!" do
|
8
|
-
let(:status_fetcher) { double(StatusFetcher, :
|
9
|
-
let(:data_copier) { double(DbCopier,
|
10
|
-
let(:master_changer) { double(MasterChanger,
|
8
|
+
let(:status_fetcher) { double(StatusFetcher, status: ()) }
|
9
|
+
let(:data_copier) { double(DbCopier, copy!: true) }
|
10
|
+
let(:master_changer) { double(MasterChanger, change!: true) }
|
11
11
|
|
12
12
|
let(:params) {
|
13
13
|
{
|
14
|
-
:status_fetcher
|
15
|
-
:data_copier
|
16
|
-
:master_changer
|
14
|
+
status_fetcher: status_fetcher,
|
15
|
+
data_copier: data_copier,
|
16
|
+
master_changer: master_changer
|
17
17
|
}
|
18
18
|
}
|
19
19
|
|
@@ -53,7 +53,7 @@ module MysqlSlaver
|
|
53
53
|
end
|
54
54
|
|
55
55
|
context "no-copy" do
|
56
|
-
let(:params) { super().merge(:
|
56
|
+
let(:params) { super().merge(no_copy: true) }
|
57
57
|
|
58
58
|
it "fetches master status" do
|
59
59
|
slaver.enslave!
|
@@ -75,11 +75,11 @@ module MysqlSlaver
|
|
75
75
|
context "instantiating collaborators" do
|
76
76
|
let(:params) {
|
77
77
|
{
|
78
|
-
:
|
79
|
-
:
|
80
|
-
:
|
81
|
-
:
|
82
|
-
:
|
78
|
+
master_host: 'my.db.host',
|
79
|
+
mysql_root_password: 'supersekrit',
|
80
|
+
database: 'myappdb',
|
81
|
+
replication_user: 'repluser',
|
82
|
+
replication_password: 'replpassword'
|
83
83
|
}
|
84
84
|
}
|
85
85
|
|
@@ -106,7 +106,7 @@ module MysqlSlaver
|
|
106
106
|
end
|
107
107
|
|
108
108
|
context "with non-standard mysql port" do
|
109
|
-
let(:params) { super().merge(:
|
109
|
+
let(:params) { super().merge(port: 3307) }
|
110
110
|
|
111
111
|
it "instantiates a master changer" do
|
112
112
|
changer = slaver.master_changer
|
@@ -115,7 +115,7 @@ module MysqlSlaver
|
|
115
115
|
end
|
116
116
|
|
117
117
|
context "with non-standard ssh port" do
|
118
|
-
let(:params) { super().merge(:
|
118
|
+
let(:params) { super().merge(ssh_port: 64389) }
|
119
119
|
|
120
120
|
it "instantiates a status fetcher" do
|
121
121
|
fetcher = slaver.status_fetcher
|
@@ -129,7 +129,7 @@ module MysqlSlaver
|
|
129
129
|
end
|
130
130
|
|
131
131
|
context "with mysql socket" do
|
132
|
-
let(:params) { super().merge(:
|
132
|
+
let(:params) { super().merge(socket_file: "/tmp/mysql.sock") }
|
133
133
|
|
134
134
|
it "instantiates a status fetcher" do
|
135
135
|
fetcher = slaver.status_fetcher
|
@@ -146,6 +146,34 @@ module MysqlSlaver
|
|
146
146
|
expect(copier.socket_file).to eq("/tmp/mysql.sock")
|
147
147
|
end
|
148
148
|
end
|
149
|
+
|
150
|
+
context "with a list of tables" do
|
151
|
+
let(:params) { super().merge(tables: 'foo bar baz') }
|
152
|
+
|
153
|
+
it "instantiates a data copier" do
|
154
|
+
copier = slaver.data_copier
|
155
|
+
expect(copier.tables).to eq('foo bar baz')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "dry-run" do
|
160
|
+
let(:params) { super().merge(dry_run: true) }
|
161
|
+
|
162
|
+
it "instantiates a status fetcher" do
|
163
|
+
fetcher = slaver.status_fetcher
|
164
|
+
expect(fetcher.executor.dry_run).to be_truthy
|
165
|
+
end
|
166
|
+
|
167
|
+
it "instantiates a master changer" do
|
168
|
+
changer = slaver.master_changer
|
169
|
+
expect(changer.executor.dry_run).to be_truthy
|
170
|
+
end
|
171
|
+
|
172
|
+
it "instantiates a data copier" do
|
173
|
+
copier = slaver.data_copier
|
174
|
+
expect(copier.executor.dry_run).to be_truthy
|
175
|
+
end
|
176
|
+
end
|
149
177
|
end
|
150
178
|
end
|
151
179
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
-
require 'spec/spec_helper'
|
1
|
+
require './spec/spec_helper'
|
2
2
|
|
3
3
|
module MysqlSlaver
|
4
4
|
describe StatusFetcher do
|
5
|
-
let(:executor) { double(Executor, :
|
5
|
+
let(:executor) { double(Executor, execute: "", ssh_command: "dummy-ssh-command") }
|
6
6
|
|
7
7
|
let(:params) {
|
8
8
|
{
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
9
|
+
master_host: 'my.db.host',
|
10
|
+
mysql_root_password: 'supersekrit',
|
11
|
+
executor: executor
|
12
12
|
}
|
13
13
|
}
|
14
14
|
subject(:fetcher) { described_class.new(params) }
|
@@ -52,13 +52,13 @@ EOF
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it "parses show master output" do
|
55
|
-
expect(fetcher.status).to eq({:
|
55
|
+
expect(fetcher.status).to eq({file: 'mysql-bin.003219', position: '37065270'})
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
60
|
context "using a socket filename" do
|
61
|
-
let(:params) { super().merge(:
|
61
|
+
let(:params) { super().merge(socket_file: "/var/run/mysqld/mysqld.master.sock") }
|
62
62
|
|
63
63
|
it "executes show master command over ssh" do
|
64
64
|
show_master = %[mysql -S /var/run/mysqld/mysqld.master.sock -u root -p supersekrit -e "show master status\\G"]
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,33 +1,34 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql-slaver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Salgado
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ! '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
description: Make a mysql server into a slave of another, optionally copying data
|
28
28
|
email: david@digitalronin.com
|
29
29
|
executables:
|
30
30
|
- mysql_slaver
|
31
|
+
- mysql_slaver-dev
|
31
32
|
extensions: []
|
32
33
|
extra_rdoc_files:
|
33
34
|
- README.md
|
@@ -37,6 +38,7 @@ files:
|
|
37
38
|
- README.md
|
38
39
|
- Rakefile
|
39
40
|
- bin/mysql_slaver
|
41
|
+
- bin/mysql_slaver-dev
|
40
42
|
- lib/mysql_slaver.rb
|
41
43
|
- lib/mysql_slaver/cli.rb
|
42
44
|
- lib/mysql_slaver/db_copier.rb
|
@@ -55,27 +57,27 @@ files:
|
|
55
57
|
- spec/spec_helper.rb
|
56
58
|
homepage: https://digitalronin.github.io/2014/04/16/mysql-slaver-gem-setup-mysql-replication/
|
57
59
|
licenses:
|
58
|
-
-
|
60
|
+
- ! '[MIT]'
|
59
61
|
metadata: {}
|
60
62
|
post_install_message:
|
61
63
|
rdoc_options:
|
62
|
-
-
|
64
|
+
- --main
|
63
65
|
- README.md
|
64
66
|
require_paths:
|
65
67
|
- lib
|
66
68
|
required_ruby_version: !ruby/object:Gem::Requirement
|
67
69
|
requirements:
|
68
|
-
- -
|
70
|
+
- - ! '>='
|
69
71
|
- !ruby/object:Gem::Version
|
70
72
|
version: '0'
|
71
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
74
|
requirements:
|
73
|
-
- -
|
75
|
+
- - ! '>='
|
74
76
|
- !ruby/object:Gem::Version
|
75
77
|
version: '0'
|
76
78
|
requirements: []
|
77
79
|
rubyforge_project:
|
78
|
-
rubygems_version: 2.4.
|
80
|
+
rubygems_version: 2.4.6
|
79
81
|
signing_key:
|
80
82
|
specification_version: 4
|
81
83
|
summary: Setup mysql replication
|