<commit_before>package plugin

import (
	"fmt"
	"net"
	"sort"
	"strconv"
	"strings"

	log "github.com/golang/glog"

	kapi "k8s.io/kubernetes/pkg/api"
	kapiunversioned "k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/client/retry"
	"k8s.io/kubernetes/pkg/types"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	utilwait "k8s.io/kubernetes/pkg/util/wait"

	osapi "github.com/openshift/origin/pkg/sdn/api"
	"github.com/openshift/origin/pkg/util/netutils"
)

func (master *OsdnMaster) SubnetStartMaster(clusterNetwork *net.IPNet, hostSubnetLength uint32) error {
	subrange := make([]string, 0)
	subnets, err := master.osClient.HostSubnets().List(kapi.ListOptions{})
	if err != nil {
		log.Errorf("Error in initializing/fetching subnets: %v", err)
		return err
	}
	for _, sub := range subnets.Items {
		subrange = append(subrange, sub.Subnet)
		if err = master.networkInfo.validateNodeIP(sub.HostIP); err != nil {
			// Don't error out; just warn so the error can be corrected with 'oc'
			log.Errorf("Failed to validate HostSubnet %s: %v", hostSubnetToString(&sub), err)
		} else {
			log.Infof("Found existing HostSubnet %s", hostSubnetToString(&sub))
		}
	}

	master.subnetAllocator, err = netutils.NewSubnetAllocator(clusterNetwork.String(), hostSubnetLength, subrange)
	if err != nil {
		return err
	}

	go utilwait.Forever(master.watchNodes, 0)
	go utilwait.Forever(master.watchSubnets, 0)
	return nil
}

func (master *OsdnMaster) addNode(nodeName string, nodeIP string, hsAnnotations map[string]string) error {
	// Validate node IP before proceeding
	if err := master.networkInfo.validateNodeIP(nodeIP); err != nil {
		return err
	}

	// Check if subnet needs to be created or updated
	sub, err := master.osClient.HostSubnets().Get(nodeName)
	if err == nil {
		if sub.HostIP == nodeIP {
			return nil
		} else {
			// Node IP changed, update old subnet
			sub.HostIP = nodeIP
			sub, err = master.osClient.HostSubnets().Update(sub)
			if err != nil {
				return fmt.Errorf("error updating subnet %s for node %s: %v", sub.Subnet, nodeName, err)
			}
			log.Infof("Updated HostSubnet %s", hostSubnetToString(sub))
			return nil
		}
	}

	// Create new subnet
	sn, err := master.subnetAllocator.GetNetwork()
	if err != nil {
		return fmt.Errorf("error allocating network for node %s: %v", nodeName, err)
	}

	sub = &osapi.HostSubnet{
		TypeMeta:   kapiunversioned.TypeMeta{Kind: "HostSubnet"},
		ObjectMeta: kapi.ObjectMeta{Name: nodeName, Annotations: hsAnnotations},
		Host:       nodeName,
		HostIP:     nodeIP,
		Subnet:     sn.String(),
	}
	sub, err = master.osClient.HostSubnets().Create(sub)
	if err != nil {
		master.subnetAllocator.ReleaseNetwork(sn)
		return fmt.Errorf("error creating subnet %s for node %s: %v", sn.String(), nodeName, err)
	}
	log.Infof("Created HostSubnet %s", hostSubnetToString(sub))
	return nil
}

func (master *OsdnMaster) deleteNode(nodeName string) error {
	sub, err := master.osClient.HostSubnets().Get(nodeName)
	if err != nil {
		return fmt.Errorf("error fetching subnet for node %q for deletion: %v", nodeName, err)
	}
	err = master.osClient.HostSubnets().Delete(nodeName)
	if err != nil {
		return fmt.Errorf("error deleting subnet %v for node %q: %v", sub, nodeName, err)
	}

	log.Infof("Deleted HostSubnet %s", hostSubnetToString(sub))
	return nil
}

func isValidNodeIP(node *kapi.Node, nodeIP string) bool {
	for _, addr := range node.Status.Addresses {
		if addr.Address == nodeIP {
			return true
		}
	}
	return false
}

func getNodeIP(node *kapi.Node) (string, error) {
	if len(node.Status.Addresses) > 0 && node.Status.Addresses[0].Address != "" {
		return node.Status.Addresses[0].Address, nil
	} else {
		return netutils.GetNodeIP(node.Name)
	}
}

// Because openshift-sdn uses an overlay and doesn't need GCE Routes, we need to
// clear the NetworkUnavailable condition that kubelet adds to initial node
// status when using GCE.
// TODO: make upstream kubelet more flexible with overlays and GCE so this
// condition doesn't get added for network plugins that don't want it, and then
// we can remove this function.
func (master *OsdnMaster) clearInitialNodeNetworkUnavailableCondition(node *kapi.Node) {
	knode := node
	cleared := false
	resultErr := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		var err error

		if knode != node {
			knode, err = master.kClient.Nodes().Get(node.ObjectMeta.Name)
			if err != nil {
				return err
			}
		}

		// Let caller modify knode's status, then push to api server.
		_, condition := kapi.GetNodeCondition(&node.Status, kapi.NodeNetworkUnavailable)
		if condition != nil && condition.Status != kapi.ConditionFalse && condition.Reason == "NoRouteCreated" {
			condition.Status = kapi.ConditionFalse
			condition.Reason = "RouteCreated"
			condition.Message = "openshift-sdn cleared kubelet-set NoRouteCreated"
			condition.LastTransitionTime = kapiunversioned.Now()
			knode, err = master.kClient.Nodes().UpdateStatus(knode)
			if err == nil {
				cleared = true
			}
		}
		return err
	})
	if resultErr != nil {
		utilruntime.HandleError(fmt.Errorf("status update failed for local node: %v", resultErr))
	} else if cleared {
		log.Infof("Cleared node NetworkUnavailable/NoRouteCreated condition for %s", node.ObjectMeta.Name)
	}
}

func (master *OsdnMaster) watchNodes() {
	nodeAddressMap := map[types.UID]string{}
	RunEventQueue(master.kClient.CoreClient.RESTClient(), Nodes, func(delta cache.Delta) error {
		node := delta.Object.(*kapi.Node)
		name := node.ObjectMeta.Name
		uid := node.ObjectMeta.UID

		nodeIP, err := getNodeIP(node)
		if err != nil {
			return fmt.Errorf("failed to get node IP for %s, skipping event: %v, node: %v", name, delta.Type, node)
		}

		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			master.clearInitialNodeNetworkUnavailableCondition(node)

			if oldNodeIP, ok := nodeAddressMap[uid]; ok && ((nodeIP == oldNodeIP) || isValidNodeIP(node, oldNodeIP)) {
				break
			}
			// Node status is frequently updated by kubelet, so log only if the above condition is not met
			log.V(5).Infof("Watch %s event for Node %q", delta.Type, name)

			err = master.addNode(name, nodeIP, nil)
			if err != nil {
				return fmt.Errorf("error creating subnet for node %s, ip %s: %v", name, nodeIP, err)
			}
			nodeAddressMap[uid] = nodeIP
		case cache.Deleted:
			log.V(5).Infof("Watch %s event for Node %q", delta.Type, name)
			delete(nodeAddressMap, uid)

			err = master.deleteNode(name)
			if err != nil {
				return fmt.Errorf("error deleting node %s: %v", name, err)
			}
		}
		return nil
	})
}

func (node *OsdnNode) SubnetStartNode() error {
	go utilwait.Forever(node.watchSubnets, 0)
	return nil
}

// Only run on the master
// Watch for all hostsubnet events and if one is found with the right annotation, use the SubnetAllocator to dole a real subnet
func (master *OsdnMaster) watchSubnets() {
	RunEventQueue(master.osClient, HostSubnets, func(delta cache.Delta) error {
		hs := delta.Object.(*osapi.HostSubnet)
		name := hs.ObjectMeta.Name
		hostIP := hs.HostIP
		subnet := hs.Subnet

		log.V(5).Infof("Watch %s event for HostSubnet %q", delta.Type, hs.ObjectMeta.Name)
		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			if _, ok := hs.Annotations[osapi.AssignHostSubnetAnnotation]; ok {
				// Delete the annotated hostsubnet and create a new one with an assigned subnet
				// We do not update (instead of delete+create) because the watchSubnets on the nodes
				// will skip the event if it finds that the hostsubnet has the same host
				// And we cannot fix the watchSubnets code for node because it will break migration if
				// nodes are upgraded after the master
				err := master.osClient.HostSubnets().Delete(name)
				if err != nil {
					log.Errorf("Error in deleting annotated subnet from master, name: %s, ip %s: %v", name, hostIP, err)
					return nil
				}
				var hsAnnotations map[string]string
				if vnid, ok := hs.Annotations[osapi.FixedVNIDHostAnnotation]; ok {
					vnidInt, err := strconv.Atoi(vnid)
					if err == nil && vnidInt >= 0 && uint32(vnidInt) <= osapi.MaxVNID {
						hsAnnotations = make(map[string]string)
						hsAnnotations[osapi.FixedVNIDHostAnnotation] = strconv.Itoa(vnidInt)
					} else {
						log.Errorf("Vnid %s is an invalid value for annotation %s. Annotation will be ignored.", vnid, osapi.FixedVNIDHostAnnotation)
					}
				}
				err = master.addNode(name, hostIP, hsAnnotations)
				if err != nil {
					log.Errorf("Error creating subnet for node %s, ip %s: %v", name, hostIP, err)
					return nil
				}
			}
		case cache.Deleted:
			if _, ok := hs.Annotations[osapi.AssignHostSubnetAnnotation]; !ok {
				// release the subnet
				_, ipnet, err := net.ParseCIDR(subnet)
				if err != nil {
					return fmt.Errorf("error parsing subnet %q for node %q for deletion: %v", subnet, name, err)
				}
				master.subnetAllocator.ReleaseNetwork(ipnet)
			}
		}
		return nil
	})
}

