Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
5 changes: 3 additions & 2 deletions cluster/kube/operators/clients/inventory/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type client struct {

type inventory struct {
inventoryV1.Cluster
ctx context.Context
}

type inventoryState struct {
Expand Down Expand Up @@ -219,13 +220,13 @@ func (cl *client) subscriber(in <-chan inventoryV1.Cluster, out chan<- ctypes.In
case inv := <-in:
pending = append(pending, inv)
if och == nil {
msg = newInventory(pending[0])
msg = newInventory(cl.ctx, pending[0])
och = out
}
case och <- msg:
pending = pending[1:]
if len(pending) > 0 {
msg = newInventory(pending[0])
msg = newInventory(cl.ctx, pending[0])
} else {
och = nil
msg = nil
Expand Down
221 changes: 221 additions & 0 deletions cluster/kube/operators/clients/inventory/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"testing"
"time"

"github.com/go-logr/logr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -211,6 +212,7 @@
kc := kfake.NewClientset()
ctx = context.WithValue(ctx, fromctx.CtxKeyKubeClientSet, kubernetes.Interface(kc))
ctx = context.WithValue(ctx, fromctx.CtxKeyInventoryUnderTest, true)
ctx = logr.NewContext(ctx, logr.Discard())

gSrv := setupInventoryGRPC(ctx, group, ports[0])

Expand Down Expand Up @@ -315,6 +317,225 @@
require.Len(t, inv.Metrics().Nodes, 0)
}

func TestInventoryMetrics_bad_gpu_values_no_overflow(t *testing.T) {
scaffold := makeInventoryScaffold(t)
cl, err := NewClient(scaffold.ctx)
require.NoError(t, err)
require.NotNil(t, cl)

scaffold.gInv.invch <- inventoryV1.Cluster{
Nodes: inventoryV1.Nodes{
inventoryV1.Node{
Name: "bad-gpu-node",
Resources: inventoryV1.NodeResources{
CPU: inventoryV1.CPU{
Quantity: inventoryV1.NewResourcePairMilli(1000, 1000, 0, resource.DecimalSI),
},
Memory: inventoryV1.Memory{
Quantity: inventoryV1.NewResourcePair(1024, 1024, 0, resource.DecimalSI),
},
GPU: inventoryV1.GPU{
Quantity: inventoryV1.NewResourcePair(0, -1, 0, resource.DecimalSI),
},
EphemeralStorage: inventoryV1.NewResourcePair(100, 100, 0, resource.DecimalSI),
VolumesAttached: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
VolumesMounted: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
Capabilities: inventoryV1.NodeCapabilities{},
},
},
}

inv := waitForInventory(t, cl.ResultChan())
require.NotNil(t, inv)

metrics := inv.Metrics()
require.Len(t, metrics.Nodes, 1)
assert.Equal(t, uint64(0), metrics.Nodes[0].Available.GPU, "negative allocatable must yield 0, not uint64 overflow")
assert.Equal(t, uint64(0), metrics.TotalAvailable.GPU)
}

func TestInventoryMetrics_negative_quantities_clamped_no_overflow(t *testing.T) {
tests := []struct {
name string

Check failure on line 360 in cluster/kube/operators/clients/inventory/client_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofmt)
cluster inventoryV1.Cluster
check func(t *testing.T, m inventoryV1.Metrics)
}{
{
name: "negative_allocatable_cpu",
cluster: inventoryV1.Cluster{
Nodes: inventoryV1.Nodes{
inventoryV1.Node{
Name: "bad-cpu-node",
Resources: inventoryV1.NodeResources{
CPU: inventoryV1.CPU{
Quantity: inventoryV1.NewResourcePairMilli(1000, -1, 0, resource.DecimalSI),
},
Memory: inventoryV1.Memory{
Quantity: inventoryV1.NewResourcePair(1024, 1024, 0, resource.DecimalSI),
},
GPU: inventoryV1.GPU{
Quantity: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
EphemeralStorage: inventoryV1.NewResourcePair(100, 100, 0, resource.DecimalSI),
VolumesAttached: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
VolumesMounted: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
Capabilities: inventoryV1.NodeCapabilities{},
},
},
},
check: func(t *testing.T, m inventoryV1.Metrics) {
require.Len(t, m.Nodes, 1)
assert.Equal(t, uint64(0), m.Nodes[0].Allocatable.CPU)
assert.Equal(t, uint64(0), m.TotalAllocatable.CPU)
},
},
{
name: "negative_allocatable_memory",
cluster: inventoryV1.Cluster{
Nodes: inventoryV1.Nodes{
inventoryV1.Node{
Name: "bad-memory-node",
Resources: inventoryV1.NodeResources{
CPU: inventoryV1.CPU{
Quantity: inventoryV1.NewResourcePairMilli(1000, 1000, 0, resource.DecimalSI),
},
Memory: inventoryV1.Memory{
Quantity: inventoryV1.NewResourcePair(1024, -1, 0, resource.DecimalSI),
},
GPU: inventoryV1.GPU{
Quantity: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
EphemeralStorage: inventoryV1.NewResourcePair(100, 100, 0, resource.DecimalSI),
VolumesAttached: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
VolumesMounted: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
Capabilities: inventoryV1.NodeCapabilities{},
},
},
},
check: func(t *testing.T, m inventoryV1.Metrics) {
require.Len(t, m.Nodes, 1)
assert.Equal(t, uint64(0), m.Nodes[0].Allocatable.Memory)
assert.Equal(t, uint64(0), m.TotalAllocatable.Memory)
},
},
{
name: "negative_allocatable_storage_ephemeral",
cluster: inventoryV1.Cluster{
Nodes: inventoryV1.Nodes{
inventoryV1.Node{
Name: "bad-storage-node",
Resources: inventoryV1.NodeResources{
CPU: inventoryV1.CPU{
Quantity: inventoryV1.NewResourcePairMilli(1000, 1000, 0, resource.DecimalSI),
},
Memory: inventoryV1.Memory{
Quantity: inventoryV1.NewResourcePair(1024, 1024, 0, resource.DecimalSI),
},
GPU: inventoryV1.GPU{
Quantity: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
EphemeralStorage: inventoryV1.NewResourcePair(100, -1, 0, resource.DecimalSI),
VolumesAttached: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
VolumesMounted: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
Capabilities: inventoryV1.NodeCapabilities{},
},
},
},
check: func(t *testing.T, m inventoryV1.Metrics) {
require.Len(t, m.Nodes, 1)
assert.Equal(t, uint64(0), m.Nodes[0].Allocatable.StorageEphemeral)
assert.Equal(t, uint64(0), m.TotalAllocatable.StorageEphemeral)
},
},
{
name: "negative_allocatable_storage_class",
cluster: inventoryV1.Cluster{
Nodes: inventoryV1.Nodes{
inventoryV1.Node{
Name: "node",
Resources: inventoryV1.NodeResources{
CPU: inventoryV1.CPU{
Quantity: inventoryV1.NewResourcePairMilli(1000, 1000, 0, resource.DecimalSI),
},
Memory: inventoryV1.Memory{
Quantity: inventoryV1.NewResourcePair(1024, 1024, 0, resource.DecimalSI),
},
GPU: inventoryV1.GPU{
Quantity: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
EphemeralStorage: inventoryV1.NewResourcePair(100, 100, 0, resource.DecimalSI),
VolumesAttached: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
VolumesMounted: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
Capabilities: inventoryV1.NodeCapabilities{
StorageClasses: []string{"default"},
},
},
},
Storage: inventoryV1.ClusterStorage{
{
Quantity: inventoryV1.NewResourcePair(0, -1, 0, resource.DecimalSI),
Info: inventoryV1.StorageInfo{Class: "default"},
},
},
},
check: func(t *testing.T, m inventoryV1.Metrics) {
assert.Equal(t, uint64(0), m.TotalAllocatable.Storage["default"])
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
{
name: "allocated_exceeds_allocatable_gpu",
cluster: inventoryV1.Cluster{
Nodes: inventoryV1.Nodes{
inventoryV1.Node{
Name: "overalloc-node",
Resources: inventoryV1.NodeResources{
CPU: inventoryV1.CPU{
Quantity: inventoryV1.NewResourcePairMilli(1000, 1000, 0, resource.DecimalSI),
},
Memory: inventoryV1.Memory{
Quantity: inventoryV1.NewResourcePair(1024, 1024, 0, resource.DecimalSI),
},
GPU: inventoryV1.GPU{
Quantity: inventoryV1.NewResourcePair(4, 4, 10, resource.DecimalSI),
},
EphemeralStorage: inventoryV1.NewResourcePair(100, 100, 0, resource.DecimalSI),
VolumesAttached: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
VolumesMounted: inventoryV1.NewResourcePair(0, 0, 0, resource.DecimalSI),
},
Capabilities: inventoryV1.NodeCapabilities{},
},
},
},
check: func(t *testing.T, m inventoryV1.Metrics) {
require.Len(t, m.Nodes, 1)
assert.Equal(t, uint64(0), m.Nodes[0].Available.GPU, "allocated>allocatable must yield 0 available")
assert.Equal(t, uint64(4), m.Nodes[0].Allocatable.GPU)
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
scaffold := makeInventoryScaffold(t)
cl, err := NewClient(scaffold.ctx)
require.NoError(t, err)
require.NotNil(t, cl)

scaffold.gInv.invch <- tt.cluster

inv := waitForInventory(t, cl.ResultChan())
require.NotNil(t, inv)

tt.check(t, inv.Metrics())
})
}
}

func TestInventorySingleNodeNoPods(t *testing.T) {
const expectedCPU = 13
const expectedMemory = 14
Expand Down
Loading
Loading