Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

router: scoped RDS (2/4): support scoped routing configuration #5839

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cdae885
Support scoped routing configuration.
AndresGuedez Feb 5, 2019
db48450
Derive test class from TestBase.
AndresGuedez Feb 5, 2019
756afcd
fix_format fixes.
AndresGuedez Feb 5, 2019
8e624ea
Merge remote-tracking branch 'upstream/master' into scoped-rds-inline…
AndresGuedez Feb 5, 2019
98149e5
Minor cleanup.
AndresGuedez Feb 5, 2019
76b7196
Fix srds.proto docs.
AndresGuedez Feb 5, 2019
f924eb7
Pendatic spelling and docs cleanup.
AndresGuedez Feb 6, 2019
51d8dff
fix_format.
AndresGuedez Feb 6, 2019
ccca775
More detailed documentation/examples for SRDS.
AndresGuedez Feb 27, 2019
e5d87db
fix_format.
AndresGuedez Feb 27, 2019
9c3d45b
Update pedantic spelling dictionary.
AndresGuedez Feb 27, 2019
afcc2a5
Clarify Scope.Key documentation.
AndresGuedez Feb 27, 2019
c774f00
Use EXPECT_NO_THROW() to validate successful allocation/construction.
AndresGuedez Feb 27, 2019
8cc1b13
fix_format.
AndresGuedez Feb 27, 2019
71f2a74
Modify SRDS to support delta updates and ease transition to incremental.
AndresGuedez Mar 23, 2019
5aed783
Support delta SRDS proto in integration test.
AndresGuedez Mar 29, 2019
15b1c7c
Cleanup.
AndresGuedez Apr 5, 2019
b216034
Add comments.
AndresGuedez Apr 10, 2019
c8f531b
Merge remote-tracking branch 'upstream/master' into scoped-rds-inline…
AndresGuedez Apr 11, 2019
39309ac
Fix build failures after master merge.
AndresGuedez Apr 12, 2019
8fe33ad
Cleanup and comments.
AndresGuedez Apr 12, 2019
8c38526
Fix format.
AndresGuedez Apr 12, 2019
dd2261c
Comments.
AndresGuedez Apr 13, 2019
a7a621b
Merge remote-tracking branch 'upstream/master' into scoped-rds-inline…
AndresGuedez Apr 15, 2019
090bbd8
Cleanup and minor refactor of ConfigProvider framework.
AndresGuedez Apr 16, 2019
8b8a5e7
clang-tidy cleanup.
AndresGuedez Apr 17, 2019
43c95e8
More clang-tidy cleanup.
AndresGuedez Apr 17, 2019
6adaa22
Fix build break.
AndresGuedez Apr 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/envoy/admin/v2alpha/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ api_proto_library_internal(
"//envoy/api/v2:cds",
"//envoy/api/v2:lds",
"//envoy/api/v2:rds",
"//envoy/api/v2:srds",
"//envoy/config/bootstrap/v2:bootstrap",
],
)
Expand Down
41 changes: 41 additions & 0 deletions api/envoy/admin/v2alpha/config_dump.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ option java_package = "io.envoyproxy.envoy.admin.v2alpha";
import "envoy/api/v2/cds.proto";
import "envoy/api/v2/lds.proto";
import "envoy/api/v2/rds.proto";
import "envoy/api/v2/srds.proto";
import "envoy/config/bootstrap/v2/bootstrap.proto";

import "google/protobuf/any.proto";
Expand Down Expand Up @@ -178,3 +179,43 @@ message RoutesConfigDump {
// The dynamically loaded route configs.
repeated DynamicRouteConfig dynamic_route_configs = 3 [(gogoproto.nullable) = false];
}

// Envoy's scoped RDS implementation fills this message with all currently loaded route
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AndresGuedez for this, my main thought looking at this is that the PR is too big to reasonably review and without diluting review bandwidth. Can we split the PR up further for review?

I think we should have this master PR kept open to show how things tie together, but then separate PRs for:

  1. Protos.
  2. Scoped RDS configuration parser and handling logic. This can just be unit tested and doesn't need to be wired up yet.
  3. Config subscription and wiring of scoped RDS into the rest of Envoy. The integration test can be added here.
  4. Optionally, other PRs to reduce the amount of renaming noise, where reasonable.

I think we can get better review velocity if we do it this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack. Working on splitting this up.

