Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

126 lines
3.3 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
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)
}
}
}
})
}
}