syntax = "proto3";
package io.linkerd.proxy.destination;
option go_package = "github.com/linkerd/linkerd2-proxy-api/go/destination";
import "google/protobuf/duration.proto";
import "http_types.proto";
import "meta.proto";
import "net.proto";
/// Destination Service ///
//
// This is the service discovery API. Given a destination, this returns a
// weighted set of addresses and address metadata. Can be implemented with DNS
// or lookups against other service discovery backends.
//
// If the service does not exist then the controller must send
// `no_endpoints{exists: false}` ASAP when a client subscribes or when the
// service stops existing. If the service exists and has endpoints available
// then the controller must send `add` that lists all (or at least a large
// number) of the endpoints for the service. If and only if the service exists
// but does not have any endpoints available then the controller SHOULD send
// `no_endpoints{exists: true}` when a client subscribes. In other words, the
// `no_endpoints` message must only be sent when there are *no*endpoints for
// the service.
//
// The controller is expected to send an Update every time there is a
// change in service discovery.
//
// The client MUST be prepared to receive messages in any order and the client
// MUST be able to cope with the presence or absence of redundant messages.
//
// `no_endpoints` followed by an `add` is *not* equivalent to just sending the
// `add` regardless of the value of the `exists` field in the `no_endpoints`
// message. `remove` followed by a `no_endpoints` message is equivalent to
// sending just the `no_endpoints` message, and a `remove` that removes the
// last endpoint is equivalent to a `no_endpoints{exists: true}` message.
//
// When the client gets disconnected from the controller and reconnects, the
// client may use stale results from its previous subscription until, and only
// until, it receives the first message. This is why the controller must send
// a message at the start of a subscription. This is also why the controller
// must not send a `no_endpoints` message before an `add` message; the client
// would clear its cached messages between the time it receives the
// `no_endpoints` message and the time it receives the `add` message, which is
// not the desired behavior.
service Destination {
// Given a destination, return all addresses in that destination as a long-
// running stream of updates.
rpc Get(GetDestination) returns(stream Update) {}
// Given a destination, return that destination's profile and send an update
// whenever it changes.
rpc GetProfile(GetDestination) returns(stream DestinationProfile) {}
}
message GetDestination {
string scheme = 1;
string path = 2;
// An opaque value that is set at injection-time and sent with destintion
// lookups.
//
// If, for instance, the token encodes a namespace or some locality
// information, the service may alter its results to take this locality into
// account.
string context_token = 3;
}
message Update {
oneof update {
// A new set of endpoints are available for the service. The set might be
// empty.
WeightedAddrSet add = 1;
// Some endpoints have been removed from the service.
AddrSet remove = 2;
// `no_endpoints{exists: false}` indicates that the service does not exist
// and the client MAY try an alternate service discovery method (e.g. DNS).
//
// `no_endpoints(exists: true)` indicates that the service does exist and
// the client MUST NOT fall back to an alternate service discovery method.
NoEndpoints no_endpoints = 3;
}
}
message AddrSet { repeated net.TcpAddress addrs = 1; }
message WeightedAddrSet {
repeated WeightedAddr addrs = 1;
map<string, string> metric_labels = 2;
}
message WeightedAddr {
net.TcpAddress addr = 1;
uint32 weight = 3;
map<string, string> metric_labels = 4;
TlsIdentity tls_identity = 5;
ProtocolHint protocol_hint = 6;
AuthorityOverride authority_override = 7;
// The HTTP/2 parameters to use when connecting to the destination, if HTTP/2
// is used. These parameters are used by proxies when the application traffic
// is HTTP/2 or when the H2 ProtocolHint is used to transport HTTP/1
// connections over HTTP/2.
Http2ClientParams http2 = 8;
// A reference to the metadata for this endpoint, usually a Pod. This may be
// omitted when the resource type is unknown.
meta.Metadata resource_ref = 9;
}
message TlsIdentity {
reserved 2;
reserved "k8s_pod_identity";
oneof strategy {
DnsLikeIdentity dns_like_identity = 1;
UriLikeIdentity uri_like_identity = 3;
}
// The server name of the endpoint. This is the value that needs to be included
// by clients in the ClientHello SNI extension of the TLS handshake when they
// initiate TLS connections to servers.
DnsLikeIdentity server_name = 4;
// Verify the certificate based on the Kubernetes pod identity.
message DnsLikeIdentity {
// A DNS-like name that encodes workload coordinates.
//
// For example:
// {name}.{namespace}.{type}.identity.{control-namespace}.{trust-domain...}
string name = 1;
}
// Verify the certificate based on an URI identity.
message UriLikeIdentity {
// A URI name that encodes workload identity.
//
// For example:
// spiffe://trust-domain/workload-dentifier
string uri = 1;
}
}
message AuthorityOverride { string authority_override = 1; }
message NoEndpoints { bool exists = 1; }
// A hint of what protocol the service knows. The default value is
// for the `hint` field to be not be set, essentially meaning "unknown".
message ProtocolHint {
oneof protocol {
// Hints that the service understands HTTP2 and the proxy's internal
// http2-upgrade mechanism.
H2 h2 = 1;
// Hints that the destination will handle this connection as an opaque TCP
// stream, and (if `opaque_transport` is set) that the proxy should not send
// a `SessionProtocol` as part of its transport header.
Opaque opaque = 3;
}
message H2 {}
message Opaque {}
// When set, indicates that the target supports receiving opaque traffic
// wrapped with the Linkerd connection header on the specified port.
OpaqueTransport opaque_transport = 2;
message OpaqueTransport {
// The target proxy's inbound port.
uint32 inbound_port = 1;
}
}
// Configures the parameters used to initialize an HTTP/2 connection.
message Http2ClientParams {
// Overrides the default client flow control settings.
FlowControl flow_control = 1;
// Enables keep-alive timeouts.
KeepAlive keep_alive = 2;
// Configures Hyper internals.
Internals internals = 3;
message FlowControl {
// Configures the maximum connection-level flow control window size.
uint32 initial_connection_window_size = 1;
// Configures the maximum stream-level flow control window size.
uint32 initial_stream_window_size = 2;
// Enables Hyper's adaptive flow control, ignoring other window settings.
bool adaptive_flow_control = 3;
}
message KeepAlive {
// The time between pings.
google.protobuf.Duration interval = 1;
// The time to wait for a ping response before considering the connection
// dead.
google.protobuf.Duration timeout = 2;
// Whether to send pings when there is no other traffic.
bool while_idle = 3;
}
message Internals {
uint32 max_concurrent_reset_streams = 1;
uint32 max_frame_size = 2;
uint32 max_send_buf_size = 3;
}
}
message DestinationProfile {
// The fully-qualified service name, if one exists.
//
// When resolving (especially by IP), this field provides the fully-qualified
// name of the resolved service, if one exists. This field does NOT include
// any port information. E.g. a lookup for 10.2.3.4:8080 might have a name
// like `foo.bar.svc.cluster.local`.
//
// Implementations MAY provide names for non-service IP-lookups (e.g., pod or
// node dns names), but this is not required.
//
// If the lookup does not refer to a known named entity, this field MUST be
// left empty.
string fully_qualified_name = 5;
// Indicates that connections on this service address should be handled as
// opaque TCP streams. HTTP routes returned on for such services will be
// ignored.
bool opaque_protocol = 4;
// A list of routes, each with a RequestMatch. If a request matches
// more than one route, the first match wins.
repeated Route routes = 1;
// The retry budget controls how much additional load the proxy can generate
// as retries. Failured requests on retryable routes will not be retried if
// there is no available budget.
RetryBudget retry_budget = 2;
// If this list is non-empty, requests to this destination should instead be
// split between the destinations in this list. Each destination should
// receive a portion of the requests proportional to its weight. If this
// list is empty, requests should be sent to this destination as normal.
repeated WeightedDst dst_overrides = 3;
// If this field is set, it indicates that the target is a known endpoint (and
// not a service address). The values of `fully_qualified_name` and
// `dst_overrides` will be ignored for the purposes of service discovery--
// traffic split and load balancing will be skipped and the single endpoint
// are used.
//
// No endpoint should be set If the target is unknown.
WeightedAddr endpoint = 6;
// A reference to the metadata for this destination, usually a service. May be
// omitted if the destination is not a service.
meta.Metadata parent_ref = 7;
// A reference to the metadata for a ServiceProfile resource. This is omitted
// when no ServiceProfile resource exists.
meta.Metadata profile_ref = 8;
}
message Route {
// This route contains requests which match this condition.
RequestMatch condition = 1;
// A list of response classes for this route. If a response matches
// more than one ResponseClass, the first match wins. If a response does not
// match any ResponseClasses, it is considered to be a successful response.
repeated ResponseClass response_classes = 2;
// Metric labels to attach to requests and responses that match this route.
map<string, string> metrics_labels = 3;
// If a route is retryable, any failed requests on that route may be retried
// by the proxy.
bool is_retryable = 4;
// After this time has elapsed since receiving the initial request, any
// outstanding request will be cancelled, a timeout error response will be
// returned, and no more retries will be attempted.
google.protobuf.Duration timeout = 5;
}
message RetryBudget {
// The ratio of additional traffic that may be added by retries. A
// retry_ratio of 0.1 means that 1 retry may be attempted for every 10 regular
// requests. A retry_ratio of 1.0 means that 1 retry may be attempted for
// every 1 regular request (in other words, total request load may be doubled
// as a result of retries).
float retry_ratio = 1;
// The proxy may always attempt this number of retries per second, even if it
// would violate the retry_ratio. This is to allow retries to happen even
// when the request rate is very low.
uint32 min_retries_per_second = 2;
// This duration indicates for how long requests should be considered for the
// purposes of enforcing the retry_ratio. A higher value considers a larger
// window and therefore allows burstier retries.
google.protobuf.Duration ttl = 3;
}
message ResponseClass {
// This class contains responses which match this condition.
ResponseMatch condition = 1;
// If responses in this class should be considered failures. This defaults
// to false (success).
bool is_failure = 2;
}
message RequestMatch {
message Seq { repeated RequestMatch matches = 1; }
oneof match {
Seq all = 1;
Seq any = 2;
RequestMatch not = 3;
PathMatch path = 4;
http_types.HttpMethod method = 5;
// TODO: match on arbitrary header
}
}
message PathMatch {
// Match if the request path matches this regex.
string regex = 1;
}
message ResponseMatch {
message Seq { repeated ResponseMatch matches = 1; }
oneof match {
Seq all = 1;
Seq any = 2;
ResponseMatch not = 3;
HttpStatusRange status = 4;
// TODO: match on arbitrary header or trailer
}
}
// If either a minimum or maximum is not specified, the range is considered to
// be over a discrete value.
message HttpStatusRange {
// Minimum matching http status code (inclusive), if specified.
uint32 min = 1;
// Maximum matching http status code (inclusive), if specified.
uint32 max = 2;
}
message WeightedDst {
// This authority will be used as the `path` in a call to the Destination.Get
// rpc.
string authority = 1;
// The proportion of requests to send to this destination. This value is
// relative to other weights in the same dst_overrides list.
uint32 weight = 2;
// A reference to the metadata for a Service resource.
meta.Metadata backend_ref = 3;
}