// configuration scopes (defined via ScopedRouteConfigurationsSet protos). This message lists both
// the scopes defined inline with the higher order object (i.e., the HttpConnectionManager) and the
// dynamically obtained scopes via the SRDS API.
message ScopedRoutesConfigDump {
message InlineScopedRouteConfigs {
// The name assigned to the scoped route configurations.
string name = 1;

// The scoped route configurations.
repeated envoy.api.v2.ScopedRouteConfiguration scoped_route_configs = 2;

// The timestamp when the scoped route config set was last updated.
google.protobuf.Timestamp last_updated = 3;
}

message DynamicScopedRouteConfigs {
// The name assigned to the scoped route configurations.
string name = 1;

// This is the per-resource version information. This version is currently taken from the
// :ref:`version_info <envoy_api_field_DiscoveryResponse.version_info>` field at the time that
// the scoped routes configuration was loaded.
string version_info = 2;

// The scoped route configurations.
repeated envoy.api.v2.ScopedRouteConfiguration scoped_route_configs = 3;

// The timestamp when the scoped route config set was last updated.
google.protobuf.Timestamp last_updated = 4;
}

// The statically loaded scoped route configs.
repeated InlineScopedRouteConfigs inline_scoped_route_configs = 1 [(gogoproto.nullable) = false];

// The dynamically loaded scoped route configs.
repeated DynamicScopedRouteConfigs dynamic_scoped_route_configs = 2
[(gogoproto.nullable) = false];
}
21 changes: 21 additions & 0 deletions api/envoy/api/v2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,24 @@ api_go_grpc_library(
"//envoy/api/v2/route:route_go_proto",
],
)

api_proto_library_internal(
name = "srds",
srcs = ["srds.proto"],
has_services = 1,
visibility = [":friends"],
deps = [
":discovery",
"//envoy/api/v2/core:base",
"//envoy/api/v2/route",
],
)

api_go_grpc_library(
name = "srds",
proto = ":srds",
deps = [
":discovery_go_proto",
"//envoy/api/v2/core:base_go_proto",
],
)
135 changes: 135 additions & 0 deletions api/envoy/api/v2/srds.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
syntax = "proto3";

package envoy.api.v2;

option java_outer_classname = "SrdsProto";
option java_package = "io.envoyproxy.envoy.api.v2";
option java_multiple_files = true;
option java_generic_services = true;

import "envoy/api/v2/discovery.proto";

import "google/api/annotations.proto";

import "validate/validate.proto";
import "gogoproto/gogo.proto";

option (gogoproto.equal_all) = true;

// [#protodoc-title: HTTP scoped routing configuration]
// * Routing :ref:`architecture overview <arch_overview_http_routing>`
//
// .. attention::
//
// The Scoped RDS API is not yet fully implemented and *should not* be enabled in
// :ref:`envoy_api_msg_config.filter.network.http_connection_manager.v2.HttpConnectionManager`.
//
// TODO(AndresGuedez): Update :ref:`arch_overview_http_routing` with scoped routing overview and
// configuration details.

// The Scoped Routes Discovery Service (SRDS) API distributes
// :ref:`ScopedRouteConfiguration<envoy_api_msg.ScopedRouteConfiguration>` resources. Each
// ScopedRouteConfiguration resource represents a "routing scope" containing a mapping that allows
// the HTTP connection manager to dynamically assign a routing table (specified via
// a :ref:`RouteConfiguration<envoy_api_msg_RouteConfiguration>` message) to each HTTP request.
service ScopedRoutesDiscoveryService {
rpc StreamScopedRoutes(stream DiscoveryRequest) returns (stream DiscoveryResponse) {
}

rpc DeltaScopedRoutes(stream DeltaDiscoveryRequest) returns (stream DeltaDiscoveryResponse) {
}

rpc FetchScopedRoutes(DiscoveryRequest) returns (DiscoveryResponse) {
option (google.api.http) = {
post: "/v2/discovery:scoped-routes"
body: "*"
};
}
}

