async-service-supervisor-envoy 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +5 -3
- data/lib/async/service/supervisor/envoy/endpoint.rb +58 -23
- data/lib/async/service/supervisor/envoy/endpoint_group.rb +63 -0
- data/lib/async/service/supervisor/envoy/monitor.rb +9 -2
- data/lib/async/service/supervisor/envoy/supervised.rb +1 -1
- data/lib/async/service/supervisor/envoy/version.rb +1 -1
- data/readme.md +5 -1
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +2 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 531e6d8c0e35b0122bcec0c0234afe37be776bb8b1e56e8ee92f5d026f5739f5
|
|
4
|
+
data.tar.gz: 6b9d8f77dd753fbf1a26b076f88fecc37a1856a69ac4a0e82bc4611370fb90c8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4708ac696da72097ebf6888cf144b3076c72392e1a695cb8311d6b2046869b1522a47850ec59464beff2f588720d760ae427d7c556a75d25e36bb353535a9311
|
|
7
|
+
data.tar.gz: 3ffad268b3785b3625fc3cec1cde6dfb50d68904547ab531a19a99317cef32baa29d52ebb1d6795cebe302885598c742a9eaa18cbde2fb7ecd95c01dcfe4b04c
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -41,16 +41,18 @@ state = {
|
|
|
41
41
|
|
|
42
42
|
Workers without endpoint state are ignored by the Envoy monitor.
|
|
43
43
|
|
|
44
|
-
Falcon
|
|
44
|
+
Falcon server workers can register their concrete bound listener automatically:
|
|
45
45
|
|
|
46
46
|
``` ruby
|
|
47
47
|
service "application" do
|
|
48
|
-
include Falcon::Environment::
|
|
48
|
+
include Falcon::Environment::Server
|
|
49
49
|
include Async::Service::Supervisor::Envoy::Supervised
|
|
50
50
|
end
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Falcon
|
|
53
|
+
`Falcon::Environment::Server` reports one listener shared by all of its workers, while `Falcon::Environment::Cluster` reports the listener bound independently by each worker. Falcon describes either bound server resource as a listener. The integration converts that listener into Envoy upstream endpoint state, including its name, scheme, supported protocols, and every concrete IP or Unix socket address.
|
|
54
|
+
|
|
55
|
+
Addresses belonging to one listener remain grouped as one Envoy load-balancer endpoint. When several workers report the same shared listener, the monitor publishes it once and keeps it available while any reporting worker is healthy.
|
|
54
56
|
|
|
55
57
|
## Monitor Usage
|
|
56
58
|
|
|
@@ -7,8 +7,23 @@ module Async
|
|
|
7
7
|
module Service
|
|
8
8
|
module Supervisor
|
|
9
9
|
module Envoy
|
|
10
|
-
# Represents
|
|
10
|
+
# Represents an upstream endpoint published to Envoy EDS.
|
|
11
11
|
class Endpoint
|
|
12
|
+
# Build an immutable endpoint.
|
|
13
|
+
# @parameter name [String] The upstream cluster name.
|
|
14
|
+
# @parameter scheme [String | Symbol] The upstream application scheme.
|
|
15
|
+
# @parameter protocols [Array(String)] The supported upstream HTTP protocol names.
|
|
16
|
+
# @parameter addresses [Array(Hash)] The grouped concrete addresses.
|
|
17
|
+
# @returns [Endpoint] The immutable endpoint.
|
|
18
|
+
def self.build(name:, scheme:, protocols:, addresses:)
|
|
19
|
+
new(
|
|
20
|
+
name.to_s,
|
|
21
|
+
scheme.to_sym,
|
|
22
|
+
protocols.map(&:to_s).uniq,
|
|
23
|
+
addresses,
|
|
24
|
+
).tap(&:freeze)
|
|
25
|
+
end
|
|
26
|
+
|
|
12
27
|
# Wrap serialized endpoint state.
|
|
13
28
|
# @parameter value [Endpoint | Hash] The value to wrap.
|
|
14
29
|
# @returns [Endpoint] The endpoint value.
|
|
@@ -17,39 +32,26 @@ module Async
|
|
|
17
32
|
when self
|
|
18
33
|
value
|
|
19
34
|
when Hash
|
|
20
|
-
|
|
35
|
+
build(**value)
|
|
21
36
|
else
|
|
22
37
|
raise ArgumentError, "Invalid Envoy endpoint: #{value.inspect}"
|
|
23
38
|
end
|
|
24
39
|
end
|
|
25
40
|
|
|
26
|
-
# Normalize a concrete endpoint address.
|
|
27
|
-
# @parameter value [Hash] The address value.
|
|
28
|
-
# @returns [Hash] A normalized IP or Unix address.
|
|
29
|
-
def self.normalize_address(value)
|
|
30
|
-
if path = value[:path]
|
|
31
|
-
raise ArgumentError, "A Unix endpoint cannot specify an IP address or port!" if value[:address] || value[:port]
|
|
32
|
-
|
|
33
|
-
{path: path.to_s}.freeze
|
|
34
|
-
elsif value[:address] && value[:port]
|
|
35
|
-
{address: value[:address].to_s, port: Integer(value[:port])}.freeze
|
|
36
|
-
else
|
|
37
|
-
raise ArgumentError, "An endpoint address requires either path, or address and port: #{value.inspect}"
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
41
|
# Initialize an endpoint.
|
|
42
42
|
# @parameter name [String] The upstream cluster name.
|
|
43
|
-
# @parameter scheme [
|
|
43
|
+
# @parameter scheme [Symbol] The upstream application scheme.
|
|
44
44
|
# @parameter protocols [Array(String)] The supported upstream HTTP protocol names.
|
|
45
45
|
# @parameter addresses [Array(Hash)] The grouped concrete addresses.
|
|
46
|
-
def initialize(name
|
|
47
|
-
@name = name
|
|
48
|
-
@scheme = scheme
|
|
49
|
-
@protocols = protocols
|
|
46
|
+
def initialize(name, scheme, protocols, addresses)
|
|
47
|
+
@name = name
|
|
48
|
+
@scheme = scheme
|
|
49
|
+
@protocols = protocols
|
|
50
50
|
raise ArgumentError, "An endpoint requires at least one protocol!" if @protocols.empty?
|
|
51
|
-
@addresses = addresses
|
|
51
|
+
@addresses = addresses
|
|
52
52
|
raise ArgumentError, "An endpoint requires at least one address!" if @addresses.empty?
|
|
53
|
+
|
|
54
|
+
@hash = nil
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# @attribute [String] The upstream cluster name.
|
|
@@ -64,6 +66,39 @@ module Async
|
|
|
64
66
|
# @attribute [Array(Hash)] The grouped concrete addresses.
|
|
65
67
|
attr :addresses
|
|
66
68
|
|
|
69
|
+
# Freeze this endpoint and cache its value hash.
|
|
70
|
+
def freeze
|
|
71
|
+
return self if frozen?
|
|
72
|
+
|
|
73
|
+
@name.freeze
|
|
74
|
+
@protocols.each(&:freeze).freeze
|
|
75
|
+
@addresses.each do |address|
|
|
76
|
+
address.each_value(&:freeze)
|
|
77
|
+
address.freeze
|
|
78
|
+
end.freeze
|
|
79
|
+
@hash = self.hash
|
|
80
|
+
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Compare this endpoint with another endpoint by value.
|
|
85
|
+
# @parameter other [Object] The object to compare.
|
|
86
|
+
# @returns [Boolean] Whether both endpoints have identical values.
|
|
87
|
+
def eql?(other)
|
|
88
|
+
other.instance_of?(self.class) &&
|
|
89
|
+
@name == other.name &&
|
|
90
|
+
@scheme == other.scheme &&
|
|
91
|
+
@protocols == other.protocols &&
|
|
92
|
+
@addresses == other.addresses
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
alias == eql?
|
|
96
|
+
|
|
97
|
+
# Compute the value hash used when grouping endpoints.
|
|
98
|
+
# @returns [Integer] The endpoint value hash.
|
|
99
|
+
def hash
|
|
100
|
+
@hash || [self.class, @name, @scheme, @protocols, @addresses].hash
|
|
101
|
+
end
|
|
67
102
|
end
|
|
68
103
|
end
|
|
69
104
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
module Async
|
|
7
|
+
module Service
|
|
8
|
+
module Supervisor
|
|
9
|
+
module Envoy
|
|
10
|
+
# Groups the workers reporting a single upstream endpoint.
|
|
11
|
+
class EndpointGroup
|
|
12
|
+
# Initialize an endpoint group.
|
|
13
|
+
# @parameter endpoint [Endpoint] The endpoint shared by the workers.
|
|
14
|
+
def initialize(endpoint)
|
|
15
|
+
@endpoint = endpoint
|
|
16
|
+
@workers = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @attribute [Endpoint] The endpoint shared by the workers.
|
|
20
|
+
attr :endpoint
|
|
21
|
+
|
|
22
|
+
# Add or update a worker report.
|
|
23
|
+
# @parameter worker [SupervisorController] The worker reporting the endpoint.
|
|
24
|
+
# @parameter healthy [Boolean] Whether the worker considers the endpoint healthy.
|
|
25
|
+
def add(worker, healthy:)
|
|
26
|
+
@workers[worker.id] = healthy
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Remove a worker report.
|
|
30
|
+
# @parameter worker [SupervisorController] The worker to remove.
|
|
31
|
+
# @returns [Boolean | Nil] The removed health value, if present.
|
|
32
|
+
def remove(worker)
|
|
33
|
+
@workers.delete(worker.id)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Count the workers reporting this endpoint.
|
|
37
|
+
# @returns [Integer] The number of reporting workers.
|
|
38
|
+
def size
|
|
39
|
+
@workers.size
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Determine whether any workers report this endpoint.
|
|
43
|
+
# @returns [Boolean] Whether the group has no workers.
|
|
44
|
+
def empty?
|
|
45
|
+
@workers.empty?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Determine the aggregate endpoint health.
|
|
49
|
+
# @returns [Boolean] Whether any reporting worker is healthy.
|
|
50
|
+
def healthy?
|
|
51
|
+
@workers.each_value.any?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Convert the group to an xDS endpoint description.
|
|
55
|
+
# @returns [Hash] The endpoint addresses and aggregate health.
|
|
56
|
+
def as_json
|
|
57
|
+
{addresses: @endpoint.addresses, healthy: healthy?}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -10,6 +10,7 @@ require "async/grpc/xds/server"
|
|
|
10
10
|
|
|
11
11
|
require_relative "delegate"
|
|
12
12
|
require_relative "endpoint"
|
|
13
|
+
require_relative "endpoint_group"
|
|
13
14
|
|
|
14
15
|
module Async
|
|
15
16
|
module Service
|
|
@@ -108,6 +109,7 @@ module Async
|
|
|
108
109
|
{
|
|
109
110
|
cluster: cluster.to_s,
|
|
110
111
|
endpoint: endpoint,
|
|
112
|
+
worker: supervisor_controller,
|
|
111
113
|
healthy: @delegate.healthy?(supervisor_controller, endpoint),
|
|
112
114
|
}
|
|
113
115
|
end
|
|
@@ -146,9 +148,14 @@ module Async
|
|
|
146
148
|
|
|
147
149
|
def build_clusters(records_by_cluster = build_records_by_cluster)
|
|
148
150
|
records_by_cluster.transform_values do |records|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
groups = {}
|
|
152
|
+
|
|
153
|
+
records.each do |record|
|
|
154
|
+
group = groups[record[:endpoint]] ||= EndpointGroup.new(record[:endpoint])
|
|
155
|
+
group.add(record[:worker], healthy: record[:healthy])
|
|
151
156
|
end
|
|
157
|
+
|
|
158
|
+
groups.each_value.map(&:as_json)
|
|
152
159
|
end
|
|
153
160
|
end
|
|
154
161
|
|
|
@@ -9,7 +9,7 @@ module Async
|
|
|
9
9
|
module Service
|
|
10
10
|
module Supervisor
|
|
11
11
|
module Envoy
|
|
12
|
-
# Registers
|
|
12
|
+
# Registers bound Falcon listeners with the supervisor.
|
|
13
13
|
module Supervised
|
|
14
14
|
include Async::Service::Supervisor::Supervised
|
|
15
15
|
|
data/readme.md
CHANGED
|
@@ -24,6 +24,10 @@ Please see the [project documentation](https://socketry.github.io/async-service-
|
|
|
24
24
|
|
|
25
25
|
Please see the [project releases](https://socketry.github.io/async-service-supervisor-envoy/releases/index) for all releases.
|
|
26
26
|
|
|
27
|
+
### v0.1.0
|
|
28
|
+
|
|
29
|
+
- Deduplicate immutable endpoint values reported by multiple supervised workers and aggregate their health.
|
|
30
|
+
|
|
27
31
|
### v0.0.1
|
|
28
32
|
|
|
29
33
|
- Register concrete Falcon cluster listeners as Envoy upstream endpoint state after binding.
|
|
@@ -32,7 +36,7 @@ Please see the [project releases](https://socketry.github.io/async-service-super
|
|
|
32
36
|
|
|
33
37
|
### v0.1.0
|
|
34
38
|
|
|
35
|
-
-
|
|
39
|
+
- Deduplicate immutable endpoint values reported by multiple supervised workers and aggregate their health.
|
|
36
40
|
|
|
37
41
|
## See Also
|
|
38
42
|
|
data/releases.md
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-service-supervisor-envoy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -104,6 +104,7 @@ files:
|
|
|
104
104
|
- lib/async/service/supervisor/envoy.rb
|
|
105
105
|
- lib/async/service/supervisor/envoy/delegate.rb
|
|
106
106
|
- lib/async/service/supervisor/envoy/endpoint.rb
|
|
107
|
+
- lib/async/service/supervisor/envoy/endpoint_group.rb
|
|
107
108
|
- lib/async/service/supervisor/envoy/monitor.rb
|
|
108
109
|
- lib/async/service/supervisor/envoy/supervised.rb
|
|
109
110
|
- lib/async/service/supervisor/envoy/version.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|