gitlab-labkit 1.15.0 → 1.16.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 +40 -4
- data/lib/labkit/rate_limit/limiter.rb +3 -16
- data/lib/labkit/rate_limit/metrics.rb +43 -0
- data/lib/labkit/rate_limit/result.rb +16 -7
- data/lib/labkit/rate_limit.rb +1 -0
- 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: 076bc93169bb2f40201938632e87cfb1ddf8fbd3cdb74f895970939da47632e3
|
|
4
|
+
data.tar.gz: 4596ceee64a5754c7adfaeaeb571a19c9f778962423ff13a2339390d8a5ede96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3bd441afdb0a7d0f2bb9faa946a9edac112f97e8779fc98ff417289add5180b661a16a26d84b5dafca50032c0f9e0b96ef9099d864cb9a51210ca763bcb15c27
|
|
7
|
+
data.tar.gz: 4b14cf9420fd83533383578e6771e553209c4e64be247d6d3333717be7e5e041381a12fb6cd8cb772cd1405822cb3b3018c410b3a01d9ae25c89137936758984
|
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,8 +24,9 @@ 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
|
-
Result.new(matched: false, error: true)
|
|
29
|
+
Result.new(matched: false, error: true, action: :allow)
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
private
|
|
@@ -34,10 +35,13 @@ 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
|
|
|
40
|
-
|
|
43
|
+
report_unmatched_metrics
|
|
44
|
+
Result.new(matched: false, action: :allow)
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
def rule_matches?(rule, identifier)
|
|
@@ -51,8 +55,12 @@ module Labkit
|
|
|
51
55
|
|
|
52
56
|
count = incr_with_ttl(redis_key, resolved_period)
|
|
53
57
|
exceeded = count > resolved_limit
|
|
58
|
+
action = exceeded ? rule.action : :allow
|
|
54
59
|
|
|
55
|
-
Result.new(
|
|
60
|
+
Result.new(
|
|
61
|
+
matched: true, exceeded: exceeded, action: action, rule: rule,
|
|
62
|
+
resolved_limit: resolved_limit, resolved_period: resolved_period
|
|
63
|
+
)
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
def build_redis_key(rule, identifier)
|
|
@@ -98,6 +106,34 @@ module Labkit
|
|
|
98
106
|
identifier: identifier&.to_h
|
|
99
107
|
)
|
|
100
108
|
end
|
|
109
|
+
|
|
110
|
+
def report_matched_metrics(result)
|
|
111
|
+
Metrics.calls_total.increment(
|
|
112
|
+
rate_limiter: @name,
|
|
113
|
+
rule: result.rule.name,
|
|
114
|
+
action: result.action.to_s
|
|
115
|
+
)
|
|
116
|
+
Metrics.limit_gauge.set(
|
|
117
|
+
{ rate_limiter: @name, rule: result.rule.name },
|
|
118
|
+
result.resolved_limit
|
|
119
|
+
)
|
|
120
|
+
Metrics.period_gauge.set(
|
|
121
|
+
{ rate_limiter: @name, rule: result.rule.name },
|
|
122
|
+
result.resolved_period
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def report_unmatched_metrics
|
|
127
|
+
Metrics.calls_total.increment(
|
|
128
|
+
rate_limiter: @name,
|
|
129
|
+
rule: "unmatched",
|
|
130
|
+
action: "allow"
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def report_error_metrics
|
|
135
|
+
Metrics.errors_total.increment(rate_limiter: @name)
|
|
136
|
+
end
|
|
101
137
|
end
|
|
102
138
|
end
|
|
103
139
|
end
|
|
@@ -21,11 +21,10 @@ module Labkit
|
|
|
21
21
|
|
|
22
22
|
def initialize(name:, rules:, redis: nil, logger: nil)
|
|
23
23
|
@logger = logger || RateLimit.config.logger || Labkit::Logging::JsonLogger.new($stdout)
|
|
24
|
-
|
|
25
|
-
@name = validated_name
|
|
24
|
+
@name = validate_name!(name)
|
|
26
25
|
|
|
27
26
|
@evaluator = Evaluator.new(
|
|
28
|
-
name:
|
|
27
|
+
name: @name,
|
|
29
28
|
rules: prepare_rules(rules),
|
|
30
29
|
redis: redis || RateLimit.config.redis,
|
|
31
30
|
logger: @logger
|
|
@@ -36,19 +35,7 @@ module Labkit
|
|
|
36
35
|
# @return [Result]
|
|
37
36
|
def check(identifier)
|
|
38
37
|
id = identifier.is_a?(Identifier) ? identifier : Identifier.new(identifier)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if result.exceeded? && result.action == :block
|
|
42
|
-
@logger.warn(
|
|
43
|
-
message: "rate_limit_check",
|
|
44
|
-
name: @name,
|
|
45
|
-
rule_name: result.rule.name,
|
|
46
|
-
exceeded: true,
|
|
47
|
-
severity: "WARN"
|
|
48
|
-
)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
result
|
|
38
|
+
@evaluator.check(id)
|
|
52
39
|
end
|
|
53
40
|
|
|
54
41
|
private
|
|
@@ -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,13 +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
|
-
# rule
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
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)
|
|
13
22
|
super
|
|
14
23
|
end
|
|
15
24
|
|
data/lib/labkit/rate_limit.rb
CHANGED
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.16.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
|