kessel-sdk 1.12.0 → 1.13.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 997a5d14ffc861c340630e331dfd5c34f367ed7f68b9f760363a817180c10711
4
- data.tar.gz: 6144712b5dec3a5355bc3f7e6a35953a0b4a2c2beeee56b017eebc659c03f3bc
3
+ metadata.gz: 6889800072d9061b0fe09d8d401cbbf909257498ec70e8a67924be71c6b5173b
4
+ data.tar.gz: 622700d85991005cf8e06ac17de8089c1c1feeacb89836055a4cbf4700b75446
5
5
  SHA512:
6
- metadata.gz: 7397b29d146033e8d2060af9e25195debd851f052b4c3fad903277bd13b25ed8a1588f5a712bc8952e18fc75315cb79bd32c3a8732c07604f0cf51d2689ba843
7
- data.tar.gz: 0c3b20634ed5d7529d66ba4b3e06c15e677eddfaf676618eaa4864205d7157a3b57d9cbab91b77de8e3adbbdbb707e255ef3055e986de6064a87b9c284b0e7dc
6
+ metadata.gz: 3a7e0603336f496723d888a4ae1b366c1d1e4c3567f9f3cb970ab04a7f454e7006348681439eb4d7d8c234f953693943ee4c597b7ef526ca84a329f640be06bb
7
+ data.tar.gz: 1e4acb64ffb25c32816b9f5bfb3fec448cc02895552558a212d80404e9f8732009d5ef86719ba9d83319c5502b92fa60262b2cf78b85975b608f4b902c80b394
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Kessel SDK for Ruby
2
2
 
