<commit_before>// Copyright 2017 Cisco Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hostagent

import (
	"errors"
	"os"

	"github.com/sirupsen/logrus"

	"k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"

	md "github.com/noironetworks/aci-containers/pkg/metadata"
	nodeinfoclientset "github.com/noironetworks/aci-containers/pkg/nodeinfo/clientset/versioned"
	qospolicyclset "github.com/noironetworks/aci-containers/pkg/qospolicy/clientset/versioned"
	rdconfigclset "github.com/noironetworks/aci-containers/pkg/rdconfig/clientset/versioned"
	snatglobalclset "github.com/noironetworks/aci-containers/pkg/snatglobalinfo/clientset/versioned"
	snatlocalinfoclset "github.com/noironetworks/aci-containers/pkg/snatlocalinfo/clientset/versioned"
	snatpolicyclset "github.com/noironetworks/aci-containers/pkg/snatpolicy/clientset/versioned"
)

type Environment interface {
	Init(agent *HostAgent) error
	PrepareRun(stopCh <-chan struct{}) (bool, error)

	CniDeviceChanged(metadataKey *string, id *md.ContainerId)
	CniDeviceDeleted(metadataKey *string, id *md.ContainerId)

	CheckPodExists(metadataKey *string) (bool, error)
}

type K8sEnvironment struct {
	kubeClient          *kubernetes.Clientset
	snatGlobalClient    *snatglobalclset.Clientset
	snatPolicyClient    *snatpolicyclset.Clientset
	qosPolicyClient     *qospolicyclset.Clientset
	nodeInfo            *nodeinfoclientset.Clientset
	rdConfig            *rdconfigclset.Clientset
	snatLocalInfoClient *snatlocalinfoclset.Clientset
	agent               *HostAgent
	podInformer         cache.SharedIndexInformer
	endpointsInformer   cache.SharedIndexInformer
	serviceInformer     cache.SharedIndexInformer
	nodeInformer        cache.SharedIndexInformer
}

func NewK8sEnvironment(config *HostAgentConfig, log *logrus.Logger) (*K8sEnvironment, error) {
	if config.NodeName == "" {
		config.NodeName = os.Getenv("KUBERNETES_NODE_NAME")
	}
	if config.NodeName == "" {
		err := errors.New("Node name not specified and $KUBERNETES_NODE_NAME empty")
		log.Error(err.Error())
		return nil, err
	}

	log.WithFields(logrus.Fields{
		"kubeconfig": config.KubeConfig,
		"node-name":  config.NodeName,
	}).Info("Setting up Kubernetes environment")

	log.Debug("Initializing kubernetes client")
	var restconfig *restclient.Config
	var err error
	if config.KubeConfig != "" {
		// use kubeconfig file from command line
		restconfig, err =
			clientcmd.BuildConfigFromFlags("", config.KubeConfig)
		if err != nil {
			return nil, err
		}
	} else {
		// creates the in-cluster config
		restconfig, err = restclient.InClusterConfig()
		if err != nil {
			return nil, err
		}
	}

	// creates the kubernetes API client
	kubeClient, err := kubernetes.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize kube client")
		return nil, err
	}
	snatGlobalClient, err := snatglobalclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snat global info client")
		return nil, err
	}
	nodeInfo, err := nodeinfoclientset.NewForConfig(restconfig)
	log.Debug("Initializing kubernetes client", nodeInfo)
	if err != nil {
		log.Debug("Failed to intialize node info client")
		return nil, err
	}
	snatPolicyClient, err := snatpolicyclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	qosPolicyClient, err := qospolicyclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	rdConfig, err := rdconfigclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	snatLocalInfoClient, err := snatlocalinfoclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	return &K8sEnvironment{kubeClient: kubeClient, snatGlobalClient: snatGlobalClient,
		nodeInfo: nodeInfo, snatPolicyClient: snatPolicyClient, qosPolicyClient: qosPolicyClient, rdConfig: rdConfig, snatLocalInfoClient: snatLocalInfoClient}, nil
}

func (env *K8sEnvironment) Init(agent *HostAgent) error {
	env.agent = agent

	env.agent.log.Debug("Initializing informers")
	env.agent.initNodeInformerFromClient(env.kubeClient)
	env.agent.initPodInformerFromClient(env.kubeClient)
	env.agent.serviceEndPoints.InitClientInformer(env.kubeClient)
	env.agent.initServiceInformerFromClient(env.kubeClient)
	env.agent.initNamespaceInformerFromClient(env.kubeClient)
	env.agent.initNetworkPolicyInformerFromClient(env.kubeClient)
	env.agent.initDeploymentInformerFromClient(env.kubeClient)
	env.agent.initRCInformerFromClient(env.kubeClient)
	env.agent.initSnatGlobalInformerFromClient(env.snatGlobalClient)
	env.agent.initSnatPolicyInformerFromClient(env.snatPolicyClient)
	env.agent.initQoSPolicyInformerFromClient(env.qosPolicyClient)
	env.agent.initRdConfigInformerFromClient(env.rdConfig)
	env.agent.initQoSPolPodIndex()
	env.agent.initNetPolPodIndex()
	env.agent.initDepPodIndex()
	env.agent.initRCPodIndex()
	env.agent.initEventPoster(env.kubeClient)
	return nil
}

func (env *K8sEnvironment) PrepareRun(stopCh <-chan struct{}) (bool, error) {
	env.agent.log.Debug("Discovering node configuration")
	env.agent.updateOpflexConfig()
	go env.agent.runTickers(stopCh)

	env.agent.log.Debug("Starting node informer")
	go env.agent.nodeInformer.Run(stopCh)
	env.agent.log.Info("Waiting for node cache sync")
	cache.WaitForCacheSync(stopCh, env.agent.nodeInformer.HasSynced)
	env.agent.log.Info("Node cache sync successful")

	env.agent.log.Debug("Starting remaining informers")
	env.agent.log.Debug("Exporting node info: ", env.agent.config.NodeName)
	go env.agent.podInformer.Run(stopCh)
	go env.agent.controllerInformer.Run(stopCh)
	env.agent.serviceEndPoints.Run(stopCh)
	go env.agent.serviceInformer.Run(stopCh)
	go env.agent.nsInformer.Run(stopCh)
	go env.agent.netPolInformer.Run(stopCh)
	go env.agent.depInformer.Run(stopCh)
	go env.agent.rcInformer.Run(stopCh)
	go env.agent.snatGlobalInformer.Run(stopCh)
	go env.agent.snatPolicyInformer.Run(stopCh)
	go env.agent.qosPolicyInformer.Run(stopCh)
	go env.agent.rdConfigInformer.Run(stopCh)
	env.agent.log.Info("Waiting for cache sync for remaining objects")
	cache.WaitForCacheSync(stopCh,
		env.agent.podInformer.HasSynced, env.agent.serviceInformer.HasSynced,
		env.agent.snatGlobalInformer.HasSynced, env.agent.snatPolicyInformer.HasSynced,
		env.agent.rdConfigInformer.HasSynced)
	env.agent.log.Info("Cache sync successful")
	return true, nil
}
func (env *K8sEnvironment) CniDeviceChanged(metadataKey *string, id *md.ContainerId) {
	env.agent.podChanged(metadataKey)
}

func (env *K8sEnvironment) CniDeviceDeleted(metadataKey *string, id *md.ContainerId) {
}

func (env *K8sEnvironment) CheckPodExists(metadataKey *string) (bool, error) {
	_, exists, err := env.agent.podInformer.GetStore().GetByKey(*metadataKey)
	return exists, err
}
<commit_msg>Pod Cache sync is done before starting the Service informers. so that all the Pod related cache will be populated before accessing it.<commit_after>// Copyright 2017 Cisco Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hostagent

import (
	"errors"
	"os"

	"github.com/sirupsen/logrus"

	"k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"

	md "github.com/noironetworks/aci-containers/pkg/metadata"
	nodeinfoclientset "github.com/noironetworks/aci-containers/pkg/nodeinfo/clientset/versioned"
	qospolicyclset "github.com/noironetworks/aci-containers/pkg/qospolicy/clientset/versioned"
	rdconfigclset "github.com/noironetworks/aci-containers/pkg/rdconfig/clientset/versioned"
	snatglobalclset "github.com/noironetworks/aci-containers/pkg/snatglobalinfo/clientset/versioned"
	snatlocalinfoclset "github.com/noironetworks/aci-containers/pkg/snatlocalinfo/clientset/versioned"
	snatpolicyclset "github.com/noironetworks/aci-containers/pkg/snatpolicy/clientset/versioned"
)

type Environment interface {
	Init(agent *HostAgent) error
	PrepareRun(stopCh <-chan struct{}) (bool, error)

	CniDeviceChanged(metadataKey *string, id *md.ContainerId)
	CniDeviceDeleted(metadataKey *string, id *md.ContainerId)

	CheckPodExists(metadataKey *string) (bool, error)
}

type K8sEnvironment struct {
	kubeClient          *kubernetes.Clientset
	snatGlobalClient    *snatglobalclset.Clientset
	snatPolicyClient    *snatpolicyclset.Clientset
	qosPolicyClient     *qospolicyclset.Clientset
	nodeInfo            *nodeinfoclientset.Clientset
	rdConfig            *rdconfigclset.Clientset
	snatLocalInfoClient *snatlocalinfoclset.Clientset
	agent               *HostAgent
	podInformer         cache.SharedIndexInformer
	endpointsInformer   cache.SharedIndexInformer
	serviceInformer     cache.SharedIndexInformer
	nodeInformer        cache.SharedIndexInformer
}

func NewK8sEnvironment(config *HostAgentConfig, log *logrus.Logger) (*K8sEnvironment, error) {
	if config.NodeName == "" {
		config.NodeName = os.Getenv("KUBERNETES_NODE_NAME")
	}
	if config.NodeName == "" {
		err := errors.New("Node name not specified and $KUBERNETES_NODE_NAME empty")
		log.Error(err.Error())
		return nil, err
	}

	log.WithFields(logrus.Fields{
		"kubeconfig": config.KubeConfig,
		"node-name":  config.NodeName,
	}).Info("Setting up Kubernetes environment")

	log.Debug("Initializing kubernetes client")
	var restconfig *restclient.Config
	var err error
	if config.KubeConfig != "" {
		// use kubeconfig file from command line
		restconfig, err =
			clientcmd.BuildConfigFromFlags("", config.KubeConfig)
		if err != nil {
			return nil, err
		}
	} else {
		// creates the in-cluster config
		restconfig, err = restclient.InClusterConfig()
		if err != nil {
			return nil, err
		}
	}

	// creates the kubernetes API client
	kubeClient, err := kubernetes.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize kube client")
		return nil, err
	}
	snatGlobalClient, err := snatglobalclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snat global info client")
		return nil, err
	}
	nodeInfo, err := nodeinfoclientset.NewForConfig(restconfig)
	log.Debug("Initializing kubernetes client", nodeInfo)
	if err != nil {
		log.Debug("Failed to intialize node info client")
		return nil, err
	}
	snatPolicyClient, err := snatpolicyclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	qosPolicyClient, err := qospolicyclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	rdConfig, err := rdconfigclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	snatLocalInfoClient, err := snatlocalinfoclset.NewForConfig(restconfig)
	if err != nil {
		log.Debug("Failed to intialize snatpolicy info client")
		return nil, err
	}
	return &K8sEnvironment{kubeClient: kubeClient, snatGlobalClient: snatGlobalClient,
		nodeInfo: nodeInfo, snatPolicyClient: snatPolicyClient, qosPolicyClient: qosPolicyClient, rdConfig: rdConfig, snatLocalInfoClient: snatLocalInfoClient}, nil
}

