diff --git a/pkg/ipamd/datastore/data_store.go b/pkg/ipamd/datastore/data_store.go index 36313c4a1b..620ffb0472 100644 --- a/pkg/ipamd/datastore/data_store.go +++ b/pkg/ipamd/datastore/data_store.go @@ -1220,10 +1220,7 @@ func (ds *DataStore) UnassignPodIPAddress(ipamKey IPAMKey) (e *ENI, ip string, d return nil, "", 0, err } addr.UnassignedTime = time.Now() - if ds.isPDEnabled && !availableCidr.IsPrefix { - ds.log.Infof("Prefix delegation is enabled and the IP is from secondary pool hence no need to update prefix pool") - ds.total-- - } + //Update prometheus for ips per cidr ipsPerCidr.With(prometheus.Labels{"cidr": availableCidr.Cidr.String()}).Dec() ds.log.Infof("UnassignPodIPAddress: sandbox %s's ipAddr %s, DeviceNumber %d", diff --git a/pkg/ipamd/datastore/data_store_test.go b/pkg/ipamd/datastore/data_store_test.go index 3658150458..9bee84c8aa 100644 --- a/pkg/ipamd/datastore/data_store_test.go +++ b/pkg/ipamd/datastore/data_store_test.go @@ -96,6 +96,57 @@ func TestDeleteENI(t *testing.T) { assert.Error(t, err) err = ds.RemoveENIFromDataStore("eni-1", true) assert.NoError(t, err) + +} + +func TestDeleteENIwithPDEnabled(t *testing.T) { + ds := NewDataStore(Testlog, NullCheckpoint{}, true) + + err := ds.AddENI("eni-1", 1, true, false, false) + assert.NoError(t, err) + + err = ds.AddENI("eni-2", 2, false, false, false) + assert.NoError(t, err) + + err = ds.AddENI("eni-3", 3, false, false, false) + assert.NoError(t, err) + + err = ds.AddENI("eni-4", 4, false, false, false) + assert.NoError(t, err) + + eniInfos := ds.GetENIInfos() + assert.Equal(t, len(eniInfos.ENIs), 4) + + err = ds.RemoveENIFromDataStore("eni-2", false) + assert.NoError(t, err) + + eniInfos = ds.GetENIInfos() + assert.Equal(t, len(eniInfos.ENIs), 3) + + err = ds.RemoveENIFromDataStore("unknown-eni", false) + assert.Error(t, err) + + eniInfos = ds.GetENIInfos() + assert.Equal(t, len(eniInfos.ENIs), 3) + + // Add a prefix and assign a pod + ipv4Addr := net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-4", ipv4Addr, true) + assert.NoError(t, err) + ip, device, err := ds.AssignPodIPv4Address( + IPAMKey{"net1", "sandbox1", "eth0"}, + IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod"}) + assert.NoError(t, err) + assert.Equal(t, "10.0.0.0", ip) + assert.Equal(t, 4, device) + + // Test force removal. The first call fails because eni-1 has an IP with a pod assigned to it, + // but the second call force-removes it and succeeds. + err = ds.RemoveENIFromDataStore("eni-4", false) + assert.Error(t, err) + err = ds.RemoveENIFromDataStore("eni-4", true) + assert.NoError(t, err) + } func TestAddENIIPv4Address(t *testing.T) { @@ -141,6 +192,49 @@ func TestAddENIIPv4Address(t *testing.T) { } +func TestAddENIIPv4AddressWithPDEnabled(t *testing.T) { + ds := NewDataStore(Testlog, NullCheckpoint{}, true) + + err := ds.AddENI("eni-1", 1, true, false, false) + assert.NoError(t, err) + + err = ds.AddENI("eni-2", 2, false, false, false) + assert.NoError(t, err) + + ipv4Addr := net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + + ipv4Addr = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.Error(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + + ipv4Addr = net.IPNet{IP: net.ParseIP("20.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 32) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + ipv4Addr = net.IPNet{IP: net.ParseIP("30.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-2", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 48) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + assert.Equal(t, len(ds.eniPool["eni-2"].AvailableIPv4Cidrs), 1) + + ipv4Addr = net.IPNet{IP: net.ParseIP("40.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("dummy-eni", ipv4Addr, true) + assert.Error(t, err) + assert.Equal(t, ds.total, 48) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + assert.Equal(t, len(ds.eniPool["eni-2"].AvailableIPv4Cidrs), 1) + +} + func TestGetENIIPs(t *testing.T) { ds := NewDataStore(Testlog, NullCheckpoint{}, false) @@ -177,6 +271,42 @@ func TestGetENIIPs(t *testing.T) { assert.Error(t, err) } +func TestGetENIIPsWithPDEnabled(t *testing.T) { + ds := NewDataStore(Testlog, NullCheckpoint{}, true) + + err := ds.AddENI("eni-1", 1, true, false, false) + assert.NoError(t, err) + + err = ds.AddENI("eni-2", 2, false, false, false) + assert.NoError(t, err) + + ipv4Addr := net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + + ipv4Addr = net.IPNet{IP: net.ParseIP("20.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 32) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + ipv4Addr = net.IPNet{IP: net.ParseIP("30.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-2", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 48) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + assert.Equal(t, len(ds.eniPool["eni-2"].AvailableIPv4Cidrs), 1) + + _, eniPrefixPool, err := ds.GetENICIDRs("eni-1") + assert.NoError(t, err) + assert.Equal(t, len(eniPrefixPool), 2) + + _, _, err = ds.GetENICIDRs("dummy-eni") + assert.Error(t, err) +} + func TestDelENIIPv4Address(t *testing.T) { ds := NewDataStore(Testlog, NullCheckpoint{}, false) err := ds.AddENI("eni-1", 1, true, false, false) @@ -235,6 +365,143 @@ func TestDelENIIPv4Address(t *testing.T) { assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) } +func TestDelENIIPv4AddressWithPDEnabled(t *testing.T) { + ds := NewDataStore(Testlog, NullCheckpoint{}, true) + err := ds.AddENI("eni-1", 1, true, false, false) + assert.NoError(t, err) + + ipv4Addr := net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + + // Assign a pod. + key := IPAMKey{"net0", "sandbox-1", "eth0"} + ip, device, err := ds.AssignPodIPv4Address(key, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}) + assert.NoError(t, err) + assert.Equal(t, "10.0.0.0", ip) + assert.Equal(t, 1, device) + + ipv4Addr = net.IPNet{IP: net.ParseIP("20.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 32) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + ipv4Addr = net.IPNet{IP: net.ParseIP("30.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 48) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 3) + + ipv4Addr = net.IPNet{IP: net.ParseIP("30.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, false) + assert.NoError(t, err) + assert.Equal(t, ds.total, 32) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + // delete a unknown IP + ipv4Addr = net.IPNet{IP: net.ParseIP("10.10.10.10"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, true) + assert.Error(t, err) + assert.Equal(t, ds.total, 32) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + // Test force removal. The first call fails because the IP has a pod assigned to it, but the + // second call force-removes it and succeeds. + ipv4Addr = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, false) + assert.Error(t, err) + assert.Equal(t, ds.total, 32) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + ipv4Addr = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) +} + +func TestTogglePD(t *testing.T) { + //DS is in secondary IP mode + ds := NewDataStore(Testlog, NullCheckpoint{}, false) + err := ds.AddENI("eni-1", 1, true, false, false) + assert.NoError(t, err) + + // Add /32 secondary IP + ipv4Addr := net.IPNet{IP: net.ParseIP("1.1.1.1"), Mask: net.IPv4Mask(255, 255, 255, 255)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, false) + assert.NoError(t, err) + assert.Equal(t, ds.total, 1) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + + // Assign a pod. + key := IPAMKey{"net0", "sandbox-1", "eth0"} + ip, device, err := ds.AssignPodIPv4Address(key, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}) + assert.NoError(t, err) + assert.Equal(t, "1.1.1.1", ip) + assert.Equal(t, 1, device) + + //enable pd mode + ds.isPDEnabled = true + + // Add a /28 prefix to the same eni + ipv4Addr = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 17) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + //Assign a pod + key = IPAMKey{"net0", "sandbox-2", "eth0"} + ip, device, err = ds.AssignPodIPv4Address(key, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-2"}) + assert.NoError(t, err) + assert.Equal(t, "10.0.0.0", ip) + assert.Equal(t, 1, device) + + //Pod deletion simulated with force delete + //Test force removal. The first call fails because the IP has a pod assigned to it, but the + //second call force-removes it and succeeds. + ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.1"), Mask: net.IPv4Mask(255, 255, 255, 255)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, false) + assert.Error(t, err) + assert.Equal(t, ds.total, 17) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.1"), Mask: net.IPv4Mask(255, 255, 255, 255)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + + //disable pd mode + ds.isPDEnabled = false + + //Add /32 secondary IP + ipv4Addr = net.IPNet{IP: net.ParseIP("1.1.1.1"), Mask: net.IPv4Mask(255, 255, 255, 255)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, false) + assert.NoError(t, err) + assert.Equal(t, ds.total, 17) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + //Pod deletion simulated with force delete + //Test force removal. The first call fails because the IP has a pod assigned to it, but the + //second call force-removes it and succeeds. + ipv4Addr = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, false) + assert.Error(t, err) + assert.Equal(t, ds.total, 17) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 2) + + ipv4Addr = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.DelIPv4CidrFromStore("eni-1", ipv4Addr, true) + assert.NoError(t, err) + assert.Equal(t, ds.total, 1) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + +} + func TestPodIPv4Address(t *testing.T) { checkpoint := NewTestCheckpoint(struct{}{}) ds := NewDataStore(Testlog, checkpoint, false) @@ -440,6 +707,180 @@ func TestPodIPv4Address(t *testing.T) { assert.Equal(t, ds.assigned, 2) } +func TestPodIPv4AddressWithPDEnabled(t *testing.T) { + checkpoint := NewTestCheckpoint(struct{}{}) + ds := NewDataStore(Testlog, checkpoint, true) + + checkpointDataCmpOpts := cmp.Options{ + cmpopts.IgnoreFields(CheckpointEntry{}, "AllocationTimestamp"), + cmpopts.SortSlices(func(lhs CheckpointEntry, rhs CheckpointEntry) bool { + return lhs.ContainerID < rhs.ContainerID + }), + } + + err := ds.AddENI("eni-1", 1, true, false, false) + assert.NoError(t, err) + + err = ds.AddENI("eni-2", 2, false, false, false) + assert.NoError(t, err) + + ipv4Addr1 := net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + err = ds.AddIPv4CidrToStore("eni-1", ipv4Addr1, true) + assert.NoError(t, err) + + key1 := IPAMKey{"net0", "sandbox-1", "eth0"} + ip, _, err := ds.AssignPodIPv4Address(key1, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}) + + assert.NoError(t, err) + assert.Equal(t, "10.0.0.0", ip) + assert.Equal(t, 16, ds.total) + assert.Equal(t, 1, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs)) + assert.Equal(t, 1, ds.eniPool["eni-1"].AssignedIPv4Addresses()) + + expectedCheckpointData := &CheckpointData{ + Version: CheckpointFormatVersion, + Allocations: []CheckpointEntry{ + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-1", IfName: "eth0"}, + IPv4: "10.0.0.0", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}, + }, + }, + } + assert.True(t, + cmp.Equal(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + cmp.Diff(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + ) + + podsInfos := ds.AllocatedIPs() + assert.Equal(t, len(podsInfos), 1) + + // duplicate add + ip, _, err = ds.AssignPodIPv4Address(key1, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}) // same id + assert.NoError(t, err) + assert.Equal(t, ip, "10.0.0.0") + assert.Equal(t, ds.total, 16) + assert.Equal(t, ds.assigned, 1) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + assert.Equal(t, ds.eniPool["eni-1"].AssignedIPv4Addresses(), 1) + + // Checkpoint error + checkpoint.Error = errors.New("fake checkpoint error") + key2 := IPAMKey{"net0", "sandbox-2", "eth0"} + _, _, err = ds.AssignPodIPv4Address(key2, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-2"}) + assert.Error(t, err) + + expectedCheckpointData = &CheckpointData{ + Version: CheckpointFormatVersion, + Allocations: []CheckpointEntry{ + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-1", IfName: "eth0"}, + IPv4: "10.0.0.0", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}, + }, + }, + } + assert.True(t, + cmp.Equal(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + cmp.Diff(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + ) + checkpoint.Error = nil + + ip, pod1Ns2Device, err := ds.AssignPodIPv4Address(key2, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-2"}) + assert.NoError(t, err) + assert.Equal(t, ip, "10.0.0.1") + assert.Equal(t, ds.total, 16) + assert.Equal(t, ds.assigned, 2) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + assert.Equal(t, ds.eniPool["eni-1"].AssignedIPv4Addresses(), 2) + + expectedCheckpointData = &CheckpointData{ + Version: CheckpointFormatVersion, + Allocations: []CheckpointEntry{ + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-1", IfName: "eth0"}, + IPv4: "10.0.0.0", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}, + }, + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-2", IfName: "eth0"}, + IPv4: "10.0.0.1", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-2"}, + }, + }, + } + assert.True(t, + cmp.Equal(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + cmp.Diff(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + ) + + podsInfos = ds.AllocatedIPs() + assert.Equal(t, len(podsInfos), 2) + + key3 := IPAMKey{"net0", "sandbox-3", "eth0"} + ip, _, err = ds.AssignPodIPv4Address(key3, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-3"}) + assert.NoError(t, err) + assert.Equal(t, ip, "10.0.0.2") + assert.Equal(t, ds.total, 16) + assert.Equal(t, ds.assigned, 3) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + assert.Equal(t, ds.eniPool["eni-1"].AssignedIPv4Addresses(), 3) + expectedCheckpointData = &CheckpointData{ + Version: CheckpointFormatVersion, + Allocations: []CheckpointEntry{ + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-1", IfName: "eth0"}, + IPv4: "10.0.0.0", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}, + }, + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-2", IfName: "eth0"}, + IPv4: "10.0.0.1", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-2"}, + }, + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-3", IfName: "eth0"}, + IPv4: "10.0.0.2", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-3"}, + }, + }, + } + assert.True(t, + cmp.Equal(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + cmp.Diff(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + ) + + _, _, deviceNum, err := ds.UnassignPodIPAddress(key2) + assert.NoError(t, err) + assert.Equal(t, ds.total, 16) + assert.Equal(t, ds.assigned, 2) + assert.Equal(t, deviceNum, pod1Ns2Device) + assert.Equal(t, len(ds.eniPool["eni-1"].AvailableIPv4Cidrs), 1) + assert.Equal(t, ds.eniPool["eni-1"].AssignedIPv4Addresses(), 2) + expectedCheckpointData = &CheckpointData{ + Version: CheckpointFormatVersion, + Allocations: []CheckpointEntry{ + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-1", IfName: "eth0"}, + IPv4: "10.0.0.0", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}, + }, + { + IPAMKey: IPAMKey{NetworkName: "net0", ContainerID: "sandbox-3", IfName: "eth0"}, + IPv4: "10.0.0.2", + Metadata: IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-3"}, + }, + }, + } + assert.True(t, + cmp.Equal(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + cmp.Diff(checkpoint.Data, expectedCheckpointData, checkpointDataCmpOpts), + ) + + assert.Equal(t, ds.total, 16) + assert.Equal(t, ds.assigned, 2) +} + func TestGetIPStatsV4(t *testing.T) { ds := NewDataStore(Testlog, NullCheckpoint{}, false) @@ -491,6 +932,58 @@ func TestGetIPStatsV4(t *testing.T) { ) } +func TestGetIPStatsV4WithPD(t *testing.T) { + ds := NewDataStore(Testlog, NullCheckpoint{}, true) + + _ = ds.AddENI("eni-1", 1, true, false, false) + + ipv4Addr := net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.IPv4Mask(255, 255, 255, 240)} + _ = ds.AddIPv4CidrToStore("eni-1", ipv4Addr, true) + key1 := IPAMKey{"net0", "sandbox-1", "eth0"} + _, _, err := ds.AssignPodIPv4Address(key1, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-1"}) + assert.NoError(t, err) + + key2 := IPAMKey{"net0", "sandbox-2", "eth0"} + _, _, err = ds.AssignPodIPv4Address(key2, IPAMMetadata{K8SPodNamespace: "default", K8SPodName: "sample-pod-2"}) + assert.NoError(t, err) + + assert.Equal(t, + DataStoreStats{ + TotalIPs: 16, + TotalPrefixes: 1, + AssignedIPs: 2, + CooldownIPs: 0, + }, + *ds.GetIPStats("4"), + ) + + _, _, _, err = ds.UnassignPodIPAddress(key2) + assert.NoError(t, err) + + assert.Equal(t, + DataStoreStats{ + TotalIPs: 16, + TotalPrefixes: 1, + AssignedIPs: 1, + CooldownIPs: 1, + }, + *ds.GetIPStats("4"), + ) + + // wait 30s (cooldown period) + time.Sleep(30 * time.Second) + + assert.Equal(t, + DataStoreStats{ + TotalIPs: 16, + TotalPrefixes: 1, + AssignedIPs: 1, + CooldownIPs: 0, + }, + *ds.GetIPStats("4"), + ) +} + func TestGetIPStatsV6(t *testing.T) { v6ds := NewDataStore(Testlog, NullCheckpoint{}, true) _ = v6ds.AddENI("eni-1", 1, true, false, false)