func()

in cni/network/invoker_cns.go [88:223]


func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, error) {
	// Parse Pod arguments.
	podInfo := cns.KubernetesPodInfo{
		PodName:      invoker.podName,
		PodNamespace: invoker.podNamespace,
	}

	orchestratorContext, err := json.Marshal(podInfo)
	if err != nil {
		logger.Info(podInfo.PodName)
		return IPAMAddResult{}, errors.Wrap(err, "Failed to unmarshal orchestrator context during add: %w")
	}

	if addConfig.args == nil {
		return IPAMAddResult{}, errEmptyCNIArgs
	}

	ipconfigs := cns.IPConfigsRequest{
		OrchestratorContext: orchestratorContext,
		PodInterfaceID:      GetEndpointID(addConfig.args),
		InfraContainerID:    addConfig.args.ContainerID,
	}

	logger.Info("Requesting IP for pod using ipconfig",
		zap.Any("pod", podInfo),
		zap.Any("ipconfig", ipconfigs))
	response, err := invoker.cnsClient.RequestIPs(context.TODO(), ipconfigs)
	if err != nil {
		if cnscli.IsUnsupportedAPI(err) {
			// If RequestIPs is not supported by CNS, use RequestIPAddress API
			logger.Error("RequestIPs not supported by CNS. Invoking RequestIPAddress API",
				zap.Any("infracontainerid", ipconfigs.InfraContainerID))
			ipconfig := cns.IPConfigRequest{
				OrchestratorContext: orchestratorContext,
				PodInterfaceID:      GetEndpointID(addConfig.args),
				InfraContainerID:    addConfig.args.ContainerID,
			}

			res, errRequestIP := invoker.cnsClient.RequestIPAddress(context.TODO(), ipconfig)
			if errRequestIP != nil {
				// if the old API fails as well then we just return the error
				logger.Error("Failed to request IP address from CNS using RequestIPAddress",
					zap.Any("infracontainerid", ipconfig.InfraContainerID),
					zap.Error(errRequestIP))
				return IPAMAddResult{}, errors.Wrap(errRequestIP, "Failed to get IP address from CNS")
			}
			response = &cns.IPConfigsResponse{
				Response: res.Response,
				PodIPInfo: []cns.PodIpInfo{
					res.PodIpInfo,
				},
			}
		} else {
			logger.Info("Failed to get IP address from CNS",
				zap.Any("response", response))
			return IPAMAddResult{}, errors.Wrap(err, "Failed to get IP address from CNS")
		}
	}

	addResult := IPAMAddResult{interfaceInfo: make(map[string]network.InterfaceInfo)}
	numInterfacesWithDefaultRoutes := 0

	for i := 0; i < len(response.PodIPInfo); i++ {
		info := IPResultInfo{
			podIPAddress:       response.PodIPInfo[i].PodIPConfig.IPAddress,
			ncSubnetPrefix:     response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength,
			ncPrimaryIP:        response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.IPAddress,
			ncGatewayIPAddress: response.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress,
			hostSubnet:         response.PodIPInfo[i].HostPrimaryIPInfo.Subnet,
			hostPrimaryIP:      response.PodIPInfo[i].HostPrimaryIPInfo.PrimaryIP,
			hostGateway:        response.PodIPInfo[i].HostPrimaryIPInfo.Gateway,
			nicType:            response.PodIPInfo[i].NICType,
			macAddress:         response.PodIPInfo[i].MacAddress,
			skipDefaultRoutes:  response.PodIPInfo[i].SkipDefaultRoutes,
			routes:             response.PodIPInfo[i].Routes,
			pnpID:              response.PodIPInfo[i].PnPID,
			endpointPolicies:   response.PodIPInfo[i].EndpointPolicies,
		}

		logger.Info("Received info for pod",
			zap.Any("ipInfo", info),
			zap.Any("podInfo", podInfo))

		//nolint:exhaustive // ignore exhaustive types check
		// Do we want to leverage this lint skip in other places of our code?
		key := invoker.getInterfaceInfoKey(info.nicType, info.macAddress)
		switch info.nicType {
		case cns.NodeNetworkInterfaceFrontendNIC:
			// only handling single v4 PodIPInfo for NodeNetworkInterfaceFrontendNIC and AccelnetNIC at the moment, will have to update once v6 gets added
			if !info.skipDefaultRoutes {
				numInterfacesWithDefaultRoutes++
			}

			// Add secondary interface info from podIPInfo to ipamAddResult
			info.hostSubnet = response.PodIPInfo[i].HostPrimaryIPInfo.Subnet
			info.hostPrimaryIP = response.PodIPInfo[i].HostPrimaryIPInfo.PrimaryIP
			info.hostGateway = response.PodIPInfo[i].HostPrimaryIPInfo.Gateway

			if err := configureSecondaryAddResult(&info, &addResult, &response.PodIPInfo[i].PodIPConfig, key); err != nil {
				return IPAMAddResult{}, err
			}
		case cns.BackendNIC:
			// TODO: check whether setting default route on IB interface
			// handle ipv4 PodIPInfo for BackendNIC
			if err := addBackendNICToResult(&info, &addResult, key); err != nil {
				return IPAMAddResult{}, err
			}
		case cns.InfraNIC, "":
			// if we change from legacy cns, the nicType will be empty, so we assume it is infra nic
			info.nicType = cns.InfraNIC

			// only count dualstack interface once
			_, exist := addResult.interfaceInfo[key]
			if !exist {
				addResult.interfaceInfo[key] = network.InterfaceInfo{}
				if !info.skipDefaultRoutes {
					numInterfacesWithDefaultRoutes++
				}
			}

			overlayMode := (invoker.ipamMode == util.V4Overlay) || (invoker.ipamMode == util.DualStackOverlay) || (invoker.ipamMode == util.Overlay)
			if err := configureDefaultAddResult(&info, &addConfig, &addResult, overlayMode, key); err != nil {
				return IPAMAddResult{}, err
			}
		default:
			logger.Warn("Unknown NIC type received from cns pod ip info", zap.String("nicType", string(info.nicType)))
		}
	}

	// Make sure default routes exist for 1 interface
	if numInterfacesWithDefaultRoutes != expectedNumInterfacesWithDefaultRoutes {
		return IPAMAddResult{}, errInvalidDefaultRouting
	}

	return addResult, nil
}