Skip to content

Commit c524294

Browse files
authored
feat: adding stateless CNI support for ACI (#3085)
* feat: adding stateless CNI support for ACI * fix:adding unit test * addressing the comments
1 parent 082d6a6 commit c524294

File tree

2 files changed

+143
-13
lines changed

2 files changed

+143
-13
lines changed

cns/restserver/ipam.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,20 +1260,25 @@ func (service *HTTPRestService) UpdateEndpointHelper(endpointID string, req map[
12601260
return ErrStoreEmpty
12611261
}
12621262
logger.Printf("[updateEndpoint] Updating endpoint state for infra container %s", endpointID)
1263-
if endpointInfo, ok := service.EndpointState[endpointID]; ok {
1264-
// Updating the InterfaceInfo map of endpoint states with the interfaceInfo map that is given by Stateless Azure CNI
1265-
for ifName, interfaceInfo := range req {
1266-
// updating the ipInfoMap
1267-
updateIPInfoMap(endpointInfo.IfnameToIPMap, interfaceInfo, ifName, endpointID)
1268-
}
1269-
err := service.EndpointStateStore.Write(EndpointStoreKey, service.EndpointState)
1270-
if err != nil {
1271-
return fmt.Errorf("[updateEndpoint] failed to write endpoint state to store for pod %s : %w", endpointInfo.PodName, err)
1272-
}
1273-
logger.Printf("[updateEndpoint] successfully write the state to the file %s", endpointID)
1274-
return nil
1263+
endpointInfo, endpointExist := service.EndpointState[endpointID]
1264+
// create a new entry in case the ednpoint does not exist in the statefile.
1265+
// this applies to the ACI scenario when the endpoint is not added to the statefile when the goalstate is sent to CNI
1266+
if !endpointExist {
1267+
logger.Printf("[updateEndpoint] endpoint could not be found in the statefile %s, new entry is being added", endpointID)
1268+
endpointInfo = &EndpointInfo{PodName: "", PodNamespace: "", IfnameToIPMap: make(map[string]*IPInfo)}
1269+
service.EndpointState[endpointID] = endpointInfo
1270+
}
1271+
// updating the InterfaceInfo map of endpoint states with the interfaceInfo map that is given by Stateless Azure CNI
1272+
for ifName, interfaceInfo := range req {
1273+
// updating the ipInfoMap
1274+
updateIPInfoMap(endpointInfo.IfnameToIPMap, interfaceInfo, ifName, endpointID)
1275+
}
1276+
err := service.EndpointStateStore.Write(EndpointStoreKey, service.EndpointState)
1277+
if err != nil {
1278+
return fmt.Errorf("[updateEndpoint] failed to write endpoint state to store for pod %s : %w", endpointInfo.PodName, err)
12751279
}
1276-
return errors.New("[updateEndpoint] endpoint could not be found in the statefile")
1280+
logger.Printf("[updateEndpoint] successfully write the state to the file %s", endpointID)
1281+
return nil
12771282
}
12781283

12791284
// updateIPInfoMap updates the IfnameToIPMap of endpoint states with the interfaceInfo map that is given by Stateless Azure CNI

cns/restserver/ipam_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,3 +2154,128 @@ func createAndSaveMockNCRequest(t *testing.T, svc *HTTPRestService, ncID string,
21542154
require.Equal(t, types.Success, returnCode)
21552155
require.Empty(t, returnMessage)
21562156
}
2157+
2158+
// Validate Statefile in Stateless CNI scenarios
2159+
func TestStatelessCNIStateFile(t *testing.T) {
2160+
svc := getTestService(cns.KubernetesCRD)
2161+
svc.EndpointStateStore = store.NewMockStore("")
2162+
// test Case 1 - AKS SIngleTenancy
2163+
endpointInfo1ContainerID := "0a4917617e15d24dc495e407d8eb5c88e4406e58fa209e4eb75a2c2fb7045eea"
2164+
endpointInfo1 := &EndpointInfo{IfnameToIPMap: make(map[string]*IPInfo)}
2165+
endpointInfo1.IfnameToIPMap["eth0"] = &IPInfo{IPv4: []net.IPNet{{IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}}}
2166+
req1 := make(map[string]*IPInfo)
2167+
req1["eth0"] = &IPInfo{IPv4: []net.IPNet{{IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}}, HnsEndpointID: "5c15cccc-830a-4dff-81f3-4b1e55cb7dcb", NICType: cns.InfraNIC}
2168+
testPod1Info = cns.NewPodInfo(endpointInfo1ContainerID, endpointInfo1ContainerID, "pod1", "default")
2169+
req := cns.IPConfigsRequest{
2170+
PodInterfaceID: testPod1Info.InterfaceID(),
2171+
InfraContainerID: testPod1Info.InfraContainerID(),
2172+
}
2173+
// test Case 2 - ACI
2174+
endpointInfo2ContainerID := "1b4917617e15d24dc495e407d8eb5c88e4406e58fa209e4eb75a2c2fb7045eea"
2175+
endpointInfo2 := &EndpointInfo{IfnameToIPMap: make(map[string]*IPInfo)}
2176+
endpointInfo2.IfnameToIPMap["eth2"] = &IPInfo{
2177+
IPv4: nil,
2178+
NICType: cns.DelegatedVMNIC,
2179+
HnsEndpointID: "5c15cccc-830a-4dff-81f3-4b1e55cb7dcb",
2180+
HnsNetworkID: "5c0712cd-824c-4898-b1c0-2fcb16ede4fb",
2181+
MacAddress: "7c:1e:52:06:d3:4b",
2182+
}
2183+
// test cases
2184+
tests := []struct {
2185+
name string
2186+
endpointID string
2187+
req map[string]*IPInfo
2188+
store store.KeyValueStore
2189+
want *EndpointInfo
2190+
wantErr bool
2191+
}{
2192+
{
2193+
name: "single-tenancy: update endpoint without error",
2194+
endpointID: endpointInfo1ContainerID,
2195+
req: req1,
2196+
store: svc.EndpointStateStore,
2197+
want: &EndpointInfo{
2198+
PodName: "pod1", PodNamespace: "default", IfnameToIPMap: map[string]*IPInfo{
2199+
"eth0": {
2200+
IPv4: []net.IPNet{{IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}},
2201+
HnsEndpointID: "5c15cccc-830a-4dff-81f3-4b1e55cb7dcb",
2202+
NICType: cns.InfraNIC,
2203+
},
2204+
},
2205+
},
2206+
wantErr: false,
2207+
},
2208+
{
2209+
name: "ACI: update and create absent endpoint without error",
2210+
endpointID: endpointInfo2ContainerID,
2211+
req: endpointInfo2.IfnameToIPMap,
2212+
store: svc.EndpointStateStore,
2213+
want: endpointInfo2,
2214+
wantErr: false,
2215+
},
2216+
}
2217+
ncStates := []ncState{
2218+
{
2219+
ncID: testNCID,
2220+
ips: []string{
2221+
testIP1,
2222+
},
2223+
},
2224+
}
2225+
2226+
ipconfigs := make(map[string]cns.IPConfigurationStatus, 0)
2227+
for i := range ncStates {
2228+
state := NewPodState(ncStates[i].ips[0], ipIDs[i][0], ncStates[i].ncID, types.Available, 0)
2229+
ipconfigs[state.ID] = state
2230+
err := UpdatePodIPConfigState(t, svc, ipconfigs, ncStates[i].ncID)
2231+
if err != nil {
2232+
t.Fatalf("Expected to not fail update service with config: %+v", err)
2233+
}
2234+
}
2235+
t.Log(ipconfigs)
2236+
b, _ := testPod1Info.OrchestratorContext()
2237+
req.OrchestratorContext = b
2238+
req.Ifname = "eth0"
2239+
podIPInfo, err := requestIPConfigsHelper(svc, req)
2240+
if err != nil {
2241+
t.Fatalf("Expected to not fail getting pod ip info: %+v", err)
2242+
}
2243+
2244+
ipInfo := &IPInfo{}
2245+
for i := range podIPInfo {
2246+
ip, ipnet, errIP := net.ParseCIDR(podIPInfo[i].PodIPConfig.IPAddress + "/" + strconv.FormatUint(uint64(podIPInfo[i].PodIPConfig.PrefixLength), 10))
2247+
if errIP != nil {
2248+
t.Fatalf("failed to parse pod ip address: %+v", errIP)
2249+
}
2250+
ipconfig := net.IPNet{IP: ip, Mask: ipnet.Mask}
2251+
if ip.To4() == nil { // is an ipv6 address
2252+
ipInfo.IPv6 = append(ipInfo.IPv6, ipconfig)
2253+
} else {
2254+
ipInfo.IPv4 = append(ipInfo.IPv4, ipconfig)
2255+
}
2256+
}
2257+
2258+
// add goalState
2259+
err = svc.updateEndpointState(req, testPod1Info, podIPInfo)
2260+
if err != nil {
2261+
t.Fatalf("Expected to not fail updating endpoint state: %+v", err)
2262+
}
2263+
// update State
2264+
for _, tt := range tests {
2265+
tt := tt
2266+
t.Run(tt.name, func(t *testing.T) {
2267+
err := svc.UpdateEndpointHelper(tt.endpointID, tt.req)
2268+
if tt.wantErr {
2269+
assert.Error(t, err)
2270+
return
2271+
}
2272+
got, err := svc.GetEndpointHelper(tt.endpointID)
2273+
if tt.wantErr {
2274+
assert.Error(t, err)
2275+
return
2276+
}
2277+
require.NoError(t, err)
2278+
assert.Equal(t, tt.want, got)
2279+
})
2280+
}
2281+
}

0 commit comments

Comments
 (0)