...
1syntax = "proto3";
2
3package io.linkerd.proxy.destination;
4
5option go_package = "github.com/linkerd/linkerd2-proxy-api/go/destination";
6
7import "google/protobuf/duration.proto";
8
9import "http_types.proto";
10import "net.proto";
11
12/// Destination Service ///
13//
14// This is the service discovery API. Given a destination, this returns a
15// weighted set of addresses and address metadata. Can be implemented with DNS
16// or lookups against other service discovery backends.
17//
18// If the service does not exist then the controller must send
19// `no_endpoints{exists: false}` ASAP when a client subscribes or when the
20// service stops existing. If the service exists and has endpoints available
21// then the controller must send `add` that lists all (or at least a large
22// number) of the endpoints for the service. If and only if the service exists
23// but does not have any endpoints available then the controller SHOULD send
24// `no_endpoints{exists: true}` when a client subscribes. In other words, the
25// `no_endpoints` message must only be sent when there are *no*endpoints for
26// the service.
27//
28// The controller is expected to send an Update every time there is a
29// change in service discovery.
30//
31// The client MUST be prepared to receive messages in any order and the client
32// MUST be able to cope with the presence or absence of redundant messages.
33//
34// `no_endpoints` followed by an `add` is *not* equivalent to just sending the
35// `add` regardless of the value of the `exists` field in the `no_endpoints`
36// message. `remove` followed by a `no_endpoints` message is equivalent to
37// sending just the `no_endpoints` message, and a `remove` that removes the
38// last endpoint is equivalent to a `no_endpoints{exists: true}` message.
39//
40// When the client gets disconnected from the controller and reconnects, the
41// client may use stale results from its previous subscription until, and only
42// until, it receives the first message. This is why the controller must send
43// a message at the start of a subscription. This is also why the controller
44// must not send a `no_endpoints` message before an `add` message; the client
45// would clear its cached messages between the time it receives the
46// `no_endpoints` message and the time it receives the `add` message, which is
47// not the desired behavior.
48
49service Destination {
50 // Given a destination, return all addresses in that destination as a long-
51 // running stream of updates.
52 rpc Get(GetDestination) returns(stream Update) {}
53
54 // Given a destination, return that destination's profile and send an update
55 // whenever it changes.
56 rpc GetProfile(GetDestination) returns(stream DestinationProfile) {}
57}
58
59message GetDestination {
60 string scheme = 1;
61 string path = 2;
62
63 // An opaque value that is set at injection-time and sent with destintion
64 // lookups.
65 //
66 // If, for instance, the token encodes a namespace or some locality
67 // information, the service may alter its results to take this locality into
68 // account.
69 string context_token = 3;
70}
71
72message Update {
73 oneof update {
74 // A new set of endpoints are available for the service. The set might be
75 // empty.
76 WeightedAddrSet add = 1;
77
78 // Some endpoints have been removed from the service.
79 AddrSet remove = 2;
80
81 // `no_endpoints{exists: false}` indicates that the service does not exist
82 // and the client MAY try an alternate service discovery method (e.g. DNS).
83 //
84 // `no_endpoints(exists: true)` indicates that the service does exist and
85 // the client MUST NOT fall back to an alternate service discovery method.
86 NoEndpoints no_endpoints = 3;
87 }
88}
89
90message AddrSet { repeated net.TcpAddress addrs = 1; }
91
92message WeightedAddrSet {
93 repeated WeightedAddr addrs = 1;
94 map<string, string> metric_labels = 2;
95}
96
97message WeightedAddr {
98 net.TcpAddress addr = 1;
99 uint32 weight = 3;
100 map<string, string> metric_labels = 4;
101 TlsIdentity tls_identity = 5;
102 ProtocolHint protocol_hint = 6;
103 AuthorityOverride authority_override = 7;
104
105 // The HTTP/2 parameters to use when connecting to the destination, if HTTP/2
106 // is used. These parameters are used by proxies when the application traffic
107 // is HTTP/2 or when the H2 ProtocolHint is used to transport HTTP/1
108 // connections over HTTP/2.
109 Http2ClientParams http2 = 8;
110}
111
112message TlsIdentity {
113 reserved 2;
114 reserved "k8s_pod_identity";
115
116 oneof strategy {
117 DnsLikeIdentity dns_like_identity = 1;
118 UriLikeIdentity uri_like_identity = 3;
119 }
120
121 // The server name of the endpoint. This is the value that needs to be included
122 // by clients in the ClientHello SNI extension of the TLS handshake when they
123 // initiate TLS connections to servers.
124 DnsLikeIdentity server_name = 4;
125
126 // Verify the certificate based on the Kubernetes pod identity.
127 message DnsLikeIdentity {
128 // A DNS-like name that encodes workload coordinates.
129 //
130 // For example:
131 // {name}.{namespace}.{type}.identity.{control-namespace}.{trust-domain...}
132 string name = 1;
133 }
134
135 // Verify the certificate based on an URI identity.
136 message UriLikeIdentity {
137 // A URI name that encodes workload identity.
138 //
139 // For example:
140 // spiffe://trust-domain/workload-dentifier
141 string uri = 1;
142 }
143}
144
145message AuthorityOverride { string authority_override = 1; }
146
147message NoEndpoints { bool exists = 1; }
148
149// A hint of what protocol the service knows. The default value is
150// for the `hint` field to be not be set, essentially meaning "unknown".
151message ProtocolHint {
152 oneof protocol {
153 // Hints that the service understands HTTP2 and the proxy's internal
154 // http2-upgrade mechanism.
155 H2 h2 = 1;
156 // Hints that the destination will handle this connection as an opaque TCP
157 // stream, and (if `opaque_transport` is set) that the proxy should not send
158 // a `SessionProtocol` as part of its transport header.
159 Opaque opaque = 3;
160 }
161
162 message H2 {}
163 message Opaque {}
164
165 // When set, indicates that the target supports receiving opaque traffic
166 // wrapped with the Linkerd connection header on the specified port.
167 OpaqueTransport opaque_transport = 2;
168
169 message OpaqueTransport {
170 // The target proxy's inbound port.
171 uint32 inbound_port = 1;
172 }
173}
174
175// Configures the parameters used to initialize an HTTP/2 connection.
176message Http2ClientParams {
177 // Overrides the default client flow control settings.
178 FlowControl flow_control = 1;
179
180 // Enables keep-alive timeouts.
181 KeepAlive keep_alive = 2;
182
183 // Configures Hyper internals.
184 Internals internals = 3;
185
186 message FlowControl {
187 // Configures the maximum connection-level flow control window size.
188 uint32 initial_connection_window_size = 1;
189
190 // Configures the maximum stream-level flow control window size.
191 uint32 initial_stream_window_size = 2;
192
193 // Enables Hyper's adaptive flow control, ignoring other window settings.
194 bool adaptive_flow_control = 3;
195 }
196
197 message KeepAlive {
198 // The time between pings.
199 google.protobuf.Duration interval = 1;
200
201 // The time to wait for a ping response before considering the connection
202 // dead.
203 google.protobuf.Duration timeout = 2;
204
205 // Whether to send pings when there is no other traffic.
206 bool while_idle = 3;
207 }
208
209 message Internals {
210 uint32 max_concurrent_reset_streams = 1;
211 uint32 max_frame_size = 2;
212 uint32 max_send_buf_size = 3;
213 }
214}
215
216message DestinationProfile {
217 // The fully-qualified service name, if one exists.
218 //
219 // When resolving (especially by IP), this field provides the fully-qualified
220 // name of the resolved service, if one exists. This field does NOT include
221 // any port information. E.g. a lookup for 10.2.3.4:8080 might have a name
222 // like `foo.bar.svc.cluster.local`.
223 //
224 // Implementations MAY provide names for non-service IP-lookups (e.g., pod or
225 // node dns names), but this is not required.
226 //
227 // If the lookup does not refer to a known named entity, this field MUST be
228 // left empty.
229 string fully_qualified_name = 5;
230
231 // Indicates that connections on this service address should be handled as
232 // opaque TCP streams. HTTP routes returned on for such services will be
233 // ignored.
234 bool opaque_protocol = 4;
235
236 // A list of routes, each with a RequestMatch. If a request matches
237 // more than one route, the first match wins.
238 repeated Route routes = 1;
239 // The retry budget controls how much additional load the proxy can generate
240 // as retries. Failured requests on retryable routes will not be retried if
241 // there is no available budget.
242 RetryBudget retry_budget = 2;
243
244 // If this list is non-empty, requests to this destination should instead be
245 // split between the destinations in this list. Each destination should
246 // receive a portion of the requests proportional to its weight. If this
247 // list is empty, requests should be sent to this destination as normal.
248 repeated WeightedDst dst_overrides = 3;
249
250 // If this field is set, it indicates that the target is a known endpoint (and
251 // not a service address). The values of `fully_qualified_name` and
252 // `dst_overrides` will be ignored for the purposes of service discovery--
253 // traffic split and load balancing will be skipped and the single endpoint
254 // are used.
255 //
256 // No endpoint should be set If the target is unknown.
257 WeightedAddr endpoint = 6;
258}
259
260message Route {
261 // This route contains requests which match this condition.
262 RequestMatch condition = 1;
263 // A list of response classes for this route. If a response matches
264 // more than one ResponseClass, the first match wins. If a response does not
265 // match any ResponseClasses, it is considered to be a successful response.
266 repeated ResponseClass response_classes = 2;
267 // Metric labels to attach to requests and responses that match this route.
268 map<string, string> metrics_labels = 3;
269 // If a route is retryable, any failed requests on that route may be retried
270 // by the proxy.
271 bool is_retryable = 4;
272 // After this time has elapsed since receiving the initial request, any
273 // outstanding request will be cancelled, a timeout error response will be
274 // returned, and no more retries will be attempted.
275 google.protobuf.Duration timeout = 5;
276}
277
278message RetryBudget {
279 // The ratio of additional traffic that may be added by retries. A
280 // retry_ratio of 0.1 means that 1 retry may be attempted for every 10 regular
281 // requests. A retry_ratio of 1.0 means that 1 retry may be attempted for
282 // every 1 regular request (in other words, total request load may be doubled
283 // as a result of retries).
284 float retry_ratio = 1;
285 // The proxy may always attempt this number of retries per second, even if it
286 // would violate the retry_ratio. This is to allow retries to happen even
287 // when the request rate is very low.
288 uint32 min_retries_per_second = 2;
289 // This duration indicates for how long requests should be considered for the
290 // purposes of enforcing the retry_ratio. A higher value considers a larger
291 // window and therefore allows burstier retries.
292 google.protobuf.Duration ttl = 3;
293}
294
295message ResponseClass {
296 // This class contains responses which match this condition.
297 ResponseMatch condition = 1;
298 // If responses in this class should be considered failures. This defaults
299 // to false (success).
300 bool is_failure = 2;
301}
302
303message RequestMatch {
304 message Seq { repeated RequestMatch matches = 1; }
305
306 oneof match {
307 Seq all = 1;
308 Seq any = 2;
309 RequestMatch not = 3;
310
311 PathMatch path = 4;
312 http_types.HttpMethod method = 5;
313 // TODO: match on arbitrary header
314 }
315}
316
317message PathMatch {
318 // Match if the request path matches this regex.
319 string regex = 1;
320}
321
322message ResponseMatch {
323 message Seq { repeated ResponseMatch matches = 1; }
324
325 oneof match {
326 Seq all = 1;
327 Seq any = 2;
328 ResponseMatch not = 3;
329
330 HttpStatusRange status = 4;
331 // TODO: match on arbitrary header or trailer
332 }
333}
334
335// If either a minimum or maximum is not specified, the range is considered to
336// be over a discrete value.
337message HttpStatusRange {
338 // Minimum matching http status code (inclusive), if specified.
339 uint32 min = 1;
340 // Maximum matching http status code (inclusive), if specified.
341 uint32 max = 2;
342}
343
344message WeightedDst {
345 // This authority will be used as the `path` in a call to the Destination.Get
346 // rpc.
347 string authority = 1;
348 // The proportion of requests to send to this destination. This value is
349 // relative to other weights in the same dst_overrides list.
350 uint32 weight = 2;
351}
View as plain text