func (p *IfPlugin) Close() error {
	p.cancel()
	p.wg.Wait()
	return safeclose.Close(
		p.dhcpDescriptor,
		p.ifStateUpdater,
		p.watchStatusReg)
}
Close stops all go routines.
package ifplugin
import (
	"context"
	"sync"
	"time"
	"github.com/pkg/errors"
	"go.ligato.io/cn-infra/v2/datasync"
	"go.ligato.io/cn-infra/v2/health/statuscheck"
	"go.ligato.io/cn-infra/v2/idxmap"
	"go.ligato.io/cn-infra/v2/infra"
	"go.ligato.io/cn-infra/v2/servicelabel"
	"go.ligato.io/cn-infra/v2/utils/safeclose"
	"go.ligato.io/vpp-agent/v3/plugins/govppmux"
	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler"
	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
	linux_ifcalls "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"
	"go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin"
	"go.ligato.io/vpp-agent/v3/plugins/netalloc"
	vppclient "go.ligato.io/vpp-agent/v3/plugins/vpp"
	"go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor"
	"go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx"
	"go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls"
	"go.ligato.io/vpp-agent/v3/proto/ligato/vpp"
	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls/vpp2005"
	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls/vpp2009"
	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls/vpp2101"
	_ "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls/vpp2106"
)
func init() {
	kvscheduler.AddNonRetryableError(vppclient.ErrPluginDisabled)
	kvscheduler.AddNonRetryableError(vppcalls.ErrIPIPUnsupported)
}
const goRoutineCount = 10
type IfPlugin struct {
	Deps
	ifHandler      vppcalls.InterfaceVppAPI
	linuxIfHandler linux_ifcalls.NetlinkAPIRead
	intfIndex ifaceidx.IfaceMetadataIndex
	dhcpIndex idxmap.NamedMapping
	linkStateDescriptor *descriptor.LinkStateDescriptor
	dhcpDescriptor      *descriptor.DHCPDescriptor
	defaultMtu uint32
	publishStats     bool
	publishLock      sync.Mutex
	statusCheckReg   bool
	watchStatusReg   datasync.WatchRegistration
	resyncStatusChan chan datasync.ResyncEvent
	ifStateChan      chan *interfaces.InterfaceNotification
	ifStateUpdater   *InterfaceStateUpdater
	ctx    context.Context
	cancel context.CancelFunc
	wg     sync.WaitGroup
}
type Deps struct {
	infra.PluginDeps
	KVScheduler  kvs.KVScheduler
	VPP          govppmux.API
	ServiceLabel servicelabel.ReaderAPI
	AddrAlloc    netalloc.AddressAllocator
	LinuxIfPlugin descriptor.LinuxPluginAPI
	NsPlugin      nsplugin.API
	StatusCheck       statuscheck.PluginStatusWriter
	PublishErrors     datasync.KeyProtoValWriter             
	Watcher           datasync.KeyValProtoWatcher            
	NotifyStates      datasync.KeyProtoValWriter             
	PublishStatistics datasync.KeyProtoValWriter             
	DataSyncs         map[string]datasync.KeyProtoValWriter  
	PushNotification  func(notification *vpp.Notification)
}
func (p *IfPlugin) Init() (err error) {
	p.ctx, p.cancel = context.WithCancel(context.Background())
	if err := p.fromConfigFile(); err != nil {
		return err
	}
	p.publishStats = p.PublishStatistics != nil || p.NotifyStates != nil
	p.fixNilPointers()
	p.ifHandler = vppcalls.CompatibleInterfaceVppHandler(p.VPP, p.Log)
	if p.ifHandler == nil {
		return errors.New("interface VPP handler is not available")
	}
	if p.LinuxIfPlugin != nil {
		p.linuxIfHandler = linux_ifcalls.NewNetLinkHandler(
			p.NsPlugin,
			p.LinuxIfPlugin.GetInterfaceIndex(),
			p.ServiceLabel.GetAgentPrefix(),
			goRoutineCount, p.Log,
		)
	}
	ifaceDescriptor, ifaceDescrCtx := descriptor.NewInterfaceDescriptor(p.ifHandler,
		p.AddrAlloc, p.defaultMtu, p.linuxIfHandler, p.LinuxIfPlugin, p.NsPlugin, p.Log)
	err = p.KVScheduler.RegisterKVDescriptor(ifaceDescriptor)
	if err != nil {
		return err
	}
	var withIndex bool
	metadataMap := p.KVScheduler.GetMetadataMap(ifaceDescriptor.Name)
	p.intfIndex, withIndex = metadataMap.(ifaceidx.IfaceMetadataIndex)
	if !withIndex {
		return errors.New("missing index with interface metadata")
	}
	ifaceDescrCtx.SetInterfaceIndex(p.intfIndex)
	var (
		linkStateDescriptor *kvs.KVDescriptor
		dhcpDescriptor      *kvs.KVDescriptor
	)
	dhcpDescriptor, p.dhcpDescriptor = descriptor.NewDHCPDescriptor(p.KVScheduler,
		p.ifHandler, p.intfIndex, p.Log)
	linkStateDescriptor, p.linkStateDescriptor = descriptor.NewLinkStateDescriptor(
		p.KVScheduler, p.ifHandler, p.intfIndex, p.Log)
	rxModeDescriptor := descriptor.NewRxModeDescriptor(p.ifHandler, p.intfIndex, p.Log)
	rxPlacementDescriptor := descriptor.NewRxPlacementDescriptor(p.ifHandler, p.intfIndex, p.Log)
	addrDescriptor := descriptor.NewInterfaceAddressDescriptor(p.ifHandler, p.AddrAlloc, p.intfIndex, p.Log)
	unIfDescriptor := descriptor.NewUnnumberedIfDescriptor(p.ifHandler, p.intfIndex, p.Log)
	bondIfDescriptor, _ := descriptor.NewBondedInterfaceDescriptor(p.ifHandler, p.intfIndex, p.Log)
	vrfDescriptor := descriptor.NewInterfaceVrfDescriptor(p.ifHandler, p.intfIndex, p.Log)
	withAddrDescriptor := descriptor.NewInterfaceWithAddrDescriptor(p.Log)
	spanDescriptor, spanDescriptorCtx := descriptor.NewSpanDescriptor(p.ifHandler, p.Log)
	spanDescriptorCtx.SetInterfaceIndex(p.intfIndex)
	ip6ndDescriptor := descriptor.NewIP6ndDescriptor(p.KVScheduler, p.ifHandler, p.intfIndex, p.Log)
	err = p.KVScheduler.RegisterKVDescriptor(
		dhcpDescriptor,
		linkStateDescriptor,
		rxModeDescriptor,
		rxPlacementDescriptor,
		addrDescriptor,
		unIfDescriptor,
		bondIfDescriptor,
		vrfDescriptor,
		withAddrDescriptor,
		spanDescriptor,
		ip6ndDescriptor,
	)
	if err != nil {
		return err
	}
	p.dhcpIndex = p.KVScheduler.GetMetadataMap(dhcpDescriptor.Name)
	if p.dhcpIndex == nil {
		return errors.New("missing index with DHCP metadata")
	}
	p.dhcpDescriptor.WatchDHCPNotifications(p.ctx)
	if p.publishStats {
		p.resyncStatusChan = make(chan datasync.ResyncEvent)
		p.wg.Add(1)
		go p.watchStatusEvents()
	}
	p.ifStateChan = make(chan *interfaces.InterfaceNotification, 1000)
	p.wg.Add(1)
	go p.publishIfStateEvents()
	p.ifStateUpdater = &InterfaceStateUpdater{}
	var n int
	var t time.Time
	ifNotifHandler := func(state *interfaces.InterfaceNotification) {
		select {
		case p.ifStateChan <- state:
		default:
			if time.Since(t) > time.Second {
				p.Log.Debugf("ifStateChan channel is full (%d)", n)
				n = 0
			} else {
				n++
			}
			t = time.Now()
		}
	}
	err = p.ifStateUpdater.Init(p.ctx, p.Log, p.KVScheduler, p.VPP, p.intfIndex, ifNotifHandler, p.publishStats)
	if err != nil {
		return err
	}
	if p.publishStats {
		if err = p.subscribeWatcher(); err != nil {
			return err
		}
	}
	return nil
}
func (p *IfPlugin) subscribeWatcher() (err error) {
	keyPrefixes := []string{interfaces.StatePrefix}
	p.Log.Debugf("subscribe to %d status prefixes: %v", len(keyPrefixes), keyPrefixes)
	p.watchStatusReg, err = p.Watcher.Watch("vpp-if-state",
		nil, p.resyncStatusChan, keyPrefixes...)
	if err != nil {
		return err
	}
	return nil
}
func (p *IfPlugin) AfterInit() error {
	err := p.ifStateUpdater.AfterInit()
	if err != nil {
		return err
	}
	if p.StatusCheck != nil {
		p.StatusCheck.Register(p.PluginName, nil)
		p.statusCheckReg = true
	}
	return nil
}