type hostSubnetMap map[string]*osapi.HostSubnet

func (plugin *OsdnNode) updateVXLANMulticastRules(subnets hostSubnetMap) {
	otx := plugin.ovs.NewTransaction()

	// Build the list of all nodes for multicast forwarding
	tun_dsts := make([]string, 0, len(subnets))
	for _, subnet := range subnets {
		if subnet.HostIP != plugin.localIP {
			tun_dsts = append(tun_dsts, fmt.Sprintf(",set_field:%s->tun_dst,output:1", subnet.HostIP))
		}
	}
	sort.Strings(tun_dsts)
	otx.AddFlow("table=111, priority=100, actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31]%s,goto_table:120", strings.Join(tun_dsts, ""))

	if err := otx.EndTransaction(); err != nil {
		log.Errorf("Error updating OVS VXLAN multicast flows: %v", err)
	}
}

// Only run on the nodes
func (node *OsdnNode) watchSubnets() {
	subnets := make(hostSubnetMap)
	RunEventQueue(node.osClient, HostSubnets, func(delta cache.Delta) error {
		hs := delta.Object.(*osapi.HostSubnet)
		if hs.HostIP == node.localIP {
			return nil
		}

		log.V(5).Infof("Watch %s event for HostSubnet %q", delta.Type, hs.ObjectMeta.Name)
		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			oldSubnet, exists := subnets[string(hs.UID)]
			if exists {
				if oldSubnet.HostIP == hs.HostIP {
					break
				} else {
					// Delete old subnet rules
					node.DeleteHostSubnetRules(oldSubnet)
				}
			}
			if err := node.networkInfo.validateNodeIP(hs.HostIP); err != nil {
				log.Warningf("Ignoring invalid subnet for node %s: %v", hs.HostIP, err)
				break
			}

			node.AddHostSubnetRules(hs)
			subnets[string(hs.UID)] = hs
		case cache.Deleted:
			delete(subnets, string(hs.UID))
			node.DeleteHostSubnetRules(hs)
		}
		// Update multicast rules after all other changes have been processed
		node.updateVXLANMulticastRules(subnets)
		return nil
	})
}
<commit_msg>Fix for bz1438402<commit_after>package plugin

import (
	"fmt"
	"net"
	"sort"
	"strconv"
	"strings"

	log "github.com/golang/glog"

	kapi "k8s.io/kubernetes/pkg/api"
	kapiunversioned "k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/client/retry"
	"k8s.io/kubernetes/pkg/types"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	utilwait "k8s.io/kubernetes/pkg/util/wait"

	osapi "github.com/openshift/origin/pkg/sdn/api"
	"github.com/openshift/origin/pkg/util/netutils"
)

func (master *OsdnMaster) SubnetStartMaster(clusterNetwork *net.IPNet, hostSubnetLength uint32) error {
	subrange := make([]string, 0)
	subnets, err := master.osClient.HostSubnets().List(kapi.ListOptions{})
	if err != nil {
		log.Errorf("Error in initializing/fetching subnets: %v", err)
		return err
	}
	for _, sub := range subnets.Items {
		subrange = append(subrange, sub.Subnet)
		if err = master.networkInfo.validateNodeIP(sub.HostIP); err != nil {
			// Don't error out; just warn so the error can be corrected with 'oc'
			log.Errorf("Failed to validate HostSubnet %s: %v", hostSubnetToString(&sub), err)
		} else {
			log.Infof("Found existing HostSubnet %s", hostSubnetToString(&sub))
		}
	}

	master.subnetAllocator, err = netutils.NewSubnetAllocator(clusterNetwork.String(), hostSubnetLength, subrange)
	if err != nil {
		return err
	}

	go utilwait.Forever(master.watchNodes, 0)
	go utilwait.Forever(master.watchSubnets, 0)
	return nil
}

// addNode takes the nodeName, a preferred nodeIP, the node's annotations and other valid ip addresses
// Creates or updates a HostSubnet if needed
// Returns the IP address used for hostsubnet (either the preferred or one from the otherValidAddresses) and any error
func (master *OsdnMaster) addNode(nodeName string, nodeIP string, hsAnnotations map[string]string, otherValidAddresses []kapi.NodeAddress) (string, error) {
	// Validate node IP before proceeding
	if err := master.networkInfo.validateNodeIP(nodeIP); err != nil {
		return "", err
	}

	// Check if subnet needs to be created or updated
	sub, err := master.osClient.HostSubnets().Get(nodeName)
	if err == nil {
		if sub.HostIP == nodeIP {
			return nodeIP, nil
		} else if isValidNodeIP(otherValidAddresses, sub.HostIP) {
			return sub.HostIP, nil
		} else {
			// Node IP changed, update old subnet
			sub.HostIP = nodeIP
			sub, err = master.osClient.HostSubnets().Update(sub)
			if err != nil {
				return "", fmt.Errorf("error updating subnet %s for node %s: %v", sub.Subnet, nodeName, err)
			}
			log.Infof("Updated HostSubnet %s", hostSubnetToString(sub))
			return nodeIP, nil
		}
	}

	// Create new subnet
	sn, err := master.subnetAllocator.GetNetwork()
	if err != nil {
		return "", fmt.Errorf("error allocating network for node %s: %v", nodeName, err)
	}

	sub = &osapi.HostSubnet{
		TypeMeta:   kapiunversioned.TypeMeta{Kind: "HostSubnet"},
		ObjectMeta: kapi.ObjectMeta{Name: nodeName, Annotations: hsAnnotations},
		Host:       nodeName,
		HostIP:     nodeIP,
		Subnet:     sn.String(),
	}
	sub, err = master.osClient.HostSubnets().Create(sub)
	if err != nil {
		master.subnetAllocator.ReleaseNetwork(sn)
		return "", fmt.Errorf("error creating subnet %s for node %s: %v", sn.String(), nodeName, err)
	}
	log.Infof("Created HostSubnet %s", hostSubnetToString(sub))
	return nodeIP, nil
}

func (master *OsdnMaster) deleteNode(nodeName string) error {
	sub, err := master.osClient.HostSubnets().Get(nodeName)
	if err != nil {
		return fmt.Errorf("error fetching subnet for node %q for deletion: %v", nodeName, err)
	}
	err = master.osClient.HostSubnets().Delete(nodeName)
	if err != nil {
		return fmt.Errorf("error deleting subnet %v for node %q: %v", sub, nodeName, err)
	}

	log.Infof("Deleted HostSubnet %s", hostSubnetToString(sub))
	return nil
}

func isValidNodeIP(validAddresses []kapi.NodeAddress, nodeIP string) bool {
	for _, addr := range validAddresses {
		if addr.Address == nodeIP {
			return true
		}
	}
	return false
}

func getNodeIP(node *kapi.Node) (string, error) {
	if len(node.Status.Addresses) > 0 && node.Status.Addresses[0].Address != "" {
		return node.Status.Addresses[0].Address, nil
	} else {
		return netutils.GetNodeIP(node.Name)
	}
}

// Because openshift-sdn uses an overlay and doesn't need GCE Routes, we need to
// clear the NetworkUnavailable condition that kubelet adds to initial node
// status when using GCE.
// TODO: make upstream kubelet more flexible with overlays and GCE so this
// condition doesn't get added for network plugins that don't want it, and then
// we can remove this function.
func (master *OsdnMaster) clearInitialNodeNetworkUnavailableCondition(node *kapi.Node) {
	knode := node
	cleared := false
	resultErr := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		var err error

		if knode != node {
			knode, err = master.kClient.Nodes().Get(node.ObjectMeta.Name)
			if err != nil {
				return err
			}
		}

		// Let caller modify knode's status, then push to api server.
		_, condition := kapi.GetNodeCondition(&node.Status, kapi.NodeNetworkUnavailable)
		if condition != nil && condition.Status != kapi.ConditionFalse && condition.Reason == "NoRouteCreated" {
			condition.Status = kapi.ConditionFalse
			condition.Reason = "RouteCreated"
			condition.Message = "openshift-sdn cleared kubelet-set NoRouteCreated"
			condition.LastTransitionTime = kapiunversioned.Now()
			knode, err = master.kClient.Nodes().UpdateStatus(knode)
			if err == nil {
				cleared = true
			}
		}
		return err
	})
	if resultErr != nil {
		utilruntime.HandleError(fmt.Errorf("status update failed for local node: %v", resultErr))
	} else if cleared {
		log.Infof("Cleared node NetworkUnavailable/NoRouteCreated condition for %s", node.ObjectMeta.Name)
	}
}

func (master *OsdnMaster) watchNodes() {
	nodeAddressMap := map[types.UID]string{}
	RunEventQueue(master.kClient.CoreClient.RESTClient(), Nodes, func(delta cache.Delta) error {
		node := delta.Object.(*kapi.Node)
		name := node.ObjectMeta.Name
		uid := node.ObjectMeta.UID

		nodeIP, err := getNodeIP(node)
		if err != nil {
			return fmt.Errorf("failed to get node IP for %s, skipping event: %v, node: %v", name, delta.Type, node)
		}

		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			master.clearInitialNodeNetworkUnavailableCondition(node)

			if oldNodeIP, ok := nodeAddressMap[uid]; ok && ((nodeIP == oldNodeIP) || isValidNodeIP(node.Status.Addresses, oldNodeIP)) {
				break
			}
			// Node status is frequently updated by kubelet, so log only if the above condition is not met
			log.V(5).Infof("Watch %s event for Node %q", delta.Type, name)

			usedNodeIP, err := master.addNode(name, nodeIP, nil, node.Status.Addresses)
			if err != nil {
				return fmt.Errorf("error creating subnet for node %s, ip %s: %v", name, nodeIP, err)
			}
			nodeAddressMap[uid] = usedNodeIP
		case cache.Deleted:
			log.V(5).Infof("Watch %s event for Node %q", delta.Type, name)
			delete(nodeAddressMap, uid)

			err = master.deleteNode(name)
			if err != nil {
				return fmt.Errorf("error deleting node %s: %v", name, err)
			}
		}
		return nil
	})
}

