Skip to content

Commit 4604d04

Browse files
chore: add ipam reconciler interface
1 parent fb25300 commit 4604d04

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

cns/ipam/interfaces.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ipam
2+
3+
import (
4+
"github.com/Azure/azure-container-networking/cns"
5+
cnstypes "github.com/Azure/azure-container-networking/cns/types"
6+
"github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha"
7+
)
8+
9+
type IpamStateReconciler interface {
10+
ReconcileIPAMState(ncRequests []*cns.CreateNetworkContainerRequest, podInfoByIP map[string]cns.PodInfo, nnc *v1alpha.NodeNetworkConfig) cnstypes.ResponseCode
11+
}

cns/nodesubnet/initialization.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package nodesubnet
2+
3+
import (
4+
"context"
5+
6+
"github.com/Azure/azure-container-networking/cns"
7+
"github.com/Azure/azure-container-networking/cns/ipam"
8+
"github.com/Azure/azure-container-networking/cns/logger"
9+
"github.com/Azure/azure-container-networking/cns/types"
10+
"github.com/pkg/errors"
11+
"golang.org/x/exp/maps"
12+
)
13+
14+
func ReconcileInitialCNSState(ctx context.Context, ipamReconciler ipam.IpamStateReconciler, podInfoByIPProvider cns.PodInfoByIPProvider) (int, error) {
15+
// Get previous PodInfo state from podInfoByIPProvider
16+
podInfoByIP, err := podInfoByIPProvider.PodInfoByIP()
17+
if err != nil {
18+
return 0, errors.Wrap(err, "provider failed to provide PodInfoByIP")
19+
}
20+
21+
logger.Printf("Reconciling initial CNS state with %d IPs", len(podInfoByIP))
22+
23+
// Create a network container request that holds all the IPs from PodInfoByIP
24+
secondaryIPs := maps.Keys(podInfoByIP)
25+
ncRequest := CreateNodeSubnetNCRequest(secondaryIPs)
26+
responseCode := ipamReconciler.ReconcileIPAMState([]*cns.CreateNetworkContainerRequest{ncRequest}, podInfoByIP, nil)
27+
28+
if responseCode != types.Success {
29+
return 0, errors.Errorf("failed to reconcile initial CNS state: %d", responseCode)
30+
}
31+
32+
return len(secondaryIPs), nil
33+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package nodesubnet_test
2+
3+
import (
4+
"context"
5+
"net"
6+
"testing"
7+
8+
"github.com/Azure/azure-container-networking/cns"
9+
"github.com/Azure/azure-container-networking/cns/cnireconciler"
10+
"github.com/Azure/azure-container-networking/cns/ipam"
11+
"github.com/Azure/azure-container-networking/cns/nodesubnet"
12+
"github.com/Azure/azure-container-networking/cns/restserver"
13+
"github.com/Azure/azure-container-networking/cns/types"
14+
"github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha"
15+
"github.com/Azure/azure-container-networking/store"
16+
)
17+
18+
func getMockStore() store.KeyValueStore {
19+
mockStore := store.NewMockStore("")
20+
endpointState := map[string]*restserver.EndpointInfo{
21+
"12e65d89e58cb23c784e97840cf76866bfc9902089bdc8e87e9f64032e312b0b": {
22+
PodName: "coredns-54b69f46b8-ldmwr",
23+
PodNamespace: "kube-system",
24+
IfnameToIPMap: map[string]*restserver.IPInfo{
25+
"eth0": {
26+
IPv4: []net.IPNet{
27+
{
28+
IP: net.IPv4(10, 10, 0, 52),
29+
Mask: net.CIDRMask(24, 32),
30+
},
31+
},
32+
},
33+
},
34+
},
35+
"1fc5176913a3a1a7facfb823dde3b4ded404041134fef4f4a0c8bba140fc0413": {
36+
PodName: "load-test-7f7d49687d-wxc9p",
37+
PodNamespace: "load-test",
38+
IfnameToIPMap: map[string]*restserver.IPInfo{
39+
"eth0": {
40+
IPv4: []net.IPNet{
41+
{
42+
IP: net.IPv4(10, 10, 0, 63),
43+
Mask: net.CIDRMask(24, 32),
44+
},
45+
},
46+
},
47+
},
48+
},
49+
}
50+
51+
err := mockStore.Write(restserver.EndpointStoreKey, endpointState)
52+
if err != nil {
53+
return nil
54+
}
55+
return mockStore
56+
}
57+
58+
type MockIpamStateReconciler struct{}
59+
60+
func (m *MockIpamStateReconciler) ReconcileIPAMState(ncRequests []*cns.CreateNetworkContainerRequest, podInfoByIP map[string]cns.PodInfo, nnc *v1alpha.NodeNetworkConfig) types.ResponseCode {
61+
if len(ncRequests) == 1 && len(ncRequests[0].SecondaryIPConfigs) == len(podInfoByIP) {
62+
return types.Success
63+
}
64+
65+
return types.UnexpectedError
66+
}
67+
68+
func TestNewCNSPodInfoProvider(t *testing.T) {
69+
tests := []struct {
70+
name string
71+
store store.KeyValueStore
72+
wantErr bool
73+
reconciler ipam.IpamStateReconciler
74+
exp int
75+
}{
76+
{
77+
name: "happy_path",
78+
store: getMockStore(),
79+
wantErr: false,
80+
reconciler: &MockIpamStateReconciler{},
81+
exp: 2,
82+
},
83+
}
84+
85+
for _, tt := range tests {
86+
tt := tt
87+
ctx, cancel := testContext(t)
88+
defer cancel()
89+
90+
t.Run(tt.name, func(t *testing.T) {
91+
podInfoByIPProvider, err := cnireconciler.NewCNSPodInfoProvider(tt.store)
92+
checkErr(t, err, false)
93+
94+
got, err := nodesubnet.ReconcileInitialCNSState(ctx, tt.reconciler, podInfoByIPProvider)
95+
checkErr(t, err, tt.wantErr)
96+
if got != tt.exp {
97+
t.Errorf("got %d IPs reconciled, expected %d", got, tt.exp)
98+
}
99+
})
100+
}
101+
}
102+
103+
// testContext creates a context from the provided testing.T that will be
104+
// canceled if the test suite is terminated.
105+
func testContext(t *testing.T) (context.Context, context.CancelFunc) {
106+
if deadline, ok := t.Deadline(); ok {
107+
return context.WithDeadline(context.Background(), deadline)
108+
}
109+
return context.WithCancel(context.Background())
110+
}

0 commit comments

Comments
 (0)