From 61e2ea094ce7b5cd064a33f20fc47c0015f59dcc Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Fri, 26 May 2023 15:52:09 -0400 Subject: [PATCH] Add `builtin/property-override` Envoy Extension (#17487) `property-override` is an extension that allows for arbitrarily patching Envoy resources based on resource matching filters. Patch operations resemble a subset of the JSON Patch spec with minor differences to facilitate patching pre-defined (protobuf) schemas. See Envoy Extension product documentation for more details. Co-authored-by: Eric Haberkorn Co-authored-by: Kyle Havlovitz --- .changelog/17487.txt | 3 + .../property-override/property_override.go | 294 +++++ .../property_override_test.go | 449 +++++++ .../property-override/structpatcher.go | 508 ++++++++ .../property-override/structpatcher_test.go | 1063 +++++++++++++++++ .../envoyextensions/registered_extensions.go | 12 +- agent/xds/delta_envoy_extender_oss_test.go | 124 ++ ...opertyoverride-add-keepalive.latest.golden | 137 +++ ...d-outlier-detection-multiple.latest.golden | 133 +++ ...erride-add-outlier-detection.latest.golden | 131 ++ ...de-add-round-robin-lb-config.latest.golden | 129 ++ ...und-doesnt-apply-to-outbound.latest.golden | 127 ++ ...ound-doesnt-apply-to-inbound.latest.golden | 127 ++ ...ide-remove-outlier-detection.latest.golden | 125 ++ ...opertyoverride-add-keepalive.latest.golden | 75 ++ ...d-outlier-detection-multiple.latest.golden | 75 ++ ...erride-add-outlier-detection.latest.golden | 75 ++ ...de-add-round-robin-lb-config.latest.golden | 75 ++ ...und-doesnt-apply-to-outbound.latest.golden | 75 ++ ...ound-doesnt-apply-to-inbound.latest.golden | 75 ++ ...ide-remove-outlier-detection.latest.golden | 75 ++ ...opertyoverride-add-keepalive.latest.golden | 234 ++++ ...d-outlier-detection-multiple.latest.golden | 234 ++++ ...erride-add-outlier-detection.latest.golden | 234 ++++ ...de-add-round-robin-lb-config.latest.golden | 234 ++++ ...und-doesnt-apply-to-outbound.latest.golden | 237 ++++ ...ound-doesnt-apply-to-inbound.latest.golden | 237 ++++ ...ide-remove-outlier-detection.latest.golden | 234 ++++ ...opertyoverride-add-keepalive.latest.golden | 5 + ...d-outlier-detection-multiple.latest.golden | 5 + ...erride-add-outlier-detection.latest.golden | 5 + ...de-add-round-robin-lb-config.latest.golden | 5 + ...und-doesnt-apply-to-outbound.latest.golden | 5 + ...ound-doesnt-apply-to-inbound.latest.golden | 5 + ...ide-remove-outlier-detection.latest.golden | 5 + api/config_entry.go | 11 +- 36 files changed, 5567 insertions(+), 10 deletions(-) create mode 100644 .changelog/17487.txt create mode 100644 agent/envoyextensions/builtin/property-override/property_override.go create mode 100644 agent/envoyextensions/builtin/property-override/property_override_test.go create mode 100644 agent/envoyextensions/builtin/property-override/structpatcher.go create mode 100644 agent/envoyextensions/builtin/property-override/structpatcher_test.go create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-keepalive.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection-multiple.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-round-robin-lb-config.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/clusters/propertyoverride-remove-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-keepalive.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection-multiple.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-round-robin-lb-config.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/endpoints/propertyoverride-remove-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-keepalive.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection-multiple.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-round-robin-lb-config.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/listeners/propertyoverride-remove-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-add-keepalive.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection-multiple.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-add-round-robin-lb-config.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden create mode 100644 agent/xds/testdata/builtin_extension/routes/propertyoverride-remove-outlier-detection.latest.golden diff --git a/.changelog/17487.txt b/.changelog/17487.txt new file mode 100644 index 000000000..a18bea889 --- /dev/null +++ b/.changelog/17487.txt @@ -0,0 +1,3 @@ +```release-note:feature +xds: Add `property-override` built-in Envoy extension that directly patches Envoy resources. +``` diff --git a/agent/envoyextensions/builtin/property-override/property_override.go b/agent/envoyextensions/builtin/property-override/property_override.go new file mode 100644 index 000000000..e110fad83 --- /dev/null +++ b/agent/envoyextensions/builtin/property-override/property_override.go @@ -0,0 +1,294 @@ +package propertyoverride + +import ( + "fmt" + "strings" + + envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + "github.com/hashicorp/go-multierror" + "github.com/mitchellh/mapstructure" + "google.golang.org/protobuf/proto" + + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/envoyextensions/extensioncommon" + "github.com/hashicorp/consul/lib/maps" +) + +type stringSet map[string]struct{} + +type propertyOverride struct { + extensioncommon.BasicExtensionAdapter + + // Patches are an array of Patch operations to be applied to the target resource(s). + Patches []Patch + // Debug controls error messages when Path matching fails. + // When set to true, all possible fields for the unmatched segment of the Path are returned. + // When set to false, only the first ten possible fields are returned. + Debug bool + // ProxyType identifies the type of Envoy proxy that this extension applies to. + // The extension will only be configured for proxies that match this type and + // will be ignored for all other proxy types. + ProxyType api.ServiceKind +} + +// ResourceFilter matches specific Envoy resources to target with a Patch operation. +type ResourceFilter struct { + // ResourceType specifies the Envoy resource type the patch applies to. Valid values are + // `cluster`, `route`, `endpoint`, and `listener`. + // This field is required. + ResourceType ResourceType + // TrafficDirection determines whether the patch will be applied to a service's inbound + // or outbound resources. + // This field is required. + TrafficDirection TrafficDirection +} + +// ResourceType is the type of Envoy resource being patched. +type ResourceType string + +const ( + ResourceTypeCluster ResourceType = "cluster" + ResourceTypeRoute ResourceType = "route" +) + +var ResourceTypes = stringSet{ + string(ResourceTypeCluster): {}, + string(ResourceTypeRoute): {}, +} + +// TrafficDirection determines whether inbound or outbound Envoy resources will be patched. +type TrafficDirection string + +const ( + TrafficDirectionInbound TrafficDirection = "inbound" + TrafficDirectionOutbound TrafficDirection = "outbound" +) + +var TrafficDirections = stringSet{string(TrafficDirectionInbound): {}, string(TrafficDirectionOutbound): {}} + +// Op is the type of JSON Patch operation being applied. +type Op string + +const ( + OpAdd Op = "add" + OpRemove Op = "remove" +) + +var Ops = stringSet{string(OpAdd): {}, string(OpRemove): {}} + +// validProxyTypes is the set of supported proxy types for this extension. +var validProxyTypes = stringSet{ + string(api.ServiceKindConnectProxy): struct{}{}, + string(api.ServiceKindTerminatingGateway): struct{}{}, +} + +// Patch describes a single patch operation to modify the specific field of matching +// Envoy resources. +// +// The semantics of Patch closely resemble those of JSON Patch (https://jsonpatch.com/, +// https://datatracker.ietf.org/doc/html/rfc6902/). +type Patch struct { + // ResourceFilter determines which Envoy resource(s) will be patched. ResourceFilter and + // its subfields are not part of the JSON Patch specification. + // This field is required. + ResourceFilter ResourceFilter + // Op represents the JSON Patch operation to be applied by the patch. Supported ops are + // `add` and `remove`: + // - add: Replaces a field with the current value at the specified Path. (Note that + // JSON Patch does not inherently support object “merges”, which must be implemented + // using one discrete add per changed field.) + // - remove: Sets the value at the given path to `nil`. As with `add`, if the target + // field does not exist in the corresponding schema, an error is returned; this + // conforms to JSON Patch semantics and is intended to avoid silent failure when a + // field removal is expected. + // This field is required. + Op Op + // Path specifies where the patch will be applied on a target resource. Path does not + // support array member lookups or appending (`-`). + // + // When an unset but schema-valid (i.e. specified in the corresponding Envoy resource + // .proto) intermediate message field is encountered on the Path, that field will be + // set to its non-`nil` empty (default) value and evaluation will continue. This means + // that even if parents of a field for a given Path are unset, a single patch can set + // deeply nested children of that parent. Subsequent patching of these initialized + // parent field(s) may be necessary to satisfy validation or configuration requirements. + // This field is required. + Path string + // Value specifies the value that will be set at the given Path in a target resource in + // an `add` operation. + // + // Value must be a map with scalar values, a scalar value, or an array of scalar values. + // (Note that this along with the Path constraints noted above imply that setting values + // nested within non-scalar arrays is not supported.) + // + // In every case, the target field will be replaced entirely with the specified value; + // this conforms to JSON Patch `add` semantics. If Value is a map, the non-`nil` empty + // value for the target field will be placed at the specified Path, and then the fields + // specified in the Value map will be explicitly set. + // This field is required if the Op is compatible with a Value per JSON Patch semantics + // (e.g. `add`), and must not be set otherwise. + Value any +} + +var _ extensioncommon.BasicExtension = (*propertyOverride)(nil) + +func (c *stringSet) checkRequired(v, fieldName string) error { + if _, ok := (*c)[v]; !ok { + if v == "" { + return fmt.Errorf("field %s is required", fieldName) + } + return fmt.Errorf("invalid %s '%q'; supported values: %s", + fieldName, v, strings.Join(maps.SliceOfKeys(*c), ", ")) + } + return nil +} + +func (f *ResourceFilter) validate() error { + if f == nil || *f == (ResourceFilter{}) { + return fmt.Errorf("field ResourceFilter is required") + } + if err := ResourceTypes.checkRequired(string(f.ResourceType), "ResourceType"); err != nil { + return err + } + if err := TrafficDirections.checkRequired(string(f.TrafficDirection), "TrafficDirection"); err != nil { + return err + } + return nil +} + +// validate validates the fields of an individual Patch. +func (p *Patch) validate(debug bool) error { + if err := p.ResourceFilter.validate(); err != nil { + return err + } + + if err := Ops.checkRequired(string(p.Op), "Op"); err != nil { + return err + } + + if p.Value != nil && p.Op != OpAdd { + return fmt.Errorf("field Value is not supported for %s operation", p.Op) + } + + // Attempt to execute the patch by applying it to a dummy empty struct. + var err error + switch p.ResourceFilter.ResourceType { + case ResourceTypeCluster: + _, err = PatchStruct(&envoy_cluster_v3.Cluster{}, *p, debug) + case ResourceTypeRoute: + _, err = PatchStruct(&envoy_route_v3.RouteConfiguration{}, *p, debug) + default: + return fmt.Errorf("path validation unimplemented for %q", p.ResourceFilter.ResourceType) + } + + return err +} + +// validate validates the fields of the property-override extension, including all of its +// configured Patches. +func (p *propertyOverride) validate() error { + if len(p.Patches) == 0 { + return fmt.Errorf("at least one patch is required") + } + + var resultErr error + for _, patch := range p.Patches { + if err := patch.validate(p.Debug); err != nil { + resultErr = multierror.Append(resultErr, err) + } + } + + if err := validProxyTypes.checkRequired(string(p.ProxyType), "ProxyType"); err != nil { + resultErr = multierror.Append(resultErr, err) + } + + return resultErr +} + +// Constructor follows a specific function signature required for the extension registration. +// It constructs a BasicEnvoyExtender with a patch Extension from the arguments provided by ext. +func Constructor(ext api.EnvoyExtension) (extensioncommon.EnvoyExtender, error) { + var p propertyOverride + + if name := ext.Name; name != api.BuiltinPropertyOverrideExtension { + return nil, fmt.Errorf("expected extension name %q but got %q", api.BuiltinPropertyOverrideExtension, name) + } + if err := mapstructure.WeakDecode(ext.Arguments, &p); err != nil { + return nil, fmt.Errorf("error decoding extension arguments: %v", err) + } + if err := p.validate(); err != nil { + return nil, err + } + + return &extensioncommon.BasicEnvoyExtender{ + Extension: &p, + }, nil +} + +// CanApply returns true if the ProxyType of the extension config matches the kind of the local proxy indicated by the +// RuntimeConfig. +func (p *propertyOverride) CanApply(config *extensioncommon.RuntimeConfig) bool { + return config.Kind == p.ProxyType +} + +// PatchRoute patches the provided Envoy Route with any applicable `route` ResourceType patches. +func (p *propertyOverride) PatchRoute(_ *extensioncommon.RuntimeConfig, r *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) { + d := TrafficDirectionOutbound + if extensioncommon.IsRouteToLocalAppCluster(r) { + d = TrafficDirectionInbound + } + return patchResourceType[*envoy_route_v3.RouteConfiguration](r, p, ResourceTypeRoute, d, &defaultStructPatcher[*envoy_route_v3.RouteConfiguration]{}) +} + +// PatchCluster patches the provided Envoy Cluster with any applicable `cluster` ResourceType patches. +func (p *propertyOverride) PatchCluster(_ *extensioncommon.RuntimeConfig, c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) { + d := TrafficDirectionOutbound + if extensioncommon.IsLocalAppCluster(c) { + d = TrafficDirectionInbound + } + return patchResourceType[*envoy_cluster_v3.Cluster](c, p, ResourceTypeCluster, d, &defaultStructPatcher[*envoy_cluster_v3.Cluster]{}) +} + +// PatchFilter does nothing as this extension does not target Filters directly. +func (p *propertyOverride) PatchFilter(_ *extensioncommon.RuntimeConfig, f *envoy_listener_v3.Filter, _ bool) (*envoy_listener_v3.Filter, bool, error) { + return f, false, nil +} + +// patchResourceType applies Patches matching the given ResourceType to the target K. +// This helper simplifies implementation of the above per-type patch methods defined by BasicExtension. +func patchResourceType[K proto.Message](k K, p *propertyOverride, t ResourceType, d TrafficDirection, patcher structPatcher[K]) (K, bool, error) { + resultPatched := false + var resultErr error + + for _, patch := range p.Patches { + if patch.ResourceFilter.ResourceType != t { + continue + } + if patch.ResourceFilter.TrafficDirection != d { + continue + } + newK, err := patcher.applyPatch(k, patch, p.Debug) + if err != nil { + resultErr = multierror.Append(resultErr, err) + } else { + k = newK + resultPatched = true + } + } + + return k, resultPatched && resultErr == nil, resultErr +} + +// structPatcher allows us to mock applyPatch in tests. +type structPatcher[K proto.Message] interface { + applyPatch(k K, patch Patch, debug bool) (result K, e error) +} + +type defaultStructPatcher[K proto.Message] struct { +} + +func (patcher *defaultStructPatcher[K]) applyPatch(k K, patch Patch, debug bool) (result K, e error) { + return PatchStruct(k, patch, debug) +} diff --git a/agent/envoyextensions/builtin/property-override/property_override_test.go b/agent/envoyextensions/builtin/property-override/property_override_test.go new file mode 100644 index 000000000..9decbe872 --- /dev/null +++ b/agent/envoyextensions/builtin/property-override/property_override_test.go @@ -0,0 +1,449 @@ +package propertyoverride + +import ( + "fmt" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + "strings" + "testing" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/envoyextensions/extensioncommon" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +// TestConstructor tests raw input to the Constructor function called to initialize +// the property-override extension. This includes implicit validation of the deserialized +// input to the extension. +func TestConstructor(t *testing.T) { + // These helpers aid in constructing valid raw example input (map[string]any) + // for the Constructor, with optional overrides for fields under test. + applyOverrides := func(m map[string]any, overrides map[string]any) map[string]any { + for k, v := range overrides { + if v == nil { + delete(m, k) + } else { + m[k] = v + } + } + return m + } + makeResourceFilter := func(overrides map[string]any) map[string]any { + f := map[string]any{ + "ResourceType": ResourceTypeRoute, + "TrafficDirection": TrafficDirectionOutbound, + } + return applyOverrides(f, overrides) + } + makePatch := func(overrides map[string]any) map[string]any { + p := map[string]any{ + "ResourceFilter": makeResourceFilter(map[string]any{}), + "Op": OpAdd, + "Path": "/name", + "Value": "foo", + } + return applyOverrides(p, overrides) + } + makeArguments := func(overrides map[string]any) map[string]any { + a := map[string]any{ + "Patches": []map[string]any{ + makePatch(map[string]any{}), + }, + "Debug": true, + "ProxyType": api.ServiceKindConnectProxy, + } + return applyOverrides(a, overrides) + } + type testCase struct { + extensionName string + arguments map[string]any + expected propertyOverride + ok bool + errMsg string + } + + validTestCase := func(o Op, d TrafficDirection, t ResourceType) testCase { + var v any = "foo" + if o != OpAdd { + v = nil + } + + // Use a valid field (name) for all resource types. + path := "/name" + + return testCase{ + arguments: makeArguments(map[string]any{ + "Patches": []map[string]any{ + makePatch(map[string]any{ + "ResourceFilter": makeResourceFilter(map[string]any{ + "ResourceType": t, + "TrafficDirection": d, + }), + "Op": o, + "Path": path, + "Value": v, + }), + }, + }), + expected: propertyOverride{ + Patches: []Patch{ + { + ResourceFilter: ResourceFilter{ + ResourceType: t, + TrafficDirection: d, + }, + Op: o, + Path: path, + Value: v, + }, + }, + Debug: true, + ProxyType: api.ServiceKindConnectProxy, + }, + ok: true, + } + } + cases := map[string]testCase{ + "with no arguments": { + arguments: nil, + ok: false, + errMsg: "at least one patch is required", + }, + "with an invalid name": { + arguments: makeArguments(map[string]any{}), + extensionName: "bad", + ok: false, + errMsg: "expected extension name \"builtin/property-override\" but got \"bad\"", + }, + "empty Patches": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{}}), + ok: false, + errMsg: "at least one patch is required", + }, + "patch with no ResourceFilter": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "ResourceFilter": nil, + }), + }}), + ok: false, + errMsg: "field ResourceFilter is required", + }, + "patch with no ResourceType": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "ResourceFilter": makeResourceFilter(map[string]any{ + "ResourceType": nil, + }), + }), + }}), + ok: false, + errMsg: "field ResourceType is required", + }, + "patch with invalid ResourceType": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "ResourceFilter": makeResourceFilter(map[string]any{ + "ResourceType": "foo", + }), + }), + }}), + ok: false, + errMsg: "invalid ResourceType", + }, + "patch with no TrafficDirection": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "ResourceFilter": makeResourceFilter(map[string]any{ + "TrafficDirection": nil, + }), + }), + }}), + ok: false, + errMsg: "field TrafficDirection is required", + }, + "patch with invalid TrafficDirection": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "ResourceFilter": makeResourceFilter(map[string]any{ + "TrafficDirection": "foo", + }), + }), + }}), + ok: false, + errMsg: "invalid TrafficDirection", + }, + "patch with no Op": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "Op": nil, + }), + }}), + ok: false, + errMsg: "field Op is required", + }, + "patch with invalid Op": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "Op": "foo", + }), + }}), + ok: false, + errMsg: "invalid Op", + }, + "patch with invalid Envoy resource Path": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "Path": "/invalid", + }), + }}), + ok: false, + errMsg: "no match for field", // this error comes from the patcher dry-run attempt + }, + "non-Add patch with Value": { + arguments: makeArguments(map[string]any{"Patches": []map[string]any{ + makePatch(map[string]any{ + "Op": OpRemove, + "Value": 1, + }), + }}), + ok: false, + errMsg: fmt.Sprintf("field Value is not supported for %s operation", OpRemove), + }, + // See decode.HookWeakDecodeFromSlice for more details. In practice, we can end up + // with a "Patches" field decoded to the single "Patch" value contained in the + // serialized slice (raised from the containing slice). Using WeakDecode solves + // for this. Ideally, we would kill that decoding hook entirely, but this test + // enforces expected behavior until we do. Multi-member slices should be unaffected + // by WeakDecode as it is a more-permissive version of the default behavior. + "single value Patches decoded as map construction succeeds": { + arguments: makeArguments(map[string]any{"Patches": makePatch(map[string]any{})}), + expected: validTestCase(OpAdd, TrafficDirectionOutbound, ResourceTypeRoute).expected, + ok: true, + }, + "invalid ProxyType": { + arguments: makeArguments(map[string]any{ + "Patches": []map[string]any{ + makePatch(map[string]any{}), + }, + "ProxyType": "invalid", + }), + ok: false, + errMsg: "invalid ProxyType", + }, + "unsupported ProxyType": { + arguments: makeArguments(map[string]any{ + "Patches": []map[string]any{ + makePatch(map[string]any{}), + }, + "ProxyType": api.ServiceKindMeshGateway, + }), + ok: false, + errMsg: "invalid ProxyType", + }, + } + + for o := range Ops { + for d := range TrafficDirections { + for t := range ResourceTypes { + cases["valid everything: "+strings.Join([]string{o, d, t}, ",")] = + validTestCase(Op(o), TrafficDirection(d), ResourceType(t)) + } + } + } + + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + + extensionName := api.BuiltinPropertyOverrideExtension + if tc.extensionName != "" { + extensionName = tc.extensionName + } + + // Build the wrapping RuntimeConfig struct, which contains the serialized + // arguments for constructing the property-override extension. + svc := api.CompoundServiceName{Name: "svc"} + ext := extensioncommon.RuntimeConfig{ + ServiceName: svc, + EnvoyExtension: api.EnvoyExtension{ + Name: extensionName, + Arguments: tc.arguments, + }, + } + + // Construct the actual extension + e, err := Constructor(ext.EnvoyExtension) + + if tc.ok { + require.NoError(t, err) + require.Equal(t, &extensioncommon.BasicEnvoyExtender{Extension: &tc.expected}, e) + } else { + require.ErrorContains(t, err, tc.errMsg) + } + }) + } +} + +func Test_patchResourceType(t *testing.T) { + makeExtension := func(patches ...Patch) *propertyOverride { + return &propertyOverride{ + Patches: patches, + } + } + makePatchWithPath := func(t ResourceType, d TrafficDirection, p string) Patch { + return Patch{ + ResourceFilter: ResourceFilter{ + ResourceType: t, + TrafficDirection: d, + }, + Op: OpAdd, + Path: p, + Value: 1, + } + } + makePatch := func(t ResourceType, d TrafficDirection) Patch { + return makePatchWithPath(t, d, "/foo") + } + + clusterOutbound := makePatch(ResourceTypeCluster, TrafficDirectionOutbound) + clusterInbound := makePatch(ResourceTypeCluster, TrafficDirectionInbound) + routeOutbound := makePatch(ResourceTypeRoute, TrafficDirectionOutbound) + routeOutbound2 := makePatchWithPath(ResourceTypeRoute, TrafficDirectionOutbound, "/bar") + routeInbound := makePatch(ResourceTypeRoute, TrafficDirectionInbound) + + type args struct { + d TrafficDirection + k proto.Message + p *propertyOverride + t ResourceType + } + type testCase struct { + args args + expectPatched bool + wantApplied []Patch + } + cases := map[string]testCase{ + "outbound gets matching patch": { + args: args{ + d: TrafficDirectionOutbound, + k: &clusterv3.Cluster{}, + p: makeExtension(clusterOutbound), + t: ResourceTypeCluster, + }, + expectPatched: true, + wantApplied: []Patch{clusterOutbound}, + }, + "inbound gets matching patch": { + args: args{ + d: TrafficDirectionInbound, + k: &clusterv3.Cluster{}, + p: makeExtension(clusterInbound), + t: ResourceTypeCluster, + }, + expectPatched: true, + wantApplied: []Patch{clusterInbound}, + }, + "multiple resources same direction only gets matching resource": { + args: args{ + d: TrafficDirectionOutbound, + k: &clusterv3.Cluster{}, + p: makeExtension(clusterOutbound, routeOutbound), + t: ResourceTypeCluster, + }, + expectPatched: true, + wantApplied: []Patch{clusterOutbound}, + }, + "multiple directions same resource only gets matching direction": { + args: args{ + d: TrafficDirectionOutbound, + k: &clusterv3.Cluster{}, + p: makeExtension(clusterOutbound, clusterInbound), + t: ResourceTypeCluster, + }, + expectPatched: true, + wantApplied: []Patch{clusterOutbound}, + }, + "multiple directions and resources only gets matching patch": { + args: args{ + d: TrafficDirectionInbound, + k: &routev3.RouteConfiguration{}, + p: makeExtension(clusterOutbound, clusterInbound, routeOutbound, routeInbound), + t: ResourceTypeRoute, + }, + expectPatched: true, + wantApplied: []Patch{routeInbound}, + }, + "multiple directions and resources multiple matches gets all matching patches": { + args: args{ + d: TrafficDirectionOutbound, + k: &routev3.RouteConfiguration{}, + p: makeExtension(clusterOutbound, clusterInbound, routeOutbound, routeInbound, routeOutbound2), + t: ResourceTypeRoute, + }, + expectPatched: true, + wantApplied: []Patch{routeOutbound, routeOutbound2}, + }, + "multiple directions and resources no matches gets no patches": { + args: args{ + d: TrafficDirectionOutbound, + k: &routev3.RouteConfiguration{}, + p: makeExtension(clusterInbound, routeOutbound, routeInbound, routeOutbound2), + t: ResourceTypeCluster, + }, + expectPatched: false, + wantApplied: nil, + }, + } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + mockPatcher := MockPatcher[proto.Message]{} + _, patched, err := patchResourceType[proto.Message](tc.args.k, tc.args.p, tc.args.t, tc.args.d, &mockPatcher) + + require.NoError(t, err, "unexpected error from mock") + require.Equal(t, tc.expectPatched, patched) + require.Equal(t, tc.wantApplied, mockPatcher.appliedPatches) + }) + } +} + +type MockPatcher[K proto.Message] struct { + appliedPatches []Patch +} + +func (m *MockPatcher[K]) applyPatch(k K, p Patch, _ bool) (result K, e error) { + m.appliedPatches = append(m.appliedPatches, p) + return k, nil +} + +func TestCanApply(t *testing.T) { + cases := map[string]struct { + ext *propertyOverride + conf *extensioncommon.RuntimeConfig + canApply bool + }{ + "valid proxy type": { + ext: &propertyOverride{ + ProxyType: api.ServiceKindConnectProxy, + }, + conf: &extensioncommon.RuntimeConfig{ + Kind: api.ServiceKindConnectProxy, + }, + canApply: true, + }, + "invalid proxy type": { + ext: &propertyOverride{ + ProxyType: api.ServiceKindTerminatingGateway, + }, + conf: &extensioncommon.RuntimeConfig{ + Kind: api.ServiceKindMeshGateway, + }, + canApply: false, + }, + } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + require.Equal(t, tc.canApply, tc.ext.CanApply(tc.conf)) + }) + } +} diff --git a/agent/envoyextensions/builtin/property-override/structpatcher.go b/agent/envoyextensions/builtin/property-override/structpatcher.go new file mode 100644 index 000000000..3a54ca25e --- /dev/null +++ b/agent/envoyextensions/builtin/property-override/structpatcher.go @@ -0,0 +1,508 @@ +package propertyoverride + +import ( + "fmt" + "strings" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// PatchStruct patches the given ProtoMessage according to the documented behavior of Patch and returns the result. +// If an error is returned, the returned K (which may be a partially modified copy) should be ignored. +func PatchStruct[K proto.Message](k K, patch Patch, debug bool) (result K, e error) { + // Don't panic due to misconfiguration. + defer func() { + if err := recover(); err != nil { + e = fmt.Errorf("unexpected panic: %v", err) + } + }() + + protoM := k.ProtoReflect() + + if patch.Path == "" || patch.Path == "/" { + // Return error with all possible fields at root of target. + return k, fmt.Errorf("non-empty, non-root Path is required;\n%s", + fieldListStr(protoM.Descriptor(), debug)) + } + + parsedPath := parsePath(patch.Path) + targetM, fieldDesc, err := findTargetMessageAndField(protoM, parsedPath, patch, debug) + if err != nil { + return k, err + } + if err := patchField(targetM, fieldDesc, patch, debug); err != nil { + return k, err + } + + // ProtoReflect returns a reflective _view_ of the underlying K, so + // we don't need to convert back explictly when we return k here. + return k, nil +} + +// parsePath returns the path tokens from a JSON Pointer (https://datatracker.ietf.org/doc/html/rfc6901/) string as +// expected by JSON Patch (e.g. "/foo/bar/baz"). +func parsePath(path string) []string { + return strings.Split(strings.TrimLeft(path, "/"), "/") +} + +// findTargetMessageAndField takes a root-level protoreflect.Message and slice of path elements and returns the +// protoreflect.FieldDescriptor that corresponds to the full path, along with the parent protoreflect.Message of +// that field. parsedPath must be non-empty. +// If any field in parsedPath cannot be matched while traversing the tree of messages, an error is returned. +func findTargetMessageAndField(m protoreflect.Message, parsedPath []string, patch Patch, debug bool) (protoreflect.Message, protoreflect.FieldDescriptor, error) { + if len(parsedPath) == 0 { + return nil, nil, fmt.Errorf("unexpected error: non-empty path is required") + } + + // Iterate until we've matched the (potentially nested) target field. + for { + fieldName := parsedPath[0] + if fieldName == "" { + return nil, nil, fmt.Errorf("empty field name in path") + } + + fieldDesc, err := childFieldDescriptor(m.Descriptor(), fieldName, debug) + if err != nil { + return nil, nil, err + } + + parsedPath = parsedPath[1:] + if len(parsedPath) == 0 { + // We've reached the end of the path, return current message and target field. + return m, fieldDesc, nil + } + + // Check whether we have a non-terminal (parent) field in the path for which we + // don't support child lookup. + switch { + case fieldDesc.IsList(): + return nil, nil, fmt.Errorf("path contains member of repeated field '%s'; repeated field member access is not supported", + fieldName) + case fieldDesc.IsMap(): + return nil, nil, fmt.Errorf("path contains member of map field '%s'; map field member access is not supported", + fieldName) + } + + fieldM := m.Get(fieldDesc).Message() + if !fieldM.IsValid() && patch.Op == OpAdd { + // Init this message field to a valid, empty value so that we can keep walking + // the path and set inner fields. Only do this for add, as remove should not + // initialize fields on its own. + m.Set(fieldDesc, protoreflect.ValueOfMessage(fieldM.New())) + fieldM = m.Get(fieldDesc).Message() + } + + // Advance our parent message "pointer" to the next field. + m = fieldM + } +} + +// patchField applies the given patch op to the target field on given parent message. +func patchField(parentM protoreflect.Message, fieldDesc protoreflect.FieldDescriptor, patch Patch, debug bool) error { + switch patch.Op { + case OpAdd: + return applyAdd(parentM, fieldDesc, patch, debug) + case OpRemove: + // Ignore Value if provided, per JSON Patch: "Members that are not explicitly defined for the + // operation in question MUST be ignored (i.e., the operation will complete as if the undefined + // member did not appear in the object)." + return applyRemove(parentM, fieldDesc) + } + return fmt.Errorf("unexpected error: no op implementation found") +} + +// removeField clears the target field on the given message. +func applyRemove(parentM protoreflect.Message, fieldDesc protoreflect.FieldDescriptor) error { + // Check whether the parent has this field, as clearing a field on an unset parent may panic. + if parentM.Has(fieldDesc) { + parentM.Clear(fieldDesc) + } + return nil +} + +// applyAdd updates the target field(s) on the given message based on the content of patch. +// If the patch value is a scalar, scalar wrapper, or scalar array, we set the target field directly with that value. +// If the patch value is a map, we set the indicated child fields on a new (empty) message matching the target field. +// Regardless, the target field is replaced entirely. This conforms to the PUT-style semantics of the JSON Patch "add" +// operation for objects (https://www.rfc-editor.org/rfc/rfc6902#section-4.1). +func applyAdd(parentM protoreflect.Message, fieldDesc protoreflect.FieldDescriptor, patch Patch, debug bool) error { + if patch.Value == nil { + return fmt.Errorf("non-nil Value is required; use an empty map to reset all fields on a message or the 'remove' op to unset fields") + } + mapValue, isMapValue := patch.Value.(map[string]interface{}) + // If the field is a proto map type, we'll treat it as a "single" field for error handling purposes. + // If we support proto map targets in the future, it will still likely be treated as a single field, + // similar to a list (repeated field). This map handling is specific to _our_ patch semantics for + // updating multiple message fields at once. + if isMapValue && !fieldDesc.IsMap() { + // Get a fresh copy of the target field's message, then set the children indicated by the patch. + fieldM := parentM.Get(fieldDesc).Message().New() + for k, v := range mapValue { + targetFieldDesc, err := childFieldDescriptor(fieldDesc.Message(), k, debug) + if err != nil { + return err + } + val, err := toProtoValue(fieldM, targetFieldDesc, v) + if err != nil { + return err + } + fieldM.Set(targetFieldDesc, val) + } + parentM.Set(fieldDesc, protoreflect.ValueOf(fieldM)) + } else { + // Just set the field directly, as our patch value is not a map. + val, err := toProtoValue(parentM, fieldDesc, patch.Value) + if err != nil { + return err + } + parentM.Set(fieldDesc, val) + } + + return nil +} + +func childFieldDescriptor(parentDesc protoreflect.MessageDescriptor, fieldName string, debug bool) (protoreflect.FieldDescriptor, error) { + if childFieldDesc := parentDesc.Fields().ByName(protoreflect.Name(fieldName)); childFieldDesc != nil { + return childFieldDesc, nil + } + return nil, fmt.Errorf("no match for field '%s'!\n%s", fieldName, fieldListStr(parentDesc, debug)) +} + +// fieldListStr prints all possible fields (debug) or the first 10 fields of a given MessageDescriptor. +func fieldListStr(messageDesc protoreflect.MessageDescriptor, debug bool) string { + // Future: it might be nice to use something like https://github.com/agnivade/levenshtein + // or https://github.com/schollz/closestmatch to inform these choices. + fields := messageDesc.Fields() + + // If we're not in debug mode and there's > 10 possible fields, only print the first 10. + printCount := fields.Len() + truncateFields := false + if !debug && printCount > 10 { + truncateFields = true + printCount = 10 + } + + msg := strings.Builder{} + msg.WriteString("available ") + msg.WriteString(string(messageDesc.FullName())) + msg.WriteString(" fields:\n") + for i := 0; i < printCount; i++ { + msg.WriteString(fields.Get(i).TextName()) + msg.WriteString("\n") + } + if truncateFields { + msg.WriteString("First 10 fields for this message included, configure with `Debug = true` to print all.") + } + + return msg.String() +} + +func toProtoValue(parentM protoreflect.Message, fieldDesc protoreflect.FieldDescriptor, patchValue interface{}) (v protoreflect.Value, e error) { + // Repeated fields. Check for these first, so we can use Kind below for single value + // fields (repeated fields have a Kind corresponding to their members). + // We have to do special handling for int types and strings since they could be enums. + if fieldDesc.IsList() { + list := parentM.NewField(fieldDesc).List() + switch val := patchValue.(type) { + case []int: + return toProtoIntOrEnumList(val, list, fieldDesc) + case []int32: + return toProtoIntOrEnumList(val, list, fieldDesc) + case []int64: + return toProtoIntOrEnumList(val, list, fieldDesc) + case []uint: + return toProtoIntOrEnumList(val, list, fieldDesc) + case []uint32: + return toProtoIntOrEnumList(val, list, fieldDesc) + case []uint64: + return toProtoIntOrEnumList(val, list, fieldDesc) + case []float32: + return toProtoNumericList(val, list, fieldDesc) + case []float64: + return toProtoNumericList(val, list, fieldDesc) + case []bool: + return toProtoList(val, list) + case []string: + if fieldDesc.Kind() == protoreflect.EnumKind { + return toProtoEnumList(val, list, fieldDesc) + } + return toProtoList(val, list) + default: + if fieldDesc.Kind() == protoreflect.MessageKind || + fieldDesc.Kind() == protoreflect.GroupKind || + fieldDesc.Kind() == protoreflect.BytesKind { + return unsupportedTargetTypeErr(fieldDesc) + } + return typeMismatchErr(fieldDesc, val) + } + } + + switch fieldDesc.Kind() { + case protoreflect.MessageKind: + // google.protobuf wrapper types are used for detecting presence of scalars. If the + // target field is a message, and the patch value is not a map, we assume the user + // is targeting a wrapper type or has misconfigured the path. + return toProtoWrapperValue(fieldDesc, patchValue) + case protoreflect.EnumKind: + return toProtoEnumValue(fieldDesc, patchValue) + case protoreflect.Int32Kind, + protoreflect.Int64Kind, + protoreflect.Sint32Kind, + protoreflect.Sint64Kind, + protoreflect.Uint32Kind, + protoreflect.Uint64Kind, + protoreflect.Fixed32Kind, + protoreflect.Fixed64Kind, + protoreflect.Sfixed32Kind, + protoreflect.Sfixed64Kind, + protoreflect.FloatKind, + protoreflect.DoubleKind: + // We have to be careful specifically obtaining the correct proto field type here, + // since conversion checking by protoreflect is stringent and will not accept e.g. + // int->uint32 mismatches, or float->int downcasts. + switch val := patchValue.(type) { + case int: + return toProtoNumericValue(fieldDesc, val) + case int32: + return toProtoNumericValue(fieldDesc, val) + case int64: + return toProtoNumericValue(fieldDesc, val) + case uint: + return toProtoNumericValue(fieldDesc, val) + case uint32: + return toProtoNumericValue(fieldDesc, val) + case uint64: + return toProtoNumericValue(fieldDesc, val) + case float32: + return toProtoNumericValue(fieldDesc, val) + case float64: + return toProtoNumericValue(fieldDesc, val) + } + } + + // Fall back to protoreflect.ValueOf, which may panic if an unexpected type is passed. + defer func() { + if err := recover(); err != nil { + _, e = typeMismatchErr(fieldDesc, patchValue) + } + }() + return protoreflect.ValueOf(patchValue), nil +} + +func toProtoList[V float32 | float64 | bool | string](vs []V, l protoreflect.List) (protoreflect.Value, error) { + for _, v := range vs { + l.Append(protoreflect.ValueOf(v)) + } + return protoreflect.ValueOfList(l), nil +} + +// toProtoIntOrEnumList takes a slice of some integer type V and returns a protoreflect.Value List of either the +// corresponding proto integer type or enum values, depending on the type of the target field. +func toProtoIntOrEnumList[V int | int32 | int64 | uint | uint32 | uint64](vs []V, l protoreflect.List, fieldDesc protoreflect.FieldDescriptor) (protoreflect.Value, error) { + if fieldDesc.Kind() == protoreflect.EnumKind { + return toProtoEnumList(vs, l, fieldDesc) + } + return toProtoNumericList(vs, l, fieldDesc) +} + +// toProtoNumericList takes a slice of some numeric type V and returns a protoreflect.Value List of the corresponding +// proto integer or float values, depending on the type of the target field. +func toProtoNumericList[V int | int32 | int64 | uint | uint32 | uint64 | float32 | float64](vs []V, l protoreflect.List, fieldDesc protoreflect.FieldDescriptor) (protoreflect.Value, error) { + for _, v := range vs { + i, err := toProtoNumericValue(fieldDesc, v) + if err != nil { + return protoreflect.Value{}, err + } + l.Append(i) + } + return protoreflect.ValueOfList(l), nil +} + +func toProtoEnumList[V int | int32 | int64 | uint | uint32 | uint64 | string](vs []V, l protoreflect.List, fieldDesc protoreflect.FieldDescriptor) (protoreflect.Value, error) { + for _, v := range vs { + e, err := toProtoEnumValue(fieldDesc, v) + if err != nil { + return protoreflect.Value{}, err + } + l.Append(e) + } + return protoreflect.ValueOfList(l), nil +} + +// toProtoNumericValue aids converting from a Go numeric type to a specific protoreflect.Value type, casting to match +// the target field type. +// +// It supports converting numeric Go slices to List values, and scalar numeric values, in a protoreflect +// validation-compatible manner by replacing the internal conversion logic of protoreflect.ValueOf with a more lenient +// "blind" cast, with the assumption that inputs to structpatcher may have been deserialized in such a way as to make +// strict type checking infeasible, even if the actual value and target type are compatible. This function does _not_ +// attempt to handle overflows, only type conversion. +// +// See https://protobuf.dev/programming-guides/proto3/#scalar for canonical proto3 type mappings, reflected here. +func toProtoNumericValue[V int | int32 | int64 | uint | uint32 | uint64 | float32 | float64](fieldDesc protoreflect.FieldDescriptor, v V) (protoreflect.Value, error) { + switch fieldDesc.Kind() { + case protoreflect.FloatKind: + return protoreflect.ValueOfFloat32(float32(v)), nil + case protoreflect.DoubleKind: + return protoreflect.ValueOfFloat64(float64(v)), nil + case protoreflect.Int32Kind: + return protoreflect.ValueOfInt32(int32(v)), nil + case protoreflect.Int64Kind: + return protoreflect.ValueOfInt64(int64(v)), nil + case protoreflect.Uint32Kind: + return protoreflect.ValueOfUint32(uint32(v)), nil + case protoreflect.Uint64Kind: + return protoreflect.ValueOfUint64(uint64(v)), nil + case protoreflect.Sint32Kind: + return protoreflect.ValueOfInt32(int32(v)), nil + case protoreflect.Sint64Kind: + return protoreflect.ValueOfInt64(int64(v)), nil + case protoreflect.Fixed32Kind: + return protoreflect.ValueOfUint32(uint32(v)), nil + case protoreflect.Fixed64Kind: + return protoreflect.ValueOfUint64(uint64(v)), nil + case protoreflect.Sfixed32Kind: + return protoreflect.ValueOfInt32(int32(v)), nil + case protoreflect.Sfixed64Kind: + return protoreflect.ValueOfInt64(int64(v)), nil + default: + // Fall back to protoreflect.ValueOf, which may panic if an unexpected type is passed. + return protoreflect.ValueOf(v), nil + } +} + +func toProtoEnumValue(fieldDesc protoreflect.FieldDescriptor, patchValue any) (protoreflect.Value, error) { + // For enums, we accept the field number or a string representation of the name + // (both supported by protojson). EnumNumber is a type alias for int32, but we + // may be dealing with a 64-bit number in Go depending on how it was obtained. + var enumValue protoreflect.EnumValueDescriptor + switch val := patchValue.(type) { + case string: + enumValue = fieldDesc.Enum().Values().ByName(protoreflect.Name(val)) + case int: + enumValue = fieldDesc.Enum().Values().ByNumber(protoreflect.EnumNumber(val)) + case int32: + enumValue = fieldDesc.Enum().Values().ByNumber(protoreflect.EnumNumber(val)) + case int64: + enumValue = fieldDesc.Enum().Values().ByNumber(protoreflect.EnumNumber(val)) + case uint: + enumValue = fieldDesc.Enum().Values().ByNumber(protoreflect.EnumNumber(val)) + case uint32: + enumValue = fieldDesc.Enum().Values().ByNumber(protoreflect.EnumNumber(val)) + case uint64: + enumValue = fieldDesc.Enum().Values().ByNumber(protoreflect.EnumNumber(val)) + } + if enumValue != nil { + return protoreflect.ValueOfEnum(enumValue.Number()), nil + } + return typeMismatchErr(fieldDesc, patchValue) +} + +// toProtoWrapperValue converts possible runtime Go types (per https://protobuf.dev/programming-guides/proto3/#scalar) +// to the appropriate proto wrapper target type based on the name of the given FieldDescriptor. +// +// This function does not attempt to handle overflows, only type conversion. +func toProtoWrapperValue(fieldDesc protoreflect.FieldDescriptor, patchValue any) (protoreflect.Value, error) { + fullName := string(fieldDesc.Message().FullName()) + if !strings.HasPrefix(fullName, "google.protobuf.") || !strings.HasSuffix(fullName, "Value") { + return unsupportedTargetTypeErr(fieldDesc) + } + + switch val := patchValue.(type) { + case int: + return toProtoIntWrapperValue(fieldDesc, val) + case int32: + return toProtoIntWrapperValue(fieldDesc, val) + case int64: + return toProtoIntWrapperValue(fieldDesc, val) + case uint: + return toProtoIntWrapperValue(fieldDesc, val) + case uint32: + return toProtoIntWrapperValue(fieldDesc, val) + case uint64: + return toProtoIntWrapperValue(fieldDesc, val) + case float32: + switch fieldDesc.Message().FullName() { + case "google.protobuf.FloatValue": + v := wrapperspb.Float(val) + return protoreflect.ValueOf(v.ProtoReflect()), nil + case "google.protobuf.DoubleValue": + v := wrapperspb.Double(float64(val)) + return protoreflect.ValueOf(v.ProtoReflect()), nil + default: + // Fall back to int wrapper, since we may actually be targeting this instead. + // Failure will result in a typical type mismatch error. + return toProtoIntWrapperValue(fieldDesc, val) + } + case float64: + switch fieldDesc.Message().FullName() { + case "google.protobuf.FloatValue": + v := wrapperspb.Float(float32(val)) + return protoreflect.ValueOf(v.ProtoReflect()), nil + case "google.protobuf.DoubleValue": + v := wrapperspb.Double(val) + return protoreflect.ValueOf(v.ProtoReflect()), nil + default: + // Fall back to int wrapper, since we may actually be targeting this instead. + // Failure will result in a typical type mismatch error. + return toProtoIntWrapperValue(fieldDesc, val) + } + case bool: + switch fieldDesc.Message().FullName() { + case "google.protobuf.BoolValue": + v := wrapperspb.Bool(val) + return protoreflect.ValueOf(v.ProtoReflect()), nil + } + case string: + switch fieldDesc.Message().FullName() { + case "google.protobuf.StringValue": + v := wrapperspb.String(val) + return protoreflect.ValueOf(v.ProtoReflect()), nil + } + } + return typeMismatchErr(fieldDesc, patchValue) +} + +func toProtoIntWrapperValue[V int | int32 | int64 | uint | uint32 | uint64 | float32 | float64](fieldDesc protoreflect.FieldDescriptor, v V) (protoreflect.Value, error) { + switch fieldDesc.Message().FullName() { + case "google.protobuf.UInt32Value": + v := wrapperspb.UInt32(uint32(v)) + return protoreflect.ValueOf(v.ProtoReflect()), nil + case "google.protobuf.UInt64Value": + v := wrapperspb.UInt64(uint64(v)) + return protoreflect.ValueOf(v.ProtoReflect()), nil + case "google.protobuf.Int32Value": + v := wrapperspb.Int32(int32(v)) + return protoreflect.ValueOf(v.ProtoReflect()), nil + case "google.protobuf.Int64Value": + v := wrapperspb.Int64(int64(v)) + return protoreflect.ValueOf(v.ProtoReflect()), nil + default: + return typeMismatchErr(fieldDesc, v) + } +} + +func typeMismatchErr(fieldDesc protoreflect.FieldDescriptor, v interface{}) (protoreflect.Value, error) { + return protoreflect.Value{}, fmt.Errorf("patch value type %T could not be applied to target field type '%s'", v, fieldType(fieldDesc)) +} + +func unsupportedTargetTypeErr(fieldDesc protoreflect.FieldDescriptor) (protoreflect.Value, error) { + return protoreflect.Value{}, fmt.Errorf("unsupported target field type '%s'", fieldType(fieldDesc)) +} + +func fieldType(fieldDesc protoreflect.FieldDescriptor) string { + v := fieldDesc.Kind().String() + // Scalars have a useful Kind string, but complex fields should have their full name for clarity. + if fieldDesc.Kind() == protoreflect.MessageKind { + v = string(fieldDesc.Message().FullName()) + } + if fieldDesc.IsList() { + v = "repeated " + v // Kind reflects the type of repeated elements in repeated fields. + } + if fieldDesc.IsMap() { + v = "map" // maps are difficult to get types from, but we don't support them, so return a simple value for now. + } + return v +} diff --git a/agent/envoyextensions/builtin/property-override/structpatcher_test.go b/agent/envoyextensions/builtin/property-override/structpatcher_test.go new file mode 100644 index 000000000..d81f15ebd --- /dev/null +++ b/agent/envoyextensions/builtin/property-override/structpatcher_test.go @@ -0,0 +1,1063 @@ +package propertyoverride + +import ( + "fmt" + envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + _struct "github.com/golang/protobuf/ptypes/struct" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/wrapperspb" + "testing" +) + +func TestPatchStruct(t *testing.T) { + makePatch := func(o Op, p string, v any) Patch { + return Patch{ + Op: o, + Path: p, + Value: v, + } + } + makeAddPatch := func(p string, v any) Patch { + return makePatch(OpAdd, p, v) + } + makeRemovePatch := func(p string) Patch { + return makePatch(OpRemove, p, nil) + } + expectFieldsListErr := func(resourceName string, truncatedFieldsWarning bool) func(t *testing.T, err error) { + return func(t *testing.T, err error) { + require.Contains(t, err.Error(), fmt.Sprintf("available %s fields:", resourceName)) + if truncatedFieldsWarning { + require.Contains(t, err.Error(), "First 10 fields for this message included, configure with `Debug = true` to print all.") + } else { + require.NotContains(t, err.Error(), "First 10 fields for this message included, configure with `Debug = true` to print all.") + } + } + } + type args struct { + k proto.Message + patches []Patch + debug bool + } + type testCase struct { + args args + expected proto.Message + ok bool + errMsg string + errFunc func(*testing.T, error) + } + + // Simplify test case construction for variants of potential input types + uint32VariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_endpoint_v3.Endpoint{ + HealthCheckConfig: &envoy_endpoint_v3.Endpoint_HealthCheckConfig{ + PortValue: 3000, + }, + }, + patches: []Patch{makeAddPatch( + "/health_check_config/port_value", + i, + )}, + }, + expected: &envoy_endpoint_v3.Endpoint{ + HealthCheckConfig: &envoy_endpoint_v3.Endpoint_HealthCheckConfig{ + PortValue: 1234, + }, + }, + ok: true, + } + } + uint32WrapperVariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + i, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(1234), + }, + }, + ok: true, + } + } + uint64WrapperVariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + MaximumRingSize: wrapperspb.UInt64(999999999), + }, + }, + }, + patches: []Patch{makeAddPatch( + "/ring_hash_lb_config/maximum_ring_size", + i, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + MaximumRingSize: wrapperspb.UInt64(12345678), + }, + }, + }, + ok: true, + } + } + doubleVariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_LeastRequestLbConfig_{ + LeastRequestLbConfig: &envoy_cluster_v3.Cluster_LeastRequestLbConfig{ + ActiveRequestBias: &corev3.RuntimeDouble{ + DefaultValue: 1.0, + }, + }, + }, + }, + patches: []Patch{makeAddPatch( + "/least_request_lb_config/active_request_bias/default_value", + i, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_LeastRequestLbConfig_{ + LeastRequestLbConfig: &envoy_cluster_v3.Cluster_LeastRequestLbConfig{ + ActiveRequestBias: &corev3.RuntimeDouble{ + DefaultValue: 1.5, + }, + }, + }, + }, + ok: true, + } + } + doubleWrapperVariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_cluster_v3.Cluster{ + PreconnectPolicy: &envoy_cluster_v3.Cluster_PreconnectPolicy{ + PerUpstreamPreconnectRatio: wrapperspb.Double(1.0), + }, + }, + patches: []Patch{makeAddPatch( + "/preconnect_policy/per_upstream_preconnect_ratio", + i, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + PreconnectPolicy: &envoy_cluster_v3.Cluster_PreconnectPolicy{ + PerUpstreamPreconnectRatio: wrapperspb.Double(1.5), + }, + }, + ok: true, + } + } + enumByNumberVariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + HashFunction: envoy_cluster_v3.Cluster_RingHashLbConfig_XX_HASH, + }, + }, + }, + patches: []Patch{makeAddPatch( + "/ring_hash_lb_config/hash_function", + i, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + HashFunction: envoy_cluster_v3.Cluster_RingHashLbConfig_MURMUR_HASH_2, + }, + }, + }, + ok: true, + } + } + repeatedIntVariantTestCase := func(i any) testCase { + return testCase{ + args: args{ + k: &envoy_route_v3.RetryPolicy{ + RetriableStatusCodes: []uint32{429, 502}, + }, + patches: []Patch{makeAddPatch( + "/retriable_status_codes", + i, + )}, + }, + expected: &envoy_route_v3.RetryPolicy{ + RetriableStatusCodes: []uint32{503, 504}, + }, + ok: true, + } + } + + cases := map[string]testCase{ + // Some variants of target types are covered in conversion code but missing + // from this table due to lacking available examples in Envoy v3 protos. An + // improvement could be a home-rolled proto with every possible target type. + "add single field: int->uint32": uint32VariantTestCase(int(1234)), + "add single field: int32->uint32": uint32VariantTestCase(int32(1234)), + "add single field: int64->uint32": uint32VariantTestCase(int64(1234)), + "add single field: uint->uint32": uint32VariantTestCase(uint(1234)), + "add single field: uint32->uint32": uint32VariantTestCase(uint32(1234)), + "add single field: uint64->uint32": uint32VariantTestCase(uint64(1234)), + "add single field: float32->uint32": uint32VariantTestCase(float32(1234.0)), + "add single field: float64->uint32": uint32VariantTestCase(float64(1234.0)), + "add single field: int->uint32 wrapper": uint32WrapperVariantTestCase(int(1234)), + "add single field: int32->uint32 wrapper": uint32WrapperVariantTestCase(int32(1234)), + "add single field: int64->uint32 wrapper": uint32WrapperVariantTestCase(int64(1234)), + "add single field: uint->uint32 wrapper": uint32WrapperVariantTestCase(uint(1234)), + "add single field: uint32->uint32 wrapper": uint32WrapperVariantTestCase(uint32(1234)), + "add single field: uint64->uint32 wrapper": uint32WrapperVariantTestCase(uint64(1234)), + "add single field: float32->uint32 wrapper": uint32WrapperVariantTestCase(float32(1234.0)), + "add single field: float64->uint32 wrapper": uint32WrapperVariantTestCase(float64(1234.0)), + "add single field: int->uint64 wrapper": uint64WrapperVariantTestCase(int(12345678)), + "add single field: int32->uint64 wrapper": uint64WrapperVariantTestCase(int32(12345678)), + "add single field: int64->uint64 wrapper": uint64WrapperVariantTestCase(int64(12345678)), + "add single field: uint->uint64 wrapper": uint64WrapperVariantTestCase(uint(12345678)), + "add single field: uint32->uint64 wrapper": uint64WrapperVariantTestCase(uint32(12345678)), + "add single field: uint64->uint64 wrapper": uint64WrapperVariantTestCase(uint64(12345678)), + "add single field: float32->uint64 wrapper": uint64WrapperVariantTestCase(float32(12345678.0)), + "add single field: float64->uint64 wrapper": uint64WrapperVariantTestCase(float64(12345678.0)), + "add single field: float32->double": doubleVariantTestCase(float32(1.5)), + "add single field: float64->double": doubleVariantTestCase(float64(1.5)), + "add single field: float32->double wrapper": doubleWrapperVariantTestCase(float32(1.5)), + "add single field: float64->double wrapper": doubleWrapperVariantTestCase(float64(1.5)), + "add single field: bool": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + RespectDnsTtl: false, + }, + patches: []Patch{makeAddPatch( + "/respect_dns_ttl", + true, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + RespectDnsTtl: true, + }, + ok: true, + }, + "add single field: bool wrapper": { + args: args{ + k: &envoy_listener_v3.Listener{ + UseOriginalDst: wrapperspb.Bool(false), + }, + patches: []Patch{makeAddPatch( + "/use_original_dst", + true, + )}, + }, + expected: &envoy_listener_v3.Listener{ + UseOriginalDst: wrapperspb.Bool(true), + }, + ok: true, + }, + "add single field: string": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + AltStatName: "foo", + }, + patches: []Patch{makeAddPatch( + "/alt_stat_name", + "bar", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + AltStatName: "bar", + }, + ok: true, + }, + "add single field: enum by name": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + HashFunction: envoy_cluster_v3.Cluster_RingHashLbConfig_XX_HASH, + }, + }, + }, + patches: []Patch{makeAddPatch( + "/ring_hash_lb_config/hash_function", + "MURMUR_HASH_2", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + HashFunction: envoy_cluster_v3.Cluster_RingHashLbConfig_MURMUR_HASH_2, + }, + }, + }, + ok: true, + }, + "add single field: enum by number int": enumByNumberVariantTestCase(int(1)), + "add single field: enum by number int32": enumByNumberVariantTestCase(int32(1)), + "add single field: enum by number int64": enumByNumberVariantTestCase(int64(1)), + "add single field: enum by number uint": enumByNumberVariantTestCase(uint(1)), + "add single field: enum by number uint32": enumByNumberVariantTestCase(uint32(1)), + "add single field: enum by number uint64": enumByNumberVariantTestCase(uint64(1)), + "add single field previously unmodified": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/alt_stat_name", + "bar", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + AltStatName: "bar", + }, + ok: true, + }, + "add single field deeply nested": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + UpstreamConnectionOptions: &envoy_cluster_v3.UpstreamConnectionOptions{ + TcpKeepalive: &corev3.TcpKeepalive{ + KeepaliveProbes: wrapperspb.UInt32(2), + }, + }, + }, + patches: []Patch{makeAddPatch( + "/upstream_connection_options/tcp_keepalive/keepalive_probes", + 5, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + UpstreamConnectionOptions: &envoy_cluster_v3.UpstreamConnectionOptions{ + TcpKeepalive: &corev3.TcpKeepalive{ + KeepaliveProbes: wrapperspb.UInt32(5), + }, + }, + }, + ok: true, + }, + "add single field deeply nested with intermediate unset field": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + // Explicitly set to nil just in case defaults change. + UpstreamConnectionOptions: nil, + }, + patches: []Patch{makeAddPatch( + "/upstream_connection_options/tcp_keepalive/keepalive_probes", + 1234, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + UpstreamConnectionOptions: &envoy_cluster_v3.UpstreamConnectionOptions{ + TcpKeepalive: &corev3.TcpKeepalive{ + KeepaliveProbes: wrapperspb.UInt32(1234), + }, + }, + }, + ok: true, + }, + "add repeated field: int->uint32": repeatedIntVariantTestCase([]int{503, 504}), + "add repeated field: int32->uint32": repeatedIntVariantTestCase([]int32{503, 504}), + "add repeated field: int64->uint32": repeatedIntVariantTestCase([]int64{503, 504}), + "add repeated field: uint->uint32": repeatedIntVariantTestCase([]uint{503, 504}), + "add repeated field: uint32->uint32": repeatedIntVariantTestCase([]uint32{503, 504}), + "add repeated field: uint64->uint32": repeatedIntVariantTestCase([]uint64{503, 504}), + "add repeated field: float32->uint32": repeatedIntVariantTestCase([]float32{503.0, 504.0}), + "add repeated field: float64->uint32": repeatedIntVariantTestCase([]float64{503.0, 504.0}), + "add repeated field: string": { + args: args{ + k: &envoy_route_v3.RouteConfiguration{}, + patches: []Patch{makeAddPatch( + "/internal_only_headers", + []string{"X-Custom-Header1", "X-Custom-Header-2"}, + )}, + }, + expected: &envoy_route_v3.RouteConfiguration{ + InternalOnlyHeaders: []string{"X-Custom-Header1", "X-Custom-Header-2"}, + }, + ok: true, + }, + "add repeated field: enum by name": { + args: args{ + k: &corev3.HealthStatusSet{ + Statuses: []corev3.HealthStatus{corev3.HealthStatus_DRAINING}, + }, + patches: []Patch{makeAddPatch( + "/statuses", + []string{"HEALTHY", "UNHEALTHY"}, + )}, + }, + expected: &corev3.HealthStatusSet{ + Statuses: []corev3.HealthStatus{corev3.HealthStatus_HEALTHY, corev3.HealthStatus_UNHEALTHY}, + }, + ok: true, + }, + "add repeated field: enum by number": { + args: args{ + k: &corev3.HealthStatusSet{ + Statuses: []corev3.HealthStatus{corev3.HealthStatus_DRAINING}, + }, + patches: []Patch{makeAddPatch( + "/statuses", + []int{1, 2}, + )}, + }, + expected: &corev3.HealthStatusSet{ + Statuses: []corev3.HealthStatus{corev3.HealthStatus_HEALTHY, corev3.HealthStatus_UNHEALTHY}, + }, + ok: true, + }, + "add message field: empty": { + args: args{ + k: &envoy_listener_v3.Listener{}, + patches: []Patch{makeAddPatch( + "/connection_balance_config/exact_balance", + map[string]any{}, + )}, + }, + expected: &envoy_listener_v3.Listener{ + ConnectionBalanceConfig: &envoy_listener_v3.Listener_ConnectionBalanceConfig{ + BalanceType: &envoy_listener_v3.Listener_ConnectionBalanceConfig_ExactBalance_{}, + }, + }, + ok: true, + }, + "add message field: multiple fields": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + FailurePercentageThreshold: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeAddPatch( + "/outlier_detection", + map[string]any{ + "enforcing_consecutive_5xx": 1234, + "failure_percentage_request_volume": 2345, + }, + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(1234), + FailurePercentageRequestVolume: wrapperspb.UInt32(2345), + }, + }, + ok: true, + }, + "add multiple single field patches merge with existing object": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + FailurePercentageThreshold: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{ + makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + 1234, + ), + makeAddPatch( + "/outlier_detection/failure_percentage_request_volume", + 2345, + ), + }, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(1234), + FailurePercentageRequestVolume: wrapperspb.UInt32(2345), // Previously unspecified field set + FailurePercentageThreshold: wrapperspb.UInt32(9999), // Existing unmodified field retained + }, + }, + ok: true, + }, + "remove single field: scalar wrapper": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeRemovePatch( + "/outlier_detection/enforcing_consecutive_5xx", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{}, + }, + ok: true, + }, + "remove single field: string (reset to empty)": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + AltStatName: "foo", + }, + patches: []Patch{makeRemovePatch( + "/alt_stat_name", + )}, + }, + expected: &envoy_cluster_v3.Cluster{}, + ok: true, + }, + "remove single field: bool (reset to false)": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + RespectDnsTtl: true, + }, + patches: []Patch{makeRemovePatch( + "/respect_dns_ttl", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + RespectDnsTtl: false, + }, + ok: true, + }, + "remove single field: enum (reset to default)": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + HashFunction: envoy_cluster_v3.Cluster_RingHashLbConfig_MURMUR_HASH_2, + }, + }, + }, + patches: []Patch{makeRemovePatch( + "/ring_hash_lb_config/hash_function", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + LbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig_{ + RingHashLbConfig: &envoy_cluster_v3.Cluster_RingHashLbConfig{ + HashFunction: envoy_cluster_v3.Cluster_RingHashLbConfig_XX_HASH, + }, + }, + }, + ok: true, + }, + "remove single field: message": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + FailurePercentageThreshold: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeRemovePatch( + "/outlier_detection", + )}, + }, + expected: &envoy_cluster_v3.Cluster{}, + ok: true, + }, + "remove single field: map": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + Metadata: &corev3.Metadata{ + FilterMetadata: map[string]*_struct.Struct{ + "foo": nil, + }, + }, + }, + patches: []Patch{makeRemovePatch( + "/metadata/filter_metadata", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + Metadata: &corev3.Metadata{}, + }, + ok: true, + }, + "remove single field deeply nested": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + UpstreamConnectionOptions: &envoy_cluster_v3.UpstreamConnectionOptions{ + TcpKeepalive: &corev3.TcpKeepalive{ + KeepaliveProbes: wrapperspb.UInt32(9999), + }, + }, + }, + patches: []Patch{makeRemovePatch( + "/upstream_connection_options/tcp_keepalive/keepalive_probes", + )}, + }, + expected: &envoy_cluster_v3.Cluster{ + UpstreamConnectionOptions: &envoy_cluster_v3.UpstreamConnectionOptions{ + TcpKeepalive: &corev3.TcpKeepalive{}, + }, + }, + ok: true, + }, + "remove repeated field: message": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + Filters: []*envoy_cluster_v3.Filter{ + { + Name: "foo", + }, + }, + }, + patches: []Patch{makeRemovePatch( + "/filters", + )}, + }, + expected: &envoy_cluster_v3.Cluster{}, + ok: true, + }, + "remove multiple single field patches merge with existing object": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + FailurePercentageThreshold: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{ + makeRemovePatch( + "/outlier_detection/enforcing_consecutive_5xx", + ), + makeRemovePatch( + "/outlier_detection/failure_percentage_request_volume", // No-op removal + ), + }, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + FailurePercentageThreshold: wrapperspb.UInt32(9999), // Existing unmodified field retained + }, + }, + ok: true, + }, + "remove does not instantiate intermediate fields that are unset": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + // Explicitly set to nil just in case defaults change. + UpstreamConnectionOptions: nil, + }, + patches: []Patch{makeRemovePatch( + "/upstream_connection_options/tcp_keepalive/keepalive_probes", + )}, + }, + expected: &envoy_cluster_v3.Cluster{}, + ok: true, + }, + "add and remove multiple single field patches merge with existing object": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + FailurePercentageRequestVolume: wrapperspb.UInt32(9999), + FailurePercentageThreshold: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{ + makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + 1234, + ), + makeRemovePatch( + "/outlier_detection/failure_percentage_request_volume", + ), + }, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(1234), // New field added + FailurePercentageThreshold: wrapperspb.UInt32(9999), // Existing unmodified field retained + }, + }, + ok: true, + }, + "add then remove respects order of operations": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{ + makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + 1234, + ), + makeRemovePatch( + "/outlier_detection", + ), + }, + }, + expected: &envoy_cluster_v3.Cluster{}, + ok: true, + }, + "remove then add respects order of operations": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + FailurePercentageRequestVolume: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{ + makeRemovePatch( + "/outlier_detection", + ), + makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + 1234, + ), + }, + }, + expected: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(1234), // New field added + // Previous field removed by remove op + }, + }, + ok: true, + }, + "add invalid value: scalar->scalar type mismatch": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + "NaN", + )}, + }, + ok: false, + errMsg: "patch value type string could not be applied to target field type 'google.protobuf.UInt32Value'", + }, + "add invalid value: non-scalar->scalar type mismatch": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeAddPatch( + "/respect_dns_ttl", + []string{"bad", "value"}, + )}, + }, + ok: false, + errMsg: "patch value type []string could not be applied to target field type 'bool'", + }, + "add invalid value: scalar->enum type mismatch": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/ring_hash_lb_config/hash_function", + 1.5, + )}, + }, + ok: false, + errMsg: "patch value type float64 could not be applied to target field type 'enum'", + }, + "add invalid value: nil scalar": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + RespectDnsTtl: false, + }, + patches: []Patch{makeAddPatch( + "/respect_dns_ttl", + nil, + )}, + }, + ok: false, + errMsg: "non-nil Value is required; use an empty map to reset all fields on a message or the 'remove' op to unset fields", + }, + "add invalid value: nil wrapper": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeAddPatch( + "/outlier_detection/enforcing_consecutive_5xx", + nil, + )}, + }, + ok: false, + errMsg: "non-nil Value is required; use an empty map to reset all fields on a message or the 'remove' op to unset fields", + }, + "add invalid value: nil message": { + + args: args{ + k: &envoy_cluster_v3.Cluster{ + OutlierDetection: &envoy_cluster_v3.OutlierDetection{ + EnforcingConsecutive_5Xx: wrapperspb.UInt32(9999), + FailurePercentageThreshold: wrapperspb.UInt32(9999), + }, + }, + patches: []Patch{makeAddPatch( + "/outlier_detection", + nil, + )}, + }, + ok: false, + errMsg: "non-nil Value is required; use an empty map to reset all fields on a message or the 'remove' op to unset fields", + }, + "add invalid value: mixed type scalar": { + args: args{ + k: &envoy_route_v3.RouteConfiguration{}, + patches: []Patch{makeAddPatch( + "/internal_only_headers", + []any{"X-Custom-Header1", 123}, + )}, + }, + expected: &envoy_route_v3.RouteConfiguration{ + InternalOnlyHeaders: []string{"X-Custom-Header1", "X-Custom-Header-2"}, + }, + ok: false, + errMsg: "patch value type []interface {} could not be applied to target field type 'repeated string'", + }, + "add unsupported target: message with non-scalar fields": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/dns_failure_refresh_rate", + map[string]any{ + "base_interval": map[string]any{}, + }, + )}, + }, + ok: false, + errMsg: "unsupported target field type 'google.protobuf.Duration'", + }, + "add unsupported target: map field": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/metadata/filter_metadata", + map[string]any{ + "foo": "bar", + }, + )}, + }, + ok: false, + errMsg: "unsupported target field type 'map'", + }, + "add unsupported target: repeated message": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/filters", + []any{}, // We don't need a value in this slice to test behavior + )}, + }, + ok: false, + errMsg: "unsupported target field type 'repeated envoy.config.cluster.v3.Filter'", + }, + "empty path": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "", + "ignored", + )}, + }, + ok: false, + errMsg: "non-empty, non-root Path is required", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.Cluster", true), + }, + "empty path debug mode": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "", + "ignored", + )}, + debug: true, + }, + ok: false, + errMsg: "non-empty, non-root Path is required", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.Cluster", false), + }, + "root path": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/", + "ignored", + )}, + }, + ok: false, + errMsg: "non-empty, non-root Path is required", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.Cluster", true), + }, + "root path debug mode": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/", + "ignored", + )}, + debug: true, + }, + ok: false, + errMsg: "non-empty, non-root Path is required", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.Cluster", false), + }, + "invalid path: add unknown field": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/outlier_detection/foo", + "ignored", + )}, + }, + ok: false, + errMsg: "no match for field 'foo'!", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.OutlierDetection", true), + }, + "invalid path: remove unknown field": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeRemovePatch( + "/outlier_detection/foo", + )}, + }, + ok: false, + errMsg: "no match for field 'foo'!", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.OutlierDetection", true), + }, + "invalid path: unknown field debug mode": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/outlier_detection/foo", + "ignored", + )}, + debug: true, + }, + ok: false, + errMsg: "no match for field 'foo'!", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.OutlierDetection", false), + }, + "error field list includes first 10 fields when not in debug mode": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "", + "ignored", + )}, + }, + ok: false, + errMsg: "transport_socket_matches\nname\nalt_stat_name\ntype\ncluster_type\neds_cluster_config\nconnect_timeout\nper_connection_buffer_limit_bytes\nlb_policy\nload_assignment", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.Cluster", true), + }, + "error field list includes all fields when in debug mode": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "", + "ignored", + )}, + debug: true, + }, + ok: false, + errMsg: "transport_socket_matches\nname\nalt_stat_name\ntype\ncluster_type\neds_cluster_config\nconnect_timeout\nper_connection_buffer_limit_bytes\nlb_policy\nload_assignment\nhealth_checks\nmax_requests_per_connection\ncircuit_breakers\nupstream_http_protocol_options\ncommon_http_protocol_options\nhttp_protocol_options\nhttp2_protocol_options\ntyped_extension_protocol_options\ndns_refresh_rate\ndns_failure_refresh_rate\nrespect_dns_ttl\ndns_lookup_family\ndns_resolvers\nuse_tcp_for_dns_lookups\ndns_resolution_config\ntyped_dns_resolver_config\nwait_for_warm_on_init\noutlier_detection\ncleanup_interval\nupstream_bind_config\nlb_subset_config\nring_hash_lb_config\nmaglev_lb_config\noriginal_dst_lb_config\nleast_request_lb_config\nround_robin_lb_config\ncommon_lb_config\ntransport_socket\nmetadata\nprotocol_selection\nupstream_connection_options\nclose_connections_on_host_health_failure\nignore_health_on_host_removal\nfilters\nload_balancing_policy\nlrs_server\ntrack_timeout_budgets\nupstream_config\ntrack_cluster_stats\npreconnect_policy\nconnection_pool_per_downstream_connection", + errFunc: expectFieldsListErr("envoy.config.cluster.v3.Cluster", false), + }, + "error field list warns about first 10 fields only when > 10 available when not in debug mode": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/upstream_connection_options/tcp_keepalive/foo", + "ignored", + )}, + debug: false, + }, + ok: false, + errMsg: "keepalive_probes\nkeepalive_time\nkeepalive_interval", + errFunc: expectFieldsListErr("envoy.config.core.v3.TcpKeepalive", false), + }, + "invalid path: empty path element": { + args: args{ + k: &envoy_cluster_v3.Cluster{}, + patches: []Patch{makeAddPatch( + "/outlier_detection//", + "ignored", + )}, + }, + ok: false, + errMsg: "empty field name in path", + }, + "invalid path: repeated field member": { + args: args{ + k: &envoy_listener_v3.Listener{}, + patches: []Patch{makeRemovePatch( + "/filter_chains/0/transport_socket_connect_timeout", + )}, + }, + ok: false, + errMsg: "path contains member of repeated field 'filter_chains'; repeated field member access is not supported", + }, + "invalid path: map field member": { + args: args{ + k: &envoy_cluster_v3.Cluster{ + Metadata: &corev3.Metadata{ + FilterMetadata: map[string]*_struct.Struct{ + "foo": nil, + }, + }, + }, + patches: []Patch{makeRemovePatch( + "/metadata/filter_metadata/foo", + )}, + }, + ok: false, + errMsg: "path contains member of map field 'filter_metadata'; map field member access is not supported", + }, + } + + copyMessage := func(m proto.Message) proto.Message { return m } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + // Copy k so that we can compare before and after + copyOfK := copyMessage(tc.args.k) + var err error + for _, p := range tc.args.patches { + // Repeatedly patch value, replacing with the new version each time + copyOfK, err = PatchStruct(copyOfK, p, tc.args.debug) + if err != nil { + break // Break on the first error + } + } + if tc.ok { + require.NoError(t, err) + if diff := cmp.Diff(tc.expected, copyOfK, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + } else { + require.Error(t, err) + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + } + if tc.errFunc != nil { + tc.errFunc(t, err) + } + } + }) + } +} diff --git a/agent/envoyextensions/registered_extensions.go b/agent/envoyextensions/registered_extensions.go index fb9423837..4d4d26611 100644 --- a/agent/envoyextensions/registered_extensions.go +++ b/agent/envoyextensions/registered_extensions.go @@ -10,6 +10,7 @@ import ( extauthz "github.com/hashicorp/consul/agent/envoyextensions/builtin/ext-authz" "github.com/hashicorp/consul/agent/envoyextensions/builtin/http/localratelimit" "github.com/hashicorp/consul/agent/envoyextensions/builtin/lua" + propertyoverride "github.com/hashicorp/consul/agent/envoyextensions/builtin/property-override" "github.com/hashicorp/consul/agent/envoyextensions/builtin/wasm" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/envoyextensions/extensioncommon" @@ -19,11 +20,12 @@ import ( type extensionConstructor func(api.EnvoyExtension) (extensioncommon.EnvoyExtender, error) var extensionConstructors = map[string]extensionConstructor{ - api.BuiltinLuaExtension: lua.Constructor, - api.BuiltinAWSLambdaExtension: awslambda.Constructor, - api.BuiltinLocalRatelimitExtension: localratelimit.Constructor, - api.BuiltinWasmExtension: wasm.Constructor, - api.BuiltinExtAuthzExtension: extauthz.Constructor, + api.BuiltinLuaExtension: lua.Constructor, + api.BuiltinAWSLambdaExtension: awslambda.Constructor, + api.BuiltinLocalRatelimitExtension: localratelimit.Constructor, + api.BuiltinPropertyOverrideExtension: propertyoverride.Constructor, + api.BuiltinWasmExtension: wasm.Constructor, + api.BuiltinExtAuthzExtension: extauthz.Constructor, } // ConstructExtension attempts to lookup and build an extension from the registry with the diff --git a/agent/xds/delta_envoy_extender_oss_test.go b/agent/xds/delta_envoy_extender_oss_test.go index cf050175f..6cfd66b37 100644 --- a/agent/xds/delta_envoy_extender_oss_test.go +++ b/agent/xds/delta_envoy_extender_oss_test.go @@ -21,6 +21,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/hashicorp/consul/agent/envoyextensions" + propertyoverride "github.com/hashicorp/consul/agent/envoyextensions/builtin/property-override" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/xds/extensionruntime" @@ -87,10 +88,133 @@ end`, } } + // Apply Prop Override extension to the local service and ensure http is used so the extension can be applied. + makePropOverrideNsFunc := func(args map[string]interface{}) func(ns *structs.NodeService) { + return func(ns *structs.NodeService) { + ns.Proxy.Config["protocol"] = "http" + if _, ok := args["ProxyType"]; !ok { + args["ProxyType"] = api.ServiceKindConnectProxy + } + ns.Proxy.EnvoyExtensions = []structs.EnvoyExtension{ + { + Name: api.BuiltinPropertyOverrideExtension, + Arguments: args, + }, + } + } + } + + propertyOverrideServiceDefaultsAddOutlierDetectionSingle := makePropOverrideNsFunc( + map[string]interface{}{ + "Patches": []map[string]interface{}{ + { + "ResourceFilter": map[string]interface{}{ + "ResourceType": propertyoverride.ResourceTypeCluster, + "TrafficDirection": propertyoverride.TrafficDirectionOutbound, + }, + "Op": "add", + "Path": "/outlier_detection/success_rate_minimum_hosts", + "Value": 1234, + }, + }, + }) + + propertyOverrideServiceDefaultsAddOutlierDetectionMultiple := makePropOverrideNsFunc( + map[string]interface{}{ + "Patches": []map[string]interface{}{ + { + "ResourceFilter": map[string]interface{}{ + "ResourceType": propertyoverride.ResourceTypeCluster, + "TrafficDirection": propertyoverride.TrafficDirectionOutbound, + }, + "Op": "add", + "Path": "/outlier_detection", + "Value": map[string]interface{}{ + "success_rate_minimum_hosts": 1234, + "failure_percentage_request_volume": 2345, + }, + }, + }, + }) + + propertyOverrideServiceDefaultsRemoveOutlierDetection := makePropOverrideNsFunc( + map[string]interface{}{ + "Patches": []map[string]interface{}{ + { + "ResourceFilter": map[string]interface{}{ + "ResourceType": propertyoverride.ResourceTypeCluster, + "TrafficDirection": propertyoverride.TrafficDirectionOutbound, + }, + "Op": "remove", + "Path": "/outlier_detection", + }, + }, + }) + + propertyOverrideServiceDefaultsAddKeepalive := makePropOverrideNsFunc( + map[string]interface{}{ + "Patches": []map[string]interface{}{ + { + "ResourceFilter": map[string]interface{}{ + "ResourceType": propertyoverride.ResourceTypeCluster, + "TrafficDirection": propertyoverride.TrafficDirectionOutbound, + }, + "Op": "add", + "Path": "/upstream_connection_options/tcp_keepalive/keepalive_probes", + "Value": 5, + }, + }, + }) + + propertyOverrideServiceDefaultsAddRoundRobinLbConfig := makePropOverrideNsFunc( + map[string]interface{}{ + "Patches": []map[string]interface{}{ + { + "ResourceFilter": map[string]interface{}{ + "ResourceType": propertyoverride.ResourceTypeCluster, + "TrafficDirection": propertyoverride.TrafficDirectionOutbound, + }, + "Op": "add", + "Path": "/round_robin_lb_config", + "Value": map[string]interface{}{}, + }, + }, + }) + tests := []struct { name string create func(t testinf.T) *proxycfg.ConfigSnapshot }{ + { + name: "propertyoverride-add-outlier-detection", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsAddOutlierDetectionSingle, nil) + }, + }, + { + name: "propertyoverride-add-outlier-detection-multiple", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsAddOutlierDetectionMultiple, nil) + }, + }, + { + name: "propertyoverride-add-keepalive", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsAddKeepalive, nil) + }, + }, + { + name: "propertyoverride-add-round-robin-lb-config", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsAddRoundRobinLbConfig, nil) + }, + }, + { + name: "propertyoverride-remove-outlier-detection", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsRemoveOutlierDetection, nil) + }, + }, { name: "lambda-connect-proxy", create: func(t testinf.T) *proxycfg.ConfigSnapshot { diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-keepalive.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-keepalive.latest.golden new file mode 100644 index 000000000..e026cc779 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-keepalive.latest.golden @@ -0,0 +1,137 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + }, + "upstreamConnectionOptions": { + "tcpKeepalive": { + "keepaliveProbes": 5 + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + }, + "upstreamConnectionOptions": { + "tcpKeepalive": { + "keepaliveProbes": 5 + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection-multiple.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection-multiple.latest.golden new file mode 100644 index 000000000..9f4ad3c4e --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection-multiple.latest.golden @@ -0,0 +1,133 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": { + "successRateMinimumHosts": 1234, + "failurePercentageRequestVolume": 2345 + }, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": { + "successRateMinimumHosts": 1234, + "failurePercentageRequestVolume": 2345 + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection.latest.golden new file mode 100644 index 000000000..c93afcc46 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-outlier-detection.latest.golden @@ -0,0 +1,131 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": { + "successRateMinimumHosts": 1234 + }, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": { + "successRateMinimumHosts": 1234 + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-round-robin-lb-config.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-round-robin-lb-config.latest.golden new file mode 100644 index 000000000..b63a9206f --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-add-round-robin-lb-config.latest.golden @@ -0,0 +1,129 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "roundRobinLbConfig": {}, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "roundRobinLbConfig": {}, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden new file mode 100644 index 000000000..c010ac87b --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden @@ -0,0 +1,127 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden new file mode 100644 index 000000000..c010ac87b --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden @@ -0,0 +1,127 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "outlierDetection": {}, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/clusters/propertyoverride-remove-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-remove-outlier-detection.latest.golden new file mode 100644 index 000000000..3e04345bb --- /dev/null +++ b/agent/xds/testdata/builtin_extension/clusters/propertyoverride-remove-outlier-detection.latest.golden @@ -0,0 +1,125 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "commonLbConfig": { + "healthyPanicThreshold": {} + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db" + } + ] + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": {}, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": {}, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + }, + "matchSubjectAltNames": [ + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target" + }, + { + "exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target" + } + ] + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-keepalive.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-keepalive.latest.golden new file mode 100644 index 000000000..6e4d37bc3 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-keepalive.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection-multiple.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection-multiple.latest.golden new file mode 100644 index 000000000..6e4d37bc3 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection-multiple.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection.latest.golden new file mode 100644 index 000000000..6e4d37bc3 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-outlier-detection.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-round-robin-lb-config.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-round-robin-lb-config.latest.golden new file mode 100644 index 000000000..6e4d37bc3 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-add-round-robin-lb-config.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden new file mode 100644 index 000000000..5cb4171b9 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden new file mode 100644 index 000000000..5cb4171b9 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-remove-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-remove-outlier-detection.latest.golden new file mode 100644 index 000000000..6e4d37bc3 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/endpoints/propertyoverride-remove-outlier-detection.latest.golden @@ -0,0 +1,75 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.10.1.1", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + }, + { + "endpoint": { + "address": { + "socketAddress": { + "address": "10.20.1.2", + "portValue": 8080 + } + } + }, + "healthStatus": "HEALTHY", + "loadBalancingWeight": 1 + } + ] + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-keepalive.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-keepalive.latest.golden new file mode 100644 index 000000000..7a4514f1d --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-keepalive.latest.golden @@ -0,0 +1,234 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection-multiple.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection-multiple.latest.golden new file mode 100644 index 000000000..7a4514f1d --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection-multiple.latest.golden @@ -0,0 +1,234 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection.latest.golden new file mode 100644 index 000000000..7a4514f1d --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-outlier-detection.latest.golden @@ -0,0 +1,234 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-round-robin-lb-config.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-round-robin-lb-config.latest.golden new file mode 100644 index 000000000..7a4514f1d --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-add-round-robin-lb-config.latest.golden @@ -0,0 +1,234 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden new file mode 100644 index 000000000..4e727c476 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden @@ -0,0 +1,237 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "statPrefix": "custom.stats.outbound.only", + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "statPrefix": "custom.stats.outbound.only", + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "statPrefix": "custom.stats.inbound.only", + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden new file mode 100644 index 000000000..4e727c476 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden @@ -0,0 +1,237 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "statPrefix": "custom.stats.outbound.only", + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "statPrefix": "custom.stats.outbound.only", + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "statPrefix": "custom.stats.inbound.only", + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/listeners/propertyoverride-remove-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-remove-outlier-detection.latest.golden new file mode 100644 index 000000000..7a4514f1d --- /dev/null +++ b/agent/xds/testdata/builtin_extension/listeners/propertyoverride-remove-outlier-detection.latest.golden @@ -0,0 +1,234 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.http_connection_manager", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager", + "statPrefix": "public_listener", + "routeConfig": { + "name": "public_listener", + "virtualHosts": [ + { + "name": "public_listener", + "domains": [ + "*" + ], + "routes": [ + { + "match": { + "prefix": "/" + }, + "route": { + "cluster": "local_app" + } + } + ] + } + ] + }, + "httpFilters": [ + { + "name": "envoy.filters.http.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC", + "rules": {} + } + }, + { + "name": "envoy.filters.http.header_to_metadata", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config", + "requestRules": [ + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "trust-domain", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\1" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "partition", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\2" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "namespace", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\3" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "datacenter", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\4" + } + } + }, + { + "header": "x-forwarded-client-cert", + "onHeaderPresent": { + "metadataNamespace": "consul", + "key": "service", + "regexValueRewrite": { + "pattern": { + "googleRe2": {}, + "regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*" + }, + "substitution": "\\5" + } + } + } + ] + } + }, + { + "name": "envoy.filters.http.router", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" + } + } + ], + "tracing": { + "randomSampling": {} + }, + "forwardClientCertDetails": "APPEND_FORWARD", + "setCurrentClientCertDetails": { + "subject": true, + "cert": true, + "chain": true, + "dns": true, + "uri": true + } + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": {}, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + "alpnProtocols": [ + "http/1.1" + ] + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-keepalive.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-keepalive.latest.golden new file mode 100644 index 000000000..9c050cbe6 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-keepalive.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection-multiple.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection-multiple.latest.golden new file mode 100644 index 000000000..9c050cbe6 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection-multiple.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection.latest.golden new file mode 100644 index 000000000..9c050cbe6 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-outlier-detection.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-round-robin-lb-config.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-round-robin-lb-config.latest.golden new file mode 100644 index 000000000..9c050cbe6 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-add-round-robin-lb-config.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden new file mode 100644 index 000000000..306f5220e --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-inbound-doesnt-apply-to-outbound.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden new file mode 100644 index 000000000..306f5220e --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-outbound-doesnt-apply-to-inbound.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/builtin_extension/routes/propertyoverride-remove-outlier-detection.latest.golden b/agent/xds/testdata/builtin_extension/routes/propertyoverride-remove-outlier-detection.latest.golden new file mode 100644 index 000000000..9c050cbe6 --- /dev/null +++ b/agent/xds/testdata/builtin_extension/routes/propertyoverride-remove-outlier-detection.latest.golden @@ -0,0 +1,5 @@ +{ + "versionInfo": "00000001", + "typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "nonce": "00000001" +} \ No newline at end of file diff --git a/api/config_entry.go b/api/config_entry.go index 381e9bd0a..b4cc966d1 100644 --- a/api/config_entry.go +++ b/api/config_entry.go @@ -39,11 +39,12 @@ const ( ) const ( - BuiltinAWSLambdaExtension string = "builtin/aws/lambda" - BuiltinExtAuthzExtension string = "builtin/ext-authz" - BuiltinLuaExtension string = "builtin/lua" - BuiltinLocalRatelimitExtension string = "builtin/http/localratelimit" - BuiltinWasmExtension string = "builtin/wasm" + BuiltinAWSLambdaExtension string = "builtin/aws/lambda" + BuiltinExtAuthzExtension string = "builtin/ext-authz" + BuiltinLuaExtension string = "builtin/lua" + BuiltinLocalRatelimitExtension string = "builtin/http/localratelimit" + BuiltinPropertyOverrideExtension string = "builtin/property-override" + BuiltinWasmExtension string = "builtin/wasm" // BuiltinValidateExtension should not be exposed directly or accepted as a valid configured // extension type, as it is only used indirectly via troubleshooting tools. It is included here // for common reference alongside other builtin extensions.