func (node *OsdnNode) SubnetStartNode() error {
	go utilwait.Forever(node.watchSubnets, 0)
	return nil
}

// Only run on the master
// Watch for all hostsubnet events and if one is found with the right annotation, use the SubnetAllocator to dole a real subnet
func (master *OsdnMaster) watchSubnets() {
	RunEventQueue(master.osClient, HostSubnets, func(delta cache.Delta) error {
		hs := delta.Object.(*osapi.HostSubnet)
		name := hs.ObjectMeta.Name
		hostIP := hs.HostIP
		subnet := hs.Subnet

		log.V(5).Infof("Watch %s event for HostSubnet %q", delta.Type, hs.ObjectMeta.Name)
		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			if _, ok := hs.Annotations[osapi.AssignHostSubnetAnnotation]; ok {
				// Delete the annotated hostsubnet and create a new one with an assigned subnet
				// We do not update (instead of delete+create) because the watchSubnets on the nodes
				// will skip the event if it finds that the hostsubnet has the same host
				// And we cannot fix the watchSubnets code for node because it will break migration if
				// nodes are upgraded after the master
				err := master.osClient.HostSubnets().Delete(name)
				if err != nil {
					log.Errorf("Error in deleting annotated subnet from master, name: %s, ip %s: %v", name, hostIP, err)
					return nil
				}
				var hsAnnotations map[string]string
				if vnid, ok := hs.Annotations[osapi.FixedVNIDHostAnnotation]; ok {
					vnidInt, err := strconv.Atoi(vnid)
					if err == nil && vnidInt >= 0 && uint32(vnidInt) <= osapi.MaxVNID {
						hsAnnotations = make(map[string]string)
						hsAnnotations[osapi.FixedVNIDHostAnnotation] = strconv.Itoa(vnidInt)
					} else {
						log.Errorf("Vnid %s is an invalid value for annotation %s. Annotation will be ignored.", vnid, osapi.FixedVNIDHostAnnotation)
					}
				}
				_, err = master.addNode(name, hostIP, hsAnnotations, nil)
				if err != nil {
					log.Errorf("Error creating subnet for node %s, ip %s: %v", name, hostIP, err)
					return nil
				}
			}
		case cache.Deleted:
			if _, ok := hs.Annotations[osapi.AssignHostSubnetAnnotation]; !ok {
				// release the subnet
				_, ipnet, err := net.ParseCIDR(subnet)
				if err != nil {
					return fmt.Errorf("error parsing subnet %q for node %q for deletion: %v", subnet, name, err)
				}
				master.subnetAllocator.ReleaseNetwork(ipnet)
			}
		}
		return nil
	})
}

type hostSubnetMap map[string]*osapi.HostSubnet

func (plugin *OsdnNode) updateVXLANMulticastRules(subnets hostSubnetMap) {
	otx := plugin.ovs.NewTransaction()

	// Build the list of all nodes for multicast forwarding
	tun_dsts := make([]string, 0, len(subnets))
	for _, subnet := range subnets {
		if subnet.HostIP != plugin.localIP {
			tun_dsts = append(tun_dsts, fmt.Sprintf(",set_field:%s->tun_dst,output:1", subnet.HostIP))
		}
	}
	sort.Strings(tun_dsts)
	otx.AddFlow("table=111, priority=100, actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31]%s,goto_table:120", strings.Join(tun_dsts, ""))

	if err := otx.EndTransaction(); err != nil {
		log.Errorf("Error updating OVS VXLAN multicast flows: %v", err)
	}
}

