flydata 0.3.24 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +3 -0
- data/VERSION +1 -1
- data/flydata.gemspec +36 -3
- data/lib/flydata.rb +8 -0
- data/lib/flydata/api/agent.rb +21 -0
- data/lib/flydata/command/helper.rb +154 -0
- data/lib/flydata/command/mysql.rb +37 -0
- data/lib/flydata/command/restart.rb +11 -0
- data/lib/flydata/command/start.rb +12 -2
- data/lib/flydata/command/status.rb +10 -0
- data/lib/flydata/command/stop.rb +10 -0
- data/lib/flydata/command/sync.rb +7 -2
- data/lib/flydata/helper/action/agent_action.rb +24 -0
- data/lib/flydata/helper/action/check_remote_actions.rb +54 -0
- data/lib/flydata/helper/action/restart_agent.rb +13 -0
- data/lib/flydata/helper/action/send_logs.rb +33 -0
- data/lib/flydata/helper/action/stop_agent.rb +13 -0
- data/lib/flydata/helper/action_ownership.rb +56 -0
- data/lib/flydata/helper/action_ownership_channel.rb +93 -0
- data/lib/flydata/helper/base_action.rb +23 -0
- data/lib/flydata/helper/config_parser.rb +197 -0
- data/lib/flydata/helper/scheduler.rb +114 -0
- data/lib/flydata/helper/server.rb +66 -0
- data/lib/flydata/helper/worker.rb +131 -0
- data/lib/flydata/output/forwarder.rb +3 -1
- data/lib/flydata/parser/mysql/dump_parser.rb +34 -19
- data/lib/flydata/sync_file_manager.rb +21 -0
- data/lib/flydata/util/file_util.rb +55 -0
- data/lib/flydata/util/shell.rb +22 -0
- data/spec/flydata/command/helper_spec.rb +121 -0
- data/spec/flydata/command/restart_spec.rb +12 -1
- data/spec/flydata/command/start_spec.rb +14 -1
- data/spec/flydata/command/stop_spec.rb +12 -1
- data/spec/flydata/helper/action/check_remote_actions_spec.rb +69 -0
- data/spec/flydata/helper/action/restart_agent_spec.rb +20 -0
- data/spec/flydata/helper/action/send_logs_spec.rb +58 -0
- data/spec/flydata/helper/action/stop_agent_spec.rb +20 -0
- data/spec/flydata/helper/action_ownership_channel_spec.rb +112 -0
- data/spec/flydata/helper/action_ownership_spec.rb +48 -0
- data/spec/flydata/helper/config_parser_spec.rb +99 -0
- data/spec/flydata/helper/helper_shared_context.rb +70 -0
- data/spec/flydata/helper/scheduler_spec.rb +35 -0
- data/spec/flydata/helper/worker_spec.rb +106 -0
- data/spec/flydata/output/forwarder_spec.rb +6 -3
- data/spec/flydata/parser/mysql/dump_parser_spec.rb +2 -1
- data/spec/flydata/util/file_util_spec.rb +110 -0
- data/spec/flydata/util/shell_spec.rb +26 -0
- data/spec/spec_helper.rb +31 -0
- metadata +46 -2
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata/helper/action_ownership_channel'
|
3
|
+
require 'timecop'
|
4
|
+
|
5
|
+
module Flydata
|
6
|
+
module Helper
|
7
|
+
describe ActionOwnershipChannel do
|
8
|
+
let(:channel) { described_class.new }
|
9
|
+
let(:action_info) do
|
10
|
+
{ id: 101, config: %Q|{"num_of_lines" : "10"}| }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#request_action' do
|
14
|
+
let(:action_name) { :send_logs }
|
15
|
+
subject { channel.request_action(action_name, action_info) }
|
16
|
+
|
17
|
+
context "when action is valid" do
|
18
|
+
it "should return true and also add the action to the queue" do
|
19
|
+
expect(channel.instance_variable_get(:@queue)).to be_empty
|
20
|
+
is_expected.to be(true)
|
21
|
+
expect(channel.instance_variable_get(:@queue).size).to be(1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when action is not valid" do
|
26
|
+
let(:action_name) { :start_agent }
|
27
|
+
it "should raise an error" do
|
28
|
+
expect {subject}.to raise_error('Received invalid action request:start_agent')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when the queue already contains the action" do
|
33
|
+
before do
|
34
|
+
channel.request_action(action_name, action_info)
|
35
|
+
end
|
36
|
+
it "should not alter the queue and return false" do
|
37
|
+
expect(channel.instance_variable_get(:@queue).size).to be(1)
|
38
|
+
is_expected.to be(false)
|
39
|
+
expect(channel.instance_variable_get(:@queue).size).to be(1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#take_action_ownership' do
|
45
|
+
let(:new_owner) { 'new-owner' }
|
46
|
+
subject { channel.take_action_ownership(new_owner)}
|
47
|
+
|
48
|
+
context "when the queue is empty" do
|
49
|
+
it "should return nil" do
|
50
|
+
is_expected.to be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when the action is being processed" do
|
55
|
+
before do
|
56
|
+
channel.request_action(:send_logs, action_info)
|
57
|
+
channel.take_action_ownership("another-owner")
|
58
|
+
channel.request_action(:send_logs, action_info)
|
59
|
+
end
|
60
|
+
it "should return nil" do
|
61
|
+
is_expected.to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the action needs to be taken exclusively \
|
66
|
+
and there are other exclusive actions in progress" do
|
67
|
+
before do
|
68
|
+
channel.request_action(:stop_agent)
|
69
|
+
channel.take_action_ownership("another-owner")
|
70
|
+
channel.request_action(:restart_agent)
|
71
|
+
end
|
72
|
+
it "should return nil" do
|
73
|
+
is_expected.to be_nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when the action can be taken" do
|
78
|
+
before do
|
79
|
+
channel.request_action(:send_logs, action_info)
|
80
|
+
end
|
81
|
+
it "should remove the action from the queue and return" do
|
82
|
+
expect(channel.instance_variable_get(:@queue).size).to be(1)
|
83
|
+
action = channel.take_action_ownership(new_owner)
|
84
|
+
expect(channel.instance_variable_get(:@queue).size).to be(0)
|
85
|
+
expect(action[:action_ownership].action_name).to be(:send_logs)
|
86
|
+
expect(action[:action_ownership].owner).to be(new_owner)
|
87
|
+
expect(action[:action_info]).to eq(action_info)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#return_action_ownership' do
|
93
|
+
before do
|
94
|
+
channel.request_action(:restart_agent)
|
95
|
+
@action_ownership = channel.take_action_ownership('test-owner')[:action_ownership]
|
96
|
+
Timecop.freeze(Time.local(2014, 8, 19, 16, 11, 24))
|
97
|
+
end
|
98
|
+
|
99
|
+
after do
|
100
|
+
Timecop.return
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'update action_ownership' do
|
104
|
+
channel.return_action_ownership(@action_ownership)
|
105
|
+
expect(@action_ownership.owner).to be_nil
|
106
|
+
expect(@action_ownership.last_processed_time).
|
107
|
+
to eq(Time.local(2014, 8, 19, 16, 11, 24).to_f)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata/helper/action_ownership'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
module Helper
|
6
|
+
describe ActionOwnership do
|
7
|
+
describe '#initialize' do
|
8
|
+
let(:action_name) { 'stop_agent' }
|
9
|
+
let(:action_class) { Action::StopAgent }
|
10
|
+
|
11
|
+
subject { described_class.new(action_name, true, action_class) }
|
12
|
+
|
13
|
+
it { expect(subject.action_name).to eq(action_name) }
|
14
|
+
it { expect(subject.resource_change).to eq(true) }
|
15
|
+
it { expect(subject.action_class).to eq(action_class) }
|
16
|
+
it { expect(subject.owner).to eq(nil) }
|
17
|
+
it { expect(subject.last_processed_time).to eq(-1) }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#processing?' do
|
21
|
+
let(:action_name) { 'stop_agent' }
|
22
|
+
let(:action_class) { Action::StopAgent }
|
23
|
+
let(:ownership) { described_class.new(action_name, true, action_class) }
|
24
|
+
|
25
|
+
subject { ownership.processing? }
|
26
|
+
|
27
|
+
context 'when owner is not set' do
|
28
|
+
it { expect(subject).to be(false) }
|
29
|
+
end
|
30
|
+
context 'when owner is set' do
|
31
|
+
before { ownership.owner = 'test-owner' }
|
32
|
+
it { expect(subject).to be(true) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.action_ownership_map' do
|
37
|
+
subject { described_class.action_ownership_map }
|
38
|
+
it do
|
39
|
+
expect(subject.size).to eq(4)
|
40
|
+
expect(subject.include?(:check_remote_actions)).to be(true)
|
41
|
+
expect(subject.include?(:send_logs)).to be(true)
|
42
|
+
expect(subject.include?(:stop_agent)).to be(true)
|
43
|
+
expect(subject.include?(:restart_agent)).to be(true)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata/helper/config_parser'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
module Helper
|
6
|
+
describe Config do
|
7
|
+
context ".create" do
|
8
|
+
subject { Config.create(config_hash) }
|
9
|
+
context "initialized with empty hash" do
|
10
|
+
let(:config_hash) { Hash.new }
|
11
|
+
it "should return a config object and respond to dynamically added methods" do
|
12
|
+
is_expected.not_to be_nil
|
13
|
+
is_expected.to respond_to(:helper_home)
|
14
|
+
end
|
15
|
+
it "should return default values correctly" do
|
16
|
+
expect(subject.helper_retry_limit).to eq(15)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
context "initialized with non-empty hash" do
|
20
|
+
let(:override_helper_home) { "/home/user/.flydata/helper" }
|
21
|
+
let(:config_hash) { {helper_home: override_helper_home} }
|
22
|
+
it "should return correct value of a property" do
|
23
|
+
expect(subject.helper_home).to eq(override_helper_home)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context '#scheduled_actions' do
|
29
|
+
subject { Config.default }
|
30
|
+
it "should return the scheduled actions correctly" do
|
31
|
+
expect(subject.scheduled_actions).to eq({
|
32
|
+
check_remote_actions:
|
33
|
+
{
|
34
|
+
check_interval: 30,
|
35
|
+
name: :check_remote_actions
|
36
|
+
}
|
37
|
+
})
|
38
|
+
end
|
39
|
+
it "should return a copy everytime" do
|
40
|
+
helper_config = subject
|
41
|
+
src = helper_config[:scheduled_actions]
|
42
|
+
copy = helper_config.scheduled_actions
|
43
|
+
expect(src.object_id).not_to eq(copy.object_id)
|
44
|
+
expect(src[:check_remote_actions].object_id).
|
45
|
+
not_to eq(copy[:check_remote_actions].object_id)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe ConfigParser do
|
51
|
+
context ".parse" do
|
52
|
+
subject { described_class.parse(conf_content)}
|
53
|
+
context "when the parsed content is empty" do
|
54
|
+
let(:conf_content) { "" }
|
55
|
+
it "should raise an error" do
|
56
|
+
expect { subject }.to raise_error
|
57
|
+
end
|
58
|
+
end
|
59
|
+
context "when the parsed content does not contain scheduled action" do
|
60
|
+
let(:conf_content) { "workers: 5" }
|
61
|
+
it "should return the default scheduled action" do
|
62
|
+
is_expected.to eq({
|
63
|
+
workers: 5,
|
64
|
+
helper: {
|
65
|
+
scheduled_actions: {
|
66
|
+
check_remote_actions: {
|
67
|
+
check_interval: '30s'
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context "when content includes scheduled action" do
|
75
|
+
let(:conf_content) { <<EOT
|
76
|
+
workers: 2
|
77
|
+
helper:
|
78
|
+
scheduled_actions:
|
79
|
+
check_remote_actions:
|
80
|
+
check_interval: 1m
|
81
|
+
EOT
|
82
|
+
}
|
83
|
+
it "should parse to a Config object" do
|
84
|
+
is_expected.to eq({
|
85
|
+
workers: 2,
|
86
|
+
helper: {
|
87
|
+
scheduled_actions: {
|
88
|
+
check_remote_actions: {
|
89
|
+
check_interval: '1m'
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Flydata
|
2
|
+
module Helper
|
3
|
+
shared_context 'helper context' do
|
4
|
+
let(:action_object) { double("action_object") }
|
5
|
+
|
6
|
+
let(:action_class) do
|
7
|
+
c = double("action_class")
|
8
|
+
allow(c).to receive(:new).and_return(action_object)
|
9
|
+
c
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:action_ownership) do
|
13
|
+
o = double("action_ownership")
|
14
|
+
allow(o).to receive(:action_name).and_return(:check_remote_actions)
|
15
|
+
allow(o).to receive(:action_class).and_return(action_class)
|
16
|
+
o
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:action) do
|
20
|
+
a = double("action")
|
21
|
+
allow(a).to receive(:[]).with(:action_ownership).and_return(action_ownership)
|
22
|
+
allow(a).to receive(:[]).with(:action_info).and_return(nil)
|
23
|
+
a
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:action_ownership_channel) { double("action_ownership_channel") }
|
27
|
+
|
28
|
+
let(:logger) do
|
29
|
+
l = double("logger")
|
30
|
+
[:fatal, :error, :warn, :info, :debug]. each do |level|
|
31
|
+
allow(l).to receive(level)
|
32
|
+
end
|
33
|
+
l
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:server) do
|
37
|
+
s = double("server")
|
38
|
+
allow(s).to receive(:logger).and_return(logger)
|
39
|
+
allow(s).to receive(:action_ownership_channel).
|
40
|
+
and_return(action_ownership_channel)
|
41
|
+
allow(s).to receive(:config).
|
42
|
+
and_return(config)
|
43
|
+
s
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:scheduled_actions) do
|
47
|
+
{
|
48
|
+
check_remote_actions: {
|
49
|
+
check_interval: 1
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:helper_conf) do
|
55
|
+
c = double("helper_conf")
|
56
|
+
allow(c).to receive(:helper_retry_limit).and_return(10)
|
57
|
+
allow(c).to receive(:helper_retry_interval).and_return(0.1)
|
58
|
+
allow(c).to receive(:helper_action_position_path).and_return("some_path")
|
59
|
+
c
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:config) do
|
63
|
+
h = double("config")
|
64
|
+
allow(h).to receive(:scheduled_actions).and_return(scheduled_actions)
|
65
|
+
allow(h).to receive(:[]).with(:helper).and_return(helper_conf)
|
66
|
+
h
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata/helper/scheduler'
|
3
|
+
require_relative 'helper_shared_context'
|
4
|
+
|
5
|
+
module Flydata
|
6
|
+
module Helper
|
7
|
+
describe Scheduler do
|
8
|
+
include_context 'helper context'
|
9
|
+
let(:scheduler) do
|
10
|
+
described_class.new(config, server)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'start and stop scheduler' do
|
14
|
+
it 'requests check_remote_actions once' do
|
15
|
+
expect(action_ownership_channel).to receive(:request_action).with(:check_remote_actions).once
|
16
|
+
scheduler.start
|
17
|
+
sleep 0.5 #Wait for start
|
18
|
+
scheduler.shutdown
|
19
|
+
expect(scheduler.running?).to be_falsey
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'start and wait for one full loop' do
|
24
|
+
it 'requests check_remote_actions twice' do
|
25
|
+
expect(action_ownership_channel).to receive(:request_action).with(:check_remote_actions).twice
|
26
|
+
scheduler.start
|
27
|
+
sleep 1.5 #Added 0.5s delta
|
28
|
+
scheduler.shutdown
|
29
|
+
expect(scheduler.running?).to be_falsey
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata/helper/worker'
|
3
|
+
require_relative 'helper_shared_context'
|
4
|
+
|
5
|
+
module Flydata
|
6
|
+
module Helper
|
7
|
+
describe Worker do
|
8
|
+
include_context 'helper context'
|
9
|
+
|
10
|
+
let(:worker_id) { 1 }
|
11
|
+
|
12
|
+
let(:worker) do
|
13
|
+
w = ServerEngine::Worker.new(server, worker_id)
|
14
|
+
w.extend(ServerEngine::Server::WorkerInitializer)
|
15
|
+
w.extend(Worker)
|
16
|
+
w.instance_eval { initialize }
|
17
|
+
w
|
18
|
+
end
|
19
|
+
|
20
|
+
def start_and_wait_worker
|
21
|
+
@thread = Thread.new { worker.run }
|
22
|
+
sleep 0.5
|
23
|
+
end
|
24
|
+
|
25
|
+
def wait_for_one_run_loop; sleep 1; end
|
26
|
+
|
27
|
+
def shutdown_worker
|
28
|
+
if @thread
|
29
|
+
worker.stop
|
30
|
+
@thread.join
|
31
|
+
@thread = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#run" do
|
36
|
+
after do
|
37
|
+
shutdown_worker
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is running' do
|
41
|
+
allow(action_ownership_channel).to receive(:take_action_ownership).and_return(nil)
|
42
|
+
start_and_wait_worker
|
43
|
+
expect(@thread.alive?).to be(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when request an action' do
|
47
|
+
before do
|
48
|
+
allow(action_ownership_channel).to receive(:take_action_ownership).and_return(action, nil)
|
49
|
+
expect(action_ownership_channel).to receive(:return_action_ownership).with(action_ownership).once
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when action does not yield' do
|
53
|
+
it 'takes and executes the action' do
|
54
|
+
expect(action_object).to receive(:execute).once
|
55
|
+
expect(action_ownership).to receive(:reset_retry_count)
|
56
|
+
start_and_wait_worker
|
57
|
+
wait_for_one_run_loop
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context 'when action yields' do
|
61
|
+
it 'requests a new action' do
|
62
|
+
expect(action_object).to receive(:execute).once.and_yield(:send_logs, nil)
|
63
|
+
expect(action_ownership_channel).to receive(:request_action).with(:send_logs, nil)
|
64
|
+
expect(action_ownership).to receive(:reset_retry_count)
|
65
|
+
start_and_wait_worker
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when action raises error' do
|
70
|
+
before do
|
71
|
+
expect(action_object).to receive(:execute).once.and_raise("test error")
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'retries by creating a action request if it has NOT hit the retry limit' do
|
75
|
+
expect(action_ownership).to receive(:increment_retry_count).and_return(1)
|
76
|
+
expect(action_ownership_channel).
|
77
|
+
to receive(:request_action).with(:check_remote_actions, nil).once
|
78
|
+
start_and_wait_worker
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'does not retry the action request if it has hit the retry limit' do
|
82
|
+
expect(action_ownership).to receive(:increment_retry_count).and_return(10)
|
83
|
+
expect(action_ownership).to receive(:reset_retry_count)
|
84
|
+
start_and_wait_worker
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#reload" do
|
91
|
+
before do
|
92
|
+
allow(action_ownership_channel).to receive(:take_action_ownership).and_return(nil)
|
93
|
+
start_and_wait_worker
|
94
|
+
end
|
95
|
+
|
96
|
+
after { shutdown_worker }
|
97
|
+
it 'stop worker' do
|
98
|
+
expect(@thread.alive?).to be_truthy
|
99
|
+
worker.reload
|
100
|
+
sleep 0.5 #wait to stop
|
101
|
+
expect(@thread.alive?).to be_falsey
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|