kessel-sdk 1.10.0 → 1.12.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/README.md +14 -12
- data/lib/google/rpc/status_pb.rb +1 -1
- data/lib/kessel/auth.rb +187 -10
- data/lib/kessel/inventory/GUIDELINES.md +161 -0
- data/lib/kessel/inventory/v1beta2/acquire_lock_request_pb.rb +21 -0
- data/lib/kessel/inventory/v1beta2/acquire_lock_response_pb.rb +19 -0
- data/lib/kessel/inventory/v1beta2/create_tuples_request_pb.rb +22 -0
- data/lib/kessel/inventory/v1beta2/create_tuples_response_pb.rb +21 -0
- data/lib/kessel/inventory/v1beta2/delete_tuples_request_pb.rb +23 -0
- data/lib/kessel/inventory/v1beta2/delete_tuples_response_pb.rb +21 -0
- data/lib/kessel/inventory/v1beta2/read_tuples_request_pb.rb +24 -0
- data/lib/kessel/inventory/v1beta2/read_tuples_response_pb.rb +23 -0
- data/lib/kessel/inventory/v1beta2/relation_fencing_check_pb.rb +21 -0
- data/lib/kessel/inventory/v1beta2/relation_object_reference_pb.rb +22 -0
- data/lib/kessel/inventory/v1beta2/relation_object_type_pb.rb +21 -0
- data/lib/kessel/inventory/v1beta2/relation_subject_filter_pb.rb +19 -0
- data/lib/kessel/inventory/v1beta2/relation_subject_reference_pb.rb +22 -0
- data/lib/kessel/inventory/v1beta2/relation_tuple_filter_pb.rb +21 -0
- data/lib/kessel/inventory/v1beta2/relationship_pb.rb +23 -0
- data/lib/kessel/inventory/v1beta2/tuple_service_pb.rb +27 -0
- data/lib/kessel/inventory/v1beta2/tuple_service_services_pb.rb +44 -0
- data/lib/kessel/rbac/GUIDELINES.md +104 -0
- data/lib/kessel/rbac/v2.rb +10 -8
- data/lib/kessel/version.rb +1 -1
- metadata +20 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 997a5d14ffc861c340630e331dfd5c34f367ed7f68b9f760363a817180c10711
|
|
4
|
+
data.tar.gz: 6144712b5dec3a5355bc3f7e6a35953a0b4a2c2beeee56b017eebc659c03f3bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7397b29d146033e8d2060af9e25195debd851f052b4c3fad903277bd13b25ed8a1588f5a712bc8952e18fc75315cb79bd32c3a8732c07604f0cf51d2689ba843
|
|
7
|
+
data.tar.gz: 0c3b20634ed5d7529d66ba4b3e06c15e677eddfaf676618eaa4864205d7157a3b57d9cbab91b77de8e3adbbdbb707e255ef3055e986de6064a87b9c284b0e7dc
|
data/README.md
CHANGED
|
@@ -58,7 +58,6 @@ lib/
|
|
|
58
58
|
sig/ # RBS type signatures for hand-written code
|
|
59
59
|
spec/ # RSpec test suite
|
|
60
60
|
examples/ # Working examples with dotenv configuration
|
|
61
|
-
docs/ # Domain-specific guidelines (see Documentation below)
|
|
62
61
|
```
|
|
63
62
|
|
|
64
63
|
Files under `lib/kessel/inventory/v*/`, `lib/google/`, and `lib/buf/` are **generated** by `buf generate` and must never be hand-edited. They are automatically regenerated every 6 hours via CI.
|
|
@@ -98,7 +97,8 @@ discovery = fetch_oidc_discovery('https://sso.example.com/auth/realms/my-realm')
|
|
|
98
97
|
oauth = OAuth2ClientCredentials.new(
|
|
99
98
|
client_id: 'my-app',
|
|
100
99
|
client_secret: 'my-secret',
|
|
101
|
-
token_endpoint: discovery.token_endpoint
|
|
100
|
+
token_endpoint: discovery.token_endpoint,
|
|
101
|
+
retry: { max_retries: 3, base_delay: 0.5, max_delay: 2.0, jitter: :full }
|
|
102
102
|
)
|
|
103
103
|
|
|
104
104
|
# Build the client -- tokens are cached and refreshed automatically
|
|
@@ -107,6 +107,13 @@ client = KesselInventoryService::ClientBuilder.new('kessel.example.com:443')
|
|
|
107
107
|
.build
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
Token fetch retries apply only to the token endpoint, not OIDC discovery. By default, network/timeout errors and HTTP
|
|
111
|
+
429/5xx responses receive four total attempts (initial plus three retries), with full-jitter delay caps of 0.5, 1,
|
|
112
|
+
and 2 seconds and total added sleep below 3.5 seconds. `retry` accepts non-negative integer `max_retries` (0
|
|
113
|
+
disables retries), positive finite-second `base_delay` and `max_delay`, and `jitter: :full` or `:none`; defaults are
|
|
114
|
+
`3`, `0.5`, `2.0`, and `:full`. Invalid retry configuration raises `ArgumentError`. Permanent OAuth/configuration
|
|
115
|
+
failures are not retried. Refresh is synchronous, so a transient SSO failure may delay an RPC.
|
|
116
|
+
|
|
110
117
|
#### Custom or No Credentials
|
|
111
118
|
|
|
112
119
|
```ruby
|
|
@@ -355,16 +362,11 @@ ruby check.rb
|
|
|
355
362
|
|
|
356
363
|
## Documentation
|
|
357
364
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
- **[API Contracts](docs/api-contracts-guidelines.md)** -- Protobuf code generation, module/namespace mapping, ClientBuilder API, request/response patterns, and RBS type signatures
|
|
361
|
-
- **[Integration](docs/integration-guidelines.md)** -- gRPC client construction, authentication flows, RBAC helpers, streaming/pagination, and environment configuration
|
|
362
|
-
- **[Security](docs/security-guidelines.md)** -- Token caching thread safety, gRPC channel security, credential validation, and secrets management
|
|
363
|
-
- **[Performance](docs/performance-guidelines.md)** -- Token caching, gRPC client reuse, bulk vs. individual operations, consistency controls, and streaming pagination
|
|
364
|
-
- **[Error Handling](docs/error-handling-guidelines.md)** -- Custom exception hierarchy, error wrapping conventions, and gRPC error passthrough policy
|
|
365
|
-
- **[Testing](docs/testing-guidelines.md)** -- RSpec configuration, mocking conventions, coverage setup, and CI expectations
|
|
365
|
+
For AI-assisted development context, see [AGENTS.md](AGENTS.md). Directory-local `GUIDELINES.md` files provide detailed conventions for specific areas of the codebase:
|
|
366
366
|
|
|
367
|
-
|
|
367
|
+
- **[lib/kessel/inventory/GUIDELINES.md](lib/kessel/inventory/GUIDELINES.md)** -- Inventory module: ClientBuilder, service wiring, V1beta2 API patterns, auth integration
|
|
368
|
+
- **[lib/kessel/rbac/GUIDELINES.md](lib/kessel/rbac/GUIDELINES.md)** -- RBAC V2 module conventions
|
|
369
|
+
- **[examples/GUIDELINES.md](examples/GUIDELINES.md)** -- Example script conventions
|
|
368
370
|
|
|
369
371
|
## Release Instructions
|
|
370
372
|
|
|
@@ -496,7 +498,7 @@ rake release
|
|
|
496
498
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
497
499
|
5. Open a Pull Request
|
|
498
500
|
|
|
499
|
-
Please review
|
|
501
|
+
Please review [AGENTS.md](AGENTS.md) and the directory-local `GUIDELINES.md` files before contributing. All specs must pass on Ruby 3.3 and 3.4. Fix RuboCop violations before merging, and update RBS type signatures in `sig/kessel/` when modifying hand-written code.
|
|
500
502
|
|
|
501
503
|
## License
|
|
502
504
|
|
data/lib/google/rpc/status_pb.rb
CHANGED
|
@@ -7,7 +7,7 @@ require 'google/protobuf'
|
|
|
7
7
|
require 'google/protobuf/any_pb'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
descriptor_data = "\n\x17google/rpc/status.proto\x12\ngoogle.rpc\x1a\x19google/protobuf/any.proto\"f\n\x06Status\x12\x12\n\x04\x63ode\x18\x01 \x01(\x05R\x04\x63ode\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message\x12.\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyR\x07\x64\
|
|
10
|
+
descriptor_data = "\n\x17google/rpc/status.proto\x12\ngoogle.rpc\x1a\x19google/protobuf/any.proto\"f\n\x06Status\x12\x12\n\x04\x63ode\x18\x01 \x01(\x05R\x04\x63ode\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message\x12.\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyR\x07\x64\x65tailsB^\n\x0e\x63om.google.rpcB\x0bStatusProtoP\x01Z7google.golang.org/genproto/googleapis/rpc/status;status\xa2\x02\x03RPCb\x06proto3"
|
|
11
11
|
|
|
12
12
|
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
13
|
pool.add_serialized_file(descriptor_data)
|
data/lib/kessel/auth.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'grpc'
|
|
4
4
|
require 'kessel/version'
|
|
5
|
+
require 'socket'
|
|
6
|
+
require 'timeout'
|
|
5
7
|
|
|
6
8
|
module Kessel
|
|
7
9
|
# OpenID Connect authentication module for Kessel services.
|
|
@@ -105,6 +107,7 @@ module Kessel
|
|
|
105
107
|
#
|
|
106
108
|
# # Get current access token (automatically cached and refreshed)
|
|
107
109
|
# token = oauth.get_token
|
|
110
|
+
# rubocop:disable Metrics/ClassLength
|
|
108
111
|
class OAuth2ClientCredentials
|
|
109
112
|
include Kessel::Auth
|
|
110
113
|
|
|
@@ -113,9 +116,14 @@ module Kessel
|
|
|
113
116
|
# @param client_id [String] OIDC client identifier
|
|
114
117
|
# @param client_secret [String] OIDC client secret
|
|
115
118
|
# @param token_endpoint [String] OIDC token endpoint URL
|
|
119
|
+
# @param retry [Hash] Optional token-endpoint retry settings. Keys are
|
|
120
|
+
# `max_retries` (non-negative Integer; 0 disables retries), `base_delay`
|
|
121
|
+
# and `max_delay` (positive finite seconds), and `jitter` (`:full` or
|
|
122
|
+
# `:none`). Defaults are 3, 0.5, 2.0, and `:full`, respectively.
|
|
116
123
|
#
|
|
117
124
|
# @raise [OAuthDependencyError] if the openid_connect gem is not available
|
|
118
125
|
# @raise [OAuthAuthenticationError] if authentication fails
|
|
126
|
+
# @raise [ArgumentError] if retry settings are invalid
|
|
119
127
|
#
|
|
120
128
|
# @example
|
|
121
129
|
# oauth = OAuth2ClientCredentials.new(
|
|
@@ -123,13 +131,20 @@ module Kessel
|
|
|
123
131
|
# client_secret: 'secret',
|
|
124
132
|
# token_endpoint: 'https://my-domain/auth/realms/my-realm/protocol/openid-connect/token'
|
|
125
133
|
# )
|
|
126
|
-
def initialize(client_id:, client_secret:, token_endpoint
|
|
134
|
+
def initialize(client_id:, client_secret:, token_endpoint:, **options)
|
|
135
|
+
validate_retry_options!(options)
|
|
136
|
+
retry_config = normalize_retry_config(options.fetch(:retry, {}))
|
|
127
137
|
check_dependencies!
|
|
128
138
|
|
|
129
139
|
@client_id = client_id
|
|
130
140
|
@client_secret = client_secret
|
|
131
141
|
@token_endpoint = token_endpoint
|
|
142
|
+
@retry_config = retry_config
|
|
132
143
|
@token_mutex = Mutex.new
|
|
144
|
+
@generation_state_mutex = Mutex.new
|
|
145
|
+
@generation_users = Hash.new(0)
|
|
146
|
+
@generation_failures = {}
|
|
147
|
+
@cached_token_generation = nil
|
|
133
148
|
@generation = 0
|
|
134
149
|
end
|
|
135
150
|
|
|
@@ -147,23 +162,80 @@ module Kessel
|
|
|
147
162
|
def get_token(force_refresh: false)
|
|
148
163
|
return @cached_token if !force_refresh && token_valid?
|
|
149
164
|
|
|
150
|
-
generation =
|
|
165
|
+
generation = register_generation
|
|
151
166
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
167
|
+
begin
|
|
168
|
+
@token_mutex.synchronize do
|
|
169
|
+
failure = generation_failure(generation)
|
|
155
170
|
|
|
156
|
-
|
|
157
|
-
|
|
171
|
+
# A later successful generation may have recovered after this caller's failure.
|
|
172
|
+
return @cached_token if newer_cached_token?(generation)
|
|
173
|
+
raise failure if failure
|
|
174
|
+
|
|
175
|
+
begin
|
|
176
|
+
token = refresh
|
|
177
|
+
rescue StandardError => e
|
|
178
|
+
record_failure_and_advance(generation, e)
|
|
179
|
+
raise
|
|
180
|
+
end
|
|
181
|
+
cache_token_and_advance(token)
|
|
158
182
|
|
|
159
|
-
|
|
183
|
+
@cached_token
|
|
184
|
+
end
|
|
160
185
|
rescue StandardError => e
|
|
161
|
-
raise OAuthAuthenticationError, "Failed to obtain client credentials token: #{e.message}"
|
|
186
|
+
raise OAuthAuthenticationError, "Failed to obtain client credentials token: #{e.message}", cause: e
|
|
187
|
+
ensure
|
|
188
|
+
unregister_generation(generation)
|
|
162
189
|
end
|
|
163
190
|
end
|
|
164
191
|
|
|
165
192
|
private
|
|
166
193
|
|
|
194
|
+
def register_generation
|
|
195
|
+
@generation_state_mutex.synchronize do
|
|
196
|
+
generation = @generation
|
|
197
|
+
@generation_users[generation] += 1
|
|
198
|
+
generation
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def generation_failure(generation)
|
|
203
|
+
@generation_state_mutex.synchronize { @generation_failures[generation] }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def record_failure_and_advance(generation, error)
|
|
207
|
+
@generation_state_mutex.synchronize do
|
|
208
|
+
@generation_failures[generation] = error
|
|
209
|
+
@generation += 1
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def advance_generation
|
|
214
|
+
@generation_state_mutex.synchronize { @generation += 1 }
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def cache_token_and_advance(token)
|
|
218
|
+
@generation_state_mutex.synchronize do
|
|
219
|
+
@cached_token = token
|
|
220
|
+
@generation += 1
|
|
221
|
+
@cached_token_generation = @generation
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def newer_cached_token?(generation)
|
|
226
|
+
!!(@cached_token_generation && @cached_token_generation > generation && token_valid?)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def unregister_generation(generation)
|
|
230
|
+
@generation_state_mutex.synchronize do
|
|
231
|
+
@generation_users[generation] -= 1
|
|
232
|
+
next unless @generation_users[generation].zero?
|
|
233
|
+
|
|
234
|
+
@generation_users.delete(generation)
|
|
235
|
+
@generation_failures.delete(generation)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
167
239
|
def refresh
|
|
168
240
|
client = create_oidc_client
|
|
169
241
|
|
|
@@ -173,13 +245,117 @@ module Kessel
|
|
|
173
245
|
client_secret: @client_secret
|
|
174
246
|
}
|
|
175
247
|
|
|
176
|
-
token_data = client
|
|
248
|
+
token_data = access_token_with_retries(client, request_params)
|
|
177
249
|
RefreshTokenResponse.new(
|
|
178
250
|
access_token: token_data.access_token,
|
|
179
251
|
expires_at: Time.now + (token_data.expires_in || DEFAULT_EXPIRES_IN)
|
|
180
252
|
).freeze
|
|
181
253
|
end
|
|
182
254
|
|
|
255
|
+
def access_token_with_retries(client, request_params)
|
|
256
|
+
retry_index = 0
|
|
257
|
+
|
|
258
|
+
begin
|
|
259
|
+
client.access_token!(request_params)
|
|
260
|
+
rescue StandardError => e
|
|
261
|
+
raise unless retryable_token_error?(e) && retry_index < @retry_config[:max_retries]
|
|
262
|
+
|
|
263
|
+
sleep(retry_delay(retry_index))
|
|
264
|
+
retry_index += 1
|
|
265
|
+
retry
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def validate_retry_options!(options)
|
|
270
|
+
unknown_keys = options.keys - [:retry]
|
|
271
|
+
return if unknown_keys.empty?
|
|
272
|
+
|
|
273
|
+
raise ArgumentError, "unknown keyword: #{unknown_keys.first.inspect}"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def normalize_retry_config(retry_options)
|
|
277
|
+
unless retry_options.is_a?(Hash) && retry_options.keys.all?(Symbol)
|
|
278
|
+
raise ArgumentError, 'retry must be a symbol-keyed Hash'
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
unknown_keys = retry_options.keys - %i[max_retries base_delay max_delay jitter]
|
|
282
|
+
raise ArgumentError, "unknown retry option: #{unknown_keys.first.inspect}" unless unknown_keys.empty?
|
|
283
|
+
|
|
284
|
+
config = {
|
|
285
|
+
max_retries: 3,
|
|
286
|
+
base_delay: 0.5,
|
|
287
|
+
max_delay: 2.0,
|
|
288
|
+
jitter: :full
|
|
289
|
+
}.merge(retry_options)
|
|
290
|
+
validate_retry_config_values!(config)
|
|
291
|
+
config.freeze
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def validate_retry_config_values!(config)
|
|
295
|
+
unless config[:max_retries].is_a?(Integer) && config[:max_retries] >= 0
|
|
296
|
+
raise ArgumentError, 'retry max_retries must be a non-negative Integer'
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
%i[base_delay max_delay].each do |key|
|
|
300
|
+
next if valid_delay?(config[key])
|
|
301
|
+
|
|
302
|
+
raise ArgumentError, "retry #{key} must be a positive finite Integer or Float"
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
return if %i[full none].include?(config[:jitter])
|
|
306
|
+
|
|
307
|
+
raise ArgumentError, 'retry jitter must be :full or :none'
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def valid_delay?(value)
|
|
311
|
+
(value.is_a?(Integer) || value.is_a?(Float)) && value.positive? &&
|
|
312
|
+
(!value.is_a?(Float) || value.finite?)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def retry_delay(retry_index)
|
|
316
|
+
cap = [@retry_config[:max_delay], @retry_config[:base_delay] * (2**retry_index)].min
|
|
317
|
+
return cap if @retry_config[:jitter] == :none
|
|
318
|
+
|
|
319
|
+
rand(cap.to_f)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def retryable_token_error?(error)
|
|
323
|
+
retryable_rack_oauth_error?(error) || retryable_transient_error?(error)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def retryable_rack_oauth_error?(error)
|
|
327
|
+
return false unless defined?(::Rack::OAuth2::Client::Error)
|
|
328
|
+
return false unless error.is_a?(::Rack::OAuth2::Client::Error)
|
|
329
|
+
|
|
330
|
+
status = error.status
|
|
331
|
+
status == 429 || (status.is_a?(Integer) && status.between?(500, 599))
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def retryable_transient_error?(error)
|
|
335
|
+
transient_error_classes.any? { |error_class| error.is_a?(error_class) }
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def transient_error_classes
|
|
339
|
+
%w[
|
|
340
|
+
Faraday::ConnectionFailed
|
|
341
|
+
Faraday::TimeoutError
|
|
342
|
+
Timeout::Error
|
|
343
|
+
SocketError
|
|
344
|
+
EOFError
|
|
345
|
+
Errno::ECONNREFUSED
|
|
346
|
+
Errno::ECONNRESET
|
|
347
|
+
Errno::ETIMEDOUT
|
|
348
|
+
Errno::EHOSTUNREACH
|
|
349
|
+
Errno::ENETUNREACH
|
|
350
|
+
].map { |name| optional_error_class(name) }.compact
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def optional_error_class(name)
|
|
354
|
+
Object.const_get(name, false)
|
|
355
|
+
rescue NameError
|
|
356
|
+
nil
|
|
357
|
+
end
|
|
358
|
+
|
|
183
359
|
# Checks if we have a valid cached token.
|
|
184
360
|
#
|
|
185
361
|
# @return [Boolean] true if token exists and not expired
|
|
@@ -206,5 +382,6 @@ module Kessel
|
|
|
206
382
|
)
|
|
207
383
|
end
|
|
208
384
|
end
|
|
385
|
+
# rubocop:enable Metrics/ClassLength
|
|
209
386
|
end
|
|
210
387
|
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Inventory Module Guidelines
|
|
2
|
+
|
|
3
|
+
This directory contains the Kessel Inventory client module -- the `ClientBuilder` fluent API, version-specific service wiring, and generated protobuf/gRPC stubs. The `ClientBuilder` base class is defined in `../inventory.rb` (the `Kessel::Inventory` module), and each version file in this directory creates service-specific builders from it.
|
|
4
|
+
|
|
5
|
+
For repo-wide conventions (testing framework, RBS signatures, file headers, error patterns), see AGENTS.md at the repository root.
|
|
6
|
+
|
|
7
|
+
## File Organization
|
|
8
|
+
|
|
9
|
+
### Hand-written files (editable)
|
|
10
|
+
|
|
11
|
+
- `../inventory.rb` -- `Kessel::Inventory` module with `client_builder_for_stub` factory and `ClientBuilder` base class
|
|
12
|
+
- `v1.rb` -- Service wiring for `KesselInventoryHealthService::ClientBuilder` (health checks)
|
|
13
|
+
- `v1beta1.rb` -- Service wiring for V1beta1 typed resource/relationship services (deprecated)
|
|
14
|
+
- `v1beta2.rb` -- Service wiring for `KesselInventoryService::ClientBuilder` (current API)
|
|
15
|
+
|
|
16
|
+
### Generated files (never edit)
|
|
17
|
+
|
|
18
|
+
Everything under `v1/`, `v1beta1/`, and `v1beta2/` subdirectories (`*_pb.rb`, `*_services_pb.rb`) is generated by `buf generate` and overwritten automatically every 6 hours via CI. Changes to these files must go upstream in the `.proto` definitions at `buf.build/project-kessel/inventory-api`.
|
|
19
|
+
|
|
20
|
+
## ClientBuilder Pattern
|
|
21
|
+
|
|
22
|
+
### Factory Function
|
|
23
|
+
|
|
24
|
+
`client_builder_for_stub(stub_class)` dynamically creates a new `Class` inheriting from `ClientBuilder` with `@stub_class` set. Each service module creates its builder as a constant:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
ClientBuilder = ::Kessel::Inventory.client_builder_for_stub(Stub)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Do not call `client_builder_for_stub` repeatedly at runtime -- the result is cached as a constant.
|
|
31
|
+
|
|
32
|
+
### Fluent API
|
|
33
|
+
|
|
34
|
+
`ClientBuilder` uses method chaining -- all authentication methods return `self`:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# Insecure (local dev only)
|
|
38
|
+
client = KesselInventoryService::ClientBuilder.new(target).insecure.build
|
|
39
|
+
|
|
40
|
+
# OAuth2 authenticated
|
|
41
|
+
client = KesselInventoryService::ClientBuilder.new(target)
|
|
42
|
+
.oauth2_client_authenticated(oauth2_client_credentials: creds)
|
|
43
|
+
.build
|
|
44
|
+
|
|
45
|
+
# Custom credentials
|
|
46
|
+
client = KesselInventoryService::ClientBuilder.new(target)
|
|
47
|
+
.authenticated(call_credentials: call_creds, channel_credentials: chan_creds)
|
|
48
|
+
.build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Credential Defaults and Validation
|
|
52
|
+
|
|
53
|
+
- When no channel credentials are set, `build` defaults to `GRPC::Core::ChannelCredentials.new` (TLS). This secure-by-default behavior must be preserved.
|
|
54
|
+
- `insecure` sets a sentinel (`:this_channel_is_insecure`) that prevents composing with call credentials.
|
|
55
|
+
- `validate_credentials` runs immediately on each auth method call, not deferred to `build`. This ensures invalid configurations fail at configuration time. Do not move validation to `build`.
|
|
56
|
+
- `build` calls `credentials.compose(@call_credentials)` when both channel and call credentials are present.
|
|
57
|
+
|
|
58
|
+
### Build Output
|
|
59
|
+
|
|
60
|
+
`build` returns a single gRPC stub instance (not a tuple). The underlying gRPC channel manages its own HTTP/2 connection pool. Build once at application startup and reuse -- do not create a new stub per request.
|
|
61
|
+
|
|
62
|
+
## Service Wiring Pattern
|
|
63
|
+
|
|
64
|
+
Every gRPC service module must follow this exact pattern:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
# frozen_string_literal: true
|
|
68
|
+
|
|
69
|
+
require 'kessel/inventory'
|
|
70
|
+
require 'kessel/inventory/v1betaN/some_service_services_pb'
|
|
71
|
+
|
|
72
|
+
include Kessel::Inventory
|
|
73
|
+
|
|
74
|
+
module Kessel
|
|
75
|
+
module Inventory
|
|
76
|
+
module V1betaN
|
|
77
|
+
module KesselSomeService
|
|
78
|
+
ClientBuilder = ::Kessel::Inventory.client_builder_for_stub(Stub)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The `include Kessel::Inventory` at the top level is required for `client_builder_for_stub` to resolve. The `Stub` constant comes from the generated `*_services_pb.rb` file.
|
|
86
|
+
|
|
87
|
+
When adding a new service, also add the `ClientBuilder` constant declaration in `sig/kessel/inventory.rbs`.
|
|
88
|
+
|
|
89
|
+
## Version Conventions
|
|
90
|
+
|
|
91
|
+
| Directory | Status | Service |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `v1/` | Active | `KesselInventoryHealthService` (health checks only) |
|
|
94
|
+
| `v1beta2/` | Active (current) | `KesselInventoryService` (unified inventory API) |
|
|
95
|
+
| `v1beta1/` | Deprecated | Typed K8s resources/relationships. Do not extend. |
|
|
96
|
+
|
|
97
|
+
All new features target `v1beta2`. The unified `KesselInventoryService` uses `ReportResource`/`DeleteResource` instead of resource-specific services.
|
|
98
|
+
|
|
99
|
+
Protobuf package `kessel.inventory.v1beta2` maps to Ruby module `Kessel::Inventory::V1beta2` -- note lowercase `beta` (not `V1Beta2`). This follows the protobuf-to-Ruby naming convention from `buf generate`.
|
|
100
|
+
|
|
101
|
+
## V1beta2 API Patterns
|
|
102
|
+
|
|
103
|
+
### Check Operations
|
|
104
|
+
|
|
105
|
+
Check operations use a triplet: `object` (ResourceReference), `relation` (string), `subject` (SubjectReference). Use `Check` for read-path authorization and `CheckForUpdate` for strongly consistent pre-mutation checks.
|
|
106
|
+
|
|
107
|
+
`CheckSelf` omits the subject (inferred from auth context).
|
|
108
|
+
|
|
109
|
+
### Bulk Operations
|
|
110
|
+
|
|
111
|
+
`CheckBulk` and `CheckForUpdateBulk` accept 1--1000 items per RPC. Responses use a `pairs` field where each pair contains either `item` (success) or `error` (`Google::Rpc::Status`). Always check per-item errors -- the RPC can succeed while individual items fail.
|
|
112
|
+
|
|
113
|
+
### Resource Reporting
|
|
114
|
+
|
|
115
|
+
`ReportResourceRequest` requires `type`, `reporter_type`, `reporter_instance_id`, `representations`, and `write_visibility`. Convert Ruby hashes to protobuf `Struct` via `Google::Protobuf::Struct.decode_json(hash.to_json)`.
|
|
116
|
+
|
|
117
|
+
### Consistency Controls
|
|
118
|
+
|
|
119
|
+
The `Consistency` message supports `minimize_latency` (default, may be stale), `at_least_as_fresh` (token from prior write), and `at_least_as_acknowledged` (waits for all acknowledged writes). `WriteVisibility` on report requests defaults to `WRITE_VISIBILITY_UNSPECIFIED` -- set `IMMEDIATE` only when the caller will immediately `Check` the newly reported resource.
|
|
120
|
+
|
|
121
|
+
### Streaming RPCs
|
|
122
|
+
|
|
123
|
+
`StreamedListObjects` and `StreamedListSubjects` use server-side streaming with `RequestPagination` (limit + continuation_token). Iterate with `.each` for constant memory.
|
|
124
|
+
|
|
125
|
+
### Allowed Enum
|
|
126
|
+
|
|
127
|
+
V1beta2 uses a shared top-level `Allowed` enum (`ALLOWED_TRUE`, `ALLOWED_FALSE`). V1beta1 uses per-response nested enums (`CheckResponse::Allowed`).
|
|
128
|
+
|
|
129
|
+
### HTTP API Routes
|
|
130
|
+
|
|
131
|
+
- V1: `/api/kessel/v1/{method}`
|
|
132
|
+
- V1beta1: `/api/inventory/v1beta1/{domain}/{method}`
|
|
133
|
+
- V1beta2: `/api/kessel/v1beta2/{method}`
|
|
134
|
+
- RBAC V2 REST: `/api/rbac/v2/workspaces/`
|
|
135
|
+
|
|
136
|
+
## Authentication Integration
|
|
137
|
+
|
|
138
|
+
Authentication feeds into the `ClientBuilder` through two paths:
|
|
139
|
+
|
|
140
|
+
- **gRPC path**: `Kessel::GRPC#oauth2_call_credentials(auth)` wraps an `OAuth2ClientCredentials` instance as `GRPC::Core::CallCredentials`. The proc is invoked on every RPC call, hitting the token cache fast path when the token is valid. Do not add blocking operations inside this proc.
|
|
141
|
+
- **HTTP path**: `Kessel::Auth#oauth2_auth_request(oauth)` returns an `OAuth2AuthRequest` implementing the `AuthRequest` interface. The `configure_request(request)` method sets the `authorization` header on `Net::HTTPRequest` objects.
|
|
142
|
+
|
|
143
|
+
The `AuthRequest` module defines an interface contract -- any class including it must implement `configure_request` or callers get `NotImplementedError`. New auth mechanisms must implement this interface and have matching RBS signatures in `sig/kessel/auth.rbs`.
|
|
144
|
+
|
|
145
|
+
### Token Caching
|
|
146
|
+
|
|
147
|
+
`OAuth2ClientCredentials` uses double-checked locking with a `@generation` counter to coalesce concurrent refresh requests into a single SSO call. Cached tokens are frozen with `.freeze` for concurrent read safety. Use `force_refresh: true` only after receiving an explicit 401/UNAUTHENTICATED error, never preemptively.
|
|
148
|
+
|
|
149
|
+
## Console Helper
|
|
150
|
+
|
|
151
|
+
`lib/kessel/console.rb` provides `principal_from_rh_identity` and `principal_from_rh_identity_header` for converting Red Hat identity headers into `SubjectReference` objects. It depends on `Kessel::RBAC::V2` factory helpers (specifically `principal_subject`).
|
|
152
|
+
|
|
153
|
+
The `IDENTITY_TYPE_FIELDS` constant maps identity types to their JSON field names (`'User' => 'user'`, `'ServiceAccount' => 'service_account'`). When adding a new identity type, add it to this hash.
|
|
154
|
+
|
|
155
|
+
## Adding a New Service Version
|
|
156
|
+
|
|
157
|
+
1. Run `buf generate` to produce stubs in `lib/kessel/inventory/<version>/`.
|
|
158
|
+
2. Create `lib/kessel/inventory/<version>.rb` following the service wiring pattern above.
|
|
159
|
+
3. Add the `ClientBuilder` constant declaration to `sig/kessel/inventory.rbs`.
|
|
160
|
+
4. Add a corresponding example in `examples/`.
|
|
161
|
+
5. Run `bundle exec rspec` and `steep check`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/acquire_lock_request.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
descriptor_data = "\n3kessel/inventory/v1beta2/acquire_lock_request.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\"6\n\x12\x41\x63quireLockRequest\x12 \n\x07lock_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x06lockIdBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
11
|
+
|
|
12
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
|
14
|
+
|
|
15
|
+
module Kessel
|
|
16
|
+
module Inventory
|
|
17
|
+
module V1beta2
|
|
18
|
+
AcquireLockRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.AcquireLockRequest").msgclass
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/acquire_lock_response.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
descriptor_data = "\n4kessel/inventory/v1beta2/acquire_lock_response.proto\x12\x18kessel.inventory.v1beta2\"4\n\x13\x41\x63quireLockResponse\x12\x1d\n\nlock_token\x18\x01 \x01(\tR\tlockTokenBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
9
|
+
|
|
10
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
|
12
|
+
|
|
13
|
+
module Kessel
|
|
14
|
+
module Inventory
|
|
15
|
+
module V1beta2
|
|
16
|
+
AcquireLockResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.AcquireLockResponse").msgclass
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/create_tuples_request.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'kessel/inventory/v1beta2/relationship_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/relation_fencing_check_pb'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
descriptor_data = "\n4kessel/inventory/v1beta2/create_tuples_request.proto\x12\x18kessel.inventory.v1beta2\x1a+kessel/inventory/v1beta2/relationship.proto\x1a\x35kessel/inventory/v1beta2/relation_fencing_check.proto\"\xd9\x01\n\x13\x43reateTuplesRequest\x12\x16\n\x06upsert\x18\x01 \x01(\x08R\x06upsert\x12>\n\x06tuples\x18\x02 \x03(\x0b\x32&.kessel.inventory.v1beta2.RelationshipR\x06tuples\x12X\n\rfencing_check\x18\x03 \x01(\x0b\x32..kessel.inventory.v1beta2.RelationFencingCheckH\x00R\x0c\x66\x65ncingCheck\x88\x01\x01\x42\x10\n\x0e_fencing_checkBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
12
|
+
|
|
13
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
14
|
+
pool.add_serialized_file(descriptor_data)
|
|
15
|
+
|
|
16
|
+
module Kessel
|
|
17
|
+
module Inventory
|
|
18
|
+
module V1beta2
|
|
19
|
+
CreateTuplesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.CreateTuplesRequest").msgclass
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/create_tuples_response.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'kessel/inventory/v1beta2/consistency_token_pb'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
descriptor_data = "\n5kessel/inventory/v1beta2/create_tuples_response.proto\x12\x18kessel.inventory.v1beta2\x1a\x30kessel/inventory/v1beta2/consistency_token.proto\"o\n\x14\x43reateTuplesResponse\x12W\n\x11\x63onsistency_token\x18\x01 \x01(\x0b\x32*.kessel.inventory.v1beta2.ConsistencyTokenR\x10\x63onsistencyTokenBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
11
|
+
|
|
12
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
|
14
|
+
|
|
15
|
+
module Kessel
|
|
16
|
+
module Inventory
|
|
17
|
+
module V1beta2
|
|
18
|
+
CreateTuplesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.CreateTuplesResponse").msgclass
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/delete_tuples_request.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/relation_tuple_filter_pb'
|
|
9
|
+
require 'kessel/inventory/v1beta2/relation_fencing_check_pb'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
descriptor_data = "\n4kessel/inventory/v1beta2/delete_tuples_request.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\x1a\x34kessel/inventory/v1beta2/relation_tuple_filter.proto\x1a\x35kessel/inventory/v1beta2/relation_fencing_check.proto\"\xd0\x01\n\x13\x44\x65leteTuplesRequest\x12M\n\x06\x66ilter\x18\x01 \x01(\x0b\x32-.kessel.inventory.v1beta2.RelationTupleFilterB\x06\xbaH\x03\xc8\x01\x01R\x06\x66ilter\x12X\n\rfencing_check\x18\x02 \x01(\x0b\x32..kessel.inventory.v1beta2.RelationFencingCheckH\x00R\x0c\x66\x65ncingCheck\x88\x01\x01\x42\x10\n\x0e_fencing_checkBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
13
|
+
|
|
14
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
15
|
+
pool.add_serialized_file(descriptor_data)
|
|
16
|
+
|
|
17
|
+
module Kessel
|
|
18
|
+
module Inventory
|
|
19
|
+
module V1beta2
|
|
20
|
+
DeleteTuplesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.DeleteTuplesRequest").msgclass
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/delete_tuples_response.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'kessel/inventory/v1beta2/consistency_token_pb'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
descriptor_data = "\n5kessel/inventory/v1beta2/delete_tuples_response.proto\x12\x18kessel.inventory.v1beta2\x1a\x30kessel/inventory/v1beta2/consistency_token.proto\"o\n\x14\x44\x65leteTuplesResponse\x12W\n\x11\x63onsistency_token\x18\x01 \x01(\x0b\x32*.kessel.inventory.v1beta2.ConsistencyTokenR\x10\x63onsistencyTokenBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
11
|
+
|
|
12
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
|
14
|
+
|
|
15
|
+
module Kessel
|
|
16
|
+
module Inventory
|
|
17
|
+
module V1beta2
|
|
18
|
+
DeleteTuplesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.DeleteTuplesResponse").msgclass
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/read_tuples_request.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/relation_tuple_filter_pb'
|
|
9
|
+
require 'kessel/inventory/v1beta2/request_pagination_pb'
|
|
10
|
+
require 'kessel/inventory/v1beta2/consistency_pb'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
descriptor_data = "\n2kessel/inventory/v1beta2/read_tuples_request.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\x1a\x34kessel/inventory/v1beta2/relation_tuple_filter.proto\x1a\x31kessel/inventory/v1beta2/request_pagination.proto\x1a*kessel/inventory/v1beta2/consistency.proto\"\xa1\x02\n\x11ReadTuplesRequest\x12M\n\x06\x66ilter\x18\x01 \x01(\x0b\x32-.kessel.inventory.v1beta2.RelationTupleFilterB\x06\xbaH\x03\xc8\x01\x01R\x06\x66ilter\x12P\n\npagination\x18\x02 \x01(\x0b\x32+.kessel.inventory.v1beta2.RequestPaginationH\x00R\npagination\x88\x01\x01\x12L\n\x0b\x63onsistency\x18\x03 \x01(\x0b\x32%.kessel.inventory.v1beta2.ConsistencyH\x01R\x0b\x63onsistency\x88\x01\x01\x42\r\n\x0b_paginationB\x0e\n\x0c_consistencyBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
14
|
+
|
|
15
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
16
|
+
pool.add_serialized_file(descriptor_data)
|
|
17
|
+
|
|
18
|
+
module Kessel
|
|
19
|
+
module Inventory
|
|
20
|
+
module V1beta2
|
|
21
|
+
ReadTuplesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.ReadTuplesRequest").msgclass
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/read_tuples_response.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'kessel/inventory/v1beta2/relationship_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/response_pagination_pb'
|
|
9
|
+
require 'kessel/inventory/v1beta2/consistency_token_pb'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
descriptor_data = "\n3kessel/inventory/v1beta2/read_tuples_response.proto\x12\x18kessel.inventory.v1beta2\x1a+kessel/inventory/v1beta2/relationship.proto\x1a\x32kessel/inventory/v1beta2/response_pagination.proto\x1a\x30kessel/inventory/v1beta2/consistency_token.proto\"\xf9\x01\n\x12ReadTuplesResponse\x12<\n\x05tuple\x18\x01 \x01(\x0b\x32&.kessel.inventory.v1beta2.RelationshipR\x05tuple\x12L\n\npagination\x18\x02 \x01(\x0b\x32,.kessel.inventory.v1beta2.ResponsePaginationR\npagination\x12W\n\x11\x63onsistency_token\x18\x03 \x01(\x0b\x32*.kessel.inventory.v1beta2.ConsistencyTokenR\x10\x63onsistencyTokenBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
13
|
+
|
|
14
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
15
|
+
pool.add_serialized_file(descriptor_data)
|
|
16
|
+
|
|
17
|
+
module Kessel
|
|
18
|
+
module Inventory
|
|
19
|
+
module V1beta2
|
|
20
|
+
ReadTuplesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.ReadTuplesResponse").msgclass
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relation_fencing_check.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
descriptor_data = "\n5kessel/inventory/v1beta2/relation_fencing_check.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\"`\n\x14RelationFencingCheck\x12 \n\x07lock_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x06lockId\x12&\n\nlock_token\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\tlockTokenBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
11
|
+
|
|
12
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
|
14
|
+
|
|
15
|
+
module Kessel
|
|
16
|
+
module Inventory
|
|
17
|
+
module V1beta2
|
|
18
|
+
RelationFencingCheck = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RelationFencingCheck").msgclass
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relation_object_reference.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/relation_object_type_pb'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
descriptor_data = "\n8kessel/inventory/v1beta2/relation_object_reference.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\x1a\x33kessel/inventory/v1beta2/relation_object_type.proto\"|\n\x17RelationObjectReference\x12H\n\x04type\x18\x01 \x01(\x0b\x32,.kessel.inventory.v1beta2.RelationObjectTypeB\x06\xbaH\x03\xc8\x01\x01R\x04type\x12\x17\n\x02id\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x02idBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
12
|
+
|
|
13
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
14
|
+
pool.add_serialized_file(descriptor_data)
|
|
15
|
+
|
|
16
|
+
module Kessel
|
|
17
|
+
module Inventory
|
|
18
|
+
module V1beta2
|
|
19
|
+
RelationObjectReference = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RelationObjectReference").msgclass
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relation_object_type.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
descriptor_data = "\n3kessel/inventory/v1beta2/relation_object_type.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\"X\n\x12RelationObjectType\x12%\n\tnamespace\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\tnamespace\x12\x1b\n\x04name\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x04nameBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
11
|
+
|
|
12
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
|
14
|
+
|
|
15
|
+
module Kessel
|
|
16
|
+
module Inventory
|
|
17
|
+
module V1beta2
|
|
18
|
+
RelationObjectType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RelationObjectType").msgclass
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relation_subject_filter.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
descriptor_data = "\n6kessel/inventory/v1beta2/relation_subject_filter.proto\x12\x18kessel.inventory.v1beta2\"\xf9\x01\n\x15RelationSubjectFilter\x12\x30\n\x11subject_namespace\x18\x01 \x01(\tH\x00R\x10subjectNamespace\x88\x01\x01\x12&\n\x0csubject_type\x18\x02 \x01(\tH\x01R\x0bsubjectType\x88\x01\x01\x12\"\n\nsubject_id\x18\x03 \x01(\tH\x02R\tsubjectId\x88\x01\x01\x12\x1f\n\x08relation\x18\x04 \x01(\tH\x03R\x08relation\x88\x01\x01\x42\x14\n\x12_subject_namespaceB\x0f\n\r_subject_typeB\r\n\x0b_subject_idB\x0b\n\t_relationBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
9
|
+
|
|
10
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
|
12
|
+
|
|
13
|
+
module Kessel
|
|
14
|
+
module Inventory
|
|
15
|
+
module V1beta2
|
|
16
|
+
RelationSubjectFilter = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RelationSubjectFilter").msgclass
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relation_subject_reference.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/relation_object_reference_pb'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
descriptor_data = "\n9kessel/inventory/v1beta2/relation_subject_reference.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\x1a\x38kessel/inventory/v1beta2/relation_object_reference.proto\"\x9d\x01\n\x18RelationSubjectReference\x12\x1f\n\x08relation\x18\x01 \x01(\tH\x00R\x08relation\x88\x01\x01\x12S\n\x07subject\x18\x02 \x01(\x0b\x32\x31.kessel.inventory.v1beta2.RelationObjectReferenceB\x06\xbaH\x03\xc8\x01\x01R\x07subjectB\x0b\n\t_relationBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
12
|
+
|
|
13
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
14
|
+
pool.add_serialized_file(descriptor_data)
|
|
15
|
+
|
|
16
|
+
module Kessel
|
|
17
|
+
module Inventory
|
|
18
|
+
module V1beta2
|
|
19
|
+
RelationSubjectReference = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RelationSubjectReference").msgclass
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relation_tuple_filter.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'kessel/inventory/v1beta2/relation_subject_filter_pb'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
descriptor_data = "\n4kessel/inventory/v1beta2/relation_tuple_filter.proto\x12\x18kessel.inventory.v1beta2\x1a\x36kessel/inventory/v1beta2/relation_subject_filter.proto\"\xf0\x02\n\x13RelationTupleFilter\x12\x32\n\x12resource_namespace\x18\x01 \x01(\tH\x00R\x11resourceNamespace\x88\x01\x01\x12(\n\rresource_type\x18\x02 \x01(\tH\x01R\x0cresourceType\x88\x01\x01\x12$\n\x0bresource_id\x18\x03 \x01(\tH\x02R\nresourceId\x88\x01\x01\x12\x1f\n\x08relation\x18\x04 \x01(\tH\x03R\x08relation\x88\x01\x01\x12[\n\x0esubject_filter\x18\x05 \x01(\x0b\x32/.kessel.inventory.v1beta2.RelationSubjectFilterH\x04R\rsubjectFilter\x88\x01\x01\x42\x15\n\x13_resource_namespaceB\x10\n\x0e_resource_typeB\x0e\n\x0c_resource_idB\x0b\n\t_relationB\x11\n\x0f_subject_filterBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
11
|
+
|
|
12
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
13
|
+
pool.add_serialized_file(descriptor_data)
|
|
14
|
+
|
|
15
|
+
module Kessel
|
|
16
|
+
module Inventory
|
|
17
|
+
module V1beta2
|
|
18
|
+
RelationTupleFilter = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RelationTupleFilter").msgclass
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/relationship.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'buf/validate/validate_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/relation_object_reference_pb'
|
|
9
|
+
require 'kessel/inventory/v1beta2/relation_subject_reference_pb'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
descriptor_data = "\n+kessel/inventory/v1beta2/relationship.proto\x12\x18kessel.inventory.v1beta2\x1a\x1b\x62uf/validate/validate.proto\x1a\x38kessel/inventory/v1beta2/relation_object_reference.proto\x1a\x39kessel/inventory/v1beta2/relation_subject_reference.proto\"\xe0\x01\n\x0cRelationship\x12U\n\x08resource\x18\x01 \x01(\x0b\x32\x31.kessel.inventory.v1beta2.RelationObjectReferenceB\x06\xbaH\x03\xc8\x01\x01R\x08resource\x12#\n\x08relation\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x08relation\x12T\n\x07subject\x18\x03 \x01(\x0b\x32\x32.kessel.inventory.v1beta2.RelationSubjectReferenceB\x06\xbaH\x03\xc8\x01\x01R\x07subjectBr\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
13
|
+
|
|
14
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
15
|
+
pool.add_serialized_file(descriptor_data)
|
|
16
|
+
|
|
17
|
+
module Kessel
|
|
18
|
+
module Inventory
|
|
19
|
+
module V1beta2
|
|
20
|
+
Relationship = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.Relationship").msgclass
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kessel/inventory/v1beta2/tuple_service.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
require 'kessel/inventory/v1beta2/create_tuples_request_pb'
|
|
8
|
+
require 'kessel/inventory/v1beta2/create_tuples_response_pb'
|
|
9
|
+
require 'kessel/inventory/v1beta2/read_tuples_request_pb'
|
|
10
|
+
require 'kessel/inventory/v1beta2/read_tuples_response_pb'
|
|
11
|
+
require 'kessel/inventory/v1beta2/delete_tuples_request_pb'
|
|
12
|
+
require 'kessel/inventory/v1beta2/delete_tuples_response_pb'
|
|
13
|
+
require 'kessel/inventory/v1beta2/acquire_lock_request_pb'
|
|
14
|
+
require 'kessel/inventory/v1beta2/acquire_lock_response_pb'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
descriptor_data = "\n,kessel/inventory/v1beta2/tuple_service.proto\x12\x18kessel.inventory.v1beta2\x1a\x34kessel/inventory/v1beta2/create_tuples_request.proto\x1a\x35kessel/inventory/v1beta2/create_tuples_response.proto\x1a\x32kessel/inventory/v1beta2/read_tuples_request.proto\x1a\x33kessel/inventory/v1beta2/read_tuples_response.proto\x1a\x34kessel/inventory/v1beta2/delete_tuples_request.proto\x1a\x35kessel/inventory/v1beta2/delete_tuples_response.proto\x1a\x33kessel/inventory/v1beta2/acquire_lock_request.proto\x1a\x34kessel/inventory/v1beta2/acquire_lock_response.proto2\xdd\x03\n\x12KesselTupleService\x12r\n\x0c\x43reateTuples\x12-.kessel.inventory.v1beta2.CreateTuplesRequest\x1a..kessel.inventory.v1beta2.CreateTuplesResponse\"\x03\x88\x02\x01\x12r\n\x0c\x44\x65leteTuples\x12-.kessel.inventory.v1beta2.DeleteTuplesRequest\x1a..kessel.inventory.v1beta2.DeleteTuplesResponse\"\x03\x88\x02\x01\x12n\n\nReadTuples\x12+.kessel.inventory.v1beta2.ReadTuplesRequest\x1a,.kessel.inventory.v1beta2.ReadTuplesResponse\"\x03\x88\x02\x01\x30\x01\x12o\n\x0b\x41\x63quireLock\x12,.kessel.inventory.v1beta2.AcquireLockRequest\x1a-.kessel.inventory.v1beta2.AcquireLockResponse\"\x03\x88\x02\x01\x42r\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
|
|
18
|
+
|
|
19
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
20
|
+
pool.add_serialized_file(descriptor_data)
|
|
21
|
+
|
|
22
|
+
module Kessel
|
|
23
|
+
module Inventory
|
|
24
|
+
module V1beta2
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
2
|
+
# Source: kessel/inventory/v1beta2/tuple_service.proto for package 'kessel.inventory.v1beta2'
|
|
3
|
+
|
|
4
|
+
require 'grpc'
|
|
5
|
+
require 'kessel/inventory/v1beta2/tuple_service_pb'
|
|
6
|
+
|
|
7
|
+
module Kessel
|
|
8
|
+
module Inventory
|
|
9
|
+
module V1beta2
|
|
10
|
+
module KesselTupleService
|
|
11
|
+
# DEPRECATED: KesselTupleService provides legacy tuple-layer operations.
|
|
12
|
+
# This service exists only for RBAC backward compatibility and will be removed.
|
|
13
|
+
# All endpoints are marked deprecated. Do not add new consumers.
|
|
14
|
+
# Use KesselInventoryService's ReportResource for new integrations.
|
|
15
|
+
class Service
|
|
16
|
+
|
|
17
|
+
include ::GRPC::GenericService
|
|
18
|
+
|
|
19
|
+
self.marshal_class_method = :encode
|
|
20
|
+
self.unmarshal_class_method = :decode
|
|
21
|
+
self.service_name = 'kessel.inventory.v1beta2.KesselTupleService'
|
|
22
|
+
|
|
23
|
+
# DEPRECATED: CreateTuples creates relationship tuples.
|
|
24
|
+
# This endpoint exists only for RBAC backward compatibility.
|
|
25
|
+
# Use ReportResource instead for new code.
|
|
26
|
+
rpc :CreateTuples, ::Kessel::Inventory::V1beta2::CreateTuplesRequest, ::Kessel::Inventory::V1beta2::CreateTuplesResponse
|
|
27
|
+
# DEPRECATED: DeleteTuples deletes relationship tuples.
|
|
28
|
+
# This endpoint exists only for RBAC backward compatibility.
|
|
29
|
+
# Use DeleteResource instead for new code.
|
|
30
|
+
rpc :DeleteTuples, ::Kessel::Inventory::V1beta2::DeleteTuplesRequest, ::Kessel::Inventory::V1beta2::DeleteTuplesResponse
|
|
31
|
+
# DEPRECATED: ReadTuples reads relationship tuples.
|
|
32
|
+
# This endpoint exists only for RBAC backward compatibility.
|
|
33
|
+
# Use StreamedListObjects/StreamedListSubjects instead for new code.
|
|
34
|
+
rpc :ReadTuples, ::Kessel::Inventory::V1beta2::ReadTuplesRequest, stream(::Kessel::Inventory::V1beta2::ReadTuplesResponse)
|
|
35
|
+
# DEPRECATED: AcquireLock acquires a distributed lock.
|
|
36
|
+
# This endpoint exists only for RBAC backward compatibility.
|
|
37
|
+
rpc :AcquireLock, ::Kessel::Inventory::V1beta2::AcquireLockRequest, ::Kessel::Inventory::V1beta2::AcquireLockResponse
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Stub = Service.rpc_stub_class
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# RBAC V2 Module Guidelines
|
|
2
|
+
|
|
3
|
+
## Directory Purpose
|
|
4
|
+
|
|
5
|
+
This directory contains the hand-written RBAC convenience layer (`Kessel::RBAC::V2`) built on top of V1beta2 protobuf types. It provides workspace operations over both HTTP and gRPC, plus factory helpers for constructing protobuf resource/subject references.
|
|
6
|
+
|
|
7
|
+
## File Organization
|
|
8
|
+
|
|
9
|
+
The module is split across three files reopening the same `Kessel::RBAC::V2` module:
|
|
10
|
+
|
|
11
|
+
- `v2.rb` -- Public API: `fetch_default_workspace`, `fetch_root_workspace`, `list_workspaces`, plus private response processing. Defines the `Workspace` struct and constants.
|
|
12
|
+
- `v2_helpers.rb` -- Factory methods for protobuf references: `workspace_type`, `role_type`, `principal_resource`, `role_resource`, `workspace_resource`, `principal_subject`, `subject`.
|
|
13
|
+
- `v2_http.rb` -- Private HTTP helpers: `run_request` (sets headers, calls auth), `check_http_client` (validates host/port match).
|
|
14
|
+
|
|
15
|
+
All three files must be required together -- `lib/kessel-sdk.rb` handles this. Do not require individual files in isolation.
|
|
16
|
+
|
|
17
|
+
## Module Design
|
|
18
|
+
|
|
19
|
+
- `V2` is a Ruby module meant to be `include`d, not instantiated. Tests use `include Kessel::RBAC::V2` directly in the RSpec context.
|
|
20
|
+
- `V2` includes `Kessel::Inventory::V1beta2` to access protobuf types (`SubjectReference`, `ResourceReference`, `RepresentationType`, etc.) without full qualification.
|
|
21
|
+
- The `include V1beta2` appears in both `v2.rb` and `v2_helpers.rb` -- this is intentional for load-order independence.
|
|
22
|
+
|
|
23
|
+
## Constants and Structs
|
|
24
|
+
|
|
25
|
+
- `WORKSPACE_ENDPOINT = '/api/rbac/v2/workspaces/'` -- the REST path appended to `rbac_base_endpoint`.
|
|
26
|
+
- `DEFAULT_PAGE_LIMIT = 1000` -- page size for `list_workspaces` auto-pagination.
|
|
27
|
+
- `Workspace = Struct.new(:id, :name, :type, :description)` -- keyword-argument struct returned by HTTP workspace fetches.
|
|
28
|
+
|
|
29
|
+
## Factory Helper Conventions
|
|
30
|
+
|
|
31
|
+
All factory methods in `v2_helpers.rb` follow these rules:
|
|
32
|
+
|
|
33
|
+
- Every RBAC resource uses `reporter_type: 'rbac'` (hardcoded, not configurable).
|
|
34
|
+
- `principal_resource(id, domain)` formats `resource_id` as `"#{domain}/#{id}"` -- the domain comes first.
|
|
35
|
+
- `principal_subject(id, domain)` wraps `principal_resource` in a `SubjectReference`.
|
|
36
|
+
- `subject(resource_ref, relation = nil)` is a generic factory -- pass `nil` for relation to omit it.
|
|
37
|
+
- `workspace_type` and `role_type` return `RepresentationType` structs, not `ResourceReference`.
|
|
38
|
+
- Factory methods return frozen protobuf objects (protobuf default). Do not `.dup` or mutate them.
|
|
39
|
+
|
|
40
|
+
## HTTP Workspace Operations
|
|
41
|
+
|
|
42
|
+
### Request Pattern
|
|
43
|
+
|
|
44
|
+
- `fetch_workspace` (private) strips trailing slashes from `rbac_base_endpoint`, appends `WORKSPACE_ENDPOINT`, sets a `type` query parameter, and issues a `GET` request.
|
|
45
|
+
- The `x-rh-rbac-org-id` header is required on every request -- set from the `org_id` parameter in `run_request`.
|
|
46
|
+
- Authentication is optional: when `auth` is non-nil, `auth.configure_request(request)` is called. The `auth` object must implement the `AuthRequest` interface (i.e., `Kessel::Auth#oauth2_auth_request(oauth)` return value).
|
|
47
|
+
|
|
48
|
+
### HTTP Client Handling
|
|
49
|
+
|
|
50
|
+
- When `http_client` is `nil`, a new `Net::HTTP` is created per call (one TCP connection per fetch).
|
|
51
|
+
- When `http_client` is provided, `check_http_client` validates that its `address` and `port` match the endpoint URI. Mismatches raise `RuntimeError`.
|
|
52
|
+
- For multiple workspace fetches, pass a pre-started `Net::HTTP` to avoid repeated TCP handshakes.
|
|
53
|
+
|
|
54
|
+
### Response Processing
|
|
55
|
+
|
|
56
|
+
- `process_response` raises `RuntimeError` for non-success HTTP status codes.
|
|
57
|
+
- It expects exactly one workspace in `data[]` -- any other count raises `RuntimeError`.
|
|
58
|
+
- `extract_workspace` maps JSON keys to the `Workspace` struct.
|
|
59
|
+
|
|
60
|
+
## gRPC Workspace Listing
|
|
61
|
+
|
|
62
|
+
`list_workspaces(inventory, subject, relation, continuation_token = nil, consistency: nil)` returns a lazy `Enumerator`:
|
|
63
|
+
|
|
64
|
+
- Internally calls `inventory.streamed_list_objects` with a `StreamedListObjectsRequest`.
|
|
65
|
+
- Auto-paginates: extracts `continuation_token` from each response's `pagination` field.
|
|
66
|
+
- Stops when the server returns no responses or `continuation_token` is nil/falsy.
|
|
67
|
+
- The `consistency` parameter is forwarded to every paginated request -- it is not modified between pages.
|
|
68
|
+
- Consume with `.each` for constant memory, or `.to_a` to materialize all results (use with caution on large datasets).
|
|
69
|
+
|
|
70
|
+
## Error Handling
|
|
71
|
+
|
|
72
|
+
- Local validation and HTTP failures use bare `RuntimeError` (via `raise "message"`) -- not custom exception classes. These cover workspace fetch HTTP errors and unexpected response shapes.
|
|
73
|
+
- gRPC errors from `streamed_list_objects` propagate directly as `GRPC::BadStatus` subclasses -- do not wrap them.
|
|
74
|
+
|
|
75
|
+
## RBS Type Signatures
|
|
76
|
+
|
|
77
|
+
- Signatures live in `sig/kessel/rbac.rbs`. Update this file when adding or changing public method signatures.
|
|
78
|
+
- The `Workspace` struct, all public methods, and `list_workspaces` return type (`Enumerator`) are declared.
|
|
79
|
+
- Run `steep check` after changes to verify type correctness.
|
|
80
|
+
|
|
81
|
+
## Testing
|
|
82
|
+
|
|
83
|
+
- Tests are in `spec/kessel/rbac/v2_spec.rb`.
|
|
84
|
+
- The spec includes `Kessel::RBAC::V2` in the test context and calls module methods directly.
|
|
85
|
+
- HTTP calls are fully mocked -- `Net::HTTP`, `Net::HTTP::Get`, `URI`, and response objects.
|
|
86
|
+
- gRPC streaming is mocked by stubbing `inventory.streamed_list_objects` with arrays or dynamic blocks.
|
|
87
|
+
- Test pagination by returning different continuation tokens across multiple mock invocations.
|
|
88
|
+
- Assert both the exception class and a message regex for error paths.
|
|
89
|
+
|
|
90
|
+
## RuboCop
|
|
91
|
+
|
|
92
|
+
This directory is subject to RuboCop rules (unlike generated code). Limits: method length 25, class length 150, cyclomatic complexity 10, line length 120.
|
|
93
|
+
|
|
94
|
+
## Relationship to Kessel::Console
|
|
95
|
+
|
|
96
|
+
`lib/kessel/console.rb` depends on `v2_helpers.rb` (via `require_relative 'rbac/v2_helpers'`) and uses `principal_subject` to convert Red Hat identity headers into `SubjectReference` objects. Changes to factory helper signatures affect `Console`.
|
|
97
|
+
|
|
98
|
+
## Adding New RBAC Operations
|
|
99
|
+
|
|
100
|
+
1. Determine whether the operation is HTTP-based (REST) or gRPC-based (protobuf service call).
|
|
101
|
+
2. Add public methods to `v2.rb`. Add factory helpers to `v2_helpers.rb`. Add HTTP plumbing to `v2_http.rb`.
|
|
102
|
+
3. Update `sig/kessel/rbac.rbs` with the new method signature.
|
|
103
|
+
4. Add tests in `spec/kessel/rbac/v2_spec.rb` following the existing mock patterns.
|
|
104
|
+
5. Run `bundle exec rspec` and `steep check`.
|
data/lib/kessel/rbac/v2.rb
CHANGED
|
@@ -14,12 +14,14 @@ module Kessel
|
|
|
14
14
|
DEFAULT_PAGE_LIMIT = 1000
|
|
15
15
|
Workspace = Struct.new(:id, :name, :type, :description)
|
|
16
16
|
|
|
17
|
-
def fetch_default_workspace(rbac_base_endpoint, org_id, auth: nil, http_client: nil)
|
|
18
|
-
fetch_workspace(rbac_base_endpoint, org_id, 'default', auth: auth, http_client: http_client
|
|
17
|
+
def fetch_default_workspace(rbac_base_endpoint, org_id, auth: nil, http_client: nil, with_ancestry: true)
|
|
18
|
+
fetch_workspace(rbac_base_endpoint, org_id, 'default', auth: auth, http_client: http_client,
|
|
19
|
+
with_ancestry: with_ancestry)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
def fetch_root_workspace(rbac_base_endpoint, org_id, auth: nil, http_client: nil)
|
|
22
|
-
fetch_workspace(rbac_base_endpoint, org_id, 'root', auth: auth, http_client: http_client
|
|
22
|
+
def fetch_root_workspace(rbac_base_endpoint, org_id, auth: nil, http_client: nil, with_ancestry: true)
|
|
23
|
+
fetch_workspace(rbac_base_endpoint, org_id, 'root', auth: auth, http_client: http_client,
|
|
24
|
+
with_ancestry: with_ancestry)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# Lists all workspaces that a subject has a specific relation to.
|
|
@@ -100,12 +102,12 @@ module Kessel
|
|
|
100
102
|
)
|
|
101
103
|
end
|
|
102
104
|
|
|
103
|
-
def fetch_workspace(rbac_base_endpoint, org_id, workspace_type, auth: nil, http_client: nil
|
|
105
|
+
def fetch_workspace(rbac_base_endpoint, org_id, workspace_type, auth: nil, http_client: nil,
|
|
106
|
+
with_ancestry: true)
|
|
104
107
|
rbac_base_endpoint = rbac_base_endpoint.delete_suffix('/')
|
|
105
108
|
uri = URI(rbac_base_endpoint + WORKSPACE_ENDPOINT)
|
|
106
|
-
query = {
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
+
query = { type: workspace_type }
|
|
110
|
+
query[:with_ancestry] = 'true' if with_ancestry
|
|
109
111
|
uri.query = URI.encode_www_form(query)
|
|
110
112
|
if http_client.nil?
|
|
111
113
|
http_client = Net::HTTP.new(uri.host, uri.port)
|
data/lib/kessel/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kessel-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Project Kessel
|
|
@@ -210,6 +210,7 @@ files:
|
|
|
210
210
|
- lib/kessel/console.rb
|
|
211
211
|
- lib/kessel/grpc.rb
|
|
212
212
|
- lib/kessel/inventory.rb
|
|
213
|
+
- lib/kessel/inventory/GUIDELINES.md
|
|
213
214
|
- lib/kessel/inventory/v1.rb
|
|
214
215
|
- lib/kessel/inventory/v1/health_pb.rb
|
|
215
216
|
- lib/kessel/inventory/v1/health_services_pb.rb
|
|
@@ -236,6 +237,8 @@ files:
|
|
|
236
237
|
- lib/kessel/inventory/v1beta1/resources/reporter_data_pb.rb
|
|
237
238
|
- lib/kessel/inventory/v1beta1/resources/resource_label_pb.rb
|
|
238
239
|
- lib/kessel/inventory/v1beta2.rb
|
|
240
|
+
- lib/kessel/inventory/v1beta2/acquire_lock_request_pb.rb
|
|
241
|
+
- lib/kessel/inventory/v1beta2/acquire_lock_response_pb.rb
|
|
239
242
|
- lib/kessel/inventory/v1beta2/allowed_pb.rb
|
|
240
243
|
- lib/kessel/inventory/v1beta2/check_bulk_request_pb.rb
|
|
241
244
|
- lib/kessel/inventory/v1beta2/check_bulk_response_pb.rb
|
|
@@ -251,10 +254,23 @@ files:
|
|
|
251
254
|
- lib/kessel/inventory/v1beta2/check_self_response_pb.rb
|
|
252
255
|
- lib/kessel/inventory/v1beta2/consistency_pb.rb
|
|
253
256
|
- lib/kessel/inventory/v1beta2/consistency_token_pb.rb
|
|
257
|
+
- lib/kessel/inventory/v1beta2/create_tuples_request_pb.rb
|
|
258
|
+
- lib/kessel/inventory/v1beta2/create_tuples_response_pb.rb
|
|
254
259
|
- lib/kessel/inventory/v1beta2/delete_resource_request_pb.rb
|
|
255
260
|
- lib/kessel/inventory/v1beta2/delete_resource_response_pb.rb
|
|
261
|
+
- lib/kessel/inventory/v1beta2/delete_tuples_request_pb.rb
|
|
262
|
+
- lib/kessel/inventory/v1beta2/delete_tuples_response_pb.rb
|
|
256
263
|
- lib/kessel/inventory/v1beta2/inventory_service_pb.rb
|
|
257
264
|
- lib/kessel/inventory/v1beta2/inventory_service_services_pb.rb
|
|
265
|
+
- lib/kessel/inventory/v1beta2/read_tuples_request_pb.rb
|
|
266
|
+
- lib/kessel/inventory/v1beta2/read_tuples_response_pb.rb
|
|
267
|
+
- lib/kessel/inventory/v1beta2/relation_fencing_check_pb.rb
|
|
268
|
+
- lib/kessel/inventory/v1beta2/relation_object_reference_pb.rb
|
|
269
|
+
- lib/kessel/inventory/v1beta2/relation_object_type_pb.rb
|
|
270
|
+
- lib/kessel/inventory/v1beta2/relation_subject_filter_pb.rb
|
|
271
|
+
- lib/kessel/inventory/v1beta2/relation_subject_reference_pb.rb
|
|
272
|
+
- lib/kessel/inventory/v1beta2/relation_tuple_filter_pb.rb
|
|
273
|
+
- lib/kessel/inventory/v1beta2/relationship_pb.rb
|
|
258
274
|
- lib/kessel/inventory/v1beta2/report_resource_request_pb.rb
|
|
259
275
|
- lib/kessel/inventory/v1beta2/report_resource_response_pb.rb
|
|
260
276
|
- lib/kessel/inventory/v1beta2/reporter_reference_pb.rb
|
|
@@ -269,7 +285,10 @@ files:
|
|
|
269
285
|
- lib/kessel/inventory/v1beta2/streamed_list_subjects_request_pb.rb
|
|
270
286
|
- lib/kessel/inventory/v1beta2/streamed_list_subjects_response_pb.rb
|
|
271
287
|
- lib/kessel/inventory/v1beta2/subject_reference_pb.rb
|
|
288
|
+
- lib/kessel/inventory/v1beta2/tuple_service_pb.rb
|
|
289
|
+
- lib/kessel/inventory/v1beta2/tuple_service_services_pb.rb
|
|
272
290
|
- lib/kessel/inventory/v1beta2/write_visibility_pb.rb
|
|
291
|
+
- lib/kessel/rbac/GUIDELINES.md
|
|
273
292
|
- lib/kessel/rbac/v2.rb
|
|
274
293
|
- lib/kessel/rbac/v2_helpers.rb
|
|
275
294
|
- lib/kessel/rbac/v2_http.rb
|