gitlab-labkit 1.0.1 → 1.1.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/config/user_experience_slis/schema.json +3 -3
- data/gitlab-labkit.gemspec +2 -1
- data/lib/labkit/json_schema/README.md +74 -0
- data/lib/labkit/json_schema/ref_resolver.rb +63 -0
- data/lib/labkit/user_experience_sli/README.md +17 -0
- data/lib/labkit/user_experience_sli/registry.rb +8 -4
- data/lib/labkit/user_experience_sli.rb +2 -1
- metadata +20 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed4c330d977763fb6063a9caab83352d59f32c46ac10a097f33f468cb06b2ddc
|
|
4
|
+
data.tar.gz: 558fae5ff6684b13cce03ee9b5f306c949a19c1a44b89c3fcae0567caa8175cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f1cd4013a210766f17a988f7922e900b9b00f74089fdde68e143cc718da629bd5d7442248ee47d15d0d7aa513e85eae6d9d642a5ddbfa5eddbe74046f7674cbb
|
|
7
|
+
data.tar.gz: 22dae3a5fb187c7118f4dd86df667982c1e72ef023d5a52aad1c6c10813845f3128d714973957c3ddca2b436b1419115904ca5bd76510315f57a7a8d3cd48078
|
|
@@ -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",
|
data/gitlab-labkit.gemspec
CHANGED
|
@@ -25,7 +25,8 @@ 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.4.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
32
|
spec.add_runtime_dependency "prometheus-client-mmap", "~> 1.2.9"
|
|
@@ -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
|
|
@@ -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`).
|
|
@@ -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
|
|
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.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Newdigate
|
|
@@ -98,19 +98,33 @@ 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
102
|
requirement: !ruby/object:Gem::Requirement
|
|
103
103
|
requirements:
|
|
104
104
|
- - "~>"
|
|
105
105
|
- !ruby/object:Gem::Version
|
|
106
|
-
version:
|
|
106
|
+
version: 2.4.0
|
|
107
107
|
type: :runtime
|
|
108
108
|
prerelease: false
|
|
109
109
|
version_requirements: !ruby/object:Gem::Requirement
|
|
110
110
|
requirements:
|
|
111
111
|
- - "~>"
|
|
112
112
|
- !ruby/object:Gem::Version
|
|
113
|
-
version:
|
|
113
|
+
version: 2.4.0
|
|
114
|
+
- !ruby/object:Gem::Dependency
|
|
115
|
+
name: openssl
|
|
116
|
+
requirement: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - "~>"
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: 3.3.2
|
|
121
|
+
type: :runtime
|
|
122
|
+
prerelease: false
|
|
123
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - "~>"
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: 3.3.2
|
|
114
128
|
- !ruby/object:Gem::Dependency
|
|
115
129
|
name: opentracing
|
|
116
130
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -468,6 +482,8 @@ files:
|
|
|
468
482
|
- lib/labkit/fields.rb
|
|
469
483
|
- lib/labkit/fips.rb
|
|
470
484
|
- lib/labkit/httpclient_publisher.rb
|
|
485
|
+
- lib/labkit/json_schema/README.md
|
|
486
|
+
- lib/labkit/json_schema/ref_resolver.rb
|
|
471
487
|
- lib/labkit/logging.rb
|
|
472
488
|
- lib/labkit/logging/grpc.rb
|
|
473
489
|
- lib/labkit/logging/grpc/server_interceptor.rb
|