2023-08-11 13:12:13 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
2023-07-31 19:24:33 +00:00
|
|
|
package xdscommon
|
|
|
|
|
|
|
|
import (
|
|
|
|
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"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
|
|
duration "google.golang.org/protobuf/types/known/durationpb"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestCloneIndexedResources(t *testing.T) {
|
|
|
|
exampleResources := map[string][]proto.Message{
|
|
|
|
ListenerType: {
|
|
|
|
&envoy_listener_v3.Listener{
|
|
|
|
Name: "listener1",
|
|
|
|
IgnoreGlobalConnLimit: true,
|
|
|
|
ListenerFiltersTimeout: &duration.Duration{
|
|
|
|
Seconds: 123,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&envoy_listener_v3.Listener{
|
|
|
|
Name: "listener2",
|
|
|
|
StatPrefix: "stats.foo",
|
|
|
|
ListenerFiltersTimeout: &duration.Duration{
|
|
|
|
Seconds: 456,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ClusterType: {
|
|
|
|
&envoy_cluster_v3.Cluster{
|
|
|
|
Name: "cluster1",
|
|
|
|
RespectDnsTtl: true,
|
|
|
|
TransportSocketMatches: []*envoy_cluster_v3.Cluster_TransportSocketMatch{
|
|
|
|
{
|
|
|
|
Name: "match1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
getPointerField := func(msg proto.Message) interface{} {
|
|
|
|
switch typedMsg := msg.(type) {
|
|
|
|
case *envoy_cluster_v3.Cluster:
|
|
|
|
return typedMsg.TransportSocketMatches[0]
|
|
|
|
case *envoy_listener_v3.Listener:
|
|
|
|
return typedMsg.ListenerFiltersTimeout
|
|
|
|
default:
|
|
|
|
panic("should not happen")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
updatePointerField := func(msg proto.Message) {
|
|
|
|
switch typedMsg := msg.(type) {
|
|
|
|
case *envoy_cluster_v3.Cluster:
|
|
|
|
typedMsg.TransportSocketMatches[0] = &envoy_cluster_v3.Cluster_TransportSocketMatch{
|
|
|
|
Name: "match1-updated",
|
|
|
|
}
|
|
|
|
case *envoy_listener_v3.Listener:
|
|
|
|
typedMsg.ListenerFiltersTimeout = &duration.Duration{
|
|
|
|
Seconds: 999,
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic("should not happen")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
input *IndexedResources
|
|
|
|
hasResources bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "simple compare",
|
|
|
|
input: IndexResources(testutil.Logger(t), exampleResources),
|
|
|
|
hasResources: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "empty input returns empty",
|
|
|
|
input: EmptyIndexedResources(),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "nil input returns nil",
|
|
|
|
input: nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
clone := Clone(tc.input)
|
|
|
|
|
|
|
|
if tc.input == nil {
|
|
|
|
require.Nil(t, clone)
|
|
|
|
} else {
|
|
|
|
if diff := cmp.Diff(tc.input, clone, protocmp.Transform()); diff != "" {
|
|
|
|
t.Errorf("unexpected difference:\n%v", diff)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NotSame(t, tc.input, clone)
|
|
|
|
require.NotSame(t, tc.input.Index, clone.Index)
|
|
|
|
require.NotSame(t, tc.input.ChildIndex, clone.ChildIndex)
|
|
|
|
|
|
|
|
// Ensure deep clone of protos
|
|
|
|
for typeURL, typeMap := range tc.input.Index {
|
|
|
|
for name, msg := range typeMap {
|
|
|
|
require.NotSame(t, msg, clone.Index[typeURL][name])
|
|
|
|
require.NotSame(t, getPointerField(msg), getPointerField(clone.Index[typeURL][name]))
|
|
|
|
updatePointerField(msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only check post-update difference if there are resources to differ
|
|
|
|
if tc.hasResources {
|
|
|
|
if diff := cmp.Diff(tc.input, clone, protocmp.Transform()); diff == "" {
|
|
|
|
t.Errorf("updated original and clone should be different:\n%v", diff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|