func (env *K8sEnvironment) Init(agent *HostAgent) error {
	env.agent = agent

	env.agent.log.Debug("Initializing informers")
	env.agent.initNodeInformerFromClient(env.kubeClient)
	env.agent.initPodInformerFromClient(env.kubeClient)
	env.agent.serviceEndPoints.InitClientInformer(env.kubeClient)
	env.agent.initServiceInformerFromClient(env.kubeClient)
	env.agent.initNamespaceInformerFromClient(env.kubeClient)
	env.agent.initNetworkPolicyInformerFromClient(env.kubeClient)
	env.agent.initDeploymentInformerFromClient(env.kubeClient)
	env.agent.initRCInformerFromClient(env.kubeClient)
	env.agent.initSnatGlobalInformerFromClient(env.snatGlobalClient)
	env.agent.initSnatPolicyInformerFromClient(env.snatPolicyClient)
	env.agent.initQoSPolicyInformerFromClient(env.qosPolicyClient)
	env.agent.initRdConfigInformerFromClient(env.rdConfig)
	env.agent.initQoSPolPodIndex()
	env.agent.initNetPolPodIndex()
	env.agent.initDepPodIndex()
	env.agent.initRCPodIndex()
	env.agent.initEventPoster(env.kubeClient)
	return nil
}

func (env *K8sEnvironment) PrepareRun(stopCh <-chan struct{}) (bool, error) {
	env.agent.log.Debug("Discovering node configuration")
	env.agent.updateOpflexConfig()
	go env.agent.runTickers(stopCh)

	env.agent.log.Debug("Starting node informer")
	go env.agent.nodeInformer.Run(stopCh)
	env.agent.log.Info("Waiting for node cache sync")
	cache.WaitForCacheSync(stopCh, env.agent.nodeInformer.HasSynced)
	env.agent.log.Info("Node cache sync successful")

	env.agent.log.Debug("Starting remaining informers")
	env.agent.log.Debug("Exporting node info: ", env.agent.config.NodeName)
	go env.agent.podInformer.Run(stopCh)
	cache.WaitForCacheSync(stopCh, env.agent.podInformer.HasSynced)
	go env.agent.controllerInformer.Run(stopCh)
	env.agent.serviceEndPoints.Run(stopCh)
	go env.agent.serviceInformer.Run(stopCh)
	go env.agent.nsInformer.Run(stopCh)
	go env.agent.netPolInformer.Run(stopCh)
	go env.agent.depInformer.Run(stopCh)
	go env.agent.rcInformer.Run(stopCh)
	go env.agent.snatGlobalInformer.Run(stopCh)
	go env.agent.snatPolicyInformer.Run(stopCh)
	go env.agent.qosPolicyInformer.Run(stopCh)
	go env.agent.rdConfigInformer.Run(stopCh)
	env.agent.log.Info("Waiting for cache sync for remaining objects")
	cache.WaitForCacheSync(stopCh, env.agent.serviceInformer.HasSynced,
		env.agent.snatGlobalInformer.HasSynced, env.agent.snatPolicyInformer.HasSynced,
		env.agent.rdConfigInformer.HasSynced)
	env.agent.log.Info("Cache sync successful")
	return true, nil
}
func (env *K8sEnvironment) CniDeviceChanged(metadataKey *string, id *md.ContainerId) {
	env.agent.podChanged(metadataKey)
}

func (env *K8sEnvironment) CniDeviceDeleted(metadataKey *string, id *md.ContainerId) {
}

func (env *K8sEnvironment) CheckPodExists(metadataKey *string) (bool, error) {
	_, exists, err := env.agent.podInformer.GetStore().GetByKey(*metadataKey)
	return exists, err
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright 2021 Red Hat, Inc.
 *
 */

package network

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"k8s.io/apimachinery/pkg/types"

	v1 "kubevirt.io/api/core/v1"
	"kubevirt.io/kubevirt/pkg/network/cache"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
)

type NetStat struct {
	ifaceCacheFactory cache.InterfaceCacheFactory

	// In memory cache, storing pod interface information.
	// key is the file path, value is the contents.
	// if key exists, then don't read directly from file.
	podInterfaceVolatileCache PodInterfaceByVMIAndName
}

func NewNetStat(ifaceCacheFactory cache.InterfaceCacheFactory) *NetStat {
	return &NetStat{
		ifaceCacheFactory:         ifaceCacheFactory,
		podInterfaceVolatileCache: PodInterfaceByVMIAndName{},
	}
}

func (c *NetStat) Teardown(vmi *v1.VirtualMachineInstance) {
	c.podInterfaceVolatileCache.DeleteAllForVMI(vmi.UID)
}

func (c *NetStat) PodInterfaceVolatileDataIsCached(vmi *v1.VirtualMachineInstance, ifaceName string) bool {
	_, exists := c.podInterfaceVolatileCache.Load(vmi.UID, ifaceName)
	return exists
}

func (c *NetStat) CachePodInterfaceVolatileData(vmi *v1.VirtualMachineInstance, ifaceName string, data *cache.PodCacheInterface) {
	c.podInterfaceVolatileCache.Store(vmi.UID, ifaceName, data)
}

func (c *NetStat) UpdateStatus(vmi *v1.VirtualMachineInstance, domain *api.Domain) error {
	if domain == nil {
		return nil
	}

	// This is needed to be backwards compatible with vmi's which have status interfaces
	// with the name not being set
	if len(domain.Spec.Devices.Interfaces) == 0 && len(vmi.Status.Interfaces) == 1 && vmi.Status.Interfaces[0].Name == "" {
		for _, network := range vmi.Spec.Networks {
			if network.NetworkSource.Pod != nil {
				vmi.Status.Interfaces[0].Name = network.Name
			}
		}
	}

	if len(vmi.Status.Interfaces) == 0 {
		// Set Pod Interface
		interfaces := make([]v1.VirtualMachineInstanceNetworkInterface, 0)
		for _, network := range vmi.Spec.Networks {
			podIface, err := c.getPodInterfacefromFileCache(vmi, network.Name)
			if err != nil {
				return err
			}

			if podIface != nil {
				ifc := v1.VirtualMachineInstanceNetworkInterface{
					Name: network.Name,
					IP:   podIface.PodIP,
					IPs:  podIface.PodIPs,
				}
				interfaces = append(interfaces, ifc)
			}
		}
		vmi.Status.Interfaces = interfaces
	}

	if len(domain.Spec.Devices.Interfaces) > 0 || len(domain.Status.Interfaces) > 0 {
		// This calculates the vmi.Status.Interfaces based on the following data sets:
		// - vmi.Status.Interfaces - previously calculated interfaces, this can contain data (pod IP)
		//   set in the previous loops (when there are no interfaces), which can not be deleted,
		//   unless overridden by Qemu agent
		// - domain.Spec - interfaces form the Spec
		// - domain.Status.Interfaces - interfaces reported by guest agent (empty if Qemu agent not running)
		newInterfaces := []v1.VirtualMachineInstanceNetworkInterface{}

		existingInterfaceStatusByName := map[string]v1.VirtualMachineInstanceNetworkInterface{}
		for _, existingInterfaceStatus := range vmi.Status.Interfaces {
			if existingInterfaceStatus.Name != "" {
				existingInterfaceStatusByName[existingInterfaceStatus.Name] = existingInterfaceStatus
			}
		}

		domainInterfaceStatusByMac := map[string]api.InterfaceStatus{}
		for _, domainInterfaceStatus := range domain.Status.Interfaces {
			domainInterfaceStatusByMac[domainInterfaceStatus.Mac] = domainInterfaceStatus
		}

		existingInterfacesSpecByName := map[string]v1.Interface{}
		for _, existingInterfaceSpec := range vmi.Spec.Domain.Devices.Interfaces {
			existingInterfacesSpecByName[existingInterfaceSpec.Name] = existingInterfaceSpec
		}
		existingNetworksByName := map[string]v1.Network{}
		for _, existingNetwork := range vmi.Spec.Networks {
			existingNetworksByName[existingNetwork.Name] = existingNetwork
		}

		// Iterate through all domain.Spec interfaces
		for _, domainInterface := range domain.Spec.Devices.Interfaces {
			interfaceMAC := domainInterface.MAC.MAC
			var newInterface v1.VirtualMachineInstanceNetworkInterface
			var isForwardingBindingInterface = false

			if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil || existingInterfacesSpecByName[domainInterface.Alias.GetName()].Slirp != nil {
				isForwardingBindingInterface = true
			}

			if existingInterface, exists := existingInterfaceStatusByName[domainInterface.Alias.GetName()]; exists {
				// Reuse previously calculated interface from vmi.Status.Interfaces, updating the MAC from domain.Spec
				// Only interfaces defined in domain.Spec are handled here
				newInterface = existingInterface
				newInterface.MAC = interfaceMAC

				// If it is a Combination of Masquerade+Pod network, check IP from file cache
				if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil && existingNetworksByName[domainInterface.Alias.GetName()].NetworkSource.Pod != nil {
					iface, err := c.getPodInterfacefromFileCache(vmi, domainInterface.Alias.GetName())
					if err != nil {
						return err
					}

					if !reflect.DeepEqual(iface.PodIPs, existingInterfaceStatusByName[domainInterface.Alias.GetName()].IPs) {
						newInterface.Name = domainInterface.Alias.GetName()
						newInterface.IP = iface.PodIP
						newInterface.IPs = iface.PodIPs
					}
				}
			} else {
				// If not present in vmi.Status.Interfaces, create a new one based on domain.Spec
				newInterface = v1.VirtualMachineInstanceNetworkInterface{
					MAC:  interfaceMAC,
					Name: domainInterface.Alias.GetName(),
				}
			}

			// Update IP info based on information from domain.Status.Interfaces (Qemu guest)
			// Remove the interface from domainInterfaceStatusByMac to mark it as handled
			if interfaceStatus, exists := domainInterfaceStatusByMac[interfaceMAC]; exists {
				newInterface.InterfaceName = interfaceStatus.InterfaceName
				// Do not update if interface has Masquerede binding
				// virt-controller should update VMI status interface with Pod IP instead
				if !isForwardingBindingInterface {
					newInterface.IP = interfaceStatus.Ip
					newInterface.IPs = interfaceStatus.IPs
				}
				delete(domainInterfaceStatusByMac, interfaceMAC)
			}
			newInterfaces = append(newInterfaces, newInterface)
		}

		// If any of domain.Status.Interfaces were not handled above, it means that the vm contains additional
		// interfaces not defined in domain.Spec.Devices.Interfaces (most likely added by user on VM or a SRIOV interface)
		// Add them to vmi.Status.Interfaces
		setMissingSRIOVInterfacesNames(existingInterfacesSpecByName, domainInterfaceStatusByMac)
		for interfaceMAC, domainInterfaceStatus := range domainInterfaceStatusByMac {
			newInterface := v1.VirtualMachineInstanceNetworkInterface{
				Name:          domainInterfaceStatus.Name,
				MAC:           interfaceMAC,
				IP:            domainInterfaceStatus.Ip,
				IPs:           domainInterfaceStatus.IPs,
				InterfaceName: domainInterfaceStatus.InterfaceName,
			}
			newInterfaces = append(newInterfaces, newInterface)
		}
		vmi.Status.Interfaces = newInterfaces
	}
	return nil
}

