tcell_agent 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -44,13 +44,16 @@ module TCellAgent
44
44
  end
45
45
  end
46
46
  class TCellRedirectSensorEvent < TCellSensorEvent
47
- def initialize(redirect_domain, original_domain, original_url, method, status_code, remote_addr, user_id=nil, session_id=nil)
47
+ def initialize(redirect_domain, original_domain, original_url, method, route_id, status_code, remote_addr, session_id=nil, user_id=nil)
48
48
  super("redirect")
49
49
  @raw_original_url = original_url
50
50
  self["method"] = method
51
51
  self["from_domain"] = original_domain
52
52
  self["status_code"] = status_code
53
53
  self["remote_addr"] = remote_addr
54
+ if route_id
55
+ self["rid"] = route_id
56
+ end
54
57
  @raw_redirect_domain = redirect_domain
55
58
  @user_id = user_id
56
59
  @raw_session_id = session_id
@@ -1,3 +1,4 @@
1
+ require("tcell_agent/servers/unicorn") if defined?(Unicorn::HttpServer)
1
2
 
2
3
  Thin::Server.class_eval do
3
4
 
@@ -18,6 +18,80 @@ Unicorn::HttpServer.class_eval do
18
18
  original_join
19
19
  end
20
20
 
21
+ # This gets called when unicorn receives the HUP signal to reload its config.
22
+ # Tcell also needs to ensure its config is reloaded and services are started
23
+ # or stopped accordingly
24
+ alias_method :original_load_config!, :load_config!
25
+ def load_config!
26
+ original_load_config!
27
+
28
+ TCellAgent::Instrumentation.safe_block("Reloading Tcell Config") do
29
+ new_config = TCellAgent::Configuration.new
30
+ TCellAgent.logger.debug("Reloading config")
31
+ TCellAgent.logger.debug(
32
+ "ENABLED:#{new_config.enabled}" +
33
+ "|ENABLE_EVENT_MANAGER:#{new_config.enable_event_manager}" +
34
+ "|ENABLE_EVENT_CONSUMER:#{new_config.enable_event_consumer}" +
35
+ "|ENABLE_POLICY_POLLING:#{new_config.enable_policy_polling}" +
36
+ "|ENABLE_INSTRUMENTATION:#{new_config.enable_instrumentation}" +
37
+ "|ENABLE_INTERCEPT_REQUESTS:#{new_config.enable_intercept_requests}"
38
+ )
39
+ old_config = TCellAgent.configuration
40
+
41
+ TCellAgent.configuration = new_config
42
+
43
+ if new_config.enabled ^ old_config.enabled
44
+ if new_config.enabled
45
+ TCellAgent.run_instrumentation("Unicorn")
46
+
47
+ else
48
+ TCellAgent.thread_agent.stop_event_processor
49
+ TCellAgent.thread_agent.stop_metrics_event_thread
50
+ TCellAgent.thread_agent.stop_policy_polling
51
+ end
52
+ end
53
+
54
+ if new_config.enable_event_manager ^ old_config.enable_event_manager
55
+ if new_config.enable_event_manager
56
+ TCellAgent.run_instrumentation("Unicorn Restart")
57
+ else
58
+ TCellAgent.thread_agent.stop_event_processor
59
+ end
60
+ else
61
+ # Just in case
62
+ if new_config.enable_event_manager
63
+ TCellAgent.thread_agent.ensure_event_processor_running
64
+ end
65
+ end
66
+
67
+ if new_config.enable_event_consumer ^ old_config.enable_event_consumer
68
+ if new_config.enable_event_consumer
69
+ TCellAgent.thread_agent.ensure_metrics_event_thread_running
70
+ else
71
+ TCellAgent.thread_agent.stop_metrics_event_thread
72
+ end
73
+ else
74
+ # Just in case
75
+ if new_config.enable_event_consumer
76
+ TCellAgent.thread_agent.ensure_metrics_event_thread_running
77
+ end
78
+ end
79
+
80
+ if new_config.enable_policy_polling ^ old_config.enable_policy_polling
81
+ if new_config.enable_policy_polling
82
+ TCellAgent.thread_agent.ensure_policy_polling_running
83
+ else
84
+ TCellAgent.thread_agent.stop_policy_polling
85
+ end
86
+ else
87
+ # Just in case
88
+ if new_config.enable_policy_polling
89
+ TCellAgent.thread_agent.ensure_policy_polling_running
90
+ end
91
+ end
92
+ end
93
+ end
94
+
21
95
  # this only runs when preload_app=true because when preload_app=false
