flydata 0.3.24 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +3 -0
  4. data/VERSION +1 -1
  5. data/flydata.gemspec +36 -3
  6. data/lib/flydata.rb +8 -0
  7. data/lib/flydata/api/agent.rb +21 -0
  8. data/lib/flydata/command/helper.rb +154 -0
  9. data/lib/flydata/command/mysql.rb +37 -0
  10. data/lib/flydata/command/restart.rb +11 -0
  11. data/lib/flydata/command/start.rb +12 -2
  12. data/lib/flydata/command/status.rb +10 -0
  13. data/lib/flydata/command/stop.rb +10 -0
  14. data/lib/flydata/command/sync.rb +7 -2
  15. data/lib/flydata/helper/action/agent_action.rb +24 -0
  16. data/lib/flydata/helper/action/check_remote_actions.rb +54 -0
  17. data/lib/flydata/helper/action/restart_agent.rb +13 -0
  18. data/lib/flydata/helper/action/send_logs.rb +33 -0
  19. data/lib/flydata/helper/action/stop_agent.rb +13 -0
  20. data/lib/flydata/helper/action_ownership.rb +56 -0
  21. data/lib/flydata/helper/action_ownership_channel.rb +93 -0
  22. data/lib/flydata/helper/base_action.rb +23 -0
  23. data/lib/flydata/helper/config_parser.rb +197 -0
  24. data/lib/flydata/helper/scheduler.rb +114 -0
  25. data/lib/flydata/helper/server.rb +66 -0
  26. data/lib/flydata/helper/worker.rb +131 -0
  27. data/lib/flydata/output/forwarder.rb +3 -1
  28. data/lib/flydata/parser/mysql/dump_parser.rb +34 -19
  29. data/lib/flydata/sync_file_manager.rb +21 -0
  30. data/lib/flydata/util/file_util.rb +55 -0
  31. data/lib/flydata/util/shell.rb +22 -0
  32. data/spec/flydata/command/helper_spec.rb +121 -0
  33. data/spec/flydata/command/restart_spec.rb +12 -1
  34. data/spec/flydata/command/start_spec.rb +14 -1
  35. data/spec/flydata/command/stop_spec.rb +12 -1
  36. data/spec/flydata/helper/action/check_remote_actions_spec.rb +69 -0
  37. data/spec/flydata/helper/action/restart_agent_spec.rb +20 -0
  38. data/spec/flydata/helper/action/send_logs_spec.rb +58 -0
  39. data/spec/flydata/helper/action/stop_agent_spec.rb +20 -0
  40. data/spec/flydata/helper/action_ownership_channel_spec.rb +112 -0
  41. data/spec/flydata/helper/action_ownership_spec.rb +48 -0
  42. data/spec/flydata/helper/config_parser_spec.rb +99 -0
  43. data/spec/flydata/helper/helper_shared_context.rb +70 -0
  44. data/spec/flydata/helper/scheduler_spec.rb +35 -0
  45. data/spec/flydata/helper/worker_spec.rb +106 -0
  46. data/spec/flydata/output/forwarder_spec.rb +6 -3
  47. data/spec/flydata/parser/mysql/dump_parser_spec.rb +2 -1
  48. data/spec/flydata/util/file_util_spec.rb +110 -0
  49. data/spec/flydata/util/shell_spec.rb +26 -0
  50. data/spec/spec_helper.rb +31 -0
  51. 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