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 "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 metric_labels = 2; } message WeightedAddr { net.TcpAddress addr = 1; uint32 weight = 3; map 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; } 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; } 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 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; }