syntax = "proto3";
package io.linkerd.proxy.outbound;
option go_package = "github.com/linkerd/linkerd2-proxy-api/go/outbound";
import "net.proto";
import "destination.proto";
import "meta.proto";
import "grpc_route.proto";
import "opaque_route.proto";
import "tls_route.proto";
import "http_route.proto";
import "google/protobuf/duration.proto";
service OutboundPolicies {
rpc Get(TrafficSpec) returns (OutboundPolicy) {}
rpc Watch(TrafficSpec) returns (stream OutboundPolicy) {}
}
message TrafficSpec {
// Uniquely identifies the source proxy workload (e.g., pod name) to the
// control plane.
string source_workload = 1;
// Describes a target address, as observed by the proxy.
oneof target {
// Indicates the proxy is connecting to a specific IP:port.
io.linkerd.proxy.net.TcpAddress addr = 2;
// Indicates the proxy is connecting to a named address (like an HTTP
// authority).
string authority = 3;
}
}
// Outbound policy for a given traffic spec.
message OutboundPolicy {
// Indicates the protocol to use for this target. This will be set to Opaque
// if the target has been marked as opaque and will be Discover otherwise.
ProxyProtocol protocol = 1;
// Describes the resource for which outbound policy has been discovered.
io.linkerd.proxy.meta.Metadata metadata = 2;
}
message ProxyProtocol {
oneof kind {
Detect detect = 1;
Opaque opaque = 2;
// HTTP/1 policy configuration.
Http1 http1 = 3;
// HTTP/2 policy configuration.
Http2 http2 = 4;
// gRPC policy configuration.
Grpc grpc = 5;
// TLS policy configuration.
Tls tls = 6;
}
message Detect {
// Protocol detection timeout.
google.protobuf.Duration timeout = 1;
Opaque opaque = 2;
// HTTP/1 policy configuration.
Http1 http1 = 3;
// HTTP/2 policy configuration.
Http2 http2 = 4;
}
message Opaque {
repeated OpaqueRoute routes = 1;
}
message Http1 {
repeated HttpRoute routes = 1;
// If empty, circuit breaking is not performed.
FailureAccrual failure_accrual = 2;
}
message Http2 {
repeated HttpRoute routes = 1;
// If empty, circuit breaking is not performed.
FailureAccrual failure_accrual = 2;
}
message Grpc {
repeated GrpcRoute routes = 1;
// If empty, circuit breaking is not performed.
FailureAccrual failure_accrual = 2;
}
message Tls {
repeated TlsRoute routes = 1;
}
}
// Outbound-specific HTTP route configuration (based on the
// [Gateway API](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.HTTPRoute)).
message HttpRoute {
io.linkerd.proxy.meta.Metadata metadata = 1;
// If empty, the host value is ignored.
repeated io.linkerd.proxy.http_route.HostMatch hosts = 2;
// Must have at least one rule.
repeated Rule rules = 3;
message Rule {
repeated io.linkerd.proxy.http_route.HttpRouteMatch matches = 1;
repeated Filter filters = 2;
Distribution backends = 3;
// DEPRECATED: use `timeouts` instead. Servers should continue to set this
// value to the same value as `timeouts.response`.
google.protobuf.Duration requestTimeout = 4 [deprecated = true];
io.linkerd.proxy.http_route.Timeouts timeouts = 5;
Retry retry = 6;
// If true, the proxy will allow headers to control retry and timeout
// behavior.
bool allow_l5d_request_headers = 7;
}
message Filter {
oneof kind {
io.linkerd.proxy.http_route.HttpFailureInjector failure_injector = 1;
io.linkerd.proxy.http_route.RequestHeaderModifier request_header_modifier = 2;
io.linkerd.proxy.http_route.RequestRedirect redirect = 3;
io.linkerd.proxy.http_route.ResponseHeaderModifier response_header_modifier = 4;
}
}
message Distribution {
oneof kind {
Empty empty = 1;
// Use the first available backend in the list.
FirstAvailable first_available = 2;
RandomAvailable random_available = 3;
}
message Empty {}
message FirstAvailable {
repeated RouteBackend backends = 1;
}
message RandomAvailable {
repeated WeightedRouteBackend backends = 1;
}
}
message Retry {
uint32 max_retries = 1;
uint32 max_request_bytes = 2;
// Must be set, even if there are no internal conditions.
Conditions conditions = 3;
google.protobuf.Duration timeout = 4;
ExponentialBackoff backoff = 5;
// Retryable conditions.
message Conditions {
// Specifies the status code ranges that should trigger a retry.
repeated StatusRange status_ranges = 1;
message StatusRange {
uint32 start = 1;
uint32 end = 2;
}
}
}
message RouteBackend {
Backend backend = 1;
repeated Filter filters = 3;
// DEPRECATED: proxies ignore this. Use Retry timeouts instead.
google.protobuf.Duration requestTimeout = 4 [deprecated = true];
}
message WeightedRouteBackend {
RouteBackend backend = 1;
uint32 weight = 2;
}
}
message GrpcRoute {
io.linkerd.proxy.meta.Metadata metadata = 1;
// If empty, the host value is ignored.
repeated io.linkerd.proxy.http_route.HostMatch hosts = 2;
// Must have at least one rule.
repeated Rule rules = 3;
message Rule {
repeated io.linkerd.proxy.grpc_route.GrpcRouteMatch matches = 1;
repeated Filter filters = 2;
Distribution backends = 3;
// DEPRECATED: use `timeouts` instead. Servers should continue to set this
// value to the same value as `timeouts.response`.
google.protobuf.Duration requestTimeout = 4 [deprecated = true];
io.linkerd.proxy.http_route.Timeouts timeouts = 5;
Retry retry = 6;
// If true, the proxy will allow headers to control retry and timeout
// behavior.
bool allow_l5d_request_headers = 7;
}
message Filter {
oneof kind {
io.linkerd.proxy.grpc_route.GrpcFailureInjector failure_injector = 1;
io.linkerd.proxy.http_route.RequestHeaderModifier request_header_modifier = 2;
}
}
message Distribution {
oneof kind {
Empty empty = 1;
// Use the first available backend in the list.
FirstAvailable first_available = 2;
RandomAvailable random_available = 3;
}
message Empty {}
message FirstAvailable {
repeated RouteBackend backends = 1;
}
message RandomAvailable {
repeated WeightedRouteBackend backends = 1;
}
}
message Retry {
uint32 max_retries = 1;
uint32 max_request_bytes = 2;
// Must be set, even if there are no internal conditions.
Conditions conditions = 3;
google.protobuf.Duration timeout = 4;
ExponentialBackoff backoff = 5;
// Retryable gRPC status codes.
message Conditions {
bool cancelled = 1;
bool deadine_exceeded = 4;
bool resource_exhausted = 8;
bool internal = 13;
bool unavailable = 14;
}
}
message RouteBackend {
Backend backend = 1;
repeated Filter filters = 3;
// DEPRECATED: proxies ignore this. Retry timeouts are used instead of this.
google.protobuf.Duration requestTimeout = 4 [deprecated = true];
}
message WeightedRouteBackend {
RouteBackend backend = 1;
uint32 weight = 2;
}
}
message OpaqueRoute {
io.linkerd.proxy.meta.Metadata metadata = 1;
// Must have at least one rule.
repeated Rule rules = 3;
reserved 4;
message Rule {
Distribution backends = 1;
repeated Filter filters = 2;
}
message Filter {
oneof kind {
io.linkerd.proxy.opaque_route.Invalid invalid = 1;
io.linkerd.proxy.opaque_route.Forbidden forbidden = 2;
}
}
message Distribution {
oneof kind {
Empty empty = 1;
// Use the first available backend in the list.
FirstAvailable first_available = 2;
RandomAvailable random_available = 3;
}
message Empty {}
message FirstAvailable {
repeated RouteBackend backends = 1;
}
message RandomAvailable {
repeated WeightedRouteBackend backends = 1;
}
}
message RouteBackend {
Backend backend = 1;
reserved 2;
repeated Filter filters = 3;
}
message WeightedRouteBackend {
RouteBackend backend = 1;
uint32 weight = 2;
}
}
message TlsRoute {
io.linkerd.proxy.meta.Metadata metadata = 1;
// If empty, the SNI value is ignored.
repeated io.linkerd.proxy.tls_route.SniMatch snis = 2;
// Must have at least one rule.
repeated Rule rules = 3;
reserved 4;
message Rule {
Distribution backends = 1;
repeated Filter filters = 2;
}
message Filter {
oneof kind {
io.linkerd.proxy.opaque_route.Invalid invalid = 1;
io.linkerd.proxy.opaque_route.Forbidden forbidden = 2;
}
}
message Distribution {
oneof kind {
Empty empty = 1;
// Use the first available backend in the list.
FirstAvailable first_available = 2;
RandomAvailable random_available = 3;
}
message Empty {}
message FirstAvailable {
repeated RouteBackend backends = 1;
}
message RandomAvailable {
repeated WeightedRouteBackend backends = 1;
}
}
message RouteBackend {
Backend backend = 1;
reserved 2;
repeated Filter filters = 3;
}
message WeightedRouteBackend {
RouteBackend backend = 1;
uint32 weight = 2;
}
}
message Backend {
io.linkerd.proxy.meta.Metadata metadata = 1;
oneof kind {
// A backend that consists of a single endpoint.
io.linkerd.proxy.destination.WeightedAddr forward = 2;
// A backend that comprises a load balanced service.
BalanceP2c balancer = 3;
}
// Describes queue configuration for a backend.
Queue queue = 4;
// A strategy for discovering endpoints for a service.
message EndpointDiscovery {
oneof kind {
// Use the `Destination` service to discover endpoints for this service.
DestinationGet dst = 1;
}
message DestinationGet {
string path = 1;
}
}
// Describes a power-of-two-choices (P2C) load balancer configuration for a
// backend.
message BalanceP2c {
EndpointDiscovery discovery = 1;
// The load estimation strategy used by this load balancer.
oneof load {
// This load balancer uses peak EWMA (exponentially weighted moving
// average) load estimates.
PeakEwma peak_ewma = 2;
}
// Parameters configuring peak EWMA load estimation.
message PeakEwma {
// Initial latency value used when no latencies have been
// recorded for an endpoint.
google.protobuf.Duration default_rtt = 1;
// The duration of the moving window over which latency is observed.
google.protobuf.Duration decay = 2;
}
}
}
message Queue {
// The number of requests that may be held in a queue before backpressure is
// exerted.
uint32 capacity = 1;
// A timeout that limits how long a backend may remain unready before any
// requests in its queue are failed.
google.protobuf.Duration failfast_timeout = 2;
}
message FailureAccrual {
message ConsecutiveFailures {
uint32 max_failures = 1;
ExponentialBackoff backoff = 2;
}
oneof kind {
ConsecutiveFailures consecutive_failures = 1;
}
}
message ExponentialBackoff {
// The minimum amount of time to wait before resuming an operation.
google.protobuf.Duration min_backoff = 1;
// The maximum amount of time to wait before resuming an operation.
// Must be greater than or equal to min_backoff.
google.protobuf.Duration max_backoff = 2;
// The ratio of the base timeout that may be randomly added to a backoff.
// Must be greater than or equal to 0.0.
float jitter_ratio = 3;
}