flydata 0.3.24 → 0.4.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 +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
|