gitlab-labkit 1.15.1 → 1.17.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 +4 -4
- data/.gitlab-ci.yml +3 -3
- data/.pre-commit-config.yaml +1 -1
- data/lib/labkit/rate_limit/evaluator.rb +43 -6
- data/lib/labkit/rate_limit/metrics.rb +43 -0
- data/lib/labkit/rate_limit/result.rb +16 -12
- data/lib/labkit/rate_limit.rb +2 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7177b8f9210f24958a14c92f17ad35cc8d4ad7732e04a9407569e865ef2bf364
|
|
4
|
+
data.tar.gz: 688c6dd7b7c1a8c1062a1e17c534fa833f86053aff8945ab4d296c2223784711
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b54fe16d5c1cfe12c704a4e84cdea8dece8155dcaa04132c9e52442bec03e6338f6a4bcb065aa87931aa2fedcbd2ecb5ecec390b00d110267ee54e95a5c694c
|
|
7
|
+
data.tar.gz: 8abc6a48c3f1b138a84f8502e5c4300bec5f05f98b0c8defb4280d12b1d424f70cf64e0afe3cc344ce61ceae145e9aaf11f252d9611da1e5c6a1f25aa37fe867
|
data/.gitlab-ci.yml
CHANGED
|
@@ -19,13 +19,13 @@ include:
|
|
|
19
19
|
# It includes standard checks, gitlab-scanners, validations and release processes
|
|
20
20
|
# common to all projects using this template library.
|
|
21
21
|
# see https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/templates/standard.md
|
|
22
|
-
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/standard-build@v3.
|
|
22
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/standard-build@v3.23
|
|
23
23
|
|
|
24
24
|
# Runs rspec tests and rubocop on the project
|
|
25
25
|
# see https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/templates/ruby.md
|
|
26
|
-
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/ruby-build@v3.
|
|
26
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/ruby-build@v3.23
|
|
27
27
|
|
|
28
|
-
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@v3.
|
|
28
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@v3.23
|
|
29
29
|
|
|
30
30
|
ruby-versions:
|
|
31
31
|
extends: rspec
|
data/.pre-commit-config.yaml
CHANGED
|
@@ -25,7 +25,7 @@ repos:
|
|
|
25
25
|
# Documentation available at
|
|
26
26
|
# https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/docs/pre-commit.md
|
|
27
27
|
- repo: https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks
|
|
28
|
-
rev: v3.
|
|
28
|
+
rev: v3.23 # renovate:managed
|
|
29
29
|
|
|
30
30
|
hooks:
|
|
31
31
|
- id: shellcheck # Run shellcheck for changed Shell files
|
|
@@ -24,6 +24,7 @@ module Labkit
|
|
|
24
24
|
rescue StandardError => e
|
|
25
25
|
# Intentionally broad: fail-open applies to any unexpected error (network,
|
|
26
26
|
# timeout, OOM) not only Redis protocol errors.
|
|
27
|
+
report_error_metrics
|
|
27
28
|
log_error(e, identifier)
|
|
28
29
|
Result.new(matched: false, error: true, action: :allow)
|
|
29
30
|
end
|
|
@@ -34,9 +35,12 @@ module Labkit
|
|
|
34
35
|
@rules.each do |rule|
|
|
35
36
|
next unless rule_matches?(rule, identifier)
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
result = evaluate_rule(rule, identifier)
|
|
39
|
+
report_matched_metrics(result)
|
|
40
|
+
return result
|
|
38
41
|
end
|
|
39
42
|
|
|
43
|
+
report_unmatched_metrics
|
|
40
44
|
Result.new(matched: false, action: :allow)
|
|
41
45
|
end
|
|
42
46
|
|
|
@@ -53,7 +57,10 @@ module Labkit
|
|
|
53
57
|
exceeded = count > resolved_limit
|
|
54
58
|
action = exceeded ? rule.action : :allow
|
|
55
59
|
|
|
56
|
-
Result.new(
|
|
60
|
+
Result.new(
|
|
61
|
+
matched: true, exceeded: exceeded, action: action, rule: rule,
|
|
62
|
+
resolved_limit: resolved_limit, resolved_period: resolved_period
|
|
63
|
+
)
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
def build_redis_key(rule, identifier)
|
|
@@ -85,10 +92,12 @@ module Labkit
|
|
|
85
92
|
end
|
|
86
93
|
|
|
87
94
|
def incr_with_ttl(redis_key, period)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
@redis.with do |conn|
|
|
96
|
+
count = conn.incr(redis_key)
|
|
97
|
+
# Set expiry only on first write to avoid resetting TTL on each call
|
|
98
|
+
conn.expire(redis_key, period) if count == 1
|
|
99
|
+
count
|
|
100
|
+
end
|
|
92
101
|
end
|
|
93
102
|
|
|
94
103
|
def log_error(error, identifier)
|
|
@@ -99,6 +108,34 @@ module Labkit
|
|
|
99
108
|
identifier: identifier&.to_h
|
|
100
109
|
)
|
|
101
110
|
end
|
|
111
|
+
|
|
112
|
+
def report_matched_metrics(result)
|
|
113
|
+
Metrics.calls_total.increment(
|
|
114
|
+
rate_limiter: @name,
|
|
115
|
+
rule: result.rule.name,
|
|
116
|
+
action: result.action.to_s
|
|
117
|
+
)
|
|
118
|
+
Metrics.limit_gauge.set(
|
|
119
|
+
{ rate_limiter: @name, rule: result.rule.name },
|
|
120
|
+
result.resolved_limit
|
|
121
|
+
)
|
|
122
|
+
Metrics.period_gauge.set(
|
|
123
|
+
{ rate_limiter: @name, rule: result.rule.name },
|
|
124
|
+
result.resolved_period
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def report_unmatched_metrics
|
|
129
|
+
Metrics.calls_total.increment(
|
|
130
|
+
rate_limiter: @name,
|
|
131
|
+
rule: "unmatched",
|
|
132
|
+
action: "allow"
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def report_error_metrics
|
|
137
|
+
Metrics.errors_total.increment(rate_limiter: @name)
|
|
138
|
+
end
|
|
102
139
|
end
|
|
103
140
|
end
|
|
104
141
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
module RateLimit
|
|
5
|
+
module Metrics
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def calls_total
|
|
9
|
+
Labkit::Metrics::Client.counter(
|
|
10
|
+
:gitlab_labkit_rate_limiter_calls_total,
|
|
11
|
+
'Total number of successful rate limit checks',
|
|
12
|
+
{ rate_limiter: nil, rule: nil, action: nil }
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def errors_total
|
|
17
|
+
Labkit::Metrics::Client.counter(
|
|
18
|
+
:gitlab_labkit_rate_limiter_errors_total,
|
|
19
|
+
'Total number of rate limit check errors',
|
|
20
|
+
{ rate_limiter: nil }
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def limit_gauge
|
|
25
|
+
Labkit::Metrics::Client.gauge(
|
|
26
|
+
:gitlab_labkit_rate_limiter_limit,
|
|
27
|
+
'The configured rate limit threshold',
|
|
28
|
+
{ rate_limiter: nil, rule: nil },
|
|
29
|
+
:max
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def period_gauge
|
|
34
|
+
Labkit::Metrics::Client.gauge(
|
|
35
|
+
:gitlab_labkit_rate_limiter_period_seconds,
|
|
36
|
+
'The configured rate limit period in seconds',
|
|
37
|
+
{ rate_limiter: nil, rule: nil },
|
|
38
|
+
:max
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
module Labkit
|
|
4
4
|
module RateLimit
|
|
5
5
|
# Result is the return value of Limiter#check.
|
|
6
|
-
# matched?
|
|
7
|
-
# exceeded?
|
|
8
|
-
# action
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# rule
|
|
15
|
-
# error?
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
# matched? - true if a rule's match conditions were satisfied
|
|
7
|
+
# exceeded? - true if the matched rule's counter exceeded its limit
|
|
8
|
+
# action - the outcome: what the caller should do
|
|
9
|
+
# :block = rule matched, exceeded, rule configured to block
|
|
10
|
+
# :log = rule matched, exceeded, rule configured to log only
|
|
11
|
+
# :allow = rule matched but count within limit, or
|
|
12
|
+
# no rule matched, or error (fail-open)
|
|
13
|
+
# The rule's configured action is available via rule.action
|
|
14
|
+
# rule - the matched Rule object (nil when matched? is false)
|
|
15
|
+
# error? - true if Redis was unavailable; result fails open (exceeded? is false)
|
|
16
|
+
# resolved_limit - the resolved limit value as Integer (nil when matched? is false or error)
|
|
17
|
+
# resolved_period - the resolved period value as Integer (nil when matched? is false or error)
|
|
18
|
+
Result = Data.define(:matched, :exceeded, :action, :rule, :error, :resolved_limit, :resolved_period) do
|
|
19
|
+
def initialize(
|
|
20
|
+
matched:, action:, exceeded: false, rule: nil, error: false,
|
|
21
|
+
resolved_limit: nil, resolved_period: nil)
|
|
18
22
|
super
|
|
19
23
|
end
|
|
20
24
|
|
data/lib/labkit/rate_limit.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Labkit
|
|
|
6
6
|
#
|
|
7
7
|
# @example Configuration (e.g. in a Rails initializer)
|
|
8
8
|
# Labkit::RateLimit.configure do |c|
|
|
9
|
-
# c.redis = Redis.
|
|
9
|
+
# c.redis = ConnectionPool.new { Redis.new } # must respond to .with { |conn| }
|
|
10
10
|
# c.logger = Labkit::Logging::JsonLogger.new($stdout)
|
|
11
11
|
# end
|
|
12
12
|
#
|
|
@@ -23,6 +23,7 @@ module Labkit
|
|
|
23
23
|
autoload :Rule, "labkit/rate_limit/rule"
|
|
24
24
|
autoload :Evaluator, "labkit/rate_limit/evaluator"
|
|
25
25
|
autoload :Limiter, "labkit/rate_limit/limiter"
|
|
26
|
+
autoload :Metrics, "labkit/rate_limit/metrics"
|
|
26
27
|
|
|
27
28
|
class << self
|
|
28
29
|
def configure
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab-labkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Newdigate
|
|
@@ -603,6 +603,7 @@ files:
|
|
|
603
603
|
- lib/labkit/rate_limit/evaluator.rb
|
|
604
604
|
- lib/labkit/rate_limit/identifier.rb
|
|
605
605
|
- lib/labkit/rate_limit/limiter.rb
|
|
606
|
+
- lib/labkit/rate_limit/metrics.rb
|
|
606
607
|
- lib/labkit/rate_limit/result.rb
|
|
607
608
|
- lib/labkit/rate_limit/rule.rb
|
|
608
609
|
- lib/labkit/rspec/README.md
|