async-grpc-xds 0.2.0 → 0.4.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
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +363 -0
- data/context/index.yaml +14 -0
- data/lib/async/grpc/xds/cluster.rb +7 -5
- data/lib/async/grpc/xds/cluster_discovery_service.rb +56 -0
- data/lib/async/grpc/xds/config_source.rb +46 -0
- data/lib/async/grpc/xds/control_plane.rb +3 -3
- data/lib/async/grpc/xds/discovery_service.rb +69 -0
- data/lib/async/grpc/xds/endpoint_discovery_service.rb +56 -0
- data/lib/async/grpc/xds/http_health_check.rb +48 -0
- data/lib/async/grpc/xds/server.rb +8 -2
- data/lib/async/grpc/xds/service.rb +5 -103
- data/lib/async/grpc/xds/stream.rb +102 -0
- data/lib/async/grpc/xds/version.rb +1 -1
- data/lib/async/grpc/xds.rb +6 -0
- data/proto/readme.md +8 -24
- data/readme.md +13 -6
- data/releases.md +9 -0
- data.tar.gz.sig +0 -0
- metadata +11 -14
- metadata.gz.sig +0 -0
- data/fixtures/async/grpc/test_interface.rb +0 -79
- data/fixtures/async/grpc/test_message.rb +0 -56
- data/xds/Dockerfile.backend +0 -24
- data/xds/Dockerfile.control-plane +0 -22
- data/xds/backend_server.rb +0 -68
- data/xds/docker-compose.yaml +0 -89
- data/xds/go.mod +0 -22
- data/xds/go.sum +0 -82
- data/xds/readme.md +0 -95
- data/xds/test/async/grpc/xds/client.rb +0 -294
- data/xds/test/async/grpc/xds/control_plane.rb +0 -93
- data/xds/test_server.go +0 -355
- data/xds/update_protos.sh +0 -123
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "google/protobuf/duration_pb"
|
|
7
|
+
require "google/protobuf/wrappers_pb"
|
|
8
|
+
|
|
9
|
+
require "envoy/config/core/v3/health_check_pb"
|
|
10
|
+
|
|
11
|
+
module Async
|
|
12
|
+
module GRPC
|
|
13
|
+
module XDS
|
|
14
|
+
# Builds Envoy HTTP active health checks.
|
|
15
|
+
module HTTPHealthCheck
|
|
16
|
+
extend self
|
|
17
|
+
|
|
18
|
+
# Build an HTTP active health check.
|
|
19
|
+
# @parameter path [String] The request path used to check each endpoint.
|
|
20
|
+
# @parameter interval [Numeric] The interval between checks in seconds.
|
|
21
|
+
# @parameter timeout [Numeric] The check timeout in seconds.
|
|
22
|
+
# @parameter unhealthy_threshold [Integer] The consecutive failures required to mark an endpoint unhealthy.
|
|
23
|
+
# @parameter healthy_threshold [Integer] The consecutive successes required to mark an endpoint healthy.
|
|
24
|
+
# @returns [Envoy::Config::Core::V3::HealthCheck] The generated health check.
|
|
25
|
+
def build(path, interval: 1, timeout: 1, unhealthy_threshold: 2, healthy_threshold: 1)
|
|
26
|
+
Envoy::Config::Core::V3::HealthCheck.new(
|
|
27
|
+
timeout: duration(timeout),
|
|
28
|
+
interval: duration(interval),
|
|
29
|
+
unhealthy_threshold: Google::Protobuf::UInt32Value.new(value: Integer(unhealthy_threshold)),
|
|
30
|
+
healthy_threshold: Google::Protobuf::UInt32Value.new(value: Integer(healthy_threshold)),
|
|
31
|
+
http_health_check: Envoy::Config::Core::V3::HealthCheck::HttpHealthCheck.new(path: path.to_s)
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def duration(value)
|
|
38
|
+
seconds = value.to_i
|
|
39
|
+
|
|
40
|
+
Google::Protobuf::Duration.new(
|
|
41
|
+
seconds: seconds,
|
|
42
|
+
nanos: ((value.to_f - seconds) * 1_000_000_000).to_i
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -16,16 +16,22 @@ module Async
|
|
|
16
16
|
class Server
|
|
17
17
|
# Initialize an xDS server.
|
|
18
18
|
# @parameter control_plane [ControlPlane] The control plane to serve.
|
|
19
|
+
# @parameter services [Array(Class)] The discovery service classes to register.
|
|
19
20
|
# @parameter options [Hash] Default options forwarded to `Async::HTTP::Server`.
|
|
20
|
-
def initialize(control_plane = ControlPlane.new, **options)
|
|
21
|
+
def initialize(control_plane = ControlPlane.new, services: [Service], **options)
|
|
21
22
|
@control_plane = control_plane
|
|
22
23
|
@dispatcher = Async::GRPC::Dispatcher.new
|
|
23
|
-
@
|
|
24
|
+
@services = services.map do |service|
|
|
25
|
+
service.new(@control_plane).tap do |instance|
|
|
26
|
+
@dispatcher.register(instance)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
24
29
|
@options = options
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
attr :control_plane
|
|
28
33
|
attr :dispatcher
|
|
34
|
+
attr :services
|
|
29
35
|
|
|
30
36
|
# Run the xDS server on an endpoint.
|
|
31
37
|
# @parameter endpoint [Async::HTTP::Endpoint] The endpoint to bind.
|
|
@@ -3,29 +3,21 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require "async"
|
|
7
|
-
require "async/queue"
|
|
8
|
-
require "async/grpc/service"
|
|
9
|
-
require "protocol/grpc/error"
|
|
10
|
-
require "protocol/grpc/status"
|
|
11
|
-
require "set"
|
|
12
|
-
|
|
13
6
|
require "envoy/service/discovery/v3/aggregated_discovery_service"
|
|
14
7
|
|
|
15
|
-
require_relative "
|
|
8
|
+
require_relative "discovery_service"
|
|
16
9
|
|
|
17
10
|
module Async
|
|
18
11
|
module GRPC
|
|
19
12
|
module XDS
|
|
20
13
|
# Serves Envoy Aggregated Discovery Service requests from a {ControlPlane}.
|
|
21
|
-
class Service <
|
|
14
|
+
class Service < DiscoveryService
|
|
22
15
|
SERVICE_NAME = "envoy.service.discovery.v3.AggregatedDiscoveryService"
|
|
23
16
|
|
|
24
17
|
# Initialize an Aggregated Discovery Service.
|
|
25
18
|
# @parameter control_plane [ControlPlane] The control plane that provides resources.
|
|
26
19
|
def initialize(control_plane)
|
|
27
|
-
super(Envoy::Service::Discovery::V3::AggregatedDiscoveryService, SERVICE_NAME)
|
|
28
|
-
@control_plane = control_plane
|
|
20
|
+
super(Envoy::Service::Discovery::V3::AggregatedDiscoveryService, SERVICE_NAME, control_plane)
|
|
29
21
|
end
|
|
30
22
|
|
|
31
23
|
# Serve a state-of-the-world Aggregated Discovery Service stream.
|
|
@@ -34,25 +26,7 @@ module Async
|
|
|
34
26
|
# @parameter call [Object] The gRPC call context.
|
|
35
27
|
# @asynchronous
|
|
36
28
|
def stream_aggregated_resources(input, output, call)
|
|
37
|
-
|
|
38
|
-
@control_plane.register_stream(stream)
|
|
39
|
-
|
|
40
|
-
reader = Async do
|
|
41
|
-
input.each do |request|
|
|
42
|
-
stream.request(request)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
writer = Async do
|
|
47
|
-
stream.run
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
reader.wait
|
|
51
|
-
ensure
|
|
52
|
-
stream&.close
|
|
53
|
-
reader&.stop
|
|
54
|
-
writer&.stop
|
|
55
|
-
@control_plane.remove_stream(stream) if stream
|
|
29
|
+
stream_resources(input, output)
|
|
56
30
|
end
|
|
57
31
|
|
|
58
32
|
# Reject a delta Aggregated Discovery Service stream, which is not supported.
|
|
@@ -61,79 +35,7 @@ module Async
|
|
|
61
35
|
# @parameter call [Object] The gRPC call context.
|
|
62
36
|
# @raises [Protocol::GRPC::Error] Always raised because delta xDS is not implemented.
|
|
63
37
|
def delta_aggregated_resources(input, output, call)
|
|
64
|
-
|
|
65
|
-
Protocol::GRPC::Status::UNIMPLEMENTED,
|
|
66
|
-
"Delta xDS is not implemented."
|
|
67
|
-
)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Represents one ADS stream and its subscribed resources.
|
|
71
|
-
class Stream
|
|
72
|
-
# Initialize an ADS stream.
|
|
73
|
-
# @parameter control_plane [ControlPlane] The control plane that provides resources.
|
|
74
|
-
# @parameter output [Interface(:write)] The discovery response stream.
|
|
75
|
-
def initialize(control_plane, output)
|
|
76
|
-
@control_plane = control_plane
|
|
77
|
-
@output = output
|
|
78
|
-
@subscriptions = Hash.new{|hash, type_url| hash[type_url] = Set.new}
|
|
79
|
-
@versions = {}
|
|
80
|
-
@queue = Async::Queue.new
|
|
81
|
-
@closed = false
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Process a discovery request and update the stream's subscriptions.
|
|
85
|
-
# @parameter request [Envoy::Service::Discovery::V3::DiscoveryRequest] The discovery request.
|
|
86
|
-
def request(request)
|
|
87
|
-
return if request.type_url.nil? || request.type_url.empty?
|
|
88
|
-
|
|
89
|
-
if request.error_detail
|
|
90
|
-
Console.warn(self, "Received xDS NACK.", type_url: request.type_url, error_detail: request.error_detail)
|
|
91
|
-
return
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
if request.resource_names.any?
|
|
95
|
-
@subscriptions[request.type_url].merge(request.resource_names)
|
|
96
|
-
else
|
|
97
|
-
@subscriptions[request.type_url]
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
@queue << request.type_url
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Schedule a resource type for delivery after it changes.
|
|
104
|
-
# @parameter type_url [String] The changed xDS resource type URL.
|
|
105
|
-
def changed(type_url)
|
|
106
|
-
@queue << type_url unless @closed
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Deliver scheduled resource updates until the stream closes.
|
|
110
|
-
# @asynchronous
|
|
111
|
-
def run
|
|
112
|
-
until @closed
|
|
113
|
-
type_url = @queue.dequeue
|
|
114
|
-
flush(type_url)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Deliver the latest resource version for a subscribed type.
|
|
119
|
-
# @parameter type_url [String] The xDS resource type URL.
|
|
120
|
-
def flush(type_url)
|
|
121
|
-
names = @subscriptions[type_url]
|
|
122
|
-
return unless names
|
|
123
|
-
|
|
124
|
-
version = @control_plane.version(type_url)
|
|
125
|
-
return if @versions[type_url] == version
|
|
126
|
-
|
|
127
|
-
response = @control_plane.response(type_url, names)
|
|
128
|
-
@output.write(response)
|
|
129
|
-
@versions[type_url] = version
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Close the stream and stop waiting for changes.
|
|
133
|
-
def close
|
|
134
|
-
@closed = true
|
|
135
|
-
@queue.close
|
|
136
|
-
end
|
|
38
|
+
delta_resources
|
|
137
39
|
end
|
|
138
40
|
end
|
|
139
41
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "async/queue"
|
|
7
|
+
require "protocol/grpc/error"
|
|
8
|
+
require "protocol/grpc/status"
|
|
9
|
+
require "set"
|
|
10
|
+
|
|
11
|
+
module Async
|
|
12
|
+
module GRPC
|
|
13
|
+
module XDS
|
|
14
|
+
# Represents one discovery stream and its subscribed resources.
|
|
15
|
+
class Stream
|
|
16
|
+
# Initialize a discovery stream.
|
|
17
|
+
# @parameter control_plane [ControlPlane] The control plane that provides resources.
|
|
18
|
+
# @parameter output [Interface(:write)] The discovery response stream.
|
|
19
|
+
# @parameter resource_type [String | Nil] The fixed resource type, or `nil` for aggregated discovery.
|
|
20
|
+
def initialize(control_plane, output, resource_type: nil)
|
|
21
|
+
@control_plane = control_plane
|
|
22
|
+
@output = output
|
|
23
|
+
@resource_type = resource_type
|
|
24
|
+
@subscriptions = {}
|
|
25
|
+
@versions = {}
|
|
26
|
+
@queue = Async::Queue.new
|
|
27
|
+
@closed = false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Process a discovery request and update the stream's subscriptions.
|
|
31
|
+
# @parameter request [Envoy::Service::Discovery::V3::DiscoveryRequest] The discovery request.
|
|
32
|
+
def request(request)
|
|
33
|
+
type_url = request.type_url
|
|
34
|
+
|
|
35
|
+
if @resource_type
|
|
36
|
+
if type_url.nil? || type_url.empty?
|
|
37
|
+
type_url = @resource_type
|
|
38
|
+
elsif type_url != @resource_type
|
|
39
|
+
raise Protocol::GRPC::Error.new(
|
|
40
|
+
Protocol::GRPC::Status::INVALID_ARGUMENT,
|
|
41
|
+
"Expected resource type #{@resource_type.inspect}, but received #{type_url.inspect}."
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
elsif type_url.nil? || type_url.empty?
|
|
45
|
+
return
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if request.error_detail
|
|
49
|
+
Console.warn(self, "Received xDS NACK.", type_url: type_url, error_detail: request.error_detail)
|
|
50
|
+
return
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
names = Set.new(request.resource_names)
|
|
54
|
+
if @subscriptions[type_url] != names
|
|
55
|
+
@subscriptions[type_url] = names
|
|
56
|
+
@versions.delete(type_url)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@queue << type_url
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Schedule a resource type for delivery after it changes.
|
|
63
|
+
# @parameter type_url [String] The changed xDS resource type URL.
|
|
64
|
+
def changed(type_url)
|
|
65
|
+
return if @resource_type && type_url != @resource_type
|
|
66
|
+
return unless @subscriptions.key?(type_url)
|
|
67
|
+
|
|
68
|
+
@queue << type_url unless @closed
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Deliver scheduled resource updates until the stream closes.
|
|
72
|
+
# @asynchronous
|
|
73
|
+
def run
|
|
74
|
+
until @closed
|
|
75
|
+
type_url = @queue.dequeue
|
|
76
|
+
flush(type_url)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Deliver the latest resource version for a subscribed type.
|
|
81
|
+
# @parameter type_url [String] The xDS resource type URL.
|
|
82
|
+
def flush(type_url)
|
|
83
|
+
names = @subscriptions[type_url]
|
|
84
|
+
return unless names
|
|
85
|
+
|
|
86
|
+
version = @control_plane.version(type_url)
|
|
87
|
+
return if @versions[type_url] == version
|
|
88
|
+
|
|
89
|
+
response = @control_plane.response(type_url, names)
|
|
90
|
+
@output.write(response)
|
|
91
|
+
@versions[type_url] = version
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Close the stream and stop waiting for changes.
|
|
95
|
+
def close
|
|
96
|
+
@closed = true
|
|
97
|
+
@queue.close
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/async/grpc/xds.rb
CHANGED
|
@@ -13,11 +13,17 @@ require_relative "xds/health_checker"
|
|
|
13
13
|
require_relative "xds/load_balancer"
|
|
14
14
|
require_relative "xds/context"
|
|
15
15
|
require_relative "xds/client"
|
|
16
|
+
require_relative "xds/config_source"
|
|
16
17
|
require_relative "xds/cluster"
|
|
17
18
|
require_relative "xds/endpoint"
|
|
19
|
+
require_relative "xds/http_health_check"
|
|
18
20
|
require_relative "xds/client_side_weighted_round_robin"
|
|
19
21
|
require_relative "xds/control_plane"
|
|
22
|
+
require_relative "xds/stream"
|
|
23
|
+
require_relative "xds/discovery_service"
|
|
20
24
|
require_relative "xds/service"
|
|
25
|
+
require_relative "xds/cluster_discovery_service"
|
|
26
|
+
require_relative "xds/endpoint_discovery_service"
|
|
21
27
|
require_relative "xds/server"
|
|
22
28
|
|
|
23
29
|
module Async
|
data/proto/readme.md
CHANGED
|
@@ -25,45 +25,29 @@ These files come from [envoyproxy/data-plane-api](https://github.com/envoyproxy/
|
|
|
25
25
|
To update these files, run:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
|
|
28
|
+
bundle exec bake update_protos
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
# Clone envoy data-plane-api
|
|
35
|
-
git clone --depth 1 https://github.com/envoyproxy/data-plane-api.git /tmp/envoy-api
|
|
36
|
-
|
|
37
|
-
# Copy needed files
|
|
38
|
-
cp -r /tmp/envoy-api/envoy proto/
|
|
39
|
-
cp -r /tmp/envoy-api/google proto/
|
|
40
|
-
|
|
41
|
-
# Cleanup
|
|
42
|
-
rm -rf /tmp/envoy-api
|
|
43
|
-
```
|
|
31
|
+
This fetches the pinned upstream revisions defined in `bake.rb`, updates the vendored definitions, and regenerates the checked-in Ruby classes.
|
|
44
32
|
|
|
45
33
|
## Generating Ruby Code
|
|
46
34
|
|
|
47
35
|
After updating proto files, generate Ruby classes:
|
|
48
36
|
|
|
49
37
|
```bash
|
|
50
|
-
bundle exec bake
|
|
38
|
+
bundle exec bake generate_protos
|
|
51
39
|
```
|
|
52
40
|
|
|
53
41
|
## Version
|
|
54
42
|
|
|
55
|
-
|
|
43
|
+
The source repositories and revisions are defined by `PROTOBUF_SOURCES` in `bake.rb`. They include:
|
|
44
|
+
|
|
56
45
|
- `envoyproxy/data-plane-api` - Envoy API definitions
|
|
57
46
|
- `protocolbuffers/protobuf` - Google protobuf well-known types
|
|
58
47
|
- `googleapis/api-common-protos` - Google RPC status
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
cd /tmp/envoy-api
|
|
64
|
-
git checkout v1.30.0 # Use specific Envoy version
|
|
65
|
-
# Then copy files
|
|
66
|
-
```
|
|
48
|
+
- `cncf/xds` - xDS API definitions
|
|
49
|
+
- `cncf/udpa` - UDPA annotations
|
|
50
|
+
- `envoyproxy/protoc-gen-validate` - Validation annotations
|
|
67
51
|
|
|
68
52
|
## Note on Dependencies
|
|
69
53
|
|
data/readme.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
xDS support for `async-grpc` clients.
|
|
4
4
|
|
|
5
|
-
This gem contains the experimental xDS implementation extracted from `async-grpc`, including Envoy xDS protobuf definitions,
|
|
5
|
+
This gem contains the experimental xDS implementation extracted from `async-grpc`, including Envoy xDS protobuf definitions, aggregated and resource-specific discovery, CDS/EDS resource handling, and client-side load balancing.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/socketry/async-grpc-xds/actions?workflow=Test)
|
|
8
8
|
|
|
@@ -10,22 +10,29 @@ This gem contains the experimental xDS implementation extracted from `async-grpc
|
|
|
10
10
|
|
|
11
11
|
Please see the [project documentation](https://socketry.github.io/async-grpc-xds/) for more details.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
This is an early implementation focused on ADS with CDS and EDS. LDS/RDS, full routing semantics, NACK handling, locality weighting, and delta xDS are not complete yet.
|
|
13
|
+
- [Getting Started](https://socketry.github.io/async-grpc-xds/guides/getting-started/index) - This guide explains how to use `async-grpc-xds` to publish CDS and EDS resources to Envoy, or to discover gRPC backends from Ruby using xDS.
|
|
16
14
|
|
|
17
15
|
## Testing
|
|
18
16
|
|
|
19
|
-
The `
|
|
17
|
+
The `integration/` directory contains Docker Compose environments for testing the Ruby xDS client against a Go control plane and the Ruby control plane against Envoy.
|
|
20
18
|
|
|
21
19
|
``` bash
|
|
22
|
-
|
|
20
|
+
bundle exec bake test:integration
|
|
23
21
|
```
|
|
24
22
|
|
|
25
23
|
## Releases
|
|
26
24
|
|
|
27
25
|
Please see the [project releases](https://socketry.github.io/async-grpc-xds/releases/index) for all releases.
|
|
28
26
|
|
|
27
|
+
### v0.4.0
|
|
28
|
+
|
|
29
|
+
- Add dedicated Cluster Discovery Service and Endpoint Discovery Service implementations, allowing resource-specific xDS streams without claiming ADS.
|
|
30
|
+
- Allow generated clusters to use a dedicated EDS configuration source instead of ADS.
|
|
31
|
+
|
|
32
|
+
### v0.3.0
|
|
33
|
+
|
|
34
|
+
- Add Envoy HTTP active health-check resources and attach health checks to generated clusters.
|
|
35
|
+
|
|
29
36
|
### v0.2.0
|
|
30
37
|
|
|
31
38
|
- Add ORCA load reporting messages, service interface, and client-side weighted-round-robin resources.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.4.0
|
|
4
|
+
|
|
5
|
+
- Add dedicated Cluster Discovery Service and Endpoint Discovery Service implementations, allowing resource-specific xDS streams without claiming ADS.
|
|
6
|
+
- Allow generated clusters to use a dedicated EDS configuration source instead of ADS.
|
|
7
|
+
|
|
8
|
+
## v0.3.0
|
|
9
|
+
|
|
10
|
+
- Add Envoy HTTP active health-check resources and attach health checks to generated clusters.
|
|
11
|
+
|
|
3
12
|
## v0.2.0
|
|
4
13
|
|
|
5
14
|
- Add ORCA load reporting messages, service interface, and client-side weighted-round-robin resources.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-grpc-xds
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -126,23 +126,29 @@ executables: []
|
|
|
126
126
|
extensions: []
|
|
127
127
|
extra_rdoc_files: []
|
|
128
128
|
files:
|
|
129
|
-
-
|
|
130
|
-
-
|
|
129
|
+
- context/getting-started.md
|
|
130
|
+
- context/index.yaml
|
|
131
131
|
- lib/async/grpc/xds.rb
|
|
132
132
|
- lib/async/grpc/xds/ads_stream.rb
|
|
133
133
|
- lib/async/grpc/xds/client.rb
|
|
134
134
|
- lib/async/grpc/xds/client_side_weighted_round_robin.rb
|
|
135
135
|
- lib/async/grpc/xds/cluster.rb
|
|
136
|
+
- lib/async/grpc/xds/cluster_discovery_service.rb
|
|
137
|
+
- lib/async/grpc/xds/config_source.rb
|
|
136
138
|
- lib/async/grpc/xds/context.rb
|
|
137
139
|
- lib/async/grpc/xds/control_plane.rb
|
|
138
140
|
- lib/async/grpc/xds/discovery_client.rb
|
|
141
|
+
- lib/async/grpc/xds/discovery_service.rb
|
|
139
142
|
- lib/async/grpc/xds/endpoint.rb
|
|
143
|
+
- lib/async/grpc/xds/endpoint_discovery_service.rb
|
|
140
144
|
- lib/async/grpc/xds/health_checker.rb
|
|
145
|
+
- lib/async/grpc/xds/http_health_check.rb
|
|
141
146
|
- lib/async/grpc/xds/load_balancer.rb
|
|
142
147
|
- lib/async/grpc/xds/resource_cache.rb
|
|
143
148
|
- lib/async/grpc/xds/resources.rb
|
|
144
149
|
- lib/async/grpc/xds/server.rb
|
|
145
150
|
- lib/async/grpc/xds/service.rb
|
|
151
|
+
- lib/async/grpc/xds/stream.rb
|
|
146
152
|
- lib/async/grpc/xds/version.rb
|
|
147
153
|
- lib/envoy.rb
|
|
148
154
|
- lib/envoy/annotations/deprecation_pb.rb
|
|
@@ -365,21 +371,12 @@ files:
|
|
|
365
371
|
- proto/xds/type/v3/typed_struct.proto
|
|
366
372
|
- readme.md
|
|
367
373
|
- releases.md
|
|
368
|
-
- xds/Dockerfile.backend
|
|
369
|
-
- xds/Dockerfile.control-plane
|
|
370
|
-
- xds/backend_server.rb
|
|
371
|
-
- xds/docker-compose.yaml
|
|
372
|
-
- xds/go.mod
|
|
373
|
-
- xds/go.sum
|
|
374
|
-
- xds/readme.md
|
|
375
|
-
- xds/test/async/grpc/xds/client.rb
|
|
376
|
-
- xds/test/async/grpc/xds/control_plane.rb
|
|
377
|
-
- xds/test_server.go
|
|
378
|
-
- xds/update_protos.sh
|
|
379
374
|
homepage: https://github.com/socketry/async-grpc-xds
|
|
380
375
|
licenses:
|
|
381
376
|
- MIT
|
|
382
377
|
metadata:
|
|
378
|
+
bug_tracker_uri: https://github.com/socketry/async-grpc-xds/issues
|
|
379
|
+
changelog_uri: https://github.com/socketry/async-grpc-xds/blob/main/releases.md
|
|
383
380
|
documentation_uri: https://socketry.github.io/async-grpc-xds/
|
|
384
381
|
source_code_uri: https://github.com/socketry/async-grpc-xds.git
|
|
385
382
|
rdoc_options: []
|
metadata.gz.sig
CHANGED
|
Binary file
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2026, by Samuel Williams.
|
|
5
|
-
|
|
6
|
-
require "protocol/grpc/interface"
|
|
7
|
-
require_relative "test_message"
|
|
8
|
-
|
|
9
|
-
module Async
|
|
10
|
-
module GRPC
|
|
11
|
-
module Fixtures
|
|
12
|
-
# Test interface for unit tests
|
|
13
|
-
# RPC names use PascalCase to match .proto files
|
|
14
|
-
class TestInterface < Protocol::GRPC::Interface
|
|
15
|
-
rpc :UnaryCall, request_class: Protocol::GRPC::Fixtures::TestMessage,
|
|
16
|
-
response_class: Protocol::GRPC::Fixtures::TestMessage, streaming: :unary
|
|
17
|
-
rpc :ServerStreamingCall, request_class: Protocol::GRPC::Fixtures::TestMessage,
|
|
18
|
-
response_class: Protocol::GRPC::Fixtures::TestMessage, streaming: :server_streaming
|
|
19
|
-
rpc :SayHello, request_class: Protocol::GRPC::Fixtures::TestMessage,
|
|
20
|
-
response_class: Protocol::GRPC::Fixtures::TestMessage, streaming: :unary
|
|
21
|
-
rpc :BidirectionalCall, request_class: Protocol::GRPC::Fixtures::TestMessage,
|
|
22
|
-
response_class: Protocol::GRPC::Fixtures::TestMessage, streaming: :bidirectional
|
|
23
|
-
rpc :ClientStreamingCall, request_class: Protocol::GRPC::Fixtures::TestMessage,
|
|
24
|
-
response_class: Protocol::GRPC::Fixtures::TestMessage, streaming: :client_streaming
|
|
25
|
-
rpc :SlowCall
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Test service implementation
|
|
29
|
-
# Method names use snake_case (Ruby convention)
|
|
30
|
-
class TestService < Async::GRPC::Service
|
|
31
|
-
def unary_call(input, output, _call)
|
|
32
|
-
request = input.read
|
|
33
|
-
response = Protocol::GRPC::Fixtures::TestMessage.new(value: "Response: #{request.value}")
|
|
34
|
-
output.write(response)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def server_streaming_call(input, output, _call)
|
|
38
|
-
request = input.read
|
|
39
|
-
3.times do |i|
|
|
40
|
-
response = Protocol::GRPC::Fixtures::TestMessage.new(value: "Response #{i}: #{request.value}")
|
|
41
|
-
output.write(response)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def say_hello(input, output, _call)
|
|
46
|
-
request = input.read
|
|
47
|
-
response = Protocol::GRPC::Fixtures::TestMessage.new(value: "Hello, #{request.value}!")
|
|
48
|
-
output.write(response)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def bidirectional_call(input, output, _call)
|
|
52
|
-
# Read all input messages and echo them back with a prefix
|
|
53
|
-
input.each do |request|
|
|
54
|
-
response = Protocol::GRPC::Fixtures::TestMessage.new(value: "Echo: #{request.value}")
|
|
55
|
-
output.write(response)
|
|
56
|
-
end
|
|
57
|
-
output.close_write
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def client_streaming_call(input, output, _call)
|
|
61
|
-
# Read all input messages and return a summary
|
|
62
|
-
values = []
|
|
63
|
-
input.each do |request|
|
|
64
|
-
values << request.value
|
|
65
|
-
end
|
|
66
|
-
response = Protocol::GRPC::Fixtures::TestMessage.new(value: "Received: #{values.join(', ')}")
|
|
67
|
-
output.write(response)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def slow_call(input, output, _call)
|
|
71
|
-
request = input.read
|
|
72
|
-
sleep 1 # Simulate a slow operation
|
|
73
|
-
response = Protocol::GRPC::Fixtures::TestMessage.new(value: "Slow response: #{request.value}")
|
|
74
|
-
output.write(response)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2026, by Samuel Williams.
|
|
5
|
-
|
|
6
|
-
module Protocol
|
|
7
|
-
module GRPC
|
|
8
|
-
module Fixtures
|
|
9
|
-
# Simple test message for unit tests.
|
|
10
|
-
# Provides a basic protobuf-like message structure.
|
|
11
|
-
class TestMessage
|
|
12
|
-
# @attribute [String | Nil] The message value.
|
|
13
|
-
attr_accessor :value
|
|
14
|
-
|
|
15
|
-
# Initialize a new test message.
|
|
16
|
-
# @parameter value [String | Nil] The message value
|
|
17
|
-
def initialize(value: nil)
|
|
18
|
-
@value = value
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Serialize the message to a binary string.
|
|
22
|
-
# @returns [String] Binary representation of the message
|
|
23
|
-
def to_proto
|
|
24
|
-
# Simple serialization: length-prefixed value
|
|
25
|
-
value_data = (@value || "").dup.force_encoding(Encoding::BINARY)
|
|
26
|
-
[value_data.bytesize].pack("N") + value_data
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
alias encode to_proto
|
|
30
|
-
|
|
31
|
-
# Deserialize a binary string into a message.
|
|
32
|
-
# @parameter data [String] Binary representation of the message
|
|
33
|
-
# @returns [TestMessage] Deserialized message instance
|
|
34
|
-
def self.decode(data)
|
|
35
|
-
# Simple binary format: first 4 bytes are length, rest is value
|
|
36
|
-
length = data[0...4].unpack1("N")
|
|
37
|
-
value = data[4...(4 + length)].dup.force_encoding(Encoding::UTF_8)
|
|
38
|
-
new(value: value)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Check equality with another message.
|
|
42
|
-
# @parameter other [Object] The object to compare
|
|
43
|
-
# @returns [Boolean] `true` if values are equal
|
|
44
|
-
def ==(other)
|
|
45
|
-
other.is_a?(TestMessage) && @value == other.value
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Get a string representation of the message.
|
|
49
|
-
# @returns [String] String representation
|
|
50
|
-
def inspect
|
|
51
|
-
"#<#{self.class.name} value=#{@value.inspect}>"
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|