diff --git a/cns/NetworkContainerContract.go b/cns/NetworkContainerContract.go index 4f0940e9a6..465b1c0963 100644 --- a/cns/NetworkContainerContract.go +++ b/cns/NetworkContainerContract.go @@ -131,6 +131,8 @@ type IPConfiguration struct { // SecondaryIPConfig contains IP info of SecondaryIP type SecondaryIPConfig struct { IPAddress string + // NCVesion will help in determining whether IP is in pending programming or available when reconciling. + NCVersion int } // IPSubnet contains ip subnet. diff --git a/cns/requestcontroller/kubecontroller/crdrequestcontroller_test.go b/cns/requestcontroller/kubecontroller/crdrequestcontroller_test.go index 0a0dcb3de2..1d451320ca 100644 --- a/cns/requestcontroller/kubecontroller/crdrequestcontroller_test.go +++ b/cns/requestcontroller/kubecontroller/crdrequestcontroller_test.go @@ -592,6 +592,7 @@ func TestInitRequestController(t *testing.T) { }, }, SubnetAddressSpace: subnetRange, + Version: "1", }, }, }, diff --git a/cns/requestcontroller/kubecontroller/crdtranslator.go b/cns/requestcontroller/kubecontroller/crdtranslator.go index aa8c9e89cd..cb0a79d62e 100644 --- a/cns/requestcontroller/kubecontroller/crdtranslator.go +++ b/cns/requestcontroller/kubecontroller/crdtranslator.go @@ -3,6 +3,7 @@ package kubecontroller import ( "fmt" "net" + "strconv" "github.com/Azure/azure-container-networking/cns" nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha" @@ -51,14 +52,18 @@ func CRDStatusToNCRequest(crdStatus nnc.NodeNetworkConfigStatus) (cns.CreateNetw ipSubnet.PrefixLength = uint8(size) ncRequest.IPConfiguration.IPSubnet = ipSubnet ncRequest.IPConfiguration.GatewayIPAddress = nc.DefaultGateway + var ncVersion int + if ncVersion, err = strconv.Atoi(ncRequest.Version); err != nil { + return ncRequest, fmt.Errorf("Invalid ncRequest.Version is %s in CRD, err:%s", ncRequest.Version, err) + } for _, ipAssignment = range nc.IPAssignments { if ip = net.ParseIP(ipAssignment.IP); ip == nil { return ncRequest, fmt.Errorf("Invalid SecondaryIP %s:", ipAssignment.IP) } - secondaryIPConfig = cns.SecondaryIPConfig{ IPAddress: ip.String(), + NCVersion: ncVersion, } ncRequest.SecondaryIPConfigs[ipAssignment.Name] = secondaryIPConfig } diff --git a/cns/requestcontroller/kubecontroller/crdtranslator_test.go b/cns/requestcontroller/kubecontroller/crdtranslator_test.go index 3153680915..e34053950e 100644 --- a/cns/requestcontroller/kubecontroller/crdtranslator_test.go +++ b/cns/requestcontroller/kubecontroller/crdtranslator_test.go @@ -1,6 +1,7 @@ package kubecontroller import ( + "strconv" "testing" "github.com/Azure/azure-container-networking/cns" @@ -235,4 +236,9 @@ func TestStatusToNCRequestSuccess(t *testing.T) { if secondaryIP.IPAddress != testSecIp1 { t.Fatalf("Expected %v as the secondary IP config but got %v", testSecIp1, secondaryIP.IPAddress) } + + ncVersionInInt, _ := strconv.Atoi(version) + if secondaryIP.NCVersion != ncVersionInInt { + t.Fatalf("Expected %d as the secondary IP config NC version but got %v", ncVersionInInt, secondaryIP.NCVersion) + } } diff --git a/cns/restserver/internalapi_test.go b/cns/restserver/internalapi_test.go index 923cc46032..92df0fea6a 100644 --- a/cns/restserver/internalapi_test.go +++ b/cns/restserver/internalapi_test.go @@ -48,6 +48,74 @@ func TestCreateOrUpdateNCWithLargerVersionComparedToNMAgent(t *testing.T) { validateCreateOrUpdateNCInternal(t, 2, "1") } +func TestCreateAndUpdateNCWithSecondaryIPNCVersion(t *testing.T) { + restartService() + setEnv(t) + setOrchestratorTypeInternal(cns.KubernetesCRD) + // NC version set as 0 which is the default initial value. + ncVersion := 0 + secondaryIPConfigs := make(map[string]cns.SecondaryIPConfig) + ncID := "testNc1" + + // Build secondaryIPConfig, it will have one item as {IPAddress:"10.0.0.16", NCVersion: 0} + ipAddress := "10.0.0.16" + secIPConfig := newSecondaryIPConfig(ipAddress, ncVersion) + ipId := uuid.New() + secondaryIPConfigs[ipId.String()] = secIPConfig + req := createNCReqInternal(t, secondaryIPConfigs, ncID, strconv.Itoa(ncVersion)) + containerStatus := svc.state.ContainerStatus[req.NetworkContainerid] + // Validate secondary IPs' NC version has been updated by NC request + receivedSecondaryIPConfigs := containerStatus.CreateNetworkContainerRequest.SecondaryIPConfigs + if len(receivedSecondaryIPConfigs) != 1 { + t.Fatalf("receivedSecondaryIPConfigs lenth must be 1, but recieved %d", len(receivedSecondaryIPConfigs)) + } + for _, secIPConfig := range receivedSecondaryIPConfigs { + if secIPConfig.IPAddress != "10.0.0.16" || secIPConfig.NCVersion != 0 { + t.Fatalf("nc request version is %d, secondary ip %s nc version is %d, expected nc version is 0", + ncVersion, secIPConfig.IPAddress, secIPConfig.NCVersion) + } + } + + // now Validate Update, simulate the CRD status where have 2 IP addresses as "10.0.0.16" and "10.0.0.17" with NC version 1 + // The secondaryIPConfigs build from CRD will be {[IPAddress:"10.0.0.16", NCVersion: 1,]; [IPAddress:"10.0.0.17", NCVersion: 1,]} + // However, when CNS saveNetworkContainerGoalState, since "10.0.0.16" already with NC version 0, it will set it back to 0. + ncVersion++ + secIPConfig = newSecondaryIPConfig(ipAddress, ncVersion) + // "10.0.0.16" will be update to NC version 1 in CRD, reuse the same uuid with it. + secondaryIPConfigs[ipId.String()] = secIPConfig + + // Add {IPAddress:"10.0.0.17", NCVersion: 1} in secondaryIPConfig + ipAddress = "10.0.0.17" + secIPConfig = newSecondaryIPConfig(ipAddress, ncVersion) + ipId = uuid.New() + secondaryIPConfigs[ipId.String()] = secIPConfig + req = createNCReqInternal(t, secondaryIPConfigs, ncID, strconv.Itoa(ncVersion)) + // Validate secondary IPs' NC version has been updated by NC request + containerStatus = svc.state.ContainerStatus[req.NetworkContainerid] + receivedSecondaryIPConfigs = containerStatus.CreateNetworkContainerRequest.SecondaryIPConfigs + if len(receivedSecondaryIPConfigs) != 2 { + t.Fatalf("receivedSecondaryIPConfigs must be 2, but received %d", len(receivedSecondaryIPConfigs)) + } + for _, secIPConfig := range receivedSecondaryIPConfigs { + switch secIPConfig.IPAddress { + case "10.0.0.16": + // Though "10.0.0.16" IP exists in NC version 1, secodanry IP still keep its original NC version 0 + if secIPConfig.NCVersion != 0 { + t.Fatalf("nc request version is %d, secondary ip %s nc version is %d, expected nc version is 0", + ncVersion, secIPConfig.IPAddress, secIPConfig.NCVersion) + } + case "10.0.0.17": + if secIPConfig.NCVersion != 1 { + t.Fatalf("nc request version is %d, secondary ip %s nc version is %d, expected nc version is 1", + ncVersion, secIPConfig.IPAddress, secIPConfig.NCVersion) + } + default: + t.Fatalf("nc request version is %d, secondary ip %s nc version is %d should not exist in receivedSecondaryIPConfigs map", + ncVersion, secIPConfig.IPAddress, secIPConfig.NCVersion) + } + } +} + func TestReconcileNCWithEmptyState(t *testing.T) { restartService() setEnv(t) @@ -73,7 +141,7 @@ func TestReconcileNCWithExistingState(t *testing.T) { var startingIndex = 6 for i := 0; i < 4; i++ { ipaddress := "10.0.0." + strconv.Itoa(startingIndex) - secIpConfig := newSecondaryIPConfig(ipaddress) + secIpConfig := newSecondaryIPConfig(ipaddress, 0) ipId := uuid.New() secondaryIPConfigs[ipId.String()] = secIpConfig startingIndex++ @@ -110,7 +178,7 @@ func TestReconcileNCWithSystemPods(t *testing.T) { var startingIndex = 6 for i := 0; i < 4; i++ { ipaddress := "10.0.0." + strconv.Itoa(startingIndex) - secIpConfig := newSecondaryIPConfig(ipaddress) + secIpConfig := newSecondaryIPConfig(ipaddress, 0) ipId := uuid.New() secondaryIPConfigs[ipId.String()] = secIpConfig startingIndex++ @@ -151,7 +219,7 @@ func validateCreateOrUpdateNCInternal(t *testing.T, secondaryIpCount int, ncVers var startingIndex = 6 for i := 0; i < secondaryIpCount; i++ { ipaddress := "10.0.0." + strconv.Itoa(startingIndex) - secIpConfig := newSecondaryIPConfig(ipaddress) + secIpConfig := newSecondaryIPConfig(ipaddress, 0) ipId := uuid.New() secondaryIPConfigs[ipId.String()] = secIpConfig startingIndex++ @@ -163,7 +231,7 @@ func validateCreateOrUpdateNCInternal(t *testing.T, secondaryIpCount int, ncVers fmt.Println("Validate Scaleup") for i := 0; i < secondaryIpCount; i++ { ipaddress := "10.0.0." + strconv.Itoa(startingIndex) - secIpConfig := newSecondaryIPConfig(ipaddress) + secIpConfig := newSecondaryIPConfig(ipaddress, 1) ipId := uuid.New() secondaryIPConfigs[ipId.String()] = secIpConfig startingIndex++ @@ -289,9 +357,12 @@ func generateNetworkContainerRequest(secondaryIps map[string]cns.SecondaryIPConf Version: ncVersion, } + ncVersionInInt, _ := strconv.Atoi(ncVersion) req.SecondaryIPConfigs = make(map[string]cns.SecondaryIPConfig) for k, v := range secondaryIps { req.SecondaryIPConfigs[k] = v + ipconfig, _ := req.SecondaryIPConfigs[k] + ipconfig.NCVersion = ncVersionInInt } fmt.Printf("NC Request %+v", req) @@ -359,6 +430,15 @@ func validateNCStateAfterReconcile(t *testing.T, ncRequest *cns.CreateNetworkCon } } +func createNCReqInternal(t *testing.T, secondaryIPConfigs map[string]cns.SecondaryIPConfig, ncID, ncVersion string) cns.CreateNetworkContainerRequest { + req := generateNetworkContainerRequest(secondaryIPConfigs, ncID, ncVersion) + returnCode := svc.CreateOrUpdateNetworkContainerInternal(req, fakes.NewFakeScalar(releasePercent, requestPercent, batchSize), fakes.NewFakeNodeNetworkConfigSpec(initPoolSize)) + if returnCode != 0 { + t.Fatalf("Failed to createNetworkContainerRequest, req: %+v, err: %d", req, returnCode) + } + return req +} + func restartService() { fmt.Println("Restart Service") diff --git a/cns/restserver/ipam_test.go b/cns/restserver/ipam_test.go index f1a280eea3..e7fe083372 100644 --- a/cns/restserver/ipam_test.go +++ b/cns/restserver/ipam_test.go @@ -49,14 +49,15 @@ func getTestService() *HTTPRestService { return svc } -func newSecondaryIPConfig(ipAddress string) cns.SecondaryIPConfig { +func newSecondaryIPConfig(ipAddress string, ncVersion int) cns.SecondaryIPConfig { return cns.SecondaryIPConfig{ IPAddress: ipAddress, + NCVersion: ncVersion, } } -func NewPodState(ipaddress string, prefixLength uint8, id, ncid, state string) cns.IPConfigurationStatus { - ipconfig := newSecondaryIPConfig(ipaddress) +func NewPodState(ipaddress string, prefixLength uint8, id, ncid, state string, ncVersion int) cns.IPConfigurationStatus { + ipconfig := newSecondaryIPConfig(ipaddress, ncVersion) return cns.IPConfigurationStatus{ IPAddress: ipconfig.IPAddress, @@ -119,8 +120,8 @@ func requestIpAddressAndGetState(t *testing.T, req cns.GetIPConfigRequest) (cns. return ipState, err } -func NewPodStateWithOrchestratorContext(ipaddress string, prefixLength uint8, id, ncid, state string, orchestratorContext cns.KubernetesPodInfo) (cns.IPConfigurationStatus, error) { - ipconfig := newSecondaryIPConfig(ipaddress) +func NewPodStateWithOrchestratorContext(ipaddress, id, ncid, state string, prefixLength uint8, ncVersion int, orchestratorContext cns.KubernetesPodInfo) (cns.IPConfigurationStatus, error) { + ipconfig := newSecondaryIPConfig(ipaddress, ncVersion) b, err := json.Marshal(orchestratorContext) return cns.IPConfigurationStatus{ IPAddress: ipconfig.IPAddress, @@ -166,7 +167,7 @@ func UpdatePodIpConfigState(t *testing.T, svc *HTTPRestService, ipconfigs map[st func TestIPAMGetAvailableIPConfig(t *testing.T) { svc := getTestService() - testState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available) + testState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ testState.ID: testState, } @@ -181,7 +182,7 @@ func TestIPAMGetAvailableIPConfig(t *testing.T) { t.Fatal("Expected IP retrieval to be nil") } - desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Allocated) + desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, 0) desiredState.OrchestratorContext = b if reflect.DeepEqual(desiredState, actualstate) != true { @@ -195,8 +196,8 @@ func TestIPAMGetNextAvailableIPConfig(t *testing.T) { // Add already allocated pod ip to state svc.PodIPIDByOrchestratorContext[testPod1Info.GetOrchestratorContextKey()] = testPod1GUID - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) - state2 := NewPodState(testIP2, 24, testPod2GUID, testNCID, cns.Available) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) + state2 := NewPodState(testIP2, 24, testPod2GUID, testNCID, cns.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, @@ -216,7 +217,7 @@ func TestIPAMGetNextAvailableIPConfig(t *testing.T) { t.Fatalf("Expected IP retrieval to be nil: %+v", err) } // want second available Pod IP State as first has been allocated - desiredState, _ := NewPodStateWithOrchestratorContext(testIP2, 24, testPod2GUID, testNCID, cns.Allocated, testPod2Info) + desiredState, _ := NewPodStateWithOrchestratorContext(testIP2, testPod2GUID, testNCID, cns.Allocated, 24, 0, testPod2Info) if reflect.DeepEqual(desiredState, actualstate) != true { t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", desiredState, actualstate) @@ -227,7 +228,7 @@ func TestIPAMGetAlreadyAllocatedIPConfigForSamePod(t *testing.T) { svc := getTestService() // Add Allocated Pod IP to state - testState, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + testState, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) ipconfigs := map[string]cns.IPConfigurationStatus{ testState.ID: testState, } @@ -245,7 +246,7 @@ func TestIPAMGetAlreadyAllocatedIPConfigForSamePod(t *testing.T) { t.Fatalf("Expected not error: %+v", err) } - desiredState, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + desiredState, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) if reflect.DeepEqual(desiredState, actualstate) != true { t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", desiredState, actualstate) @@ -256,7 +257,7 @@ func TestIPAMAttemptToRequestIPNotFoundInPool(t *testing.T) { svc := getTestService() // Add Available Pod IP to state - testState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available) + testState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ testState.ID: testState, } @@ -281,7 +282,7 @@ func TestIPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T) { svc := getTestService() // Add Available Pod IP to state - testState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available) + testState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ testState.ID: testState, } @@ -301,7 +302,7 @@ func TestIPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T) { t.Fatalf("Expected IP retrieval to be nil: %+v", err) } - desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Allocated) + desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, 0) desiredState.OrchestratorContext = b if reflect.DeepEqual(desiredState, actualstate) != true { @@ -313,7 +314,7 @@ func TestIPAMFailToGetDesiredIPConfigWithAlreadyAllocatedSpecfiedIP(t *testing.T svc := getTestService() // set state as already allocated - testState, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + testState, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) ipconfigs := map[string]cns.IPConfigurationStatus{ testState.ID: testState, } @@ -338,8 +339,8 @@ func TestIPAMFailToGetIPWhenAllIPsAreAllocated(t *testing.T) { svc := getTestService() // set state as already allocated - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) - state2, _ := NewPodStateWithOrchestratorContext(testIP2, 24, testPod2GUID, testNCID, cns.Allocated, testPod2Info) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) + state2, _ := NewPodStateWithOrchestratorContext(testIP2, testPod2GUID, testNCID, cns.Allocated, 24, 0, testPod2Info) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, @@ -369,7 +370,7 @@ func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) { svc := getTestService() // set state as already allocated - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, } @@ -409,7 +410,7 @@ func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) { t.Fatalf("Expected IP retrieval to be nil: %+v", err) } - desiredState, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + desiredState, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) // want first available Pod IP State desiredState.IPAddress = desiredIpAddress desiredState.OrchestratorContext = b @@ -422,7 +423,7 @@ func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) { func TestIPAMReleaseIPIdempotency(t *testing.T) { svc := getTestService() // set state as already allocated - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, } @@ -448,7 +449,7 @@ func TestIPAMReleaseIPIdempotency(t *testing.T) { func TestIPAMAllocateIPIdempotency(t *testing.T) { svc := getTestService() // set state as already allocated - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, @@ -468,9 +469,9 @@ func TestIPAMAllocateIPIdempotency(t *testing.T) { func TestAvailableIPConfigs(t *testing.T) { svc := getTestService() - state1 := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available) - state2 := NewPodState(testIP2, 24, testPod2GUID, testNCID, cns.Available) - state3 := NewPodState(testIP3, 24, testPod3GUID, testNCID, cns.Available) + state1 := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Available, 0) + state2 := NewPodState(testIP2, 24, testPod2GUID, testNCID, cns.Available, 0) + state3 := NewPodState(testIP3, 24, testPod3GUID, testNCID, cns.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, @@ -505,7 +506,7 @@ func TestAvailableIPConfigs(t *testing.T) { availableIps = svc.GetAvailableIPConfigs() validateIpState(t, availableIps, desiredAvailableIps) - desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Allocated) + desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, 0) desiredState.OrchestratorContext = b desiredAllocatedIpConfigs[desiredState.ID] = desiredState allocatedIps = svc.GetAllocatedIPConfigs() @@ -537,7 +538,7 @@ func validateIpState(t *testing.T, actualIps []cns.IPConfigurationStatus, expect func TestIPAMMarkIPCountAsPending(t *testing.T) { svc := getTestService() // set state as already allocated - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Available, testPod1Info) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Available, 24, 0, testPod1Info) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, } @@ -580,8 +581,8 @@ func TestIPAMMarkExistingIPConfigAsPending(t *testing.T) { // Add already allocated pod ip to state svc.PodIPIDByOrchestratorContext[testPod1Info.GetOrchestratorContextKey()] = testPod1GUID - state1, _ := NewPodStateWithOrchestratorContext(testIP1, 24, testPod1GUID, testNCID, cns.Allocated, testPod1Info) - state2 := NewPodState(testIP2, 24, testPod2GUID, testNCID, cns.Available) + state1, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, cns.Allocated, 24, 0, testPod1Info) + state2 := NewPodState(testIP2, 24, testPod2GUID, testNCID, cns.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ state1.ID: state1, diff --git a/cns/restserver/util.go b/cns/restserver/util.go index 645aa06d62..2b635680e6 100644 --- a/cns/restserver/util.go +++ b/cns/restserver/util.go @@ -237,9 +237,9 @@ func (service *HTTPRestService) updateIpConfigsStateUntransacted(req cns.CreateN nmagentNCVersion = service.imdsClient.GetNetworkContainerInfoFromHostWithoutToken() if nmagentNCVersion >= newNCVersion { - service.addIPConfigStateUntransacted(cns.Available, req.NetworkContainerid, newIPConfigs) + service.addIPConfigStateUntransacted(cns.Available, req.NetworkContainerid, newIPConfigs, existingSecondaryIPConfigs) } else { - service.addIPConfigStateUntransacted(cns.PendingProgramming, req.NetworkContainerid, newIPConfigs) + service.addIPConfigStateUntransacted(cns.PendingProgramming, req.NetworkContainerid, newIPConfigs, existingSecondaryIPConfigs) } return 0, "" @@ -248,9 +248,16 @@ func (service *HTTPRestService) updateIpConfigsStateUntransacted(req cns.CreateN // addIPConfigStateUntransacted adds the IPConfigs to the PodIpConfigState map with Available state // If the IP is already added then it will be an idempotent call. Also note, caller will // acquire/release the service lock. -func (service *HTTPRestService) addIPConfigStateUntransacted(newIPCNSStatus, ncId string, ipconfigs map[string]cns.SecondaryIPConfig) { +func (service *HTTPRestService) addIPConfigStateUntransacted(newIPCNSStatus, ncId string, ipconfigs, existingSecondaryIPConfigs map[string]cns.SecondaryIPConfig) { // add ipconfigs to state for ipId, ipconfig := range ipconfigs { + // New secondary IP configs has new NC version however, CNS don't want to override existing IPs'with new NC version + // Set it back to previous NC version if IP already exist. + if existingIPConfig, existsInPreviousIPConfig := existingSecondaryIPConfigs[ipId]; existsInPreviousIPConfig { + ipconfig.NCVersion = existingIPConfig.NCVersion + ipconfigs[ipId] = ipconfig + } + logger.Printf("[Azure-Cns] Set IP %s version to %d", ipconfig.IPAddress, ipconfig.NCVersion) if _, exists := service.PodIPConfigState[ipId]; exists { continue }