Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ BUF_VERSION='v1.4.0'
PROTOC_GEN_GO_GRPC_VERSION="v1.2.0"
MOG_VERSION='v0.3.0'
PROTOC_GO_INJECT_TAG_VERSION='v1.3.0'
DEEP_COPY_VERSION='bc3f5aa5735d8a54961580a3a24422c308c831c2'

MOCKED_PB_DIRS= pbdns

Expand Down Expand Up @@ -321,6 +322,10 @@ lint-tools:
proto-tools:
@$(SHELL) $(CURDIR)/build-support/scripts/devtools.sh -protobuf

.PHONY: codegen-tools
Comment thread
rboyer marked this conversation as resolved.
codegen-tools:
@$(SHELL) $(CURDIR)/build-support/scripts/devtools.sh -codegen

version:
@echo -n "Version: "
@$(SHELL) $(CURDIR)/build-support/scripts/version.sh
Expand Down
13 changes: 13 additions & 0 deletions agent/proxycfg/deep-copy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh

# Uses: https://github.com/globusdigital/deep-copy
deep-copy -pointer-receiver \
Comment thread
boxofrad marked this conversation as resolved.
-o ./proxycfg.deepcopy.go \
-type ConfigSnapshot \
-type ConfigSnapshotUpstreams \
-type configSnapshotConnectProxy \
-type configSnapshotIngressGateway \
-type configSnapshotMeshGateway \
-type configSnapshotTerminatingGateway \
-type PeerServersValue \
./
27 changes: 25 additions & 2 deletions agent/proxycfg/internal/watch/watchmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ package watch

import "context"

// DeepCopyable describes a type that implements the DeepCopy
// method to get a copy of itself that is safe to pass around
// without worrying about receivers modifying the original.
type DeepCopyable[T any] interface {
DeepCopy() T
}

// Map safely stores and retrieves values by validating that
// there is a live watch for a key. InitWatch must be called
// to associate a key with its cancel function before any
// Set's are called.
type Map[K comparable, V any] struct {
type Map[K comparable, V DeepCopyable[V]] struct {
M map[K]watchedVal[V]
}

Expand All @@ -20,10 +27,26 @@ type watchedVal[V any] struct {
cancel context.CancelFunc
}

func NewMap[K comparable, V any]() Map[K, V] {
func NewMap[K comparable, V DeepCopyable[V]]() Map[K, V] {
return Map[K, V]{M: make(map[K]watchedVal[V])}
}

// DeepCopy returns a copy of the Map that is safe to be passed
// around without worrying about receivers modifying the original
// or canceling its watches.
func (m Map[K, V]) DeepCopy() Map[K, V] {
dup := make(map[K]watchedVal[V], len(m.M))
for k, v := range m.M {
var val *V
if v.Val != nil {
dc := (*v.Val).DeepCopy()
val = &dc
}
dup[k] = watchedVal[V]{Val: val}
}
return Map[K, V]{M: dup}
}

// InitWatch associates a cancel function with a key,
// allowing Set to be called for the key. The cancel
// function is allowed to be nil.
Expand Down
56 changes: 36 additions & 20 deletions agent/proxycfg/internal/watch/watchmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func TestMap(t *testing.T) {
m := NewMap[string, string]()
m := NewMap[string, testVal]()

// Set without init is a no-op
{
Expand Down Expand Up @@ -51,7 +51,7 @@ func TestMap(t *testing.T) {
{
got, ok := m.Get("hello")
require.True(t, ok)
require.Equal(t, "world", got)
require.Equal(t, "world", string(got))
}

// CancelWatch successful
Expand All @@ -76,15 +76,11 @@ func TestMap(t *testing.T) {
}

func TestMap_ForEach(t *testing.T) {
type testType struct {
s string
}

m := NewMap[string, any]()
inputs := map[string]any{
"hello": 13,
"foo": struct{}{},
"bar": &testType{s: "wow"},
m := NewMap[string, testVal]()
inputs := map[string]testVal{
"hello": "world",
"foo": "bar",
"baz": "bat",
}
for k, v := range inputs {
m.InitWatch(k, nil)
Expand Down Expand Up @@ -114,15 +110,11 @@ func TestMap_ForEach(t *testing.T) {
}

func TestMap_ForEachE(t *testing.T) {
type testType struct {
s string
}

m := NewMap[string, any]()
inputs := map[string]any{
"hello": 13,
"foo": struct{}{},
"bar": &testType{s: "wow"},
m := NewMap[string, testVal]()
inputs := map[string]testVal{
"hello": "world",
"foo": "bar",
"baz": "bat",
}
for k, v := range inputs {
m.InitWatch(k, nil)
Expand Down Expand Up @@ -152,3 +144,27 @@ func TestMap_ForEachE(t *testing.T) {
require.Errorf(t, err, "boo")
}
}

func TestMap_DeepCopy(t *testing.T) {
orig := NewMap[string, testVal]()
inputs := map[string]testVal{
"hello": "world",
"foo": "bar",
"baz": "bat",
}
for k, v := range inputs {
orig.InitWatch(k, nil)
orig.Set(k, v)
}
require.Equal(t, 3, orig.Len())

clone := orig.DeepCopy()
require.Equal(t, 3, clone.Len())

orig.CancelWatch("hello")
require.NotEqual(t, orig.Len(), clone.Len())
}

type testVal string

func (tv testVal) DeepCopy() testVal { return tv }
4 changes: 1 addition & 3 deletions agent/proxycfg/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,7 @@ func TestManager_BasicLifecycle(t *testing.T) {
dataSources.ConfigEntry.Set(meshConfigReq, &structs.ConfigEntryResponse{Entry: nil})
tt.setup(t, dataSources)

expectSnapCopy, err := tt.expectSnap.Clone()
require.NoError(t, err)

expectSnapCopy := tt.expectSnap.Clone()
webProxyCopy, err := copystructure.Copy(webProxy)
require.NoError(t, err)

Expand Down
Loading