rookout 0.1.0 → 0.1.5

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rookout/augs/actions/action_run_processor.rb +3 -3
  3. data/lib/rookout/augs/aug.rb +24 -90
  4. data/lib/rookout/augs/aug_factory.rb +24 -14
  5. data/lib/rookout/augs/aug_rate_limiter.rb +3 -3
  6. data/lib/rookout/augs/augs_manager.rb +3 -1
  7. data/lib/rookout/augs/conditions/condition.rb +4 -2
  8. data/lib/rookout/augs/locations/location.rb +75 -1
  9. data/lib/rookout/augs/locations/location_exception_handler.rb +22 -0
  10. data/lib/rookout/augs/locations/location_file_line.rb +16 -4
  11. data/lib/rookout/com_ws/agent_com_ws.rb +28 -33
  12. data/lib/rookout/com_ws/information.rb +1 -1
  13. data/lib/rookout/com_ws/output.rb +4 -10
  14. data/lib/rookout/com_ws/pinger.rb +36 -0
  15. data/lib/rookout/com_ws/websocket_client.rb +143 -0
  16. data/lib/rookout/commit.rb +3 -0
  17. data/lib/rookout/config.rb +1 -0
  18. data/lib/rookout/exceptions.rb +40 -0
  19. data/lib/rookout/interface.rb +41 -24
  20. data/lib/rookout/logger.rb +16 -2
  21. data/lib/rookout/processor/namespace_serializer.rb +1 -1
  22. data/lib/rookout/processor/namespaces/frame_namespace.rb +1 -0
  23. data/lib/rookout/processor/namespaces/namespace.rb +2 -2
  24. data/lib/rookout/processor/namespaces/noop_namespace.rb +1 -1
  25. data/lib/rookout/processor/namespaces/ruby_object_serializer.rb +4 -3
  26. data/lib/rookout/processor/operations/set_operation.rb +4 -1
  27. data/lib/rookout/processor/paths/arithmetic_path.rb +1 -1
  28. data/lib/rookout/processor/paths/canopy/markers.rb +5 -2
  29. data/lib/rookout/rookout_singleton.rb +4 -3
  30. data/lib/rookout/services/position.rb +73 -73
  31. data/lib/rookout/services/tracer.rb +8 -5
  32. data/lib/rookout/trigger_services.rb +2 -2
  33. data/lib/rookout/user_warnings.rb +2 -0
  34. data/lib/rookout/utils.rb +9 -0
  35. data/lib/rookout/version.rb +1 -2
  36. metadata +37 -32
@@ -1,7 +1,5 @@
1
1
  module Rookout
2
2
  module ComWs
3
- require "securerandom"
4
- require "kontena-websocket-client"
5
3
  require "event_emitter"
6
4
  require "google/protobuf/well_known_types"
7
5
  require "concurrent"
@@ -10,14 +8,17 @@ module Rookout
10
8
  require_relative "../logger"
11
9
  require_relative "../user_warnings"
12
10
  require_relative "../exceptions"
11
+ require_relative "../utils"
13
12
 
14
13
  require_relative "../processor/rook_error"
15
14
 
16
15
  require_relative "../protobuf/messages_pb"
17
16
  require_relative "../protobuf/envelope_pb"
18
17
 
18
+ require_relative "websocket_client"
19
19
  require_relative "backoff"
20
20
  require_relative "information"
21
+ require_relative "pinger"
21
22
 
22
23
  class AgentComWs
23
24
  include EventEmitter
@@ -68,7 +69,11 @@ module Rookout
68
69
  end
69
70
 
70
71
  def wait_for_ready
71
- @ready_event.wait Config.agent_com_timeout
72
+ is_finished = @ready_event.wait Config.agent_com_timeout
73
+ # We didn't finish - will keep trying in the background
74
+ raise Exceptions::RookCommunicationException unless is_finished
75
+
76
+ # We finished - raise if we failed
72
77
  raise @connection_error if @connection_error
73
78
  end
74
79
 
@@ -86,17 +91,15 @@ module Rookout
86
91
  while @running
87
92
  begin
88
93
  backoff.before_connection_attempt
89
- Kontena::Websocket::Client.connect(@uri, **create_options) do |client|
90
- register client
94
+ client = open_new_connection
91
95
 