func (c *NetStat) getPodInterfacefromFileCache(vmi *v1.VirtualMachineInstance, ifaceName string) (*cache.PodCacheInterface, error) {
	// Once the Interface files are set on the handler, they don't change
	// If already present in the map, don't read again
	podInterface, exists := c.podInterfaceVolatileCache.Load(vmi.UID, ifaceName)

	if exists {
		return podInterface, nil
	}

	//FIXME error handling?
	podInterface, _ = c.ifaceCacheFactory.CacheForVMI(vmi).Read(ifaceName)

	c.podInterfaceVolatileCache.Store(vmi.UID, ifaceName, podInterface)

	return podInterface, nil
}

func setMissingSRIOVInterfacesNames(interfacesSpecByName map[string]v1.Interface, interfacesStatusByMac map[string]api.InterfaceStatus) {
	for name, ifaceSpec := range interfacesSpecByName {
		if ifaceSpec.SRIOV == nil || ifaceSpec.MacAddress == "" {
			continue
		}
		if domainIfaceStatus, exists := interfacesStatusByMac[ifaceSpec.MacAddress]; exists {
			domainIfaceStatus.Name = name
			interfacesStatusByMac[ifaceSpec.MacAddress] = domainIfaceStatus
		}
	}
}

type PodInterfaceByVMIAndName struct {
	syncMap sync.Map
}

func (p *PodInterfaceByVMIAndName) DeleteAllForVMI(vmiUID types.UID) {
	// Clean Pod interface cache from map and files
	p.syncMap.Range(func(key, value interface{}) bool {
		if strings.Contains(key.(string), string(vmiUID)) {
			p.syncMap.Delete(key)
		}
		return true
	})
}

func (p *PodInterfaceByVMIAndName) Load(vmiUID types.UID, interfaceName string) (*cache.PodCacheInterface, bool) {
	result, exists := p.syncMap.Load(p.key(vmiUID, interfaceName))

	if !exists {
		return nil, false
	}
	return p.cast(result), true
}

func (p *PodInterfaceByVMIAndName) Store(vmiUID types.UID, interfaceName string, podCacheInterface *cache.PodCacheInterface) {
	p.syncMap.Store(p.key(vmiUID, interfaceName), podCacheInterface)
}

func (*PodInterfaceByVMIAndName) cast(result interface{}) *cache.PodCacheInterface {
	podCacheInterface, ok := result.(*cache.PodCacheInterface)
	if !ok {
		panic(fmt.Sprintf("failed casting %+v to *PodCacheInterface", result))
	}
	return podCacheInterface
}

func (*PodInterfaceByVMIAndName) key(vmiUID types.UID, interfaceName string) string {
	return fmt.Sprintf("%s/%s", vmiUID, interfaceName)
}
<commit_msg>netstat: Remove pod interface volatile cache wrapper struct<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright 2021 Red Hat, Inc.
 *
 */

package network

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"k8s.io/apimachinery/pkg/types"

	v1 "kubevirt.io/api/core/v1"
	"kubevirt.io/kubevirt/pkg/network/cache"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
)

type NetStat struct {
	ifaceCacheFactory cache.InterfaceCacheFactory

	// In memory cache, storing pod interface information.
	// key is the file path, value is the contents.
	// if key exists, then don't read directly from file.
	podInterfaceVolatileCache sync.Map
}

func NewNetStat(ifaceCacheFactory cache.InterfaceCacheFactory) *NetStat {
	return &NetStat{
		ifaceCacheFactory:         ifaceCacheFactory,
		podInterfaceVolatileCache: sync.Map{},
	}
}

func (c *NetStat) Teardown(vmi *v1.VirtualMachineInstance) {
	c.podInterfaceVolatileCache.Range(func(key, value interface{}) bool {
		if strings.Contains(key.(string), string(vmi.UID)) {
			c.podInterfaceVolatileCache.Delete(key)
		}
		return true
	})
}

func (c *NetStat) PodInterfaceVolatileDataIsCached(vmi *v1.VirtualMachineInstance, ifaceName string) bool {
	_, exists := c.podInterfaceVolatileCache.Load(vmiInterfaceKey(vmi.UID, ifaceName))
	return exists
}

func (c *NetStat) CachePodInterfaceVolatileData(vmi *v1.VirtualMachineInstance, ifaceName string, data *cache.PodCacheInterface) {
	c.podInterfaceVolatileCache.Store(vmiInterfaceKey(vmi.UID, ifaceName), data)
}

func (c *NetStat) UpdateStatus(vmi *v1.VirtualMachineInstance, domain *api.Domain) error {
	if domain == nil {
		return nil
	}

	// This is needed to be backwards compatible with vmi's which have status interfaces
	// with the name not being set
	if len(domain.Spec.Devices.Interfaces) == 0 && len(vmi.Status.Interfaces) == 1 && vmi.Status.Interfaces[0].Name == "" {
		for _, network := range vmi.Spec.Networks {
			if network.NetworkSource.Pod != nil {
				vmi.Status.Interfaces[0].Name = network.Name
			}
		}
	}

	if len(vmi.Status.Interfaces) == 0 {
		// Set Pod Interface
		interfaces := make([]v1.VirtualMachineInstanceNetworkInterface, 0)
		for _, network := range vmi.Spec.Networks {
			podIface, err := c.getPodInterfacefromFileCache(vmi, network.Name)
			if err != nil {
				return err
			}

			if podIface != nil {
				ifc := v1.VirtualMachineInstanceNetworkInterface{
					Name: network.Name,
					IP:   podIface.PodIP,
					IPs:  podIface.PodIPs,
				}
				interfaces = append(interfaces, ifc)
			}
		}
		vmi.Status.Interfaces = interfaces
	}

	if len(domain.Spec.Devices.Interfaces) > 0 || len(domain.Status.Interfaces) > 0 {
		// This calculates the vmi.Status.Interfaces based on the following data sets:
		// - vmi.Status.Interfaces - previously calculated interfaces, this can contain data (pod IP)
		//   set in the previous loops (when there are no interfaces), which can not be deleted,
		//   unless overridden by Qemu agent
		// - domain.Spec - interfaces form the Spec
		// - domain.Status.Interfaces - interfaces reported by guest agent (empty if Qemu agent not running)
		newInterfaces := []v1.VirtualMachineInstanceNetworkInterface{}

		existingInterfaceStatusByName := map[string]v1.VirtualMachineInstanceNetworkInterface{}
		for _, existingInterfaceStatus := range vmi.Status.Interfaces {
			if existingInterfaceStatus.Name != "" {
				existingInterfaceStatusByName[existingInterfaceStatus.Name] = existingInterfaceStatus
			}
		}

		domainInterfaceStatusByMac := map[string]api.InterfaceStatus{}
		for _, domainInterfaceStatus := range domain.Status.Interfaces {
			domainInterfaceStatusByMac[domainInterfaceStatus.Mac] = domainInterfaceStatus
		}

		existingInterfacesSpecByName := map[string]v1.Interface{}
		for _, existingInterfaceSpec := range vmi.Spec.Domain.Devices.Interfaces {
			existingInterfacesSpecByName[existingInterfaceSpec.Name] = existingInterfaceSpec
		}
		existingNetworksByName := map[string]v1.Network{}
		for _, existingNetwork := range vmi.Spec.Networks {
			existingNetworksByName[existingNetwork.Name] = existingNetwork
		}

		// Iterate through all domain.Spec interfaces
		for _, domainInterface := range domain.Spec.Devices.Interfaces {
			interfaceMAC := domainInterface.MAC.MAC
			var newInterface v1.VirtualMachineInstanceNetworkInterface
			var isForwardingBindingInterface = false

			if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil || existingInterfacesSpecByName[domainInterface.Alias.GetName()].Slirp != nil {
				isForwardingBindingInterface = true
			}

			if existingInterface, exists := existingInterfaceStatusByName[domainInterface.Alias.GetName()]; exists {
				// Reuse previously calculated interface from vmi.Status.Interfaces, updating the MAC from domain.Spec
				// Only interfaces defined in domain.Spec are handled here
				newInterface = existingInterface
				newInterface.MAC = interfaceMAC

				// If it is a Combination of Masquerade+Pod network, check IP from file cache
				if existingInterfacesSpecByName[domainInterface.Alias.GetName()].Masquerade != nil && existingNetworksByName[domainInterface.Alias.GetName()].NetworkSource.Pod != nil {
					iface, err := c.getPodInterfacefromFileCache(vmi, domainInterface.Alias.GetName())
					if err != nil {
						return err
					}

					if !reflect.DeepEqual(iface.PodIPs, existingInterfaceStatusByName[domainInterface.Alias.GetName()].IPs) {
						newInterface.Name = domainInterface.Alias.GetName()
						newInterface.IP = iface.PodIP
						newInterface.IPs = iface.PodIPs
					}
				}
			} else {
				// If not present in vmi.Status.Interfaces, create a new one based on domain.Spec
				newInterface = v1.VirtualMachineInstanceNetworkInterface{
					MAC:  interfaceMAC,
					Name: domainInterface.Alias.GetName(),
				}
			}

			// Update IP info based on information from domain.Status.Interfaces (Qemu guest)
			// Remove the interface from domainInterfaceStatusByMac to mark it as handled
			if interfaceStatus, exists := domainInterfaceStatusByMac[interfaceMAC]; exists {
				newInterface.InterfaceName = interfaceStatus.InterfaceName
				// Do not update if interface has Masquerede binding
				// virt-controller should update VMI status interface with Pod IP instead
				if !isForwardingBindingInterface {
					newInterface.IP = interfaceStatus.Ip
					newInterface.IPs = interfaceStatus.IPs
				}
				delete(domainInterfaceStatusByMac, interfaceMAC)
			}
			newInterfaces = append(newInterfaces, newInterface)
		}

		// If any of domain.Status.Interfaces were not handled above, it means that the vm contains additional
		// interfaces not defined in domain.Spec.Devices.Interfaces (most likely added by user on VM or a SRIOV interface)
		// Add them to vmi.Status.Interfaces
		setMissingSRIOVInterfacesNames(existingInterfacesSpecByName, domainInterfaceStatusByMac)
		for interfaceMAC, domainInterfaceStatus := range domainInterfaceStatusByMac {
			newInterface := v1.VirtualMachineInstanceNetworkInterface{
				Name:          domainInterfaceStatus.Name,
				MAC:           interfaceMAC,
				IP:            domainInterfaceStatus.Ip,
				IPs:           domainInterfaceStatus.IPs,
				InterfaceName: domainInterfaceStatus.InterfaceName,
			}
			newInterfaces = append(newInterfaces, newInterface)
		}
		vmi.Status.Interfaces = newInterfaces
	}
	return nil
}

