aikido-zen 1.5.0 → 1.6.0.beta.1
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 +4 -4
- data/benchmarks/ipc/benchmark.rb +149 -0
- data/benchmarks/rpc/benchmark.rb +57 -0
- data/docs/rails.md +22 -0
- data/lib/aikido/zen/actor.rb +10 -10
- data/lib/aikido/zen/agent.rb +4 -12
- data/lib/aikido/zen/api_cache.rb +8 -0
- data/lib/aikido/zen/api_stream.rb +1 -26
- data/lib/aikido/zen/attack.rb +14 -14
- data/lib/aikido/zen/attack_wave.rb +8 -8
- data/lib/aikido/zen/collector/event.rb +26 -26
- data/lib/aikido/zen/collector/routes.rb +4 -4
- data/lib/aikido/zen/collector/sink_stats.rb +10 -10
- data/lib/aikido/zen/collector/stats.rb +17 -17
- data/lib/aikido/zen/config.rb +44 -39
- data/lib/aikido/zen/context.rb +4 -2
- data/lib/aikido/zen/errors.rb +0 -8
- data/lib/aikido/zen/event.rb +12 -12
- data/lib/aikido/zen/ipc/ipc.rb +502 -0
- data/lib/aikido/zen/ipc/rpc.rb +349 -0
- data/lib/aikido/zen/ipc.rb +4 -0
- data/lib/aikido/zen/middleware/fork_detector.rb +8 -2
- data/lib/aikido/zen/middleware/rack_throttler.rb +2 -4
- data/lib/aikido/zen/outbound_connection.rb +3 -3
- data/lib/aikido/zen/payload.rb +3 -3
- data/lib/aikido/zen/rate_limiter/result.rb +20 -0
- data/lib/aikido/zen/request.rb +14 -9
- data/lib/aikido/zen/route.rb +3 -3
- data/lib/aikido/zen/sinks/action_controller.rb +2 -4
- data/lib/aikido/zen/system_info.rb +13 -13
- data/lib/aikido/zen/version.rb +1 -1
- data/lib/aikido/zen/worker_process/agent/client.rb +112 -0
- data/lib/aikido/zen/worker_process/agent/server.rb +88 -0
- data/lib/aikido/zen/worker_process/agent.rb +4 -0
- data/lib/aikido/zen/worker_process.rb +3 -0
- data/lib/aikido/zen.rb +55 -29
- metadata +12 -6
- data/lib/aikido/zen/detached_agent/agent.rb +0 -79
- data/lib/aikido/zen/detached_agent/front_object.rb +0 -41
- data/lib/aikido/zen/detached_agent/server.rb +0 -78
- data/lib/aikido/zen/detached_agent.rb +0 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0cf8df166776f46ad02f951fd01710135db8e90cbeede8c54c4ff643a6463d0
|
|
4
|
+
data.tar.gz: 60b5c0e61ce8627e8739441b92843a32f10d519f68afcd52b91aa172812b0207
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fcf428661f4688f886b9d4d86daa6d65fac6a348fbb6f222099af2d6723b70a27166ca8e74314afcba03c38fac30b7b3fccc6c39b0a290dbe7e95114db193a2a
|
|
7
|
+
data.tar.gz: 268766e8d50d75b1d218918fea7acac88bde561361bf176fb025509148781065bdf460e4446001373cc51478d6656f9f510457d15bf26ee33540110d3cd2ce25
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "benchmark"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "aikido-zen"
|
|
6
|
+
|
|
7
|
+
class BasicIO
|
|
8
|
+
def initialize(data, timeout: 5)
|
|
9
|
+
@data = data
|
|
10
|
+
@size = data.bytesize
|
|
11
|
+
@timeout = timeout
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read(socket)
|
|
15
|
+
buffer = String.new(encoding: Encoding::BINARY)
|
|
16
|
+
|
|
17
|
+
while buffer.bytesize < @size
|
|
18
|
+
case chunk = socket.read_nonblock(@size - buffer.bytesize, exception: false)
|
|
19
|
+
when :wait_readable
|
|
20
|
+
raise Errno::ETIMEDOUT, "read timed out" unless IO.select([socket], nil, nil, @timeout)
|
|
21
|
+
when nil
|
|
22
|
+
raise EOFError
|
|
23
|
+
else
|
|
24
|
+
buffer << chunk
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
buffer
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def write(socket)
|
|
32
|
+
written = 0
|
|
33
|
+
|
|
34
|
+
while written < @data.bytesize
|
|
35
|
+
case n = socket.write_nonblock(@data.byteslice(written..), exception: false)
|
|
36
|
+
when :wait_writable
|
|
37
|
+
raise Errno::ETIMEDOUT, "write timed out" unless IO.select(nil, [socket], nil, @timeout)
|
|
38
|
+
else
|
|
39
|
+
written += n
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class TimedIO
|
|
46
|
+
include Aikido::Zen::IPC::TimedIO
|
|
47
|
+
|
|
48
|
+
def initialize(data, timeout: 5)
|
|
49
|
+
@data = data
|
|
50
|
+
@size = data.bytesize
|
|
51
|
+
@timeout = timeout
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def read(socket)
|
|
55
|
+
read_with_deadline(socket, @size, Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def write(socket)
|
|
59
|
+
write_with_deadline(socket, @data, Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class FramedIO
|
|
64
|
+
include Aikido::Zen::IPC::FramedIO
|
|
65
|
+
|
|
66
|
+
def initialize(data, timeout: 5)
|
|
67
|
+
@data = data
|
|
68
|
+
@timeout = timeout
|
|
69
|
+
@buffer = String.new(encoding: Encoding::BINARY)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def read(socket)
|
|
73
|
+
read_coalesced_frame_with_timeout(socket, @buffer, nil, @timeout)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def write(socket)
|
|
77
|
+
write_coalesced_frame_with_timeout(socket, @data, nil, @timeout)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def run(duration, io)
|
|
82
|
+
secret = SecureRandom.bytes(32)
|
|
83
|
+
|
|
84
|
+
server = Aikido::Zen::IPC::Server.start(secret) do |socket|
|
|
85
|
+
loop do
|
|
86
|
+
io.read(socket)
|
|
87
|
+
end
|
|
88
|
+
rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
|
|
89
|
+
# disconnected
|
|
90
|
+
rescue IOError
|
|
91
|
+
# client stopped
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
client = Aikido::Zen::IPC::Client.new(secret, server.host, server.port)
|
|
95
|
+
|
|
96
|
+
sent = 0
|
|
97
|
+
|
|
98
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
|
|
99
|
+
|
|
100
|
+
result = Benchmark.measure do
|
|
101
|
+
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < deadline
|
|
102
|
+
io.write(client.socket)
|
|
103
|
+
|
|
104
|
+
sent += 1
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
[sent, result.real]
|
|
109
|
+
ensure
|
|
110
|
+
client.close
|
|
111
|
+
server.stop
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def report(label, sent, real, data_size, baseline: nil)
|
|
115
|
+
rate = sent / real
|
|
116
|
+
mib_per_sec = rate * data_size / (1024 * 1024)
|
|
117
|
+
|
|
118
|
+
puts label
|
|
119
|
+
puts "#{rate.round} messages/sec, #{mib_per_sec.round(2)} MiB/sec"
|
|
120
|
+
|
|
121
|
+
if baseline
|
|
122
|
+
overhead = 100 - (100.0 * rate / baseline)
|
|
123
|
+
puts "#{overhead.round(1)}% fewer messages/sec than baseline"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if __FILE__ == $0
|
|
128
|
+
duration = (ENV["DURATION"] || 5).to_i
|
|
129
|
+
data_size = (ENV["DATA_SIZE"] || 1024).to_i
|
|
130
|
+
|
|
131
|
+
data = SecureRandom.bytes(data_size)
|
|
132
|
+
|
|
133
|
+
basic_io = BasicIO.new(data)
|
|
134
|
+
basic_io_sent, basic_io_real = run(duration, basic_io)
|
|
135
|
+
report("baseline", basic_io_sent, basic_io_real, data_size)
|
|
136
|
+
basic_io_rate = basic_io_sent / basic_io_real
|
|
137
|
+
|
|
138
|
+
puts
|
|
139
|
+
|
|
140
|
+
timed_io = TimedIO.new(data)
|
|
141
|
+
timed_io_sent, timed_io_real = run(duration, timed_io)
|
|
142
|
+
report("TimedIO", timed_io_sent, timed_io_real, data_size, baseline: basic_io_rate)
|
|
143
|
+
|
|
144
|
+
puts
|
|
145
|
+
|
|
146
|
+
framed_io = FramedIO.new(data)
|
|
147
|
+
framed_io_sent, framed_io_real = run(duration, framed_io)
|
|
148
|
+
report("FramedIO (coalesced)", framed_io_sent, framed_io_real, data_size, baseline: basic_io_rate)
|
|
149
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "benchmark"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "logger"
|
|
6
|
+
require "aikido-zen"
|
|
7
|
+
|
|
8
|
+
def run(duration, concurrency)
|
|
9
|
+
secret = SecureRandom.bytes(32)
|
|
10
|
+
logger = Logger.new(File::NULL)
|
|
11
|
+
|
|
12
|
+
server = Aikido::Zen::RPC::Server.start(secret, logger: logger) do |server|
|
|
13
|
+
server.handle("echo") { |respond, value| respond.call(value) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
client = Aikido::Zen::RPC::Client.start(secret, server.host, server.port, logger: logger)
|
|
17
|
+
|
|
18
|
+
calls = Concurrent::AtomicFixnum.new(0)
|
|
19
|
+
|
|
20
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
|
|
21
|
+
|
|
22
|
+
result = Benchmark.measure do
|
|
23
|
+
threads = concurrency.times.map do
|
|
24
|
+
Thread.new do
|
|
25
|
+
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < deadline
|
|
26
|
+
client.invoke("echo", 5.0, "hello")
|
|
27
|
+
|
|
28
|
+
calls.increment
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
threads.each(&:join)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
[calls.value, result.real]
|
|
37
|
+
ensure
|
|
38
|
+
client.stop
|
|
39
|
+
server.stop
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def report(label, total_calls, real)
|
|
43
|
+
rate = total_calls / real
|
|
44
|
+
latency_ms = real / total_calls * 1000
|
|
45
|
+
|
|
46
|
+
puts label
|
|
47
|
+
puts "#{rate.round} calls/sec, #{latency_ms.round(3)} ms/call"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if __FILE__ == $0
|
|
51
|
+
duration = (ENV["DURATION"] || 5).to_i
|
|
52
|
+
concurrency = (ENV["CONCURRENCY"] || 1).to_i
|
|
53
|
+
|
|
54
|
+
calls, real = run(duration, concurrency)
|
|
55
|
+
|
|
56
|
+
report("RPC echo (concurrency: #{concurrency})", calls, real)
|
|
57
|
+
end
|
data/docs/rails.md
CHANGED
|
@@ -27,6 +27,28 @@ Bundler.require(*Rails.groups)
|
|
|
27
27
|
That's it! Zen will start to run inside your app when it starts getting
|
|
28
28
|
requests.
|
|
29
29
|
|
|
30
|
+
## Running with multiple worker processes (Puma)
|
|
31
|
+
|
|
32
|
+
Zen can share rate limiting state, runtime settings, and runtime stats
|
|
33
|
+
across all of an app's worker processes. This only works if Rails is booted
|
|
34
|
+
once and then forked into workers, which on Puma requires
|
|
35
|
+
[`preload_app!`][puma-preload]:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
# config/puma.rb
|
|
39
|
+
preload_app!
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Without `preload_app!`, each worker boots Rails independently, so Zen
|
|
43
|
+
tracks rate limits, settings, and stats separately per worker instead
|
|
44
|
+
of sharing them.
|
|
45
|
+
|
|
46
|
+
> [!NOTE]
|
|
47
|
+
> This only matters for multi-process setups; it doesn't apply to
|
|
48
|
+
> single-process or threaded-only deployments.
|
|
49
|
+
|
|
50
|
+
[puma-preload]: https://github.com/puma/puma#cluster-mode
|
|
51
|
+
|
|
30
52
|
## Rate limiting and user blocking
|
|
31
53
|
|
|
32
54
|
If you want to add the rate limiting feature to your app, modify your code like this:
|
data/lib/aikido/zen/actor.rb
CHANGED
|
@@ -40,11 +40,11 @@ module Aikido::Zen
|
|
|
40
40
|
class Actor
|
|
41
41
|
def self.from_json(data)
|
|
42
42
|
new(
|
|
43
|
-
id: data[
|
|
44
|
-
name: data[
|
|
45
|
-
ip: data[
|
|
46
|
-
first_seen_at: Time.at(data[
|
|
47
|
-
last_seen_at: Time.at(data[
|
|
43
|
+
id: data["id"],
|
|
44
|
+
name: data["name"],
|
|
45
|
+
ip: data["lastIpAddress"],
|
|
46
|
+
first_seen_at: Time.at(data["firstSeenAt"] / 1000),
|
|
47
|
+
last_seen_at: Time.at(data["lastSeenAt"] / 1000)
|
|
48
48
|
)
|
|
49
49
|
end
|
|
50
50
|
|
|
@@ -135,11 +135,11 @@ module Aikido::Zen
|
|
|
135
135
|
|
|
136
136
|
def as_json
|
|
137
137
|
{
|
|
138
|
-
id
|
|
139
|
-
name
|
|
140
|
-
lastIpAddress
|
|
141
|
-
firstSeenAt
|
|
142
|
-
lastSeenAt
|
|
138
|
+
"id" => id,
|
|
139
|
+
"name" => name,
|
|
140
|
+
"lastIpAddress" => ip,
|
|
141
|
+
"firstSeenAt" => first_seen_at.to_i * 1000,
|
|
142
|
+
"lastSeenAt" => last_seen_at.to_i * 1000
|
|
143
143
|
}.compact
|
|
144
144
|
end
|
|
145
145
|
end
|
data/lib/aikido/zen/agent.rb
CHANGED
|
@@ -19,14 +19,12 @@ module Aikido::Zen
|
|
|
19
19
|
def initialize(
|
|
20
20
|
config: Aikido::Zen.config,
|
|
21
21
|
collector: Aikido::Zen.collector,
|
|
22
|
-
detached_agent: Aikido::Zen.detached_agent,
|
|
23
22
|
worker: Aikido::Zen::Worker.new(config: config),
|
|
24
23
|
api_client: Aikido::Zen::APIClient.new(config: config),
|
|
25
24
|
api_stream: Aikido::Zen::APIStream.new(config: config)
|
|
26
25
|
)
|
|
27
26
|
@config = config
|
|
28
27
|
@collector = collector
|
|
29
|
-
@detached_agent = detached_agent
|
|
30
28
|
@worker = worker
|
|
31
29
|
@api_client = api_client
|
|
32
30
|
@api_stream = api_stream
|
|
@@ -80,16 +78,8 @@ module Aikido::Zen
|
|
|
80
78
|
end
|
|
81
79
|
|
|
82
80
|
if @config.realtime_settings_updates_enabled?
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@api_stream.start!
|
|
86
|
-
|
|
87
|
-
# Use the realtime setting updates endpoint when polling to check
|
|
88
|
-
# whether settings should be fetched.
|
|
89
|
-
@api_client.should_fetch_settings_endpoint = @config.realtime_settings_updates_endpoint
|
|
90
|
-
else
|
|
91
|
-
@config.logger.warn("Can't reach #{Aikido::Zen.config.realtime_settings_updates_endpoint}, make sure it's in your outbound firewall allowlist. Realtime config updates won't be available, switched to polling.")
|
|
92
|
-
end
|
|
81
|
+
@api_stream.handle("config-updated") { |event| settings_updated(event) }
|
|
82
|
+
@api_stream.start!
|
|
93
83
|
end
|
|
94
84
|
|
|
95
85
|
poll_for_setting_updates
|
|
@@ -243,6 +233,7 @@ module Aikido::Zen
|
|
|
243
233
|
def update_settings_from_runtime_config!(data)
|
|
244
234
|
return unless @runtime_config_update_mutex.try_lock
|
|
245
235
|
begin
|
|
236
|
+
Aikido::Zen.api_cache.runtime_config = data
|
|
246
237
|
Aikido::Zen.runtime_settings.update_from_runtime_config_json(data)
|
|
247
238
|
ensure
|
|
248
239
|
@runtime_config_update_mutex.unlock
|
|
@@ -254,6 +245,7 @@ module Aikido::Zen
|
|
|
254
245
|
def update_settings_from_runtime_firewall_lists!(data)
|
|
255
246
|
return unless @runtime_firewall_lists_update_mutex.try_lock
|
|
256
247
|
begin
|
|
248
|
+
Aikido::Zen.api_cache.runtime_firewall_lists = data
|
|
257
249
|
Aikido::Zen.runtime_settings.update_from_runtime_firewall_lists_json(data)
|
|
258
250
|
ensure
|
|
259
251
|
@runtime_firewall_lists_update_mutex.unlock
|
|
@@ -27,7 +27,7 @@ module Aikido::Zen
|
|
|
27
27
|
@thread = nil
|
|
28
28
|
@http = nil
|
|
29
29
|
|
|
30
|
-
endpoint = @config.
|
|
30
|
+
endpoint = @config.realtime_endpoint
|
|
31
31
|
|
|
32
32
|
@host = endpoint.host
|
|
33
33
|
@port = endpoint.port
|
|
@@ -37,31 +37,6 @@ module Aikido::Zen
|
|
|
37
37
|
@handlers = Concurrent::Array.new
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
# @return [Boolean] whether we could connect to the realtime endpoint
|
|
41
|
-
def can_connect?
|
|
42
|
-
http = Net::HTTP.new(@host, @port)
|
|
43
|
-
http.use_ssl = @use_ssl
|
|
44
|
-
http.open_timeout = 5
|
|
45
|
-
http.write_timeout = 5
|
|
46
|
-
http.read_timeout = 5
|
|
47
|
-
http.max_retries = 0
|
|
48
|
-
|
|
49
|
-
request = Net::HTTP::Get.new("/config")
|
|
50
|
-
request["Authorization"] = @token
|
|
51
|
-
|
|
52
|
-
begin
|
|
53
|
-
http.request(request)
|
|
54
|
-
|
|
55
|
-
return true
|
|
56
|
-
rescue Timeout::Error, SocketError, IOError, SystemCallError, OpenSSL::OpenSSLError => err
|
|
57
|
-
@config.logger.debug("Error probing realtime endpoint: #{err.class}: #{err.message}")
|
|
58
|
-
rescue => err
|
|
59
|
-
@config.logger.error("Error probing realtime endpoint: #{err.class}: #{err.message}")
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
false
|
|
63
|
-
end
|
|
64
|
-
|
|
65
40
|
def running?
|
|
66
41
|
@running.true?
|
|
67
42
|
end
|
data/lib/aikido/zen/attack.rb
CHANGED
|
@@ -44,11 +44,11 @@ module Aikido::Zen
|
|
|
44
44
|
|
|
45
45
|
def as_json
|
|
46
46
|
{
|
|
47
|
-
kind
|
|
48
|
-
blocked
|
|
49
|
-
metadata
|
|
50
|
-
operation
|
|
51
|
-
stack
|
|
47
|
+
"kind" => kind,
|
|
48
|
+
"blocked" => blocked?,
|
|
49
|
+
"metadata" => metadata,
|
|
50
|
+
"operation" => @operation,
|
|
51
|
+
"stack" => @stack
|
|
52
52
|
}.compact.merge(input.as_json)
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -70,7 +70,7 @@ module Aikido::Zen
|
|
|
70
70
|
|
|
71
71
|
def metadata
|
|
72
72
|
{
|
|
73
|
-
filename
|
|
73
|
+
"filename" => filepath
|
|
74
74
|
}
|
|
75
75
|
end
|
|
76
76
|
|
|
@@ -107,7 +107,7 @@ module Aikido::Zen
|
|
|
107
107
|
|
|
108
108
|
def metadata
|
|
109
109
|
{
|
|
110
|
-
command
|
|
110
|
+
"command" => @command
|
|
111
111
|
}
|
|
112
112
|
end
|
|
113
113
|
|
|
@@ -139,9 +139,9 @@ module Aikido::Zen
|
|
|
139
139
|
|
|
140
140
|
def metadata
|
|
141
141
|
{
|
|
142
|
-
sql
|
|
143
|
-
dialect
|
|
144
|
-
failedToTokenize
|
|
142
|
+
"sql" => @query,
|
|
143
|
+
"dialect" => @dialect.name,
|
|
144
|
+
"failedToTokenize" => @failed_to_tokenize || nil
|
|
145
145
|
}.compact
|
|
146
146
|
end
|
|
147
147
|
|
|
@@ -174,8 +174,8 @@ module Aikido::Zen
|
|
|
174
174
|
|
|
175
175
|
def metadata
|
|
176
176
|
{
|
|
177
|
-
hostname
|
|
178
|
-
port
|
|
177
|
+
"hostname" => @request.uri.hostname,
|
|
178
|
+
"port" => @request.uri.port.to_s
|
|
179
179
|
}
|
|
180
180
|
end
|
|
181
181
|
end
|
|
@@ -212,8 +212,8 @@ module Aikido::Zen
|
|
|
212
212
|
|
|
213
213
|
def metadata
|
|
214
214
|
{
|
|
215
|
-
hostname
|
|
216
|
-
privateIP
|
|
215
|
+
"hostname" => @hostname,
|
|
216
|
+
"privateIP" => @address
|
|
217
217
|
}
|
|
218
218
|
end
|
|
219
219
|
end
|
|
@@ -69,9 +69,9 @@ module Aikido::Zen
|
|
|
69
69
|
|
|
70
70
|
def as_json
|
|
71
71
|
{
|
|
72
|
-
ipAddress
|
|
73
|
-
userAgent
|
|
74
|
-
source
|
|
72
|
+
"ipAddress" => @ip_address,
|
|
73
|
+
"userAgent" => @user_agent,
|
|
74
|
+
"source" => @source
|
|
75
75
|
}.compact
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -101,10 +101,10 @@ module Aikido::Zen
|
|
|
101
101
|
|
|
102
102
|
def as_json
|
|
103
103
|
{
|
|
104
|
-
metadata
|
|
105
|
-
samples
|
|
104
|
+
"metadata" => {
|
|
105
|
+
"samples" => @samples.as_json.to_json # The API only accepts string values in metadata
|
|
106
106
|
},
|
|
107
|
-
user
|
|
107
|
+
"user" => @user.as_json
|
|
108
108
|
}.compact
|
|
109
109
|
end
|
|
110
110
|
|
|
@@ -130,8 +130,8 @@ module Aikido::Zen
|
|
|
130
130
|
|
|
131
131
|
def as_json
|
|
132
132
|
{
|
|
133
|
-
method
|
|
134
|
-
url
|
|
133
|
+
"method" => @verb.as_json,
|
|
134
|
+
"url" => @path.as_json
|
|
135
135
|
}.compact
|
|
136
136
|
end
|
|
137
137
|
|
|
@@ -12,7 +12,7 @@ module Aikido::Zen
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.from_json(data)
|
|
15
|
-
type = data[
|
|
15
|
+
type = data["type"]
|
|
16
16
|
subclass = @@registry[type]
|
|
17
17
|
subclass.from_json(data)
|
|
18
18
|
end
|
|
@@ -25,7 +25,7 @@ module Aikido::Zen
|
|
|
25
25
|
|
|
26
26
|
def as_json
|
|
27
27
|
{
|
|
28
|
-
type
|
|
28
|
+
"type" => @type
|
|
29
29
|
}
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -72,7 +72,7 @@ module Aikido::Zen
|
|
|
72
72
|
register "track_user_agent"
|
|
73
73
|
|
|
74
74
|
def self.from_json(data)
|
|
75
|
-
new(data[
|
|
75
|
+
new(data["user_agent_keys"])
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def initialize(user_agent_keys)
|
|
@@ -82,7 +82,7 @@ module Aikido::Zen
|
|
|
82
82
|
|
|
83
83
|
def as_json
|
|
84
84
|
super.update({
|
|
85
|
-
user_agent_keys
|
|
85
|
+
"user_agent_keys" => @user_agent_keys
|
|
86
86
|
})
|
|
87
87
|
end
|
|
88
88
|
|
|
@@ -99,7 +99,7 @@ module Aikido::Zen
|
|
|
99
99
|
register "track_ip_list"
|
|
100
100
|
|
|
101
101
|
def self.from_json(data)
|
|
102
|
-
new(data[
|
|
102
|
+
new(data["ip_list_keys"])
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def initialize(ip_list_keys)
|
|
@@ -109,7 +109,7 @@ module Aikido::Zen
|
|
|
109
109
|
|
|
110
110
|
def as_json
|
|
111
111
|
super.update({
|
|
112
|
-
ip_list_keys
|
|
112
|
+
"ip_list_keys" => @ip_list_keys
|
|
113
113
|
})
|
|
114
114
|
end
|
|
115
115
|
|
|
@@ -127,7 +127,7 @@ module Aikido::Zen
|
|
|
127
127
|
|
|
128
128
|
def self.from_json(data)
|
|
129
129
|
new(
|
|
130
|
-
being_blocked: data[
|
|
130
|
+
being_blocked: data["being_blocked"]
|
|
131
131
|
)
|
|
132
132
|
end
|
|
133
133
|
|
|
@@ -138,7 +138,7 @@ module Aikido::Zen
|
|
|
138
138
|
|
|
139
139
|
def as_json
|
|
140
140
|
super.update({
|
|
141
|
-
being_blocked
|
|
141
|
+
"being_blocked" => @being_blocked
|
|
142
142
|
})
|
|
143
143
|
end
|
|
144
144
|
|
|
@@ -156,9 +156,9 @@ module Aikido::Zen
|
|
|
156
156
|
|
|
157
157
|
def self.from_json(data)
|
|
158
158
|
new(
|
|
159
|
-
data[
|
|
160
|
-
data[
|
|
161
|
-
has_errors: data[
|
|
159
|
+
data["sink_name"],
|
|
160
|
+
data["duration"],
|
|
161
|
+
has_errors: data["has_errors"]
|
|
162
162
|
)
|
|
163
163
|
end
|
|
164
164
|
|
|
@@ -171,9 +171,9 @@ module Aikido::Zen
|
|
|
171
171
|
|
|
172
172
|
def as_json
|
|
173
173
|
super.update({
|
|
174
|
-
sink_name
|
|
175
|
-
duration
|
|
176
|
-
has_errors
|
|
174
|
+
"sink_name" => @sink_name,
|
|
175
|
+
"duration" => @duration,
|
|
176
|
+
"has_errors" => @has_errors
|
|
177
177
|
})
|
|
178
178
|
end
|
|
179
179
|
|
|
@@ -191,8 +191,8 @@ module Aikido::Zen
|
|
|
191
191
|
|
|
192
192
|
def self.from_json(data)
|
|
193
193
|
new(
|
|
194
|
-
data[
|
|
195
|
-
being_blocked: data[
|
|
194
|
+
data["sink_name"],
|
|
195
|
+
being_blocked: data["being_blocked"]
|
|
196
196
|
)
|
|
197
197
|
end
|
|
198
198
|
|
|
@@ -204,8 +204,8 @@ module Aikido::Zen
|
|
|
204
204
|
|
|
205
205
|
def as_json
|
|
206
206
|
super.update({
|
|
207
|
-
sink_name
|
|
208
|
-
being_blocked
|
|
207
|
+
"sink_name" => @sink_name,
|
|
208
|
+
"being_blocked" => @being_blocked
|
|
209
209
|
})
|
|
210
210
|
end
|
|
211
211
|
|
|
@@ -222,7 +222,7 @@ module Aikido::Zen
|
|
|
222
222
|
register "track_user"
|
|
223
223
|
|
|
224
224
|
def self.from_json(data)
|
|
225
|
-
new(Aikido::Zen::Actor.from_json(data[
|
|
225
|
+
new(Aikido::Zen::Actor.from_json(data["actor"]))
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
def initialize(actor)
|
|
@@ -232,7 +232,7 @@ module Aikido::Zen
|
|
|
232
232
|
|
|
233
233
|
def as_json
|
|
234
234
|
super.update({
|
|
235
|
-
actor
|
|
235
|
+
"actor" => @actor.as_json
|
|
236
236
|
})
|
|
237
237
|
end
|
|
238
238
|
|
|
@@ -249,7 +249,7 @@ module Aikido::Zen
|
|
|
249
249
|
register "track_outbound"
|
|
250
250
|
|
|
251
251
|
def self.from_json(data)
|
|
252
|
-
new(OutboundConnection.from_json(data[
|
|
252
|
+
new(OutboundConnection.from_json(data["connection"]))
|
|
253
253
|
end
|
|
254
254
|
|
|
255
255
|
def initialize(connection)
|
|
@@ -259,7 +259,7 @@ module Aikido::Zen
|
|
|
259
259
|
|
|
260
260
|
def as_json
|
|
261
261
|
super.update({
|
|
262
|
-
connection
|
|
262
|
+
"connection" => @connection.as_json
|
|
263
263
|
})
|
|
264
264
|
end
|
|
265
265
|
|
|
@@ -277,8 +277,8 @@ module Aikido::Zen
|
|
|
277
277
|
|
|
278
278
|
def self.from_json(data)
|
|
279
279
|
new(
|
|
280
|
-
Route.from_json(data[
|
|
281
|
-
Request::Schema.from_json(data[
|
|
280
|
+
Route.from_json(data["route"]),
|
|
281
|
+
Request::Schema.from_json(data["schema"])
|
|
282
282
|
)
|
|
283
283
|
end
|
|
284
284
|
|
|
@@ -290,8 +290,8 @@ module Aikido::Zen
|
|
|
290
290
|
|
|
291
291
|
def as_json
|
|
292
292
|
super.update({
|
|
293
|
-
route
|
|
294
|
-
schema
|
|
293
|
+
"route" => @route.as_json,
|
|
294
|
+
"schema" => @schema.as_json
|
|
295
295
|
})
|
|
296
296
|
end
|
|
297
297
|
|
|
@@ -26,10 +26,10 @@ module Aikido::Zen
|
|
|
26
26
|
def as_json
|
|
27
27
|
@visits.map do |route, record|
|
|
28
28
|
{
|
|
29
|
-
method
|
|
30
|
-
path
|
|
31
|
-
hits
|
|
32
|
-
apispec
|
|
29
|
+
"method" => route.verb,
|
|
30
|
+
"path" => route.path,
|
|
31
|
+
"hits" => record.hits,
|
|
32
|
+
"apispec" => record.schema.as_json
|
|
33
33
|
}.compact
|
|
34
34
|
end
|
|
35
35
|
end
|