92
- Logger.instance.debug "WebSocket connected successfully"
93
- @token_valid = true
94
- backoff.after_connect
96
+ Logger.instance.debug "WebSocket connected successfully"
97
+ @token_valid = true
98
+ backoff.after_connect
95
99
 
96
- connection_pump client
97
- end
100
+ connection_pump client
98
101
  rescue Exception => e
99
- if !@token_valid && e.is_a?(Kontena::Websocket::ProtocolError) && e.message.include?("403")
102
+ if !@token_valid && e.message.include?("403")
100
103
  @connection_error = Exceptions::RookInvalidToken.new @token
101
104
  @ready_event.set
102
105
  end
@@ -109,26 +112,15 @@ module Rookout
109
112
  end
110
113
  end
111
114
 
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
115
+ def open_new_connection
116
+ client = WebsocketClient.new @uri, @proxy, @token
117
+ client.connect
126
118
 
127
- def register client
128
119
  Logger.instance.info "Registering agent with id #{@agent_id}"
129
-
130
120
  msg = Com::Rookout::NewAgentMessage.new agent_info: @info.pack
131
- client.send wrap_in_envelope(msg)
121
+ client.send_frame wrap_in_envelope(msg)
122
+
123
+ client
132
124
  end
133
125
 
134
126
  ACCEPTED_MESSAGE_TYPES = [
@@ -140,11 +132,11 @@ module Rookout
140
132
  ].freeze
141
133
 
142
134
  def connection_pump client
143
- on_outgoing_exit = proc { client.disconnect }
135
+ on_outgoing_exit = proc { client.close }
144
136
  send_thread = Thread.new { outgoing client, on_outgoing_exit }
145
137
  send_thread.name = "rookout-outgoing-thread"
146
138
 
147
- client.read do |raw_message|
139
+ message_handler = proc do |raw_message|
148
140
  envelope = Com::Rookout::Envelope.decode raw_message.pack("c*")
149
141
 
150
142
  ACCEPTED_MESSAGE_TYPES.each do |klass|
@@ -153,6 +145,8 @@ module Rookout
153
145
  end
154
146
  end
155
147
 
148
+ client.connection_pump message_handler
149
+
156
150
  Logger.instance.debug "Incoming loop - socket disconnected"
157
151
 
158
152
  @pending_messages.push ExitMessage.new(send_thread)
@@ -160,7 +154,7 @@ module Rookout
160
154
  end
161
155
 
162
156
  def outgoing client, on_exit
163
- loop do
157
+ Pinger.new(client, Config.agent_com_ping_interval, Config.agent_com_ping_timeout).repeat do
164
158
  begin
165
159
  msg = @pending_messages.pop true
166
160
  rescue ThreadError
@@ -174,7 +168,7 @@ module Rookout
174
168
  msg.event.set
175
169
  else
176
170
  begin
177
- client.send msg
171
+ client.send_frame msg
178
172
  rescue RuntimeError
179
173
  @queue << msg
180
174
  break
@@ -184,6 +178,7 @@ module Rookout
184
178
  rescue Exception => e
185
179
  Logger.instance.exception "Outgoing thread failed", e
186
180
  ensure
181
+ Logger.instance.debug "Outgoing thread exiting"
187
182
  on_exit.call
188
183
  end
189
184
 
@@ -196,7 +191,7 @@ module Rookout
196
191
  end
197
192
 
198
193
  def reset_id
199
- @agent_id = SecureRandom.uuid
194
+ @agent_id = Utils.uuid
200
195
  @output.agent_id = @agent_id
201
196
  @info.agent_id = @agent_id
202
197
  end
@@ -14,7 +14,7 @@ module Rookout
14
14
  def initialize labels
15
15
  @agent_id = nil
16
16
  @labels = labels.clone
17
- @labels["rookout_debug"] = "on"
17
+ @labels["rookout_debug"] = "on" if Config.debug
18
18
 
19
19
  @ip_addr = local_ip
20
20
 
@@ -57,19 +57,13 @@ module Rookout
57
57
  return if @closing || !@agent_com
58
58
 
59
59
  @rule_status_update_bucket.if_available do
60
- protobuf_error = nil
60
+ status = Com::Rookout::RuleStatusMessage.new agent_id: @agent_id,
61
+ rule_id: rule_id,
62
+ active: active
61
63
  if error