func (c *NetStat) getPodInterfacefromFileCache(vmi *v1.VirtualMachineInstance, ifaceName string) (*cache.PodCacheInterface, error) {
	// Once the Interface files are set on the handler, they don't change
	// If already present in the map, don't read again
	cacheData, exists := c.podInterfaceVolatileCache.Load(vmiInterfaceKey(vmi.UID, ifaceName))
	if exists {
		return cacheData.(*cache.PodCacheInterface), nil
	}

	//FIXME error handling?
	podInterface, _ := c.ifaceCacheFactory.CacheForVMI(vmi).Read(ifaceName)

	c.podInterfaceVolatileCache.Store(vmiInterfaceKey(vmi.UID, ifaceName), podInterface)

	return podInterface, nil
}

func setMissingSRIOVInterfacesNames(interfacesSpecByName map[string]v1.Interface, interfacesStatusByMac map[string]api.InterfaceStatus) {
	for name, ifaceSpec := range interfacesSpecByName {
		if ifaceSpec.SRIOV == nil || ifaceSpec.MacAddress == "" {
			continue
		}
		if domainIfaceStatus, exists := interfacesStatusByMac[ifaceSpec.MacAddress]; exists {
			domainIfaceStatus.Name = name
			interfacesStatusByMac[ifaceSpec.MacAddress] = domainIfaceStatus
		}
	}
}

func vmiInterfaceKey(vmiUID types.UID, interfaceName string) string {
	return fmt.Sprintf("%s/%s", vmiUID, interfaceName)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Nuclio Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package runner

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/nuclio/nuclio/pkg/errors"
	"github.com/nuclio/nuclio/pkg/functioncr"
	"github.com/nuclio/nuclio/pkg/nuctl"
	"github.com/nuclio/nuclio/pkg/nuctl/builder"
	"github.com/nuclio/nuclio/pkg/util/common"

	"github.com/nuclio/nuclio-sdk"
	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type FunctionRunner struct {
	logger       nuclio.Logger
	options      *Options
	kubeConsumer *nuctl.KubeConsumer
}

type RunResult struct {
	NodePort int
}

func NewFunctionRunner(parentLogger nuclio.Logger) (*FunctionRunner, error) {
	newFunctionRunner := &FunctionRunner{
		logger: parentLogger.GetChild("runner").(nuclio.Logger),
	}

	return newFunctionRunner, nil
}

func (fr *FunctionRunner) Run(kubeConsumer *nuctl.KubeConsumer, options *Options) (*RunResult, error) {
	var runResult *RunResult

	// save options, consumer
	fr.options = options
	fr.kubeConsumer = kubeConsumer

	fr.logger.InfoWith("Running function", "name", options.Common.Identifier)

	// create a function, set default values and try to update from file
	functioncrInstance := functioncr.Function{}
	functioncrInstance.SetDefaults()
	functioncrInstance.Name = options.Common.Identifier

	if options.SpecPath != "" {
		err := functioncr.FromSpecFile(options.SpecPath, &functioncrInstance)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to read function spec file")
		}
	}

	// override with options
	if err := UpdateFunctioncrWithOptions(options, &functioncrInstance); err != nil {
		return nil, errors.Wrap(err, "Failed to update function with options")
	}

	if err := fr.deletePreexistingFunction(options.Common.Namespace, options.Common.Identifier); err != nil {
		return nil, errors.Wrap(err, "Failed to delete pre-existing function")
	}

	// create a builder
	builder, err := builder.NewFunctionBuilder(fr.logger)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create builder")
	}

	// execute the build, set image
	processorImageName, err := builder.Build(&options.Build)
	if err != nil {
		return nil, err
	}

	// set the image
	functioncrInstance.Spec.Image = fmt.Sprintf("%s/%s", options.RunRegistry, processorImageName)

	// deploy the function
	err = fr.deployFunction(&functioncrInstance)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to deploy function")
	}

	// get the function (might take a few seconds til it's created)
	service, err := fr.getFunctionService(options.Common.Namespace, options.Common.Identifier)
	if err == nil {
		runResult = &RunResult{
			NodePort: int(service.Spec.Ports[0].NodePort),
		}
	}

	fr.logger.InfoWith("Function run complete", "node_port", runResult.NodePort)

	return runResult, nil
}

func UpdateFunctioncrWithOptions(options *Options, functioncrInstance *functioncr.Function) error {

	if options.Description != "" {
		functioncrInstance.Spec.Description = options.Description
	}

	// update replicas if scale was specified
	if options.Scale != "" {

		// TODO: handle/Set Min/Max replicas (used only with Auto mode)
		if options.Scale == "auto" {
			functioncrInstance.Spec.Replicas = 0
		} else {
			i, err := strconv.Atoi(options.Scale)
			if err != nil {
				return fmt.Errorf(`Invalid function scale, must be "auto" or an integer value`)
			} else {
				functioncrInstance.Spec.Replicas = int32(i)
			}
		}
	}

	// Set specified labels, is label = "" remove it (if exists)
	labels := common.StringToStringMap(options.Labels)

	// create map if it doesn't exist and there are labels
	if len(labels) > 0 && functioncrInstance.Labels == nil {
		functioncrInstance.Labels = map[string]string{}
	}

	for labelName, labelValue := range labels {
		if labelName != "name" && labelName != "version" && labelName != "alias" {
			if labelValue == "" {
				delete(functioncrInstance.Labels, labelName)
			} else {
				functioncrInstance.Labels[labelName] = labelValue
			}
		}
	}

	envmap := common.StringToStringMap(options.Env)
	newenv := []v1.EnvVar{}

	// merge new Environment var: update existing then add new
	for _, e := range functioncrInstance.Spec.Env {
		if v, ok := envmap[e.Name]; ok {
			if v != "" {
				newenv = append(newenv, v1.EnvVar{Name: e.Name, Value: v})
			}
			delete(envmap, e.Name)
		} else {
			newenv = append(newenv, e)
		}
	}

	for k, v := range envmap {
		newenv = append(newenv, v1.EnvVar{Name: k, Value: v})
	}

	functioncrInstance.Spec.Env = newenv

	if options.HTTPPort != 0 {
		functioncrInstance.Spec.HTTPPort = options.HTTPPort
	}

	if options.Publish {
		functioncrInstance.Spec.Publish = options.Publish
	}

	if options.Disabled {
		functioncrInstance.Spec.Disabled = options.Disabled // TODO: use string to detect if noop/true/false
	}

	// update data bindings
	functioncrInstance.Spec.DataBindings = options.DataBindings

	return nil
}

func updateDataBindings(encodedDataBindings string, function *functioncr.Function) error {

	// if user passed nothing, no data bindings required
	if encodedDataBindings == "" {
		return nil
	}

	return json.Unmarshal([]byte(encodedDataBindings), &function.Spec.DataBindings)
}

func (fr *FunctionRunner) deployFunction(functioncrToCreate *functioncr.Function) error {

	// get invocation logger. if it wasn't passed, use instance logger
	fr.options.Common.GetLogger(fr.logger).DebugWith("Deploying function", "function", functioncrToCreate)

	createdFunctioncr, err := fr.kubeConsumer.FunctioncrClient.Create(functioncrToCreate)
	if err != nil {
		return err
	}

	// wait until function is processed
	return fr.kubeConsumer.FunctioncrClient.WaitUntilCondition(createdFunctioncr.Namespace,
		createdFunctioncr.Name,
		functioncr.WaitConditionProcessed,
		10*time.Second,
	)
}

func (fr *FunctionRunner) getFunctionService(namespace string, name string) (service *v1.Service, err error) {
	deadline := time.Now().Add(10 * time.Second)

	for {

		// after a few seconds, give up
		if time.Now().After(deadline) {
			break
		}

		service, err = fr.kubeConsumer.Clientset.CoreV1().Services(namespace).Get(name, meta_v1.GetOptions{})

		// if there was an error other than the fact that the service wasn't found,
		// return now
		if !apierrors.IsNotFound(err) {
			return
		}

		// if we got a service, check that it has a node port
		if service != nil && len(service.Spec.Ports) > 0 && service.Spec.Ports[0].NodePort != 0 {
			return
		}

		// wait a bit
		time.Sleep(1 * time.Second)
	}

	return
}