22
96
  # the gems aren't loaded early enough for tcell to override
23
97
  # the class definitions
@@ -25,13 +99,15 @@ Unicorn::HttpServer.class_eval do
25
99
  def init_worker_process(work)
26
100
  start_process = original_init_worker_process(work)
27
101
 
28
- begin
29
- TCellAgent.thread_agent.policy_polling_worker_mutex = Mutex.new
30
- TCellAgent.thread_agent.policy_polling_thread = nil
31
- TCellAgent.thread_agent.start
102
+ if TCellAgent.configuration.enabled && TCellAgent.configuration.should_instrument?
103
+ begin
104
+ TCellAgent.thread_agent.policy_polling_worker_mutex = Mutex.new
105
+ TCellAgent.thread_agent.policy_polling_thread = nil
106
+ TCellAgent.thread_agent.start
32
107
 
33
- rescue Exception => e
34
- TCellAgent.logger.error("Could not start thread agent. #{e.message}")
108
+ rescue Exception => e
109
+ TCellAgent.logger.error("Could not start thread agent. #{e.message}")
110
+ end
35
111
  end
36
112
 
37
113
  start_process
@@ -5,22 +5,22 @@ require 'tcell_agent/agent'
5
5
  require 'tcell_agent/configuration'
6
6
  require 'thread'
7
7
 
8
- if TCellAgent.configuration.enabled
9
- module TCellAgent
10
- #require 'tcell_agent/sinatra' if defined?(Sinatra)
11
- require 'tcell_agent/rails' if defined?(Rails)
8
+ module TCellAgent
9
+ #require 'tcell_agent/sinatra' if defined?(Sinatra)
10
+ require 'tcell_agent/rails' if defined?(Rails)
12
11
 
13
- def self.run_instrumentation(server_name)
12
+ def self.run_instrumentation(server_name)
14
13
 
15
- require 'tcell_agent/rails/on_start' if defined?(Rails)
14
+ require 'tcell_agent/rails/on_start' if defined?(Rails)
16
15
 
17
- begin
18
- TCellAgent.logger.debug("Instrumenting: #{server_name}")
19
- TCellAgent.thread_agent.start
20
- rescue Exception => e
21
- TCellAgent.logger.error("Could not start thread agent. #{e.message}")
22
- end
16
+ begin
17
+ TCellAgent.logger.debug("Instrumenting: #{server_name}")
18
+ TCellAgent.thread_agent.start
19
+ rescue Exception => e
20
+ TCellAgent.logger.error("Could not start thread agent. #{e.message}")
21
+ end
23
22
 
23
+ if TCellAgent.configuration.should_instrument?
24
24
  Thread.abort_on_exception = TCellAgent.configuration.raise_exceptions
25
25
  Thread.new do
26
26
 
@@ -34,36 +34,34 @@ if TCellAgent.configuration.enabled
34
34
  TCellAgent.send_event(event)
35
35
  end
36
36
 
37
- if (TCellAgent.configuration.enabled && TCellAgent.configuration.should_instrument? && defined?(Rails))
37
+ if defined?(Rails)
38
38
  TCellAgent::Instrumentation.safe_block("Instrumenting routes") do
39
39
  TCellAgent::Instrumentation::Rails.instrument_routes
40
40
  end
41
41
  end