62
- protobuf_error = Com::Rookout::Error.new message: error.messsage,
63
- type: error.type
64
- protobuf_error.parameters.copy_from error.parameters
65
- protobuf_error.exc.copy_from error.exc
66
- protobuf_error.traceback.copy_from error.traceback
64
+ status.error = error.dumps
67
65
  end
68
66
 
69
- status = Com::Rookout::RuleStatusMessage.new agent_id: @agent_id,
70
- rule_id: rule_id,
71
- active: active,
72
- error: protobuf_error
73
67
  @agent_com.add status
74
68
  end
75
69
  end
@@ -0,0 +1,36 @@
1
+ module Rookout
2
+ module ComWs
3
+ require_relative "../logger"
4
+
5
+ class Pinger
6
+ def initialize connection, interval, timeout
7
+ @interval = interval
8
+ @timeout = timeout
9
+ @connection = connection
10
+
11
+ @last_pong = Time.now
12
+ @last_ping = Time.now
13
+ end
14
+
15
+ def repeat
16
+ loop do
17
+ if Time.now - @last_ping > @interval
18
+ Logger.instance.debug "Sending Ping"
19
+ @last_ping = Time.now
20
+ @connection.ping Time.now.to_s do
21
+ Logger.instance.debug "Got Ping reply"
22
+ @last_pong = Time.now
23
+ end
24
+ end
25
+
26
+ if Time.now - @last_pong > @timeout
27
+ Logger.instance.debug "Ping timeout"
28
+ break
29
+ end
30
+
31
+ yield
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,143 @@
1
+ module Rookout
2
+ module ComWs
3
+ require "openssl"
4
+ require "websocket/driver"
5
+
6
+ require_relative "../config"
7
+ require_relative "../logger"
8
+ require_relative "../exceptions"
9
+
10
+ class WebsocketClient
11
+ def initialize url, proxy, token
12
+ @token = token
13
+ @connection = WebsocketConnection.new url, proxy
14
+ @driver = nil
15
+ @error = nil
16
+ @last_ping = nil
17
+ end
18
+
19
+ def connect
20
+ # TODO: add proxy support
21
+ @connection.connect
22
+ @driver = WebSocket::Driver.client @connection
23
+
24
+ headers.each do |key, value|
25
+ @driver.set_header key, value
26
+ end
27
+
28
+ @driver.on :error do |error|
29
+ @error = error
30
+ end
31
+
32
+ # Connect to the remote server
33
+ @driver.start
34
+ # TODO: ADD CONNECT TIMEOUT
35
+ while @driver.state == :connecting
36
+ recv_data = @connection.read_char
37
+ @driver.parse recv_data
38
+ end
39
+
40
+ raise Exceptions::RookWebsocketException, @error if @driver.state != :open
41
+ end
42
+
43
+ def connection_pump message_handler
44
+ @driver.on :message do |e|
45
+ message_handler.call e.data
46
+ end
47
+
48
+ until @driver.state == :closed
49
+ recv_data = @connection.read_char
50
+ @driver.parse recv_data
51
+ end
52
+ end
53
+
54
+ def send_frame msg
55
+ @driver.binary msg
56
+ end
57
+
58
+ def ping message, &callback
59
+ @driver.ping message, &callback
60
+ end
61
+
62
+ def close
63
+ return if @driver.nil?
64
+
65
+ @driver.close
66
+ @connection.close
67
+ end
68
+
69
+ private
70
+
71
+ def headers
72
+ result = {
73
+ "User-Agent" => "RookoutAgent/#{Config.rookout_version}+#{Config.rookout_commit}"
74
+ }
75
+
76
+ result["X-Rookout-Token"] = @token unless @token.nil?
77
+
78
+ result
79
+ end
80
+ end
81
+
82
+ class WebsocketConnection
83
+ def initialize url, proxy
84
+ @url = url
85
+ @proxy = proxy
86
+
87
+ @uri = URI.parse @url
88
+ @secure = %w[https wss].include? @uri.scheme
89
+
90
+ @socket = nil
91
+ end
92
+
93
+ def connect
94
+ if @secure
95
+ @socket = ssl_connect
96
+ else
97
+ @socket = tcp_connect
98
+ end
99
+ end
100
+
101
+ attr_reader :url
102
+
103
+ def read_char
104
+ @socket.getc
105
+ end
106
+
107
+ def write buffer
108
+ @socket << buffer
109
+ end
110
+
111
+ def close
112
+ return if @socket.nil?
113
+
114
+ @socket.close
115
+ @socket = nil
116
+ end
117
+
118
+ private
119
+
120
+ def tcp_connect
121
+ TCPSocket.new @uri.host,
122
+ @uri.port || (@secure ? 443 : 80)
123
+ end
124
+
125
+ def ssl_connect
126
+ tcp_socket = tcp_connect
127
+
128
+ ctx = ::OpenSSL::SSL::SSLContext.new
129
+ ctx.min_version = ::OpenSSL::SSL::TLS1_2_VERSION
130
+ cert_store = ::OpenSSL::X509::Store.new
131
+ cert_store.set_default_paths
132
+ ctx.cert_store = cert_store
133
+
134
+ ssl_socket = ::OpenSSL::SSL::SSLSocket.new tcp_socket, ctx
135
+ ssl_socket.hostname = @uri.host
136
+ ssl_socket.sync_close = true
137
+ ssl_socket.connect
138
+
139
+ ssl_socket
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,3 @@
1
+ module Rookout
2
+ COMMIT = "2a847bf58dabb4d6abd2957f4c382256a4a467db".freeze
3
+ end
@@ -1,5 +1,6 @@
1
1
  module Rookout
