feat: support static gateway direct routes
This commit is contained in:
parent
d9d3a3d6c0
commit
3bcc7298a2
@ -99,6 +99,7 @@ message RoutingRule {
|
|||||||
RoutingStrategy strategy = 8;
|
RoutingStrategy strategy = 8;
|
||||||
UnavailableAction on_unavailable = 9;
|
UnavailableAction on_unavailable = 9;
|
||||||
google.protobuf.Duration wait_timeout = 10;
|
google.protobuf.Duration wait_timeout = 10;
|
||||||
|
RoutingAction action = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RoutingStrategy {
|
message RoutingStrategy {
|
||||||
@ -123,6 +124,13 @@ enum UnavailableAction {
|
|||||||
UNAVAILABLE_ACTION_DIRECT = 3;
|
UNAVAILABLE_ACTION_DIRECT = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RoutingAction {
|
||||||
|
ROUTING_ACTION_UNSPECIFIED = 0;
|
||||||
|
ROUTING_ACTION_PROXY = 1;
|
||||||
|
ROUTING_ACTION_DIRECT = 2;
|
||||||
|
ROUTING_ACTION_REJECT = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message OwnedProxy {
|
message OwnedProxy {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string upstream = 2;
|
string upstream = 2;
|
||||||
|
|||||||
@ -444,6 +444,7 @@ routing:
|
|||||||
- name: api-post
|
- name: api-post
|
||||||
enabled: true
|
enabled: true
|
||||||
purpose: gateway
|
purpose: gateway
|
||||||
|
action: proxy
|
||||||
match:
|
match:
|
||||||
hostRegex: '^api\\.example\\.com$'
|
hostRegex: '^api\\.example\\.com$'
|
||||||
methods: [POST]
|
methods: [POST]
|
||||||
@ -463,6 +464,10 @@ routing:
|
|||||||
|
|
||||||
- Routing 列表有序,首条匹配后停止。
|
- Routing 列表有序,首条匹配后停止。
|
||||||
- `purpose` 为 `gateway` 或 `extract`。
|
- `purpose` 为 `gateway` 或 `extract`。
|
||||||
|
- `action` 省略时为 `proxy`。`proxy` 使用 Upstream 与 Strategy;`direct` 直接连接已通过
|
||||||
|
目标地址策略验证的目标;`reject` 拒绝匹配请求。`direct` 与 `reject` 仅允许
|
||||||
|
`gateway` Routing,且不得同时配置 `upstreams`、`strategy`、`onUnavailable` 或
|
||||||
|
`check.targets`。
|
||||||
- `strategy.type` 支持 `sequential`、`random`、`roundRobin`、`weighted`、
|
- `strategy.type` 支持 `sequential`、`random`、`roundRobin`、`weighted`、
|
||||||
`leastConnections`。
|
`leastConnections`。
|
||||||
- `weighted` 使用 `weights` 映射,键必须引用本 Routing 的 Upstream。
|
- `weighted` 使用 `weights` 映射,键必须引用本 Routing 的 Upstream。
|
||||||
@ -470,6 +475,10 @@ routing:
|
|||||||
`switchAfterEmptyFetch`;`endBehavior` 省略时默认为 `stop`,也可显式设置
|
`switchAfterEmptyFetch`;`endBehavior` 省略时默认为 `stop`,也可显式设置
|
||||||
`loop` 或 `stayLast`。
|
`loop` 或 `stayLast`。
|
||||||
- `onUnavailable.action` 为 `reject`、`wait` 或 `direct`;默认建议 `reject`。
|
- `onUnavailable.action` 为 `reject`、`wait` 或 `direct`;默认建议 `reject`。
|
||||||
|
- `action: direct` 在 Dispatcher 前执行,不申请 Proxy 容量、不写 Proxy Outcome,也不建立
|
||||||
|
粘性 Proxy 绑定;它仍执行入口认证、Client 路由约束、速率/并发限制和目标地址策略。
|
||||||
|
`onUnavailable.action: direct` 仅是 `action: proxy` 在本地没有候选时的回退,二者
|
||||||
|
不可互换。
|
||||||
- `check.targets` 是 Routing 级 HTTP/HTTPS 探测目标。每个 Routing 最多 16 个 URL,且一个
|
- `check.targets` 是 Routing 级 HTTP/HTTPS 探测目标。每个 Routing 最多 16 个 URL,且一个
|
||||||
启用 Upstream 被其引用的 TARGET Profile 总数最多 64 个。每个 Profile 的身份是
|
启用 Upstream 被其引用的 TARGET Profile 总数最多 64 个。每个 Profile 的身份是
|
||||||
`(routing.name, target URL)`;失败只影响该 Profile,不改变 Proxy 的全局健康状态。
|
`(routing.name, target URL)`;失败只影响该 Profile,不改变 Proxy 的全局健康状态。
|
||||||
|
|||||||
@ -134,6 +134,58 @@ func (UnavailableAction) EnumDescriptor() ([]byte, []int) {
|
|||||||
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{1}
|
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RoutingAction int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoutingAction_ROUTING_ACTION_UNSPECIFIED RoutingAction = 0
|
||||||
|
RoutingAction_ROUTING_ACTION_PROXY RoutingAction = 1
|
||||||
|
RoutingAction_ROUTING_ACTION_DIRECT RoutingAction = 2
|
||||||
|
RoutingAction_ROUTING_ACTION_REJECT RoutingAction = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for RoutingAction.
|
||||||
|
var (
|
||||||
|
RoutingAction_name = map[int32]string{
|
||||||
|
0: "ROUTING_ACTION_UNSPECIFIED",
|
||||||
|
1: "ROUTING_ACTION_PROXY",
|
||||||
|
2: "ROUTING_ACTION_DIRECT",
|
||||||
|
3: "ROUTING_ACTION_REJECT",
|
||||||
|
}
|
||||||
|
RoutingAction_value = map[string]int32{
|
||||||
|
"ROUTING_ACTION_UNSPECIFIED": 0,
|
||||||
|
"ROUTING_ACTION_PROXY": 1,
|
||||||
|
"ROUTING_ACTION_DIRECT": 2,
|
||||||
|
"ROUTING_ACTION_REJECT": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x RoutingAction) Enum() *RoutingAction {
|
||||||
|
p := new(RoutingAction)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x RoutingAction) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RoutingAction) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_controlplane_v1_controlplane_proto_enumTypes[2].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RoutingAction) Type() protoreflect.EnumType {
|
||||||
|
return &file_controlplane_v1_controlplane_proto_enumTypes[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x RoutingAction) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use RoutingAction.Descriptor instead.
|
||||||
|
func (RoutingAction) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
type ProxyProtocol int32
|
type ProxyProtocol int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -170,11 +222,11 @@ func (x ProxyProtocol) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ProxyProtocol) Descriptor() protoreflect.EnumDescriptor {
|
func (ProxyProtocol) Descriptor() protoreflect.EnumDescriptor {
|
||||||
return file_controlplane_v1_controlplane_proto_enumTypes[2].Descriptor()
|
return file_controlplane_v1_controlplane_proto_enumTypes[3].Descriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ProxyProtocol) Type() protoreflect.EnumType {
|
func (ProxyProtocol) Type() protoreflect.EnumType {
|
||||||
return &file_controlplane_v1_controlplane_proto_enumTypes[2]
|
return &file_controlplane_v1_controlplane_proto_enumTypes[3]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x ProxyProtocol) Number() protoreflect.EnumNumber {
|
func (x ProxyProtocol) Number() protoreflect.EnumNumber {
|
||||||
@ -183,7 +235,7 @@ func (x ProxyProtocol) Number() protoreflect.EnumNumber {
|
|||||||
|
|
||||||
// Deprecated: Use ProxyProtocol.Descriptor instead.
|
// Deprecated: Use ProxyProtocol.Descriptor instead.
|
||||||
func (ProxyProtocol) EnumDescriptor() ([]byte, []int) {
|
func (ProxyProtocol) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{2}
|
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
type OutcomeStage int32
|
type OutcomeStage int32
|
||||||
@ -225,11 +277,11 @@ func (x OutcomeStage) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (OutcomeStage) Descriptor() protoreflect.EnumDescriptor {
|
func (OutcomeStage) Descriptor() protoreflect.EnumDescriptor {
|
||||||
return file_controlplane_v1_controlplane_proto_enumTypes[3].Descriptor()
|
return file_controlplane_v1_controlplane_proto_enumTypes[4].Descriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (OutcomeStage) Type() protoreflect.EnumType {
|
func (OutcomeStage) Type() protoreflect.EnumType {
|
||||||
return &file_controlplane_v1_controlplane_proto_enumTypes[3]
|
return &file_controlplane_v1_controlplane_proto_enumTypes[4]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x OutcomeStage) Number() protoreflect.EnumNumber {
|
func (x OutcomeStage) Number() protoreflect.EnumNumber {
|
||||||
@ -238,7 +290,7 @@ func (x OutcomeStage) Number() protoreflect.EnumNumber {
|
|||||||
|
|
||||||
// Deprecated: Use OutcomeStage.Descriptor instead.
|
// Deprecated: Use OutcomeStage.Descriptor instead.
|
||||||
func (OutcomeStage) EnumDescriptor() ([]byte, []int) {
|
func (OutcomeStage) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{3}
|
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckLevel int32
|
type CheckLevel int32
|
||||||
@ -277,11 +329,11 @@ func (x CheckLevel) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (CheckLevel) Descriptor() protoreflect.EnumDescriptor {
|
func (CheckLevel) Descriptor() protoreflect.EnumDescriptor {
|
||||||
return file_controlplane_v1_controlplane_proto_enumTypes[4].Descriptor()
|
return file_controlplane_v1_controlplane_proto_enumTypes[5].Descriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (CheckLevel) Type() protoreflect.EnumType {
|
func (CheckLevel) Type() protoreflect.EnumType {
|
||||||
return &file_controlplane_v1_controlplane_proto_enumTypes[4]
|
return &file_controlplane_v1_controlplane_proto_enumTypes[5]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x CheckLevel) Number() protoreflect.EnumNumber {
|
func (x CheckLevel) Number() protoreflect.EnumNumber {
|
||||||
@ -290,7 +342,7 @@ func (x CheckLevel) Number() protoreflect.EnumNumber {
|
|||||||
|
|
||||||
// Deprecated: Use CheckLevel.Descriptor instead.
|
// Deprecated: Use CheckLevel.Descriptor instead.
|
||||||
func (CheckLevel) EnumDescriptor() ([]byte, []int) {
|
func (CheckLevel) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{4}
|
return file_controlplane_v1_controlplane_proto_rawDescGZIP(), []int{5}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegisterWorkerRequest struct {
|
type RegisterWorkerRequest struct {
|
||||||
@ -885,6 +937,7 @@ type RoutingRule struct {
|
|||||||
Strategy *RoutingStrategy `protobuf:"bytes,8,opt,name=strategy,proto3" json:"strategy,omitempty"`
|
Strategy *RoutingStrategy `protobuf:"bytes,8,opt,name=strategy,proto3" json:"strategy,omitempty"`
|
||||||
OnUnavailable UnavailableAction `protobuf:"varint,9,opt,name=on_unavailable,json=onUnavailable,proto3,enum=proxy_pool.controlplane.v1.UnavailableAction" json:"on_unavailable,omitempty"`
|
OnUnavailable UnavailableAction `protobuf:"varint,9,opt,name=on_unavailable,json=onUnavailable,proto3,enum=proxy_pool.controlplane.v1.UnavailableAction" json:"on_unavailable,omitempty"`
|
||||||
WaitTimeout *durationpb.Duration `protobuf:"bytes,10,opt,name=wait_timeout,json=waitTimeout,proto3" json:"wait_timeout,omitempty"`
|
WaitTimeout *durationpb.Duration `protobuf:"bytes,10,opt,name=wait_timeout,json=waitTimeout,proto3" json:"wait_timeout,omitempty"`
|
||||||
|
Action RoutingAction `protobuf:"varint,11,opt,name=action,proto3,enum=proxy_pool.controlplane.v1.RoutingAction" json:"action,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -989,6 +1042,13 @@ func (x *RoutingRule) GetWaitTimeout() *durationpb.Duration {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RoutingRule) GetAction() RoutingAction {
|
||||||
|
if x != nil {
|
||||||
|
return x.Action
|
||||||
|
}
|
||||||
|
return RoutingAction_ROUTING_ACTION_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
type RoutingStrategy struct {
|
type RoutingStrategy struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Type StrategyType `protobuf:"varint,1,opt,name=type,proto3,enum=proxy_pool.controlplane.v1.StrategyType" json:"type,omitempty"`
|
Type StrategyType `protobuf:"varint,1,opt,name=type,proto3,enum=proxy_pool.controlplane.v1.StrategyType" json:"type,omitempty"`
|
||||||
@ -2240,7 +2300,7 @@ const file_controlplane_v1_controlplane_proto_rawDesc = "" +
|
|||||||
"\x10upserted_routing\x18\x06 \x03(\v2'.proxy_pool.controlplane.v1.RoutingRuleR\x0fupsertedRouting\x122\n" +
|
"\x10upserted_routing\x18\x06 \x03(\v2'.proxy_pool.controlplane.v1.RoutingRuleR\x0fupsertedRouting\x122\n" +
|
||||||
"\x15removed_routing_names\x18\a \x03(\tR\x13removedRoutingNames\x12Q\n" +
|
"\x15removed_routing_names\x18\a \x03(\tR\x13removedRoutingNames\x12Q\n" +
|
||||||
"\x10upserted_proxies\x18\b \x03(\v2&.proxy_pool.controlplane.v1.OwnedProxyR\x0fupsertedProxies\x12*\n" +
|
"\x10upserted_proxies\x18\b \x03(\v2&.proxy_pool.controlplane.v1.OwnedProxyR\x0fupsertedProxies\x12*\n" +
|
||||||
"\x11removed_proxy_ids\x18\t \x03(\tR\x0fremovedProxyIds\"\x9a\x04\n" +
|
"\x11removed_proxy_ids\x18\t \x03(\tR\x0fremovedProxyIds\"\xdd\x04\n" +
|
||||||
"\vRoutingRule\x12\x12\n" +
|
"\vRoutingRule\x12\x12\n" +
|
||||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" +
|
"\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" +
|
||||||
"\aenabled\x18\x02 \x01(\bR\aenabled\x12\x1d\n" +
|
"\aenabled\x18\x02 \x01(\bR\aenabled\x12\x1d\n" +
|
||||||
@ -2254,7 +2314,8 @@ const file_controlplane_v1_controlplane_proto_rawDesc = "" +
|
|||||||
"\bstrategy\x18\b \x01(\v2+.proxy_pool.controlplane.v1.RoutingStrategyR\bstrategy\x12T\n" +
|
"\bstrategy\x18\b \x01(\v2+.proxy_pool.controlplane.v1.RoutingStrategyR\bstrategy\x12T\n" +
|
||||||
"\x0eon_unavailable\x18\t \x01(\x0e2-.proxy_pool.controlplane.v1.UnavailableActionR\ronUnavailable\x12<\n" +
|
"\x0eon_unavailable\x18\t \x01(\x0e2-.proxy_pool.controlplane.v1.UnavailableActionR\ronUnavailable\x12<\n" +
|
||||||
"\fwait_timeout\x18\n" +
|
"\fwait_timeout\x18\n" +
|
||||||
" \x01(\v2\x19.google.protobuf.DurationR\vwaitTimeout\x1a:\n" +
|
" \x01(\v2\x19.google.protobuf.DurationR\vwaitTimeout\x12A\n" +
|
||||||
|
"\x06action\x18\v \x01(\x0e2).proxy_pool.controlplane.v1.RoutingActionR\x06action\x1a:\n" +
|
||||||
"\fHeadersEntry\x12\x10\n" +
|
"\fHeadersEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x8a\x02\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x8a\x02\n" +
|
||||||
@ -2397,7 +2458,12 @@ const file_controlplane_v1_controlplane_proto_rawDesc = "" +
|
|||||||
"\x1eUNAVAILABLE_ACTION_UNSPECIFIED\x10\x00\x12\x1d\n" +
|
"\x1eUNAVAILABLE_ACTION_UNSPECIFIED\x10\x00\x12\x1d\n" +
|
||||||
"\x19UNAVAILABLE_ACTION_REJECT\x10\x01\x12\x1b\n" +
|
"\x19UNAVAILABLE_ACTION_REJECT\x10\x01\x12\x1b\n" +
|
||||||
"\x17UNAVAILABLE_ACTION_WAIT\x10\x02\x12\x1d\n" +
|
"\x17UNAVAILABLE_ACTION_WAIT\x10\x02\x12\x1d\n" +
|
||||||
"\x19UNAVAILABLE_ACTION_DIRECT\x10\x03*}\n" +
|
"\x19UNAVAILABLE_ACTION_DIRECT\x10\x03*\x7f\n" +
|
||||||
|
"\rRoutingAction\x12\x1e\n" +
|
||||||
|
"\x1aROUTING_ACTION_UNSPECIFIED\x10\x00\x12\x18\n" +
|
||||||
|
"\x14ROUTING_ACTION_PROXY\x10\x01\x12\x19\n" +
|
||||||
|
"\x15ROUTING_ACTION_DIRECT\x10\x02\x12\x19\n" +
|
||||||
|
"\x15ROUTING_ACTION_REJECT\x10\x03*}\n" +
|
||||||
"\rProxyProtocol\x12\x1e\n" +
|
"\rProxyProtocol\x12\x1e\n" +
|
||||||
"\x1aPROXY_PROTOCOL_UNSPECIFIED\x10\x00\x12\x17\n" +
|
"\x1aPROXY_PROTOCOL_UNSPECIFIED\x10\x00\x12\x17\n" +
|
||||||
"\x13PROXY_PROTOCOL_HTTP\x10\x01\x12\x18\n" +
|
"\x13PROXY_PROTOCOL_HTTP\x10\x01\x12\x18\n" +
|
||||||
@ -2437,102 +2503,104 @@ func file_controlplane_v1_controlplane_proto_rawDescGZIP() []byte {
|
|||||||
return file_controlplane_v1_controlplane_proto_rawDescData
|
return file_controlplane_v1_controlplane_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_controlplane_v1_controlplane_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
|
var file_controlplane_v1_controlplane_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
|
||||||
var file_controlplane_v1_controlplane_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
var file_controlplane_v1_controlplane_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
||||||
var file_controlplane_v1_controlplane_proto_goTypes = []any{
|
var file_controlplane_v1_controlplane_proto_goTypes = []any{
|
||||||
(StrategyType)(0), // 0: proxy_pool.controlplane.v1.StrategyType
|
(StrategyType)(0), // 0: proxy_pool.controlplane.v1.StrategyType
|
||||||
(UnavailableAction)(0), // 1: proxy_pool.controlplane.v1.UnavailableAction
|
(UnavailableAction)(0), // 1: proxy_pool.controlplane.v1.UnavailableAction
|
||||||
(ProxyProtocol)(0), // 2: proxy_pool.controlplane.v1.ProxyProtocol
|
(RoutingAction)(0), // 2: proxy_pool.controlplane.v1.RoutingAction
|
||||||
(OutcomeStage)(0), // 3: proxy_pool.controlplane.v1.OutcomeStage
|
(ProxyProtocol)(0), // 3: proxy_pool.controlplane.v1.ProxyProtocol
|
||||||
(CheckLevel)(0), // 4: proxy_pool.controlplane.v1.CheckLevel
|
(OutcomeStage)(0), // 4: proxy_pool.controlplane.v1.OutcomeStage
|
||||||
(*RegisterWorkerRequest)(nil), // 5: proxy_pool.controlplane.v1.RegisterWorkerRequest
|
(CheckLevel)(0), // 5: proxy_pool.controlplane.v1.CheckLevel
|
||||||
(*RegisterWorkerResponse)(nil), // 6: proxy_pool.controlplane.v1.RegisterWorkerResponse
|
(*RegisterWorkerRequest)(nil), // 6: proxy_pool.controlplane.v1.RegisterWorkerRequest
|
||||||
(*WatchSnapshotsRequest)(nil), // 7: proxy_pool.controlplane.v1.WatchSnapshotsRequest
|
(*RegisterWorkerResponse)(nil), // 7: proxy_pool.controlplane.v1.RegisterWorkerResponse
|
||||||
(*SnapshotEnvelope)(nil), // 8: proxy_pool.controlplane.v1.SnapshotEnvelope
|
(*WatchSnapshotsRequest)(nil), // 8: proxy_pool.controlplane.v1.WatchSnapshotsRequest
|
||||||
(*WorkerSnapshot)(nil), // 9: proxy_pool.controlplane.v1.WorkerSnapshot
|
(*SnapshotEnvelope)(nil), // 9: proxy_pool.controlplane.v1.SnapshotEnvelope
|
||||||
(*SnapshotCredential)(nil), // 10: proxy_pool.controlplane.v1.SnapshotCredential
|
(*WorkerSnapshot)(nil), // 10: proxy_pool.controlplane.v1.WorkerSnapshot
|
||||||
(*SnapshotDelta)(nil), // 11: proxy_pool.controlplane.v1.SnapshotDelta
|
(*SnapshotCredential)(nil), // 11: proxy_pool.controlplane.v1.SnapshotCredential
|
||||||
(*RoutingRule)(nil), // 12: proxy_pool.controlplane.v1.RoutingRule
|
(*SnapshotDelta)(nil), // 12: proxy_pool.controlplane.v1.SnapshotDelta
|
||||||
(*RoutingStrategy)(nil), // 13: proxy_pool.controlplane.v1.RoutingStrategy
|
(*RoutingRule)(nil), // 13: proxy_pool.controlplane.v1.RoutingRule
|
||||||
(*OwnedProxy)(nil), // 14: proxy_pool.controlplane.v1.OwnedProxy
|
(*RoutingStrategy)(nil), // 14: proxy_pool.controlplane.v1.RoutingStrategy
|
||||||
(*AcknowledgeSnapshotRequest)(nil), // 15: proxy_pool.controlplane.v1.AcknowledgeSnapshotRequest
|
(*OwnedProxy)(nil), // 15: proxy_pool.controlplane.v1.OwnedProxy
|
||||||
(*OutcomeBatch)(nil), // 16: proxy_pool.controlplane.v1.OutcomeBatch
|
(*AcknowledgeSnapshotRequest)(nil), // 16: proxy_pool.controlplane.v1.AcknowledgeSnapshotRequest
|
||||||
(*ProxyOutcome)(nil), // 17: proxy_pool.controlplane.v1.ProxyOutcome
|
(*OutcomeBatch)(nil), // 17: proxy_pool.controlplane.v1.OutcomeBatch
|
||||||
(*ReportOutcomesResponse)(nil), // 18: proxy_pool.controlplane.v1.ReportOutcomesResponse
|
(*ProxyOutcome)(nil), // 18: proxy_pool.controlplane.v1.ProxyOutcome
|
||||||
(*ReportRuntimeRequest)(nil), // 19: proxy_pool.controlplane.v1.ReportRuntimeRequest
|
(*ReportOutcomesResponse)(nil), // 19: proxy_pool.controlplane.v1.ReportOutcomesResponse
|
||||||
(*ProxyRuntime)(nil), // 20: proxy_pool.controlplane.v1.ProxyRuntime
|
(*ReportRuntimeRequest)(nil), // 20: proxy_pool.controlplane.v1.ReportRuntimeRequest
|
||||||
(*ReportRuntimeResponse)(nil), // 21: proxy_pool.controlplane.v1.ReportRuntimeResponse
|
(*ProxyRuntime)(nil), // 21: proxy_pool.controlplane.v1.ProxyRuntime
|
||||||
(*StreamCheckTasksRequest)(nil), // 22: proxy_pool.controlplane.v1.StreamCheckTasksRequest
|
(*ReportRuntimeResponse)(nil), // 22: proxy_pool.controlplane.v1.ReportRuntimeResponse
|
||||||
(*CheckTask)(nil), // 23: proxy_pool.controlplane.v1.CheckTask
|
(*StreamCheckTasksRequest)(nil), // 23: proxy_pool.controlplane.v1.StreamCheckTasksRequest
|
||||||
(*ObservationBatch)(nil), // 24: proxy_pool.controlplane.v1.ObservationBatch
|
(*CheckTask)(nil), // 24: proxy_pool.controlplane.v1.CheckTask
|
||||||
(*HealthObservation)(nil), // 25: proxy_pool.controlplane.v1.HealthObservation
|
(*ObservationBatch)(nil), // 25: proxy_pool.controlplane.v1.ObservationBatch
|
||||||
(*ReportObservationsResponse)(nil), // 26: proxy_pool.controlplane.v1.ReportObservationsResponse
|
(*HealthObservation)(nil), // 26: proxy_pool.controlplane.v1.HealthObservation
|
||||||
nil, // 27: proxy_pool.controlplane.v1.RegisterWorkerRequest.LabelsEntry
|
(*ReportObservationsResponse)(nil), // 27: proxy_pool.controlplane.v1.ReportObservationsResponse
|
||||||
nil, // 28: proxy_pool.controlplane.v1.RoutingRule.HeadersEntry
|
nil, // 28: proxy_pool.controlplane.v1.RegisterWorkerRequest.LabelsEntry
|
||||||
nil, // 29: proxy_pool.controlplane.v1.RoutingStrategy.WeightsEntry
|
nil, // 29: proxy_pool.controlplane.v1.RoutingRule.HeadersEntry
|
||||||
nil, // 30: proxy_pool.controlplane.v1.OwnedProxy.TagsEntry
|
nil, // 30: proxy_pool.controlplane.v1.RoutingStrategy.WeightsEntry
|
||||||
(*durationpb.Duration)(nil), // 31: google.protobuf.Duration
|
nil, // 31: proxy_pool.controlplane.v1.OwnedProxy.TagsEntry
|
||||||
(*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp
|
(*durationpb.Duration)(nil), // 32: google.protobuf.Duration
|
||||||
(*emptypb.Empty)(nil), // 33: google.protobuf.Empty
|
(*timestamppb.Timestamp)(nil), // 33: google.protobuf.Timestamp
|
||||||
|
(*emptypb.Empty)(nil), // 34: google.protobuf.Empty
|
||||||
}
|
}
|
||||||
var file_controlplane_v1_controlplane_proto_depIdxs = []int32{
|
var file_controlplane_v1_controlplane_proto_depIdxs = []int32{
|
||||||
27, // 0: proxy_pool.controlplane.v1.RegisterWorkerRequest.labels:type_name -> proxy_pool.controlplane.v1.RegisterWorkerRequest.LabelsEntry
|
28, // 0: proxy_pool.controlplane.v1.RegisterWorkerRequest.labels:type_name -> proxy_pool.controlplane.v1.RegisterWorkerRequest.LabelsEntry
|
||||||
31, // 1: proxy_pool.controlplane.v1.RegisterWorkerResponse.heartbeat_interval:type_name -> google.protobuf.Duration
|
32, // 1: proxy_pool.controlplane.v1.RegisterWorkerResponse.heartbeat_interval:type_name -> google.protobuf.Duration
|
||||||
31, // 2: proxy_pool.controlplane.v1.RegisterWorkerResponse.max_stale_age:type_name -> google.protobuf.Duration
|
32, // 2: proxy_pool.controlplane.v1.RegisterWorkerResponse.max_stale_age:type_name -> google.protobuf.Duration
|
||||||
9, // 3: proxy_pool.controlplane.v1.SnapshotEnvelope.full:type_name -> proxy_pool.controlplane.v1.WorkerSnapshot
|
10, // 3: proxy_pool.controlplane.v1.SnapshotEnvelope.full:type_name -> proxy_pool.controlplane.v1.WorkerSnapshot
|
||||||
11, // 4: proxy_pool.controlplane.v1.SnapshotEnvelope.delta:type_name -> proxy_pool.controlplane.v1.SnapshotDelta
|
12, // 4: proxy_pool.controlplane.v1.SnapshotEnvelope.delta:type_name -> proxy_pool.controlplane.v1.SnapshotDelta
|
||||||
32, // 5: proxy_pool.controlplane.v1.WorkerSnapshot.generated_at:type_name -> google.protobuf.Timestamp
|
33, // 5: proxy_pool.controlplane.v1.WorkerSnapshot.generated_at:type_name -> google.protobuf.Timestamp
|
||||||
32, // 6: proxy_pool.controlplane.v1.WorkerSnapshot.valid_until:type_name -> google.protobuf.Timestamp
|
33, // 6: proxy_pool.controlplane.v1.WorkerSnapshot.valid_until:type_name -> google.protobuf.Timestamp
|
||||||
12, // 7: proxy_pool.controlplane.v1.WorkerSnapshot.routing:type_name -> proxy_pool.controlplane.v1.RoutingRule
|
13, // 7: proxy_pool.controlplane.v1.WorkerSnapshot.routing:type_name -> proxy_pool.controlplane.v1.RoutingRule
|
||||||
14, // 8: proxy_pool.controlplane.v1.WorkerSnapshot.proxies:type_name -> proxy_pool.controlplane.v1.OwnedProxy
|
15, // 8: proxy_pool.controlplane.v1.WorkerSnapshot.proxies:type_name -> proxy_pool.controlplane.v1.OwnedProxy
|
||||||
10, // 9: proxy_pool.controlplane.v1.WorkerSnapshot.credentials:type_name -> proxy_pool.controlplane.v1.SnapshotCredential
|
11, // 9: proxy_pool.controlplane.v1.WorkerSnapshot.credentials:type_name -> proxy_pool.controlplane.v1.SnapshotCredential
|
||||||
32, // 10: proxy_pool.controlplane.v1.SnapshotDelta.generated_at:type_name -> google.protobuf.Timestamp
|
33, // 10: proxy_pool.controlplane.v1.SnapshotDelta.generated_at:type_name -> google.protobuf.Timestamp
|
||||||
12, // 11: proxy_pool.controlplane.v1.SnapshotDelta.upserted_routing:type_name -> proxy_pool.controlplane.v1.RoutingRule
|
13, // 11: proxy_pool.controlplane.v1.SnapshotDelta.upserted_routing:type_name -> proxy_pool.controlplane.v1.RoutingRule
|
||||||
14, // 12: proxy_pool.controlplane.v1.SnapshotDelta.upserted_proxies:type_name -> proxy_pool.controlplane.v1.OwnedProxy
|
15, // 12: proxy_pool.controlplane.v1.SnapshotDelta.upserted_proxies:type_name -> proxy_pool.controlplane.v1.OwnedProxy
|
||||||
28, // 13: proxy_pool.controlplane.v1.RoutingRule.headers:type_name -> proxy_pool.controlplane.v1.RoutingRule.HeadersEntry
|
29, // 13: proxy_pool.controlplane.v1.RoutingRule.headers:type_name -> proxy_pool.controlplane.v1.RoutingRule.HeadersEntry
|
||||||
13, // 14: proxy_pool.controlplane.v1.RoutingRule.strategy:type_name -> proxy_pool.controlplane.v1.RoutingStrategy
|
14, // 14: proxy_pool.controlplane.v1.RoutingRule.strategy:type_name -> proxy_pool.controlplane.v1.RoutingStrategy
|
||||||
1, // 15: proxy_pool.controlplane.v1.RoutingRule.on_unavailable:type_name -> proxy_pool.controlplane.v1.UnavailableAction
|
1, // 15: proxy_pool.controlplane.v1.RoutingRule.on_unavailable:type_name -> proxy_pool.controlplane.v1.UnavailableAction
|
||||||
31, // 16: proxy_pool.controlplane.v1.RoutingRule.wait_timeout:type_name -> google.protobuf.Duration
|
32, // 16: proxy_pool.controlplane.v1.RoutingRule.wait_timeout:type_name -> google.protobuf.Duration
|
||||||
0, // 17: proxy_pool.controlplane.v1.RoutingStrategy.type:type_name -> proxy_pool.controlplane.v1.StrategyType
|
2, // 17: proxy_pool.controlplane.v1.RoutingRule.action:type_name -> proxy_pool.controlplane.v1.RoutingAction
|
||||||
29, // 18: proxy_pool.controlplane.v1.RoutingStrategy.weights:type_name -> proxy_pool.controlplane.v1.RoutingStrategy.WeightsEntry
|
0, // 18: proxy_pool.controlplane.v1.RoutingStrategy.type:type_name -> proxy_pool.controlplane.v1.StrategyType
|
||||||
2, // 19: proxy_pool.controlplane.v1.OwnedProxy.protocol:type_name -> proxy_pool.controlplane.v1.ProxyProtocol
|
30, // 19: proxy_pool.controlplane.v1.RoutingStrategy.weights:type_name -> proxy_pool.controlplane.v1.RoutingStrategy.WeightsEntry
|
||||||
32, // 20: proxy_pool.controlplane.v1.OwnedProxy.expires_at:type_name -> google.protobuf.Timestamp
|
3, // 20: proxy_pool.controlplane.v1.OwnedProxy.protocol:type_name -> proxy_pool.controlplane.v1.ProxyProtocol
|
||||||
30, // 21: proxy_pool.controlplane.v1.OwnedProxy.tags:type_name -> proxy_pool.controlplane.v1.OwnedProxy.TagsEntry
|
33, // 21: proxy_pool.controlplane.v1.OwnedProxy.expires_at:type_name -> google.protobuf.Timestamp
|
||||||
32, // 22: proxy_pool.controlplane.v1.OwnedProxy.usable_until:type_name -> google.protobuf.Timestamp
|
31, // 22: proxy_pool.controlplane.v1.OwnedProxy.tags:type_name -> proxy_pool.controlplane.v1.OwnedProxy.TagsEntry
|
||||||
17, // 23: proxy_pool.controlplane.v1.OutcomeBatch.outcomes:type_name -> proxy_pool.controlplane.v1.ProxyOutcome
|
33, // 23: proxy_pool.controlplane.v1.OwnedProxy.usable_until:type_name -> google.protobuf.Timestamp
|
||||||
3, // 24: proxy_pool.controlplane.v1.ProxyOutcome.stage:type_name -> proxy_pool.controlplane.v1.OutcomeStage
|
18, // 24: proxy_pool.controlplane.v1.OutcomeBatch.outcomes:type_name -> proxy_pool.controlplane.v1.ProxyOutcome
|
||||||
31, // 25: proxy_pool.controlplane.v1.ProxyOutcome.latency:type_name -> google.protobuf.Duration
|
4, // 25: proxy_pool.controlplane.v1.ProxyOutcome.stage:type_name -> proxy_pool.controlplane.v1.OutcomeStage
|
||||||
32, // 26: proxy_pool.controlplane.v1.ProxyOutcome.observed_at:type_name -> google.protobuf.Timestamp
|
32, // 26: proxy_pool.controlplane.v1.ProxyOutcome.latency:type_name -> google.protobuf.Duration
|
||||||
20, // 27: proxy_pool.controlplane.v1.ReportRuntimeRequest.counters:type_name -> proxy_pool.controlplane.v1.ProxyRuntime
|
33, // 27: proxy_pool.controlplane.v1.ProxyOutcome.observed_at:type_name -> google.protobuf.Timestamp
|
||||||
32, // 28: proxy_pool.controlplane.v1.ReportRuntimeRequest.observed_at:type_name -> google.protobuf.Timestamp
|
21, // 28: proxy_pool.controlplane.v1.ReportRuntimeRequest.counters:type_name -> proxy_pool.controlplane.v1.ProxyRuntime
|
||||||
4, // 29: proxy_pool.controlplane.v1.StreamCheckTasksRequest.supported_levels:type_name -> proxy_pool.controlplane.v1.CheckLevel
|
33, // 29: proxy_pool.controlplane.v1.ReportRuntimeRequest.observed_at:type_name -> google.protobuf.Timestamp
|
||||||
2, // 30: proxy_pool.controlplane.v1.CheckTask.protocol:type_name -> proxy_pool.controlplane.v1.ProxyProtocol
|
5, // 30: proxy_pool.controlplane.v1.StreamCheckTasksRequest.supported_levels:type_name -> proxy_pool.controlplane.v1.CheckLevel
|
||||||
4, // 31: proxy_pool.controlplane.v1.CheckTask.level:type_name -> proxy_pool.controlplane.v1.CheckLevel
|
3, // 31: proxy_pool.controlplane.v1.CheckTask.protocol:type_name -> proxy_pool.controlplane.v1.ProxyProtocol
|
||||||
31, // 32: proxy_pool.controlplane.v1.CheckTask.timeout:type_name -> google.protobuf.Duration
|
5, // 32: proxy_pool.controlplane.v1.CheckTask.level:type_name -> proxy_pool.controlplane.v1.CheckLevel
|
||||||
32, // 33: proxy_pool.controlplane.v1.CheckTask.deadline:type_name -> google.protobuf.Timestamp
|
32, // 33: proxy_pool.controlplane.v1.CheckTask.timeout:type_name -> google.protobuf.Duration
|
||||||
25, // 34: proxy_pool.controlplane.v1.ObservationBatch.observations:type_name -> proxy_pool.controlplane.v1.HealthObservation
|
33, // 34: proxy_pool.controlplane.v1.CheckTask.deadline:type_name -> google.protobuf.Timestamp
|
||||||
4, // 35: proxy_pool.controlplane.v1.HealthObservation.level:type_name -> proxy_pool.controlplane.v1.CheckLevel
|
26, // 35: proxy_pool.controlplane.v1.ObservationBatch.observations:type_name -> proxy_pool.controlplane.v1.HealthObservation
|
||||||
31, // 36: proxy_pool.controlplane.v1.HealthObservation.latency:type_name -> google.protobuf.Duration
|
5, // 36: proxy_pool.controlplane.v1.HealthObservation.level:type_name -> proxy_pool.controlplane.v1.CheckLevel
|
||||||
32, // 37: proxy_pool.controlplane.v1.HealthObservation.observed_at:type_name -> google.protobuf.Timestamp
|
32, // 37: proxy_pool.controlplane.v1.HealthObservation.latency:type_name -> google.protobuf.Duration
|
||||||
5, // 38: proxy_pool.controlplane.v1.WorkerControlPlane.RegisterWorker:input_type -> proxy_pool.controlplane.v1.RegisterWorkerRequest
|
33, // 38: proxy_pool.controlplane.v1.HealthObservation.observed_at:type_name -> google.protobuf.Timestamp
|
||||||
7, // 39: proxy_pool.controlplane.v1.WorkerControlPlane.WatchSnapshots:input_type -> proxy_pool.controlplane.v1.WatchSnapshotsRequest
|
6, // 39: proxy_pool.controlplane.v1.WorkerControlPlane.RegisterWorker:input_type -> proxy_pool.controlplane.v1.RegisterWorkerRequest
|
||||||
15, // 40: proxy_pool.controlplane.v1.WorkerControlPlane.AcknowledgeSnapshot:input_type -> proxy_pool.controlplane.v1.AcknowledgeSnapshotRequest
|
8, // 40: proxy_pool.controlplane.v1.WorkerControlPlane.WatchSnapshots:input_type -> proxy_pool.controlplane.v1.WatchSnapshotsRequest
|
||||||
16, // 41: proxy_pool.controlplane.v1.WorkerControlPlane.ReportOutcomes:input_type -> proxy_pool.controlplane.v1.OutcomeBatch
|
16, // 41: proxy_pool.controlplane.v1.WorkerControlPlane.AcknowledgeSnapshot:input_type -> proxy_pool.controlplane.v1.AcknowledgeSnapshotRequest
|
||||||
19, // 42: proxy_pool.controlplane.v1.WorkerControlPlane.ReportRuntime:input_type -> proxy_pool.controlplane.v1.ReportRuntimeRequest
|
17, // 42: proxy_pool.controlplane.v1.WorkerControlPlane.ReportOutcomes:input_type -> proxy_pool.controlplane.v1.OutcomeBatch
|
||||||
22, // 43: proxy_pool.controlplane.v1.CheckerControlPlane.StreamCheckTasks:input_type -> proxy_pool.controlplane.v1.StreamCheckTasksRequest
|
20, // 43: proxy_pool.controlplane.v1.WorkerControlPlane.ReportRuntime:input_type -> proxy_pool.controlplane.v1.ReportRuntimeRequest
|
||||||
24, // 44: proxy_pool.controlplane.v1.CheckerControlPlane.ReportObservations:input_type -> proxy_pool.controlplane.v1.ObservationBatch
|
23, // 44: proxy_pool.controlplane.v1.CheckerControlPlane.StreamCheckTasks:input_type -> proxy_pool.controlplane.v1.StreamCheckTasksRequest
|
||||||
6, // 45: proxy_pool.controlplane.v1.WorkerControlPlane.RegisterWorker:output_type -> proxy_pool.controlplane.v1.RegisterWorkerResponse
|
25, // 45: proxy_pool.controlplane.v1.CheckerControlPlane.ReportObservations:input_type -> proxy_pool.controlplane.v1.ObservationBatch
|
||||||
8, // 46: proxy_pool.controlplane.v1.WorkerControlPlane.WatchSnapshots:output_type -> proxy_pool.controlplane.v1.SnapshotEnvelope
|
7, // 46: proxy_pool.controlplane.v1.WorkerControlPlane.RegisterWorker:output_type -> proxy_pool.controlplane.v1.RegisterWorkerResponse
|
||||||
33, // 47: proxy_pool.controlplane.v1.WorkerControlPlane.AcknowledgeSnapshot:output_type -> google.protobuf.Empty
|
9, // 47: proxy_pool.controlplane.v1.WorkerControlPlane.WatchSnapshots:output_type -> proxy_pool.controlplane.v1.SnapshotEnvelope
|
||||||
18, // 48: proxy_pool.controlplane.v1.WorkerControlPlane.ReportOutcomes:output_type -> proxy_pool.controlplane.v1.ReportOutcomesResponse
|
34, // 48: proxy_pool.controlplane.v1.WorkerControlPlane.AcknowledgeSnapshot:output_type -> google.protobuf.Empty
|
||||||
21, // 49: proxy_pool.controlplane.v1.WorkerControlPlane.ReportRuntime:output_type -> proxy_pool.controlplane.v1.ReportRuntimeResponse
|
19, // 49: proxy_pool.controlplane.v1.WorkerControlPlane.ReportOutcomes:output_type -> proxy_pool.controlplane.v1.ReportOutcomesResponse
|
||||||
23, // 50: proxy_pool.controlplane.v1.CheckerControlPlane.StreamCheckTasks:output_type -> proxy_pool.controlplane.v1.CheckTask
|
22, // 50: proxy_pool.controlplane.v1.WorkerControlPlane.ReportRuntime:output_type -> proxy_pool.controlplane.v1.ReportRuntimeResponse
|
||||||
26, // 51: proxy_pool.controlplane.v1.CheckerControlPlane.ReportObservations:output_type -> proxy_pool.controlplane.v1.ReportObservationsResponse
|
24, // 51: proxy_pool.controlplane.v1.CheckerControlPlane.StreamCheckTasks:output_type -> proxy_pool.controlplane.v1.CheckTask
|
||||||
45, // [45:52] is the sub-list for method output_type
|
27, // 52: proxy_pool.controlplane.v1.CheckerControlPlane.ReportObservations:output_type -> proxy_pool.controlplane.v1.ReportObservationsResponse
|
||||||
38, // [38:45] is the sub-list for method input_type
|
46, // [46:53] is the sub-list for method output_type
|
||||||
38, // [38:38] is the sub-list for extension type_name
|
39, // [39:46] is the sub-list for method input_type
|
||||||
38, // [38:38] is the sub-list for extension extendee
|
39, // [39:39] is the sub-list for extension type_name
|
||||||
0, // [0:38] is the sub-list for field type_name
|
39, // [39:39] is the sub-list for extension extendee
|
||||||
|
0, // [0:39] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_controlplane_v1_controlplane_proto_init() }
|
func init() { file_controlplane_v1_controlplane_proto_init() }
|
||||||
@ -2549,7 +2617,7 @@ func file_controlplane_v1_controlplane_proto_init() {
|
|||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controlplane_v1_controlplane_proto_rawDesc), len(file_controlplane_v1_controlplane_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_controlplane_v1_controlplane_proto_rawDesc), len(file_controlplane_v1_controlplane_proto_rawDesc)),
|
||||||
NumEnums: 5,
|
NumEnums: 6,
|
||||||
NumMessages: 26,
|
NumMessages: 26,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 2,
|
NumServices: 2,
|
||||||
|
|||||||
@ -214,6 +214,7 @@ type Routing struct {
|
|||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
Purpose string `yaml:"purpose"`
|
Purpose string `yaml:"purpose"`
|
||||||
|
Action string `yaml:"action"`
|
||||||
Match RoutingMatch `yaml:"match"`
|
Match RoutingMatch `yaml:"match"`
|
||||||
Upstreams []string `yaml:"upstreams"`
|
Upstreams []string `yaml:"upstreams"`
|
||||||
Strategy Strategy `yaml:"strategy"`
|
Strategy Strategy `yaml:"strategy"`
|
||||||
|
|||||||
@ -488,6 +488,22 @@ func TestValidateGatewayStickySession(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateStaticGatewayRoutingAction(t *testing.T) {
|
||||||
|
cfg := mustLoadValidConfig(t)
|
||||||
|
cfg.Routing[0] = Routing{
|
||||||
|
Name: "direct-api", Enabled: true, Purpose: "gateway", Action: "direct",
|
||||||
|
Match: RoutingMatch{HostRegex: "^api\\.example\\.test$"},
|
||||||
|
}
|
||||||
|
if err := Validate(cfg); err != nil {
|
||||||
|
t.Fatalf("Validate(static direct routing) = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Routing[0].Purpose = "extract"
|
||||||
|
if err := Validate(cfg); err == nil || !strings.Contains(err.Error(), "only supported on gateway") {
|
||||||
|
t.Fatalf("Validate(extract direct routing) error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateGatewayTransport(t *testing.T) {
|
func TestValidateGatewayTransport(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@ -423,6 +423,28 @@ func validateRouting(index int, route Routing, upstreams map[string]Upstream, se
|
|||||||
return fmt.Errorf("validate %s pathRegex: %w", scope, err)
|
return fmt.Errorf("validate %s pathRegex: %w", scope, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
action := routingAction(route.Action)
|
||||||
|
if err := validateEnum(scope+" action", action, "proxy", "direct", "reject"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if action != "proxy" {
|
||||||
|
if route.Purpose != "gateway" {
|
||||||
|
return fmt.Errorf("validate %s action: only supported on gateway", scope)
|
||||||
|
}
|
||||||
|
if len(route.Upstreams) != 0 {
|
||||||
|
return fmt.Errorf("validate %s action: direct and reject routes must not reference upstreams", scope)
|
||||||
|
}
|
||||||
|
if routingStrategyConfigured(route.Strategy) {
|
||||||
|
return fmt.Errorf("validate %s action: direct and reject routes must not configure strategy", scope)
|
||||||
|
}
|
||||||
|
if route.OnUnavailable.Action != "" || route.OnUnavailable.WaitTimeout != 0 {
|
||||||
|
return fmt.Errorf("validate %s action: direct and reject routes must not configure onUnavailable", scope)
|
||||||
|
}
|
||||||
|
if len(route.Check.Targets) != 0 {
|
||||||
|
return fmt.Errorf("validate %s action: direct and reject routes must not configure check targets", scope)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for _, upstream := range route.Upstreams {
|
for _, upstream := range route.Upstreams {
|
||||||
if _, ok := upstreams[upstream]; !ok {
|
if _, ok := upstreams[upstream]; !ok {
|
||||||
return fmt.Errorf("validate %s: upstream %q does not exist", scope, upstream)
|
return fmt.Errorf("validate %s: upstream %q does not exist", scope, upstream)
|
||||||
@ -446,6 +468,20 @@ func validateRouting(index int, route Routing, upstreams map[string]Upstream, se
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func routingAction(value string) string {
|
||||||
|
if value == "" {
|
||||||
|
return "proxy"
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func routingStrategyConfigured(strategy Strategy) bool {
|
||||||
|
return strategy.Type != "" ||
|
||||||
|
strategy.SwitchAfterEmptyFetch != 0 ||
|
||||||
|
strategy.EndBehavior != "" ||
|
||||||
|
len(strategy.Weights) != 0
|
||||||
|
}
|
||||||
|
|
||||||
func validateStrategy(scope string, upstreams []string, strategy Strategy) error {
|
func validateStrategy(scope string, upstreams []string, strategy Strategy) error {
|
||||||
if err := validateEnum(scope+" strategy.type", strategy.Type,
|
if err := validateEnum(scope+" strategy.type", strategy.Type,
|
||||||
"sequential", "random", "roundRobin", "weighted", "leastConnections"); err != nil {
|
"sequential", "random", "roundRobin", "weighted", "leastConnections"); err != nil {
|
||||||
|
|||||||
@ -98,29 +98,42 @@ func buildGatewayRouting(configuration *config.Config, state *adminstate.Snapsho
|
|||||||
if !hasState && routingStates != nil {
|
if !hasState && routingStates != nil {
|
||||||
return nil, fmt.Errorf("routing %q is missing from admin state", configured.Name)
|
return nil, fmt.Errorf("routing %q is missing from admin state", configured.Name)
|
||||||
}
|
}
|
||||||
|
routeAction, err := wireRoutingAction(configured.Action)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("routing %q action: %w", configured.Name, err)
|
||||||
|
}
|
||||||
|
enabled := configured.Enabled && (routingStates == nil || state.Enabled)
|
||||||
|
if routeAction != controlplanev1.RoutingAction_ROUTING_ACTION_PROXY {
|
||||||
|
rules = append(rules, &controlplanev1.RoutingRule{
|
||||||
|
Name: configured.Name, Enabled: enabled, HostRegex: configured.Match.HostRegex,
|
||||||
|
Methods: append([]string(nil), configured.Match.Methods...), PathRegex: configured.Match.PathRegex,
|
||||||
|
Headers: cloneRoutingHeaders(configured.Match.Headers), Action: routeAction,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
upstreams := enabledRoutingUpstreams(configured.Upstreams, upstreamEnabled)
|
upstreams := enabledRoutingUpstreams(configured.Upstreams, upstreamEnabled)
|
||||||
current := configuredCurrentUpstream(configured, state, hasState)
|
current := configuredCurrentUpstream(configured, state, hasState)
|
||||||
strategy, err := wireRoutingStrategy(configured.Strategy, current, upstreams)
|
strategy, err := wireRoutingStrategy(configured.Strategy, current, upstreams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("routing %q strategy: %w", configured.Name, err)
|
return nil, fmt.Errorf("routing %q strategy: %w", configured.Name, err)
|
||||||
}
|
}
|
||||||
enabled := configured.Enabled && (routingStates == nil || state.Enabled) && len(upstreams) > 0
|
enabled = enabled && len(upstreams) > 0
|
||||||
if strategy.GetType() == controlplanev1.StrategyType_STRATEGY_TYPE_SEQUENTIAL && !containsUpstream(upstreams, current) {
|
if strategy.GetType() == controlplanev1.StrategyType_STRATEGY_TYPE_SEQUENTIAL && !containsUpstream(upstreams, current) {
|
||||||
enabled = false
|
enabled = false
|
||||||
}
|
}
|
||||||
action, err := wireUnavailableAction(configured.OnUnavailable.Action)
|
unavailableAction, err := wireUnavailableAction(configured.OnUnavailable.Action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("routing %q unavailable action: %w", configured.Name, err)
|
return nil, fmt.Errorf("routing %q unavailable action: %w", configured.Name, err)
|
||||||
}
|
}
|
||||||
var waitTimeout *durationpb.Duration
|
var waitTimeout *durationpb.Duration
|
||||||
if action == controlplanev1.UnavailableAction_UNAVAILABLE_ACTION_WAIT {
|
if unavailableAction == controlplanev1.UnavailableAction_UNAVAILABLE_ACTION_WAIT {
|
||||||
waitTimeout = durationpb.New(configured.OnUnavailable.WaitTimeout.Value())
|
waitTimeout = durationpb.New(configured.OnUnavailable.WaitTimeout.Value())
|
||||||
}
|
}
|
||||||
rules = append(rules, &controlplanev1.RoutingRule{
|
rules = append(rules, &controlplanev1.RoutingRule{
|
||||||
Name: configured.Name, Enabled: enabled, HostRegex: configured.Match.HostRegex,
|
Name: configured.Name, Enabled: enabled, HostRegex: configured.Match.HostRegex,
|
||||||
Methods: append([]string(nil), configured.Match.Methods...), PathRegex: configured.Match.PathRegex,
|
Methods: append([]string(nil), configured.Match.Methods...), PathRegex: configured.Match.PathRegex,
|
||||||
Headers: cloneRoutingHeaders(configured.Match.Headers), Upstreams: upstreams, Strategy: strategy,
|
Headers: cloneRoutingHeaders(configured.Match.Headers), Upstreams: upstreams, Strategy: strategy,
|
||||||
OnUnavailable: action, WaitTimeout: waitTimeout,
|
OnUnavailable: unavailableAction, WaitTimeout: waitTimeout, Action: controlplanev1.RoutingAction_ROUTING_ACTION_PROXY,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return rules, nil
|
return rules, nil
|
||||||
@ -232,6 +245,19 @@ func wireUnavailableAction(action string) (controlplanev1.UnavailableAction, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wireRoutingAction(action string) (controlplanev1.RoutingAction, error) {
|
||||||
|
switch action {
|
||||||
|
case "", "proxy":
|
||||||
|
return controlplanev1.RoutingAction_ROUTING_ACTION_PROXY, nil
|
||||||
|
case "direct":
|
||||||
|
return controlplanev1.RoutingAction_ROUTING_ACTION_DIRECT, nil
|
||||||
|
case "reject":
|
||||||
|
return controlplanev1.RoutingAction_ROUTING_ACTION_REJECT, nil
|
||||||
|
default:
|
||||||
|
return controlplanev1.RoutingAction_ROUTING_ACTION_UNSPECIFIED, fmt.Errorf("unsupported action %q", action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func cloneRoutingHeaders(source map[string]string) map[string]string {
|
func cloneRoutingHeaders(source map[string]string) map[string]string {
|
||||||
if source == nil {
|
if source == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -114,6 +114,27 @@ func TestGatewayRoutingSourceDisablesSequentialRuleWhenCurrentUpstreamIsUnavaila
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGatewayRoutingSourceBuildsStaticDirectRuleWithoutUpstreams(t *testing.T) {
|
||||||
|
configuration := &config.Config{Routing: []config.Routing{{
|
||||||
|
Name: "direct-api", Enabled: true, Purpose: "gateway", Action: "direct",
|
||||||
|
Match: config.RoutingMatch{HostRegex: "^api\\.example\\.test$"},
|
||||||
|
}}}
|
||||||
|
source, err := NewGatewayRoutingSource(staticGatewayRoutingConfiguration{configuration: configuration, revision: 1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewGatewayRoutingSource(): %v", err)
|
||||||
|
}
|
||||||
|
rules, err := source.Read(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Read(): %v", err)
|
||||||
|
}
|
||||||
|
if len(rules) != 1 || !rules[0].GetEnabled() ||
|
||||||
|
rules[0].GetAction() != controlplanev1.RoutingAction_ROUTING_ACTION_DIRECT ||
|
||||||
|
len(rules[0].GetUpstreams()) != 0 || rules[0].GetStrategy() != nil ||
|
||||||
|
rules[0].GetOnUnavailable() != controlplanev1.UnavailableAction_UNAVAILABLE_ACTION_UNSPECIFIED {
|
||||||
|
t.Fatalf("rules = %+v", rules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type staticGatewayRoutingConfiguration struct {
|
type staticGatewayRoutingConfiguration struct {
|
||||||
configuration *config.Config
|
configuration *config.Config
|
||||||
revision uint64
|
revision uint64
|
||||||
|
|||||||
@ -187,6 +187,26 @@ func wireRouting(source []*controlplanev1.RoutingRule) ([]routingDomain.Rule, er
|
|||||||
if !rule.GetEnabled() {
|
if !rule.GetEnabled() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
routeAction, err := wireRoutingAction(rule.GetAction())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if routeAction != routingDomain.ActionProxy {
|
||||||
|
if len(rule.GetUpstreams()) != 0 || rule.GetStrategy() != nil ||
|
||||||
|
rule.GetOnUnavailable() != controlplanev1.UnavailableAction_UNAVAILABLE_ACTION_UNSPECIFIED ||
|
||||||
|
rule.GetWaitTimeout() != nil {
|
||||||
|
return nil, ErrInvalidSnapshotWatcher
|
||||||
|
}
|
||||||
|
result = append(result, routingDomain.Rule{
|
||||||
|
Name: rule.GetName(),
|
||||||
|
Match: routingDomain.Match{
|
||||||
|
HostRegex: rule.GetHostRegex(), Methods: append([]string(nil), rule.GetMethods()...),
|
||||||
|
PathRegex: rule.GetPathRegex(), Headers: cloneRoutingHeaders(rule.GetHeaders()),
|
||||||
|
},
|
||||||
|
Action: routeAction,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
upstreams, err := wireRoutingUpstreams(rule.GetUpstreams())
|
upstreams, err := wireRoutingUpstreams(rule.GetUpstreams())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -195,11 +215,11 @@ func wireRouting(source []*controlplanev1.RoutingRule) ([]routingDomain.Rule, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
action, err := wireUnavailableAction(rule.GetOnUnavailable())
|
unavailableAction, err := wireUnavailableAction(rule.GetOnUnavailable())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
waitTimeout, err := wireWaitTimeout(action, rule.GetWaitTimeout())
|
waitTimeout, err := wireWaitTimeout(unavailableAction, rule.GetWaitTimeout())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -209,8 +229,8 @@ func wireRouting(source []*controlplanev1.RoutingRule) ([]routingDomain.Rule, er
|
|||||||
HostRegex: rule.GetHostRegex(), Methods: append([]string(nil), rule.GetMethods()...),
|
HostRegex: rule.GetHostRegex(), Methods: append([]string(nil), rule.GetMethods()...),
|
||||||
PathRegex: rule.GetPathRegex(), Headers: cloneRoutingHeaders(rule.GetHeaders()),
|
PathRegex: rule.GetPathRegex(), Headers: cloneRoutingHeaders(rule.GetHeaders()),
|
||||||
},
|
},
|
||||||
Upstreams: upstreams, Action: routingDomain.ActionProxy, Strategy: strategy,
|
Upstreams: upstreams, Action: routeAction, Strategy: strategy,
|
||||||
OnUnavailable: action, WaitTimeout: waitTimeout,
|
OnUnavailable: unavailableAction, WaitTimeout: waitTimeout,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -286,6 +306,19 @@ func wireUnavailableAction(action controlplanev1.UnavailableAction) (routingDoma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wireRoutingAction(action controlplanev1.RoutingAction) (routingDomain.Action, error) {
|
||||||
|
switch action {
|
||||||
|
case controlplanev1.RoutingAction_ROUTING_ACTION_UNSPECIFIED, controlplanev1.RoutingAction_ROUTING_ACTION_PROXY:
|
||||||
|
return routingDomain.ActionProxy, nil
|
||||||
|
case controlplanev1.RoutingAction_ROUTING_ACTION_DIRECT:
|
||||||
|
return routingDomain.ActionDirect, nil
|
||||||
|
case controlplanev1.RoutingAction_ROUTING_ACTION_REJECT:
|
||||||
|
return routingDomain.ActionReject, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("%w: unsupported routing action", ErrInvalidSnapshotWatcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func wireWaitTimeout(action routingDomain.OnUnavailableAction, source *durationpb.Duration) (time.Duration, error) {
|
func wireWaitTimeout(action routingDomain.OnUnavailableAction, source *durationpb.Duration) (time.Duration, error) {
|
||||||
if source == nil {
|
if source == nil {
|
||||||
if action == routingDomain.OnUnavailableWait {
|
if action == routingDomain.OnUnavailableWait {
|
||||||
|
|||||||
@ -141,6 +141,30 @@ func TestSnapshotWatcherRejectsWaitRoutingWithoutTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWireRoutingAcceptsStaticDirectAction(t *testing.T) {
|
||||||
|
rules, err := wireRouting([]*controlplanev1.RoutingRule{{
|
||||||
|
Name: "direct-api", Enabled: true, HostRegex: "^api\\.example\\.test$",
|
||||||
|
Action: controlplanev1.RoutingAction_ROUTING_ACTION_DIRECT,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("wireRouting(): %v", err)
|
||||||
|
}
|
||||||
|
if len(rules) != 1 || rules[0].Action != routing.ActionDirect || len(rules[0].Upstreams) != 0 ||
|
||||||
|
rules[0].Strategy.Type != "" || rules[0].OnUnavailable != "" {
|
||||||
|
t.Fatalf("rules = %+v", rules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWireRoutingRejectsStaticDirectActionWithProxySettings(t *testing.T) {
|
||||||
|
_, err := wireRouting([]*controlplanev1.RoutingRule{{
|
||||||
|
Name: "direct-api", Enabled: true, HostRegex: "^api\\.example\\.test$", Upstreams: []string{"provider-a"},
|
||||||
|
Action: controlplanev1.RoutingAction_ROUTING_ACTION_DIRECT,
|
||||||
|
}})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("wireRouting() error = nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type snapshotClientStub struct {
|
type snapshotClientStub struct {
|
||||||
stream SnapshotStream
|
stream SnapshotStream
|
||||||
watch *controlplanev1.WatchSnapshotsRequest
|
watch *controlplanev1.WatchSnapshotsRequest
|
||||||
|
|||||||
@ -19,6 +19,7 @@ var (
|
|||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
RoutingName string
|
RoutingName string
|
||||||
|
Action routing.Action
|
||||||
Strategy routing.Strategy
|
Strategy routing.Strategy
|
||||||
OnUnavailable routing.OnUnavailableAction
|
OnUnavailable routing.OnUnavailableAction
|
||||||
WaitTimeout time.Duration
|
WaitTimeout time.Duration
|
||||||
|
|||||||
@ -219,6 +219,10 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Requ
|
|||||||
writeGatewayError(writer, err)
|
writeGatewayError(writer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if route.Action == routing.ActionDirect {
|
||||||
|
handler.connectDirect(writer, request, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
binding, err := handler.prepareStickySession(request, &route)
|
binding, err := handler.prepareStickySession(request, &route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeGatewayError(writer, err)
|
writeGatewayError(writer, err)
|
||||||
@ -245,6 +249,10 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Requ
|
|||||||
writeGatewayError(writer, err)
|
writeGatewayError(writer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if route.Action == routing.ActionDirect {
|
||||||
|
handler.forwardHTTPDirect(writer, request, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
binding, err := handler.prepareStickySession(request, &route)
|
binding, err := handler.prepareStickySession(request, &route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeGatewayError(writer, err)
|
writeGatewayError(writer, err)
|
||||||
@ -272,12 +280,7 @@ func (handler *Handler) connect(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
if errors.Is(err, dispatch.ErrNoCandidate) && route.OnUnavailable == routing.OnUnavailableDirect {
|
if errors.Is(err, dispatch.ErrNoCandidate) && route.OnUnavailable == routing.OnUnavailableDirect {
|
||||||
direct, directErr := handler.directTransport()
|
upstream, directErr := handler.openDirectTunnel(request.Context(), target.DialAddress())
|
||||||
if directErr != nil {
|
|
||||||
lastErr = directErr
|
|
||||||
break
|
|
||||||
}
|
|
||||||
upstream, directErr := direct.OpenDirectTunnel(request.Context(), target.DialAddress())
|
|
||||||
if directErr != nil {
|
if directErr != nil {
|
||||||
lastErr = directErr
|
lastErr = directErr
|
||||||
break
|
break
|
||||||
@ -326,6 +329,15 @@ func (handler *Handler) connect(
|
|||||||
writeGatewayError(writer, fmt.Errorf("establish gateway CONNECT: %w", lastErr))
|
writeGatewayError(writer, fmt.Errorf("establish gateway CONNECT: %w", lastErr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) connectDirect(writer http.ResponseWriter, request *http.Request, target policy.Authority) {
|
||||||
|
upstream, err := handler.openDirectTunnel(request.Context(), target.DialAddress())
|
||||||
|
if err != nil {
|
||||||
|
writeGatewayError(writer, fmt.Errorf("establish direct CONNECT: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler.serveTunnel(writer, request, nil, upstream, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
func (handler *Handler) writeConnectError(writer http.ResponseWriter, responseError *transportDomain.ProxyResponseError) {
|
func (handler *Handler) writeConnectError(writer http.ResponseWriter, responseError *transportDomain.ProxyResponseError) {
|
||||||
header := responseError.Header.Clone()
|
header := responseError.Header.Clone()
|
||||||
removeHopByHop(header)
|
removeHopByHop(header)
|
||||||
@ -517,12 +529,7 @@ func (handler *Handler) forwardHTTP(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
if errors.Is(err, dispatch.ErrNoCandidate) && route.OnUnavailable == routing.OnUnavailableDirect {
|
if errors.Is(err, dispatch.ErrNoCandidate) && route.OnUnavailable == routing.OnUnavailableDirect {
|
||||||
direct, directErr := handler.directTransport()
|
response, directErr := handler.roundTripDirect(request.Context(), attemptRequest)
|
||||||
if directErr != nil {
|
|
||||||
lastErr = directErr
|
|
||||||
break
|
|
||||||
}
|
|
||||||
response, directErr := direct.RoundTripDirect(request.Context(), attemptRequest)
|
|
||||||
if directErr != nil {
|
if directErr != nil {
|
||||||
lastErr = directErr
|
lastErr = directErr
|
||||||
break
|
break
|
||||||
@ -580,6 +587,22 @@ func (handler *Handler) forwardHTTP(
|
|||||||
writeGatewayError(writer, fmt.Errorf("forward gateway request: %w", lastErr))
|
writeGatewayError(writer, fmt.Errorf("forward gateway request: %w", lastErr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) forwardHTTPDirect(writer http.ResponseWriter, request *http.Request, target policy.Authority) {
|
||||||
|
attemptRequest, err := requestForAttempt(request, 0)
|
||||||
|
if err != nil {
|
||||||
|
writeGatewayError(writer, fmt.Errorf("prepare direct request: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pinHTTPDestination(attemptRequest, target)
|
||||||
|
removeHopByHop(attemptRequest.Header)
|
||||||
|
response, err := handler.roundTripDirect(request.Context(), attemptRequest)
|
||||||
|
if err != nil {
|
||||||
|
writeGatewayError(writer, fmt.Errorf("forward direct request: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler.writeResponse(writer, response)
|
||||||
|
}
|
||||||
|
|
||||||
func (handler *Handler) recordOutcome(
|
func (handler *Handler) recordOutcome(
|
||||||
proxyID, routingName string,
|
proxyID, routingName string,
|
||||||
stage outcomeDomain.Stage,
|
stage outcomeDomain.Stage,
|
||||||
@ -688,6 +711,22 @@ func (handler *Handler) directTransport() (DirectTransport, error) {
|
|||||||
return direct, nil
|
return direct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) openDirectTunnel(ctx context.Context, target string) (net.Conn, error) {
|
||||||
|
direct, err := handler.directTransport()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return direct.OpenDirectTunnel(ctx, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) roundTripDirect(ctx context.Context, request *http.Request) (*http.Response, error) {
|
||||||
|
direct, err := handler.directTransport()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return direct.RoundTripDirect(ctx, request)
|
||||||
|
}
|
||||||
|
|
||||||
func pinHTTPDestination(request *http.Request, target policy.Authority) {
|
func pinHTTPDestination(request *http.Request, target policy.Authority) {
|
||||||
if request == nil || request.URL == nil || (!target.ResolvedIP.IsValid() && !target.LiteralIP.IsValid()) {
|
if request == nil || request.URL == nil || (!target.ResolvedIP.IsValid() && !target.LiteralIP.IsValid()) {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ func TestHandlerReportsLocalRequestAndTunnelLifecycles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandlerRejectsGatewayRoutingOutsideCredentialPolicy(t *testing.T) {
|
func TestHandlerRejectsStaticDirectRouteOutsideCredentialPolicy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
authentication, err := httpsecurity.New(httpsecurity.Config{
|
authentication, err := httpsecurity.New(httpsecurity.Config{
|
||||||
@ -137,7 +138,7 @@ func TestHandlerRejectsGatewayRoutingOutsideCredentialPolicy(t *testing.T) {
|
|||||||
return policy.Authority{Host: "example.test", Port: 80}, nil
|
return policy.Authority{Host: "example.test", Port: 80}, nil
|
||||||
}},
|
}},
|
||||||
Router: RouteFunc(func(*http.Request) (dispatch.Request, error) {
|
Router: RouteFunc(func(*http.Request) (dispatch.Request, error) {
|
||||||
return dispatch.Request{RoutingName: "catalog", Upstreams: []string{"provider-a"}}, nil
|
return dispatch.Request{RoutingName: "catalog", Action: routing.ActionDirect}, nil
|
||||||
}),
|
}),
|
||||||
Dispatcher: DispatcherFunc(func(dispatch.Request) (*dispatch.Lease, error) {
|
Dispatcher: DispatcherFunc(func(dispatch.Request) (*dispatch.Lease, error) {
|
||||||
t.Fatal("dispatcher must not run for a route outside the credential policy")
|
t.Fatal("dispatcher must not run for a route outside the credential policy")
|
||||||
@ -159,6 +160,37 @@ func TestHandlerRejectsGatewayRoutingOutsideCredentialPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandlerEvaluatesTargetPolicyBeforeStaticDirectRoute(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
handler, err := New(Config{}, Dependencies{
|
||||||
|
Targets: fakeTargets{evaluateURL: func(context.Context, string) (policy.Authority, error) {
|
||||||
|
return policy.Authority{}, policy.ErrTargetDenied
|
||||||
|
}},
|
||||||
|
Router: RouteFunc(func(*http.Request) (dispatch.Request, error) {
|
||||||
|
t.Fatal("router must not run before target policy")
|
||||||
|
return dispatch.Request{}, nil
|
||||||
|
}),
|
||||||
|
Dispatcher: DispatcherFunc(func(dispatch.Request) (*dispatch.Lease, error) {
|
||||||
|
t.Fatal("dispatcher must not run for a denied target")
|
||||||
|
return nil, nil
|
||||||
|
}),
|
||||||
|
Transport: &fakeTransport{directRoundTrip: func(context.Context, *http.Request) (*http.Response, error) {
|
||||||
|
t.Fatal("direct transport must not run for a denied target")
|
||||||
|
return nil, nil
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "http://blocked.example/resource", nil))
|
||||||
|
if response.Code != http.StatusForbidden {
|
||||||
|
t.Fatalf("status = %d, want 403", response.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHandlerReleasesCredentialConcurrencyAfterRequest(t *testing.T) {
|
func TestHandlerReleasesCredentialConcurrencyAfterRequest(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -584,6 +616,91 @@ func TestHandlerUsesDirectFallbackAfterTargetPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandlerUsesStaticDirectRouteForHTTP(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var dispatcherCalls atomic.Int64
|
||||||
|
transport := &fakeTransport{directRoundTrip: func(_ context.Context, request *http.Request) (*http.Response, error) {
|
||||||
|
if request.URL.Host != "198.51.100.10:80" || request.Host != "example.test" {
|
||||||
|
t.Fatalf("direct request = URL %q Host %q", request.URL.Host, request.Host)
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: http.StatusNoContent, Header: make(http.Header), Body: http.NoBody}, nil
|
||||||
|
}}
|
||||||
|
handler, err := New(Config{StickySession: StickySessionConfig{
|
||||||
|
Header: "X-Proxy-Session", TTL: time.Minute, MaxEntries: 10,
|
||||||
|
}}, Dependencies{
|
||||||
|
Targets: fakeTargets{evaluateURL: func(context.Context, string) (policy.Authority, error) {
|
||||||
|
return policy.Authority{Host: "example.test", Port: 80, ResolvedIP: netip.MustParseAddr("198.51.100.10")}, nil
|
||||||
|
}},
|
||||||
|
Router: RouteFunc(func(*http.Request) (dispatch.Request, error) {
|
||||||
|
return dispatch.Request{RoutingName: "direct-api", Action: routing.ActionDirect}, nil
|
||||||
|
}),
|
||||||
|
Dispatcher: DispatcherFunc(func(dispatch.Request) (*dispatch.Lease, error) {
|
||||||
|
dispatcherCalls.Add(1)
|
||||||
|
return nil, dispatch.ErrNoCandidate
|
||||||
|
}),
|
||||||
|
Transport: transport,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New(): %v", err)
|
||||||
|
}
|
||||||
|
recorder := &outcomeRecorder{}
|
||||||
|
handler.outcomes = recorder
|
||||||
|
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "http://example.test/resource", nil))
|
||||||
|
|
||||||
|
if response.Code != http.StatusNoContent || dispatcherCalls.Load() != 0 || len(recorder.Events()) != 0 {
|
||||||
|
t.Fatalf("response = %d, dispatcher calls = %d, outcomes = %+v", response.Code, dispatcherCalls.Load(), recorder.Events())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandlerUsesStaticDirectRouteForCONNECT(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var dispatcherCalls atomic.Int64
|
||||||
|
var directCalls atomic.Int64
|
||||||
|
transport := &fakeTransport{
|
||||||
|
directTunnel: func(_ context.Context, target string) (net.Conn, error) {
|
||||||
|
if target != "198.51.100.10:443" {
|
||||||
|
t.Fatalf("direct target = %q", target)
|
||||||
|
}
|
||||||
|
directCalls.Add(1)
|
||||||
|
upstream, peer := net.Pipe()
|
||||||
|
_ = peer.Close()
|
||||||
|
return upstream, nil
|
||||||
|
},
|
||||||
|
relay: func(context.Context, net.Conn, net.Conn) error { return nil },
|
||||||
|
}
|
||||||
|
handler, err := New(Config{}, Dependencies{
|
||||||
|
Targets: fakeTargets{evaluateConnect: func(context.Context, string) (policy.Authority, error) {
|
||||||
|
return policy.Authority{Host: "example.test", Port: 443, ResolvedIP: netip.MustParseAddr("198.51.100.10")}, nil
|
||||||
|
}},
|
||||||
|
Router: RouteFunc(func(*http.Request) (dispatch.Request, error) {
|
||||||
|
return dispatch.Request{RoutingName: "direct-connect", Action: routing.ActionDirect}, nil
|
||||||
|
}),
|
||||||
|
Dispatcher: DispatcherFunc(func(dispatch.Request) (*dispatch.Lease, error) {
|
||||||
|
dispatcherCalls.Add(1)
|
||||||
|
return nil, dispatch.ErrNoCandidate
|
||||||
|
}),
|
||||||
|
Transport: transport,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New(): %v", err)
|
||||||
|
}
|
||||||
|
recorder := &outcomeRecorder{}
|
||||||
|
handler.outcomes = recorder
|
||||||
|
gateway := httptest.NewServer(handler)
|
||||||
|
defer gateway.Close()
|
||||||
|
|
||||||
|
response := sendConnect(t, gateway.URL, "example.test:443")
|
||||||
|
defer response.Body.Close()
|
||||||
|
if response.StatusCode != http.StatusOK || directCalls.Load() != 1 || dispatcherCalls.Load() != 0 || len(recorder.Events()) != 0 {
|
||||||
|
t.Fatalf("response = %d, direct calls = %d, dispatcher calls = %d, outcomes = %+v",
|
||||||
|
response.StatusCode, directCalls.Load(), dispatcherCalls.Load(), recorder.Events())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHandlerWaitsOnlyForWaitRoutingAction(t *testing.T) {
|
func TestHandlerWaitsOnlyForWaitRoutingAction(t *testing.T) {
|
||||||
dispatcher := &waitRecordingDispatcher{}
|
dispatcher := &waitRecordingDispatcher{}
|
||||||
handler := &Handler{dispatcher: dispatcher}
|
handler := &Handler{dispatcher: dispatcher}
|
||||||
|
|||||||
@ -98,13 +98,13 @@ func routeWithMatcher(request *http.Request, match routeMatcher) (dispatch.Reque
|
|||||||
switch matched.Action {
|
switch matched.Action {
|
||||||
case routing.ActionProxy:
|
case routing.ActionProxy:
|
||||||
return dispatch.Request{
|
return dispatch.Request{
|
||||||
RoutingName: matched.Name, Upstreams: append([]string(nil), matched.Upstreams...),
|
RoutingName: matched.Name, Action: matched.Action, Upstreams: append([]string(nil), matched.Upstreams...),
|
||||||
Strategy: matched.Strategy, OnUnavailable: matched.OnUnavailable, WaitTimeout: matched.WaitTimeout,
|
Strategy: matched.Strategy, OnUnavailable: matched.OnUnavailable, WaitTimeout: matched.WaitTimeout,
|
||||||
}, nil
|
}, nil
|
||||||
case routing.ActionReject:
|
case routing.ActionReject:
|
||||||
return dispatch.Request{}, fmt.Errorf("%w: %s", ErrRouteRejected, matched.Name)
|
return dispatch.Request{}, fmt.Errorf("%w: %s", ErrRouteRejected, matched.Name)
|
||||||
case routing.ActionDirect:
|
case routing.ActionDirect:
|
||||||
return dispatch.Request{}, fmt.Errorf("%w: %s", ErrDirectRouteUnsupported, matched.Name)
|
return dispatch.Request{RoutingName: matched.Name, Action: matched.Action}, nil
|
||||||
default:
|
default:
|
||||||
return dispatch.Request{}, fmt.Errorf("%w: %s has action %q", ErrRouteRejected, matched.Name, matched.Action)
|
return dispatch.Request{}, fmt.Errorf("%w: %s has action %q", ErrRouteRejected, matched.Name, matched.Action)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,25 @@ func TestRulesRouterReturnsMatchedUpstreams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRulesRouterReturnsStaticDirectRoute(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rules, err := routing.Compile([]routing.Rule{{
|
||||||
|
Name: "direct-api", Match: routing.Match{HostRegex: "^api\\.example\\.test$"},
|
||||||
|
Action: routing.ActionDirect,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("routing.Compile() error = %v", err)
|
||||||
|
}
|
||||||
|
result, err := NewRulesRouter(rules).Route(httptest.NewRequest(http.MethodGet, "http://api.example.test/v1/items", nil))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Route() error = %v", err)
|
||||||
|
}
|
||||||
|
if result.RoutingName != "direct-api" || result.Action != routing.ActionDirect || len(result.Upstreams) != 0 {
|
||||||
|
t.Fatalf("Route() = %+v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRulesRouterRejectsExplicitRejectAndMissingRoute(t *testing.T) {
|
func TestRulesRouterRejectsExplicitRejectAndMissingRoute(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user