linkerd2-proxy-api 0.18.0

Linkerd Proxy API gRPC bindings and utilities
Documentation
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;
}