func (fr *FunctionRunner) deletePreexistingFunction(namespace string, name string) error {

	// before we do anything, delete the current version of the function if it exists
	_, err := fr.kubeConsumer.FunctioncrClient.Get(namespace, name)

	// note that existingFunctioncrInstance will contain a value regardless of whether there was an error
	if err != nil {

		// if it wasn't a not found error, log a warning
		if !apierrors.IsNotFound(err) {

			// don't fail, maybe we'll succeed in deploying
			fr.logger.WarnWith("Failed to get function while checking if it already exists", "err", err)
		}

	} else {

		// if the function exists, delete it
		fr.logger.InfoWith("Function already exists, deleting")

		if err := fr.kubeConsumer.FunctioncrClient.Delete(namespace, name, &meta_v1.DeleteOptions{}); err != nil {

			// don't fail
			fr.logger.WarnWith("Failed to delete existing function", "err", err)
		} else {

			// wait a bit to work around a controller bug
			time.Sleep(2 * time.Second)
		}
	}

	return nil
}
<commit_msg>nuctl will use namespace if provided (#228)<commit_after>/*
Copyright 2017 The Nuclio Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package runner

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/nuclio/nuclio/pkg/errors"
	"github.com/nuclio/nuclio/pkg/functioncr"
	"github.com/nuclio/nuclio/pkg/nuctl"
	"github.com/nuclio/nuclio/pkg/nuctl/builder"
	"github.com/nuclio/nuclio/pkg/util/common"

	"github.com/nuclio/nuclio-sdk"
	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type FunctionRunner struct {
	logger       nuclio.Logger
	options      *Options
	kubeConsumer *nuctl.KubeConsumer
}

type RunResult struct {
	NodePort int
}

func NewFunctionRunner(parentLogger nuclio.Logger) (*FunctionRunner, error) {
	newFunctionRunner := &FunctionRunner{
		logger: parentLogger.GetChild("runner").(nuclio.Logger),
	}

	return newFunctionRunner, nil
}

func (fr *FunctionRunner) Run(kubeConsumer *nuctl.KubeConsumer, options *Options) (*RunResult, error) {
	var runResult *RunResult

	// save options, consumer
	fr.options = options
	fr.kubeConsumer = kubeConsumer

	fr.logger.InfoWith("Running function", "name", options.Common.Identifier)

	// create a function, set default values and try to update from file
	functioncrInstance := functioncr.Function{}
	functioncrInstance.SetDefaults()
	functioncrInstance.Name = options.Common.Identifier

	if options.SpecPath != "" {
		err := functioncr.FromSpecFile(options.SpecPath, &functioncrInstance)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to read function spec file")
		}
	}

	// override with options
	if err := UpdateFunctioncrWithOptions(options, &functioncrInstance); err != nil {
		return nil, errors.Wrap(err, "Failed to update function with options")
	}

	if err := fr.deletePreexistingFunction(options.Common.Namespace, options.Common.Identifier); err != nil {
		return nil, errors.Wrap(err, "Failed to delete pre-existing function")
	}

	// create a builder
	builder, err := builder.NewFunctionBuilder(fr.logger)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to create builder")
	}

	// execute the build, set image
	processorImageName, err := builder.Build(&options.Build)
	if err != nil {
		return nil, err
	}

	// set the image
	functioncrInstance.Spec.Image = fmt.Sprintf("%s/%s", options.RunRegistry, processorImageName)

	// deploy the function
	err = fr.deployFunction(&functioncrInstance)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to deploy function")
	}

	// get the function (might take a few seconds til it's created)
	service, err := fr.getFunctionService(options.Common.Namespace, options.Common.Identifier)
	if err == nil {
		runResult = &RunResult{
			NodePort: int(service.Spec.Ports[0].NodePort),
		}
	}

	fr.logger.InfoWith("Function run complete", "node_port", runResult.NodePort)

	return runResult, nil
}

func UpdateFunctioncrWithOptions(options *Options, functioncrInstance *functioncr.Function) error {

	if options.Description != "" {
		functioncrInstance.Spec.Description = options.Description
	}

	// update replicas if scale was specified
	if options.Scale != "" {

		// TODO: handle/Set Min/Max replicas (used only with Auto mode)
		if options.Scale == "auto" {
			functioncrInstance.Spec.Replicas = 0
		} else {
			i, err := strconv.Atoi(options.Scale)
			if err != nil {
				return fmt.Errorf(`Invalid function scale, must be "auto" or an integer value`)
			} else {
				functioncrInstance.Spec.Replicas = int32(i)
			}
		}
	}

	// Set specified labels, is label = "" remove it (if exists)
	labels := common.StringToStringMap(options.Labels)

	// create map if it doesn't exist and there are labels
	if len(labels) > 0 && functioncrInstance.Labels == nil {
		functioncrInstance.Labels = map[string]string{}
	}

	for labelName, labelValue := range labels {
		if labelName != "name" && labelName != "version" && labelName != "alias" {
			if labelValue == "" {
				delete(functioncrInstance.Labels, labelName)
			} else {
				functioncrInstance.Labels[labelName] = labelValue
			}
		}
	}

	envmap := common.StringToStringMap(options.Env)
	newenv := []v1.EnvVar{}

	// merge new Environment var: update existing then add new
	for _, e := range functioncrInstance.Spec.Env {
		if v, ok := envmap[e.Name]; ok {
			if v != "" {
				newenv = append(newenv, v1.EnvVar{Name: e.Name, Value: v})
			}
			delete(envmap, e.Name)
		} else {
			newenv = append(newenv, e)
		}
	}

	for k, v := range envmap {
		newenv = append(newenv, v1.EnvVar{Name: k, Value: v})
	}

	functioncrInstance.Spec.Env = newenv

	if options.HTTPPort != 0 {
		functioncrInstance.Spec.HTTPPort = options.HTTPPort
	}

	if options.Publish {
		functioncrInstance.Spec.Publish = options.Publish
	}

	if options.Disabled {
		functioncrInstance.Spec.Disabled = options.Disabled // TODO: use string to detect if noop/true/false
	}

	// update data bindings
	functioncrInstance.Spec.DataBindings = options.DataBindings

	if options.Common.Namespace != "" {
		functioncrInstance.Namespace = options.Common.Namespace
	}

	return nil
}

func updateDataBindings(encodedDataBindings string, function *functioncr.Function) error {

	// if user passed nothing, no data bindings required
	if encodedDataBindings == "" {
		return nil
	}

	return json.Unmarshal([]byte(encodedDataBindings), &function.Spec.DataBindings)
}

func (fr *FunctionRunner) deployFunction(functioncrToCreate *functioncr.Function) error {

	// get invocation logger. if it wasn't passed, use instance logger
	fr.options.Common.GetLogger(fr.logger).DebugWith("Deploying function", "function", functioncrToCreate)

	createdFunctioncr, err := fr.kubeConsumer.FunctioncrClient.Create(functioncrToCreate)
	if err != nil {
		return err
	}

	// wait until function is processed
	return fr.kubeConsumer.FunctioncrClient.WaitUntilCondition(createdFunctioncr.Namespace,
		createdFunctioncr.Name,
		functioncr.WaitConditionProcessed,
		10*time.Second,
	)
}

func (fr *FunctionRunner) getFunctionService(namespace string, name string) (service *v1.Service, err error) {
	deadline := time.Now().Add(10 * time.Second)

	for {

		// after a few seconds, give up
		if time.Now().After(deadline) {
			break
		}

		service, err = fr.kubeConsumer.Clientset.CoreV1().Services(namespace).Get(name, meta_v1.GetOptions{})

		// if there was an error other than the fact that the service wasn't found,
		// return now
		if !apierrors.IsNotFound(err) {
			return
		}

		// if we got a service, check that it has a node port
		if service != nil && len(service.Spec.Ports) > 0 && service.Spec.Ports[0].NodePort != 0 {
			return
		}

		// wait a bit
		time.Sleep(1 * time.Second)
	}

	return
}

