gitlab-labkit 1.0.1 → 1.1.3
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/.copier-answers.yml +1 -1
- data/.gitlab-ci-asdf-versions.yml +1 -1
- data/.gitlab-ci.yml +3 -3
- data/.pre-commit-config.yaml +1 -1
- data/.tool-versions +1 -1
- data/config/user_experience_slis/schema.json +3 -3
- data/doc/architecture/decisions/001_field_standardization_dynamic_runtime_linting.md +124 -0
- data/gitlab-labkit.gemspec +3 -2
- data/lib/labkit/json_schema/README.md +74 -0
- data/lib/labkit/json_schema/ref_resolver.rb +63 -0
- data/lib/labkit/logging/json_logger.rb +8 -12
- data/lib/labkit/user_experience_sli/README.md +17 -0
- data/lib/labkit/user_experience_sli/experience.rb +3 -3
- data/lib/labkit/user_experience_sli/registry.rb +8 -4
- data/lib/labkit/user_experience_sli.rb +2 -1
- data/scripts/prepare-dev-env.sh +4 -1
- metadata +37 -9
- data/scripts/install-asdf-plugins.sh +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bbb89ac1275f06d828f9d9fa4a24e20e33f4a01d201956d4dd8d30e87dd8f5a
|
|
4
|
+
data.tar.gz: 1d6b24a17f8b2096e34303fba306ea1271146b3245cb5edcb3fabbccd26f1e45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4950f9c1615b432b5d53ab54a51cca84a10cbf548b434665fdda93a2204f912f172540652df7b634907c61fa88f03f50a1ad9de96e04d2795a59393fe3fdcf93
|
|
7
|
+
data.tar.gz: a67c2262dd119373431f271779baf8311c5eb47a794a2970e1b4b9dcd28c59a4253ff844654f20a99026f95aabe6538f8fbdc864da29f007cd6c94a99eb427ad
|
data/.copier-answers.yml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# See the project for instructions on how to update the project
|
|
4
4
|
#
|
|
5
5
|
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
|
|
6
|
-
_commit: v1.
|
|
6
|
+
_commit: v1.38.0
|
|
7
7
|
_src_path: https://gitlab.com/gitlab-com/gl-infra/common-template-copier.git
|
|
8
8
|
ee_licensed: false
|
|
9
9
|
golang: false
|
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@
|
|
22
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/standard-build@v3.4
|
|
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@
|
|
26
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/ruby-build@v3.4
|
|
27
27
|
|
|
28
|
-
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@
|
|
28
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@v3.4
|
|
29
29
|
|
|
30
30
|
.test_template: &test_definition
|
|
31
31
|
extends: .with_bundle
|
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.4 # renovate:managed
|
|
29
29
|
|
|
30
30
|
hooks:
|
|
31
31
|
- id: shellcheck # Run shellcheck for changed Shell files
|
data/.tool-versions
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/raw/master/config/user_experience_slis/schema.json",
|
|
4
4
|
"title": "User Experience SLI Definition",
|
|
5
5
|
"description": "Schema for GitLab User Experience SLI files",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
},
|
|
13
13
|
"feature_category": {
|
|
14
14
|
"type": "string",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"description": "GitLab feature category this experience belongs to",
|
|
16
|
+
"$ref": "https://gitlab.com/gitlab-org/gitlab/-/raw/master/config/feature_categories/schema.json"
|
|
17
17
|
},
|
|
18
18
|
"urgency": {
|
|
19
19
|
"type": "string",
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
owning-stage: '~devops::developer experience'
|
|
3
|
+
description: 'Logging Field Standardization: Dynamic Runtime Linting'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Logging Field Standardization ADR: Dynamic Runtime Linting
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
GitLab is implementing a [logging field standardization initiative](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_field_standardisation/) to ensure logs are queryable and actionable across all systems. We need a mechanism to identify and track deprecated or non-standard logging fields across multiple codebases.
|
|
11
|
+
|
|
12
|
+
### Requirements
|
|
13
|
+
|
|
14
|
+
The solution must:
|
|
15
|
+
|
|
16
|
+
1. Detect deprecated fields at the point of emission (runtime).
|
|
17
|
+
1. Provide immediate feedback to developers (shift-left).
|
|
18
|
+
1. Prevent new violations without blocking existing work.
|
|
19
|
+
1. Support gradual migration from deprecated to standard fields.
|
|
20
|
+
1. Never affect production performance.
|
|
21
|
+
1. Provide clear guidance on standard field replacements.
|
|
22
|
+
|
|
23
|
+
### The problem with static analysis
|
|
24
|
+
|
|
25
|
+
We initially considered RuboCop (static linting), but this approach is inadequate due to Ruby's [dynamic and complex field construction](https://gitlab.com/gitlab-org/gitlab/-/blob/cfd4fb97968d1f7d30f39f89740e414e9437063a/lib/bulk_imports/logger.rb#L46).
|
|
26
|
+
|
|
27
|
+
Static analysis can't reliably detect:
|
|
28
|
+
|
|
29
|
+
- Fields merged from hash arguments.
|
|
30
|
+
- Dynamically constructed field names.
|
|
31
|
+
- Fields passed through multiple abstraction layers.
|
|
32
|
+
- Conditional field inclusion based on runtime state.
|
|
33
|
+
|
|
34
|
+
## Decision
|
|
35
|
+
|
|
36
|
+
Implement dynamic runtime linting to validate logging fields as they're emitted during development and testing, rather than using static analysis.
|
|
37
|
+
|
|
38
|
+
The validator will:
|
|
39
|
+
|
|
40
|
+
- Intercept logging calls at runtime to detect deprecated fields.
|
|
41
|
+
- Compare detected violations against a frozen baseline of known violations.
|
|
42
|
+
- Raise an error on new violations while ignoring tracked existing tracked violations.
|
|
43
|
+
- Provide immediate feedback to developers during local development.
|
|
44
|
+
- Never affect production performance.
|
|
45
|
+
|
|
46
|
+
## Consequences
|
|
47
|
+
|
|
48
|
+
### Benefits
|
|
49
|
+
|
|
50
|
+
- **Shift-left feedback**: Developers discover violations during local development, not in code review.
|
|
51
|
+
- **Comprehensive detection**: Captures violations from all code execution paths (tests, Rake tasks, console, development environments).
|
|
52
|
+
- **Accurate detection**: Runtime interception catches dynamically constructed fields that static analysis misses.
|
|
53
|
+
- **Non-blocking**: Existing violations don't prevent development. They're tracked explicitly in baseline.
|
|
54
|
+
- **Regression prevention**: CI fails on new violations, preventing deprecated fields from reappearing.
|
|
55
|
+
- **Clear guidance**: Exact replacement field suggested for each violation.
|
|
56
|
+
- **Zero production impact**: Validation only runs in development and test environments.
|
|
57
|
+
|
|
58
|
+
### Trade-offs
|
|
59
|
+
|
|
60
|
+
- **Execution path dependency**: Only detects violations in code paths that execute during the process.
|
|
61
|
+
- **Runtime overhead**: Adds interception to all non-production processes.
|
|
62
|
+
- **At-exit reporting**: Violations not visible until process completes.
|
|
63
|
+
- **Baseline maintenance**: YAML files require updates when violations are fixed or added.
|
|
64
|
+
- **Learning curve**: Developers must understand baseline management.
|
|
65
|
+
- **File-level scoping**: Multiple violations in the same file are tracked together, making partial fixes during test runs challenging.
|
|
66
|
+
|
|
67
|
+
### Risks and mitigations
|
|
68
|
+
|
|
69
|
+
| Risk | Mitigation |
|
|
70
|
+
|------|------------|
|
|
71
|
+
| Limited code path coverage misses violations | Use Kibana to identify fields in production logs, run comprehensive test suites, use development server testing |
|
|
72
|
+
| Baseline drift across branches | Clear documentation, automated baseline regeneration support |
|
|
73
|
+
| At-exit reporting missed if process crashes | Violations still prevented in CI where processes complete successfully |
|
|
74
|
+
|
|
75
|
+
## Alternatives
|
|
76
|
+
|
|
77
|
+
### Alternative 1: Static analysis with RuboCop
|
|
78
|
+
|
|
79
|
+
Use custom RuboCop cops to detect deprecated field usage.
|
|
80
|
+
|
|
81
|
+
Rejected because:
|
|
82
|
+
|
|
83
|
+
- Can't handle fields merged from hash variables.
|
|
84
|
+
- Would produce many false negatives.
|
|
85
|
+
- Complex pattern matching still misses dynamic cases.
|
|
86
|
+
- Poor developer experience with unclear violations.
|
|
87
|
+
|
|
88
|
+
### Alternative 2: Manual tracking
|
|
89
|
+
|
|
90
|
+
Track violations in spreadsheets or GitLab issues.
|
|
91
|
+
|
|
92
|
+
Rejected because:
|
|
93
|
+
|
|
94
|
+
- No automated enforcement or detection.
|
|
95
|
+
- Manual process becomes stale quickly.
|
|
96
|
+
- No shift-left feedback to developers.
|
|
97
|
+
- Can't prevent regressions.
|
|
98
|
+
|
|
99
|
+
### Alternative 3: Grep-based CI checks
|
|
100
|
+
|
|
101
|
+
Search source code for deprecated field strings in CI.
|
|
102
|
+
|
|
103
|
+
Rejected because:
|
|
104
|
+
|
|
105
|
+
- High false positive rate (matches in comments, strings, tests).
|
|
106
|
+
- Can't distinguish field usage from definitions.
|
|
107
|
+
- No stable tracking across refactoring.
|
|
108
|
+
- Poor user experience with unclear error messages.
|
|
109
|
+
|
|
110
|
+
### Alternative 4: Production log analysis only
|
|
111
|
+
|
|
112
|
+
Rely solely on Kibana analysis to find deprecated fields.
|
|
113
|
+
|
|
114
|
+
Rejected because:
|
|
115
|
+
|
|
116
|
+
- No prevention, only reactive detection.
|
|
117
|
+
- Violations already in production when discovered.
|
|
118
|
+
- No developer feedback during development.
|
|
119
|
+
- Difficult to trace back to specific code locations.
|
|
120
|
+
|
|
121
|
+
## References
|
|
122
|
+
|
|
123
|
+
- [Parent Epic](https://gitlab.com/groups/gitlab-org/quality/-/work_items/235)
|
|
124
|
+
- [Observability Field Standardisation](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_field_standardisation/)
|
data/gitlab-labkit.gemspec
CHANGED
|
@@ -25,10 +25,11 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.add_runtime_dependency "grpc", ">= 1.75" # Be sure to update the "grpc-tools" dev_dependency too
|
|
26
26
|
spec.add_runtime_dependency "google-protobuf", ">= 3.25", "< 5.0"
|
|
27
27
|
spec.add_runtime_dependency "jaeger-client", "~> 1.1.0"
|
|
28
|
-
spec.add_runtime_dependency '
|
|
28
|
+
spec.add_runtime_dependency 'json_schemer', '>= 2.3.0', '< 3.0'
|
|
29
|
+
spec.add_runtime_dependency "openssl", "~> 3.3.2"
|
|
29
30
|
spec.add_runtime_dependency "opentracing", "~> 0.4"
|
|
30
31
|
spec.add_runtime_dependency "pg_query", ">= 6.1.0", "< 7.0"
|
|
31
|
-
spec.add_runtime_dependency "prometheus-client-mmap", "
|
|
32
|
+
spec.add_runtime_dependency "prometheus-client-mmap", ">= 1.2", "< 2.0"
|
|
32
33
|
spec.add_runtime_dependency "redis", "> 3.0.0", "< 6.0.0"
|
|
33
34
|
|
|
34
35
|
# Please maintain alphabetical order for dev dependencies
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# JSON Schema Reference Resolver
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `RefResolver` class provides HTTP/HTTPS-based resolution for external JSON schema references. It enables validation of JSON data against schemas that reference remote schema definitions, with built-in caching and timeout protection.
|
|
6
|
+
|
|
7
|
+
## Why Use RefResolver?
|
|
8
|
+
|
|
9
|
+
When working with JSON schemas, you may encounter `$ref` properties that point to external schema definitions hosted remotely:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"$ref": "https://example.com/schemas/common.json#/definitions/address"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The `RefResolver` solves several key challenges:
|
|
18
|
+
|
|
19
|
+
- **Remote Schema Resolution**: Automatically fetches and parses external schema definitions over HTTP/HTTPS
|
|
20
|
+
- **Performance Optimization**: Caches fetched schemas to avoid redundant network requests
|
|
21
|
+
- **Reliability**: Implements configurable timeouts to prevent hanging on slow or unresponsive endpoints
|
|
22
|
+
- **Error Handling**: Provides clear error messages for network failures, timeouts, and invalid JSON responses
|
|
23
|
+
|
|
24
|
+
## How to Use
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
The `RefResolver` is designed to work with the [JSONSchemer](https://github.com/davishmcclurg/json_schemer) gem:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
require 'labkit/json_schema/ref_resolver'
|
|
32
|
+
|
|
33
|
+
# Create a resolver instance
|
|
34
|
+
resolver = Labkit::JsonSchema::RefResolver.new
|
|
35
|
+
|
|
36
|
+
# Use with JSONSchemer
|
|
37
|
+
schema = JSONSchemer.schema(
|
|
38
|
+
your_schema_hash,
|
|
39
|
+
ref_resolver: resolver
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Validate data
|
|
43
|
+
schema.valid?(your_data)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Custom Timeout Configuration
|
|
47
|
+
|
|
48
|
+
By default, the resolver uses a 2-second timeout for both connection and read operations. You can customize this:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# Use a 5-second timeout
|
|
52
|
+
resolver = Labkit::JsonSchema::RefResolver.new(timeout_s: 5)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Cache Management
|
|
56
|
+
|
|
57
|
+
The resolver maintains a class-level cache to share fetched schemas across instances:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Access the cache
|
|
61
|
+
Labkit::JsonSchema::RefResolver.cache
|
|
62
|
+
|
|
63
|
+
# Clear the cache if needed
|
|
64
|
+
Labkit::JsonSchema::RefResolver.cache.clear
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Use Case Example
|
|
68
|
+
|
|
69
|
+
The `RefResolver` is used in the User Experience SLI Registry [lib/labkit/user_experience_sli/registry.rb](lib/labkit/user_experience_sli/registry.rb) to validate user experience definitions against a JSON schema.
|
|
70
|
+
|
|
71
|
+
## Implementation Details
|
|
72
|
+
|
|
73
|
+
- Only HTTP and HTTPS schemes are supported. Other schemes (FTP, file://, etc.) will raise an error.
|
|
74
|
+
- The class-level cache is shared across all instances. In multi-threaded environments, consider using appropriate synchronization mechanisms if cache consistency is critical.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Labkit
|
|
8
|
+
module JsonSchema
|
|
9
|
+
# This class resolves JSON schema references (e.g., "$ref": "http://example.com/schema.json")
|
|
10
|
+
# by fetching remote schemas over HTTP/HTTPS and caching them.
|
|
11
|
+
# It is used by JSONSchemer to resolve external schema definitions.
|
|
12
|
+
# We need it to validate JSON data against schemas that might be hosted remotely.
|
|
13
|
+
class RefResolver
|
|
14
|
+
mattr_accessor :cache, default: {}
|
|
15
|
+
|
|
16
|
+
def initialize(timeout_s: 2)
|
|
17
|
+
@timeout_s = timeout_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call(uri)
|
|
21
|
+
uri_str = uri.to_s
|
|
22
|
+
|
|
23
|
+
return cache[uri_str] if cache.key?(uri_str)
|
|
24
|
+
|
|
25
|
+
cache[uri_str] = fetch_remote_schema(uri_str)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def fetch_remote_schema(uri_str)
|
|
31
|
+
uri = URI(uri_str)
|
|
32
|
+
|
|
33
|
+
raise(JSONSchemer::UnknownRef, "Unsupported URI scheme: #{uri_str}") unless %w[http https].include?(uri.scheme)
|
|
34
|
+
|
|
35
|
+
response = Net::HTTP.start(
|
|
36
|
+
uri.host,
|
|
37
|
+
uri.port,
|
|
38
|
+
use_ssl: uri.scheme == 'https',
|
|
39
|
+
open_timeout: @timeout_s,
|
|
40
|
+
read_timeout: @timeout_s
|
|
41
|
+
) do |http|
|
|
42
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
|
43
|
+
http.request(request)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
47
|
+
raise(
|
|
48
|
+
JSONSchemer::UnknownRef,
|
|
49
|
+
"Failed to fetch #{uri_str}: #{response.code} #{response.message}"
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
JSON.parse(response.body)
|
|
54
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
55
|
+
raise(JSONSchemer::UnknownRef, "Timeout fetching #{uri_str}: #{e.message}")
|
|
56
|
+
rescue JSON::ParserError => e
|
|
57
|
+
raise(JSONSchemer::UnknownRef, "Invalid JSON at #{uri_str}: #{e.message}")
|
|
58
|
+
rescue SocketError, Errno::ECONNREFUSED => e
|
|
59
|
+
raise(JSONSchemer::UnknownRef, "Connection failed for #{uri_str}: #{e.message}")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "time"
|
|
3
|
+
require "date"
|
|
3
4
|
require "logger"
|
|
4
5
|
require "json"
|
|
5
6
|
|
|
@@ -37,6 +38,12 @@ module Labkit
|
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def format_message(severity, timestamp, progname, message)
|
|
41
|
+
data = format_data(severity, timestamp, progname, message)
|
|
42
|
+
|
|
43
|
+
dump_json(data) << "\n"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def format_data(severity, timestamp, progname, message)
|
|
40
47
|
data = default_attributes
|
|
41
48
|
data[:severity] = severity
|
|
42
49
|
data[:time] = timestamp.utc.iso8601(3)
|
|
@@ -52,11 +59,10 @@ module Labkit
|
|
|
52
59
|
data[:message] = message
|
|
53
60
|
when Hash
|
|
54
61
|
reject_reserved_log_keys!(message)
|
|
55
|
-
format_time!(data)
|
|
56
62
|
data.merge!(message)
|
|
57
63
|
end
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
data
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
private
|
|
@@ -78,16 +84,6 @@ module Labkit
|
|
|
78
84
|
"\n\nUse key names that are descriptive e.g. by using a prefix."
|
|
79
85
|
end
|
|
80
86
|
end
|
|
81
|
-
|
|
82
|
-
def format_time!(hash)
|
|
83
|
-
hash.each do |key, value|
|
|
84
|
-
if value.is_a?(Time)
|
|
85
|
-
hash[key] = value.utc.iso8601(3)
|
|
86
|
-
elsif value.is_a?(Hash)
|
|
87
|
-
format_time!(value)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
87
|
end
|
|
92
88
|
end
|
|
93
89
|
end
|
|
@@ -33,6 +33,23 @@ This allows you to:
|
|
|
33
33
|
|
|
34
34
|
**Note:** The registry is automatically reset when the configuration changes, so the new path takes effect immediately.
|
|
35
35
|
|
|
36
|
+
### RefResolver Timeout Configuration
|
|
37
|
+
|
|
38
|
+
When validating user experience SLI definitions against JSON schemas, the system may need to fetch remote schema references over HTTP/HTTPS. By default, the timeout for these requests is 2 seconds. You can configure a custom timeout:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
Labkit::UserExperienceSli.configure do |config|
|
|
42
|
+
config.ref_resolver_timeout = 5 # Set timeout to 5 seconds
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This configuration is useful when:
|
|
47
|
+
- Working in environments with slower network connections
|
|
48
|
+
- Fetching schemas from remote servers with higher latency
|
|
49
|
+
- Needing stricter timeout constraints for faster failure detection
|
|
50
|
+
|
|
51
|
+
**Note:** The timeout applies to both connection opening and reading operations when fetching remote JSON schema references.
|
|
52
|
+
|
|
36
53
|
### User Experience Definitions
|
|
37
54
|
|
|
38
55
|
User Experience SLI definitions will be lazy loaded from the default directory (`config/user_experience_slis`).
|
|
@@ -236,9 +236,9 @@ module Labkit
|
|
|
236
236
|
user_experience_id: id,
|
|
237
237
|
feature_category: @definition.feature_category,
|
|
238
238
|
urgency: @definition.urgency,
|
|
239
|
-
start_time: @start_time,
|
|
240
|
-
checkpoint_time: @checkpoint_time,
|
|
241
|
-
end_time: @end_time,
|
|
239
|
+
start_time: @start_time&.iso8601(3),
|
|
240
|
+
checkpoint_time: @checkpoint_time&.iso8601(3),
|
|
241
|
+
end_time: @end_time&.iso8601(3),
|
|
242
242
|
elapsed_time_s: elapsed_time,
|
|
243
243
|
urgency_threshold_s: urgency_threshold
|
|
244
244
|
)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'forwardable'
|
|
4
|
-
require '
|
|
4
|
+
require 'json_schemer'
|
|
5
5
|
require 'pathname'
|
|
6
6
|
require 'yaml'
|
|
7
|
+
require 'labkit/json_schema/ref_resolver'
|
|
7
8
|
|
|
8
9
|
module Labkit
|
|
9
10
|
module UserExperienceSli
|
|
@@ -77,8 +78,7 @@ module Labkit
|
|
|
77
78
|
content = YAML.safe_load(file_path.read)
|
|
78
79
|
return nil unless content.is_a?(Hash)
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
return Definition.new(user_experience_id: experience_id, **content) if errors.empty?
|
|
81
|
+
return Definition.new(user_experience_id: experience_id, **content) if schema.valid?(content)
|
|
82
82
|
|
|
83
83
|
warn("Invalid schema for #{file_path}")
|
|
84
84
|
|
|
@@ -90,7 +90,11 @@ module Labkit
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def schema
|
|
93
|
-
@schema ||=
|
|
93
|
+
@schema ||= begin
|
|
94
|
+
schema = JSON.parse(File.read(SCHEMA_PATH))
|
|
95
|
+
timeout = Labkit::UserExperienceSli.configuration.ref_resolver_timeout
|
|
96
|
+
JSONSchemer.schema(schema, ref_resolver: Labkit::JsonSchema::RefResolver.new(timeout_s: timeout))
|
|
97
|
+
end
|
|
94
98
|
end
|
|
95
99
|
|
|
96
100
|
def warn(message)
|
|
@@ -16,11 +16,12 @@ module Labkit
|
|
|
16
16
|
module UserExperienceSli
|
|
17
17
|
# Configuration class for UserExperienceSli
|
|
18
18
|
class Configuration
|
|
19
|
-
attr_accessor :logger, :registry_path
|
|
19
|
+
attr_accessor :logger, :registry_path, :ref_resolver_timeout
|
|
20
20
|
|
|
21
21
|
def initialize
|
|
22
22
|
@logger = Labkit::Logging::JsonLogger.new($stdout)
|
|
23
23
|
@registry_path = File.join("config", "user_experience_slis")
|
|
24
|
+
@ref_resolver_timeout = 2
|
|
24
25
|
end
|
|
25
26
|
end
|
|
26
27
|
|
data/scripts/prepare-dev-env.sh
CHANGED
|
@@ -53,8 +53,11 @@ fi
|
|
|
53
53
|
|
|
54
54
|
# install mise/asdf dependencies
|
|
55
55
|
echo "installing required plugins with mise install.."
|
|
56
|
-
mise plugins update
|
|
56
|
+
mise plugins update -q
|
|
57
57
|
mise install
|
|
58
|
+
|
|
59
|
+
# set PROMPT_COMMAND to empty value for mise if unset
|
|
60
|
+
: "${PROMPT_COMMAND:=}"
|
|
58
61
|
eval "$(mise activate bash)"
|
|
59
62
|
|
|
60
63
|
# pre-commit is optional
|
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.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Newdigate
|
|
@@ -98,19 +98,39 @@ dependencies:
|
|
|
98
98
|
- !ruby/object:Gem::Version
|
|
99
99
|
version: 1.1.0
|
|
100
100
|
- !ruby/object:Gem::Dependency
|
|
101
|
-
name:
|
|
101
|
+
name: json_schemer
|
|
102
|
+
requirement: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: 2.3.0
|
|
107
|
+
- - "<"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '3.0'
|
|
110
|
+
type: :runtime
|
|
111
|
+
prerelease: false
|
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 2.3.0
|
|
117
|
+
- - "<"
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
version: '3.0'
|
|
120
|
+
- !ruby/object:Gem::Dependency
|
|
121
|
+
name: openssl
|
|
102
122
|
requirement: !ruby/object:Gem::Requirement
|
|
103
123
|
requirements:
|
|
104
124
|
- - "~>"
|
|
105
125
|
- !ruby/object:Gem::Version
|
|
106
|
-
version:
|
|
126
|
+
version: 3.3.2
|
|
107
127
|
type: :runtime
|
|
108
128
|
prerelease: false
|
|
109
129
|
version_requirements: !ruby/object:Gem::Requirement
|
|
110
130
|
requirements:
|
|
111
131
|
- - "~>"
|
|
112
132
|
- !ruby/object:Gem::Version
|
|
113
|
-
version:
|
|
133
|
+
version: 3.3.2
|
|
114
134
|
- !ruby/object:Gem::Dependency
|
|
115
135
|
name: opentracing
|
|
116
136
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -149,16 +169,22 @@ dependencies:
|
|
|
149
169
|
name: prometheus-client-mmap
|
|
150
170
|
requirement: !ruby/object:Gem::Requirement
|
|
151
171
|
requirements:
|
|
152
|
-
- - "
|
|
172
|
+
- - ">="
|
|
173
|
+
- !ruby/object:Gem::Version
|
|
174
|
+
version: '1.2'
|
|
175
|
+
- - "<"
|
|
153
176
|
- !ruby/object:Gem::Version
|
|
154
|
-
version:
|
|
177
|
+
version: '2.0'
|
|
155
178
|
type: :runtime
|
|
156
179
|
prerelease: false
|
|
157
180
|
version_requirements: !ruby/object:Gem::Requirement
|
|
158
181
|
requirements:
|
|
159
|
-
- - "
|
|
182
|
+
- - ">="
|
|
160
183
|
- !ruby/object:Gem::Version
|
|
161
|
-
version: 1.2
|
|
184
|
+
version: '1.2'
|
|
185
|
+
- - "<"
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '2.0'
|
|
162
188
|
- !ruby/object:Gem::Dependency
|
|
163
189
|
name: redis
|
|
164
190
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -455,6 +481,7 @@ files:
|
|
|
455
481
|
- Rakefile
|
|
456
482
|
- config/user_experience_slis/schema.json
|
|
457
483
|
- config/user_experience_slis/testing_sample.yml
|
|
484
|
+
- doc/architecture/decisions/001_field_standardization_dynamic_runtime_linting.md
|
|
458
485
|
- gitlab-labkit.gemspec
|
|
459
486
|
- lib/gitlab-labkit.rb
|
|
460
487
|
- lib/labkit/context.rb
|
|
@@ -468,6 +495,8 @@ files:
|
|
|
468
495
|
- lib/labkit/fields.rb
|
|
469
496
|
- lib/labkit/fips.rb
|
|
470
497
|
- lib/labkit/httpclient_publisher.rb
|
|
498
|
+
- lib/labkit/json_schema/README.md
|
|
499
|
+
- lib/labkit/json_schema/ref_resolver.rb
|
|
471
500
|
- lib/labkit/logging.rb
|
|
472
501
|
- lib/labkit/logging/grpc.rb
|
|
473
502
|
- lib/labkit/logging/grpc/server_interceptor.rb
|
|
@@ -539,7 +568,6 @@ files:
|
|
|
539
568
|
- lib/labkit/user_experience_sli/null.rb
|
|
540
569
|
- lib/labkit/user_experience_sli/registry.rb
|
|
541
570
|
- renovate.json
|
|
542
|
-
- scripts/install-asdf-plugins.sh
|
|
543
571
|
- scripts/prepare-dev-env.sh
|
|
544
572
|
- scripts/update-asdf-version-variables.sh
|
|
545
573
|
homepage: https://gitlab.com/gitlab-org/labkit-ruby
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Vendored from https://gitlab.com/gitlab-com/gl-infra/common-template-copier
|
|
3
|
-
# Consider contributing upstream when updating this file
|
|
4
|
-
|
|
5
|
-
# This file is deprecated: going forward running `mise install` should be sufficient.
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
IFS=$'\n\t'
|
|
9
|
-
|
|
10
|
-
echo >&2 -e "2024-08-07: this file is deprecated: going forward, simply run 'mise install' to install plugins."
|
|
11
|
-
echo >&2 -e "Recommended reading: https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/docs/developer-setup.md"
|
|
12
|
-
|
|
13
|
-
mise install
|