rookout 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +12 -0
- data/bin/rookout +30 -0
- data/lib/rookout.rb +18 -0
- data/lib/rookout/augs/actions/action.rb +11 -0
- data/lib/rookout/augs/actions/action_run_processor.rb +29 -0
- data/lib/rookout/augs/aug.rb +121 -0
- data/lib/rookout/augs/aug_factory.rb +69 -0
- data/lib/rookout/augs/aug_rate_limiter.rb +96 -0
- data/lib/rookout/augs/augs_manager.rb +77 -0
- data/lib/rookout/augs/conditions/condition.rb +15 -0
- data/lib/rookout/augs/locations/location.rb +11 -0
- data/lib/rookout/augs/locations/location_file_line.rb +26 -0
- data/lib/rookout/com_ws/agent_com_ws.rb +221 -0
- data/lib/rookout/com_ws/backoff.rb +35 -0
- data/lib/rookout/com_ws/command_handler.rb +22 -0
- data/lib/rookout/com_ws/git.rb +53 -0
- data/lib/rookout/com_ws/information.rb +85 -0
- data/lib/rookout/com_ws/output.rb +135 -0
- data/lib/rookout/com_ws/token_bucket.rb +36 -0
- data/lib/rookout/config.rb +55 -0
- data/lib/rookout/exceptions.rb +140 -0
- data/lib/rookout/interface.rb +140 -0
- data/lib/rookout/logger.rb +158 -0
- data/lib/rookout/processor/namespace_serializer.rb +26 -0
- data/lib/rookout/processor/namespaces/container_namespace.rb +45 -0
- data/lib/rookout/processor/namespaces/frame_namespace.rb +65 -0
- data/lib/rookout/processor/namespaces/namespace.rb +28 -0
- data/lib/rookout/processor/namespaces/noop_namespace.rb +32 -0
- data/lib/rookout/processor/namespaces/ruby_object_namespace.rb +97 -0
- data/lib/rookout/processor/namespaces/ruby_object_serializer.rb +208 -0
- data/lib/rookout/processor/namespaces/ruby_utils_namespace.rb +65 -0
- data/lib/rookout/processor/namespaces/stack_namespace.rb +30 -0
- data/lib/rookout/processor/namespaces/traceback_namespace.rb +40 -0
- data/lib/rookout/processor/operations/operation.rb +11 -0
- data/lib/rookout/processor/operations/set_operation.rb +48 -0
- data/lib/rookout/processor/paths/arithmetic_path.rb +84 -0
- data/lib/rookout/processor/paths/canopy/actions.rb +184 -0
- data/lib/rookout/processor/paths/canopy/consts.rb +82 -0
- data/lib/rookout/processor/paths/canopy/maps.rb +2837 -0
- data/lib/rookout/processor/paths/canopy/markers.rb +186 -0
- data/lib/rookout/processor/paths/path.rb +15 -0
- data/lib/rookout/processor/processor.rb +34 -0
- data/lib/rookout/processor/processor_factory.rb +23 -0
- data/lib/rookout/processor/rook_error.rb +45 -0
- data/lib/rookout/protobuf/.gitignore +0 -0
- data/lib/rookout/protobuf/agent_info_pb.rb +68 -0
- data/lib/rookout/protobuf/controller_info_pb.rb +29 -0
- data/lib/rookout/protobuf/envelope_pb.rb +21 -0
- data/lib/rookout/protobuf/messages_pb.rb +189 -0
- data/lib/rookout/protobuf/variant_pb.rb +139 -0
- data/lib/rookout/rookout_singleton.rb +73 -0
- data/lib/rookout/services/position.rb +163 -0
- data/lib/rookout/services/tracer.rb +83 -0
- data/lib/rookout/trigger_services.rb +34 -0
- data/lib/rookout/user_warnings.rb +25 -0
- data/lib/rookout/version.rb +4 -0
- metadata +269 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rookout
|
2
|
+
module Augs
|
3
|
+
module Locations
|
4
|
+
require_relative "location"
|
5
|
+
|
6
|
+
class LocationFileLine < Location
|
7
|
+
NAME = "file_line".freeze
|
8
|
+
|
9
|
+
def initialize arguments, _processor_factory
|
10
|
+
@filename = arguments["filename"]
|
11
|
+
@lineno = arguments["lineno"]
|
12
|
+
|
13
|
+
@file_hash = arguments["sha256"]
|
14
|
+
@line_crc = arguments["line_crc32_2"]
|
15
|
+
@line_unique = arguments["line_unique"] != 0
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :filename, :lineno, :file_hash, :line_crc, :line_unique
|
19
|
+
|
20
|
+
def add_aug trigger_services, aug
|
21
|
+
trigger_services.get_service("position").add_aug self, aug
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
module Rookout
|
2
|
+
module ComWs
|
3
|
+
require "securerandom"
|
4
|
+
require "kontena-websocket-client"
|
5
|
+
require "event_emitter"
|
6
|
+
require "google/protobuf/well_known_types"
|
7
|
+
require "concurrent"
|
8
|
+
|
9
|
+
require_relative "../config"
|
10
|
+
require_relative "../logger"
|
11
|
+
require_relative "../user_warnings"
|
12
|
+
require_relative "../exceptions"
|
13
|
+
|
14
|
+
require_relative "../processor/rook_error"
|
15
|
+
|
16
|
+
require_relative "../protobuf/messages_pb"
|
17
|
+
require_relative "../protobuf/envelope_pb"
|
18
|
+
|
19
|
+
require_relative "backoff"
|
20
|
+
require_relative "information"
|
21
|
+
|
22
|
+
class AgentComWs
|
23
|
+
include EventEmitter
|
24
|
+
|
25
|
+
def initialize output, agent_host, agent_port, proxy, token, labels
|
26
|
+
agent_host_with_protocl = agent_host.include?("://") ? agent_host : "ws://#{agent_host}"
|
27
|
+
@uri = "#{agent_host_with_protocl}:#{agent_port}/v1"
|
28
|
+
if proxy.nil? || proxy.empty?
|
29
|
+
@proxy = nil
|
30
|
+
else
|
31
|
+
@proxy = proxy.include?("://") ? proxy : "http://#{proxy}"
|
32
|
+
end
|
33
|
+
|
34
|
+
@token = token
|
35
|
+
@token_valid = false
|
36
|
+
|
37
|
+
@output = output
|
38
|
+
@info = Information.new labels
|
39
|
+
reset_id
|
40
|
+
|
41
|
+
@thread = nil
|
42
|
+
@pending_messages = Queue.new
|
43
|
+
|
44
|
+
@running = false
|
45
|
+
@ready_event = Concurrent::Event.new
|
46
|
+
once("Com::Rookout::InitialAugsCommand") { @ready_event.set }
|
47
|
+
end
|
48
|
+
|
49
|
+
def add message
|
50
|
+
buffer = wrap_in_envelope message
|
51
|
+
if buffer.length > Config.agent_com_max_message_limit
|
52
|
+
exc = Exceptions::RookMessageSizeExceeded.new buffer.length, Coonfig.agent_com_max_message_limit
|
53
|
+
warning = RookError.new exc, message
|
54
|
+
UserWarnings.notify_warning warning
|
55
|
+
|
56
|
+
Logger.instance.warn "Dropping message, size was #{buffer.length} which is over the message size limit"
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
@pending_messages.push buffer if @pending_messages.length < Config.agent_com_max_queued_messages
|
61
|
+
end
|
62
|
+
|
63
|
+
def connect
|
64
|
+
@running = true
|
65
|
+
|
66
|
+
@thread = Thread.new { connection_thread }
|
67
|
+
@thread.name = "rookout-connection-thread"
|
68
|
+
end
|
69
|
+
|
70
|
+
def wait_for_ready
|
71
|
+
@ready_event.wait Config.agent_com_timeout
|
72
|
+
raise @connection_error if @connection_error
|
73
|
+
end
|
74
|
+
|
75
|
+
def flush_all_messages
|
76
|
+
flush = FlushMessage
|
77
|
+
@pending_messages.push flush
|
78
|
+
flush.event.wait Config.agent_com_flush_timeout
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def connection_thread
|
84
|
+
backoff = Backoff.new
|
85
|
+
|
86
|
+
while @running
|
87
|
+
begin
|
88
|
+
backoff.before_connection_attempt
|
89
|
+
Kontena::Websocket::Client.connect(@uri, **create_options) do |client|
|
90
|
+
register client
|
91
|
+
|
92
|
+
Logger.instance.debug "WebSocket connected successfully"
|
93
|
+
@token_valid = true
|
94
|
+
backoff.after_connect
|
95
|
+
|
96
|
+
connection_pump client
|
97
|
+
end
|
98
|
+
rescue Exception => e
|
99
|
+
if !@token_valid && e.is_a?(Kontena::Websocket::ProtocolError) && e.message.include?("403")
|
100
|
+
@connection_error = Exceptions::RookInvalidToken.new @token
|
101
|
+
@ready_event.set
|
102
|
+
end
|
103
|
+
|
104
|
+
Logger.instance.info "Connection failed; reason = #{e.message}"
|
105
|
+
end
|
106
|
+
|
107
|
+
backoff.after_disconnect
|
108
|
+
Logger.instance.debug "Reconnecting"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def create_options
|
113
|
+
headers = {
|
114
|
+
"User-Agent" => "RookoutAgent/#{Config.rookout_version}+#{Config.rookout_commit}"
|
115
|
+
}
|
116
|
+
|
117
|
+
headers["X-Rookout-Token"] = @token unless @token.nil?
|
118
|
+
|
119
|
+
# NOTE: WE DONT HAVE PROXY SUPPORT (YET) - THIS WILL PROBABLY REQUIRE FORKING KONTENA
|
120
|
+
{ headers: headers,
|
121
|
+
connect_timeout: Config.agent_com_timeout,
|
122
|
+
open_timeout: Config.agent_com_timeout,
|
123
|
+
ping_interval: Config.agent_com_ping_interval,
|
124
|
+
ping_timeout: Config.agent_com_ping_timeout }
|
125
|
+
end
|
126
|
+
|
127
|
+
def register client
|
128
|
+
Logger.instance.info "Registering agent with id #{@agent_id}"
|
129
|
+
|
130
|
+
msg = Com::Rookout::NewAgentMessage.new agent_info: @info.pack
|
131
|
+
client.send wrap_in_envelope(msg)
|
132
|
+
end
|
133
|
+
|
134
|
+
ACCEPTED_MESSAGE_TYPES = [
|
135
|
+
Com::Rookout::InitialAugsCommand,
|
136
|
+
Com::Rookout::AddAugCommand,
|
137
|
+
Com::Rookout::ClearAugsCommand,
|
138
|
+
Com::Rookout::PingMessage,
|
139
|
+
Com::Rookout::RemoveAugCommand
|
140
|
+
].freeze
|
141
|
+
|
142
|
+
def connection_pump client
|
143
|
+
on_outgoing_exit = proc { client.disconnect }
|
144
|
+
send_thread = Thread.new { outgoing client, on_outgoing_exit }
|
145
|
+
send_thread.name = "rookout-outgoing-thread"
|
146
|
+
|
147
|
+
client.read do |raw_message|
|
148
|
+
envelope = Com::Rookout::Envelope.decode raw_message.pack("c*")
|
149
|
+
|
150
|
+
ACCEPTED_MESSAGE_TYPES.each do |klass|
|
151
|
+
next unless envelope.msg.is klass
|
152
|
+
emit klass.name, envelope.msg.unpack(klass)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Logger.instance.debug "Incoming loop - socket disconnected"
|
157
|
+
|
158
|
+
@pending_messages.push ExitMessage.new(send_thread)
|
159
|
+
send_thread.join
|
160
|
+
end
|
161
|
+
|
162
|
+
def outgoing client, on_exit
|
163
|
+
loop do
|
164
|
+
begin
|
165
|
+
msg = @pending_messages.pop true
|
166
|
+
rescue ThreadError
|
167
|
+
sleep 0.25
|
168
|
+
next
|
169
|
+
end
|
170
|
+
|
171
|
+
if msg.is_a? ExitMessage
|
172
|
+
break if msg.thread == Thread.current
|
173
|
+
elsif msg.is_a? FlushMessage
|
174
|
+
msg.event.set
|
175
|
+
else
|
176
|
+
begin
|
177
|
+
client.send msg
|
178
|
+
rescue RuntimeError
|
179
|
+
@queue << msg
|
180
|
+
break
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
rescue Exception => e
|
185
|
+
Logger.instance.exception "Outgoing thread failed", e
|
186
|
+
ensure
|
187
|
+
on_exit.call
|
188
|
+
end
|
189
|
+
|
190
|
+
def wrap_in_envelope message
|
191
|
+
any_message = Google::Protobuf::Any.pack message
|
192
|
+
timestamp = Google::Protobuf::Timestamp.new
|
193
|
+
timestamp.from_time Time.new
|
194
|
+
envelope = Com::Rookout::Envelope.new msg: any_message, timestamp: timestamp
|
195
|
+
envelope.to_proto.bytes
|
196
|
+
end
|
197
|
+
|
198
|
+
def reset_id
|
199
|
+
@agent_id = SecureRandom.uuid
|
200
|
+
@output.agent_id = @agent_id
|
201
|
+
@info.agent_id = @agent_id
|
202
|
+
end
|
203
|
+
|
204
|
+
class ExitMessage
|
205
|
+
def initialize thread
|
206
|
+
@thread = thread
|
207
|
+
end
|
208
|
+
|
209
|
+
attr_reader :thread
|
210
|
+
end
|
211
|
+
|
212
|
+
class FlushMessage
|
213
|
+
def initialize
|
214
|
+
@event = Concurrent::Event.new
|
215
|
+
end
|
216
|
+
|
217
|
+
attr_reader :event
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Rookout
|
2
|
+
module ComWs
|
3
|
+
require_relative "../config"
|
4
|
+
|
5
|
+
class Backoff
|
6
|
+
def initialize
|
7
|
+
@connected = false
|
8
|
+
@last_successful_connection = Time.mktime 1970
|
9
|
+
reset_backoff
|
10
|
+
end
|
11
|
+
|
12
|
+
def before_connection_attempt
|
13
|
+
return unless Time.new > @last_successful_connection + Config.backoff_reset_time
|
14
|
+
reset_backoff
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_disconnect
|
18
|
+
@connected = false
|
19
|
+
sleep @next_backoff
|
20
|
+
@next_backoff = [@next_backoff * 2, Config.backoff_max_time].min
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_connect
|
24
|
+
@connected = true
|
25
|
+
@last_successful_connection = Time.now
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def reset_backoff
|
31
|
+
@next_backoff = Config.backoff_factor
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Rookout
|
2
|
+
module ComWs
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
class CommandHandler
|
6
|
+
def initialize agent_com, augs_manager
|
7
|
+
agent_com.on "Com::Rookout::InitialAugsCommand" do |initial_augs|
|
8
|
+
augs = initial_augs.augs.map { |aug_json| JSON.parse aug_json }
|
9
|
+
augs_manager.initialize_augs augs
|
10
|
+
end
|
11
|
+
|
12
|
+
agent_com.on "Com::Rookout::AddAugCommand" do |command|
|
13
|
+
augs_manager.add_aug JSON.parse(command.aug_json)
|
14
|
+
end
|
15
|
+
|
16
|
+
agent_com.on "Com::Rookout::RemoveAugCommand" do |command|
|
17
|
+
augs_manager.remove_aug command.aug_id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Rookout
|
2
|
+
module ComWs
|
3
|
+
module Git
|
4
|
+
GIT_FOLDER = ".git".freeze
|
5
|
+
GIT_HEAD = "HEAD".freeze
|
6
|
+
GIT_CONFIG = "config".freeze
|
7
|
+
|
8
|
+
REMOTE_ORIGINS = /\[remote "origin"\]\s*url\s*=\s*(\S*)/.freeze
|
9
|
+
|
10
|
+
def find_root path
|
11
|
+
# Return if found
|
12
|
+
return path if git? path
|
13
|
+
|
14
|
+
# Get next, fail if there's no next
|
15
|
+
next_path = File.dirname path
|
16
|
+
return nil if next_path == path
|
17
|
+
|
18
|
+
# Recursive call
|
19
|
+
find_root next_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def revision path
|
23
|
+
follow_sym_links File.join(path, GIT_FOLDER), GIT_HEAD
|
24
|
+
end
|
25
|
+
|
26
|
+
def remote_origin path
|
27
|
+
git_config_path = File.join path, GIT_FOLDER, GIT_CONFIG
|
28
|
+
git_config_contents = File.read git_config_path
|
29
|
+
match = git_config_contents.match REMOTE_ORIGINS
|
30
|
+
return "" if match.captures.empty?
|
31
|
+
|
32
|
+
match.captures[0]
|
33
|
+
end
|
34
|
+
|
35
|
+
def git? path
|
36
|
+
potential_git_folder = File.join path, GIT_FOLDER
|
37
|
+
File.directory? potential_git_folder
|
38
|
+
end
|
39
|
+
|
40
|
+
def follow_sym_links root, link
|
41
|
+
link_path = File.join root, link
|
42
|
+
link_contents = File.read link_path
|
43
|
+
|
44
|
+
if link_contents.start_with? "ref:"
|
45
|
+
next_link = (link_contents.split " ")[1].strip
|
46
|
+
follow_sym_links root, next_link
|
47
|
+
else
|
48
|
+
link_contents.strip
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Rookout
|
2
|
+
module ComWs
|
3
|
+
require "socket"
|
4
|
+
require "English"
|
5
|
+
|
6
|
+
require_relative "../config"
|
7
|
+
|
8
|
+
require_relative "../protobuf/agent_info_pb"
|
9
|
+
|
10
|
+
class Information
|
11
|
+
require_relative "git"
|
12
|
+
include Git
|
13
|
+
|
14
|
+
def initialize labels
|
15
|
+
@agent_id = nil
|
16
|
+
@labels = labels.clone
|
17
|
+
@labels["rookout_debug"] = "on"
|
18
|
+
|
19
|
+
@ip_addr = local_ip
|
20
|
+
|
21
|
+
@scm_info = create_scm_information
|
22
|
+
end
|
23
|
+
|
24
|
+
def pack
|
25
|
+
version_info = Com::Rookout::VersionInformation.new version: Config.rookout_version,
|
26
|
+
commit: Config.rookout_commit
|
27
|
+
|
28
|
+
network_info = Com::Rookout::NetworkInformation.new ip_addr: @ip_addr
|
29
|
+
|
30
|
+
system_info = Com::Rookout::SystemInformation.new hostname: Socket.gethostname,
|
31
|
+
os: RUBY_PLATFORM,
|
32
|
+
os_version: "",
|
33
|
+
distro: "",
|
34
|
+
arch: ""
|
35
|
+
|
36
|
+
platform_info = Com::Rookout::PlatformInformation.new platform: "ruby",
|
37
|
+
version: RUBY_VERSION,
|
38
|
+
variant: RUBY_ENGINE
|
39
|
+
|
40
|
+
Com::Rookout::AgentInformation.new agent_id: @agent_id,
|
41
|
+
version: version_info,
|
42
|
+
network: network_info,
|
43
|
+
system: system_info,
|
44
|
+
platform: platform_info,
|
45
|
+
executable: $PROGRAM_NAME,
|
46
|
+
command_arguments: ARGV,
|
47
|
+
process_id: Process.pid,
|
48
|
+
labels: @labels,
|
49
|
+
scm: @scm_info
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_accessor :agent_id
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# rubocop:disable Style/ParallelAssignment
|
57
|
+
def local_ip
|
58
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
|
59
|
+
UDPSocket.open do |s|
|
60
|
+
s.connect "10.255.255.255", 1
|
61
|
+
s.addr.last
|
62
|
+
end
|
63
|
+
ensure
|
64
|
+
Socket.do_not_reverse_lookup = orig
|
65
|
+
end
|
66
|
+
# rubocop:enable Style/ParallelAssignment
|
67
|
+
|
68
|
+
def create_scm_information
|
69
|
+
user_git_origin = Config.user_git_origin || ENV["ROOKOUT_ORIGIN"]
|
70
|
+
user_git_commit = Config.user_git_commit || ENV["ROOKOUT_COMMIT"]
|
71
|
+
|
72
|
+
if user_git_origin.nil? && user_git_commit.nil?
|
73
|
+
search_path = File.dirname File.absolute_path($PROGRAM_NAME)
|
74
|
+
git_root = find_root search_path
|
75
|
+
if git_root
|
76
|
+
user_git_origin = remote_origin git_root
|
77
|
+
user_git_commit = revision git_root
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Com::Rookout::SCMInformation.new origin: user_git_origin, commit: user_git_commit
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|