tcell_agent 0.2.8 → 0.2.9

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