Skip to content

Commit bd61026

Browse files
authored
Add client-policy types (#2236)
This change introduces the client policy types that will be used to configure outbound proxies. These types are analogous to those used for server policy in inbound proxies. They support integration with the Kubernetes Gateway API. These types are relatively sketchy at the moment. They will change as they are integrated into the outbound proxy.
1 parent 91bbbd4 commit bd61026

File tree

7 files changed

+341
-0
lines changed

7 files changed

+341
-0
lines changed

Cargo.lock

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,19 @@ dependencies = [
13491349
"tracing",
13501350
]
13511351

1352+
[[package]]
1353+
name = "linkerd-proxy-client-policy"
1354+
version = "0.1.0"
1355+
dependencies = [
1356+
"http",
1357+
"ipnet",
1358+
"linkerd-http-route",
1359+
"linkerd-proxy-api-resolve",
1360+
"linkerd-proxy-core",
1361+
"maplit",
1362+
"quickcheck",
1363+
]
1364+
13521365
[[package]]
13531366
name = "linkerd-proxy-core"
13541367
version = "0.1.0"

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ members = [
4040
"linkerd/opencensus",
4141
"linkerd/proxy/api-resolve",
4242
"linkerd/proxy/balance",
43+
"linkerd/proxy/client-policy",
4344
"linkerd/proxy/core",
4445
"linkerd/proxy/dns-resolve",
4546
"linkerd/proxy/http",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "linkerd-proxy-client-policy"
3+
version = "0.1.0"
4+
authors = ["Linkerd Developers <[email protected]>"]
5+
license = "Apache-2.0"
6+
edition = "2021"
7+
publish = false
8+
9+
[features]
10+
# proto = [
11+
# "linkerd-http-route/proto",
12+
# "linkerd-proxy-core/proto",
13+
# "linkerd2-proxy-api",
14+
# "prost-types",
15+
# ]
16+
17+
[dependencies]
18+
ipnet = "2"
19+
http = "0.2"
20+
linkerd-proxy-api-resolve = { path = "../api-resolve" }
21+
linkerd-proxy-core = { path = "../core" }
22+
linkerd-http-route = { path = "../../http-route" }
23+
# prost-types = { version = "0.11", optional = true }
24+
# thiserror = "1"
25+
26+
[dev-dependencies]
27+
maplit = "1"
28+
quickcheck = { version = "1", default-features = false }
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use linkerd_http_route::{grpc, http};
2+
use std::sync::Arc;
3+
4+
pub use linkerd_http_route::grpc::{filter, r#match, RouteMatch};
5+
6+
pub type Policy = crate::RoutePolicy<Filter>;
7+
pub type Route = grpc::Route<Policy>;
8+
pub type Rule = grpc::Rule<Policy>;
9+
10+
// TODO HTTP2 settings
11+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12+
pub struct Grpc {
13+
pub routes: Arc<[Route]>,
14+
}
15+
16+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17+
pub enum Filter {
18+
InjectFailure(filter::InjectFailure),
19+
RequestHeaders(http::filter::ModifyHeader),
20+
InternalError(&'static str),
21+
}
22+
23+
#[inline]
24+
pub fn find<'r, B>(
25+
routes: &'r [Route],
26+
req: &::http::Request<B>,
27+
) -> Option<(RouteMatch, &'r Policy)> {
28+
grpc::find(routes, req)
29+
}
30+
31+
pub fn default(distribution: crate::RouteDistribution<Filter>) -> Route {
32+
Route {
33+
hosts: vec![],
34+
rules: vec![Rule {
35+
matches: vec![],
36+
policy: Policy {
37+
meta: crate::Meta::new_default("default"),
38+
filters: Arc::new([]),
39+
distribution,
40+
},
41+
}],
42+
}
43+
}
44+
45+
impl Default for Grpc {
46+
fn default() -> Self {
47+
Self {
48+
routes: Arc::new([]),
49+
}
50+
}
51+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use linkerd_http_route::http;
2+
use std::sync::Arc;
3+
4+
pub use linkerd_http_route::http::{filter, r#match, RouteMatch};
5+
6+
pub type Policy = crate::RoutePolicy<Filter>;
7+
pub type Route = http::Route<Policy>;
8+
pub type Rule = http::Rule<Policy>;
9+
10+
// TODO: keepalive settings, etc.
11+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12+
pub struct Http1 {
13+
pub routes: Arc<[Route]>,
14+
}
15+
16+
// TODO: window sizes, etc
17+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18+
pub struct Http2 {
19+
pub routes: Arc<[Route]>,
20+
}
21+
22+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
23+
pub enum Filter {
24+
InjectFailure(filter::InjectFailure),
25+
Redirect(filter::RedirectRequest),
26+
RequestHeaders(filter::ModifyHeader),
27+
InternalError(&'static str),
28+
}
29+
30+
#[inline]
31+
pub fn find<'r, B>(
32+
routes: &'r [Route],
33+
req: &::http::Request<B>,
34+
) -> Option<(http::RouteMatch, &'r Policy)> {
35+
http::find(routes, req)
36+
}
37+
38+
pub fn default(distribution: crate::RouteDistribution<Filter>) -> Route {
39+
Route {
40+
hosts: vec![],
41+
rules: vec![Rule {
42+
matches: vec![],
43+
policy: Policy {
44+
meta: crate::Meta::new_default("default"),
45+
filters: Arc::new([]),
46+
distribution,
47+
},
48+
}],
49+
}
50+
}
51+
52+
impl Default for Http1 {
53+
fn default() -> Self {
54+
Self {
55+
routes: Arc::new([]),
56+
}
57+
}
58+
}
59+
60+
impl Default for Http2 {
61+
fn default() -> Self {
62+
Self {
63+
routes: Arc::new([]),
64+
}
65+
}
66+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#![deny(rust_2018_idioms, clippy::disallowed_methods, clippy::disallowed_types)]
2+
#![forbid(unsafe_code)]
3+
4+
use std::{borrow::Cow, hash::Hash, net::SocketAddr, sync::Arc, time};
5+
6+
pub mod grpc;
7+
pub mod http;
8+
pub mod opaq;
9+
10+
pub use linkerd_http_route as route;
11+
pub use linkerd_proxy_api_resolve::Metadata as EndpointMetadata;
12+
13+
#[derive(Clone, Debug, PartialEq, Eq)]
14+
pub struct ClientPolicy {
15+
pub protocol: Protocol,
16+
pub backends: Arc<[Backend]>,
17+
}
18+
19+
// TODO additional server configs (e.g. concurrency limits, window sizes, etc)
20+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
21+
pub enum Protocol {
22+
Detect {
23+
timeout: time::Duration,
24+
http1: http::Http1,
25+
http2: http::Http2,
26+
opaque: opaq::Opaque,
27+
},
28+
29+
Http1(http::Http1),
30+
Http2(http::Http2),
31+
Grpc(grpc::Grpc),
32+
33+
Opaque(opaq::Opaque),
34+
35+
// TODO(ver) TLS-aware type
36+
Tls(opaq::Opaque),
37+
}
38+
39+
#[derive(Debug, Eq)]
40+
pub enum Meta {
41+
Default {
42+
name: Cow<'static, str>,
43+
},
44+
Resource {
45+
group: String,
46+
kind: String,
47+
name: String,
48+
namespace: String,
49+
section: Option<String>,
50+
},
51+
}
52+
53+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
54+
pub struct RoutePolicy<T> {
55+
pub meta: Arc<Meta>,
56+
pub filters: Arc<[T]>,
57+
pub distribution: RouteDistribution<T>,
58+
}
59+
60+
// TODO(ver) Weighted random WITHOUT availability awareness, as required by
61+
// HTTPRoute.
62+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
63+
pub enum RouteDistribution<T> {
64+
Empty,
65+
66+
FirstAvailable(Arc<[RouteBackend<T>]>),
67+
68+
RandomAvailable(Arc<[(RouteBackend<T>, u32)]>),
69+
}
70+
71+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
72+
pub struct RouteBackend<T> {
73+
pub filters: Arc<[T]>,
74+
pub backend: Backend,
75+
}
76+
77+
// TODO(ver) how does configuration like failure accrual fit in here? What about
78+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
79+
pub struct Backend {
80+
pub meta: Arc<Meta>,
81+
pub queue: Queue,
82+
pub dispatcher: BackendDispatcher,
83+
}
84+
85+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
86+
pub struct Queue {
87+
pub capacity: usize,
88+
pub failfast_timeout: time::Duration,
89+
}
90+
91+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
92+
pub enum BackendDispatcher {
93+
Forward(SocketAddr, EndpointMetadata),
94+
BalanceP2c(Load, EndpointDiscovery),
95+
}
96+
97+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
98+
pub enum EndpointDiscovery {
99+
DestinationGet { path: String },
100+
}
101+
102+
/// Configures the load balancing strategy for a backend.
103+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
104+
pub enum Load {
105+
PeakEwma(PeakEwma),
106+
}
107+
108+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
109+
pub struct PeakEwma {
110+
pub decay: time::Duration,
111+
pub default_rtt: time::Duration,
112+
}
113+
114+
// === impl Meta ===
115+
116+
impl Meta {
117+
pub fn new_default(name: impl Into<Cow<'static, str>>) -> Arc<Self> {
118+
Arc::new(Self::Default { name: name.into() })
119+
}
120+
121+
pub fn group(&self) -> &str {
122+
match self {
123+
Self::Default { .. } => "",
124+
Self::Resource { group, .. } => group,
125+
}
126+
}
127+
128+
pub fn kind(&self) -> &str {
129+
match self {
130+
Self::Default { .. } => "default",
131+
Self::Resource { kind, .. } => kind,
132+
}
133+
}
134+
135+
pub fn name(&self) -> &str {
136+
match self {
137+
Self::Default { name } => name,
138+
Self::Resource { name, .. } => name,
139+
}
140+
}
141+
142+
pub fn namespace(&self) -> &str {
143+
match self {
144+
Self::Default { .. } => "",
145+
Self::Resource { namespace, .. } => namespace,
146+
}
147+
}
148+
149+
pub fn section(&self) -> &str {
150+
match self {
151+
Self::Default { .. } => "",
152+
Self::Resource { section, .. } => section.as_deref().unwrap_or(""),
153+
}
154+
}
155+
}
156+
157+
impl std::cmp::PartialEq for Meta {
158+
fn eq(&self, other: &Self) -> bool {
159+
// Resources that look like Defaults are considered equal.
160+
self.group() == other.group() && self.kind() == other.kind() && self.name() == other.name()
161+
}
162+
}
163+
164+
impl std::hash::Hash for Meta {
165+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
166+
// Resources that look like Defaults are considered the same.
167+
self.group().hash(state);
168+
self.kind().hash(state);
169+
self.name().hash(state);
170+
}
171+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use crate::RoutePolicy;
2+
3+
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
4+
pub struct Opaque {
5+
pub policy: Option<Policy>,
6+
}
7+
8+
pub type Policy = RoutePolicy<Filter>;
9+
10+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11+
pub enum Filter {}

0 commit comments

Comments
 (0)