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