// Specifies a routing scope, which associates a :ref:`envoy_api_msg_RouteConfiguration` (identified
// by its resource name) to a :ref:`Key<envoy_api_msg_ScopedRouteConfiguration.Key>`.
//
// The HTTP connection manager builds up a table consisting of these Key to RouteConfiguration
// mappings, and looks up the RouteConfiguration to use per request according to the algorithm
// specified in the
// :ref:`scope_key_builder<envoy_api_field_config.filter.network.http_connection_manager.v2.ScopedRoutes.scope_key_builder>`
// assigned to the HttpConnectionManager.
//
// For example, with the following configurations (in YAML):
//
// HttpConnectionManager config:
//
// .. code::
//
// ...
// scope_key_builder:
// fragments:
// - header_element:
// name: X-Route-Selector
// element_separator: ,
// element:
// separator: =
// key: vip
//
// ScopedRouteConfiguration resources (specified statically via
// HttpConnectionManager.ScopedRoutes.scoped_route_configurations_list or obtained dynamically via
// SRDS):
//
// .. code::
//
// (1)
// name: route-scope1
// route_configuration_name: route-config1
// key:
// fragments:
// - string_key: 172.10.10.20
//
// (2)
// name: route-scope2
// route_configuration_name: route-config2
// key:
// fragments:
// - string_key: 172.20.20.30
//
// A request from a client such as:
//
// .. code::
//
// GET / HTTP/1.1
// Host: foo.com
// X-Route-Selector: vip=172.10.10.20
//
// Would result in the routing table defined by the `route-config1` RouteConfiguration being
// assigned to the HTTP request/stream.
//
// [#comment:next free field: 4]
message ScopedRouteConfiguration {
// The name assigned to the routing scope.
string name = 1 [(validate.rules).string.min_bytes = 1];

// Specifies a key which is matched against the output of the
// :ref:`scope_key_builder<envoy_api_field_config.filter.network.http_connection_manager.v2.ScopedRoutes.scope_key_builder>`
// specified in the HttpConnectionManager. The matching is done per HTTP request and is dependent
// on the order of the fragments contained in the Key.
message Key {
message Fragment {
oneof type {
option (validate.required) = true;

// A string to match against.
string string_key = 1;
}
}

// The ordered set of fragments to match against.
repeated Fragment fragments = 1 [(validate.rules).repeated .min_items = 1];
}

// The resource name to use for a :ref:`envoy_api_msg_DiscoveryRequest` to an RDS server to
// fetch the :ref:`envoy_api_msg_RouteConfiguration` associated with this scope.
string route_configuration_name = 2 [(validate.rules).string.min_bytes = 1];

// The key to match against.
Key key = 3 [(validate.rules).message.required = true];
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ api_proto_library_internal(
srcs = ["http_connection_manager.proto"],
deps = [
"//envoy/api/v2:rds",
"//envoy/api/v2:srds",
"//envoy/api/v2/core:base",
"//envoy/api/v2/core:config_source",
"//envoy/api/v2/core:protocol",
Expand All @@ -20,6 +21,7 @@ api_go_proto_library(
proto = ":http_connection_manager",
deps = [
"//envoy/api/v2:rds_go_grpc",
"//envoy/api/v2:srds_go_grpc",
"//envoy/api/v2/core:base_go_proto",
"//envoy/api/v2/core:config_source_go_proto",
"//envoy/api/v2/core:protocol_go_proto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ option go_package = "v2";
import "envoy/api/v2/core/config_source.proto";
import "envoy/api/v2/core/protocol.proto";
import "envoy/api/v2/rds.proto";
import "envoy/api/v2/srds.proto";
import "envoy/config/filter/accesslog/v2/accesslog.proto";
import "envoy/type/percent.proto";

Expand All @@ -24,7 +25,7 @@ import "gogoproto/gogo.proto";
// [#protodoc-title: HTTP connection manager]
// HTTP connection manager :ref:`configuration overview <config_http_conn_man>`.

// [#comment:next free field: 31]
// [#comment:next free field: 32]
message HttpConnectionManager {
enum CodecType {
option (gogoproto.goproto_enum_prefix) = false;
Expand Down Expand Up @@ -61,6 +62,11 @@ message HttpConnectionManager {

// The route table for the connection manager is static and is specified in this property.
envoy.api.v2.RouteConfiguration route_config = 4;

// A route table will be dynamically assigned to each request based on request attributes
// (e.g., the value of a header). The "routing scopes" (i.e., route tables) and "scope keys" are
// specified in this message.
ScopedRoutes scoped_routes = 31;
}

// A list of individual HTTP filters that make up the filter chain for
Expand Down Expand Up @@ -419,6 +425,115 @@ message Rds {
string route_config_name = 2 [(validate.rules).string.min_bytes = 1];
}

// This message is used to work around the limitations with 'oneof' and repeated fields.
message ScopedRouteConfigurationsList {
repeated envoy.api.v2.ScopedRouteConfiguration scoped_route_configurations = 1
[(validate.rules).repeated .min_items = 1];
}

message ScopedRoutes {
// The name assigned to the scoped routing configuration.
string name = 1 [(validate.rules).string.min_bytes = 1];

// Specifies the mechanism for constructing "scope keys" based on HTTP request attributes. These
// keys are matched against a set of :ref:`Key<envoy_api_msg_ScopedRouteConfiguration.Key>`
// objects assembled from :ref:`ScopedRouteConfiguration<envoy_api_msg_ScopedRouteConfiguration>`
// messages distributed via SRDS (the Scoped Route Discovery Service) or assigned statically via
// :ref:`scoped_route_configurations_list<envoy_api_field_config.filter.network.http_connection_manager.v2.ScopedRoutes.scoped_route_configurations_list>`.
//
// Upon receiving a request's headers, the Router will build a key using the algorithm specified
// by this message. This key will be used to look up the routing table (i.e., the
// :ref:`RouteConfiguration<envoy_api_msg_RouteConfiguration>`) to use for the request.
message ScopeKeyBuilder {
// Specifies the mechanism for constructing key fragments which are composed into scope keys.
message FragmentBuilder {
// Specifies how the value of a header should be extracted.
// The following example maps the structure of a header to the fields in this message.
//
// .. code::
//
// X-Header: a=b;c=d
// | || |
// | || \----> <element_separator>
// | ||
// | |\----> <element.separator>
// | |
// | \----> <element.key>
// |
// \----> <name>
//
// Each 'a=b' key-value pair constitutes an 'element' of the header field.
message HeaderValueExtractor {
// The name of the header field to extract the value from.
string name = 1 [(validate.rules).string.min_bytes = 1];

// The element separator (e.g., ';' separates 'a;b;c;d').
string element_separator = 2;

// Specifies a header field's key value pair to match on.
message KvElement {
// The separator between key and value (e.g., '=' separates 'k=v;...').
string separator = 1;

// The key to match on.
string key = 2;
}

oneof extract_type {
// Specifies the index of the element to extract.
int32 index = 3;

// Specifies the key value pair to extract the value from.
KvElement element = 4;
}
}

oneof type {
option (validate.required) = true;

// Specifies how a header field's value should be extracted.
HeaderValueExtractor header_value_extractor = 1;
}
}

// The final scope key consists of the ordered union of these fragments.
repeated FragmentBuilder fragments = 1 [(validate.rules).repeated .min_items = 1];
}

// The algorithm to use for constructing a scope key for each request.
ScopeKeyBuilder scope_key_builder = 2 [(validate.rules).message.required = true];

// Configuration source specifier for RDS.
// This config source is used to subscribe to RouteConfiguration resources specified in
// ScopedRouteConfiguration messages.
envoy.api.v2.core.ConfigSource rds_config_source = 3
[(validate.rules).message.required = true, (gogoproto.nullable) = false];

oneof config_specifier {
option (validate.required) = true;

// The set of routing scopes corresponding to the HCM. A scope is assigned to a request by
// matching a key constructed from the request's attributes according to the algorithm specified
// by the
// :ref:`ScopeKeyBuilder<envoy_api_msg_config.filter.network.http_connection_manager.v2.ScopedRoutes.ScopeKeyBuilder>`
// in this message.
ScopedRouteConfigurationsList scoped_route_configurations_list = 4;

// The set of routing scopes associated with the HCM will be dynamically loaded via the SRDS
// API. A scope is assigned to a request by matching a key constructed from the request's
// attributes according to the algorithm specified by the
// :ref:`ScopeKeyBuilder<envoy_api_msg_config.filter.network.http_connection_manager.v2.ScopedRoutes.ScopeKeyBuilder>`
// in this message.
ScopedRds scoped_rds = 5;
}
}

message ScopedRds {
// Configuration source specifier for scoped RDS.
envoy.api.v2.core.ConfigSource scoped_rds_config_source = 1
[(validate.rules).message.required = true, (gogoproto.nullable) = false];
}

message HttpFilter {
// The name of the filter to instantiate. The name must match a supported
// filter. The built-in filters are:
Expand Down
1 change: 1 addition & 0 deletions docs/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ PROTO_RST="
/envoy/api/v2/cluster/circuit_breaker/envoy/api/v2/cluster/circuit_breaker.proto.rst
/envoy/api/v2/rds/envoy/api/v2/rds.proto.rst
/envoy/api/v2/route/route/envoy/api/v2/route/route.proto.rst
/envoy/api/v2/srds/envoy/api/v2/srds.proto.rst
/envoy/api/v2/lds/envoy/api/v2/lds.proto.rst
/envoy/api/v2/listener/listener/envoy/api/v2/listener/listener.proto.rst
/envoy/api/v2/ratelimit/ratelimit/envoy/api/v2/ratelimit/ratelimit.proto.rst
Expand Down
1 change: 1 addition & 0 deletions docs/root/api-v2/http_routes/http_routes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ HTTP route management
:maxdepth: 2

../api/v2/rds.proto
../api/v2/srds.proto
../api/v2/route/route.proto
Loading