func GetIdentifiers(args *skel.CmdArgs, nodename string) (*WEPIdentifiers, error) {
	k8sArgs := types.K8sArgs{}
	if err := cnitypes.LoadArgs(args.Args, &k8sArgs); err != nil {
		return nil, err
	}
	logrus.Debugf("Getting WEP identifiers with arguments: %s, for node %s", args.Args, nodename)
	logrus.Debugf("Loaded k8s arguments: %v", k8sArgs)
	epIDs := WEPIdentifiers{}
	epIDs.ContainerID = args.ContainerID
	epIDs.Node = nodename
	epIDs.Endpoint = args.IfName
	if string(k8sArgs.K8S_POD_NAMESPACE) != "" && string(k8sArgs.K8S_POD_NAME) != "" {
		epIDs.Orchestrator = "k8s"
		epIDs.Pod = string(k8sArgs.K8S_POD_NAME)
		epIDs.Namespace = string(k8sArgs.K8S_POD_NAMESPACE)
	} else {
		epIDs.Orchestrator = "cni"
		epIDs.Pod = ""
		epIDs.Namespace = "default"
		CNITestArgs := types.CNITestArgs{}
		if err := cnitypes.LoadArgs(args.Args, &CNITestArgs); err == nil {
			if string(CNITestArgs.CNI_TEST_NAMESPACE) != "" {
				epIDs.Namespace = string(CNITestArgs.CNI_TEST_NAMESPACE)
			}
		}
	}
	return &epIDs, nil
}
GetIdentifiers takes CNI command arguments, and extracts identifiers i.e. pod name, pod namespace,
container ID, endpoint(container interface name) and orchestratorID based on the orchestrator.
package utils
import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"github.com/containernetworking/cni/pkg/skel"
	cnitypes "github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/containernetworking/plugins/pkg/ipam"
	"github.com/sirupsen/logrus"
	lumberjack "gopkg.in/natefinch/lumberjack.v2"
	"github.com/projectcalico/cni-plugin/internal/pkg/azure"
	"github.com/projectcalico/cni-plugin/pkg/types"
	"github.com/projectcalico/libcalico-go/lib/apiconfig"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
	"github.com/projectcalico/libcalico-go/lib/names"
	cnet "github.com/projectcalico/libcalico-go/lib/net"
	"github.com/projectcalico/libcalico-go/lib/options"
)
func Min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func DetermineNodename(conf types.NetConf) (nodename string) {
	if conf.Nodename != "" {
		logrus.Debugf("Read node name from CNI conf: %s", conf.Nodename)
		nodename = conf.Nodename
	} else if nff := nodenameFromFile(conf.NodenameFile); nff != "" {
		logrus.Debugf("Read node name from file: %s", nff)
		nodename = nff
	} else if conf.Hostname != "" {
		nodename = conf.Hostname
		logrus.Warn("Configuration option 'hostname' is deprecated, use 'nodename' instead")
	} else {
		nodename, _ = names.Hostname()
		logrus.Debugf("Read node name from OS Hostname")
	}
	logrus.Debugf("Using node name %s", nodename)
	return
}
func nodenameFromFile(filename string) string {
	if filename == "" {
		filename = "/var/lib/calico/nodename"
	}
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			logrus.Infof("File %s does not exist", filename)
			return ""
		}
		logrus.WithError(err).Errorf("Failed to read %s", filename)
		return ""
	}
	return string(data)
}
func MTUFromFile(filename string) (int, error) {
	if filename == "" {
		filename = "/var/lib/calico/mtu"
	}
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			logrus.Infof("File %s does not exist", filename)
			return 0, nil
		}
		logrus.WithError(err).Errorf("Failed to read %s", filename)
		return 0, err
	}
	return strconv.Atoi(strings.TrimSpace(string(data)))
}
func CreateOrUpdate(ctx context.Context, client client.Interface, wep *api.WorkloadEndpoint) (*api.WorkloadEndpoint, error) {
	if wep.ResourceVersion != "" {
		return client.WorkloadEndpoints().Update(ctx, wep, options.SetOptions{})
	}
	return client.WorkloadEndpoints().Create(ctx, wep, options.SetOptions{})
}
func AddIPAM(conf types.NetConf, args *skel.CmdArgs, logger *logrus.Entry) (*current.Result, error) {
	var an *azure.AzureNetwork
	if conf.IPAM.Type == "azure-vnet-ipam" {
		logger.Info("Configured to use Azure IPAM, check for subnet")
		an = &azure.AzureNetwork{Name: conf.Name}
		if err := an.Load(); err != nil {
			return nil, err
		}
		if err := azure.MutateConfigAdd(args, *an); err != nil {
			return nil, err
		}
	}
	logger.Debugf("Calling IPAM plugin %s", conf.IPAM.Type)
	ipamResult, err := ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
	if err != nil {
		return nil, err
	}
	logger.Debugf("IPAM plugin returned: %+v", ipamResult)
	result, err := current.NewResultFromResult(ipamResult)
	if err != nil {
		return nil, err
	}
	if len(result.IPs) == 0 {
		return nil, errors.New("IPAM plugin returned missing IP config")
	}
	if an != nil {
		subnetPrefix := result.IPs[0].Address
		subnetPrefix.IP = subnetPrefix.IP.Mask(subnetPrefix.Mask)
		an.Subnets = []string{subnetPrefix.String()}
		if err := an.Write(); err != nil {
			return nil, err
		}
		logger.Infof("Stored azure subnet on disk: %s", subnetPrefix)
		var ips []string
		for _, ip := range result.IPs {
			ips = append(ips, ip.Address.IP.String())
		}
		ae := azure.AzureEndpoint{
			Network:     conf.Name,
			ContainerID: args.ContainerID,
			Interface:   args.IfName,
			Addresses:   ips,
		}
		if err := ae.Write(); err != nil {
			return nil, err
		}
	}
	return result, nil
}
func DeleteIPAM(conf types.NetConf, args *skel.CmdArgs, logger *logrus.Entry) error {
	logger.Info("Calico CNI releasing IP address")
	logger.WithFields(logrus.Fields{"paths": os.Getenv("CNI_PATH"),
		"type": conf.IPAM.Type}).Debug("Looking for IPAM plugin in paths")
	var ae *azure.AzureEndpoint
	if conf.IPAM.Type == "host-local" {
		const dummyPodCidr = "0.0.0.0/0"
		var stdinData map[string]interface{}
		err := json.Unmarshal(args.StdinData, &stdinData)
		if err != nil {
			return err
		}
		logger.WithField("podCidr", dummyPodCidr).Info("Using a dummy podCidr to release the IP")
		getDummyPodCIDR := func() (string, error) {
			return dummyPodCidr, nil
		}
		err = ReplaceHostLocalIPAMPodCIDRs(logger, stdinData, getDummyPodCIDR)
		if err != nil {
			return err
		}
		args.StdinData, err = json.Marshal(stdinData)
		if err != nil {
			return err
		}
		logger.Debug("Updated stdin data for Delete Cmd")
	} else if conf.IPAM.Type == "azure-vnet-ipam" {
		logger.Info("Configured to use Azure IPAM, load network and endpoint")
		an := &azure.AzureNetwork{Name: conf.Name}
		if err := an.Load(); err != nil {
			return err
		}
		ae = &azure.AzureEndpoint{Network: conf.Name, ContainerID: args.ContainerID, Interface: args.IfName}
		if err := ae.Load(); err != nil {
			return err
		}
		if len(ae.Addresses) == 0 {
			logger.WithField("AzureEndpoint", ae).Infof("No endpoint addresses, skip IPAM release")
			return nil
		}
		if err := azure.MutateConfigDel(args, *an, *ae); err != nil {
			return err
		}
	}
	err := ipam.ExecDel(conf.IPAM.Type, args.StdinData)
	if err != nil {
		logger.Error(err)
	} else if ae != nil {
		if err := ae.Delete(); err != nil {
			logger.WithError(err).Errorf("Error deleting Azure endpoint")
		}
	}
	return err
}
func ReplaceHostLocalIPAMPodCIDRs(logger *logrus.Entry, stdinData map[string]interface{}, getPodCIDR func() (string, error)) error {
	ipamData, ok := stdinData["ipam"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("failed to parse host-local IPAM data; was expecting a dict, not: %v", stdinData["ipam"])
	}
	err := replaceHostLocalIPAMPodCIDR(logger, ipamData, getPodCIDR)
	if err != nil {
		return err
	}
	untypedRanges := ipamData["ranges"]
	if untypedRanges != nil {
		rangeSets, ok := untypedRanges.([]interface{})
		if !ok {
			return fmt.Errorf("failed to parse host-local IPAM ranges section; was expecting a list, not: %v",
				ipamData["ranges"])
		}
		for _, urs := range rangeSets {
			rs, ok := urs.([]interface{})
			if !ok {
				return fmt.Errorf("failed to parse host-local IPAM range set; was expecting a list, not: %v", rs)
			}
			for _, r := range rs {
				err := replaceHostLocalIPAMPodCIDR(logger, r, getPodCIDR)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
func replaceHostLocalIPAMPodCIDR(logger *logrus.Entry, rawIpamData interface{}, getPodCidr func() (string, error)) error {
	logrus.WithField("ipamData", rawIpamData).Debug("Examining IPAM data for usePodCidr")
	ipamData, ok := rawIpamData.(map[string]interface{})
	if !ok {
		return fmt.Errorf("failed to parse host-local IPAM data; was expecting a dict, not: %v", rawIpamData)
	}
	subnet, _ := ipamData["subnet"].(string)
	if strings.EqualFold(subnet, "usePodCidr") {
		logger.Info("Calico CNI fetching podCidr from Kubernetes")
		podCidr, err := getPodCidr()
		if err != nil {
			logger.Info("Failed to getPodCidr")
			return err
		}
		logger.WithField("podCidr", podCidr).Info("Fetched podCidr")
		ipamData["subnet"] = podCidr
		subnet = podCidr
		logger.Infof("Calico CNI passing podCidr to host-local IPAM: %s", podCidr)
	}
	err := updateHostLocalIPAMDataForOS(subnet, ipamData)
	if err != nil {
		return err
	}
	return nil
}
func UpdateHostLocalIPAMDataForWindows(subnet string, ipamData map[string]interface{}) error {
	if len(subnet) == 0 {
		return nil
	}
	logrus.Info("Updating host-local IPAM configuration to reserve IPs for Windows bridge.")
	ip, ipnet, err := net.ParseCIDR(subnet)
	if err != nil {
		return err
	}
	if ip.To4() != nil {
		expStartRange, expEndRange := getIPRanges(ip, ipnet)
		rangeStart, _ := ipamData["rangeStart"].(string)
		startRange, err := validateRangeOrSetDefault(rangeStart, expStartRange, ipnet, true)
		if err != nil {
			return err
		}
		ipamData["rangeStart"] = startRange
		rangeEnd, _ := ipamData["rangeEnd"].(string)
		endRange, err := validateRangeOrSetDefault(rangeEnd, expEndRange, ipnet, false)
		if err != nil {
			return err
		}
		ipamData["rangeEnd"] = endRange
	}
	return nil
}
func getIPRanges(ip net.IP, ipnet *net.IPNet) (string, string) {
	ip = ip.To4()
	ip.Mask(ipnet.Mask)
	ip[len(ip)-1] |= 3
	startRange := ip.String()
	for i := 0; i < len(ip); i++ {
		ip[i] |= (^ipnet.Mask[i])
	}
	ip[len(ip)-1] -= 1
	endRange := ip.String()
	return startRange, endRange
}
func validateStartRange(startRange net.IP, expStartRange net.IP) (net.IP, error) {
	startRange = startRange.To4()
	expStartRange = expStartRange.To4()
	if startRange == nil || expStartRange == nil {
		return nil, fmt.Errorf("Invalid ip address")
	}
	if bytes.Compare([]byte(startRange), []byte(expStartRange)) < 0 {
		return expStartRange, nil
	}
	return startRange, nil
}
func validateEndRange(endRange net.IP, expEndRange net.IP) (net.IP, error) {
	endRange = endRange.To4()
	expEndRange = expEndRange.To4()
	if endRange == nil || expEndRange == nil {
		return nil, fmt.Errorf("Invalid ip address")
	}
	if bytes.Compare([]byte(endRange), []byte(expEndRange)) > 0 {
		return expEndRange, nil
	}
	return endRange, nil
}
func validateRangeOrSetDefault(rangeData string, expRange string, ipnet *net.IPNet, isRangeStart bool) (string, error) {
	var parsedIP *cnet.IP
	var expRangeIP *cnet.IP
	var ip net.IP
	if expRangeIP = cnet.ParseIP(expRange); expRangeIP == nil {
		return "", fmt.Errorf("expRange contains invalid ip")
	}
	if len(rangeData) > 0 {
		if parsedIP = cnet.ParseIP(rangeData); parsedIP == nil {
			return "", fmt.Errorf("range contains invalid ip")
		} else if ipnet.Contains(parsedIP.IP) {  
			if isRangeStart {
				ip, _ = validateStartRange(parsedIP.IP, expRangeIP.IP)
			} else {
				ip, _ = validateEndRange(parsedIP.IP, expRangeIP.IP)
			}
			return ip.String(), nil
		}
	}
	return expRangeIP.IP.String(), nil
}
func ValidateNetworkName(name string) error {
	matched, err := regexp.MatchString(`^[a-zA-Z0-9_\.\-]+$`, name)
	if err != nil {
		return err
	}
	if !matched {
		return errors.New("invalid characters detected in the given network name. " +
			"Only letters a-z, numbers 0-9, and symbols _.- are supported")
	}
	return nil
}
func SanitizeMesosLabel(s string) string {
	invalidChar := regexp.MustCompile("[^-_.a-zA-Z0-9]+")
	dotDashSeq := regexp.MustCompile("[.-]*[.][.-]*")
	trailingLeadingDotsDashes := regexp.MustCompile("^[.-]*(.*?)[.-]*$")
	s = strings.Replace(s, "/", ".", -1)
	s = invalidChar.ReplaceAllString(s, "-")
	s = dotDashSeq.ReplaceAllString(s, ".")
	submatches := trailingLeadingDotsDashes.FindStringSubmatch(s)
	s = submatches[1]
	return s
}
func AddIgnoreUnknownArgs() error {
	cniArgs := "IgnoreUnknown=1"
	if os.Getenv("CNI_ARGS") != "" {
		cniArgs = fmt.Sprintf("%s;%s", cniArgs, os.Getenv("CNI_ARGS"))
	}
	return os.Setenv("CNI_ARGS", cniArgs)
}
func CreateResultFromEndpoint(wep *api.WorkloadEndpoint) (*current.Result, error) {
	result := &current.Result{}
	for _, v := range wep.Spec.IPNetworks {
		parsedIPConfig := current.IPConfig{}
		ipAddr, ipNet, err := net.ParseCIDR(v)
		if err != nil {
			return nil, err
		}
		parsedIPConfig.Address = *ipNet
		if ipAddr.To4() != nil {
			parsedIPConfig.Version = "4"
		} else {
			parsedIPConfig.Version = "6"
		}
		result.IPs = append(result.IPs, &parsedIPConfig)
	}
	return result, nil
}
func PopulateEndpointNets(wep *api.WorkloadEndpoint, result *current.Result) error {
	var copyIpNet net.IPNet
	if len(result.IPs) == 0 {
		return errors.New("IPAM plugin did not return any IP addresses")
	}
	for _, ipNet := range result.IPs {
		copyIpNet = net.IPNet{IP: ipNet.Address.IP, Mask: ipNet.Address.Mask}
		if ipNet.Version == "4" {
			copyIpNet.Mask = net.CIDRMask(32, 32)
		} else {
			copyIpNet.Mask = net.CIDRMask(128, 128)
		}
		wep.Spec.IPNetworks = append(wep.Spec.IPNetworks, copyIpNet.String())
	}
	return nil
}
type WEPIdentifiers struct {
	Namespace string
	WEPName   string
	names.WorkloadEndpointIdentifiers
}