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.
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