diff --git a/changelogs/unreleased/4387-sunjayBhatia-small.md b/changelogs/unreleased/4387-sunjayBhatia-small.md new file mode 100644 index 00000000000..256ee866e79 --- /dev/null +++ b/changelogs/unreleased/4387-sunjayBhatia-small.md @@ -0,0 +1,2 @@ +RTDS now serves dynamic runtime configuration layer which is requested by bootstrap configuration. +In this future, contents of this runtime configuration will be made configurable by users. diff --git a/internal/envoy/v3/bootstrap.go b/internal/envoy/v3/bootstrap.go index d5a5ee493fc..780707400c9 100644 --- a/internal/envoy/v3/bootstrap.go +++ b/internal/envoy/v3/bootstrap.go @@ -31,7 +31,6 @@ import ( envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_file_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" - envoy_v3_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" envoy_service_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "github.com/golang/protobuf/proto" @@ -39,12 +38,6 @@ import ( "github.com/projectcontour/contour/internal/envoy" "github.com/projectcontour/contour/internal/protobuf" "github.com/projectcontour/contour/internal/timeout" - "google.golang.org/protobuf/types/known/structpb" -) - -const ( - maxRegexProgramSizeError = 1 << 20 - maxRegexProgramSizeWarn = 1000 ) // WriteBootstrap writes bootstrap configuration to files. @@ -168,11 +161,15 @@ func bootstrapConfig(c *envoy.BootstrapConfig) *envoy_bootstrap_v3.Bootstrap { { Name: "base", LayerSpecifier: &envoy_bootstrap_v3.RuntimeLayer_StaticLayer{ - StaticLayer: &structpb.Struct{ - Fields: map[string]*structpb.Value{ - "re2.max_program_size.error_level": {Kind: &structpb.Value_NumberValue{NumberValue: maxRegexProgramSizeError}}, - "re2.max_program_size.warn_level": {Kind: &structpb.Value_NumberValue{NumberValue: maxRegexProgramSizeWarn}}, - }, + StaticLayer: baseRuntimeLayer(), + }, + }, + { + Name: "dynamic", + LayerSpecifier: &envoy_bootstrap_v3.RuntimeLayer_RtdsLayer_{ + RtdsLayer: &envoy_bootstrap_v3.RuntimeLayer_RtdsLayer{ + Name: DynamicRuntimeLayerName, + RtdsConfig: ConfigSource("contour"), }, }, }, @@ -290,9 +287,9 @@ func upstreamFileTLSContext(c *envoy.BootstrapConfig) *envoy_tls_v3.UpstreamTlsC }, }, // TODO(youngnick): Does there need to be a flag wired down to here? - MatchTypedSubjectAltNames: []*envoy_v3_tls.SubjectAltNameMatcher{ + MatchTypedSubjectAltNames: []*envoy_tls_v3.SubjectAltNameMatcher{ { - SanType: envoy_v3_tls.SubjectAltNameMatcher_DNS, + SanType: envoy_tls_v3.SubjectAltNameMatcher_DNS, Matcher: &matcher.StringMatcher{ MatchPattern: &matcher.StringMatcher_Exact{ Exact: "contour", @@ -379,9 +376,9 @@ func validationContextSdsSecretConfig(c *envoy.BootstrapConfig) *envoy_service_d Filename: c.GrpcCABundle, }, }, - MatchTypedSubjectAltNames: []*envoy_v3_tls.SubjectAltNameMatcher{ + MatchTypedSubjectAltNames: []*envoy_tls_v3.SubjectAltNameMatcher{ { - SanType: envoy_v3_tls.SubjectAltNameMatcher_DNS, + SanType: envoy_tls_v3.SubjectAltNameMatcher_DNS, Matcher: &matcher.StringMatcher{ MatchPattern: &matcher.StringMatcher_Exact{ Exact: "contour", diff --git a/internal/envoy/v3/bootstrap_test.go b/internal/envoy/v3/bootstrap_test.go index 64c1cd3e92c..88b1fb5c179 100644 --- a/internal/envoy/v3/bootstrap_test.go +++ b/internal/envoy/v3/bootstrap_test.go @@ -181,6 +181,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -338,6 +358,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -495,6 +535,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -653,6 +713,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -811,6 +891,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -969,6 +1069,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -1129,6 +1249,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -1323,6 +1463,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} @@ -1515,6 +1675,26 @@ func TestBootstrap(t *testing.T) { "re2.max_program_size.warn_level": 1000 } }, + { + "name": "dynamic", + "rtds_layer": { + "name": "dynamic", + "rtds_config": { + "api_config_source": { + "api_type": "GRPC", + "transport_api_version": "V3", + "grpc_services": [ + { + "envoy_grpc": { + "cluster_name": "contour" + } + } + ] + }, + "resource_api_version": "V3" + } + } + }, { "name": "admin", "admin_layer": {} diff --git a/internal/envoy/v3/runtime.go b/internal/envoy/v3/runtime.go new file mode 100644 index 00000000000..8e2a121e06b --- /dev/null +++ b/internal/envoy/v3/runtime.go @@ -0,0 +1,43 @@ +// Copyright Project Contour Authors +// 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. + +package v3 + +import ( + envoy_service_runtime_v3 "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3" + "google.golang.org/protobuf/types/known/structpb" +) + +const ( + DynamicRuntimeLayerName = "dynamic" + maxRegexProgramSizeError = 1 << 20 + maxRegexProgramSizeWarn = 1000 +) + +func RuntimeLayers() []*envoy_service_runtime_v3.Runtime { + return []*envoy_service_runtime_v3.Runtime{ + { + Name: DynamicRuntimeLayerName, + Layer: baseRuntimeLayer(), + }, + } +} + +func baseRuntimeLayer() *structpb.Struct { + return &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "re2.max_program_size.error_level": {Kind: &structpb.Value_NumberValue{NumberValue: maxRegexProgramSizeError}}, + "re2.max_program_size.warn_level": {Kind: &structpb.Value_NumberValue{NumberValue: maxRegexProgramSizeWarn}}, + }, + } +} diff --git a/internal/envoy/v3/runtime_test.go b/internal/envoy/v3/runtime_test.go new file mode 100644 index 00000000000..a0ca20d3cf0 --- /dev/null +++ b/internal/envoy/v3/runtime_test.go @@ -0,0 +1,36 @@ +// Copyright Project Contour Authors +// 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. + +package v3 + +import ( + "testing" + + envoy_service_runtime_v3 "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestRuntimeLayers(t *testing.T) { + require.Equal(t, []*envoy_service_runtime_v3.Runtime{ + { + Name: "dynamic", + Layer: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "re2.max_program_size.error_level": {Kind: &structpb.Value_NumberValue{NumberValue: 1 << 20}}, + "re2.max_program_size.warn_level": {Kind: &structpb.Value_NumberValue{NumberValue: 1000}}, + }, + }, + }, + }, RuntimeLayers()) +} diff --git a/internal/xdscache/v3/runtime.go b/internal/xdscache/v3/runtime.go index 2bdf6110db9..e417791696f 100644 --- a/internal/xdscache/v3/runtime.go +++ b/internal/xdscache/v3/runtime.go @@ -18,6 +18,8 @@ import ( "github.com/golang/protobuf/proto" "github.com/projectcontour/contour/internal/contour" "github.com/projectcontour/contour/internal/dag" + envoy_v3 "github.com/projectcontour/contour/internal/envoy/v3" + "github.com/projectcontour/contour/internal/protobuf" ) // RuntimeCache manages the contents of the gRPC RTDS cache. @@ -25,13 +27,18 @@ type RuntimeCache struct { contour.Cond } -// Contents returns an empty set of layers for now. +// Contents returns all Runtime layers. func (c *RuntimeCache) Contents() []proto.Message { - return []proto.Message{} + return protobuf.AsMessages(envoy_v3.RuntimeLayers()) } -// Query returns an empty set of layers for now. +// Query returns only the "dynamic" layer if requested, otherwise empty. func (c *RuntimeCache) Query(names []string) []proto.Message { + for _, name := range names { + if name == envoy_v3.DynamicRuntimeLayerName { + return protobuf.AsMessages(envoy_v3.RuntimeLayers()) + } + } return []proto.Message{} } diff --git a/internal/xdscache/v3/runtime_test.go b/internal/xdscache/v3/runtime_test.go new file mode 100644 index 00000000000..ff2418ea7f4 --- /dev/null +++ b/internal/xdscache/v3/runtime_test.go @@ -0,0 +1,68 @@ +// Copyright Project Contour Authors +// 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. + +package v3 + +import ( + "testing" + + envoy_service_runtime_v3 "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3" + "github.com/golang/protobuf/proto" + "github.com/projectcontour/contour/internal/protobuf" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestRuntimeCacheContents(t *testing.T) { + rc := &RuntimeCache{} + protobuf.ExpectEqual(t, runtimeLayers(), rc.Contents()) +} + +func TestRuntimeCacheQuery(t *testing.T) { + testCases := map[string]struct { + names []string + expected []proto.Message + }{ + "empty names": { + names: []string{}, + expected: []proto.Message{}, + }, + "names include dynamic": { + names: []string{"foo", "dynamic", "bar"}, + expected: runtimeLayers(), + }, + "names excludes dynamic": { + names: []string{"foo", "bar", "baz"}, + expected: []proto.Message{}, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + rc := &RuntimeCache{} + protobuf.ExpectEqual(t, tc.expected, rc.Query(tc.names)) + }) + } +} + +func runtimeLayers() []proto.Message { + return []proto.Message{ + &envoy_service_runtime_v3.Runtime{ + Name: "dynamic", + Layer: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "re2.max_program_size.error_level": {Kind: &structpb.Value_NumberValue{NumberValue: 1 << 20}}, + "re2.max_program_size.warn_level": {Kind: &structpb.Value_NumberValue{NumberValue: 1000}}, + }, + }, + }, + } +}