diff --git a/proxy/v1/config/cfg.proto b/proxy/v1/config/cfg.proto new file mode 100644 index 00000000000..83b8501438a --- /dev/null +++ b/proxy/v1/config/cfg.proto @@ -0,0 +1,472 @@ +// Copyright 2016 IBM Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "google/protobuf/wrappers.proto"; + +package istio.proxy.v1alpha.config; + +// Glossary & concepts + +// Cluster - set of pods/VMs/containers where the service is running. The +// members of a cluster share one or more common attributes. For e.g., all +// pods in a cluster could share a common set of labels, or be running the +// same version of the application binary. + +// Multiple clusters/service - In a continuous deployment scenario, for a +// given service, there can be multiple clusters running potentially +// different variants of the application binary. These variants are not +// necessarily different API versions. They could be iterative changes to +// the same service, deployed in different environments (prod, staging, +// dev, etc.). Common scenarios where this occurs include A/B testing, +// canary rollouts, etc. The choice of a particular cluster can be decided +// based on various criterion (headers, url, etc.) and/or by weights +// assigned to each cluster. + +// Cluster identifier: A name and one or more tags uniquely identify each +// cluster in a service. Names and tags can be arbitrary strings. Parsing +// of names and tags is specific to the platform (e.g., in kubernetes, name +// could correspond to the service Name, while each tag string could be +// parsed into a pod label (key:value)) + +// Downstream service/cluster - client (browser or another service cluster) +// calling the proxy/sidecar (typically to reach another service). + +// Upstream service - The remote service to which the proxy/sidecar is +// talking to, on behalf of the Downstream service. There can be one or +// more upstream clusters for a given upstream service (see the discussion +// on versions above). The proxy would choose the upstream cluster based on +// various routing rules. + +// Applications address only the upstream service without knowledge of +// individual upstream clusters. The actual choice of the cluster is +// determined by the proxy, enabling the application code to decouple +// itself from the evolution of dependent services. + +// Proxy level global configurations go here +message ProxyConfig { + // config specification version. + int32 revision = 1; + + // Traffic routing (http|l4) related configurations for the + // proxy/sidecar. The configuration generated for the proxy will + // preserve the order of rules specified by the user in the Istio config. + + // Requests can match either a l4 or Http (l7) rule, but not both. A + // request can match multiple Http (l7) rules, but it can match only one + // L4 rule. This can be enforced by checking the match attributes. A Http + // match rule matches on both L4 and L7 attributes, while a L4 rule + // matches based only on L4 attributes. Any duplication or collision in + // the match attributes between two L4 rules or between a L4 and L7 rule + // should throw an error. + + // Since a Http request can match multiple route rules (e.g., there are + // two routes for the prefix /foo, with differing Http match attributes + // such as Http headers), the first rule to match will/should be chosen + // by proxy implementations N.B.: When a request does not match any rule + // for Http, the downstream service would receive a HTTP 404. + repeated RouteRule route_rules = 2; + + // RouteRule determines the upstream cluster to which traffic should be + // routed to. The UpstreamCluster describes how to obtain cluster + // members (through discovery or through static configuration), and + // policies that determine how to handle traffic (load balancing + // policies, failure recovery policies such as timeouts, retries, circuit + // breakers), etc. + repeated UpstreamCluster upstream_clusters = 3; +} + +// Describes rules for routing a request/connection to an upstream +// service, based on attributes associated with the request/connection +// and downstream service invoking the API call. The choice of the specific +// upstream cluster will be determined by the routing rule. +message RouteRule { + oneof route_rule { + L4RouteRule layer4 = 1; + HttpRouteRule http = 2; + } +} + +// Routes incoming Tcp/Udp traffic to one of the clusters of a service +// based on match criterion. +message L4RouteRule { + // Set of conditions that must be satisfied, such as downstream cluster + // labels, connection attributes, etc. + L4MatchCondition match = 1; + + // Each routing rule is associated with one or more upstream clusters, + // (see glossary in beginning of document). Weights associated with the + // cluster determine the proportion of traffic it receives. + repeated WeightedCluster weighted_clusters = 2; + + // Faults can be injected into the connections from downstream by the + // proxy, for testing the failure recovery capabilities of downstream + // services. Faults include aborting the request/connection from + // downstream service, delaying the proxying of request/connection to the + // upstream service, and throttling the bandwidth of the connection + // (either end). + + // TODO: This should be done on per-cluster basis. + L4FaultInjection fault = 3; +} + +// Routes Http requests to one of the upstream clusters based on +// match criterion. +message HttpRouteRule { + // Set of conditions that must be satisfied, such as downstream service + // labels, request attributes, etc. + HttpMatchCondition match = 1; + + // TODO: add redirect support + + // Each routing rule is associated with one or more upstream clusters, + // (see glossary in beginning of document). Weights associated with the + // cluster determine the proportion of traffic it receives. + repeated WeightedCluster weighted_clusters = 2; + + // Faults can be injected into the API calls by the proxy, for + // testing the failure recovery capabilities of downstream services. + // Faults include aborting the Http request from downstream service, + // delaying the proxying of request to the upstream service, or both. + // TODO: This should move into UpstreamCluster + HttpFaultInjection fault = 3; + + // Custom properties per rule (depends on proxy), such as open tracing, + // access log formats, etc. Some of these will be defined in future + // iterations. + google.protobuf.Any custom_impl = 4; +} + +// Basic routing rule match criterion using Tcp attributes and downstream +// cluster identifier +message L4MatchCondition { + // Set of layer 4 match attributes such as src ip/port, dst ip/port and + // protocol + L4MatchAttributes l4attributes = 1; + + // Identify the downstream cluster initiating the connection. + ClusterIdentifier src_cluster = 2; +} + +// L4 connection match attributes +message L4MatchAttributes { + // IPv4 or IPv6 ip address with optional subnet. E.g., a.b.c.d/xx form or + // just a.b.c.d + repeated string src_ip_subnet = 1; + // source port + google.protobuf.UInt32Value src_port = 2; + // IPv4 or IPv6 ip address with optional subnet. E.g., a.b.c.d/xx form or + // just a.b.c.d + repeated string dst_ip_subnet = 3; + // destination port + google.protobuf.UInt32Value dst_port = 4; + + // protocol Tcp|Udp + enum L4Protocol { + TCP = 0; + UDP = 1; + } + L4Protocol protocol = 5; +} + +// A name and one or more tags uniquely identify an upstream or downstream +// cluster. Names and tags can be arbitrary strings, and their +// interpretation is specific to the underlying platform (e.g., in +// kubernetes, name field could correspond to the service Name, while each +// tag string could be parsed into a pod label (key:value)) +message ClusterIdentifier { + string name = 1; + repeated string tags = 2; +} + +// Each routing rule is associated with one or more upstream clusters, +// (see glossary in beginning of document). Weights associated with the +// cluster determine the proportion of traffic it receives. +message WeightedCluster { + // Unique identity of the upstream cluster. Must be the same as those used + // in the definition of the UpstreamClusterPolicy. + ClusterIdentifier dst_cluster = 1; + + // The proportion of connections to be forwarded to the upstream + // cluster. Max is 100. Sum of weights across versions should add up to + // 100. + uint32 weight = 2; +} + +// TODO: Merge with the proposal for "Deriving Request Metadata in +// Kubernetes" by Sven Manson + +// Http/1.1|Http/2|gRPC routing rule match criterion built on top of BaseMatchCondition +message HttpMatchCondition { + // Set of layer 4 match attributes such as src ip/port, dst ip/port and + // protocol + L4MatchAttributes l4attributes = 1; + + // Identify the downstream cluster initiating the connection. + ClusterIdentifier src_cluster = 2; + + // Set of Http request level match attributes + string scheme = 3; + + // Match based on authority + StringMatch authority = 4; + + // Match based on URI + StringMatch uri = 5; + + // Match Http requests based on the specified headers + // Support exact/prefix/regex match on header values + map headers = 6; +} + +// Describes how to matches a given string (exact match, prefix-based match +// or posix style regex based match). +message StringMatch { + oneof match_type { + string exact = 1; + string prefix = 2; + // posix style regex match. Throw error if user's chosen proxy does not + // support posix regex. + string regex = 3; + } +} + +// Describes how to identify members of a specific upstream cluster along +// with load balancing and failure recovery policies for the cluster. The +// load balancing/failure recovery policies are cluster specific. For a +// given upstream service, one can have multiple upstream clusters running +// different versions of the service, each with a different +// policy. + +// N.B. The policies are enforced on egress connections or requests, +// i.e., enforced when the downstream service (caller) is opening a +// connection/sending a request via the proxy to the upstream service. +message UpstreamCluster { + // Unique identifier of the upstream cluster. The name and tags in the + // cluster identifier are used by the service discovery component of the + // proxy to identify the IP addresses of the pods|VMs running this + // service. + // TODO: Need to have a way to statically specify the hosts/IPs. + ClusterIdentifier cluster = 1; + + // Should be either http://.. or tcp://.. + string health_check_endpoint = 2; + LoadBalancingPolicy lb_policy = 3; + TimeoutPolicy timeout = 4; + RetryPolicy retry = 5; + CircuitBreakerPolicy circuit_breaker = 6; +} + +// Load balancing policy to use when forwarding traffic to upstream clusters. +message LoadBalancingPolicy { + enum SimpleLBPolicy { + // These four simple load balancing policies have literally no + // additional configuration. + ROUND_ROBIN = 0; + LEAST_CONN = 1; + IP_HASH = 2; + RANDOM = 3; + } + oneof lb_policy { + SimpleLBPolicy name = 1; + //Custom policy implementations + google.protobuf.Any custom_impl = 2; + } +} + +// Request timeout: wait time until a response is received. Does not +// indicate the time for the entire response to arrive. +message TimeoutPolicy { + message SimpleTimeoutPolicy { + // timeout is per attempt, when retries are specified as well. + // seconds.nanoseconds format + double timeout_seconds = 1; + // Downstream service could specify timeout via Http header to the + // proxy, if the proxy supports such a feature. + string override_header_name = 2; + } + oneof timeout_policy { + SimpleTimeoutPolicy simple_timeout = 1; + // For proxies that support custom timeout policies + google.protobuf.Any custom_impl = 2; + } +} + +// Retry policy to use when a request to the upstream cluster fails. +message RetryPolicy { + message SimpleRetryPolicy { + // number of times the request to the upstream cluster should be retried. + // total timeout would be attempts * timeout + uint32 attempts = 1; + // Downstream Service could specify retry attempts via Http header to + // the proxy, if the proxy supports such a feature. + string override_header_name = 2; + } + oneof retry_policy { + SimpleRetryPolicy simple_retry = 1; + // For proxies that support custom retry policies + google.protobuf.Any custom_impl = 2; + } +} + +// A minimal circuit breaker configuration for the upstream cluster. +message CircuitBreakerPolicy { + message SimpleCircuitBreakerPolicy { + // (for an unhealthy upstream cluster) number of consecutive requests that + // should succeed before the upstream cluster is marked healthy. + uint32 success_threshold = 1; + + // (for a healthy upstream cluster) number of consecutive requests that + // can fail before the upstream cluster is marked unhealthy. + uint32 failure_threshold = 2; + + // When a healthy upstream cluster becomes unhealthy, duration to wait before + // attempting to send requests to that upstream cluster. + // format seconds.nanoseconds + double reset_timeout_seconds = 3; + } + oneof cb_policy { + SimpleCircuitBreakerPolicy simple_cb = 1; + // For proxies that support custom circuit breaker policies. + google.protobuf.Any custom_impl = 2; + } +} + +// Faults can be injected into the API calls by the proxy, for testing the +// failure recovery capabilities of downstream services. Faults include +// aborting the Http request from downstream service, delaying the proxying +// of request to the upstream clusters, or both. +message HttpFaultInjection { + // Delay requests before forwarding to upstream cluster, emulating + // various failures such as network issues, overloaded upstream service, etc. + Delay delay = 1; + + // Abort Http request attempts and return error codes back to downstream + // service, giving the impression that the upstream service is faulty. + // N.B. Both delay and abort can be specified simultaneously. Delay and + // Abort are independent of one another. For e.g., if Delay is restricted + // to 5% of requests while Abort is restricted to 10% of requests, the + // 10% in abort specification applies to all requests directed to the + // service. It may be the case that one or more requests being aborted + // were also delayed. + Abort abort = 2; + + // Only requests with these Http headers will be subjected to fault + // injection + map headers = 3; + + // Either a fixed delay or exponential delay. + message Delay { + oneof http_delay_type { + FixedDelay fixed_delay = 1; + ExponentialDelay exp_delay = 2; + } + // Specify delay duration as part of Http request. + // TODO: The semantics and syntax of the headers is undefined. + string override_header_name = 3; + } + + // Add a fixed delay before forwarding the request to upstream cluster + message FixedDelay { + // percentage of requests on which the delay will be injected + float percent = 1; + // delay duration in seconds.nanoseconds + double fixed_delay_seconds = 2; + } + + // Add a delay (based on an exponential function) before forwarding the + // request to upstream cluster + message ExponentialDelay { + // percentage of requests on which the delay will be injected + float percent = 1; + // mean delay needed to derive the exponential delay values + double mean_delay_seconds = 2; + } + + // Abort Http request attempts and return error codes back to downstream + // service. + message Abort { + // percentage of requests to be aborted with the error code provided. + float percent = 1; + // Error code to use to abort the Http request. Requests can be aborted + // either with Http/1.1 status codes | http2 error codes or gRPC status + // codes. + oneof error_type { + string grpc_status = 2; + string http2_error = 3; + uint32 http_status = 4; + } + // Specify abort code as part of Http request. + // TODO: The semantics and syntax of the headers is undefined. + string override_header_name = 5; + } +} + +// Faults can be injected into the L4 traffic forwarded by the proxy, for +// testing the failure recovery capabilities of downstream services. +// Faults include terminating established Tcp connections, throttling the +// upstream/downstream bandwidth (for Tcp|Udp), or both. +message L4FaultInjection { + // Unlike Http services, we have very little context for raw Tcp|Udp + // connections. We could throttle bandwidth of the connections (slow down + // the connection) and/or abruptly reset (terminate) the Tcp connection + // after it has been established. + + // We first throttle (if set) and then terminate the connection. + Throttle throttle = 1; + Terminate terminate = 2; + + // Bandwidth throttling for Tcp and Udp connections + message Throttle { + // percentage of connections to throttle. + float percent = 1; + // bandwidth limit in "bits" per second between downstream and proxy + uint64 downstream_limit_bps = 2; + // bandwidth limits in "bits" per second between proxy and upstream + uint64 upstream_limit_bps = 3; + + oneof throttle_after { + // Wait for X seconds after the connection is established, before + // starting bandwidth throttling. This would allow us to inject fault + // after the application protocol (e.g., MySQL) has had time to + // establish sessions/whatever handshake necessary. + double throttle_after_seconds = 4; + + // Alternatively, we could wait for a certain number of bytes to be + // transferred to upstream before throttling the bandwidth. + double throttle_after_bytes = 5; + } + + // Stop throttling after the given duration. If not set, the connection + // will be throttled for its lifetime. + google.protobuf.DoubleValue throttle_for_seconds = 6; + } + + // Abruptly reset (terminate) the Tcp connection after it has been + // established, emulating remote server crash or link failure. + message Terminate { + // percentage of established Tcp connections to be terminated/reset + float percent = 1; + + // Wait for X seconds after the connection is established, before + // terminating the connection. Set to 0 to terminate immediately on + // connection establishment. + + // TODO: see if it makes sense to create a generic Duration type to + // express time interval related configs. + double terminate_after_seconds = 2; + } +}