mysql-slaver 0.1.13 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|