rookout 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +12 -0
  3. data/bin/rookout +30 -0
  4. data/lib/rookout.rb +18 -0
  5. data/lib/rookout/augs/actions/action.rb +11 -0
  6. data/lib/rookout/augs/actions/action_run_processor.rb +29 -0
  7. data/lib/rookout/augs/aug.rb +121 -0
  8. data/lib/rookout/augs/aug_factory.rb +69 -0
  9. data/lib/rookout/augs/aug_rate_limiter.rb +96 -0
  10. data/lib/rookout/augs/augs_manager.rb +77 -0
  11. data/lib/rookout/augs/conditions/condition.rb +15 -0
  12. data/lib/rookout/augs/locations/location.rb +11 -0
  13. data/lib/rookout/augs/locations/location_file_line.rb +26 -0
  14. data/lib/rookout/com_ws/agent_com_ws.rb +221 -0
  15. data/lib/rookout/com_ws/backoff.rb +35 -0
  16. data/lib/rookout/com_ws/command_handler.rb +22 -0
  17. data/lib/rookout/com_ws/git.rb +53 -0
  18. data/lib/rookout/com_ws/information.rb +85 -0
  19. data/lib/rookout/com_ws/output.rb +135 -0
  20. data/lib/rookout/com_ws/token_bucket.rb +36 -0
  21. data/lib/rookout/config.rb +55 -0
  22. data/lib/rookout/exceptions.rb +140 -0
  23. data/lib/rookout/interface.rb +140 -0
  24. data/lib/rookout/logger.rb +158 -0
  25. data/lib/rookout/processor/namespace_serializer.rb +26 -0
  26. data/lib/rookout/processor/namespaces/container_namespace.rb +45 -0
  27. data/lib/rookout/processor/namespaces/frame_namespace.rb +65 -0
  28. data/lib/rookout/processor/namespaces/namespace.rb +28 -0
  29. data/lib/rookout/processor/namespaces/noop_namespace.rb +32 -0
  30. data/lib/rookout/processor/namespaces/ruby_object_namespace.rb +97 -0
  31. data/lib/rookout/processor/namespaces/ruby_object_serializer.rb +208 -0
  32. data/lib/rookout/processor/namespaces/ruby_utils_namespace.rb +65 -0
  33. data/lib/rookout/processor/namespaces/stack_namespace.rb +30 -0
  34. data/lib/rookout/processor/namespaces/traceback_namespace.rb +40 -0
  35. data/lib/rookout/processor/operations/operation.rb +11 -0
  36. data/lib/rookout/processor/operations/set_operation.rb +48 -0
  37. data/lib/rookout/processor/paths/arithmetic_path.rb +84 -0
  38. data/lib/rookout/processor/paths/canopy/actions.rb +184 -0
  39. data/lib/rookout/processor/paths/canopy/consts.rb +82 -0
  40. data/lib/rookout/processor/paths/canopy/maps.rb +2837 -0
  41. data/lib/rookout/processor/paths/canopy/markers.rb +186 -0
  42. data/lib/rookout/processor/paths/path.rb +15 -0
  43. data/lib/rookout/processor/processor.rb +34 -0
  44. data/lib/rookout/processor/processor_factory.rb +23 -0
  45. data/lib/rookout/processor/rook_error.rb +45 -0
  46. data/lib/rookout/protobuf/.gitignore +0 -0
  47. data/lib/rookout/protobuf/agent_info_pb.rb +68 -0
  48. data/lib/rookout/protobuf/controller_info_pb.rb +29 -0
  49. data/lib/rookout/protobuf/envelope_pb.rb +21 -0
  50. data/lib/rookout/protobuf/messages_pb.rb +189 -0
  51. data/lib/rookout/protobuf/variant_pb.rb +139 -0
  52. data/lib/rookout/rookout_singleton.rb +73 -0
  53. data/lib/rookout/services/position.rb +163 -0
  54. data/lib/rookout/services/tracer.rb +83 -0
  55. data/lib/rookout/trigger_services.rb +34 -0
  56. data/lib/rookout/user_warnings.rb +25 -0
  57. data/lib/rookout/version.rb +4 -0
  58. metadata +269 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 17eda3a68e52adf758e353e65066c8e68e65c5df18e67299c95e111a60717260