42
-
43
42
  end
44
43
 
45
44
  end
46
45
 
47
46
  end
48
47
 
49
- tcell_server = ENV["TCELL_AGENT_SERVER"]
48
+ end
49
+
50
+ tcell_server = ENV["TCELL_AGENT_SERVER"]
50
51
 
52
+ if TCellAgent.configuration.should_instrument?
51
53
  if (!(tcell_server && tcell_server == "mock"))
52
54
 
53
55
  if (tcell_server && tcell_server == "webrick") || defined?(Rails::Server)
54
-
55
56
  require("tcell_agent/servers/rails_server")
56
57
 
57
58
  elsif (tcell_server && tcell_server == "thin") || defined?(Thin)
58
-
59
59
  require("tcell_agent/servers/thin")
60
60
 
61
61
  elsif (tcell_server && tcell_server == "puma") || defined?(Puma)
62
-
63
62
  require("tcell_agent/servers/puma")
64
63
 
65
64
  elsif (tcell_server && tcell_server == "unicorn") || defined?(Unicorn)
66
-
67
65
  require("tcell_agent/servers/unicorn")
68
66
 
69
67
  else
@@ -74,4 +72,11 @@ if TCellAgent.configuration.enabled
74
72
  puts "[tCell.io] **********************************************************************"
75
73
  end
76
74
  end
75
+
76
+ else
77
+
78
+ # unicorn is always instrumented to support rolling restarts
79
+ if (tcell_server && tcell_server == "unicorn") || defined?(Unicorn)
80
+ require("tcell_agent/servers/unicorn")
81
+ end
77
82
  end
@@ -1,5 +1,5 @@
1
1
  # See the file "LICENSE" for the full license governing this code.
2
2
 
3
3
  module TCellAgent