2
2
  require_relative "version"
3
+ require_relative "commit"
3
4
 
4
5
  module Config
5
6
  # Magic to allow for module variables to be easily accessible
@@ -136,5 +136,45 @@ module Rookout
136
136
  super "No Rookout token was supplied. Make sure to pass the Rookout Token when starting the rook"
137
137
  end
138
138
  end
139
+
140
+ class RookInvalidOptions < ToolException
141
+ def initialize description
142
+ super description
143
+ end
144
+ end
145
+
146
+ class RookCrcMismatchException < ToolException
147
+ def initialize filepath, expected, calculated
148
+ super "Line CRC32s do not match! path: #{filepath}, expected: #{expected}, calculated:#{calculated}",
149
+ {
150
+ "filepath" => filepath,
151
+ "expected" => expected,
152
+ "calculated" => calculated
153
+ }
154
+ end
155
+ end
156
+
157
+ class RookLineMoved < ToolException
158
+ def initialize filepath, old_line_no, new_line_no
159
+ super "Line has moved! path: #{filepath}, original line no: #{old_line_no}, new line no: #{new_line_no}",
160
+ {
161
+ "filepath" => filepath,
162
+ "old_line_no" => old_line_no,
163
+ "new_line_no" => new_line_no
164
+ }
165
+ end
166
+ end
167
+
168
+ class RookCommunicationException < ToolException
169
+ def initialize
170
+ super "Failed to connect to the controller - will continue attempting in the background"
171
+ end
172
+ end
173
+
174
+ class RookWebsocketException < ToolException
175
+ def initialize error
176
+ super "Error from Websocket #{error}", { "error" => error }
177
+ end
178
+ end
139
179
  end
140
180
  end
@@ -5,6 +5,7 @@ module Rookout
5
5
 
6
6
  require_relative "config"
7
7
  require_relative "exceptions"
8
+ include Exceptions
8
9
 
9
10
  def initialize
10
11
  @rook = nil
@@ -19,24 +20,21 @@ module Rookout
19
20
  require_relative "rookout_singleton"
20
21
 
21
22
  configure_logging options
22
- labels = options[:labels] || parse_labels(ENV["ROOKOUT_LABELS"])
23
- validate_labels labels
24
-
25
23
  configure_git options
26
24
 
27
- com = configure_com options
28
-
29
- async_start = true? ENV["ROOKOUT_ASYNC_START"]
30
- fork = evaluate_flag options[:fork], "ROOKOUT_ENABLE_FORK"
31
-
32
- print_config com, labels: labels, async_start: async_start, fork: fork if Config.debug
25
+ start_options = configure_start_options options
26
+ print_config start_options
33
27
 
34
28
  rook = RookoutSingleton.instance
35
- rook.connect com[:token], com[:host], com[:port], com[:proxy], labels, async_start, fork
36
-
37
- # TODO: ADD DETAILED PROCESSING FOR SPECIFIC ERRORS
29
+ rook.connect(**start_options)
30
+ rescue RookMissingToken, RookInvalidToken, RookInvalidOptions, RookVersionNotSupported => e
31
+ raise if throw_errors
32
+ STDERR.puts "[Rookout] Failed to start Rookout: #{e.message}"
33
+ rescue RookCommunicationException => e
34
+ raise if throw_errors
35
+ STDERR.puts "[Rookout] " + e.message
38
36
  rescue Exception => e