3
+ [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/project-kessel/kessel-sdk-ruby/badge)](https://securityscorecards.dev/viewer/?uri=github.com/project-kessel/kessel-sdk-ruby)
4
+
3
5
  [![CI](https://github.com/project-kessel/kessel-sdk-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/project-kessel/kessel-sdk-ruby/actions/workflows/ci.yml)
4
6
 
5
7
  The official Ruby gRPC client SDK for [Project Kessel](https://github.com/project-kessel) services. It provides generated protobuf/gRPC bindings for the Kessel Inventory API, a fluent client builder with credential validation, OAuth 2.0 Client Credentials authentication (via OIDC), and RBAC convenience helpers for workspace operations.
@@ -130,6 +132,31 @@ client = KesselInventoryService::ClientBuilder.new(target)
130
132
 
131
133
  Build the client **once at application startup and reuse it**. The underlying gRPC channel manages its own HTTP/2 connection pool.
132
134
 
135
+ ### HTTP/2 Keepalive
136
+
137
+ All inventory clients now use these transport keepalive defaults, including clients created by existing builder chains:
138
+
139
+ - Ping interval: 45 seconds
140
+ - Ping acknowledgement timeout: 10 seconds
141
+ - Permit pings while there are no active calls: enabled
142
+
143
+ Override these values with the fluent `keepalive` method. Durations are finite positive real numbers in seconds and
144
+ are converted to integer milliseconds by truncating any fractional millisecond downward. The resulting value must be
145
+ between 1 and 2,147,483,647 milliseconds. Passing `nil` leaves that setting unchanged; `permit_without_calls` accepts
146
+ only `true` or `false` (including `false` as an explicit override).
147
+
148
+ ```ruby
149
+ client = Kessel::Inventory::V1beta2::KesselInventoryService::ClientBuilder
150
+ .new(ENV.fetch('KESSEL_ENDPOINT', nil))
151
+ .keepalive(interval: 60, timeout: 10, permit_without_calls: false)
152
+ .build
153
+ ```
154
+
155
+ The Ruby SDK sets `grpc.http2.max_pings_without_data` internally to avoid a local cap; it is not a public configuration
156
+ option. Before rollout, confirm that the Kessel server and any gateway enforce compatible keepalive policies
157
+ (RHCLOUD-51673). Keepalive is a transport-level ping mechanism, not an RPC health check and does not provide retries
158
+ or load-balancer guarantees. The builder still returns a gRPC stub; build it once and reuse it as usual.
159
+
133
160
  ### Check Permissions
134
161
 
135
162
  ```ruby
@@ -347,6 +374,7 @@ The `examples/` directory contains working examples. Set up environment variable
347
374
  | `check_for_update_bulk.rb` | Bulk strongly consistent update checks |
348
375
  | `delete_resource.rb` | Deleting resources |
349
376
  | `fetch_workspaces.rb` | Fetching workspaces via RBAC HTTP API |
377
+ | `keepalive.rb` | Configuring HTTP/2 keepalive on an inventory client |
350
378
  | `list_workspaces.rb` | Listing workspaces with auto-pagination |
351
379
  | `report_resource.rb` | Reporting resource state |
352
380
  | `console_principal.rb` | Building principals from `x-rh-identity` headers |
@@ -59,6 +59,25 @@ client = KesselInventoryService::ClientBuilder.new(target)
59
59
 
60
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
61
 
62
+ ### HTTP/2 Keepalive
63
+
64
+ Every `ClientBuilder` passes these channel arguments to its service stub, including builders that do not call `keepalive`:
65
+
66
+ - `grpc.keepalive_time_ms`: 45,000 (45 seconds)
67
+ - `grpc.keepalive_timeout_ms`: 10,000 (10 seconds)
68
+ - `grpc.keepalive_permit_without_calls`: 1 (enabled)
69
+ - `grpc.http2.max_pings_without_data`: 0 (internal Ruby gRPC setting; not exposed as a public option)
70
+
71
+ `keepalive(interval: nil, timeout: nil, permit_without_calls: nil)` is fluent. Duration keywords are finite positive real
72
+ numbers in seconds, converted to integer milliseconds by truncating fractional milliseconds downward; the result must
73
+ be 1 through 2,147,483,647. `nil` leaves the existing setting unchanged, and `permit_without_calls` accepts only the
74
+ actual boolean values `true` and `false`. Each call validates every supplied value before changing any builder state.
75
+
76
+ These are transport ping settings, not RPC health checks, retries, or load-balancer guarantees. Check server and gateway
77
+ keepalive enforcement compatibility before rollout (RHCLOUD-51673); in particular, the 45-second interval may be
78
+ affected by an infrastructure-enforced minimum. Ruby's `grpc.http2.max_pings_without_data` value is internal and is not a
79
+ cross-SDK configuration knob.
80
+
62
81
  ## Service Wiring Pattern
63
82
 
64
83
  Every gRPC service module must follow this exact pattern:
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: kessel/inventory/v1beta2/add_resource_relationships_request.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'google/protobuf/struct_pb'
8
+ require 'kessel/inventory/v1beta2/write_visibility_pb'
9
+ require 'buf/validate/validate_pb'
10
+
11
+
12
+ descriptor_data = "\nAkessel/inventory/v1beta2/add_resource_relationships_request.proto\x12\x18kessel.inventory.v1beta2\x1a\x1cgoogle/protobuf/struct.proto\x1a/kessel/inventory/v1beta2/write_visibility.proto\x1a\x1b\x62uf/validate/validate.proto\"\xc2\x06\n\x1f\x41\x64\x64ResourceRelationshipsRequest\x12&\n\x0cinventory_id\x18\x01 \x01(\tH\x01R\x0binventoryId\x88\x01\x01\x12-\n\x04type\x18\x02 \x01(\tB\x19\xbaH\x16r\x14\x10\x01\x32\x10^[A-Za-z0-9_-]+$R\x04type\x12>\n\rreporter_type\x18\x03 \x01(\tB\x19\xbaH\x16r\x14\x10\x01\x32\x10^[A-Za-z0-9_-]+$R\x0creporterType\x12\x39\n\x14reporter_instance_id\x18\x04 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x12reporterInstanceId\x12\x33\n\x11local_resource_id\x18\x05 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x0flocalResourceId\x12\x38\n\x08reporter\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructH\x02R\x08reporter\x88\x01\x01\x12\x34\n\x06\x63ommon\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructH\x03R\x06\x63ommon\x88\x01\x01\x12\'\n\x0etransaction_id\x18\x08 \x01(\tH\x00R\rtransactionId\x12^\n\x10write_visibility\x18\t \x01(\x0e\x32).kessel.inventory.v1beta2.WriteVisibilityB\x08\xbaH\x05\x82\x01\x02\x10\x01R\x0fwriteVisibility:\xe2\x01\xbaH\xde\x01\x1a\xdb\x01\n6add_resource_relationships.at_least_one_representation\x12\x41\x61t least one of reporter or common must be provided and non-empty\x1a^(has(this.reporter) && size(this.reporter) > 0) || (has(this.common) && size(this.common) > 0)B\x11\n\x0fidempotency_keyB\x0f\n\r_inventory_idB\x0b\n\t_reporterB\t\n\x07_commonBr\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
+ AddResourceRelationshipsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.AddResourceRelationshipsRequest").msgclass
21
+ end
22
+ end
23
+ 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/add_resource_relationships_response.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+
8
+ descriptor_data = "\nBkessel/inventory/v1beta2/add_resource_relationships_response.proto\x12\x18kessel.inventory.v1beta2\"\"\n AddResourceRelationshipsResponseBr\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
+ AddResourceRelationshipsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.AddResourceRelationshipsResponse").msgclass
17
+ end
18
+ end
19
+ end
@@ -13,6 +13,10 @@ require 'kessel/inventory/v1beta2/report_resource_request_pb'
13
13
  require 'kessel/inventory/v1beta2/report_resource_response_pb'
14
14
  require 'kessel/inventory/v1beta2/delete_resource_request_pb'
15
15
  require 'kessel/inventory/v1beta2/delete_resource_response_pb'
16
+ require 'kessel/inventory/v1beta2/add_resource_relationships_request_pb'
17
+ require 'kessel/inventory/v1beta2/add_resource_relationships_response_pb'
18
+ require 'kessel/inventory/v1beta2/remove_resource_relationships_request_pb'
19
+ require 'kessel/inventory/v1beta2/remove_resource_relationships_response_pb'
16
20
  require 'kessel/inventory/v1beta2/streamed_list_objects_request_pb'
17
21
  require 'kessel/inventory/v1beta2/streamed_list_objects_response_pb'
18
22
  require 'kessel/inventory/v1beta2/streamed_list_subjects_request_pb'
@@ -27,7 +31,7 @@ require 'kessel/inventory/v1beta2/check_for_update_bulk_request_pb'
27
31
  require 'kessel/inventory/v1beta2/check_for_update_bulk_response_pb'
28
32
 
29
33
 
30
- descriptor_data = "\n0kessel/inventory/v1beta2/inventory_service.proto\x12\x18kessel.inventory.v1beta2\x1a\x1cgoogle/api/annotations.proto\x1a,kessel/inventory/v1beta2/check_request.proto\x1a-kessel/inventory/v1beta2/check_response.proto\x1a\x37kessel/inventory/v1beta2/check_for_update_request.proto\x1a\x38kessel/inventory/v1beta2/check_for_update_response.proto\x1a\x36kessel/inventory/v1beta2/report_resource_request.proto\x1a\x37kessel/inventory/v1beta2/report_resource_response.proto\x1a\x36kessel/inventory/v1beta2/delete_resource_request.proto\x1a\x37kessel/inventory/v1beta2/delete_resource_response.proto\x1a<kessel/inventory/v1beta2/streamed_list_objects_request.proto\x1a=kessel/inventory/v1beta2/streamed_list_objects_response.proto\x1a=kessel/inventory/v1beta2/streamed_list_subjects_request.proto\x1a>kessel/inventory/v1beta2/streamed_list_subjects_response.proto\x1a\x31kessel/inventory/v1beta2/check_bulk_request.proto\x1a\x32kessel/inventory/v1beta2/check_bulk_response.proto\x1a\x31kessel/inventory/v1beta2/check_self_request.proto\x1a\x32kessel/inventory/v1beta2/check_self_response.proto\x1a\x36kessel/inventory/v1beta2/check_self_bulk_request.proto\x1a\x37kessel/inventory/v1beta2/check_self_bulk_response.proto\x1a<kessel/inventory/v1beta2/check_for_update_bulk_request.proto\x1a=kessel/inventory/v1beta2/check_for_update_bulk_response.proto2\x86\x0c\n\x16KesselInventoryService\x12~\n\x05\x43heck\x12&.kessel.inventory.v1beta2.CheckRequest\x1a\'.kessel.inventory.v1beta2.CheckResponse\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/api/kessel/v1beta2/check:\x01*\x12\x8e\x01\n\tCheckSelf\x12*.kessel.inventory.v1beta2.CheckSelfRequest\x1a+.kessel.inventory.v1beta2.CheckSelfResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/api/kessel/v1beta2/checkself:\x01*\x12\xa2\x01\n\x0e\x43heckForUpdate\x12/.kessel.inventory.v1beta2.CheckForUpdateRequest\x1a\x30.kessel.inventory.v1beta2.CheckForUpdateResponse\"-\x82\xd3\xe4\x93\x02\'\"\"/api/kessel/v1beta2/checkforupdate:\x01*\x12\xb2\x01\n\x12\x43heckForUpdateBulk\x12\x33.kessel.inventory.v1beta2.CheckForUpdateBulkRequest\x1a\x34.kessel.inventory.v1beta2.CheckForUpdateBulkResponse\"1\x82\xd3\xe4\x93\x02+\"&/api/kessel/v1beta2/checkforupdatebulk:\x01*\x12\x8e\x01\n\tCheckBulk\x12*.kessel.inventory.v1beta2.CheckBulkRequest\x1a+.kessel.inventory.v1beta2.CheckBulkResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/api/kessel/v1beta2/checkbulk:\x01*\x12\x9e\x01\n\rCheckSelfBulk\x12..kessel.inventory.v1beta2.CheckSelfBulkRequest\x1a/.kessel.inventory.v1beta2.CheckSelfBulkResponse\",\x82\xd3\xe4\x93\x02&\"!/api/kessel/v1beta2/checkselfbulk:\x01*\x12\x9d\x01\n\x0eReportResource\x12/.kessel.inventory.v1beta2.ReportResourceRequest\x1a\x30.kessel.inventory.v1beta2.ReportResourceResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/api/kessel/v1beta2/resources:\x01*\x12\x9d\x01\n\x0e\x44\x65leteResource\x12/.kessel.inventory.v1beta2.DeleteResourceRequest\x1a\x30.kessel.inventory.v1beta2.DeleteResourceResponse\"(\x82\xd3\xe4\x93\x02\"*\x1d/api/kessel/v1beta2/resources:\x01*\x12\x84\x01\n\x13StreamedListObjects\x12\x34.kessel.inventory.v1beta2.StreamedListObjectsRequest\x1a\x35.kessel.inventory.v1beta2.StreamedListObjectsResponse0\x01\x12\x87\x01\n\x14StreamedListSubjects\x12\x35.kessel.inventory.v1beta2.StreamedListSubjectsRequest\x1a\x36.kessel.inventory.v1beta2.StreamedListSubjectsResponse0\x01\x42r\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
34
+ descriptor_data = "\n0kessel/inventory/v1beta2/inventory_service.proto\x12\x18kessel.inventory.v1beta2\x1a\x1cgoogle/api/annotations.proto\x1a,kessel/inventory/v1beta2/check_request.proto\x1a-kessel/inventory/v1beta2/check_response.proto\x1a\x37kessel/inventory/v1beta2/check_for_update_request.proto\x1a\x38kessel/inventory/v1beta2/check_for_update_response.proto\x1a\x36kessel/inventory/v1beta2/report_resource_request.proto\x1a\x37kessel/inventory/v1beta2/report_resource_response.proto\x1a\x36kessel/inventory/v1beta2/delete_resource_request.proto\x1a\x37kessel/inventory/v1beta2/delete_resource_response.proto\x1a\x41kessel/inventory/v1beta2/add_resource_relationships_request.proto\x1a\x42kessel/inventory/v1beta2/add_resource_relationships_response.proto\x1a\x44kessel/inventory/v1beta2/remove_resource_relationships_request.proto\x1a\x45kessel/inventory/v1beta2/remove_resource_relationships_response.proto\x1a<kessel/inventory/v1beta2/streamed_list_objects_request.proto\x1a=kessel/inventory/v1beta2/streamed_list_objects_response.proto\x1a=kessel/inventory/v1beta2/streamed_list_subjects_request.proto\x1a>kessel/inventory/v1beta2/streamed_list_subjects_response.proto\x1a\x31kessel/inventory/v1beta2/check_bulk_request.proto\x1a\x32kessel/inventory/v1beta2/check_bulk_response.proto\x1a\x31kessel/inventory/v1beta2/check_self_request.proto\x1a\x32kessel/inventory/v1beta2/check_self_response.proto\x1a\x36kessel/inventory/v1beta2/check_self_bulk_request.proto\x1a\x37kessel/inventory/v1beta2/check_self_bulk_response.proto\x1a<kessel/inventory/v1beta2/check_for_update_bulk_request.proto\x1a=kessel/inventory/v1beta2/check_for_update_bulk_response.proto2\xa7\x0f\n\x16KesselInventoryService\x12~\n\x05\x43heck\x12&.kessel.inventory.v1beta2.CheckRequest\x1a\'.kessel.inventory.v1beta2.CheckResponse\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/api/kessel/v1beta2/check:\x01*\x12\x8e\x01\n\tCheckSelf\x12*.kessel.inventory.v1beta2.CheckSelfRequest\x1a+.kessel.inventory.v1beta2.CheckSelfResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/api/kessel/v1beta2/checkself:\x01*\x12\xa2\x01\n\x0e\x43heckForUpdate\x12/.kessel.inventory.v1beta2.CheckForUpdateRequest\x1a\x30.kessel.inventory.v1beta2.CheckForUpdateResponse\"-\x82\xd3\xe4\x93\x02\'\"\"/api/kessel/v1beta2/checkforupdate:\x01*\x12\xb2\x01\n\x12\x43heckForUpdateBulk\x12\x33.kessel.inventory.v1beta2.CheckForUpdateBulkRequest\x1a\x34.kessel.inventory.v1beta2.CheckForUpdateBulkResponse\"1\x82\xd3\xe4\x93\x02+\"&/api/kessel/v1beta2/checkforupdatebulk:\x01*\x12\x8e\x01\n\tCheckBulk\x12*.kessel.inventory.v1beta2.CheckBulkRequest\x1a+.kessel.inventory.v1beta2.CheckBulkResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/api/kessel/v1beta2/checkbulk:\x01*\x12\x9e\x01\n\rCheckSelfBulk\x12..kessel.inventory.v1beta2.CheckSelfBulkRequest\x1a/.kessel.inventory.v1beta2.CheckSelfBulkResponse\",\x82\xd3\xe4\x93\x02&\"!/api/kessel/v1beta2/checkselfbulk:\x01*\x12\x9d\x01\n\x0eReportResource\x12/.kessel.inventory.v1beta2.ReportResourceRequest\x1a\x30.kessel.inventory.v1beta2.ReportResourceResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/api/kessel/v1beta2/resources:\x01*\x12\x9d\x01\n\x0e\x44\x65leteResource\x12/.kessel.inventory.v1beta2.DeleteResourceRequest\x1a\x30.kessel.inventory.v1beta2.DeleteResourceResponse\"(\x82\xd3\xe4\x93\x02\"*\x1d/api/kessel/v1beta2/resources:\x01*\x12\xc9\x01\n\x18\x41\x64\x64ResourceRelationships\x12\x39.kessel.inventory.v1beta2.AddResourceRelationshipsRequest\x1a:.kessel.inventory.v1beta2.AddResourceRelationshipsResponse\"6\x82\xd3\xe4\x93\x02\x30\"+/api/kessel/v1beta2/resources/relationships:\x01*\x12\xd2\x01\n\x1bRemoveResourceRelationships\x12<.kessel.inventory.v1beta2.RemoveResourceRelationshipsRequest\x1a=.kessel.inventory.v1beta2.RemoveResourceRelationshipsResponse\"6\x82\xd3\xe4\x93\x02\x30*+/api/kessel/v1beta2/resources/relationships:\x01*\x12\x84\x01\n\x13StreamedListObjects\x12\x34.kessel.inventory.v1beta2.StreamedListObjectsRequest\x1a\x35.kessel.inventory.v1beta2.StreamedListObjectsResponse0\x01\x12\x87\x01\n\x14StreamedListSubjects\x12\x35.kessel.inventory.v1beta2.StreamedListSubjectsRequest\x1a\x36.kessel.inventory.v1beta2.StreamedListSubjectsResponse0\x01\x42r\n(org.project_kessel.api.inventory.v1beta2P\x01ZDgithub.com/project-kessel/inventory-api/api/kessel/inventory/v1beta2b\x06proto3"
31
35
 
32
36
  pool = ::Google::Protobuf::DescriptorPool.generated_pool
33
37
  pool.add_serialized_file(descriptor_data)
@@ -121,6 +121,48 @@ module Kessel
121
121
  #
122
122
  # As an example, it can revoke previously granted access across the system.
123
123
  rpc :DeleteResource, ::Kessel::Inventory::V1beta2::DeleteResourceRequest, ::Kessel::Inventory::V1beta2::DeleteResourceResponse
124
+ # Adds one or more relationships to an existing *Resource*.
125
+ #
126
+ # If the *Resource* does not yet exist for this *Reporter*, it is created with
127
+ # the provided relationship fields as its initial representation. All other
128
+ # relationships already reported for that *Resource* are unaffected.
129
+ #
130
+ # Unlike `ReportResource`, which asserts the complete state of a *Resource*,
131
+ # this operation is a targeted mutation: it only touches the specific
132
+ # relationship fields and values named in the request. Attribute fields,
133
+ # other relationship fields, and metadata are left exactly as they were.
134
+ #
135
+ # Only relationship-typed fields from the *Resource's* schema are accepted;
136
+ # attribute fields are rejected.
137
+ #
138
+ # The relationships added through this API are used to determine relationship
139
+ # check outcomes via the Check and CheckForUpdate APIs.
140
+ #
141
+ # This API does **not** guarantee immediate read-your-writes consistency by default.
142
+ # If a reporter requires newly added relationships to be visible in subsequent
143
+ # checks (e.g., `Check`), the request must explicitly set `write_visibility = IMMEDIATE`.
144
+ rpc :AddResourceRelationships, ::Kessel::Inventory::V1beta2::AddResourceRelationshipsRequest, ::Kessel::Inventory::V1beta2::AddResourceRelationshipsResponse
145
+ # Removes one or more specific relationships from an existing *Resource*.
146
+ #
147
+ # All other relationships for the *Resource* are unaffected. If the *Resource*
148
+ # does not exist, this operation returns `NOT_FOUND` — matching the behaviour
149
+ # of `DeleteResource`.
150
+ #
151
+ # Unlike `ReportResource`, which asserts the complete state of a *Resource*,
152
+ # this operation is a targeted mutation: it only removes the specific
153
+ # relationship values named in the request. Attribute fields and other
154
+ # relationship fields are left exactly as they were.
155
+ #
156
+ # Only relationship-typed fields from the *Resource's* schema are accepted;
157
+ # attribute fields are rejected.
158
+ #
159
+ # The relationships removed through this API are used to determine relationship
160
+ # check outcomes via the Check and CheckForUpdate APIs.
161
+ #
162
+ # This API does **not** guarantee immediate read-your-writes consistency by default.
163
+ # If a reporter requires removed relationships to no longer be visible in subsequent
164
+ # checks (e.g., `Check`), the request must explicitly set `write_visibility = IMMEDIATE`.
165
+ rpc :RemoveResourceRelationships, ::Kessel::Inventory::V1beta2::RemoveResourceRelationshipsRequest, ::Kessel::Inventory::V1beta2::RemoveResourceRelationshipsResponse
124
166
  # Streams a list of objects where the given subject has the specified relation.
125
167
  #
126
168
  # This relationship query answers the question:
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: kessel/inventory/v1beta2/remove_resource_relationships_request.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'google/protobuf/struct_pb'
8
+ require 'kessel/inventory/v1beta2/write_visibility_pb'
9
+ require 'buf/validate/validate_pb'
10
+
11
+
12
+ descriptor_data = "\nDkessel/inventory/v1beta2/remove_resource_relationships_request.proto\x12\x18kessel.inventory.v1beta2\x1a\x1cgoogle/protobuf/struct.proto\x1a/kessel/inventory/v1beta2/write_visibility.proto\x1a\x1b\x62uf/validate/validate.proto\"\xc8\x06\n\"RemoveResourceRelationshipsRequest\x12&\n\x0cinventory_id\x18\x01 \x01(\tH\x01R\x0binventoryId\x88\x01\x01\x12-\n\x04type\x18\x02 \x01(\tB\x19\xbaH\x16r\x14\x10\x01\x32\x10^[A-Za-z0-9_-]+$R\x04type\x12>\n\rreporter_type\x18\x03 \x01(\tB\x19\xbaH\x16r\x14\x10\x01\x32\x10^[A-Za-z0-9_-]+$R\x0creporterType\x12\x39\n\x14reporter_instance_id\x18\x04 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x12reporterInstanceId\x12\x33\n\x11local_resource_id\x18\x05 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x0flocalResourceId\x12\x38\n\x08reporter\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructH\x02R\x08reporter\x88\x01\x01\x12\x34\n\x06\x63ommon\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructH\x03R\x06\x63ommon\x88\x01\x01\x12\'\n\x0etransaction_id\x18\x08 \x01(\tH\x00R\rtransactionId\x12^\n\x10write_visibility\x18\t \x01(\x0e\x32).kessel.inventory.v1beta2.WriteVisibilityB\x08\xbaH\x05\x82\x01\x02\x10\x01R\x0fwriteVisibility:\xe5\x01\xbaH\xe1\x01\x1a\xde\x01\n9remove_resource_relationships.at_least_one_representation\x12\x41\x61t least one of reporter or common must be provided and non-empty\x1a^(has(this.reporter) && size(this.reporter) > 0) || (has(this.common) && size(this.common) > 0)B\x11\n\x0fidempotency_keyB\x0f\n\r_inventory_idB\x0b\n\t_reporterB\t\n\x07_commonBr\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
+ RemoveResourceRelationshipsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RemoveResourceRelationshipsRequest").msgclass
21
+ end
22
+ end
23
+ 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/remove_resource_relationships_response.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+
8
+ descriptor_data = "\nEkessel/inventory/v1beta2/remove_resource_relationships_response.proto\x12\x18kessel.inventory.v1beta2\"%\n#RemoveResourceRelationshipsResponseBr\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
+ RemoveResourceRelationshipsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kessel.inventory.v1beta2.RemoveResourceRelationshipsResponse").msgclass
17
+ end
18
+ end
19
+ end
@@ -14,9 +14,24 @@ module Kessel
14
14
  class ClientBuilder
15
15
  include Kessel::GRPC
16
16
 
17
+ MAX_KEEPALIVE_MILLISECONDS = 2_147_483_647
18
+ private_constant :MAX_KEEPALIVE_MILLISECONDS
19
+
17
20
  def initialize(target)
18
21
  @target = target
19
22
  raise 'Invalid target type' if @target.nil? || !@target.is_a?(String)
23
+
24
+ @channel_args = {
25
+ 'grpc.keepalive_time_ms' => 45_000,
26
+ 'grpc.keepalive_timeout_ms' => 10_000,
27
+ 'grpc.keepalive_permit_without_calls' => 1,
28
+ 'grpc.http2.max_pings_without_data' => 0
29
+ }
30
+ end
31
+
32
+ def keepalive(interval: nil, timeout: nil, permit_without_calls: nil)
33
+ @channel_args.merge!(keepalive_channel_args(interval, timeout, permit_without_calls))
34
+ self
20
35
  end
21
36
 
22
37
  def oauth2_client_authenticated(oauth2_client_credentials:, channel_credentials: nil)
@@ -52,7 +67,7 @@ module Kessel
52
67
 
53
68
  credentials = @channel_credentials
54
69
  credentials = credentials.compose(@call_credentials) unless @call_credentials.nil?
55
- self.class.stub_class.new(@target, credentials)
70
+ self.class.stub_class.new(@target, credentials, channel_args: @channel_args.dup)
56
71
  end
57
72
 
58
73
  private
@@ -66,6 +81,41 @@ module Kessel
66
81
 
67
82
  raise 'Invalid credential configuration: can not authenticate with insecure channel'
68
83
  end
84
+
85
+ def keepalive_channel_args(interval, timeout, permit_without_calls)
86
+ channel_args = {}
87
+ channel_args['grpc.keepalive_time_ms'] = duration_to_milliseconds(interval, 'interval') unless interval.nil?
88
+ channel_args['grpc.keepalive_timeout_ms'] = duration_to_milliseconds(timeout, 'timeout') unless timeout.nil?
89
+
90
+ unless permit_without_calls.nil?
91
+ unless permit_without_calls.equal?(true) || permit_without_calls.equal?(false)
92
+ raise 'Invalid keepalive permit_without_calls: must be true, false, or nil'
93
+ end
94
+
95
+ channel_args['grpc.keepalive_permit_without_calls'] = permit_without_calls ? 1 : 0
96
+ end
97
+
98
+ channel_args
99
+ end
100
+
101
+ def duration_to_milliseconds(value, name)
102
+ unless value.is_a?(Numeric) && !value.is_a?(Complex) && value.respond_to?(:finite?) && value.finite?
103
+ raise "Invalid keepalive #{name}: must be a finite real number of seconds"
104
+ end
105
+
106
+ scaled_milliseconds = value * 1000
107
+ unless valid_scaled_keepalive_milliseconds?(scaled_milliseconds)
108
+ raise "Invalid keepalive #{name}: must convert to 1..#{MAX_KEEPALIVE_MILLISECONDS} milliseconds"
109
+ end
110
+
111
+ scaled_milliseconds.floor
112
+ end
113
+
114
+ def valid_scaled_keepalive_milliseconds?(milliseconds)
115
+ milliseconds.respond_to?(:finite?) &&
116
+ milliseconds.finite? &&
117
+ (1...(MAX_KEEPALIVE_MILLISECONDS + 1)).cover?(milliseconds)
118
+ end
69
119
  end
70
120
  end
71
121
  end
@@ -47,9 +47,10 @@ All factory methods in `v2_helpers.rb` follow these rules:
47
47
 
48
48
  ### HTTP Client Handling
49
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.
50
+ - When `http_client` is `nil`, a new `Net::HTTP` is created per call (one TCP connection per fetch). TLS is automatically enabled when the endpoint scheme is `https` via `use_ssl = (uri.scheme == 'https')`.
51
+ - When `http_client` is provided, `check_http_client` validates that its `address` and `port` match the endpoint URI **and** that its `use_ssl?` setting matches the endpoint scheme. Host/port mismatches and SSL mismatches both raise `RuntimeError`.
52
+ - For multiple workspace fetches, pass a pre-started `Net::HTTP` to avoid repeated TCP handshakes. Ensure the client's `use_ssl` setting matches the endpoint scheme.
53
+ - Plaintext (`http://`) connections work unchanged for existing internal-URL consumers. No explicit opt-in is required for `http://` endpoints — TLS is only enforced when the scheme is `https`.
53
54
 
54
55
  ### Response Processing
55
56
 
@@ -111,6 +111,7 @@ module Kessel
111
111
  uri.query = URI.encode_www_form(query)
112
112
  if http_client.nil?
113
113
  http_client = Net::HTTP.new(uri.host, uri.port)
114
+ http_client.use_ssl = (uri.scheme == 'https')
114
115
  else
115
116
  check_http_client(http_client, uri)
116
117
  end
@@ -17,9 +17,13 @@ module Kessel
17
17
  end
18
18
 
19
19
  def check_http_client(http_client, uri)
20
- return if uri.host == http_client.address && uri.port == http_client.port
20
+ unless uri.host == http_client.address && uri.port == http_client.port
21
+ raise 'http client host and port do not match rbac_base_endpoint'
22
+ end
21
23
 
22
- raise 'http client host and port do not match rbac_base_endpoint'
24
+ return if http_client.use_ssl? == (uri.scheme == 'https')
25
+
26
+ raise 'http client SSL configuration does not match endpoint scheme'
23
27
  end
24
28
  end
25
29
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kessel
4
4
  module Inventory
5
- VERSION = '1.12.0'
5
+ VERSION = '1.13.1'
6
6
  end
7
7
  end
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.12.0
4
+ version: 1.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Project Kessel
@@ -9,6 +9,20 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: grpc
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -169,14 +183,14 @@ dependencies:
169
183
  requirements:
170
184
  - - "~>"
171
185
  - !ruby/object:Gem::Version
172
- version: 1.10.0
186
+ version: 2.1.0
173
187
  type: :development
174
188
  prerelease: false
175
189
  version_requirements: !ruby/object:Gem::Requirement
176
190
  requirements:
177
191
  - - "~>"
178
192
  - !ruby/object:Gem::Version
179
- version: 1.10.0
193
+ version: 2.1.0
180
194
  - !ruby/object:Gem::Dependency
181
195
  name: typeprof
182
196
  requirement: !ruby/object:Gem::Requirement
@@ -239,6 +253,8 @@ files:
239
253
  - lib/kessel/inventory/v1beta2.rb
240
254
  - lib/kessel/inventory/v1beta2/acquire_lock_request_pb.rb
241
255
  - lib/kessel/inventory/v1beta2/acquire_lock_response_pb.rb
256
+ - lib/kessel/inventory/v1beta2/add_resource_relationships_request_pb.rb
257
+ - lib/kessel/inventory/v1beta2/add_resource_relationships_response_pb.rb
242
258
  - lib/kessel/inventory/v1beta2/allowed_pb.rb
243
259
  - lib/kessel/inventory/v1beta2/check_bulk_request_pb.rb
244
260
  - lib/kessel/inventory/v1beta2/check_bulk_response_pb.rb
@@ -271,6 +287,8 @@ files:
271
287
  - lib/kessel/inventory/v1beta2/relation_subject_reference_pb.rb
272
288
  - lib/kessel/inventory/v1beta2/relation_tuple_filter_pb.rb
273
289
  - lib/kessel/inventory/v1beta2/relationship_pb.rb
290
+ - lib/kessel/inventory/v1beta2/remove_resource_relationships_request_pb.rb
291
+ - lib/kessel/inventory/v1beta2/remove_resource_relationships_response_pb.rb
274
292
  - lib/kessel/inventory/v1beta2/report_resource_request_pb.rb
275
293
  - lib/kessel/inventory/v1beta2/report_resource_response_pb.rb
276
294
  - lib/kessel/inventory/v1beta2/reporter_reference_pb.rb
@@ -314,7 +332,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
332
  - !ruby/object:Gem::Version
315
333
  version: '0'
316
334
  requirements: []
317
- rubygems_version: 4.0.7
335
+ rubygems_version: 4.0.22
318
336
  specification_version: 4
319
337
  summary: Ruby SDK for Project Kessel
320
338
  test_files: []