Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
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"
"github.com/hashicorp/consul/lib/decode"
"github.com/hashicorp/go-multierror"
"github.com/mitchellh/mapstructure"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -73,7 +74,7 @@ func matchesResourceFilter[K proto.Message](rf ResourceFilter, resourceType Reso
}

type ServiceName struct {
api.CompoundServiceName
api.CompoundServiceName `mapstructure:",squash"`
Copy link
Member Author

Choose a reason for hiding this comment

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

}

// ResourceType is the type of Envoy resource being patched.
Expand Down Expand Up @@ -275,7 +276,21 @@ func Constructor(ext api.EnvoyExtension) (extensioncommon.EnvoyExtender, error)
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 {
// This avoids issues with decoding nested slices, which are error-prone
// due to slice<->map coercion by mapstructure. See HookWeakDecodeFromSlice
// and WeaklyTypedInput docs for more details.
d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.ComposeDecodeHookFunc(
decode.HookWeakDecodeFromSlice,
decode.HookTranslateKeys,
),
WeaklyTypedInput: true,
Result: &p,
})
Comment on lines 279 to 289
Copy link
Member Author

@zalimeni zalimeni Jun 6, 2023

Choose a reason for hiding this comment

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

I'd be happy to have someone highly familiar w/ this code review it, since I'm halfway between cargo culting and having a full understanding of these decoder patterns. That said, I'm pretty confident that we need both the hook and WeaklyTypedInput for this to behave, as that combo is what made the integration test pass and docs indicate WeaklyTypedInput is needed when the hook is used in prior decoding steps, which it is.

The difference between this and the prior version is the inclusion of the HookWeakDecodeFromSlice hook, which appears necessary to handle the nested []*ServiceName parsing.

Hand-testing indicates normal patching continues to work as expected.

if err != nil {
return nil, fmt.Errorf("error configuring decoder: %v", err)
}
if err := d.Decode(ext.Arguments); err != nil {
return nil, fmt.Errorf("error decoding extension arguments: %v", err)
}
if err := p.validate(); err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ func TestConstructor(t *testing.T) {
arguments: makeArguments(map[string]any{"Patches": []map[string]any{
makePatch(map[string]any{
"ResourceFilter": makeResourceFilter(map[string]any{
"Services": []ServiceName{
{CompoundServiceName: api.CompoundServiceName{}},
"Services": []map[string]any{
{},
},
}),
}),
Expand All @@ -240,6 +240,42 @@ func TestConstructor(t *testing.T) {
expected: validTestCase(OpAdd, extensioncommon.TrafficDirectionOutbound, ResourceTypeRoute).expected,
ok: true,
},
// Ensure that embedded api struct used for Services is parsed correctly.
// See also above comment on decode.HookWeakDecodeFromSlice.
"single value Services decoded as map construction succeeds": {
arguments: makeArguments(map[string]any{"Patches": []map[string]any{
makePatch(map[string]any{
"ResourceFilter": makeResourceFilter(map[string]any{
"Services": []map[string]any{
{"Name": "foo"},
},
}),
}),
}}),
expected: propertyOverride{
Patches: []Patch{
{
ResourceFilter: ResourceFilter{
ResourceType: ResourceTypeRoute,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Services: []*ServiceName{
{CompoundServiceName: api.CompoundServiceName{
Name: "foo",
Namespace: "default",
Partition: "default",
}},
},
},
Op: OpAdd,
Path: "/name",
Value: "foo",
},
},
Debug: true,
ProxyType: api.ServiceKindConnectProxy,
},
ok: true,
},
"invalid ProxyType": {
arguments: makeArguments(map[string]any{
"Patches": []map[string]any{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@

snapshot_envoy_admin localhost:19000 s1 primary || true
snapshot_envoy_admin localhost:19001 s2 primary || true
snapshot_envoy_admin localhost:19002 s3 primary || true
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ services {
{
destination_name = "s2"
local_bind_port = 5000
},
{
destination_name = "s3"
local_bind_port = 5001
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

services {
name = "s3"
port = 8181
connect { sidecar_service {} }
}
23 changes: 21 additions & 2 deletions test/integration/connect/envoy/case-property-override/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ EnvoyExtensions = [
]
'

upsert_config_entry primary '
Kind = "service-defaults"
Name = "s3"
Protocol = "http"
'

upsert_config_entry primary '
Kind = "service-defaults"
Name = "s1"
Expand All @@ -37,15 +43,28 @@ EnvoyExtensions = [
Name = "builtin/property-override"
Arguments = {
ProxyType = "connect-proxy"
Patches = [{
Patches = [
{
ResourceFilter = {
ResourceType = "cluster"
TrafficDirection = "outbound"
}
Op = "add"
Path = "/upstream_connection_options/tcp_keepalive/keepalive_probes"
Value = 1234
}]
},
{
ResourceFilter = {
ResourceType = "cluster"
TrafficDirection = "outbound"
Services = [{
Name = "s2"
}]
}
Op = "remove"
Path = "/outlier_detection"
}
]
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# SPDX-License-Identifier: MPL-2.0


export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy s3 s3-sidecar-proxy"
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ load helpers
[ "$status" == 0 ]

[ "$(echo "$output" | jq -r '.upstream_connection_options.tcp_keepalive.keepalive_probes')" == "1234" ]
[ "$(echo "$output" | jq -r '.outlier_detection')" == "null" ]

run get_envoy_cluster_config localhost:19000 s3
[ "$status" == 0 ]

[ "$(echo "$output" | jq -r '.upstream_connection_options.tcp_keepalive.keepalive_probes')" == "1234" ]
[ "$(echo "$output" | jq -r '.outlier_detection')" == "{}" ]
}

@test "s2 proxy is configured with the expected envoy patches" {
Expand Down