39
- puts e.full_message if Config.debug
37
+ STDERR.puts e.full_message if Config.debug
40
38
  raise if throw_errors
41
39
  end
42
40
  end
@@ -59,7 +57,7 @@ module Rookout
59
57
  def configure_logging options
60
58
  if Config.debug
61
59
  log_to_stderr = true
62
- log_level = "DEBUG"
60
+ log_level = :DEBUG
63
61
  else
64
62
  log_to_stderr = evaluate_flag options[:log_to_stderr], "ROOKOUT_LOG_TO_STDERR"
65
63
  log_level = options[:log_level] || ENV["ROOKOUT_LOG_FILE"]
@@ -77,16 +75,22 @@ module Rookout
77
75
  Config.user_git_commit = options[:git_commit] if options[:git_commit]
78
76
  end
79
77
 
80
- def configure_com options
78
+ def configure_start_options options
81
79
  host = evaluate_config options[:host], "ROOKOUT_CONTROLLER_HOST", "wss://control.rookout.com"
82
80
  port = evaluate_config options[:port], "ROOKOUT_CONTROLLER_PORT", 443
83
81
  proxy = evaluate_config options[:proxy], "ROOKOUT_PROXY"
84
82
  token = evaluate_config options[:token], "ROOKOUT_TOKEN"
85
83
 
86
- raise Exceptions::RookMissingToken if token.nil? && host == "wss://control.rookout.com"
84
+ raise RookMissingToken if token.nil? && host == "wss://control.rookout.com"
87
85
  verify_token token if token
88
86
 
89
- { host: host, port: port, proxy: proxy, token: token }
87
+ labels = stringify_labels(options[:labels]) || parse_labels(ENV["ROOKOUT_LABELS"])
88
+ validate_labels labels
89
+
90
+ async_start = true? ENV["ROOKOUT_ASYNC_START"]
91
+ fork = evaluate_flag options[:fork], "ROOKOUT_ENABLE_FORK"
92
+
93
+ { host: host, port: port, proxy: proxy, token: token, labels: labels, async_start: async_start, fork: fork }
90
94
  end
91
95
 
92
96
  def evaluate_flag argument, env_var_name
@@ -104,6 +108,18 @@ module Rookout
104
108
  default
105
109
  end
106
110
 
111
+ def stringify_labels labels
112
+ return nil unless labels
113
+
114
+ stringified_labels = {}
115
+
116
+ labels.each do |label_name, label_value|
117
+ stringified_labels[label_name.to_s] = label_value.to_s
118
+ end
119
+
120
+ stringified_labels
121
+ end
122
+
107
123
  def parse_labels raw_labels
108
124
  labels = {}
109
125
  return labels if raw_labels.nil?
@@ -120,21 +136,22 @@ module Rookout
120
136
  def validate_labels labels
121
137
  labels.each do |label_name, _|
122
138
  if label_name.start_with? "$"
123
- raise Exceptions::RookInvalidLabel, label_name
139
+ raise RookInvalidLabel, label_name
124
140
  end
125
141
  end
126
142
  end
127
143
 
128
144
  def verify_token token
129
- raise Exceptions::RookInvalidOptions, "Rookout token should be a String" unless token.is_a? String
130
- raise Exceptions::RookInvalidOptions, "Rookout token should be 64 characters" unless token.length == 64
131
- raise Exceptions::RookInvalidOptions, "Rookout token must consist of only hexadecimal characters" unless
145
+ raise RookInvalidOptions, "Rookout token should be a String" unless token.is_a? String
146
+ raise RookInvalidOptions, "Rookout token should be 64 characters" unless token.length == 64
147
+ raise RookInvalidOptions, "Rookout token must consist of only hexadecimal characters" unless
132
148
  token.match(/^[0-9a-zA-Z]{0,64}$/)
133
149
  end
134
150
 
135
- def print_config com, options
136
- puts "[Rookout] Communication Options: #{com}"
137
- puts "[Rookout] Other Options #{options}"
151
+ def print_config options
152
+ return unless Config.debug
153
+
154
+ puts "[Rookout] Start Options: #{options}"
138
155
  end
139
156
  end
140
157
  end