func (fr *FunctionRunner) deletePreexistingFunction(namespace string, name string) error {

	// before we do anything, delete the current version of the function if it exists
	_, err := fr.kubeConsumer.FunctioncrClient.Get(namespace, name)

	// note that existingFunctioncrInstance will contain a value regardless of whether there was an error
	if err != nil {

		// if it wasn't a not found error, log a warning
		if !apierrors.IsNotFound(err) {

			// don't fail, maybe we'll succeed in deploying
			fr.logger.WarnWith("Failed to get function while checking if it already exists", "err", err)
		}

	} else {

		// if the function exists, delete it
		fr.logger.InfoWith("Function already exists, deleting")

		if err := fr.kubeConsumer.FunctioncrClient.Delete(namespace, name, &meta_v1.DeleteOptions{}); err != nil {

			// don't fail
			fr.logger.WarnWith("Failed to delete existing function", "err", err)
		} else {

			// wait a bit to work around a controller bug
			time.Sleep(2 * time.Second)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package helm

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/util"
	"github.com/Aptomi/aptomi/pkg/util/retry"
	"io/ioutil"
	"k8s.io/helm/cmd/helm/installer"
	"k8s.io/helm/pkg/helm"
	"k8s.io/helm/pkg/helm/portforwarder"
	"k8s.io/helm/pkg/kube"
	"k8s.io/helm/pkg/repo"
	"strings"
)

func getHelmReleaseInfo(params util.NestedParameterMap) (repository, name, version string, err error) {
	repository, ok := params["chartRepo"].(string)
	if !ok {
		err = fmt.Errorf("chartRepo is a mandatory paraneter")
		return
	}

	name, ok = params["chartName"].(string)
	if !ok {
		err = fmt.Errorf("chartName is a mandatory paraneter")
		return
	}

	version = params["chartVersion"].(string)

	return
}

func getHelmReleaseName(deployName string) string {
	return strings.ToLower(util.EscapeName(deployName))
}

func findHelmRelease(helmClient *helm.Client, name string) (bool, error) {
	// todo(slukjanov): use release list filter
	resp, err := helmClient.ListReleases()
	if err != nil {
		return false, err
	}

	for _, release := range resp.Releases {
		if release.Name == name {
			return true, nil
		}
	}

	return false, nil
}

func (cache *clusterCache) newHelmClient(eventLog *event.Log) (*helm.Client, error) {
	return helm.NewClient(helm.Host(cache.tillerHost)), nil
}

func (cache *clusterCache) ensureTillerTunnel(eventLog *event.Log) error {
	if len(cache.tillerHost) > 0 {
		// todo(slukjanov): verify that tunnel is still alive??
		// connection already set up, skip
		return nil
	}

	var tunnelErr error
	ok := retry.Do(120, 5, func() bool {
		cache.tillerTunnel, tunnelErr = cache.newTillerTunnel()

		if tunnelErr != nil {
			if strings.Contains(tunnelErr.Error(), "could not find tiller") {
				err := cache.setupTiller(eventLog)
				if err != nil {
					tunnelErr = err
					return false
				}
			}

			return false
		}

		port := cache.tillerTunnel.Local
		cache.tillerHost = fmt.Sprintf("localhost:%d", port)
		eventLog.WithFields(event.Fields{}).Debugf("Created k8s tunnel using local port: %d", port)

		return true
	})

	if !ok {
		if tunnelErr != nil {
			return tunnelErr
		}

		return fmt.Errorf("tiller tunnel creation timeout for cluster: %s", cache.cluster.Name)
	}

	return nil
}

func (cache *clusterCache) newTillerTunnel() (*kube.Tunnel, error) {
	client, err := cache.newKubeClient()
	if err != nil {
		return nil, err
	}

	return portforwarder.New(cache.tillerNamespace, client, cache.kubeConfig)
}

func (cache *clusterCache) setupTiller(eventLog *event.Log) error {
	client, err := cache.newKubeClient()
	if err != nil {
		return err
	}

	eventLog.WithFields(event.Fields{}).Debugf("Setting up tiller in cluster %s namespace %s", cache.cluster.Name, cache.tillerNamespace)

	err = cache.ensureKubeNamespace(client, cache.tillerNamespace)
	if err != nil {
		return err
	}

	err = cache.createKubeServiceAccount(client, cache.tillerNamespace)
	if err != nil {
		return err
	}

	err = cache.createKubeClusterRoleBinding(client, cache.tillerNamespace)
	if err != nil {
		return err
	}

	return installer.Install(client, &installer.Options{
		Namespace:      cache.tillerNamespace,
		ImageSpec:      "gcr.io/kubernetes-helm/tiller:v2.6.2",
		ServiceAccount: "tiller-" + cache.tillerNamespace,
	})
}

func (plugin *Plugin) fetchChart(repository, name, version string) (string, error) {
	chartURL, err := repo.FindChartInRepoURL(
		repository, name, version,
		"", "", "",
		newGetterProviders(),
	)
	if err != nil {
		return "", fmt.Errorf("error while getting chart url in repo: %s", err)
	}

	chartFile, err := ioutil.TempFile("", name)
	if err != nil {
		return "", fmt.Errorf("error while creating temp file for downloading chart: %s", err)
	}

	chartGetter, err := newHTTPGetter(chartURL, "", "", "")
	if err != nil {
		return "", fmt.Errorf("error while creating chart downloader: %s", err)
	}

	resp, err := chartGetter.Get(chartURL)
	if err != nil {
		return "", fmt.Errorf("error while downloading chart: %s", err)
	}

	_, err = chartFile.Write(resp.Bytes())
	if err != nil {
		return "", fmt.Errorf("error while writing downloaded chart to the temp file")
	}

	return chartFile.Name(), nil
}
<commit_msg>Add todo about need to awaiting for tiller to start up<commit_after>package helm

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/util"
	"github.com/Aptomi/aptomi/pkg/util/retry"
	"io/ioutil"
	"k8s.io/helm/cmd/helm/installer"
	"k8s.io/helm/pkg/helm"
	"k8s.io/helm/pkg/helm/portforwarder"
	"k8s.io/helm/pkg/kube"
	"k8s.io/helm/pkg/repo"
	"strings"
)

func getHelmReleaseInfo(params util.NestedParameterMap) (repository, name, version string, err error) {
	repository, ok := params["chartRepo"].(string)
	if !ok {
		err = fmt.Errorf("chartRepo is a mandatory paraneter")
		return
	}

	name, ok = params["chartName"].(string)
	if !ok {
		err = fmt.Errorf("chartName is a mandatory paraneter")
		return
	}

	version = params["chartVersion"].(string)

	return
}

func getHelmReleaseName(deployName string) string {
	return strings.ToLower(util.EscapeName(deployName))
}

func findHelmRelease(helmClient *helm.Client, name string) (bool, error) {
	// todo(slukjanov): use release list filter
	resp, err := helmClient.ListReleases()
	if err != nil {
		return false, err
	}

	for _, release := range resp.Releases {
		if release.Name == name {
			return true, nil
		}
	}

	return false, nil
}

func (cache *clusterCache) newHelmClient(eventLog *event.Log) (*helm.Client, error) {
	return helm.NewClient(helm.Host(cache.tillerHost)), nil
}

func (cache *clusterCache) ensureTillerTunnel(eventLog *event.Log) error {
	if len(cache.tillerHost) > 0 {
		// todo(slukjanov): verify that tunnel is still alive??
		// connection already set up, skip
		return nil
	}

	var tunnelErr error
	ok := retry.Do(120, 5, func() bool {
		cache.tillerTunnel, tunnelErr = cache.newTillerTunnel()

		if tunnelErr != nil {
			if strings.Contains(tunnelErr.Error(), "could not find tiller") {
				err := cache.setupTiller(eventLog)
				if err != nil {
					tunnelErr = err
					return false
				}
			}

			return false
		}

		port := cache.tillerTunnel.Local
		cache.tillerHost = fmt.Sprintf("localhost:%d", port)
		eventLog.WithFields(event.Fields{}).Debugf("Created k8s tunnel using local port: %d", port)

		return true
	})

	if !ok {
		if tunnelErr != nil {
			return tunnelErr
		}

		return fmt.Errorf("tiller tunnel creation timeout for cluster: %s", cache.cluster.Name)
	}

	return nil
}

func (cache *clusterCache) newTillerTunnel() (*kube.Tunnel, error) {
	client, err := cache.newKubeClient()
	if err != nil {
		return nil, err
	}

	return portforwarder.New(cache.tillerNamespace, client, cache.kubeConfig)
}

func (cache *clusterCache) setupTiller(eventLog *event.Log) error {
	client, err := cache.newKubeClient()
	if err != nil {
		return err
	}

	eventLog.WithFields(event.Fields{}).Debugf("Setting up tiller in cluster %s namespace %s", cache.cluster.Name, cache.tillerNamespace)

	err = cache.ensureKubeNamespace(client, cache.tillerNamespace)
	if err != nil {
		return err
	}

	err = cache.createKubeServiceAccount(client, cache.tillerNamespace)
	if err != nil {
		return err
	}

	err = cache.createKubeClusterRoleBinding(client, cache.tillerNamespace)
	if err != nil {
		return err
	}

	// todo wait until tiller available
	return installer.Install(client, &installer.Options{
		Namespace:      cache.tillerNamespace,
		ImageSpec:      "gcr.io/kubernetes-helm/tiller:v2.6.2",
		ServiceAccount: "tiller-" + cache.tillerNamespace,
	})
}

func (plugin *Plugin) fetchChart(repository, name, version string) (string, error) {
	chartURL, err := repo.FindChartInRepoURL(
		repository, name, version,
		"", "", "",
		newGetterProviders(),
	)
	if err != nil {
		return "", fmt.Errorf("error while getting chart url in repo: %s", err)
	}

	chartFile, err := ioutil.TempFile("", name)
	if err != nil {
		return "", fmt.Errorf("error while creating temp file for downloading chart: %s", err)
	}

	chartGetter, err := newHTTPGetter(chartURL, "", "", "")
	if err != nil {
		return "", fmt.Errorf("error while creating chart downloader: %s", err)
	}

	resp, err := chartGetter.Get(chartURL)
	if err != nil {
		return "", fmt.Errorf("error while downloading chart: %s", err)
	}

	_, err = chartFile.Write(resp.Bytes())
	if err != nil {
		return "", fmt.Errorf("error while writing downloaded chart to the temp file")
	}

	return chartFile.Name(), nil
}
<|endoftext|>


<commit_before>// Copyright 2015,2016,2017,2018,2019 SeukWon Kang (kasworld@gmail.com)
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package basiclog

import (
	"bytes"
	"fmt"
	"math/bits"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/kasworld/log/logdestination_file"
	"github.com/kasworld/log/logdestination_stdio"
	"github.com/kasworld/log/logdestinationgroup"
	"github.com/kasworld/log/logdestinationi"
	"github.com/kasworld/log/logflagi"
	"github.com/kasworld/log/logflags"
)

type LL_Type uint64

func (ll LL_Type) ToShiftedNum() int {
	return bits.Len(uint(ll)) - 1
}

func (ll LL_Type) LevelsString() string {
	var buff bytes.Buffer

	buff.WriteString("LL_Type[")
	for i := LL_Type(1); i < LL_END; i <<= 1 {
		if ll.IsLevel(i) {
			fmt.Fprintf(&buff, "%s, ", i)
		}
	}
	buff.WriteString("]")
	return buff.String()
}

func (ll LL_Type) IsLevel(level LL_Type) bool {
	return ll&level != 0
}

func (i LL_Type) String() string {
	if str, ok := leveldata[i]; ok {
		return str
	}
	return "LL_Type(" + strconv.FormatInt(int64(i), 10) + ")"
}

///////////////////////////////////////////////////////////////////////////////////////////

var (
	OutputStdout = logdestination_stdio.NewStdOut()
	OutputStderr = logdestination_stdio.NewStdErr()
)

type logDestInfo struct {
	refCntByLogLv int // count referenced by each loglv
	dest          logdestinationi.LogDestinationI
}

type LogBase struct {
	mutex sync.RWMutex

	flag     logflagi.LogFlagI // properties
	prefix   string            // prefix to write at beginning of each line
	loglevel LL_Type

	ltype2destgrp     []*logdestinationgroup.LogDestinationGroup
	allDestInfoByName map[string]*logDestInfo
}

func New(prefix string, lf logflagi.LogFlagI, lv LL_Type) *LogBase {

	maxlen := LL_END.ToShiftedNum()
	dstgrp := make([]*logdestinationgroup.LogDestinationGroup, maxlen)
	for i := 0; i < maxlen; i++ {
		dstgrp[i] = logdestinationgroup.New()
	}

	return &LogBase{
		ltype2destgrp:     dstgrp,
		allDestInfoByName: make(map[string]*logDestInfo),
		flag:              lf,
		prefix:            prefix,
		loglevel:          lv,
	}
}

func NewWithDstDir(prefix string, logdir string, lf logflagi.LogFlagI,
	loglevel LL_Type, splitLogLevel LL_Type) (*LogBase, error) {
	logdir = strings.TrimSpace(logdir)
	if logdir == "" {
		return nil, fmt.Errorf("logdir empty %v", logdir)
	}
	if fileinfo, err := os.Stat(logdir); err != nil { // PathError
		if mkerr := os.Mkdir(logdir, os.ModePerm); mkerr != nil {
			return nil, mkerr
		}
	} else if fileinfo.IsDir() == false {
		return nil, fmt.Errorf("not a directory %v", logdir)
	}

	basename := filepath.Base(logdir)
	newlg := New(prefix, lf, loglevel)

	fnameForOther := fmt.Sprintf("%s.%s.%s", basename, "Other", "log")
	fpathForOther := filepath.Join(logdir, fnameForOther)
	newDestForOther, err := logdestination_file.New(fpathForOther)
	if err != nil {
		return nil, err
	}
	newlg.AddDestination(LL_All^splitLogLevel, newDestForOther)

	for ll := LL_Type(1); ll < LL_END; ll <<= 1 {
		if splitLogLevel&ll == ll {
			fnameForLL := fmt.Sprintf("%s.%s.%s", basename, ll.String(), "log")
			fpathForLL := filepath.Join(logdir, fnameForLL)
			newDestForLL, serr := logdestination_file.New(fpathForLL)
			if serr != nil {
				return nil, serr
			}
			newlg.AddDestination(ll, newDestForLL)
		}
	}
	newlg.AddDestination(LL_Fatal, OutputStdout)
	newlg.AddDestination(LL_Fatal, OutputStderr)
	return newlg, nil
}

func (lg *LogBase) AddDestination(
	ll LL_Type, o logdestinationi.LogDestinationI) {

	lg.mutex.Lock()
	defer lg.mutex.Unlock()

	for i := 0; i < len(lg.ltype2destgrp); i++ {
		s := LL_Type(1 << uint(i))
		if ll&s == 0 {
			continue
		}
		lg.addDestination1DestGrp(i, o)
	}
}

func (lg *LogBase) addDestination1DestGrp(
	i int, o logdestinationi.LogDestinationI) {

	added := lg.ltype2destgrp[i].AddDestination(o)
	if !added {
		if _, ok := lg.allDestInfoByName[o.Name()]; !ok {
			panic(fmt.Sprintf(
				"%v failed to AddDestination to destgroup index:%v, abnormal state",
				lg,
				i))
		}
		fmt.Printf("%v not added to destgroup index:%v\n", o, i)
		return
	}

	if dstinfo, ok := lg.allDestInfoByName[o.Name()]; ok {
		dstinfo.refCntByLogLv++
	} else {
		lg.allDestInfoByName[o.Name()] = &logDestInfo{
			refCntByLogLv: 1,
			dest:          o,
		}
	}
}

func (lg *LogBase) DelDestination(
	ll LL_Type, o logdestinationi.LogDestinationI) {

	lg.mutex.Lock()
	defer lg.mutex.Unlock()

	for i := 0; i < len(lg.ltype2destgrp); i++ {
		s := LL_Type(1 << uint(i))
		if ll&s == 0 {
			continue
		}
		lg.delDestinationFrom1DestGrp(i, o)
	}
}

func (lg *LogBase) delDestinationFrom1DestGrp(
	i int, o logdestinationi.LogDestinationI) {

	deleted := lg.ltype2destgrp[i].DelDestination(o)
	if !deleted {
		fmt.Printf("%v not deleted from destgroup index:%v\n", o, i)
		return
	}

	if dstinfo, ok := lg.allDestInfoByName[o.Name()]; ok {
		dstinfo.refCntByLogLv--
		if dstinfo.refCntByLogLv <= 0 {
			delete(lg.allDestInfoByName, o.Name())
		}
	} else {
		panic(fmt.Sprintf(
			"%v failed to DelDestination %v from destgroup index:%v, abnormal state",
			lg,
			o,
			i,
		))
	}
}

func (lg *LogBase) Reload() error {
	lg.mutex.RLock()
	defer lg.mutex.RUnlock()

	for _, v := range lg.allDestInfoByName {
		if err := v.dest.Reload(); err != nil {
			fmt.Println(err)
		}
	}
	return nil
}

func (lg *LogBase) LogPrintf(
	calldepth int, ll LL_Type,
	format string, v ...interface{}) ([]byte, error) {
	s := lg.Format2Bytes(calldepth+1, ll, format, v...)
	err := lg.Output(ll, s)
	return s, err
}

func (lg *LogBase) Format2Bytes(
	calldepth int, ll LL_Type,
	format string, v ...interface{}) []byte {

	if !lg.IsLevel(ll) {
		return nil
	}
	s := fmt.Sprintf(format, v...)

	var buf []byte
	llinfo := fmt.Sprintf("%s", ll)
	lg.flag.FormatHeader(&buf, calldepth+2, time.Now(), lg.prefix, llinfo)
	buf = append(buf, s...)
	if len(s) == 0 || s[len(s)-1] != '\n' {
		buf = append(buf, '\n')
	}
	return buf
}

func (lg *LogBase) Output(ll LL_Type, b []byte) error {
	i := ll.ToShiftedNum()
	return lg.ltype2destgrp[i].Write(b)
}

func (lg *LogBase) Panic(format string, v ...interface{}) error {
	s, err := lg.LogPrintf(2, LL_Fatal, format, v...)
	panic(string(s))
	return err
}

func (lg *LogBase) AddLevel(level LL_Type) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.loglevel |= level
}

