kubemq 1.0.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 +7 -0
- data/CHANGELOG.md +30 -0
- data/LICENSE +201 -0
- data/README.md +237 -0
- data/lib/kubemq/base_client.rb +180 -0
- data/lib/kubemq/cancellation_token.rb +63 -0
- data/lib/kubemq/channel_info.rb +84 -0
- data/lib/kubemq/configuration.rb +247 -0
- data/lib/kubemq/cq/client.rb +446 -0
- data/lib/kubemq/cq/command_message.rb +59 -0
- data/lib/kubemq/cq/command_received.rb +52 -0
- data/lib/kubemq/cq/command_response.rb +44 -0
- data/lib/kubemq/cq/command_response_message.rb +58 -0
- data/lib/kubemq/cq/commands_subscription.rb +45 -0
- data/lib/kubemq/cq/queries_subscription.rb +45 -0
- data/lib/kubemq/cq/query_message.rb +70 -0
- data/lib/kubemq/cq/query_received.rb +52 -0
- data/lib/kubemq/cq/query_response.rb +59 -0
- data/lib/kubemq/cq/query_response_message.rb +67 -0
- data/lib/kubemq/error_codes.rb +181 -0
- data/lib/kubemq/errors/error_mapper.rb +134 -0
- data/lib/kubemq/errors.rb +276 -0
- data/lib/kubemq/interceptors/auth_interceptor.rb +78 -0
- data/lib/kubemq/interceptors/error_mapping_interceptor.rb +75 -0
- data/lib/kubemq/interceptors/metrics_interceptor.rb +95 -0
- data/lib/kubemq/interceptors/retry_interceptor.rb +119 -0
- data/lib/kubemq/proto/kubemq_pb.rb +43 -0
- data/lib/kubemq/proto/kubemq_services_pb.rb +35 -0
- data/lib/kubemq/pubsub/client.rb +475 -0
- data/lib/kubemq/pubsub/event_message.rb +52 -0
- data/lib/kubemq/pubsub/event_received.rb +48 -0
- data/lib/kubemq/pubsub/event_send_result.rb +31 -0
- data/lib/kubemq/pubsub/event_sender.rb +112 -0
- data/lib/kubemq/pubsub/event_store_message.rb +53 -0
- data/lib/kubemq/pubsub/event_store_received.rb +47 -0
- data/lib/kubemq/pubsub/event_store_result.rb +33 -0
- data/lib/kubemq/pubsub/event_store_sender.rb +164 -0
- data/lib/kubemq/pubsub/events_store_subscription.rb +81 -0
- data/lib/kubemq/pubsub/events_subscription.rb +43 -0
- data/lib/kubemq/queues/client.rb +366 -0
- data/lib/kubemq/queues/downstream_receiver.rb +247 -0
- data/lib/kubemq/queues/queue_message.rb +99 -0
- data/lib/kubemq/queues/queue_message_received.rb +148 -0
- data/lib/kubemq/queues/queue_poll_request.rb +77 -0
- data/lib/kubemq/queues/queue_poll_response.rb +138 -0
- data/lib/kubemq/queues/queue_send_result.rb +49 -0
- data/lib/kubemq/queues/upstream_sender.rb +180 -0
- data/lib/kubemq/server_info.rb +57 -0
- data/lib/kubemq/subscription.rb +98 -0
- data/lib/kubemq/telemetry/otel.rb +64 -0
- data/lib/kubemq/telemetry/semconv.rb +51 -0
- data/lib/kubemq/transport/channel_manager.rb +212 -0
- data/lib/kubemq/transport/converter.rb +287 -0
- data/lib/kubemq/transport/grpc_transport.rb +411 -0
- data/lib/kubemq/transport/message_buffer.rb +105 -0
- data/lib/kubemq/transport/reconnect_manager.rb +111 -0
- data/lib/kubemq/transport/state_machine.rb +150 -0
- data/lib/kubemq/types.rb +80 -0
- data/lib/kubemq/validation/validator.rb +216 -0
- data/lib/kubemq/version.rb +6 -0
- data/lib/kubemq.rb +118 -0
- metadata +138 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KubeMQ
|
|
4
|
+
# Thread-safe cooperative cancellation token for stopping subscriptions.
|
|
5
|
+
#
|
|
6
|
+
# Create a token, pass it to a +subscribe_to_*+ method, and call {#cancel}
|
|
7
|
+
# to gracefully stop the subscription. Multiple threads may safely check
|
|
8
|
+
# {#cancelled?} or {#wait} concurrently.
|
|
9
|
+
#
|
|
10
|
+
# @note This class is thread-safe. All state access is synchronized via
|
|
11
|
+
# a +Mutex+ and +ConditionVariable+.
|
|
12
|
+
#
|
|
13
|
+
# @example Cancel a subscription after 10 seconds
|
|
14
|
+
# token = KubeMQ::CancellationToken.new
|
|
15
|
+
# subscription = client.subscribe_to_events(sub, cancellation_token: token) do |event|
|
|
16
|
+
# process(event)
|
|
17
|
+
# end
|
|
18
|
+
# sleep 10
|
|
19
|
+
# token.cancel
|
|
20
|
+
# subscription.wait(5)
|
|
21
|
+
#
|
|
22
|
+
# @see Subscription
|
|
23
|
+
class CancellationToken
|
|
24
|
+
# Creates a new uncancelled token.
|
|
25
|
+
def initialize
|
|
26
|
+
@mutex = Mutex.new
|
|
27
|
+
@condition = ConditionVariable.new
|
|
28
|
+
@cancelled = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Signals cancellation and wakes all threads waiting on this token.
|
|
32
|
+
#
|
|
33
|
+
# This method is idempotent — calling it multiple times is safe.
|
|
34
|
+
#
|
|
35
|
+
# @return [void]
|
|
36
|
+
def cancel
|
|
37
|
+
@mutex.synchronize do
|
|
38
|
+
@cancelled = true
|
|
39
|
+
@condition.broadcast
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns whether cancellation has been signalled.
|
|
44
|
+
#
|
|
45
|
+
# @return [Boolean] +true+ if {#cancel} has been called
|
|
46
|
+
def cancelled?
|
|
47
|
+
@mutex.synchronize { @cancelled }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Blocks the calling thread until the token is cancelled or the timeout elapses.
|
|
51
|
+
#
|
|
52
|
+
# @param timeout [Numeric, nil] maximum seconds to wait (+nil+ for indefinite)
|
|
53
|
+
# @return [Boolean] +true+ if cancelled, +false+ if timed out
|
|
54
|
+
def wait(timeout: nil)
|
|
55
|
+
@mutex.synchronize do
|
|
56
|
+
return true if @cancelled
|
|
57
|
+
|
|
58
|
+
@condition.wait(@mutex, timeout)
|
|
59
|
+
@cancelled
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KubeMQ
|
|
4
|
+
# Metadata about a KubeMQ channel returned by {BaseClient#list_channels}.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# channels = client.list_channels(channel_type: KubeMQ::ChannelType::EVENTS)
|
|
8
|
+
# channels.each do |ch|
|
|
9
|
+
# puts "#{ch.name} active=#{ch.active?} in=#{ch.incoming} out=#{ch.outgoing}"
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# @see BaseClient#list_channels
|
|
13
|
+
# @see ChannelType
|
|
14
|
+
class ChannelInfo
|
|
15
|
+
# @!attribute [r] name
|
|
16
|
+
# @return [String] channel name
|
|
17
|
+
# @!attribute [r] type
|
|
18
|
+
# @return [String] channel type (one of {ChannelType} constants)
|
|
19
|
+
# @!attribute [r] last_activity
|
|
20
|
+
# @return [Integer] Unix timestamp of the last activity on this channel
|
|
21
|
+
# @!attribute [r] is_active
|
|
22
|
+
# @return [Boolean] whether the channel currently has active subscribers
|
|
23
|
+
# @!attribute [r] incoming
|
|
24
|
+
# @return [Integer] total number of incoming messages
|
|
25
|
+
# @!attribute [r] outgoing
|
|
26
|
+
# @return [Integer] total number of outgoing messages
|
|
27
|
+
attr_reader :name, :type, :last_activity, :is_active, :incoming, :outgoing
|
|
28
|
+
|
|
29
|
+
# Creates a new ChannelInfo instance.
|
|
30
|
+
#
|
|
31
|
+
# @param name [String] channel name
|
|
32
|
+
# @param type [String] channel type (one of {ChannelType} constants)
|
|
33
|
+
# @param last_activity [Integer] Unix timestamp of last activity (default: +0+)
|
|
34
|
+
# @param is_active [Boolean] whether the channel has active subscribers (default: +false+)
|
|
35
|
+
# @param incoming [Integer] total incoming message count (default: +0+)
|
|
36
|
+
# @param outgoing [Integer] total outgoing message count (default: +0+)
|
|
37
|
+
def initialize(name:, type:, last_activity: 0, is_active: false, incoming: 0, outgoing: 0)
|
|
38
|
+
@name = name
|
|
39
|
+
@type = type
|
|
40
|
+
@last_activity = last_activity
|
|
41
|
+
@is_active = is_active
|
|
42
|
+
@incoming = incoming
|
|
43
|
+
@outgoing = outgoing
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns whether the channel currently has active subscribers.
|
|
47
|
+
#
|
|
48
|
+
# @return [Boolean]
|
|
49
|
+
def active?
|
|
50
|
+
@is_active
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Constructs a {ChannelInfo} from a parsed JSON hash.
|
|
54
|
+
#
|
|
55
|
+
# Accepts both camelCase (from the broker REST API) and snake_case keys.
|
|
56
|
+
#
|
|
57
|
+
# @api private
|
|
58
|
+
# @param hash [Hash] parsed JSON hash with channel metadata
|
|
59
|
+
# @return [ChannelInfo]
|
|
60
|
+
def self.from_json(hash)
|
|
61
|
+
is_active_val = if hash.key?('isActive') then hash['isActive']
|
|
62
|
+
elsif hash.key?('is_active') then hash['is_active']
|
|
63
|
+
elsif hash.key?(:is_active) then hash[:is_active]
|
|
64
|
+
else false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
new(
|
|
68
|
+
name: hash['name'] || hash[:name] || '',
|
|
69
|
+
type: hash['type'] || hash[:type] || '',
|
|
70
|
+
last_activity: hash['lastActivity'] || hash['last_activity'] || hash[:last_activity] || 0,
|
|
71
|
+
is_active: is_active_val,
|
|
72
|
+
incoming: hash['incoming'] || hash[:incoming] || 0,
|
|
73
|
+
outgoing: hash['outgoing'] || hash[:outgoing] || 0
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns a human-readable summary of the channel information.
|
|
78
|
+
#
|
|
79
|
+
# @return [String]
|
|
80
|
+
def to_s
|
|
81
|
+
"ChannelInfo(name=#{@name}, type=#{@type}, active=#{active?})"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KubeMQ
|
|
4
|
+
# TLS/SSL configuration for secure broker connections.
|
|
5
|
+
#
|
|
6
|
+
# Enable mutual TLS (mTLS) by providing both +cert_file+ and +key_file+.
|
|
7
|
+
# For server-only TLS, set +ca_file+ alone or use +insecure_skip_verify+
|
|
8
|
+
# for self-signed certificates in development.
|
|
9
|
+
#
|
|
10
|
+
# @example mTLS configuration
|
|
11
|
+
# tls = KubeMQ::TLSConfig.new(
|
|
12
|
+
# enabled: true,
|
|
13
|
+
# cert_file: "/certs/client.pem",
|
|
14
|
+
# key_file: "/certs/client-key.pem",
|
|
15
|
+
# ca_file: "/certs/ca.pem"
|
|
16
|
+
# )
|
|
17
|
+
#
|
|
18
|
+
# @see Configuration
|
|
19
|
+
class TLSConfig
|
|
20
|
+
# @!attribute [rw] enabled
|
|
21
|
+
# @return [Boolean] whether TLS is enabled (default: +false+)
|
|
22
|
+
# @!attribute [rw] cert_file
|
|
23
|
+
# @return [String, nil] path to client certificate PEM file
|
|
24
|
+
# @!attribute [rw] key_file
|
|
25
|
+
# @return [String, nil] path to client private key PEM file
|
|
26
|
+
# @!attribute [rw] ca_file
|
|
27
|
+
# @return [String, nil] path to CA certificate PEM file
|
|
28
|
+
# @!attribute [rw] insecure_skip_verify
|
|
29
|
+
# @return [Boolean] skip server certificate verification (default: +false+)
|
|
30
|
+
attr_accessor :enabled, :cert_file, :key_file, :ca_file, :insecure_skip_verify
|
|
31
|
+
|
|
32
|
+
# Creates a new TLS configuration.
|
|
33
|
+
#
|
|
34
|
+
# @param enabled [Boolean] whether TLS is enabled (default: +false+)
|
|
35
|
+
# @param cert_file [String, nil] path to client certificate PEM file
|
|
36
|
+
# @param key_file [String, nil] path to client private key PEM file
|
|
37
|
+
# @param ca_file [String, nil] path to CA certificate PEM file
|
|
38
|
+
# @param insecure_skip_verify [Boolean] skip server certificate verification (default: +false+)
|
|
39
|
+
def initialize(enabled: false, cert_file: nil, key_file: nil, ca_file: nil,
|
|
40
|
+
insecure_skip_verify: false)
|
|
41
|
+
@enabled = enabled
|
|
42
|
+
@cert_file = cert_file
|
|
43
|
+
@key_file = key_file
|
|
44
|
+
@ca_file = ca_file
|
|
45
|
+
@insecure_skip_verify = insecure_skip_verify
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# gRPC keepalive configuration for long-lived connections.
|
|
50
|
+
#
|
|
51
|
+
# When enabled (default), periodic pings are sent to detect dead
|
|
52
|
+
# connections and keep firewalls/load-balancers from closing idle
|
|
53
|
+
# connections.
|
|
54
|
+
#
|
|
55
|
+
# @example Aggressive keepalive for flaky networks
|
|
56
|
+
# keepalive = KubeMQ::KeepAliveConfig.new(
|
|
57
|
+
# ping_interval_seconds: 5,
|
|
58
|
+
# ping_timeout_seconds: 3
|
|
59
|
+
# )
|
|
60
|
+
#
|
|
61
|
+
# @see Configuration
|
|
62
|
+
class KeepAliveConfig
|
|
63
|
+
# @!attribute [rw] enabled
|
|
64
|
+
# @return [Boolean] whether gRPC keepalive is enabled (default: +true+)
|
|
65
|
+
# @!attribute [rw] ping_interval_seconds
|
|
66
|
+
# @return [Integer] seconds between keepalive pings (default: +10+)
|
|
67
|
+
# @!attribute [rw] ping_timeout_seconds
|
|
68
|
+
# @return [Integer] seconds to wait for a ping response before considering
|
|
69
|
+
# the connection dead (default: +5+)
|
|
70
|
+
# @!attribute [rw] permit_without_calls
|
|
71
|
+
# @return [Boolean] send pings even when there are no active RPCs (default: +true+)
|
|
72
|
+
attr_accessor :enabled, :ping_interval_seconds, :ping_timeout_seconds, :permit_without_calls
|
|
73
|
+
|
|
74
|
+
# Creates a new keepalive configuration.
|
|
75
|
+
#
|
|
76
|
+
# @param enabled [Boolean] whether gRPC keepalive is enabled (default: +true+)
|
|
77
|
+
# @param ping_interval_seconds [Integer] seconds between keepalive pings (default: +10+)
|
|
78
|
+
# @param ping_timeout_seconds [Integer] seconds to wait for a ping response (default: +5+)
|
|
79
|
+
# @param permit_without_calls [Boolean] send pings without active RPCs (default: +true+)
|
|
80
|
+
def initialize(enabled: true, ping_interval_seconds: 10, ping_timeout_seconds: 5,
|
|
81
|
+
permit_without_calls: true)
|
|
82
|
+
@enabled = enabled
|
|
83
|
+
@ping_interval_seconds = Integer(ping_interval_seconds)
|
|
84
|
+
@ping_timeout_seconds = Integer(ping_timeout_seconds)
|
|
85
|
+
@permit_without_calls = permit_without_calls
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Reconnection policy with exponential backoff and jitter.
|
|
90
|
+
#
|
|
91
|
+
# When enabled (default), the transport automatically reconnects after
|
|
92
|
+
# transient failures. The delay between attempts is computed as:
|
|
93
|
+
#
|
|
94
|
+
# delay = min(base_interval * multiplier^(attempt-1), max_delay) +/- jitter
|
|
95
|
+
#
|
|
96
|
+
# @example Custom reconnect policy
|
|
97
|
+
# policy = KubeMQ::ReconnectPolicy.new(
|
|
98
|
+
# base_interval: 2.0,
|
|
99
|
+
# max_delay: 60.0,
|
|
100
|
+
# max_attempts: 10
|
|
101
|
+
# )
|
|
102
|
+
#
|
|
103
|
+
# @see Configuration
|
|
104
|
+
class ReconnectPolicy
|
|
105
|
+
# @!attribute [rw] enabled
|
|
106
|
+
# @return [Boolean] whether auto-reconnect is enabled (default: +true+)
|
|
107
|
+
# @!attribute [rw] base_interval
|
|
108
|
+
# @return [Float] initial delay between reconnect attempts in seconds (default: +1.0+)
|
|
109
|
+
# @!attribute [rw] multiplier
|
|
110
|
+
# @return [Float] multiplier applied to delay after each attempt (default: +2.0+)
|
|
111
|
+
# @!attribute [rw] max_delay
|
|
112
|
+
# @return [Float] maximum delay cap in seconds (default: +30.0+)
|
|
113
|
+
# @!attribute [rw] jitter_percent
|
|
114
|
+
# @return [Float] jitter as a fraction of the computed delay (default: +0.25+)
|
|
115
|
+
# @!attribute [rw] max_attempts
|
|
116
|
+
# @return [Integer] maximum reconnect attempts; +0+ means unlimited (default: +0+)
|
|
117
|
+
attr_accessor :enabled, :base_interval, :multiplier, :max_delay, :jitter_percent, :max_attempts
|
|
118
|
+
|
|
119
|
+
# Creates a new reconnect policy.
|
|
120
|
+
#
|
|
121
|
+
# @param enabled [Boolean] whether auto-reconnect is enabled (default: +true+)
|
|
122
|
+
# @param base_interval [Float] initial delay in seconds (default: +1.0+)
|
|
123
|
+
# @param multiplier [Float] backoff multiplier (default: +2.0+)
|
|
124
|
+
# @param max_delay [Float] maximum delay cap in seconds (default: +30.0+)
|
|
125
|
+
# @param jitter_percent [Float] jitter fraction of the computed delay (default: +0.25+)
|
|
126
|
+
# @param max_attempts [Integer] maximum attempts; +0+ for unlimited (default: +0+)
|
|
127
|
+
def initialize(enabled: true, base_interval: 1.0, multiplier: 2.0, max_delay: 30.0,
|
|
128
|
+
jitter_percent: 0.25, max_attempts: 0)
|
|
129
|
+
@enabled = enabled
|
|
130
|
+
@base_interval = Float(base_interval)
|
|
131
|
+
@multiplier = Float(multiplier)
|
|
132
|
+
@max_delay = Float(max_delay)
|
|
133
|
+
@jitter_percent = Float(jitter_percent)
|
|
134
|
+
@max_attempts = Integer(max_attempts)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Central configuration for KubeMQ client connections.
|
|
139
|
+
#
|
|
140
|
+
# Resolved in precedence order:
|
|
141
|
+
# 1. Constructor keyword arguments
|
|
142
|
+
# 2. Global {KubeMQ.configure} block
|
|
143
|
+
# 3. Environment variables (+KUBEMQ_ADDRESS+, +KUBEMQ_AUTH_TOKEN+)
|
|
144
|
+
# 4. Built-in defaults
|
|
145
|
+
#
|
|
146
|
+
# @example Full configuration
|
|
147
|
+
# config = KubeMQ::Configuration.new(
|
|
148
|
+
# address: "broker.example.com:50000",
|
|
149
|
+
# client_id: "order-service",
|
|
150
|
+
# auth_token: ENV["KUBEMQ_AUTH_TOKEN"],
|
|
151
|
+
# tls: KubeMQ::TLSConfig.new(enabled: true, ca_file: "/certs/ca.pem"),
|
|
152
|
+
# log_level: :info
|
|
153
|
+
# )
|
|
154
|
+
# client = KubeMQ::PubSubClient.new(config: config)
|
|
155
|
+
#
|
|
156
|
+
# @see TLSConfig
|
|
157
|
+
# @see KeepAliveConfig
|
|
158
|
+
# @see ReconnectPolicy
|
|
159
|
+
class Configuration
|
|
160
|
+
# @!attribute [rw] address
|
|
161
|
+
# @return [String] broker host:port (default: +ENV["KUBEMQ_ADDRESS"]+ or +"localhost:50000"+)
|
|
162
|
+
# @!attribute [rw] client_id
|
|
163
|
+
# @return [String] unique client identifier (default: auto-generated)
|
|
164
|
+
# @!attribute [rw] auth_token
|
|
165
|
+
# @return [String, nil] bearer authentication token
|
|
166
|
+
# @!attribute [rw] tls
|
|
167
|
+
# @return [TLSConfig] TLS/mTLS configuration
|
|
168
|
+
# @!attribute [rw] keepalive
|
|
169
|
+
# @return [KeepAliveConfig] gRPC keepalive settings
|
|
170
|
+
# @!attribute [rw] reconnect_policy
|
|
171
|
+
# @return [ReconnectPolicy] auto-reconnect policy
|
|
172
|
+
# @!attribute [rw] max_send_size
|
|
173
|
+
# @return [Integer] maximum outbound message size in bytes (default: 104_857_600 / 100 MB)
|
|
174
|
+
# @!attribute [rw] max_receive_size
|
|
175
|
+
# @return [Integer] maximum inbound message size in bytes (default: 104_857_600 / 100 MB)
|
|
176
|
+
# @!attribute [rw] log_level
|
|
177
|
+
# @return [Symbol] log level — +:debug+, +:info+, +:warn+, +:error+ (default: +:warn+)
|
|
178
|
+
# @!attribute [rw] default_timeout
|
|
179
|
+
# @return [Integer] default gRPC deadline in seconds (default: +30+)
|
|
180
|
+
attr_accessor :address, :client_id, :auth_token,
|
|
181
|
+
:tls, :keepalive, :reconnect_policy,
|
|
182
|
+
:max_send_size, :max_receive_size,
|
|
183
|
+
:log_level, :default_timeout
|
|
184
|
+
|
|
185
|
+
# Creates a new configuration with the given options.
|
|
186
|
+
#
|
|
187
|
+
# Unspecified options fall back to environment variables and then to
|
|
188
|
+
# built-in defaults.
|
|
189
|
+
#
|
|
190
|
+
# @param address [String, nil] broker host:port (default: +ENV["KUBEMQ_ADDRESS"]+ or +"localhost:50000"+)
|
|
191
|
+
# @param client_id [String, nil] unique client identifier (default: auto-generated)
|
|
192
|
+
# @param auth_token [String, nil] bearer authentication token
|
|
193
|
+
# @param tls [TLSConfig, nil] TLS/mTLS configuration (default: TLS disabled)
|
|
194
|
+
# @param keepalive [KeepAliveConfig, nil] gRPC keepalive settings (default: enabled)
|
|
195
|
+
# @param reconnect_policy [ReconnectPolicy, nil] auto-reconnect policy (default: enabled)
|
|
196
|
+
# @param max_send_size [Integer] maximum outbound message size in bytes (default: 100 MB)
|
|
197
|
+
# @param max_receive_size [Integer] maximum inbound message size in bytes (default: 100 MB)
|
|
198
|
+
# @param log_level [Symbol] log level — +:debug+, +:info+, +:warn+, +:error+ (default: +:warn+)
|
|
199
|
+
# @param default_timeout [Integer] default gRPC deadline in seconds (default: +30+)
|
|
200
|
+
def initialize(address: nil, client_id: nil, auth_token: nil,
|
|
201
|
+
tls: nil, keepalive: nil, reconnect_policy: nil,
|
|
202
|
+
max_send_size: 104_857_600, max_receive_size: 104_857_600,
|
|
203
|
+
log_level: :warn, default_timeout: 30)
|
|
204
|
+
@address = address || ENV.fetch('KUBEMQ_ADDRESS', 'localhost:50000')
|
|
205
|
+
@client_id = client_id || "kubemq-ruby-#{SecureRandom.hex(4)}"
|
|
206
|
+
@auth_token = auth_token || ENV.fetch('KUBEMQ_AUTH_TOKEN', nil)
|
|
207
|
+
@tls = tls || TLSConfig.new
|
|
208
|
+
@keepalive = keepalive || KeepAliveConfig.new
|
|
209
|
+
@reconnect_policy = reconnect_policy || ReconnectPolicy.new
|
|
210
|
+
@max_send_size = max_send_size
|
|
211
|
+
@max_receive_size = max_receive_size
|
|
212
|
+
@log_level = log_level
|
|
213
|
+
@default_timeout = Integer(default_timeout)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Returns a developer-friendly string with the auth token redacted.
|
|
217
|
+
#
|
|
218
|
+
# @return [String]
|
|
219
|
+
def inspect
|
|
220
|
+
token_display = @auth_token ? '[REDACTED]' : 'nil'
|
|
221
|
+
"#<#{self.class} address=#{@address.inspect} client_id=#{@client_id.inspect} auth_token=#{token_display}>"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Validates this configuration and raises on invalid settings.
|
|
225
|
+
#
|
|
226
|
+
# Checks that +address+ is present and that TLS cert/key files are
|
|
227
|
+
# provided as a pair when TLS is enabled.
|
|
228
|
+
#
|
|
229
|
+
# @return [Boolean] +true+ if the configuration is valid
|
|
230
|
+
#
|
|
231
|
+
# @raise [ConfigurationError] if +address+ is nil or empty
|
|
232
|
+
# @raise [ConfigurationError] if TLS +cert_file+ is set without +key_file+ or vice versa
|
|
233
|
+
# rubocop:disable Naming/PredicateMethod -- bang API: raises on invalid config or returns true
|
|
234
|
+
def validate!
|
|
235
|
+
raise ConfigurationError, 'address is required' if @address.nil? || @address.empty?
|
|
236
|
+
if @tls.enabled && @tls.cert_file && !@tls.key_file
|
|
237
|
+
raise ConfigurationError, 'TLS key_file is required when cert_file is set'
|
|
238
|
+
end
|
|
239
|
+
if @tls.enabled && @tls.key_file && !@tls.cert_file
|
|
240
|
+
raise ConfigurationError, 'TLS cert_file is required when key_file is set'
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
true
|
|
244
|
+
end
|
|
245
|
+
# rubocop:enable Naming/PredicateMethod
|
|
246
|
+
end
|
|
247
|
+
end
|