func main()

in cns/service/main.go [497:1144]


func main() {
	// Initialize and parse command line arguments.
	acn.ParseArgs(&args, printVersion)

	cniPath := acn.GetArg(acn.OptNetPluginPath).(string)
	cniConfigFile := acn.GetArg(acn.OptNetPluginConfigFile).(string)
	cnsURL := acn.GetArg(acn.OptCnsURL).(string)
	cnsPort := acn.GetArg(acn.OptCnsPort).(string)
	logLevel := acn.GetArg(acn.OptLogLevel).(int)
	logTarget := acn.GetArg(acn.OptLogTarget).(int)
	logDirectory := acn.GetArg(acn.OptLogLocation).(string)

	vers := acn.GetArg(acn.OptVersion).(bool)
	createDefaultExtNetworkType := acn.GetArg(acn.OptCreateDefaultExtNetworkType).(string)
	telemetryEnabled := acn.GetArg(acn.OptTelemetry).(bool)
	httpConnectionTimeout := acn.GetArg(acn.OptHttpConnectionTimeout).(int)
	httpResponseHeaderTimeout := acn.GetArg(acn.OptHttpResponseHeaderTimeout).(int)
	storeFileLocation := acn.GetArg(acn.OptStoreFileLocation).(string)
	privateEndpoint := acn.GetArg(acn.OptPrivateEndpoint).(string)
	infravnet := acn.GetArg(acn.OptInfrastructureNetworkID).(string)
	nodeID := acn.GetArg(acn.OptNodeID).(string)
	clientDebugCmd := acn.GetArg(acn.OptDebugCmd).(string)
	clientDebugArg := acn.GetArg(acn.OptDebugArg).(string)
	cmdLineConfigPath := acn.GetArg(acn.OptCNSConfigPath).(string)
	telemetryDaemonEnabled := acn.GetArg(acn.OptTelemetryService).(bool)
	cniConflistFilepathArg := acn.GetArg(acn.OptCNIConflistFilepath).(string)
	cniConflistScenarioArg := acn.GetArg(acn.OptCNIConflistScenario).(string)

	if vers {
		printVersion()
		os.Exit(0)
	}

	// Initialize CNS.
	var (
		err                error
		config             common.ServiceConfig
		endpointStateStore store.KeyValueStore
	)

	config.Version = version
	config.Name = name
	// Create a channel to receive unhandled errors from CNS.
	config.ErrChan = rootErrCh

	// Create logging provider.
	logger.InitLogger(name, logLevel, logTarget, logDirectory)

	if clientDebugCmd != "" {
		err := cnscli.HandleCNSClientCommands(rootCtx, clientDebugCmd, clientDebugArg)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		os.Exit(0)
	}

	if !telemetryEnabled {
		logger.Errorf("[Azure CNS] Cannot disable telemetry via cmdline. Update cns_config.json to disable telemetry.")
	}

	cnsconfig, err := configuration.ReadConfig(cmdLineConfigPath)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			logger.Warnf("config file does not exist, using default")
			cnsconfig = &configuration.CNSConfig{}
		} else {
			logger.Errorf("fatal: failed to read cns config: %v", err)
			os.Exit(1)
		}
	}
	configuration.SetCNSConfigDefaults(cnsconfig)

	disableTelemetry := cnsconfig.TelemetrySettings.DisableAll
	if !disableTelemetry {
		ts := cnsconfig.TelemetrySettings
		aiConfig := aitelemetry.AIConfig{
			AppName:                      name,
			AppVersion:                   version,
			BatchSize:                    ts.TelemetryBatchSizeBytes,
			BatchInterval:                ts.TelemetryBatchIntervalInSecs,
			RefreshTimeout:               ts.RefreshIntervalInSecs,
			DisableMetadataRefreshThread: ts.DisableMetadataRefreshThread,
			DebugMode:                    ts.DebugMode,
		}

		if aiKey := cnsconfig.TelemetrySettings.AppInsightsInstrumentationKey; aiKey != "" {
			logger.InitAIWithIKey(aiConfig, aiKey, ts.DisableTrace, ts.DisableMetric, ts.DisableEvent)
		} else {
			logger.InitAI(aiConfig, ts.DisableTrace, ts.DisableMetric, ts.DisableEvent)
		}

		if cnsconfig.TelemetrySettings.ConfigSnapshotIntervalInMins > 0 {
			go metric.SendCNSConfigSnapshot(rootCtx, cnsconfig)
		}
	}
	logger.Printf("[Azure CNS] Using config: %+v", cnsconfig)

	_, envEnableConflistGeneration := os.LookupEnv(envVarEnableCNIConflistGeneration)
	var conflistGenerator restserver.CNIConflistGenerator
	if cnsconfig.EnableCNIConflistGeneration || envEnableConflistGeneration {
		conflistFilepath := cnsconfig.CNIConflistFilepath
		if cniConflistFilepathArg != "" {
			// allow the filepath to get overidden by command line arg
			conflistFilepath = cniConflistFilepathArg
		}
		writer, newWriterErr := acnfs.NewAtomicWriter(conflistFilepath)
		if newWriterErr != nil {
			logger.Errorf("unable to create atomic writer to generate cni conflist: %v", newWriterErr)
			os.Exit(1)
		}

		// allow the scenario to get overridden by command line arg
		scenarioString := cnsconfig.CNIConflistScenario
		if cniConflistScenarioArg != "" {
			scenarioString = cniConflistScenarioArg
		}

		switch scenario := cniConflistScenario(scenarioString); scenario {
		case scenarioV4Overlay:
			conflistGenerator = &cniconflist.V4OverlayGenerator{Writer: writer}
		case scenarioDualStackOverlay:
			conflistGenerator = &cniconflist.DualStackOverlayGenerator{Writer: writer}
		case scenarioOverlay:
			conflistGenerator = &cniconflist.OverlayGenerator{Writer: writer}
		case scenarioCilium:
			conflistGenerator = &cniconflist.CiliumGenerator{Writer: writer}
		case scenarioSWIFT:
			conflistGenerator = &cniconflist.SWIFTGenerator{Writer: writer}
		default:
			logger.Errorf("unable to generate cni conflist for unknown scenario: %s", scenario)
			os.Exit(1)
		}
	}

	// configure zap logger
	zconfig := zap.NewProductionConfig()
	zconfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	if z, err = zconfig.Build(); err != nil {
		fmt.Printf("failed to create logger: %v", err)
		os.Exit(1)
	}

	// start the healthz/readyz/metrics server
	readyCh := make(chan any)
	readyChecker := healthz.CheckHandler{
		Checker: healthz.Checker(func(*http.Request) error {
			select {
			default:
				return errors.New("not ready")
			case <-readyCh:
			}
			return nil
		}),
	}

	healthzHandler, err := healthserver.NewHealthzHandlerWithChecks(&healthserver.Config{PingAPIServer: cnsconfig.EnableAPIServerHealthPing})
	if err != nil {
		logger.Errorf("unable to initialize a healthz handler: %v", err)
		return
	}
	go healthserver.Start(z, cnsconfig.MetricsBindAddress, healthzHandler, readyChecker)

	nmaConfig, err := nmagent.NewConfig(cnsconfig.WireserverIP)
	if err != nil {
		logger.Errorf("[Azure CNS] Failed to produce NMAgent config from the supplied wireserver ip: %v", err)
		return
	}

	nmaClient, err := nmagent.NewClient(nmaConfig)
	if err != nil {
		logger.Errorf("[Azure CNS] Failed to start nmagent client due to error: %v", err)
		return
	}

	// copy ChannelMode from cnsconfig to HTTPRemoteRestService config
	config.ChannelMode = cnsconfig.ChannelMode
	if cnsconfig.ChannelMode == cns.Managed {
		privateEndpoint = cnsconfig.ManagedSettings.PrivateEndpoint
		infravnet = cnsconfig.ManagedSettings.InfrastructureNetworkID
		nodeID = cnsconfig.ManagedSettings.NodeID
	}
	if isManaged, ok := acn.GetArg(acn.OptManaged).(bool); ok && isManaged {
		config.ChannelMode = cns.Managed
	}

	homeAzMonitor := restserver.NewHomeAzMonitor(nmaClient, time.Duration(cnsconfig.AZRSettings.PopulateHomeAzCacheRetryIntervalSecs)*time.Second)
	// homeAz monitor is only required when there is a direct channel between DNC and CNS.
	// This will prevent the monitor from unnecessarily calling NMA APIs for other scenarios such as AKS-swift, swiftv2
	if cnsconfig.ChannelMode == cns.Direct {
		homeAzMonitor.Start()
		defer homeAzMonitor.Stop()
	}

	if telemetryDaemonEnabled {
		logger.Printf("CNI Telemetry is enabled")
		go startTelemetryService(rootCtx)
	}

	// Log platform information.
	logger.Printf("Running on %v", platform.GetOSInfo())

	err = platform.CreateDirectory(storeFileLocation)
	if err != nil {
		logger.Errorf("Failed to create File Store directory %s, due to Error:%v", storeFileLocation, err.Error())
		return
	}

	lockclient, err := processlock.NewFileLock(platform.CNILockPath + name + store.LockExtension)
	if err != nil {
		logger.Printf("Error initializing file lock:%v", err)
		return
	}

	// Create the key value store.
	storeFileName := storeFileLocation + name + ".json"
	config.Store, err = store.NewJsonFileStore(storeFileName, lockclient, nil)
	if err != nil {
		logger.Errorf("Failed to create store file: %s, due to error %v\n", storeFileName, err)
		return
	}

	// Initialize endpoint state store if cns is managing endpoint state.
	if cnsconfig.ManageEndpointState {
		logger.Printf("[Azure CNS] Configured to manage endpoints state")
		endpointStoreLock, err := processlock.NewFileLock(platform.CNILockPath + endpointStoreName + store.LockExtension) // nolint
		if err != nil {
			logger.Printf("Error initializing endpoint state file lock:%v", err)
			return
		}
		defer endpointStoreLock.Unlock() // nolint

		err = platform.CreateDirectory(endpointStorePath)
		if err != nil {
			logger.Errorf("Failed to create File Store directory %s, due to Error:%v", storeFileLocation, err.Error())
			return
		}
		// Create the key value store.
		storeFileName := endpointStorePath + endpointStoreName + ".json"
		logger.Printf("EndpointStoreState path is %s", storeFileName)
		endpointStateStore, err = store.NewJsonFileStore(storeFileName, endpointStoreLock, nil)
		if err != nil {
			logger.Errorf("Failed to create endpoint state store file: %s, due to error %v\n", storeFileName, err)
			return
		}
	}

	wsProxy := wireserver.Proxy{
		Host:       cnsconfig.WireserverIP,
		HTTPClient: &http.Client{},
	}

	wsclient := &wireserver.Client{
		HostPort:   cnsconfig.WireserverIP,
		HTTPClient: &http.Client{},
		Logger:     logger.Log,
	}

	imdsClient := imds.NewClient()
	httpRemoteRestService, err := restserver.NewHTTPRestService(&config, wsclient, &wsProxy, &restserver.IPtablesProvider{}, nmaClient,
		endpointStateStore, conflistGenerator, homeAzMonitor, imdsClient)
	if err != nil {
		logger.Errorf("Failed to create CNS object, err:%v.\n", err)
		return
	}

	// Set CNS options.
	httpRemoteRestService.SetOption(acn.OptCnsURL, cnsURL)
	httpRemoteRestService.SetOption(acn.OptCnsPort, cnsPort)
	httpRemoteRestService.SetOption(acn.OptNetPluginPath, cniPath)
	httpRemoteRestService.SetOption(acn.OptNetPluginConfigFile, cniConfigFile)
	httpRemoteRestService.SetOption(acn.OptCreateDefaultExtNetworkType, createDefaultExtNetworkType)
	httpRemoteRestService.SetOption(acn.OptHttpConnectionTimeout, httpConnectionTimeout)
	httpRemoteRestService.SetOption(acn.OptHttpResponseHeaderTimeout, httpResponseHeaderTimeout)
	httpRemoteRestService.SetOption(acn.OptProgramSNATIPTables, cnsconfig.ProgramSNATIPTables)
	httpRemoteRestService.SetOption(acn.OptManageEndpointState, cnsconfig.ManageEndpointState)

	// Create default ext network if commandline option is set
	if len(strings.TrimSpace(createDefaultExtNetworkType)) > 0 {
		if err := hnsclient.CreateDefaultExtNetwork(createDefaultExtNetworkType); err == nil {
			logger.Printf("[Azure CNS] Successfully created default ext network")
		} else {
			logger.Printf("[Azure CNS] Failed to create default ext network due to error: %v", err)
			return
		}
	}

	logger.Printf("[Azure CNS] Initialize HTTPRemoteRestService")
	if httpRemoteRestService != nil {
		if cnsconfig.UseHTTPS {
			config.TLSSettings = localtls.TlsSettings{
				TLSSubjectName:                     cnsconfig.TLSSubjectName,
				TLSCertificatePath:                 cnsconfig.TLSCertificatePath,
				TLSPort:                            cnsconfig.TLSPort,
				KeyVaultURL:                        cnsconfig.KeyVaultSettings.URL,
				KeyVaultCertificateName:            cnsconfig.KeyVaultSettings.CertificateName,
				MSIResourceID:                      cnsconfig.MSISettings.ResourceID,
				KeyVaultCertificateRefreshInterval: time.Duration(cnsconfig.KeyVaultSettings.RefreshIntervalInHrs) * time.Hour,
				UseMTLS:                            cnsconfig.UseMTLS,
				MinTLSVersion:                      cnsconfig.MinTLSVersion,
			}
		}

		err = httpRemoteRestService.Init(&config)
		if err != nil {
			logger.Errorf("Failed to init HTTPService, err:%v.\n", err)
			return
		}
	}

	// Setting the remote ARP MAC address to 12-34-56-78-9a-bc on windows for external traffic if HNS is enabled
	err = platform.SetSdnRemoteArpMacAddress(rootCtx)
	if err != nil {
		logger.Errorf("Failed to set remote ARP MAC address: %v", err)
		return
	}

	// We are only setting the PriorityVLANTag in 'cns.Direct' mode, because it neatly maps today, to 'isUsingMultitenancy'
	// In the future, we would want to have a better CNS flag, to explicitly say, this CNS is using multitenancy
	if cnsconfig.ChannelMode == cns.Direct {
		// Set Mellanox adapter's PriorityVLANTag value to 3 if adapter exists
		// reg key value for PriorityVLANTag = 3  --> Packet priority and VLAN enabled
		// for more details goto https://docs.nvidia.com/networking/display/winof2v230/Configuring+the+Driver+Registry+Keys#ConfiguringtheDriverRegistryKeys-GeneralRegistryKeysGeneralRegistryKeys
		if platform.HasMellanoxAdapter() {
			go platform.MonitorAndSetMellanoxRegKeyPriorityVLANTag(rootCtx, cnsconfig.MellanoxMonitorIntervalSecs)
		}

		// if swiftv2 scenario is enabled, we need to initialize the ServiceFabric(standalone) swiftv2 middleware to process IPConfigsRequests
		// RequestIPConfigs() will be invoked only for swiftv2 or swiftv1 k8s scenarios. For swiftv1 direct mode different api will be invoked.
		// So initializing this middleware always under direct mode should not affect any other scenarios
		swiftV2Middleware := &middlewares.StandaloneSWIFTv2Middleware{}
		httpRemoteRestService.AttachIPConfigsHandlerMiddleware(swiftV2Middleware)
	}

	// Initialze state in if CNS is running in CRD mode
	// State must be initialized before we start HTTPRestService
	if config.ChannelMode == cns.CRD {
		// Add APIServer FQDN to Log metadata
		logger.Log.SetAPIServer(os.Getenv("KUBERNETES_SERVICE_HOST"))

		// Check the CNI statefile mount, and if the file is empty
		// stub an empty JSON object
		if err := cnireconciler.WriteObjectToCNIStatefile(); err != nil {
			logger.Errorf("Failed to write empty object to CNI state: %v", err)
			return
		}

		// We might be configured to reinitialize state from the CNI instead of the apiserver.
		// If so, we should check that the CNI is new enough to support the state commands,
		// otherwise we fall back to the existing behavior.
		if cnsconfig.InitializeFromCNI {
			var isGoodVer bool
			isGoodVer, err = cnireconciler.IsDumpStateVer()
			if err != nil {
				logger.Errorf("error checking CNI ver: %v", err)
			}

			// override the prior config flag with the result of the ver check.
			cnsconfig.InitializeFromCNI = isGoodVer

			if cnsconfig.InitializeFromCNI {
				// Set the PodInfoVersion by initialization type, so that the
				// PodInfo maps use the correct key schema
				cns.GlobalPodInfoScheme = cns.InterfaceIDPodInfoScheme
			}
		}
		// If cns manageendpointstate is true, then cns maintains its own state and reconciles from it.
		// in this case, cns maintains state with containerid as key and so in-memory cache can lookup
		// and update based on container id.
		if cnsconfig.ManageEndpointState {
			cns.GlobalPodInfoScheme = cns.InfraIDPodInfoScheme
		}

		logger.Printf("Set GlobalPodInfoScheme %v (InitializeFromCNI=%t)", cns.GlobalPodInfoScheme, cnsconfig.InitializeFromCNI)

		err = InitializeCRDState(rootCtx, httpRemoteRestService, cnsconfig)
		if err != nil {
			logger.Errorf("Failed to start CRD Controller, err:%v.\n", err)
			return
		}

		if cnsconfig.EnableSwiftV2 {
			// No-op for linux, mapping is set for windows in aks swiftv2 scenario
			logger.Printf("Fetching backend nics for the node")
			if err = httpRemoteRestService.SavePnpIDMacaddressMapping(rootCtx); err != nil {
				logger.Errorf("Failed to fetch PnpIDMacaddress mapping: %v", err)
			}

			// No-op for linux, setting primary macaddress if VF is enabled on the nics for aks swiftv2 windows
			logger.Printf("Setting VF for accelnet nics if feature is enabled (only on windows VM & swiftV2 scenario)")
			if err = httpRemoteRestService.SetVFForAccelnetNICs(); err != nil {
				logger.Errorf("Failed to set VF for accelnet NICs: %v", err)
			}
		}
	}

	// AzureHost channelmode indicates Nodesubnet. IPs are to be fetched from NMagent.
	if config.ChannelMode == cns.AzureHost {
		if !cnsconfig.ManageEndpointState {
			logger.Errorf("ManageEndpointState must be set to true for AzureHost mode")
			return
		}

		// If cns manageendpointstate is true, then cns maintains its own state and reconciles from it.
		// in this case, cns maintains state with containerid as key and so in-memory cache can lookup
		// and update based on container id.
		cns.GlobalPodInfoScheme = cns.InfraIDPodInfoScheme

		var podInfoByIPProvider cns.PodInfoByIPProvider
		podInfoByIPProvider, err = getPodInfoByIPProvider(rootCtx, cnsconfig, httpRemoteRestService, nil, "")
		if err != nil {
			logger.Errorf("[Azure CNS] Failed to get PodInfoByIPProvider: %v", err)
			return
		}

		err = httpRemoteRestService.InitializeNodeSubnet(rootCtx, podInfoByIPProvider)
		if err != nil {
			logger.Errorf("[Azure CNS] Failed to initialize node subnet: %v", err)
			return
		}
	}

	// Initialize multi-tenant controller if the CNS is running in MultiTenantCRD mode.
	// It must be started before we start HTTPRemoteRestService.
	if config.ChannelMode == cns.MultiTenantCRD {
		err = InitializeMultiTenantController(rootCtx, httpRemoteRestService, *cnsconfig)
		if err != nil {
			logger.Errorf("Failed to start multiTenantController, err:%v.\n", err)
			return
		}
	}

	if cnsconfig.EnableSwiftV2 && cnsconfig.EnableK8sDevicePlugin {
		// Create device plugin manager instance
		pluginManager := deviceplugin.NewPluginManager(z)
		pluginManager.AddPlugin(mtv1alpha1.DeviceTypeVnetNIC, initialVnetNICCount)
		pluginManager.AddPlugin(mtv1alpha1.DeviceTypeInfiniBandNIC, initialIBNICCount)

		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()

		// Start device plugin manager in a separate goroutine
		go func() {
			retryCount := 0
			ticker := time.NewTicker(defaultDevicePluginRetryInterval)
			// Ensure the ticker is stopped on exit
			defer ticker.Stop()
			for {
				select {
				case <-ctx.Done():
					z.Info("Context canceled, stopping plugin manager")
					return
				case <-ticker.C:
					if pluginErr := pluginManager.Run(ctx); pluginErr != nil {
						z.Error("plugin manager exited with error", zap.Error(pluginErr))
						retryCount++
						// Implementing a basic circuit breaker
						if retryCount >= defaultDevicePluginMaxRetryCount {
							z.Error("Max retries reached, stopping plugin manager")
							return
						}
					} else {
						return
					}
				}
			}
		}()

		// go routine to poll node info crd and update device counts
		go func() {
			if pollErr := pollNodeInfoCRDAndUpdatePlugin(ctx, z, pluginManager); pollErr != nil {
				z.Error("Error in pollNodeInfoCRDAndUpdatePlugin", zap.Error(pollErr))
			}
		}()
	}

	// Conditionally initialize and start the gRPC server
	if cnsconfig.GRPCSettings.Enable {
		// Define gRPC server settings
		settings := grpc.ServerSettings{
			IPAddress: cnsconfig.GRPCSettings.IPAddress,
			Port:      cnsconfig.GRPCSettings.Port,
		}

		// Initialize CNS service
		cnsService := &grpc.CNS{Logger: z}

		// Create a new gRPC server
		server, grpcErr := grpc.NewServer(settings, cnsService, z)
		if grpcErr != nil {
			logger.Errorf("[Listener] Could not initialize gRPC server: %v", grpcErr)
			return
		}

		// Start the gRPC server
		go func() {
			if grpcErr := server.Start(); grpcErr != nil {
				logger.Errorf("[Listener] Could not start gRPC server: %v", grpcErr)
				return
			}
		}()
	}

	// if user provides cns url by -c option, then only start HTTP remote server using this url

	logger.Printf("[Azure CNS] Start HTTP Remote server")
	if httpRemoteRestService != nil {
		if cnsconfig.EnablePprof {
			httpRemoteRestService.RegisterPProfEndpoints()
		}

		err = httpRemoteRestService.Start(&config)
		if err != nil {
			logger.Errorf("Failed to start CNS, err:%v.\n", err)
			return
		}

	}

	// if user does not provide cns url by -c option, then start http local server
	// TODO: we will deprecated -c option in next phase and start local server in any case
	if config.Server.EnableLocalServer {
		logger.Printf("[Azure CNS] Start HTTP local server")

		var localServerURL string
		if config.Server.Port != "" {
			localServerURL = fmt.Sprintf(defaultLocalServerIP + ":" + config.Server.Port)
		} else {
			localServerURL = fmt.Sprintf(defaultLocalServerIP + ":" + defaultLocalServerPort)
		}

		httpLocalRestService := restserverv2.New(httpRemoteRestService)
		if httpLocalRestService != nil {
			go func() {
				err = httpLocalRestService.Start(rootCtx, localServerURL)
				if err != nil {
					logger.Errorf("Failed to start local echo server, err:%v.\n", err)
					return
				}
			}()
		}
	}

	if cnsconfig.EnableAsyncPodDelete {
		// Start fs watcher here
		z.Info("AsyncPodDelete is enabled")
		logger.Printf("AsyncPodDelete is enabled")
		cnsclient, err := cnsclient.New("", cnsReqTimeout) // nolint
		if err != nil {
			z.Error("failed to create cnsclient", zap.Error(err))
		}
		go func() {
			_ = retry.Do(func() error {
				z.Info("starting fsnotify watcher to process missed Pod deletes")
				logger.Printf("starting fsnotify watcher to process missed Pod deletes")
				var endpointCleanup fsnotify.ReleaseIPsClient = cnsclient
				// using endpointmanager implmentation for stateless CNI sceanrio to remove HNS endpoint alongside the IPs
				if cnsconfig.IsStalessCNIWindows() {
					endpointCleanup = endpointmanager.WithPlatformReleaseIPsManager(cnsclient)
				}
				w, err := fsnotify.New(endpointCleanup, cnsconfig.AsyncPodDeletePath, z)
				if err != nil {
					z.Error("failed to create fsnotify watcher", zap.Error(err))
					return errors.Wrap(err, "failed to create fsnotify watcher, will retry")
				}
				if err := w.Start(rootCtx); err != nil {
					z.Error("failed to start fsnotify watcher, will retry", zap.Error(err))
					return errors.Wrap(err, "failed to start fsnotify watcher, will retry")
				}
				return nil
			}, retry.DelayType(retry.BackOffDelay), retry.Attempts(0), retry.Context(rootCtx)) // infinite cancellable exponential backoff retrier
		}()
	}

	if !disableTelemetry {
		go metric.SendHeartBeat(rootCtx, time.Minute*time.Duration(cnsconfig.TelemetrySettings.HeartBeatIntervalInMins), homeAzMonitor, cnsconfig.ChannelMode)
		go httpRemoteRestService.SendNCSnapShotPeriodically(rootCtx, cnsconfig.TelemetrySettings.SnapshotIntervalInMins)
	}

	// If CNS is running on managed DNC mode
	if config.ChannelMode == cns.Managed {
		if privateEndpoint == "" || infravnet == "" || nodeID == "" {
			logger.Errorf("[Azure CNS] Missing required values to run in managed mode: PrivateEndpoint: %s InfrastructureNetworkID: %s NodeID: %s",
				privateEndpoint,
				infravnet,
				nodeID)
			return
		}

		httpRemoteRestService.SetOption(acn.OptPrivateEndpoint, privateEndpoint)
		httpRemoteRestService.SetOption(acn.OptInfrastructureNetworkID, infravnet)
		httpRemoteRestService.SetOption(acn.OptNodeID, nodeID)

		// Passing in the default http client that already implements Do function
		standardClient := http.DefaultClient

		registerErr := registerNode(rootCtx, standardClient, httpRemoteRestService, privateEndpoint, infravnet, nodeID, nmaClient)
		if registerErr != nil {
			logger.Errorf("[Azure CNS] Registering Node failed with error: %v PrivateEndpoint: %s InfrastructureNetworkID: %s NodeID: %s",
				registerErr,
				privateEndpoint,
				infravnet,
				nodeID)
			return
		}
		go func(ep, vnet, node string) {
			// Periodically poll DNC for node updates
			tickerChannel := time.Tick(time.Duration(cnsconfig.ManagedSettings.NodeSyncIntervalInSeconds) * time.Second)
			for {
				<-tickerChannel
				httpRemoteRestService.SyncNodeStatus(ep, vnet, node, json.RawMessage{})
			}
		}(privateEndpoint, infravnet, nodeID)
	}

	if config.ChannelMode == cns.AzureHost {
		// at this point, rest service is running. We can now start serving new requests. So call StartNodeSubnet, which
		// will fetch secondary IPs and generate conflist. Do not move this all before rest service start - this will cause
		// CNI to start sending requests, and if the service doesn't start successfully, the requests will fail.
		httpRemoteRestService.StartNodeSubnet(rootCtx)
	}

	// mark the service as "ready"
	close(readyCh)
	// block until process exiting
	<-rootCtx.Done()

	if len(strings.TrimSpace(createDefaultExtNetworkType)) > 0 {
		if err := hnsclient.DeleteDefaultExtNetwork(); err == nil {
			logger.Printf("[Azure CNS] Successfully deleted default ext network")
		} else {
			logger.Printf("[Azure CNS] Failed to delete default ext network due to error: %v", err)
		}
	}

	logger.Printf("stop cns service")
	// Cleanup.
	if httpRemoteRestService != nil {
		httpRemoteRestService.Stop()
	}

	if err = lockclient.Unlock(); err != nil {
		logger.Errorf("lockclient cns unlock error:%v", err)
	}

	logger.Printf("CNS exited")
	logger.Close()
}