rookout 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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