Files
velero/pkg/exposer/vgdp_counter_test.go
T
2026-09-15 17:38:30 +08:00

209 lines
5.7 KiB
Go

package exposer
import (
"sync"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/vmware-tanzu/velero/pkg/builder"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
)
func TestIsConstrained(t *testing.T) {
tests := []struct {
name string
counter *VgdpCounter
kubeClientObj []client.Object
getErr bool
expected bool
}{
{
name: "no change, constrained",
counter: &VgdpCounter{},
expected: true,
},
{
name: "no change, not constrained",
counter: &VgdpCounter{allowedQueueLength: 1},
},
{
name: "change in du, get failed",
counter: &VgdpCounter{
allowedQueueLength: 1,
duState: dynamicQueueLength{0, 1},
},
getErr: true,
},
{
name: "change in du, constrained",
counter: &VgdpCounter{
allowedQueueLength: 1,
duState: dynamicQueueLength{0, 1},
},
kubeClientObj: []client.Object{
builder.ForDataUpload("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
},
expected: true,
},
{
name: "change in dd, get failed",
counter: &VgdpCounter{
allowedQueueLength: 1,
ddState: dynamicQueueLength{0, 1},
},
getErr: true,
},
{
name: "change in dd, constrained",
counter: &VgdpCounter{
allowedQueueLength: 1,
ddState: dynamicQueueLength{0, 1},
},
kubeClientObj: []client.Object{
builder.ForDataDownload("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
},
expected: true,
},
{
name: "change in pvb, get failed",
counter: &VgdpCounter{
allowedQueueLength: 1,
pvbState: dynamicQueueLength{0, 1},
},
getErr: true,
},
{
name: "change in pvb, constrained",
counter: &VgdpCounter{
allowedQueueLength: 1,
pvbState: dynamicQueueLength{0, 1},
},
kubeClientObj: []client.Object{
builder.ForPodVolumeBackup("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
},
expected: true,
},
{
name: "change in pvr, get failed",
counter: &VgdpCounter{
allowedQueueLength: 1,
pvrState: dynamicQueueLength{0, 1},
},
getErr: true,
},
{
name: "change in pvr, constrained",
counter: &VgdpCounter{
allowedQueueLength: 1,
pvrState: dynamicQueueLength{0, 1},
},
kubeClientObj: []client.Object{
builder.ForPodVolumeRestore("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
},
expected: true,
},
{
name: "change in du, pvb, not constrained",
counter: &VgdpCounter{
allowedQueueLength: 3,
duState: dynamicQueueLength{0, 1},
pvbState: dynamicQueueLength{0, 1},
},
kubeClientObj: []client.Object{
builder.ForDataUpload("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
builder.ForPodVolumeBackup("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
},
},
{
name: "change in dd, pvr, constrained",
counter: &VgdpCounter{
allowedQueueLength: 1,
ddState: dynamicQueueLength{0, 1},
pvrState: dynamicQueueLength{0, 1},
},
kubeClientObj: []client.Object{
builder.ForDataDownload("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
builder.ForPodVolumeRestore("velero", "test-1").Labels(map[string]string{ExposeOnGoingLabel: "true"}).Result(),
},
expected: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
scheme := runtime.NewScheme()
if !test.getErr {
err := velerov1api.AddToScheme(scheme)
require.NoError(t, err)
err = velerov2alpha1api.AddToScheme(scheme)
require.NoError(t, err)
}
test.counter.client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(test.kubeClientObj...).Build()
result := test.counter.IsConstrained(t.Context(), velerotest.NewLogger())
assert.Equal(t, test.expected, result)
if !test.getErr {
assert.Equal(t, test.counter.duState.changeID, test.counter.duCacheState.changeID)
assert.Equal(t, test.counter.ddState.changeID, test.counter.ddCacheState.changeID)
assert.Equal(t, test.counter.pvbState.changeID, test.counter.pvbCacheState.changeID)
assert.Equal(t, test.counter.pvrState.changeID, test.counter.pvrCacheState.changeID)
} else {
or := test.counter.duState.changeID != test.counter.duCacheState.changeID
if !or {
or = test.counter.ddState.changeID != test.counter.ddCacheState.changeID
}
if !or {
or = test.counter.pvbState.changeID != test.counter.pvbCacheState.changeID
}
if !or {
or = test.counter.pvrState.changeID != test.counter.pvrCacheState.changeID
}
assert.True(t, or)
}
})
}
}
func TestVgdpCounterConcurrentIsConstrained(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, velerov1api.AddToScheme(scheme))
require.NoError(t, velerov2alpha1api.AddToScheme(scheme))
counter := &VgdpCounter{
allowedQueueLength: 10,
client: fake.NewClientBuilder().WithScheme(scheme).Build(),
}
var wg sync.WaitGroup
for i := 0; i < 4; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 200; j++ {
atomic.AddUint64(&counter.duState.changeID, 1)
atomic.AddUint64(&counter.ddState.changeID, 1)
counter.IsConstrained(t.Context(), velerotest.NewLogger())
}
}()
}
wg.Wait()
}