// Only run on the nodes
func (node *OsdnNode) watchSubnets() {
	subnets := make(hostSubnetMap)
	RunEventQueue(node.osClient, HostSubnets, func(delta cache.Delta) error {
		hs := delta.Object.(*osapi.HostSubnet)
		if hs.HostIP == node.localIP {
			return nil
		}

		log.V(5).Infof("Watch %s event for HostSubnet %q", delta.Type, hs.ObjectMeta.Name)
		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			oldSubnet, exists := subnets[string(hs.UID)]
			if exists {
				if oldSubnet.HostIP == hs.HostIP {
					break
				} else {
					// Delete old subnet rules
					node.DeleteHostSubnetRules(oldSubnet)
				}
			}
			if err := node.networkInfo.validateNodeIP(hs.HostIP); err != nil {
				log.Warningf("Ignoring invalid subnet for node %s: %v", hs.HostIP, err)
				break
			}

			node.AddHostSubnetRules(hs)
			subnets[string(hs.UID)] = hs
		case cache.Deleted:
			delete(subnets, string(hs.UID))
			node.DeleteHostSubnetRules(hs)
		}
		// Update multicast rules after all other changes have been processed
		node.updateVXLANMulticastRules(subnets)
		return nil
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 sync

import (
	"context"
	"fmt"
	"os/exec"
	"path/filepath"

	"github.com/bmatcuk/doublestar"
	"github.com/sirupsen/logrus"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/watch"
	"github.com/pkg/errors"
	v1 "k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	// For testing
	WorkingDir = retrieveWorkingDir
)

type Syncer interface {
	Sync(context.Context, *Item) error
}

type Item struct {
	Image  string
	Copy   map[string]string
	Delete map[string]string
}

func NewItem(a *latest.Artifact, e watch.Events, builds []build.Artifact) (*Item, error) {
	// If there are no changes, short circuit and don't sync anything
	if !e.HasChanged() || len(a.Sync) == 0 {
		return nil, nil
	}

	tag := latestTag(a.ImageName, builds)
	if tag == "" {
		return nil, fmt.Errorf("could not find latest tag for image %s in builds: %v", a.ImageName, builds)
	}

	wd, err := WorkingDir(tag)
	if err != nil {
		return nil, errors.Wrapf(err, "retrieving working dir for %s", tag)
	}

	toCopy, err := intersect(a.Workspace, a.Sync, append(e.Added, e.Modified...), wd)
	if err != nil {
		return nil, errors.Wrap(err, "intersecting sync map and added, modified files")
	}

	toDelete, err := intersect(a.Workspace, a.Sync, e.Deleted, wd)
	if err != nil {
		return nil, errors.Wrap(err, "intersecting sync map and deleted files")
	}

	// Something went wrong, don't sync, rebuild.
	if toCopy == nil || toDelete == nil {
		return nil, nil
	}

	return &Item{
		Image:  tag,
		Copy:   toCopy,
		Delete: toDelete,
	}, nil
}

func retrieveWorkingDir(image string) (string, error) {
	cf, err := docker.RetrieveRemoteConfig(image)
	if err != nil {
		return "", errors.Wrap(err, "retrieving remote config")
	}
	if cf.Config.WorkingDir == "" {
		return "/", nil
	}
	return cf.Config.WorkingDir, nil
}

func latestTag(image string, builds []build.Artifact) string {
	for _, build := range builds {
		if build.ImageName == image {
			return build.Tag
		}
	}
	return ""
}

func intersect(context string, syncMap map[string]string, files []string, workingDir string) (map[string]string, error) {
	ret := map[string]string{}

	for _, f := range files {
		relPath, err := filepath.Rel(context, f)
		if err != nil {
			return nil, errors.Wrapf(err, "changed file %s can't be found relative to context %s", f, context)
		}
		var matches bool
		for p, dst := range syncMap {
			match, err := doublestar.PathMatch(filepath.FromSlash(p), relPath)
			if err != nil {
				return nil, errors.Wrapf(err, "pattern error for %s", relPath)
			}
			if match {
				if filepath.IsAbs(dst) {
					dst = filepath.ToSlash(filepath.Join(dst, filepath.Base(relPath)))
				} else {
					dst = filepath.ToSlash(filepath.Join(workingDir, dst, filepath.Base(relPath)))
				}
				// Every file must match at least one sync pattern, if not we'll have to
				// skip the entire sync
				matches = true
				ret[f] = dst
			}
		}
		if !matches {
			logrus.Infof("Changed file %s does not match any sync pattern. Skipping sync", relPath)
			return nil, nil
		}

	}
	return ret, nil
}

func Perform(ctx context.Context, image string, files map[string]string, cmdFn func(context.Context, v1.Pod, v1.Container, map[string]string) []*exec.Cmd, namespaces []string) error {
	if len(files) == 0 {
		return nil
	}

	client, err := kubernetes.Client()
	if err != nil {
		return errors.Wrap(err, "getting k8s client")
	}

	numSynced := 0
	for _, ns := range namespaces {
		pods, err := client.CoreV1().Pods(ns).List(meta_v1.ListOptions{})
		if err != nil {
			return errors.Wrap(err, "getting pods for namespace "+ns)
		}

		for _, p := range pods.Items {
			for _, c := range p.Spec.Containers {
				if c.Image != image {
					continue
				}

				cmds := cmdFn(ctx, p, c, files)
				for _, cmd := range cmds {
					if err := util.RunCmd(cmd); err != nil {
						return err
					}
					numSynced++
				}
			}
		}
	}

	if numSynced == 0 {
		return errors.New("didn't sync any files")
	}

	return nil
}
<commit_msg>linting error<commit_after>/*
Copyright 2019 The Skaffold 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 sync

import (
	"context"
	"fmt"
	"os/exec"
	"path/filepath"

	"github.com/bmatcuk/doublestar"
	"github.com/sirupsen/logrus"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/watch"
	"github.com/pkg/errors"
	v1 "k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	// WorkingDir is here for testing
	WorkingDir = retrieveWorkingDir
)

type Syncer interface {
	Sync(context.Context, *Item) error
}

type Item struct {
	Image  string
	Copy   map[string]string
	Delete map[string]string
}

func NewItem(a *latest.Artifact, e watch.Events, builds []build.Artifact) (*Item, error) {
	// If there are no changes, short circuit and don't sync anything
	if !e.HasChanged() || len(a.Sync) == 0 {
		return nil, nil
	}

	tag := latestTag(a.ImageName, builds)
	if tag == "" {
		return nil, fmt.Errorf("could not find latest tag for image %s in builds: %v", a.ImageName, builds)
	}

	wd, err := WorkingDir(tag)
	if err != nil {
		return nil, errors.Wrapf(err, "retrieving working dir for %s", tag)
	}

	toCopy, err := intersect(a.Workspace, a.Sync, append(e.Added, e.Modified...), wd)
	if err != nil {
		return nil, errors.Wrap(err, "intersecting sync map and added, modified files")
	}

	toDelete, err := intersect(a.Workspace, a.Sync, e.Deleted, wd)
	if err != nil {
		return nil, errors.Wrap(err, "intersecting sync map and deleted files")
	}

	// Something went wrong, don't sync, rebuild.
	if toCopy == nil || toDelete == nil {
		return nil, nil
	}

	return &Item{
		Image:  tag,
		Copy:   toCopy,
		Delete: toDelete,
	}, nil
}

func retrieveWorkingDir(image string) (string, error) {
	cf, err := docker.RetrieveRemoteConfig(image)
	if err != nil {
		return "", errors.Wrap(err, "retrieving remote config")
	}
	if cf.Config.WorkingDir == "" {
		return "/", nil
	}
	return cf.Config.WorkingDir, nil
}

func latestTag(image string, builds []build.Artifact) string {
	for _, build := range builds {
		if build.ImageName == image {
			return build.Tag
		}
	}
	return ""
}

func intersect(context string, syncMap map[string]string, files []string, workingDir string) (map[string]string, error) {
	ret := map[string]string{}

	for _, f := range files {
		relPath, err := filepath.Rel(context, f)
		if err != nil {
			return nil, errors.Wrapf(err, "changed file %s can't be found relative to context %s", f, context)
		}
		var matches bool
		for p, dst := range syncMap {
			match, err := doublestar.PathMatch(filepath.FromSlash(p), relPath)
			if err != nil {
				return nil, errors.Wrapf(err, "pattern error for %s", relPath)
			}
			if match {
				if filepath.IsAbs(dst) {
					dst = filepath.ToSlash(filepath.Join(dst, filepath.Base(relPath)))
				} else {
					dst = filepath.ToSlash(filepath.Join(workingDir, dst, filepath.Base(relPath)))
				}
				// Every file must match at least one sync pattern, if not we'll have to
				// skip the entire sync
				matches = true
				ret[f] = dst
			}
		}
		if !matches {
			logrus.Infof("Changed file %s does not match any sync pattern. Skipping sync", relPath)
			return nil, nil
		}

	}
	return ret, nil
}

func Perform(ctx context.Context, image string, files map[string]string, cmdFn func(context.Context, v1.Pod, v1.Container, map[string]string) []*exec.Cmd, namespaces []string) error {
	if len(files) == 0 {
		return nil
	}

	client, err := kubernetes.Client()
	if err != nil {
		return errors.Wrap(err, "getting k8s client")
	}

	numSynced := 0
	for _, ns := range namespaces {
		pods, err := client.CoreV1().Pods(ns).List(meta_v1.ListOptions{})
		if err != nil {
			return errors.Wrap(err, "getting pods for namespace "+ns)
		}

		for _, p := range pods.Items {
			for _, c := range p.Spec.Containers {
				if c.Image != image {
					continue
				}

				cmds := cmdFn(ctx, p, c, files)
				for _, cmd := range cmds {
					if err := util.RunCmd(cmd); err != nil {
						return err
					}
					numSynced++
				}
			}
		}
	}

	if numSynced == 0 {
		return errors.New("didn't sync any files")
	}

	return nil
}
<|endoftext|>


<commit_before>package smokescreen

import (
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	log "github.com/sirupsen/logrus"
)

type Config struct {
	Ip                           string
	Port                         uint16
	DenyRanges                   []net.IPNet
	AllowRanges                  []net.IPNet
	ConnectTimeout               time.Duration
	ExitTimeout                  time.Duration
	MaintenanceFile              string
	StatsdClient                 *statsd.Client
	EgressAcl                    EgressAcl
	SupportProxyProtocol         bool
	TlsConfig                    *tls.Config
	CrlByAuthorityKeyId          map[string]*pkix.CertificateList
	RoleFromRequest              func(subject *http.Request) (string, error)
	clientCasBySubjectKeyId      map[string]*x509.Certificate
	AdditionalErrorMessageOnDeny string
	Log                          *log.Logger
	DisabledAclPolicyActions     []string
	AllowMissingRole             bool
	StatsSocketDir               string
	StatsSocketFileMode          os.FileMode
	StatsServer                  interface{} // StatsServer
	ConnTracker                  sync.Map    // The zero Map is empty and ready to use
}

type missingRoleError struct {
	error
}

func MissingRoleError(s string) error {
	return missingRoleError{errors.New(s)}
}

func IsMissingRoleError(err error) bool {
	_, ok := err.(missingRoleError)
	return ok
}

func parseRanges(rangeStrings []string) ([]net.IPNet, error) {
	outRanges := make([]net.IPNet, len(rangeStrings))
	for i, str := range rangeStrings {
		_, ipnet, err := net.ParseCIDR(str)
		if err != nil {
			return outRanges, err
		}
		outRanges[i] = *ipnet
	}
	return outRanges, nil
}

func (config *Config) SetDenyRanges(rangeStrings []string) error {
	var err error
	config.DenyRanges, err = parseRanges(rangeStrings)
	return err
}

func (config *Config) SetAllowRanges(rangeStrings []string) error {
	var err error
	config.AllowRanges, err = parseRanges(rangeStrings)
	return err
}

// RFC 5280,  4.2.1.1
type authKeyId struct {
	Id []byte `asn1:"optional,tag:0"`
}

func NewConfig() *Config {
	return &Config{
		CrlByAuthorityKeyId:     make(map[string]*pkix.CertificateList),
		clientCasBySubjectKeyId: make(map[string]*x509.Certificate),
		Log:                     log.New(),
		Port:                    4750,
		ExitTimeout:             60 * time.Second,
		StatsSocketFileMode:     os.FileMode(700),
	}
}

func (config *Config) SetupCrls(crlFiles []string) error {
	for _, crlFile := range crlFiles {
		crlBytes, err := ioutil.ReadFile(crlFile)
		if err != nil {
			return err
		}

		certList, err := x509.ParseCRL(crlBytes)
		if err != nil {
			log.Printf("Failed to parse CRL in '%s': %#v\n", crlFile, err)
		}

		// find the X509v3 Authority Key Identifier in the extensions (2.5.29.35)
		crlIssuerId := ""
		extensionOid := []int{2, 5, 29, 35}
		for _, v := range certList.TBSCertList.Extensions {
			if v.Id.Equal(extensionOid) { // Hurray, we found it
				// Boo, it's ASN.1.
				var crlAuthorityKey authKeyId
				_, err := asn1.Unmarshal(v.Value, &crlAuthorityKey)
				if err != nil {
					fmt.Printf("error: Failed to read AuthorityKey: %#v\n", err)
					continue
				}
				crlIssuerId = string(crlAuthorityKey.Id)
				break
			}
		}
		if crlIssuerId == "" {
			log.Print(fmt.Errorf("error: CRL from '%s' has no Authority Key Identifier: ignoring it\n", crlFile))
			continue
		}

		// Make sure we have a CA for this CRL or warn
		caCert, ok := config.clientCasBySubjectKeyId[crlIssuerId]

		if !ok {
			log.Printf("warn: CRL loaded for issuer '%s' but no such CA loaded: ignoring it\n", hex.EncodeToString([]byte(crlIssuerId)))
			fmt.Printf("%#v loaded certs\n", len(config.clientCasBySubjectKeyId))
			continue
		}

		// At this point, we have the CA certificate and the CRL. All that's left before evicting the CRL we currently trust is to verify the new CRL's signature
		err = caCert.CheckCRLSignature(certList)
		if err != nil {
			fmt.Printf("error: Could not trust CRL. Error during signature check: %#v\n", err)
			continue
		}

		// At this point, we have a new CRL which we trust. Let's evict the old one.
		config.CrlByAuthorityKeyId[crlIssuerId] = certList
		fmt.Printf("info: Loaded CRL for Authority ID '%s'\n", hex.EncodeToString([]byte(crlIssuerId)))
	}

	// Verify that all CAs loaded have a CRL
	for k, _ := range config.clientCasBySubjectKeyId {
		_, ok := config.CrlByAuthorityKeyId[k]
		if !ok {
			fmt.Printf("warn: no CRL loaded for Authority ID '%s'\n", hex.EncodeToString([]byte(k)))
		}
	}
	return nil
}

func (config *Config) SetupStatsdWithNamespace(addr, namespace string) error {
	if addr == "" {
		config.StatsdClient = nil
		return nil
	}

	client, err := statsd.New(addr)
	if err != nil {
		return err
	}

	config.StatsdClient = client

	config.StatsdClient.Namespace = namespace

	return nil
}

func (config *Config) SetupStatsd(addr string) error {
	return config.SetupStatsdWithNamespace(addr, DefaultStatsdNamespace)
}

func (config *Config) SetupEgressAcl(aclFile string) error {
	if aclFile == "" {
		config.EgressAcl = nil
		return nil
	}

	log.Printf("Loading egress ACL from %s", aclFile)
	egressAcl, err := LoadYamlAclFromFilePath(config, aclFile)
	if err != nil {
		log.Print(err)
		return err
	}
	config.EgressAcl = egressAcl

	return nil
}

func addCertsFromFile(config *Config, pool *x509.CertPool, fileName string) error {
	data, err := ioutil.ReadFile(fileName)

	//TODO this is a bit awkward
	config.populateClientCaMap(data)

	if err != nil {
		return err
	}
	ok := pool.AppendCertsFromPEM(data)
	if !ok {
		return fmt.Errorf("Failed to load any certificates from file '%s'", fileName)
	}
	return nil
}

// certFile and keyFile may be the same file containing concatenated PEM blocks
func (config *Config) SetupTls(certFile, keyFile string, clientCAFiles []string) error {
	if certFile == "" || keyFile == "" {
		return errors.New("both certificate and key files must be specified to set up TLS")
	}

	serverCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	clientAuth := tls.NoClientCert
	clientCAs := x509.NewCertPool()

	if len(clientCAFiles) != 0 {
		clientAuth = tls.VerifyClientCertIfGiven
		for _, caFile := range clientCAFiles {
			err = addCertsFromFile(config, clientCAs, caFile)
			if err != nil {
				return err
			}
		}
	}

	config.TlsConfig = &tls.Config{
		Certificates: []tls.Certificate{serverCert},
		ClientAuth:   clientAuth,
		ClientCAs:    clientCAs,
	}

	return nil
}

func (config *Config) populateClientCaMap(pemCerts []byte) (ok bool) {

	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			continue
		}
		fmt.Printf("info: Loaded CA with Authority ID '%s'\n", hex.EncodeToString(cert.SubjectKeyId))
		config.clientCasBySubjectKeyId[string(cert.SubjectKeyId)] = cert
		ok = true
	}
	return
}
<commit_msg>don't get tricked by octal number literals<commit_after>package smokescreen

import (
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	log "github.com/sirupsen/logrus"
)

type Config struct {
	Ip                           string
	Port                         uint16
	DenyRanges                   []net.IPNet
	AllowRanges                  []net.IPNet
	ConnectTimeout               time.Duration
	ExitTimeout                  time.Duration
	MaintenanceFile              string
	StatsdClient                 *statsd.Client
	EgressAcl                    EgressAcl
	SupportProxyProtocol         bool
	TlsConfig                    *tls.Config
	CrlByAuthorityKeyId          map[string]*pkix.CertificateList
	RoleFromRequest              func(subject *http.Request) (string, error)
	clientCasBySubjectKeyId      map[string]*x509.Certificate
	AdditionalErrorMessageOnDeny string
	Log                          *log.Logger
	DisabledAclPolicyActions     []string
	AllowMissingRole             bool
	StatsSocketDir               string
	StatsSocketFileMode          os.FileMode
	StatsServer                  interface{} // StatsServer
	ConnTracker                  sync.Map    // The zero Map is empty and ready to use
}

type missingRoleError struct {
	error
}

func MissingRoleError(s string) error {
	return missingRoleError{errors.New(s)}
}

func IsMissingRoleError(err error) bool {
	_, ok := err.(missingRoleError)
	return ok
}

func parseRanges(rangeStrings []string) ([]net.IPNet, error) {
	outRanges := make([]net.IPNet, len(rangeStrings))
	for i, str := range rangeStrings {
		_, ipnet, err := net.ParseCIDR(str)
		if err != nil {
			return outRanges, err
		}
		outRanges[i] = *ipnet
	}
	return outRanges, nil
}

func (config *Config) SetDenyRanges(rangeStrings []string) error {
	var err error
	config.DenyRanges, err = parseRanges(rangeStrings)
	return err
}

func (config *Config) SetAllowRanges(rangeStrings []string) error {
	var err error
	config.AllowRanges, err = parseRanges(rangeStrings)
	return err
}

// RFC 5280,  4.2.1.1
type authKeyId struct {
	Id []byte `asn1:"optional,tag:0"`
}

func NewConfig() *Config {
	return &Config{
		CrlByAuthorityKeyId:     make(map[string]*pkix.CertificateList),
		clientCasBySubjectKeyId: make(map[string]*x509.Certificate),
		Log:                     log.New(),
		Port:                    4750,
		ExitTimeout:             60 * time.Second,
		StatsSocketFileMode:     os.FileMode(0700),
	}
}

func (config *Config) SetupCrls(crlFiles []string) error {
	for _, crlFile := range crlFiles {
		crlBytes, err := ioutil.ReadFile(crlFile)
		if err != nil {
			return err
		}

		certList, err := x509.ParseCRL(crlBytes)
		if err != nil {
			log.Printf("Failed to parse CRL in '%s': %#v\n", crlFile, err)
		}

		// find the X509v3 Authority Key Identifier in the extensions (2.5.29.35)
		crlIssuerId := ""
		extensionOid := []int{2, 5, 29, 35}
		for _, v := range certList.TBSCertList.Extensions {
			if v.Id.Equal(extensionOid) { // Hurray, we found it
				// Boo, it's ASN.1.
				var crlAuthorityKey authKeyId
				_, err := asn1.Unmarshal(v.Value, &crlAuthorityKey)
				if err != nil {
					fmt.Printf("error: Failed to read AuthorityKey: %#v\n", err)
					continue
				}
				crlIssuerId = string(crlAuthorityKey.Id)
				break
			}
		}
		if crlIssuerId == "" {
			log.Print(fmt.Errorf("error: CRL from '%s' has no Authority Key Identifier: ignoring it\n", crlFile))
			continue
		}

		// Make sure we have a CA for this CRL or warn
		caCert, ok := config.clientCasBySubjectKeyId[crlIssuerId]

		if !ok {
			log.Printf("warn: CRL loaded for issuer '%s' but no such CA loaded: ignoring it\n", hex.EncodeToString([]byte(crlIssuerId)))
			fmt.Printf("%#v loaded certs\n", len(config.clientCasBySubjectKeyId))
			continue
		}

		// At this point, we have the CA certificate and the CRL. All that's left before evicting the CRL we currently trust is to verify the new CRL's signature
		err = caCert.CheckCRLSignature(certList)
		if err != nil {
			fmt.Printf("error: Could not trust CRL. Error during signature check: %#v\n", err)
			continue
		}

		// At this point, we have a new CRL which we trust. Let's evict the old one.
		config.CrlByAuthorityKeyId[crlIssuerId] = certList
		fmt.Printf("info: Loaded CRL for Authority ID '%s'\n", hex.EncodeToString([]byte(crlIssuerId)))
	}

	// Verify that all CAs loaded have a CRL
	for k, _ := range config.clientCasBySubjectKeyId {
		_, ok := config.CrlByAuthorityKeyId[k]
		if !ok {
			fmt.Printf("warn: no CRL loaded for Authority ID '%s'\n", hex.EncodeToString([]byte(k)))
		}
	}
	return nil
}

func (config *Config) SetupStatsdWithNamespace(addr, namespace string) error {
	if addr == "" {
		config.StatsdClient = nil
		return nil
	}

	client, err := statsd.New(addr)
	if err != nil {
		return err
	}

	config.StatsdClient = client

	config.StatsdClient.Namespace = namespace

	return nil
}

func (config *Config) SetupStatsd(addr string) error {
	return config.SetupStatsdWithNamespace(addr, DefaultStatsdNamespace)
}

func (config *Config) SetupEgressAcl(aclFile string) error {
	if aclFile == "" {
		config.EgressAcl = nil
		return nil
	}

	log.Printf("Loading egress ACL from %s", aclFile)
	egressAcl, err := LoadYamlAclFromFilePath(config, aclFile)
	if err != nil {
		log.Print(err)
		return err
	}
	config.EgressAcl = egressAcl

	return nil
}

func addCertsFromFile(config *Config, pool *x509.CertPool, fileName string) error {
	data, err := ioutil.ReadFile(fileName)

	//TODO this is a bit awkward
	config.populateClientCaMap(data)

	if err != nil {
		return err
	}
	ok := pool.AppendCertsFromPEM(data)
	if !ok {
		return fmt.Errorf("Failed to load any certificates from file '%s'", fileName)
	}
	return nil
}

// certFile and keyFile may be the same file containing concatenated PEM blocks
func (config *Config) SetupTls(certFile, keyFile string, clientCAFiles []string) error {
	if certFile == "" || keyFile == "" {
		return errors.New("both certificate and key files must be specified to set up TLS")
	}

	serverCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	clientAuth := tls.NoClientCert
	clientCAs := x509.NewCertPool()

	if len(clientCAFiles) != 0 {
		clientAuth = tls.VerifyClientCertIfGiven
		for _, caFile := range clientCAFiles {
			err = addCertsFromFile(config, clientCAs, caFile)
			if err != nil {
				return err
			}
		}
	}

	config.TlsConfig = &tls.Config{
		Certificates: []tls.Certificate{serverCert},
		ClientAuth:   clientAuth,
		ClientCAs:    clientCAs,
	}

	return nil
}

func (config *Config) populateClientCaMap(pemCerts []byte) (ok bool) {

	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			continue
		}
		fmt.Printf("info: Loaded CA with Authority ID '%s'\n", hex.EncodeToString(cert.SubjectKeyId))
		config.clientCasBySubjectKeyId[string(cert.SubjectKeyId)] = cert
		ok = true
	}
	return
}
<|endoftext|>


<commit_before>package sync

import (
	"testing"
)

func TestConflictNilInvalid(t *testing.T) {
	var conflict *Conflict
	if conflict.EnsureValid() == nil {
		t.Error("nil conflict considered valid")
	}
}

func TestConflictNoAlphaChangesInvalid(t *testing.T) {
	conflict := &Conflict{BetaChanges: []*Change{{New: testFile1Entry}}}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with no alpha changes considered valid")
	}
}

func TestConflictNoBetaChangesInvalid(t *testing.T) {
	conflict := &Conflict{AlphaChanges: []*Change{{New: testFile1Entry}}}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with no beta changes considered valid")
	}
}

func TestConflictInvalidAlphaChangeInvalid(t *testing.T) {
	conflict := &Conflict{
		AlphaChanges: []*Change{{}},
		BetaChanges:  []*Change{{New: testFile1Entry}},
	}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with invalid alpha change considered valid")
	}
}

func TestConflictInvalidBetaChangeInvalid(t *testing.T) {
	conflict := &Conflict{
		AlphaChanges: []*Change{{New: testFile1Entry}},
		BetaChanges:  []*Change{{}},
	}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with invalid beta change considered valid")
	}
}

func TestConflictValid(t *testing.T) {
	conflict := &Conflict{
		AlphaChanges: []*Change{{New: testFile1Entry}},
		BetaChanges:  []*Change{{New: testDirectory1Entry}},
	}
	if err := conflict.EnsureValid(); err != nil {
		t.Error("valid conflict considered invalid:", err)
	}
}
<commit_msg>Added more extensive tests for Conflict.Root.<commit_after>package sync

import (
	"testing"
)

func TestConflictRootInvalid(t *testing.T) {
	// Defer a handler that checks for a panic.
	defer func() {
		if recover() == nil {
			t.Error("conflict root computation did not panic for invalid conflict")
		}
	}()

	// Create an invalid conflict.
	conflict := &Conflict{}

	// Attempt to compute the root.
	conflict.Root()
}

func TestConflictRootBothAtRoot(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "",
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "",
				New:  testFile2Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := ""

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootBothOneChangeAlphaAtRoot(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "subpath",
				New:  testFile1Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := ""

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootBothOneChangeBetaAtRoot(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "subpath",
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := ""

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootBothOneChangeAlphaHigher(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "path",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "path/subpath",
				New:  testFile1Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := "path"

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootBothOneChangeBetaHigher(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "path/subpath",
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "path",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := "path"

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootBetaMultipleAlphaAtRoot(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "subpath",
				New:  testFile1Entry,
			},
			{
				Path: "subpath2",
				New:  testFile2Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := ""

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootAlphaMultipleBetaAtRoot(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "subpath",
				New:  testFile1Entry,
			},
			{
				Path: "subpath2",
				New:  testFile2Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := ""

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootBetaMultipleAlphaHigher(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "path",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "path/subpath",
				New:  testFile1Entry,
			},
			{
				Path: "path/subpath2",
				New:  testFile2Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := "path"

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictRootAlphaMultipleBetaHigher(t *testing.T) {
	// Create a test conflict.
	conflict := &Conflict{
		AlphaChanges: []*Change{
			{
				Path: "path/subpath",
				New:  testFile1Entry,
			},
			{
				Path: "path/subpath2",
				New:  testFile2Entry,
			},
		},
		BetaChanges: []*Change{
			{
				Path: "path",
				Old:  testDirectory1Entry,
				New:  testFile1Entry,
			},
		},
	}

	// Set the expected root.
	expectedRoot := "path"

	// Verify that the root is correct.
	if conflict.Root() != expectedRoot {
		t.Error("conflict root does not match expected:", conflict.Root(), "!=", expectedRoot)
	}
}

func TestConflictNilInvalid(t *testing.T) {
	var conflict *Conflict
	if conflict.EnsureValid() == nil {
		t.Error("nil conflict considered valid")
	}
}

func TestConflictNoAlphaChangesInvalid(t *testing.T) {
	conflict := &Conflict{BetaChanges: []*Change{{New: testFile1Entry}}}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with no alpha changes considered valid")
	}
}

func TestConflictNoBetaChangesInvalid(t *testing.T) {
	conflict := &Conflict{AlphaChanges: []*Change{{New: testFile1Entry}}}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with no beta changes considered valid")
	}
}

func TestConflictInvalidAlphaChangeInvalid(t *testing.T) {
	conflict := &Conflict{
		AlphaChanges: []*Change{{}},
		BetaChanges:  []*Change{{New: testFile1Entry}},
	}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with invalid alpha change considered valid")
	}
}

func TestConflictInvalidBetaChangeInvalid(t *testing.T) {
	conflict := &Conflict{
		AlphaChanges: []*Change{{New: testFile1Entry}},
		BetaChanges:  []*Change{{}},
	}
	if conflict.EnsureValid() == nil {
		t.Error("conflict with invalid beta change considered valid")
	}
}

func TestConflictValid(t *testing.T) {
	conflict := &Conflict{
		AlphaChanges: []*Change{{New: testFile1Entry}},
		BetaChanges:  []*Change{{New: testDirectory1Entry}},
	}
	if err := conflict.EnsureValid(); err != nil {
		t.Error("valid conflict considered invalid:", err)
	}
}
<|endoftext|>


<commit_before>package components

import (
	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"math/big"
	"time"

	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	v12 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"kubevirt.io/kubevirt/pkg/certificates/bootstrap"
	certutil "kubevirt.io/kubevirt/pkg/certificates/triple/cert"
)

var _ = Describe("Certificate Management", func() {
	Context("CA certificate bundle", func() {

		It("should drop expired CAs", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(-10*time.Minute)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be properly appended when within the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out the first CA cert if it is outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-3*time.Minute), now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out a CA cert which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[1]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out multiple CA certs which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(-5*time.Minute).Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should ensure that the current CA is not added over and over again", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				current,
				current,
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be protected against misuse by cropping big arrays", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{}
			for i := 1; i < 20; i++ {
				given = append(given, NewSelfSignedCert(now.Add(-1*time.Minute), now.Add(1*time.Hour)))
			}
			givenBundle := CACertsToBundle(given)
			_, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(11))
		})

		It("should immediately suggest a rotation if the cert is not signed by the provided CA", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			ca := NewSelfSignedCert(now, now.Add(1*time.Hour))
			duration := &v1.Duration{Duration: 5 * time.Hour}
			deadline := NextRotationDeadline(current, ca, duration)
			Expect(deadline.Before(time.Now())).To(BeTrue())
		})

		It("should set notBefore on the certificate to notBefore value of the CA certificate ", func() {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(crt.Leaf.NotBefore).To(Equal(caCrt.Leaf.NotBefore))
		})

		table.DescribeTable("should set the notAfter on the certificate according to the supplied duration", func(caDuration time.Duration) {
			now := time.Now()
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			//deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			Expect(crt.Leaf.NotAfter.Unix()).To(BeNumerically("==", now.Add(crtDuration.Duration).Unix(), 1))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should suggaest a rotation on the certificate according on 80% of the certificate lifespan", func(caDuration time.Duration) {
			now := time.Now()
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			// Generating certificates may take a little bit of time to execute (entropy, ...). Since we can't
			// inject a fake time into the foreign code which generates the certificates, allow a generous diff of three
			// seconds.
			Expect(NextRotationDeadline(crt, caCrt, crtDuration).Unix()).To(BeNumerically("==", deadline.Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should successfully sign with the current CA the certificate for", func(scretName string) {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			var crtSecret *v12.Secret
			for _, s := range NewCertSecrets("test", "test") {
				if s.Name == scretName {
					crtSecret = s
					break
				}
			}
			Expect(crtSecret).ToNot(BeNil())
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(err).ToNot(HaveOccurred())
			Expect(crt).ToNot(BeNil())
		},
			table.Entry("virt-handler", VirtHandlerCertSecretName),
			table.Entry("virt-controller", VirtControllerCertSecretName),
			table.Entry("virt-api", VirtApiCertSecretName),
			table.Entry("virt-operator", VirtOperatorCertSecretName),
		)
	})

	It("should set the right namespaces on the certificate secrets", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		for _, secret := range secrets[:len(secrets)-1] {
			Expect(secret.Namespace).To(Equal("install_namespace"))
			Expect(secret.Name).ToNot(Equal(VirtOperatorCertSecretName))
		}
		Expect(secrets[len(secrets)-1].Namespace).To(Equal("operator_namespace"))
	})

	It("should create the kubevirt-ca configmap for the right namespace", func() {
		configMap := NewKubeVirtCAConfigMap("namespace")
		Expect(configMap.Namespace).To(Equal("namespace"))
	})

	It("should populate secrets with certificates", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		caSecret := NewCACertSecret("operator_namespace")
		Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
		Expect(caSecret.Data).To(HaveKey(bootstrap.CertBytesValue))
		Expect(caSecret.Data).To(HaveKey(bootstrap.KeyBytesValue))

		caCert, err := LoadCertificates(caSecret)
		Expect(err).ToNot(HaveOccurred())

		for _, secret := range secrets {
			Expect(PopulateSecretWithCertificate(secret, caCert, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
			Expect(secret.Data).To(HaveKey(bootstrap.CertBytesValue))
			Expect(secret.Data).To(HaveKey(bootstrap.KeyBytesValue))
			_, err = LoadCertificates(secret)
			Expect(err).ToNot(HaveOccurred())
		}
	})
})

// NewSelfSignedCert creates a CA certificate
func NewSelfSignedCert(notBefore time.Time, notAfter time.Time) *tls.Certificate {
	key, err := certutil.NewPrivateKey()
	Expect(err).ToNot(HaveOccurred())
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   "who",
			Organization: []string{"cares"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	Expect(err).ToNot(HaveOccurred())
	leaf, err := x509.ParseCertificate(certDERBytes)
	Expect(err).ToNot(HaveOccurred())
	keyBytes := certutil.EncodePrivateKeyPEM(key)
	Expect(err).ToNot(HaveOccurred())

	crtBytes := certutil.EncodeCertPEM(leaf)
	crt, err := tls.X509KeyPair(crtBytes, keyBytes)
	Expect(err).ToNot(HaveOccurred())
	crt.Leaf = leaf
	return &crt
}

func CACertsToBundle(crts []*tls.Certificate) []byte {
	var caBundle []byte
	for _, crt := range crts {
		caBundle = append(caBundle, certutil.EncodeCertPEM(crt.Leaf)...)
	}
	return caBundle
}
<commit_msg>Fix another flaky ertificate related unit test<commit_after>package components

import (
	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"math/big"
	"time"

	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	v12 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"kubevirt.io/kubevirt/pkg/certificates/bootstrap"
	certutil "kubevirt.io/kubevirt/pkg/certificates/triple/cert"
)

var _ = Describe("Certificate Management", func() {
	Context("CA certificate bundle", func() {

		It("should drop expired CAs", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(-10*time.Minute)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be properly appended when within the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out the first CA cert if it is outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-3*time.Minute), now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out a CA cert which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[1]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out multiple CA certs which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(-5*time.Minute).Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should ensure that the current CA is not added over and over again", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				current,
				current,
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be protected against misuse by cropping big arrays", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{}
			for i := 1; i < 20; i++ {
				given = append(given, NewSelfSignedCert(now.Add(-1*time.Minute), now.Add(1*time.Hour)))
			}
			givenBundle := CACertsToBundle(given)
			_, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(11))
		})

		It("should immediately suggest a rotation if the cert is not signed by the provided CA", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			ca := NewSelfSignedCert(now, now.Add(1*time.Hour))
			duration := &v1.Duration{Duration: 5 * time.Hour}
			deadline := NextRotationDeadline(current, ca, duration)
			Expect(deadline.Before(time.Now())).To(BeTrue())
		})

		It("should set notBefore on the certificate to notBefore value of the CA certificate ", func() {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(crt.Leaf.NotBefore).To(Equal(caCrt.Leaf.NotBefore))
		})

		table.DescribeTable("should set the notAfter on the certificate according to the supplied duration", func(caDuration time.Duration) {
			now := time.Now()
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			//deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			Expect(crt.Leaf.NotAfter.Unix()).To(BeNumerically("==", now.Add(crtDuration.Duration).Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should suggaest a rotation on the certificate according on 80% of the certificate lifespan", func(caDuration time.Duration) {
			now := time.Now()
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			// Generating certificates may take a little bit of time to execute (entropy, ...). Since we can't
			// inject a fake time into the foreign code which generates the certificates, allow a generous diff of three
			// seconds.
			Expect(NextRotationDeadline(crt, caCrt, crtDuration).Unix()).To(BeNumerically("==", deadline.Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should successfully sign with the current CA the certificate for", func(scretName string) {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			var crtSecret *v12.Secret
			for _, s := range NewCertSecrets("test", "test") {
				if s.Name == scretName {
					crtSecret = s
					break
				}
			}
			Expect(crtSecret).ToNot(BeNil())
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(err).ToNot(HaveOccurred())
			Expect(crt).ToNot(BeNil())
		},
			table.Entry("virt-handler", VirtHandlerCertSecretName),
			table.Entry("virt-controller", VirtControllerCertSecretName),
			table.Entry("virt-api", VirtApiCertSecretName),
			table.Entry("virt-operator", VirtOperatorCertSecretName),
		)
	})

	It("should set the right namespaces on the certificate secrets", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		for _, secret := range secrets[:len(secrets)-1] {
			Expect(secret.Namespace).To(Equal("install_namespace"))
			Expect(secret.Name).ToNot(Equal(VirtOperatorCertSecretName))
		}
		Expect(secrets[len(secrets)-1].Namespace).To(Equal("operator_namespace"))
	})

	It("should create the kubevirt-ca configmap for the right namespace", func() {
		configMap := NewKubeVirtCAConfigMap("namespace")
		Expect(configMap.Namespace).To(Equal("namespace"))
	})

	It("should populate secrets with certificates", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		caSecret := NewCACertSecret("operator_namespace")
		Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
		Expect(caSecret.Data).To(HaveKey(bootstrap.CertBytesValue))
		Expect(caSecret.Data).To(HaveKey(bootstrap.KeyBytesValue))

		caCert, err := LoadCertificates(caSecret)
		Expect(err).ToNot(HaveOccurred())

		for _, secret := range secrets {
			Expect(PopulateSecretWithCertificate(secret, caCert, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
			Expect(secret.Data).To(HaveKey(bootstrap.CertBytesValue))
			Expect(secret.Data).To(HaveKey(bootstrap.KeyBytesValue))
			_, err = LoadCertificates(secret)
			Expect(err).ToNot(HaveOccurred())
		}
	})
})

// NewSelfSignedCert creates a CA certificate
func NewSelfSignedCert(notBefore time.Time, notAfter time.Time) *tls.Certificate {
	key, err := certutil.NewPrivateKey()
	Expect(err).ToNot(HaveOccurred())
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   "who",
			Organization: []string{"cares"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	Expect(err).ToNot(HaveOccurred())
	leaf, err := x509.ParseCertificate(certDERBytes)
	Expect(err).ToNot(HaveOccurred())
	keyBytes := certutil.EncodePrivateKeyPEM(key)
	Expect(err).ToNot(HaveOccurred())

	crtBytes := certutil.EncodeCertPEM(leaf)
	crt, err := tls.X509KeyPair(crtBytes, keyBytes)
	Expect(err).ToNot(HaveOccurred())
	crt.Leaf = leaf
	return &crt
}

func CACertsToBundle(crts []*tls.Certificate) []byte {
	var caBundle []byte
	for _, crt := range crts {
		caBundle = append(caBundle, certutil.EncodeCertPEM(crt.Leaf)...)
	}
	return caBundle
}
<|endoftext|>


<commit_before>package ops

import (
	"reflect"
	"testing"
)

func Test_opFalse(t *testing.T) {
	type args struct {
		c *context
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"pushes false",
			args{emptyContext()},
			&stack{[]byte{0x00, 0x00, 0x0, 0x00}},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := opFalse(tt.args.c)

			if (err != nil) != tt.wantErr {
				t.Errorf("opFalse() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(tt.args.c.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.args.c, tt.want)
			}
		})
	}
}

func Test_op1Negate(t *testing.T) {
	type args struct {
		c *context
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"pushes negative 1",
			args{emptyContext()},
			&stack{[]byte{0x40, 0x00, 0x00, 0x01}},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := op1Negate(tt.args.c)

			if (err != nil) != tt.wantErr {
				t.Errorf("op1Negate() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(tt.args.c.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.args.c, tt.want)
			}
		})
	}
}

func Test_opTrue(t *testing.T) {
	type args struct {
		c *context
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"pushes 1",
			args{emptyContext()},
			&stack{[]byte{0x00, 0x00, 0x00, 0x01}},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := opTrue(tt.args.c)

			if (err != nil) != tt.wantErr {
				t.Errorf("opTrue() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(tt.args.c.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.args.c, tt.want)
			}
		})
	}
}

func Test_createOpPushNBytes(t *testing.T) {
	type args struct {
		context *context
		n       uint8
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"zero",
			args{contextWithData([]byte{0x00}), 0},
			&stack{},
			false,
		},
		{
			"correct number of bytes available",
			args{contextWithData([]byte{0x00}), 1},
			&stack{[]byte{0x00}},
			false,
		},
		{
			"incorrect number of bytes available",
			args{contextWithData([]byte{0x01}), 2},
			&stack{},
			true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := createOpPushNBytes(tt.args.n)

			err := op(tt.args.context)

			if (err != nil) != tt.wantErr {
				t.Errorf("op() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(tt.args.context.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.want, tt.args.context.stack)
			}
		})
	}
}

func Test_createOpPushN(t *testing.T) {
	type args struct {
		n uint8
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"simple",
			args{0xff},
			&stack{[]byte{0x00, 0x00, 0x00, 0xff}},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := createOpPushN(tt.args.n)
			context := emptyContext()

			err := op(context)

			if (err != nil) != tt.wantErr {
				t.Errorf("op() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(context.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.want, context.stack)
			}
		})
	}
}
<commit_msg>Simplify constant tests<commit_after>package ops

import (
	"reflect"
	"testing"
)

func TestConstantsOps(t *testing.T) {
	type args struct {
		context *context
	}
	type want struct {
		stack *stack
		alt   *stack
		err   error
	}
	type test struct {
		name string
		args args
		want want
	}

	tests := []struct {
		name  string
		op    Op
		tests []test
	}{
		{
			"opFalse",
			opFalse,
			[]test{
				{
					"pushes false",
					args{emptyContext()},
					want{
						&stack{[]byte{0x00, 0x00, 0x0, 0x00}},
						&stack{},
						nil,
					},
				},
			},
		},
		{
			"op1Negate",
			op1Negate,
			[]test{
				{
					"pushes negative 1",
					args{emptyContext()},
					want{
						&stack{[]byte{0x40, 0x00, 0x00, 0x01}},
						&stack{},
						nil,
					},
				},
			},
		},
		{
			"opTrue",
			opTrue,
			[]test{
				//
				{
					"pushes 1",
					args{emptyContext()},
					want{
						&stack{[]byte{0x00, 0x00, 0x00, 0x01}},
						&stack{},
						nil,
					},
				},
			},
		},
	}

	for _, opTest := range tests {
		for _, test := range opTest.tests {

			t.Run(opTest.name+" "+test.name, func(t *testing.T) {
				err := opTest.op(test.args.context)
				if err != test.want.err {
					t.Errorf("%s() error = %v, want err %v", opTest.name, err, test.want.err)
				}

				if !reflect.DeepEqual(test.want.stack, test.args.context.stack) {
					t.Errorf("want %v; got %v", test.want.stack, test.args.context.stack)
				}

				if !reflect.DeepEqual(test.want.alt, test.args.context.alt) {
					t.Errorf("want %v; got %v", test.want.alt, test.args.context.alt)
				}
			})

		}
	}
}

func Test_createOpPushNBytes(t *testing.T) {
	type args struct {
		context *context
		n       uint8
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"zero",
			args{contextWithData([]byte{0x00}), 0},
			&stack{},
			false,
		},
		{
			"correct number of bytes available",
			args{contextWithData([]byte{0x00}), 1},
			&stack{[]byte{0x00}},
			false,
		},
		{
			"incorrect number of bytes available",
			args{contextWithData([]byte{0x01}), 2},
			&stack{},
			true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := createOpPushNBytes(tt.args.n)

			err := op(tt.args.context)

			if (err != nil) != tt.wantErr {
				t.Errorf("op() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(tt.args.context.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.want, tt.args.context.stack)
			}
		})
	}
}

func Test_createOpPushN(t *testing.T) {
	type args struct {
		n uint8
	}
	tests := []struct {
		name    string
		args    args
		want    *stack
		wantErr bool
	}{
		{
			"simple",
			args{0xff},
			&stack{[]byte{0x00, 0x00, 0x00, 0xff}},
			false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := createOpPushN(tt.args.n)
			context := emptyContext()

			err := op(context)

			if (err != nil) != tt.wantErr {
				t.Errorf("op() error = %v, wantErr %v", err, tt.wantErr)
			}

			if !reflect.DeepEqual(context.stack, tt.want) {
				t.Errorf("Want %+v; Got %+v", tt.want, context.stack)
			}
		})
	}
}
<|endoftext|>


<commit_before>package queue

import (
	"fmt"
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
)

var testRedis = "redis://redisqueue.kaveh.me:6379"

func TestPartitions(t *testing.T) {
	Partitions([]string{testRedis})
	_, err := redisPool[0].conn.Do("PING")
	if err != nil {
		t.Error("SetRedisPool items are not set correctly")
	}
}

func TestPartitionsBadURL(t *testing.T) {
	paniced := false
	defer func() {
		// recover from panic if one occured. Set err to nil otherwise.
		e := recover()
		if e != nil {
			paniced = true
		}
	}()
	Partitions([]string{"redis://127.0.0.1:0"})
	if !paniced {
		t.Error("Accepted a bad URL without panic")
	}
}

func TestAddTask(t *testing.T) {
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(4, "test")
	r, e := redisdb.Do("RPOP", "WAREHOUSE_0")
	s, e := redis.String(r, e)
	if s != "4;test" {
		t.Error("Task is stored incorrectly: ", s)
	}
}

func TestQueuesInPartision(t *testing.T) {
	QueuesInPartision(5)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(4, "test")
	r, e := redisdb.Do("RPOP", "WAREHOUSE_4")
	s, e := redis.String(r, e)
	if s != "4;test" {
		t.Error("Task is stored incorrectly: ", s)
	}

}

func TestAnalysePool(t *testing.T) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	AddTask(2, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			}
		}
	}
	exitOnEmpty := func() bool {
		return true
	}
	AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	r, e := redisdb.Do("LLEN", "WAREHOUSE_0")
	s, e := redis.Int64(r, e)
	if s != 0 {
		t.Error("Queue is not empty after processing tasks: ", s)
	}

}

func TestAnalysePoolFailurePending(t *testing.T) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			case <-time.After(1 * time.Second):
				fmt.Println("no new event for 2 seconds for ID", id)
				<-next
				success <- false
				return
			}
		}
	}
	exitOnEmpty := func() bool {
		return true
	}
	AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	r, e := redisdb.Do("GET", "PENDING::2")
	s, e := redis.Int(r, e)
	if s != 1 {
		t.Error("Task id 2 is not pending: ", s)
	}

}

func TestAnalysePoolCheckingWaiting(t *testing.T) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			}
		}
	}
	exit := false
	exitOnEmpty := func() bool {
		return exit
	}
	go AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	time.Sleep(100 * time.Millisecond)
	r, e := redisdb.Do("GET", "PENDING::2")
	s, e := redis.Int(r, e)
	if s != 1 {
		t.Error("Task id 2 is not pending after queue is empty: ", s)
	}
	AddTask(2, "stop")
	time.Sleep(200 * time.Millisecond)
	r, e = redisdb.Do("GET", "PENDING::2")
	s, e = redis.Int(r, e)
	if s != 0 {
		t.Error("Task 2 did not clear: ", s)
	}
	exit = true
	time.Sleep(200 * time.Millisecond)
}

func BenchmarkAddTask(b *testing.B) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisPool[0].conn.Do("FLUSHALL")
	for i := 0; i < b.N; i++ {
		AddTask(i, "stop")
	}
}

func BenchmarkRemoveTask(b *testing.B) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisPool[0].conn.Do("FLUSHALL")
	redisdb := redisPool[0].conn
	for i := 0; i < b.N; i++ {
		removeTask(redisdb, "WAREHOUSE_0")
	}
}

// This will act both as test and example in documentation
func ExampleAnalysePool() {
	QueuesInPartision(1)
	Partitions([]string{"redis://redisqueue.kaveh.me:6379"})
	redisPool[0].conn.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	AddTask(2, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if id == 2 {
					time.Sleep(20 * time.Millisecond)
				}
				fmt.Println(id, msg)
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			case <-time.After(2 * time.Second):
				fmt.Println("no new event for 2 seconds for ID", id)
				<-next
				success <- false
				return
			}
		}
	}
	exitOnEmpty := func() bool {
		return true
	}
	AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	// Output:
	// 1 start
	// 1 stop
	// 2 start
	// 2 stop

}
<commit_msg>redis url is wrong<commit_after>package queue

import (
	"fmt"
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
)

var testRedis = "redis://localhost:6379"

func TestPartitions(t *testing.T) {
	Partitions([]string{testRedis})
	_, err := redisPool[0].conn.Do("PING")
	if err != nil {
		t.Error("SetRedisPool items are not set correctly")
	}
}

func TestPartitionsBadURL(t *testing.T) {
	paniced := false
	defer func() {
		// recover from panic if one occured. Set err to nil otherwise.
		e := recover()
		if e != nil {
			paniced = true
		}
	}()
	Partitions([]string{"redis://127.0.0.1:0"})
	if !paniced {
		t.Error("Accepted a bad URL without panic")
	}
}

func TestAddTask(t *testing.T) {
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(4, "test")
	r, e := redisdb.Do("RPOP", "WAREHOUSE_0")
	s, e := redis.String(r, e)
	if s != "4;test" {
		t.Error("Task is stored incorrectly: ", s)
	}
}

func TestQueuesInPartision(t *testing.T) {
	QueuesInPartision(5)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(4, "test")
	r, e := redisdb.Do("RPOP", "WAREHOUSE_4")
	s, e := redis.String(r, e)
	if s != "4;test" {
		t.Error("Task is stored incorrectly: ", s)
	}

}

func TestAnalysePool(t *testing.T) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	AddTask(2, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			}
		}
	}
	exitOnEmpty := func() bool {
		return true
	}
	AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	r, e := redisdb.Do("LLEN", "WAREHOUSE_0")
	s, e := redis.Int64(r, e)
	if s != 0 {
		t.Error("Queue is not empty after processing tasks: ", s)
	}

}

func TestAnalysePoolFailurePending(t *testing.T) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			case <-time.After(1 * time.Second):
				fmt.Println("no new event for 2 seconds for ID", id)
				<-next
				success <- false
				return
			}
		}
	}
	exitOnEmpty := func() bool {
		return true
	}
	AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	r, e := redisdb.Do("GET", "PENDING::2")
	s, e := redis.Int(r, e)
	if s != 1 {
		t.Error("Task id 2 is not pending: ", s)
	}

}

func TestAnalysePoolCheckingWaiting(t *testing.T) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisdb := redisPool[0].conn
	redisdb.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			}
		}
	}
	exit := false
	exitOnEmpty := func() bool {
		return exit
	}
	go AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	time.Sleep(100 * time.Millisecond)
	r, e := redisdb.Do("GET", "PENDING::2")
	s, e := redis.Int(r, e)
	if s != 1 {
		t.Error("Task id 2 is not pending after queue is empty: ", s)
	}
	AddTask(2, "stop")
	time.Sleep(200 * time.Millisecond)
	r, e = redisdb.Do("GET", "PENDING::2")
	s, e = redis.Int(r, e)
	if s != 0 {
		t.Error("Task 2 did not clear: ", s)
	}
	exit = true
	time.Sleep(200 * time.Millisecond)
}

func BenchmarkAddTask(b *testing.B) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisPool[0].conn.Do("FLUSHALL")
	for i := 0; i < b.N; i++ {
		AddTask(i, "stop")
	}
}

func BenchmarkRemoveTask(b *testing.B) {
	QueuesInPartision(1)
	Partitions([]string{testRedis})
	redisPool[0].conn.Do("FLUSHALL")
	redisdb := redisPool[0].conn
	for i := 0; i < b.N; i++ {
		removeTask(redisdb, "WAREHOUSE_0")
	}
}

// This will act both as test and example in documentation
func ExampleAnalysePool() {
	QueuesInPartision(1)
	Partitions([]string{"redis://localhost:6379"})
	redisPool[0].conn.Do("FLUSHALL")
	AddTask(1, "start")
	AddTask(2, "start")
	AddTask(1, "stop")
	AddTask(2, "stop")
	analyzer := func(id int, msg_channel chan string, success chan bool, next chan bool) {
		for {
			select {
			case msg := <-msg_channel:
				if id == 2 {
					time.Sleep(20 * time.Millisecond)
				}
				fmt.Println(id, msg)
				if msg == "stop" {
					<-next
					success <- true
					return
				}
			case <-time.After(2 * time.Second):
				fmt.Println("no new event for 2 seconds for ID", id)
				<-next
				success <- false
				return
			}
		}
	}
	exitOnEmpty := func() bool {
		return true
	}
	AnalysePool(1, 2, 1, exitOnEmpty, analyzer)
	// Output:
	// 1 start
	// 1 stop
	// 2 start
	// 2 stop

}
<|endoftext|>