func (lg *LogBase) SetLevel(level LL_Type) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.loglevel = level
}

func (lg *LogBase) DelLevel(level LL_Type) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.loglevel &= ^level
}

func (lg *LogBase) IsLevel(level LL_Type) bool {
	return lg.loglevel&level != 0
}

func (lg *LogBase) FlagString() string {
	return lg.flag.FlagString()
}

func (lg *LogBase) LevelString() string {
	return lg.loglevel.LevelsString()
}

func (lg *LogBase) SetPrefix(p string) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.prefix = p
}

// Prefix returns the output prefix for the logger.
func (lg *LogBase) GetPrefix() string {
	return lg.prefix
}

// Flags returns the output flags for the logger.
func (lg *LogBase) GetFlags() logflagi.LogFlagI {
	return lg.flag
}

// SetFlags sets the output flags for the logger.
func (lg *LogBase) SetFlags(flag logflagi.LogFlagI) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.flag = flag
}

func (lg *LogBase) String() string {
	return fmt.Sprintf("LogBase[%v %v]",
		lg.FlagString(), lg.LevelString(),
	)
}

//////////////////////////////////////////////////////////////////

var GlobalLogger = New("", logflags.DefaultValue(false), LL_All)

func init() {
	GlobalLogger.AddDestination(LL_All, OutputStderr)
}

func Reload() error {
	return GlobalLogger.Reload()
}

func GetLogger() *LogBase {
	return GlobalLogger
}
func SetLogger(l *LogBase) {
	GlobalLogger = l
}

func LevelString() string {
	return GlobalLogger.String()
}

func AddLevel(level LL_Type) {
	GlobalLogger.AddLevel(level)
}

func SetLevel(level LL_Type) {
	GlobalLogger.SetLevel(level)
}

func DelLevel(level LL_Type) {
	GlobalLogger.DelLevel(level)
}

func IsLevel(level LL_Type) bool {
	return GlobalLogger.IsLevel(level)
}

func SetPrefix(p string) {
	GlobalLogger.SetPrefix(p)
}

// Prefix returns the output prefix for the GlobalLogger.
func GetPrefix() string {
	return GlobalLogger.GetPrefix()
}

// Flags returns the output flags for the GlobalLogger.
func GetFlags() logflagi.LogFlagI {
	return GlobalLogger.GetFlags()
}

// SetFlags sets the output flags for the GlobalLogger.
func SetFlags(flag logflagi.LogFlagI) {
	GlobalLogger.SetFlags(flag)
}

func Panic(format string, v ...interface{}) error {
	s, err := GlobalLogger.LogPrintf(2, LL_Fatal, format, v...)
	panic(string(s))
	return err
}

func SetLog(logdir string, loglevel LL_Type, splitLogLevel LL_Type) error {
	if logdir != "" {
		newlg, err := NewWithDstDir(
			"",
			logdir,
			logflags.DefaultValue(false),
			loglevel,
			splitLogLevel,
		)
		if err != nil {
			return err
		}
		SetLogger(newlg)
	} else {
		SetLevel(loglevel)
	}
	return nil
}
<commit_msg>update logbase<commit_after>// Copyright 2015,2016,2017,2018,2019 SeukWon Kang (kasworld@gmail.com)
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package basiclog

import (
	"bytes"
	"fmt"
	"math/bits"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/kasworld/log/logdestination_file"
	"github.com/kasworld/log/logdestination_stdio"
	"github.com/kasworld/log/logdestinationgroup"
	"github.com/kasworld/log/logdestinationi"
	"github.com/kasworld/log/logflagi"
	"github.com/kasworld/log/logflags"
)

type LL_Type uint64

func (ll LL_Type) ToShiftedNum() int {
	return bits.Len(uint(ll)) - 1
}

func (ll LL_Type) LevelsString() string {
	var buff bytes.Buffer

	buff.WriteString("LL_Type[")
	for i := LL_Type(1); i < LL_END; i <<= 1 {
		if ll.IsLevel(i) {
			fmt.Fprintf(&buff, "%s, ", i)
		}
	}
	buff.WriteString("]")
	return buff.String()
}

func (ll LL_Type) IsLevel(level LL_Type) bool {
	return ll&level != 0
}

func (i LL_Type) String() string {
	if str, ok := leveldata[i]; ok {
		return str
	}
	return "LL_Type(" + strconv.FormatInt(int64(i), 10) + ")"
}

///////////////////////////////////////////////////////////////////////////////////////////

var (
	OutputStdout = logdestination_stdio.NewStdOut()
	OutputStderr = logdestination_stdio.NewStdErr()
)

type logDestInfo struct {
	refCntByLogLv int // count referenced by each loglv
	dest          logdestinationi.LogDestinationI
}

type LogBase struct {
	mutex sync.RWMutex

	flag     logflagi.LogFlagI // properties
	prefix   string            // prefix to write at beginning of each line
	loglevel LL_Type

	ltype2destgrp     []*logdestinationgroup.LogDestinationGroup
	allDestInfoByName map[string]*logDestInfo
}

func New(prefix string, lf logflagi.LogFlagI, lv LL_Type) *LogBase {

	maxlen := LL_END.ToShiftedNum()
	dstgrp := make([]*logdestinationgroup.LogDestinationGroup, maxlen)
	for i := 0; i < maxlen; i++ {
		dstgrp[i] = logdestinationgroup.New()
	}

	return &LogBase{
		ltype2destgrp:     dstgrp,
		allDestInfoByName: make(map[string]*logDestInfo),
		flag:              lf,
		prefix:            prefix,
		loglevel:          lv,
	}
}

func makeLogFilename(logdir string, ll string) string {
	basename := filepath.Base(logdir)
	filename := fmt.Sprintf("%s.%s.%s", basename, ll, "log")
	return filepath.Join(logdir, filename)
}

func NewWithDstDir(prefix string, logdir string, lf logflagi.LogFlagI,
	loglevel LL_Type, splitLogLevel LL_Type) (*LogBase, error) {
	logdir = strings.TrimSpace(logdir)
	if logdir == "" {
		return nil, fmt.Errorf("logdir empty")
	}
	if err := os.MkdirAll(logdir, os.ModePerm); err != nil {
		return nil, err
	}
	newlg := New(prefix, lf, loglevel)
	newDestForOther, err := logdestination_file.New(
		makeLogFilename(logdir, "Other"))
	if err != nil {
		return nil, err
	}
	newlg.AddDestination(LL_All^splitLogLevel, newDestForOther)
	for ll := LL_Type(1); ll < LL_END; ll <<= 1 {
		if splitLogLevel&ll == ll {
			newDestForLL, serr := logdestination_file.New(
				makeLogFilename(logdir, ll.String()))
			if serr != nil {
				return nil, serr
			}
			newlg.AddDestination(ll, newDestForLL)
		}
	}
	newlg.AddDestination(LL_Fatal, OutputStderr)
	return newlg, nil
}