4
- VERSION = "0.2.8"
4
+ VERSION = "0.2.9"
5
5
  end
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ module SensorEvents
5
+
6
+ describe Agent do
7
+
8
+ describe "#cache" do
9
+ context "with an existing cached file" do
10
+
11
+ context "with two processes" do
12
+ context "while one process is writing to the cached file" do
13
+ before(:each) do
14
+ TCellAgent.thread_agent.cache(
15
+ "http-redirect",
16
+ {
17
+ "app_id"=>"raftest-EyJZR",
18
+ "policy_id"=>"363b8b60-a9a8-11e5-bb10-a9372a56b8a7",
19
+ "data"=> {
20
+ "enabled"=>true,
21
+ "block"=>false,
22
+ "whitelist"=>[]
23
+ }
24
+ }
25
+ )
26
+ end
27
+
28
+ it "should raise a timeout exception when the other process tries to write to it" do
29
+ file_lock_holder = ForkBreak::Process.new do |breakpoints|
30
+
31
+ original_dump = JSON.method(:dump)
32
+ JSON.stub(:dump) do |*args|
33
+ breakpoints << :before_dump
34
+ original_dump.call(*args)
35
+ end
36
+
37
+ TCellAgent.thread_agent.cache(
38
+ "http-redirect",
39
+ {
40
+ "app_id"=>"raftest-EyJZR",
41
+ "policy_id"=>"363b8b60-a9a8-11e5-bb10-a9372a56b8a7",
42
+ "data"=> {
43
+ "enabled"=>true,
44
+ "block"=>false,
45
+ "whitelist"=>[]
46
+ }
47
+ }
48
+ )
49
+ end
50
+
51
+ file_lock_holder.run_until(:before_dump).wait
52
+
53
+ logger = double("logger")
54
+ expect(TCellAgent).to receive(:logger).and_return(logger)
55
+ expect(logger).to receive(:error).with("execution expired")
56
+
57
+ TCellAgent.thread_agent.cache(
58
+ "http-redirect",
59
+ {
60
+ "app_id"=>"raftest-EyJZR",
61
+ "policy_id"=>"363b8b60-a9a8-11e5-bb10-a9372a56b8a7",
62
+ "data"=> {
63
+ "enabled"=>true,
64
+ "block"=>false,
65
+ "whitelist"=>[]
66
+ }
67
+ }
68
+ )
69
+
70
+ file_lock_holder.finish.wait
71
+ end
72
+
73
+
74
+ after(:each) do
75
+ File.delete(TCellAgent.configuration.cache_filename_with_app_id)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ context "with 10 processes updating the cached file" do
82
+ it "should update the cached file with all updates" do
83
+ processes = 5.times.map do |process_number|
84
+ ForkBreak::Process.new do |breakpoints|
85
+ original_dump = JSON.method(:dump)
86
+ JSON.stub(:dump) do |*args|
87
+ breakpoints << :before_dump
88
+ original_dump.call(*args)
89
+ end
90
+
91
+ TCellAgent.thread_agent.cache(
92
+ "process_#{process_number}",
93
+ {
94
+ "app_id"=>"raftest-EyJZR",
95
+ "policy_id"=>"policy_id",
96
+ "data"=> { "enabled"=>true }
97
+ }
98
+ )
99
+
100
+ end
101
+ end
102
+
103
+ processes.map(&:finish).map(&:wait)
104
+
105
+ policies = JSON.parse(open(TCellAgent.configuration.cache_filename_with_app_id).read)
106
+
107
+ File.delete(TCellAgent.configuration.cache_filename_with_app_id)
108
+
109
+ expect(policies).to eq({
110
+ "process_0"=>{"app_id"=>"raftest-EyJZR", "policy_id"=>"policy_id", "data"=>{"enabled"=>true}},
111
+ "process_1"=>{"app_id"=>"raftest-EyJZR", "policy_id"=>"policy_id", "data"=>{"enabled"=>true}},
112
+ "process_2"=>{"app_id"=>"raftest-EyJZR", "policy_id"=>"policy_id", "data"=>{"enabled"=>true}},
113
+ "process_3"=>{"app_id"=>"raftest-EyJZR", "policy_id"=>"policy_id", "data"=>{"enabled"=>true}},
114
+ "process_4"=>{"app_id"=>"raftest-EyJZR", "policy_id"=>"policy_id", "data"=>{"enabled"=>true}}
115
+ })
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#policies_from_cachefile" do
121
+ context "with no cache file" do
122
+ it "should return nil" do
123
+
124
+ expect(File).to receive(:exist?).with(/tcell\/cache\/tcell_agent.cache/).and_return(false)
125
+ expect_any_instance_of(TCellAgent::Agent).to_not receive(:processPolicyJson)
126
+ agent = TCellAgent::Agent.new(Process.pid)
127
+
128
+ expect(agent.policies).to eq({})
129
+ end
130
+ end
131
+
132
+ context "with a cache file present" do
133
+ context "that is empty" do
134
+ it "should raise a json error" do
135
+ cache_file = double("cache_file")
136
+ expect(File).to receive(:exist?).with(/tcell\/cache\/tcell_agent.cache/).and_return(true)
137
+ expect(File).to receive(:open).and_return(cache_file)
138
+ expect(cache_file).to receive(:flock).and_return(true)
139
+ expect(cache_file).to receive(:read).and_return("")
140
+ expect(cache_file).to receive(:close)
141
+
142
+ logger = double("logger")
143
+ expect(TCellAgent).to receive(:logger).and_return(logger)
144
+ expect(logger).to receive(:error).with("A JSON text must at least contain two octets!")
145
+
146
+ expect_any_instance_of(TCellAgent::Agent).to_not receive(:processPolicyJson)
147
+
148
+ agent = TCellAgent::Agent.new(Process.pid)
149
+
150
+ expect(agent.policies).to eq({})
151
+ end
152
+ end
153
+
154
+ context "that has content" do
155
+ context "that is malformed" do
156
+ it "should raise a json error" do
157
+ cache_file = double("cache_file")
158
+ expect(File).to receive(:exist?).with(/tcell\/cache\/tcell_agent.cache/).and_return(true)
159
+ expect(File).to receive(:open).and_return(cache_file)
160
+ expect(cache_file).to receive(:flock).and_return(true)
161
+ expect(cache_file).to receive(:read).and_return("bad_json")
162
+ expect(cache_file).to receive(:close)
163
+
164
+ logger = double("logger")
165
+ expect(TCellAgent).to receive(:logger).and_return(logger)
166
+ expect(logger).to receive(:error).with("757: unexpected token at 'bad_json'")
167
+ expect_any_instance_of(TCellAgent::Agent).to_not receive(:processPolicyJson)
168
+
169
+ agent = TCellAgent::Agent.new(Process.pid)
170
+
171
+ expect(agent.policies).to eq({})
172
+ end
173
+ end
174
+
175
+ context "that is an empty json object" do
176
+ it "should raise a json error" do
177
+ cache_file = double("cache_file")
178
+ expect(File).to receive(:exist?).with(/tcell\/cache\/tcell_agent.cache/).and_return(true)
179
+ expect(File).to receive(:open).and_return(cache_file)
180
+ expect(cache_file).to receive(:flock).and_return(true)
181
+ expect(cache_file).to receive(:read).and_return("{}")
182
+ expect(cache_file).to receive(:close)
183
+
184
+ expect(TCellAgent).to_not receive(:logger)
185
+
186
+ expect_any_instance_of(TCellAgent::Agent).to receive(:processPolicyJson).with({}, false)
187
+
188
+ agent = TCellAgent::Agent.new(Process.pid)
189
+ end
190
+ end
191
+
192
+ context "that is a valid policy" do
193
+ it "should set the policies" do
194
+ cache_file = double("cache_file")
195
+ expect(File).to receive(:exist?).with(/tcell\/cache\/tcell_agent.cache/).and_return(true)
196
+ expect(File).to receive(:open).and_return(cache_file)
197
+ expect(cache_file).to receive(:flock).and_return(true)
198
+ expect(cache_file).to receive(:read).and_return(
199
+ {
200
+ process_0: {app_id: "raftest-EyJZR", policy_id: "policy_id", data: {enabled: true}}
201
+ }.to_json
202
+ )
203
+ expect(cache_file).to receive(:close)
204
+ expect_any_instance_of(TCellAgent::Agent).to receive(:processPolicyJson).with(
205
+ {"process_0"=>{"app_id"=>"raftest-EyJZR", "policy_id"=>"policy_id", "data"=>{"enabled"=>true}}},
206
+ false
207
+ )
208
+
209
+ TCellAgent::Agent.new(Process.pid)
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ end
218
+ end
@@ -46,14 +46,14 @@ module TCellAgent
46
46
  http_redirect_from_json.enabled = false
47
47
  http_redirect_from_json.block = true
48
48
  http_redirect_from_json.whitelist = ["*.google.com"]
49
- result = http_redirect_from_json.enforce("https://test.google.com", "www.test.com", "/path/a", "GET", "1.1.1.1", 400)
49
+ result = http_redirect_from_json.enforce("https://test.google.com", "www.test.com", "/path/a", "GET", "routex", "1.1.1.1", 400)
50
50
  expect(result).to eq(nil)
51
51
  end
52
52
  it "domain enforce enabled true, block true" do
53
53
  http_redirect_from_json.enabled = true
54
54
  http_redirect_from_json.block = true
55
55
  http_redirect_from_json.whitelist = ["good.com"]
56
- result = http_redirect_from_json.enforce("https://www.google.com/abc/def", "localhost", "/path/a", "GET", "1.1.1.1", 400)
56
+ result = http_redirect_from_json.enforce("https://www.google.com/abc/def", "localhost", "/path/a", "GET", "routey", "1.1.1.1", 400)
57
57
  expect(result).to eq("/")
58
58
  end
59
59
  end