4
+ data.tar.gz: fd7cadcf2f2890130e9d92c6f56312c7c1a6bf1ad55efc84a83c39ad67f68020
5
+ SHA512:
6
+ metadata.gz: c1968166a99e6c85d7476de449bea06d7105bd9b0b71681c8c4263ccc33d88440e2a956a1d29d4681db44af1d6a8ea5e1cfb142895c182bb1388868c6f920035
7
+ data.tar.gz: f805c321af5605bade4d443557b3c575d7f432afa392820c4589bc8023af8a971e7a7d72f851ff2899ae7c4aca5fd3864f4e35dd0fb5e942cc4c3e570402cbdc
data/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ This software is a proprietary product of Rookout, and is protected under copyright laws and international copyright treaties. Any use of the software is subject to and governed by the license conditions below:
2
+ 1. You may run, execute or otherwise actively use this software (whether as a whole or portions of it) only if you have entered into a separate software-as-a-service agreement with Rookout.
3
+ 2. You may republish and redistribute this software but only within the same code repository it is found in.
4
+ 3. All other uses, reproductions, copies, distributions or publications of this software are strictly prohibited.
5
+ 4. You may not use this software to breach the security of the Rookout software-as-a-service, to circumvent, manipulate, impair or disrupt its operation, or perform any benchmark or penetration testing of the Rookout software-as-a-service.
6
+ 5. YOU MAY NOT USE THIS SOFTWARE FOR ANY ACTIVITY THAT CONSTITUTES, OR ENCOURAGES CONDUCT THAT WOULD CONSTITUTE, A CRIMINAL OFFENSE, GIVE RISE TO CIVIL LIABILITY OR OTHERWISE VIOLATE ANY APPLICABLE LAW.
7
+ Any use of this software is further subject to and governed by the following limitations:
8
+ THIS SOFTWARE IS PROVIDED TO YOU “AS IS”. ROOKOUT DOES NOT GUARANTEE, MAKES NO REPRESENTATION, AND PROVIDES NO WARRANTY ABOUT THIS SOFTWARE, INCLUDING ON QUALITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT.
9
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, ROOKOUT AND ITS STAFF WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, SPECIAL, STATUTORY OR PUNITIVE DAMAGES, LOSSES ARISING FROM, OR IN CONNECTION, WITH YOUR USE OF, OR RELIANCE UPON, THIS SOFTWARE.
10
+ IF YOU ARE ESTABLISHED OR LOCATED IN THE UNITED STATES OF AMERICA, THEN THIS LICENSE IS BY ROOKOUT, INC., A DELAWARE CORPORATE.
11
+ IF YOU ARE ESTABLISHED OR LOCATED ELSEWHERE THAN THE UNITED STATES OF AMERICA, THEN THIS LICENSE IS BY ROOKOUT, LTD., AN ISRAELI COMPANY.
12
+ © 2018 Rookout. All rights reserved.
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ def rookout_test
4
+ puts "[Rookout] Testing connection to agent"
5
+
6
+ debug = ARGV.include? "-v"
7
+
8
+ require "rookout/config"
9
+ Rookout::Config.agent_com_timeout = 10
10
+
11
+ begin
12
+ require "rookout"
13
+ Rookout.start debug: debug, throw_errors: true
14
+
15
+ success = true
16
+ rescue StandardError => e
17
+ puts e.full_message
18
+ success = false
19
+ end
20
+
21
+ if success
22
+ puts "[Rookout] Test Finished Successfully"
23
+ exit 0
24
+ else
25
+ puts "[Rookout] Test Failed"
26
+ exit 1
27
+ end
28
+ end
29
+
30
+ rookout_test
@@ -0,0 +1,18 @@
1
+ module Rookout
2
+ module_function
3
+
4
+ def start options = {}
5
+ require_relative "rookout/interface"
6
+ Interface.instance.start options
7
+ end
8
+
9
+ def flush
10
+ require_relative "rookout/interface"
11
+ Interface.instance.flush
12
+ end
13
+
14
+ def stop
15
+ require_relative "rookout/interface"
16
+ Interface.instance.stop
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ module Rookout
2
+ module Augs
3
+ module Actions
4
+ class Action
5
+ def execute _aug_id, _report_id, _namespace, _output
6
+ raise NotImplementedError
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "action"
2
+
3
+ module Rookout
4
+ module Augs
5
+ module Actions
6
+ class ActionRunProcessor < Action
7
+ def initialize arguments, processor_factory
8
+ @processor = processor_factory.create_processor arguments["operations"]
9
+
10
+ post_operations = arguments["post_operations"]
11
+ if post_operations
12
+ @post_processor = processor_factory.create_processor post_operations
13
+ else
14
+ @post_processor = nil
15
+ end
16
+ end
17
+
18
+ def execute aug_id, report_id, namespace, _output
19
+ @processor.process namespace
20
+ @output.send_user_message aug_id, report_id, namespace.read_attribute("store")
21
+ return unless @post_processor
22
+
23
+ @output.flush_message
24
+ @post_processor.process namespace
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,121 @@
1
+ require_relative "../logger"
2
+ require_relative "../user_warnings"
3
+ require_relative "../processor/rook_error"
4
+
5
+ module Rookout
6
+ module Augs
7
+ require "securerandom"
8
+ require "concurrent-ruby/concurrent/atom"
9
+
10
+ require_relative "../processor/namespaces/frame_namespace"
11
+ require_relative "../processor/namespaces/stack_namespace"
12
+ require_relative "../processor/namespaces/container_namespace"
13
+ require_relative "../processor/namespaces/ruby_utils_namespace"
14
+ require_relative "../processor/namespaces/noop_namespace"
15
+
16
+ require_relative "../logger"
17
+
18
+ MAX_LOG_CACHE_SIZE = 10
19
+
20
+ class Aug
21
+ def initialize aug_id, location, action, condition, rate_limiter, _max_aug_time, output
22
+ # NOTE: max_aux_time is not implemented
23
+
24
+ @id = aug_id
25
+ @location = location
26
+ @action = action
27
+ @condition = condition
28
+ @rate_limiter = rate_limiter
29
+ @output = output
30
+
31
+ @enabled = true
32
+ @status = nil
33
+ @log_cache = []
34
+ end
35
+ end
36
+
37
+ attr_reader :id
38
+
39
+ def add_aug trigger_services
40
+ @location.add_aug trigger_services, self
41
+ rescue SystemExit
42
+ raise
43
+ rescue Exception => e
44
+ message = "Exception when adding aug"
45
+ Logger.instance.error message, e
46
+ notify_error RookError.new e, message
47
+ end
48
+
49
+ def execute frame, extracted
50
+ return unless @enabled
51
+
52
+ UserWarnings.with self do
53
+ begin
54
+ namespace = create_namespaces frame, extracted
55
+
56
+ return if @condition && !@condition.evaluate(namespace)
57
+
58
+ @rate_limiter.with_limit do
59
+ report_id = SecureRandom.uuid
60
+ @action.execute @aug_id, report_id, namespace, @output
61
+ end
62
+ rescue SystemExit
63
+ raise
64
+ rescue Exception => e
65
+ message = "Exception while processing Aug"
66
+ error = RookError.new e, message
67
+ notify_warning error unless silence_log? error
68
+ end
69
+ end
70
+ end
71
+
72
+ def notify_active
73
+ send_rule_status :Active
74
+ end
75
+
76
+ def notify_pending
77
+ send_rule_status :Pending
78
+ end
79
+
80
+ def notify_removed
81
+ send_rule_status :Deleted
82
+ end
83
+
84
+ def notify_error error
85
+ send_rule_status :Error, error
86
+ end
87
+
88
+ def notify_warning error
89
+ return if silence_log? error
90
+ send_rule_status :Warning, error
91
+ end
92
+
93
+ private
94
+
95
+ def create_namespaces frame, extracted
96
+ ContainerNamespace.new("frame": Processor::Namespaces::FrameNamespace.new(frame[1]),
97
+ "stack": Processor::Namespaces::StackNamespace.new(frame, 1),
98
+ "extracted": Processor::Namespaces::ContainerNamespace.new(extracted),
99
+ "store": Processor::Namespaces::ContainerNamespace.new,
100
+ "temp": Processor::Namespaces::ContainerNamespace.new,
101
+ "utils": Processor::Namespaces::RubyUtisNamespace.new,
102
+ "trace": Processor::Namespaces::NoopNamespace.new)
103
+ end
104
+
105
+ def silence_log? error
106
+ return true if @log_cache.length >= MAX_LOG_CACHE_SIZE || @log_cache.include(error.message)
107
+
108
+ @log_cache << error.message
109
+ false
110
+ end
111
+
112
+ def send_rule_status status, error = nil
113
+ return unless @status != status
114
+
115
+ Logger.instance.info "Updating rule status for #{@id} to #{status}"
116
+ @status = status
117
+
118
+ @output.send_rule_status @id, status, error
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,69 @@
1
+ module Rookout
2
+ module Augs
3
+ require_relative "../exceptions"
4
+ require_relative "../config"
5
+ require_relative "../processor/processor_factory"
6
+
7
+ require_relative "actions/action_run_processor"
8
+ require_relative "conditions/condition"
9
+ require_relative "aug_rate_limiter"
10
+
11
+ class AugFactory
12
+ def initialize output
13
+ @output = output
14
+ @factory = Processor::ProcessorFactory.new
15
+ end
16
+
17
+ def create_aug configuration
18
+ aug_id = configuration["id"]
19
+ raise Exceptions.RookAugInvalidKey "id", configuration unless aug_id.is_a? String
20
+
21
+ location_configuration = configuration["location"]
22
+ raise Exceptions.RookAugInvalidKey "location", configuration unless location_configuration.is_a? Hash
23
+ location = create_location location_configuration
24
+
25
+ action_configuration = configuration["action"]
26
+ raise Exceptions.RookAugInvalidKey "action", configuration unless action_configuration.is_a? Hash
27
+ action = Actions.ActionRunProcess.new action_configuration, @factory
28
+
29
+ max_aug_time = configuration["maxAugTime"] || Config.INSTRUMENTATION_MAX_AUG_TIME
30
+
31
+ condition_configuration = configuration["conditional"]
32
+ raise Exceptions.RookAugInvalidKey "conditional", configuration unless condition_configuration.is_a? String
33
+ condition = Conditions.Condition.new condition_configuration
34
+
35
+ rate_limit = create_rate_limit configuration
36
+
37
+ Aug.new aug_id, location, action, condition, rate_limit, max_aug_time, @output
38
+ end
39
+
40
+ private
41
+
42
+ def create_location configuration
43
+ name = configuration["name"]
44
+ raise Exceptions.RookObjectNameMissing if name.nil?
45
+ raise Exceptions.RookUnsupportedLocation if name != "file_line"
46
+
47
+ Locations.LocationFileLine.new configuration, @factory
48
+ end
49
+
50
+ def create_rate_limit configuration
51
+ window_quota = 200
52
+ window_size = 5000
53
+
54
+ rate_limit = configuration["rate_limit"]
55
+ if rate_limit
56
+ rate_limit_splitted = rate_limit.split "/"
57
+ if rate_limit_splitted.length == 2
58
+ window_quota = rate_limit_splitted[0]
59
+ window_size = rate_limit_splitted[1]
60
+ end
61
+ end
62
+
63
+ rate_limit_modifier = configuration["rateLimitModifier"] || 5
64
+
65
+ AugRateLimiter.new window_quota, window_size, rate_limit_modifier
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,96 @@
1
+ module Rookout
2
+ module Augs
3
+ require_relative "../exceptions"
4
+
5
+ class AugRateLimiter
6
+ def initialize quota, window_size, active_limit
7
+ @quota = quota
8
+ @window_size = window_size
9
+ @active_weight = quota / active_limit
10
+
11
+ @mutex = Mutex.new
12
+ @active_count = 0
13
+ @windows = {}
14
+ end
15
+
16
+ def with_limit start_time = nil
17
+ active = false
18
+ start_time ||= Time.new
19
+
20
+ # If quota, verify it
21
+ if @quota
22
+ # Get current time
23
+ now_ms = (now * 1000).to_i
24
+
25
+ # Calculate window keys
26
+ current_window_key = (now_ms / @window_size) * @window_size
27
+ prev_window_key = current_window_key - @window_size
28
+
29
+ @mutex.synchronize do
30
+ # Clean old windows
31
+ cleanup now_ms
32
+
33
+ # Increase active count
34
+ @active_count += 1
35
+ active = true
36
+
37
+ # If exceeding quota
38
+ if current_usage(now_ms, current_window_key, prev_window_key) > @quota
39
+ warning = RookError.new Exceptions::RookRuleRateLimited.new
40
+ UserWarnings.notify_warning warning
41
+ return
42
+ end
43
+ end
44
+ end
45
+
46
+ begin
47
+ yield
48
+ ensure
49
+ @mutex.synchronize { record_usage current_window_key, (Time.now - start_time) * 1000 } if @quota
50
+ end
51
+ ensure
52
+ # Reduce active count
53
+ @mutex.synchronize { @active_count -= 1 } if active
54
+ end
55
+
56
+ private
57
+
58
+ def current_usage now_ms, current_window_key, prev_window_key
59
+ # Get usage information
60
+ current_window_usage = @windows[current_window_key]
61
+ if current_window_usage.nil?
62
+ # Create new window
63
+ current_window_usage = @windows[current_window_key] = 0
64
+ end
65
+ prev_window_usage = @windows[prev_window_key] || 0
66
+
67
+ # Previous window weight
68
+ prev_weight = 1 - (now_ms - current_window_key) / @window_size.to_f
69
+
70
+ # Final weighted usage
71
+ (prev_window_usage * prev_weight) + (@active_count * @active_weight) + current_window_usage
72
+ end
73
+
74
+ def record_usage current_window_key, duration
75
+ # windows might be cleared while aug is running (unlikely)
76
+ prev_value = @windows[current_window_key]
77
+ return if prev_value.nil?
78
+
79
+ # Add value to quota
80
+ @windows[current_window_key] += max(duration, 5).to_f
81
+ end
82
+
83
+ def cleanup now_ms
84
+ # Don't bother with contention
85
+ return if @mutex.locked?
86
+
87
+ @mutex.synchronize do
88
+ # every 5 windows-times, clear windows older than 10 window-times
89
+ if @windows.length > 10
90
+ @windows.reject! { |key, _| key < (now_ms - @window_size * 5) }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,77 @@
1
+ module Rookout
2
+ module Augs
3
+ require_relative "../logger"
4
+
5
+ require_relative "aug_factory"
6
+
7
+ class AugsManager
8
+ def initialize trigger_services, output
9
+ @trigger_services = trigger_services
10
+ @output = output
11
+ @aug_factory = AugFactory.new @output
12
+ @augs = []
13
+ end
14
+
15
+ def initialize_augs augs
16
+ leftovers = @augs.clone
17
+
18
+ augs.each do |aug|
19
+ begin
20
+ aug_id = aug["id"]
21
+ rescue KeyError
22
+ next
23
+ end
24
+
25
+ leftovers.delete aug_id
26
+
27
+ add_aug aug
28
+ end
29
+
30
+ leftovers.each { |aug_id| remove_aug aug_id }
31
+ end
32
+
33
+ def add_aug configuration
34
+ begin
35
+ aug = @aug_factory.create_aug configuration
36
+ rescue StandardError => e
37
+ message = "Failed to parse aug"
38
+ Logger.instance.exception message, e
39
+
40
+ begin
41
+ aug_id = configuration["id"]
42
+ rescue StandardError
43
+ return
44
+ end
45
+
46
+ error = RookError.new e, message
47
+ @output.send_rule_status aug_id, "Error", error
48
+ return
49
+ end
50
+
51
+ if @augs.include? aug.id
52
+ Logger.instance.debug "Aug already set - #{aug.id}"
53
+ return
54
+ end
55
+
56
+ Logger.instance.debug "Adding aug-\t#{aug.id}"
57
+
58
+ aug.add_aug @trigger_services
59
+ @augs.push aug.id
60
+ end
61
+
62
+ def remove_aug aug_id
63
+ Logger.instance.debug "Removing aug-\t#{aug_id}"
64
+
65
+ @trigger_services.remove_aug aug_id
66
+ @augs.delete aug_id
67
+ end
68
+
69
+ def clear_augs
70
+ Logger.instance.debug "Clearing all augs"
71
+
72
+ @augs.clone.each { |aug| remove_aug aug }
73
+ @trigger_services.clear_augs
74
+ end
75
+ end
76
+ end
77
+ end