func (lg *LogBase) AddDestination(
	ll LL_Type, o logdestinationi.LogDestinationI) {

	lg.mutex.Lock()
	defer lg.mutex.Unlock()

	for i := 0; i < len(lg.ltype2destgrp); i++ {
		s := LL_Type(1 << uint(i))
		if ll&s == 0 {
			continue
		}
		lg.addDestination1DestGrp(i, o)
	}
}

func (lg *LogBase) addDestination1DestGrp(
	i int, o logdestinationi.LogDestinationI) {

	added := lg.ltype2destgrp[i].AddDestination(o)
	if !added {
		if _, ok := lg.allDestInfoByName[o.Name()]; !ok {
			panic(fmt.Sprintf(
				"%v failed to AddDestination to destgroup index:%v, abnormal state",
				lg,
				i))
		}
		fmt.Printf("%v not added to destgroup index:%v\n", o, i)
		return
	}

	if dstinfo, ok := lg.allDestInfoByName[o.Name()]; ok {
		dstinfo.refCntByLogLv++
	} else {
		lg.allDestInfoByName[o.Name()] = &logDestInfo{
			refCntByLogLv: 1,
			dest:          o,
		}
	}
}

func (lg *LogBase) DelDestination(
	ll LL_Type, o logdestinationi.LogDestinationI) {

	lg.mutex.Lock()
	defer lg.mutex.Unlock()

	for i := 0; i < len(lg.ltype2destgrp); i++ {
		s := LL_Type(1 << uint(i))
		if ll&s == 0 {
			continue
		}
		lg.delDestinationFrom1DestGrp(i, o)
	}
}

func (lg *LogBase) delDestinationFrom1DestGrp(
	i int, o logdestinationi.LogDestinationI) {

	deleted := lg.ltype2destgrp[i].DelDestination(o)
	if !deleted {
		fmt.Printf("%v not deleted from destgroup index:%v\n", o, i)
		return
	}

	if dstinfo, ok := lg.allDestInfoByName[o.Name()]; ok {
		dstinfo.refCntByLogLv--
		if dstinfo.refCntByLogLv <= 0 {
			delete(lg.allDestInfoByName, o.Name())
		}
	} else {
		panic(fmt.Sprintf(
			"%v failed to DelDestination %v from destgroup index:%v, abnormal state",
			lg,
			o,
			i,
		))
	}
}

func (lg *LogBase) Reload() error {
	lg.mutex.RLock()
	defer lg.mutex.RUnlock()

	for _, v := range lg.allDestInfoByName {
		if err := v.dest.Reload(); err != nil {
			fmt.Println(err)
		}
	}
	return nil
}

func (lg *LogBase) LogPrintf(
	calldepth int, ll LL_Type,
	format string, v ...interface{}) ([]byte, error) {
	s := lg.Format2Bytes(calldepth+1, ll, format, v...)
	err := lg.Output(ll, s)
	return s, err
}

func (lg *LogBase) Format2Bytes(
	calldepth int, ll LL_Type,
	format string, v ...interface{}) []byte {

	if !lg.IsLevel(ll) {
		return nil
	}
	s := fmt.Sprintf(format, v...)

	var buf []byte
	llinfo := fmt.Sprintf("%s", ll)
	lg.flag.FormatHeader(&buf, calldepth+2, time.Now(), lg.prefix, llinfo)
	buf = append(buf, s...)
	if len(s) == 0 || s[len(s)-1] != '\n' {
		buf = append(buf, '\n')
	}
	return buf
}

func (lg *LogBase) Output(ll LL_Type, b []byte) error {
	i := ll.ToShiftedNum()
	return lg.ltype2destgrp[i].Write(b)
}

func (lg *LogBase) Panic(format string, v ...interface{}) error {
	s, err := lg.LogPrintf(2, LL_Fatal, format, v...)
	panic(string(s))
	return err
}

func (lg *LogBase) AddLevel(level LL_Type) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.loglevel |= level
}

func (lg *LogBase) SetLevel(level LL_Type) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.loglevel = level
}

func (lg *LogBase) DelLevel(level LL_Type) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.loglevel &= ^level
}

func (lg *LogBase) IsLevel(level LL_Type) bool {
	return lg.loglevel&level != 0
}

func (lg *LogBase) FlagString() string {
	return lg.flag.FlagString()
}

func (lg *LogBase) LevelString() string {
	return lg.loglevel.LevelsString()
}

func (lg *LogBase) SetPrefix(p string) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.prefix = p
}

// Prefix returns the output prefix for the logger.
func (lg *LogBase) GetPrefix() string {
	return lg.prefix
}

// Flags returns the output flags for the logger.
func (lg *LogBase) GetFlags() logflagi.LogFlagI {
	return lg.flag
}

// SetFlags sets the output flags for the logger.
func (lg *LogBase) SetFlags(flag logflagi.LogFlagI) {
	lg.mutex.Lock()
	defer lg.mutex.Unlock()
	lg.flag = flag
}

func (lg *LogBase) String() string {
	return fmt.Sprintf("LogBase[%v %v]",
		lg.FlagString(), lg.LevelString(),
	)
}

//////////////////////////////////////////////////////////////////

var GlobalLogger = New("", logflags.DefaultValue(false), LL_All)

func init() {
	GlobalLogger.AddDestination(LL_All, OutputStderr)
}

func Reload() error {
	return GlobalLogger.Reload()
}

func GetLogger() *LogBase {
	return GlobalLogger
}
func SetLogger(l *LogBase) {
	GlobalLogger = l
}

func LevelString() string {
	return GlobalLogger.String()
}

func AddLevel(level LL_Type) {
	GlobalLogger.AddLevel(level)
}

func SetLevel(level LL_Type) {
	GlobalLogger.SetLevel(level)
}

func DelLevel(level LL_Type) {
	GlobalLogger.DelLevel(level)
}

func IsLevel(level LL_Type) bool {
	return GlobalLogger.IsLevel(level)
}

func SetPrefix(p string) {
	GlobalLogger.SetPrefix(p)
}

// Prefix returns the output prefix for the GlobalLogger.
func GetPrefix() string {
	return GlobalLogger.GetPrefix()
}

// Flags returns the output flags for the GlobalLogger.
func GetFlags() logflagi.LogFlagI {
	return GlobalLogger.GetFlags()
}

// SetFlags sets the output flags for the GlobalLogger.
func SetFlags(flag logflagi.LogFlagI) {
	GlobalLogger.SetFlags(flag)
}

func Panic(format string, v ...interface{}) error {
	s, err := GlobalLogger.LogPrintf(2, LL_Fatal, format, v...)
	panic(string(s))
	return err
}

func SetLog(logdir string, loglevel LL_Type, splitLogLevel LL_Type) error {
	if logdir != "" {
		newlg, err := NewWithDstDir(
			"",
			logdir,
			logflags.DefaultValue(false),
			loglevel,
			splitLogLevel,
		)
		if err != nil {
			return err
		}
		SetLogger(newlg)
	} else {
		SetLevel(loglevel)
	}
	return nil
}
<|endoftext|>


<commit_before>package mqe

import (
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/tsdb"
)

func NewQueryParser() *QueryParser {
	return &QueryParser{}
}

type QueryParser struct{}

func (qp *QueryParser) Parse(model *simplejson.Json, dsInfo *models.DataSource, queryContext *tsdb.QueryContext) (*Query, error) {
	query := &Query{TimeRange: queryContext.TimeRange}
	query.AddClusterToAlias = model.Get("addClusterToAlias").MustBool(false)
	query.AddHostToAlias = model.Get("addHostToAlias").MustBool(false)
	query.UseRawQuery = model.Get("rawQuery").MustBool(false)
	query.RawQuery = model.Get("query").MustString("")

	query.Cluster = model.Get("cluster").MustStringArray([]string{})
	query.Hosts = model.Get("hosts").MustStringArray([]string{})

	var metrics []Metric
	var err error
	for _, metricsObj := range model.Get("metrics").MustArray() {
		metricJson := simplejson.NewFromAny(metricsObj)
		var m Metric

		m.Alias = metricJson.Get("alias").MustString("")
		m.Metric, err = metricJson.Get("metric").String()
		if err != nil {
			return nil, err
		}

		metrics = append(metrics, m)
	}

	query.Metrics = metrics

	var functions []Function
	for _, functionListObj := range model.Get("functionList").MustArray() {
		functionListJson := simplejson.NewFromAny(functionListObj)
		var f Function

		f.Func = functionListJson.Get("func").MustString("")
		if err != nil {
			return nil, err
		}

		functions = append(functions, f)
	}

	query.FunctionList = functions

	return query, nil
}
<commit_msg>fix(mqe): only parse none empty functions<commit_after>package mqe

import (
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/tsdb"
)

func NewQueryParser() *QueryParser {
	return &QueryParser{}
}

type QueryParser struct{}

func (qp *QueryParser) Parse(model *simplejson.Json, dsInfo *models.DataSource, queryContext *tsdb.QueryContext) (*Query, error) {
	query := &Query{TimeRange: queryContext.TimeRange}
	query.AddClusterToAlias = model.Get("addClusterToAlias").MustBool(false)
	query.AddHostToAlias = model.Get("addHostToAlias").MustBool(false)
	query.UseRawQuery = model.Get("rawQuery").MustBool(false)
	query.RawQuery = model.Get("query").MustString("")

	query.Cluster = model.Get("cluster").MustStringArray([]string{})
	query.Hosts = model.Get("hosts").MustStringArray([]string{})

	var metrics []Metric
	var err error
	for _, metricsObj := range model.Get("metrics").MustArray() {
		metricJson := simplejson.NewFromAny(metricsObj)
		var m Metric

		m.Alias = metricJson.Get("alias").MustString("")
		m.Metric, err = metricJson.Get("metric").String()
		if err != nil {
			return nil, err
		}

		metrics = append(metrics, m)
	}

	query.Metrics = metrics

	var functions []Function
	for _, functionListObj := range model.Get("functionList").MustArray() {
		functionListJson := simplejson.NewFromAny(functionListObj)
		var f Function

		f.Func = functionListJson.Get("func").MustString("")
		if err != nil {
			return nil, err
		}

		if f.Func != "" {
			functions = append(functions, f)
		}
	}

	query.FunctionList = functions

	return query, nil
}
<|endoftext|>