<commit_before>/*
Copyright 2018 The Kubernetes 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 route

import (
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	coreinformers "k8s.io/client-go/informers/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	v1node "k8s.io/kubernetes/pkg/api/v1/node"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/util/metrics"
	nodeutil "k8s.io/kubernetes/pkg/util/node"
)

const (
	// Maximal number of concurrent CreateRoute API calls.
	// TODO: This should be per-provider.
	maxConcurrentRouteCreations int = 200
	// Maximum number of retries of route creations.
	maxRetries int = 5
	// Maximum number of retries of node status update.
	updateNodeStatusMaxRetries int = 3
)

type RouteController struct {
	routes           cloudprovider.Routes
	kubeClient       clientset.Interface
	clusterName      string
	clusterCIDR      *net.IPNet
	nodeLister       corelisters.NodeLister
	nodeListerSynced cache.InformerSynced
	broadcaster      record.EventBroadcaster
	recorder         record.EventRecorder
}

func New(routes cloudprovider.Routes, kubeClient clientset.Interface, nodeInformer coreinformers.NodeInformer, clusterName string, clusterCIDR *net.IPNet) *RouteController {
	if kubeClient != nil && kubeClient.CoreV1().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("route_controller", kubeClient.CoreV1().RESTClient().GetRateLimiter())
	}

	if clusterCIDR == nil {
		glog.Fatal("RouteController: Must specify clusterCIDR.")
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "route_controller"})

	rc := &RouteController{
		routes:           routes,
		kubeClient:       kubeClient,
		clusterName:      clusterName,
		clusterCIDR:      clusterCIDR,
		nodeLister:       nodeInformer.Lister(),
		nodeListerSynced: nodeInformer.Informer().HasSynced,
		broadcaster:      eventBroadcaster,
		recorder:         recorder,
	}

	return rc
}

func (rc *RouteController) Run(stopCh <-chan struct{}, syncPeriod time.Duration) {
	defer utilruntime.HandleCrash()

	glog.Info("Starting route controller")
	defer glog.Info("Shutting down route controller")

	if !controller.WaitForCacheSync("route", stopCh, rc.nodeListerSynced) {
		return
	}

	if rc.broadcaster != nil {
		rc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(rc.kubeClient.CoreV1().RESTClient()).Events("")})
	}

	// TODO: If we do just the full Resync every 5 minutes (default value)
	// that means that we may wait up to 5 minutes before even starting
	// creating a route for it. This is bad.
	// We should have a watch on node and if we observe a new node (with CIDR?)
	// trigger reconciliation for that node.
	go wait.NonSlidingUntil(func() {
		if err := rc.reconcileNodeRoutes(); err != nil {
			glog.Errorf("Couldn't reconcile node routes: %v", err)
		}
	}, syncPeriod, stopCh)

	<-stopCh
}

func (rc *RouteController) reconcileNodeRoutes() error {
	routeList, err := rc.routes.ListRoutes(rc.clusterName)
	if err != nil {
		return fmt.Errorf("error listing routes: %v", err)
	}
	nodes, err := rc.nodeLister.List(labels.Everything())
	if err != nil {
		return fmt.Errorf("error listing nodes: %v", err)
	}
	return rc.reconcile(nodes, routeList)
}

func (rc *RouteController) reconcile(nodes []*v1.Node, routes []*cloudprovider.Route) error {
	// nodeCIDRs maps nodeName->nodeCIDR
	nodeCIDRs := make(map[types.NodeName]string)
	// routeMap maps routeTargetNode->route
	routeMap := make(map[types.NodeName]*cloudprovider.Route)
	for _, route := range routes {
		if route.TargetNode != "" {
			routeMap[route.TargetNode] = route
		}
	}

	wg := sync.WaitGroup{}
	rateLimiter := make(chan struct{}, maxConcurrentRouteCreations)
	for _, route := range routes {
		if rc.isResponsibleForRoute(route) {
			// Check if this route is a blackhole, or applies to a node we know about & has an incorrect CIDR.
			if route.Blackhole || (cidrForNode(nodes, string(route.TargetNode)) != route.DestinationCIDR) {
				wg.Add(1)
				// Delete the route.
				go func(route *cloudprovider.Route, startTime time.Time) {
					glog.Infof("Deleting route %s %s", route.Name, route.DestinationCIDR)
					if err := rc.routes.DeleteRoute(rc.clusterName, route); err != nil {
						glog.Errorf("Could not delete route %s %s after %v: %v", route.Name, route.DestinationCIDR, time.Now().Sub(startTime), err)
					} else {
						glog.Infof("Deleted route %s %s after %v", route.Name, route.DestinationCIDR, time.Now().Sub(startTime))
					}
					wg.Done()

				}(route, time.Now())
			}
		}
	}
	wg.Wait()
	for _, node := range nodes {
		// Skip if the node hasn't been assigned a CIDR yet.
		if node.Spec.PodCIDR == "" {
			continue
		}
		nodeName := types.NodeName(node.Name)
		// Check if we have a route for this node w/ the correct CIDR.
		r := routeMap[nodeName]
		if r == nil || r.DestinationCIDR != node.Spec.PodCIDR {
			// If not, create the route.
			route := &cloudprovider.Route{
				TargetNode:      nodeName,
				DestinationCIDR: node.Spec.PodCIDR,
			}
			nameHint := string(node.UID)
			wg.Add(1)
			go func(nodeName types.NodeName, nameHint string, route *cloudprovider.Route) {
				defer wg.Done()
				backoff := wait.Backoff{
					Duration: 1 * time.Second,
					Factor:   2,
					Jitter:   0,
					Steps:    4,
				}
				wait.ExponentialBackoff(backoff, func() (bool, error) {
					startTime := time.Now()
					// Ensure that we don't have more than maxConcurrentRouteCreations
					// CreateRoute calls in flight.
					rateLimiter <- struct{}{}
					glog.Infof("Creating route for node %s %s with hint %s, throttled %v", nodeName, route.DestinationCIDR, nameHint, time.Now().Sub(startTime))
					err := rc.routes.CreateRoute(rc.clusterName, nameHint, route)
					<-rateLimiter

					rc.updateNetworkingCondition(nodeName, err == nil)
					if err != nil {
						msg := fmt.Sprintf("Could not create route %s %s for node %s after %v: %v", nameHint, route.DestinationCIDR, nodeName, time.Now().Sub(startTime), err)
						if rc.recorder != nil {
							rc.recorder.Eventf(
								&v1.ObjectReference{
									Kind:      "Node",
									Name:      string(nodeName),
									UID:       types.UID(nodeName),
									Namespace: "",
								}, v1.EventTypeWarning, "FailedToCreateRoute", msg)
						}
						glog.Error(msg)

					} else {
						glog.Infof("Created route for node %s %s with hint %s after %v", nodeName, route.DestinationCIDR, nameHint, time.Now().Sub(startTime))
						return true, nil
					}
					return false, nil
				})
			}(nodeName, nameHint, route)
		} else {
			// Update condition only if it doesn't reflect the current state.
			_, condition := v1node.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
			if condition == nil || condition.Status != v1.ConditionFalse {
				rc.updateNetworkingCondition(types.NodeName(node.Name), true)
			}
		}
		nodeCIDRs[nodeName] = node.Spec.PodCIDR
	}
	wg.Wait()
	return nil
}

func cidrForNode(nodes []*v1.Node, name string) string {
	for _, node := range nodes {
		if node.Name == name {
			return node.Spec.PodCIDR
		}
	}
	return ""
}

func (rc *RouteController) updateNetworkingCondition(nodeName types.NodeName, routeCreated bool) error {
	var err error
	for i := 0; i < updateNodeStatusMaxRetries; i++ {
		// Patch could also fail, even though the chance is very slim. So we still do
		// patch in the retry loop.
		currentTime := metav1.Now()
		if routeCreated {
			err = nodeutil.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionFalse,
				Reason:             "RouteCreated",
				Message:            "RouteController created a route",
				LastTransitionTime: currentTime,
			})
		} else {
			err = nodeutil.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionTrue,
				Reason:             "NoRouteCreated",
				Message:            "RouteController failed to create a route",
				LastTransitionTime: currentTime,
			})
		}
		if err == nil {
			return nil
		}
		if !errors.IsConflict(err) {
			glog.Errorf("Error updating node %s: %v", nodeName, err)
			return err
		}
		glog.V(4).Infof("Error updating node %s, retrying: %v", nodeName, err)
	}
	glog.Errorf("Error updating node %s: %v", nodeName, err)
	return err
}

func (rc *RouteController) isResponsibleForRoute(route *cloudprovider.Route) bool {
	_, cidr, err := net.ParseCIDR(route.DestinationCIDR)
	if err != nil {
		glog.Errorf("Ignoring route %s, unparsable CIDR: %v", route.Name, err)
		return false
	}
	// Not responsible if this route's CIDR is not within our clusterCIDR
	lastIP := make([]byte, len(cidr.IP))
	for i := range lastIP {
		lastIP[i] = cidr.IP[i] | ^cidr.Mask[i]
	}
	if !rc.clusterCIDR.Contains(cidr.IP) || !rc.clusterCIDR.Contains(lastIP) {
		return false
	}
	return true
}
<commit_msg>soe log<commit_after>/*
Copyright 2018 The Kubernetes 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 route

import (
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	coreinformers "k8s.io/client-go/informers/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	v1node "k8s.io/kubernetes/pkg/api/v1/node"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/util/metrics"
	nodeutil "k8s.io/kubernetes/pkg/util/node"
)

const (
	// Maximal number of concurrent CreateRoute API calls.
	// TODO: This should be per-provider.
	maxConcurrentRouteCreations int = 200
	// Maximum number of retries of route creations.
	maxRetries int = 5
	// Maximum number of retries of node status update.
	updateNodeStatusMaxRetries int = 3
)

type RouteController struct {
	routes           cloudprovider.Routes
	kubeClient       clientset.Interface
	clusterName      string
	clusterCIDR      *net.IPNet
	nodeLister       corelisters.NodeLister
	nodeListerSynced cache.InformerSynced
	broadcaster      record.EventBroadcaster
	recorder         record.EventRecorder
}

func New(routes cloudprovider.Routes, kubeClient clientset.Interface, nodeInformer coreinformers.NodeInformer, clusterName string, clusterCIDR *net.IPNet) *RouteController {
	if kubeClient != nil && kubeClient.CoreV1().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("route_controller", kubeClient.CoreV1().RESTClient().GetRateLimiter())
	}

	if clusterCIDR == nil {
		glog.Fatal("RouteController: Must specify clusterCIDR.")
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "route_controller"})

	rc := &RouteController{
		routes:           routes,
		kubeClient:       kubeClient,
		clusterName:      clusterName,
		clusterCIDR:      clusterCIDR,
		nodeLister:       nodeInformer.Lister(),
		nodeListerSynced: nodeInformer.Informer().HasSynced,
		broadcaster:      eventBroadcaster,
		recorder:         recorder,
	}

	return rc
}

func (rc *RouteController) Run(stopCh <-chan struct{}, syncPeriod time.Duration) {
	defer utilruntime.HandleCrash()

	glog.Info("Starting route controller")
	defer glog.Info("Shutting down route controller")

	if !controller.WaitForCacheSync("route", stopCh, rc.nodeListerSynced) {
		return
	}

	if rc.broadcaster != nil {
		rc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(rc.kubeClient.CoreV1().RESTClient()).Events("")})
	}

	// TODO: If we do just the full Resync every 5 minutes (default value)
	// that means that we may wait up to 5 minutes before even starting
	// creating a route for it. This is bad.
	// We should have a watch on node and if we observe a new node (with CIDR?)
	// trigger reconciliation for that node.
	go wait.NonSlidingUntil(func() {
		if err := rc.reconcileNodeRoutes(); err != nil {
			glog.Errorf("Couldn't reconcile node routes: %v", err)
		}
	}, syncPeriod, stopCh)

	<-stopCh
}

func (rc *RouteController) reconcileNodeRoutes() error {
	routeList, err := rc.routes.ListRoutes(rc.clusterName)
	if err != nil {
		return fmt.Errorf("error listing routes: %v", err)
	}
	nodes, err := rc.nodeLister.List(labels.Everything())
	if err != nil {
		return fmt.Errorf("error listing nodes: %v", err)
	}
	return rc.reconcile(nodes, routeList)
}

func (rc *RouteController) reconcile(nodes []*v1.Node, routes []*cloudprovider.Route) error {
	// nodeCIDRs maps nodeName->nodeCIDR
	nodeCIDRs := make(map[types.NodeName]string)
	// routeMap maps routeTargetNode->route
	routeMap := make(map[types.NodeName]*cloudprovider.Route)
	for _, route := range routes {
		glog.Infof("ListRoutes: reconcile %+v",route)
		if route.TargetNode != "" {
			routeMap[route.TargetNode] = route
		}
	}

	wg := sync.WaitGroup{}
	rateLimiter := make(chan struct{}, maxConcurrentRouteCreations)
	for _, route := range routes {
		if rc.isResponsibleForRoute(route) {
			// Check if this route is a blackhole, or applies to a node we know about & has an incorrect CIDR.
			if route.Blackhole || (cidrForNode(nodes, string(route.TargetNode)) != route.DestinationCIDR) {
				glog.Infof("responsible for: %t, %t,%t",route.Blackhole,cidrForNode(nodes, string(route.TargetNode)) != route.DestinationCIDR,route.Blackhole || (cidrForNode(nodes, string(route.TargetNode)) != route.DestinationCIDR))
				wg.Add(1)
				// Delete the route.
				go func(route *cloudprovider.Route, startTime time.Time) {
					glog.Infof("Deleting route %s %s", route.Name, route.DestinationCIDR)
					if err := rc.routes.DeleteRoute(rc.clusterName, route); err != nil {
						glog.Errorf("Could not delete route %s %s after %v: %v", route.Name, route.DestinationCIDR, time.Now().Sub(startTime), err)
					} else {
						glog.Infof("Deleted route %s %s after %v", route.Name, route.DestinationCIDR, time.Now().Sub(startTime))
					}
					wg.Done()

				}(route, time.Now())
			}
		}
	}
	wg.Wait()
	for _, node := range nodes {
		// Skip if the node hasn't been assigned a CIDR yet.
		if node.Spec.PodCIDR == "" {
			continue
		}
		nodeName := types.NodeName(node.Name)
		// Check if we have a route for this node w/ the correct CIDR.
		r := routeMap[nodeName]
		glog.Infof("Node: %s, r=%+v, node.Spec.PodCIDR=%s",node.Name,r, node.Spec.PodCIDR)
		if r == nil || r.DestinationCIDR != node.Spec.PodCIDR {
			// If not, create the route.
			route := &cloudprovider.Route{
				TargetNode:      nodeName,
				DestinationCIDR: node.Spec.PodCIDR,
			}
			nameHint := string(node.UID)
			wg.Add(1)
			go func(nodeName types.NodeName, nameHint string, route *cloudprovider.Route) {
				defer wg.Done()
				backoff := wait.Backoff{
					Duration: 1 * time.Second,
					Factor:   2,
					Jitter:   0,
					Steps:    4,
				}
				wait.ExponentialBackoff(backoff, func() (bool, error) {
					startTime := time.Now()
					// Ensure that we don't have more than maxConcurrentRouteCreations
					// CreateRoute calls in flight.
					rateLimiter <- struct{}{}
					glog.Infof("Creating route for node %s %s with hint %s, throttled %v", nodeName, route.DestinationCIDR, nameHint, time.Now().Sub(startTime))
					err := rc.routes.CreateRoute(rc.clusterName, nameHint, route)
					<-rateLimiter

					rc.updateNetworkingCondition(nodeName, err == nil)
					if err != nil {
						msg := fmt.Sprintf("Could not create route %s %s for node %s after %v: %v", nameHint, route.DestinationCIDR, nodeName, time.Now().Sub(startTime), err)
						if rc.recorder != nil {
							rc.recorder.Eventf(
								&v1.ObjectReference{
									Kind:      "Node",
									Name:      string(nodeName),
									UID:       types.UID(nodeName),
									Namespace: "",
								}, v1.EventTypeWarning, "FailedToCreateRoute", msg)
						}
						glog.Error(msg)

					} else {
						glog.Infof("Created route for node %s %s with hint %s after %v", nodeName, route.DestinationCIDR, nameHint, time.Now().Sub(startTime))
						return true, nil
					}
					return false, nil
				})
			}(nodeName, nameHint, route)
		} else {
			// Update condition only if it doesn't reflect the current state.
			_, condition := v1node.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
			if condition == nil || condition.Status != v1.ConditionFalse {
				rc.updateNetworkingCondition(types.NodeName(node.Name), true)
			}
		}
		nodeCIDRs[nodeName] = node.Spec.PodCIDR
	}
	wg.Wait()
	return nil
}

func cidrForNode(nodes []*v1.Node, name string) string {
	for _, node := range nodes {
		if node.Name == name {
			return node.Spec.PodCIDR
		}
	}
	return ""
}

func (rc *RouteController) updateNetworkingCondition(nodeName types.NodeName, routeCreated bool) error {
	var err error
	for i := 0; i < updateNodeStatusMaxRetries; i++ {
		// Patch could also fail, even though the chance is very slim. So we still do
		// patch in the retry loop.
		currentTime := metav1.Now()
		if routeCreated {
			err = nodeutil.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionFalse,
				Reason:             "RouteCreated",
				Message:            "RouteController created a route",
				LastTransitionTime: currentTime,
			})
		} else {
			err = nodeutil.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionTrue,
				Reason:             "NoRouteCreated",
				Message:            "RouteController failed to create a route",
				LastTransitionTime: currentTime,
			})
		}
		if err == nil {
			return nil
		}
		if !errors.IsConflict(err) {
			glog.Errorf("Error updating node %s: %v", nodeName, err)
			return err
		}
		glog.V(4).Infof("Error updating node %s, retrying: %v", nodeName, err)
	}
	glog.Errorf("Error updating node %s: %v", nodeName, err)
	return err
}

func (rc *RouteController) isResponsibleForRoute(route *cloudprovider.Route) bool {
	_, cidr, err := net.ParseCIDR(route.DestinationCIDR)
	if err != nil {
		glog.Errorf("Ignoring route %s, unparsable CIDR: %v", route.Name, err)
		return false
	}
	// Not responsible if this route's CIDR is not within our clusterCIDR
	lastIP := make([]byte, len(cidr.IP))
	for i := range lastIP {
		lastIP[i] = cidr.IP[i] | ^cidr.Mask[i]
	}
	if !rc.clusterCIDR.Contains(cidr.IP) || !rc.clusterCIDR.Contains(lastIP) {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package subscribed

import (
	"k8s.io/apimachinery/pkg/labels"

	knv1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1"
	"knative.dev/eventing/pkg/client/clientset/versioned"
	"knative.dev/eventing/pkg/client/informers/externalversions"
	kneventinglister "knative.dev/eventing/pkg/client/listers/eventing/v1alpha1"
	"knative.dev/pkg/signals"
)

const (
	keySource           = "source"
	keyEventType        = "type"
	keyEventTypeVersion = "eventtypeversion"
)

//EventsClient interface
type EventsClient interface {
	GetSubscribedEvents(appName string) (Events, error)
}

//Events represents collection of all events with subscriptions
type Events struct {
	EventsInfo []Event `json:"eventsInfo"`
}

//Event represents basic information about event
type Event struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

type eventsClient struct {
	triggerLister kneventinglister.TriggerLister
}

//NewEventsClient function creates client for retrieving all active events
func NewEventsClient(client versioned.Interface) EventsClient {
	informerFactory := externalversions.NewSharedInformerFactory(client, 0)
	lister := informerFactory.Eventing().V1alpha1().Triggers().Lister()
	ctx := signals.NewContext()
	informerFactory.Start(ctx.Done())
	informerFactory.WaitForCacheSync(ctx.Done())

	return &eventsClient{
		triggerLister: lister,
	}
}

func (ec *eventsClient) GetSubscribedEvents(appName string) (Events, error) {
	activeEvents, err := ec.getEventList(appName)
	if err != nil {
		return Events{}, err
	}
	activeEvents = removeDuplicates(activeEvents)
	return Events{EventsInfo: activeEvents}, nil
}

func (ec *eventsClient) getEventList(appName string) ([]Event, error) {
	triggerList, err := ec.triggerLister.List(labels.Everything())
	if err != nil {
		return nil, err
	}

	return getEventsFromTriggers(triggerList, appName), nil
}

func getEventsFromTriggers(triggerList []*knv1alpha1.Trigger, appName string) []Event {
	events := make([]Event, 0, len(triggerList))

	for _, trigger := range triggerList {
		if trigger == nil {
			continue
		}
		if trigger.Spec.Filter == nil {
			continue
		}
		if trigger.Spec.Filter.Attributes == nil {
			continue
		}
		if source, ok := (*trigger.Spec.Filter.Attributes)[keySource]; ok && source == appName {
			event := Event{
				Name:    (*trigger.Spec.Filter.Attributes)[keyEventType],
				Version: (*trigger.Spec.Filter.Attributes)[keyEventTypeVersion],
			}
			events = append(events, event)
		}
	}
	return events
}

func removeDuplicates(events []Event) []Event {
	keys := make(map[Event]bool)
	list := make([]Event, 0)
	for _, event := range events {
		if _, value := keys[event]; !value {
			keys[event] = true
			list = append(list, event)
		}
	}
	return list
}
<commit_msg>Refactor event-service informer (#8018)<commit_after>package subscribed

import (
	"context"
	"log"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/labels"

	knv1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1"
	"knative.dev/eventing/pkg/client/clientset/versioned"
	"knative.dev/eventing/pkg/client/informers/externalversions"
	kneventinglister "knative.dev/eventing/pkg/client/listers/eventing/v1alpha1"
	"knative.dev/pkg/signals"
)

const (
	keySource           = "source"
	keyEventType        = "type"
	keyEventTypeVersion = "eventtypeversion"

	informerSyncTimeout = time.Second * 5
)

//EventsClient interface
type EventsClient interface {
	GetSubscribedEvents(appName string) (Events, error)
}

//Events represents collection of all events with subscriptions
type Events struct {
	EventsInfo []Event `json:"eventsInfo"`
}

//Event represents basic information about event
type Event struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

type eventsClient struct {
	triggerLister kneventinglister.TriggerLister
}

// waitForInformersSyncOrDie blocks until all informer caches are synced, or panics after a timeout.
func waitForInformersSyncOrDie(f externalversions.SharedInformerFactory) {
	log.Println("waiting for informers caches sync...")
	ctx, cancel := context.WithTimeout(context.Background(), informerSyncTimeout)
	defer cancel()

	err := hasSynced(ctx, f.WaitForCacheSync)
	if err != nil {
		log.Fatalf("Error waiting for caches sync: %s", err)
	}
}

type waitForCacheSyncFunc func(stopCh <-chan struct{}) map[reflect.Type]bool

// hasSynced blocks until the given informer sync waiting function completes. It returns an error if the passed context
// gets canceled.
func hasSynced(ctx context.Context, fn waitForCacheSyncFunc) error {
	// synced gets closed as soon as fn returns
	synced := make(chan struct{})

	// closing stopWait forces fn to return, which happens whenever ctx
	// gets canceled
	stopWait := make(chan struct{})
	defer close(stopWait)

	// close the synced channel if the `WaitForCacheSync()` finished the execution cleanly
	go func() {
		informersCacheSync := fn(stopWait)
		res := true
		for _, sync := range informersCacheSync {
			if !sync {
				res = false
			}
		}
		if res {
			close(synced)
		}
	}()

	// wait for closure of the goroutine or return an error if it timed out
	select {
	case <-ctx.Done():
		return ctx.Err()
	case <-synced:
	}

	return nil
}

//NewEventsClient function creates client for retrieving all active events
func NewEventsClient(client versioned.Interface) EventsClient {
	informerFactory := externalversions.NewSharedInformerFactory(client, 0)
	lister := informerFactory.Eventing().V1alpha1().Triggers().Lister()
	ctx := signals.NewContext()
	informerFactory.Start(ctx.Done())
	waitForInformersSyncOrDie(informerFactory)

	return &eventsClient{
		triggerLister: lister,
	}
}

func (ec *eventsClient) GetSubscribedEvents(appName string) (Events, error) {
	activeEvents, err := ec.getEventList(appName)
	if err != nil {
		return Events{}, err
	}
	activeEvents = removeDuplicates(activeEvents)
	return Events{EventsInfo: activeEvents}, nil
}

func (ec *eventsClient) getEventList(appName string) ([]Event, error) {
	triggerList, err := ec.triggerLister.List(labels.Everything())
	if err != nil {
		return nil, err
	}

	return getEventsFromTriggers(triggerList, appName), nil
}

func getEventsFromTriggers(triggerList []*knv1alpha1.Trigger, appName string) []Event {
	events := make([]Event, 0, len(triggerList))

	for _, trigger := range triggerList {
		if trigger == nil {
			continue
		}
		if trigger.Spec.Filter == nil {
			continue
		}
		if trigger.Spec.Filter.Attributes == nil {
			continue
		}
		if source, ok := (*trigger.Spec.Filter.Attributes)[keySource]; ok && source == appName {
			event := Event{
				Name:    (*trigger.Spec.Filter.Attributes)[keyEventType],
				Version: (*trigger.Spec.Filter.Attributes)[keyEventTypeVersion],
			}
			events = append(events, event)
		}
	}
	return events
}

func removeDuplicates(events []Event) []Event {
	keys := make(map[Event]bool)
	list := make([]Event, 0)
	for _, event := range events {
		if _, value := keys[event]; !value {
			keys[event] = true
			list = append(list, event)
		}
	}
	return list
}
<|endoftext|>


<commit_before>package dns

// A structure for handling zone data

import (
	"github.com/miekg/radix"
	"strings"
	"sync"
)

// Zone represents a DNS zone.
type Zone struct {
	Origin       string // Origin of the zone
	Wildcard     int    // Whenever we see a wildcard name, this is incremented
	*radix.Radix        // Zone data
	mutex	 *sync.RWMutex
}

// ZoneData holds all the RRs having their owner name equal to Name.
type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map of the RR type to the RR
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
	NonAuth    bool                   // Always false, except for NSsets that differ from z.Origin
	mutex      *sync.RWMutex
}

// toRadixName reverses a domain name so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
	if d == "." {
		return "."
	}
	s := ""
	for _, l := range SplitLabels(d) {
		s = strings.ToLower(l) + "." + s
	}
	return "." + s
}

// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
	if origin == "" {
		origin = "."
	}
	if _, _, ok := IsDomainName(origin); !ok {
		return nil
	}
	z := new(Zone)
	z.mutex = new(sync.RWMutex)
	z.Origin = Fqdn(origin)
	z.Radix = radix.New()
	return z
}

// Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error {
	if !IsSubDomain(z.Origin, r.Header().Name) {
		return &Error{Err: "out of zone data", Name: r.Header().Name}
	}

	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd := z.Radix.Find(key)
	if zd == nil {
		defer z.mutex.Unlock()
		// Check if its a wildcard name
		if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
			z.Wildcard++
		}
		zd := new(ZoneData)
		zd.Name = r.Header().Name
		zd.RR = make(map[uint16][]RR)
		zd.Signatures = make(map[uint16][]*RR_RRSIG)
		zd.mutex = new(sync.RWMutex)
		switch t := r.Header().Rrtype; t {
		case TypeRRSIG:
			sigtype := r.(*RR_RRSIG).TypeCovered
			zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
		case TypeNS:
			// NS records with other names than z.Origin are non-auth
			if r.Header().Name != z.Origin {
				zd.NonAuth = true
			}
			fallthrough
		default:
			zd.RR[t] = append(zd.RR[t], r)
		}
		z.Radix.Insert(key, zd)
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	// Name already there
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
	case TypeNS:
		if r.Header().Name != z.Origin {
			zd.Value.(*ZoneData).NonAuth = true
		}
		fallthrough
	default:
		zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
	}
	return nil
}

// Remove removes the RR r from the zone. If there RR can not be found,
// this is a no-op.
func (z *Zone) Remove(r RR) error {
	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd := z.Radix.Find(key)
	if zd == nil {
		defer z.mutex.Unlock()
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	remove := false
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
			if r == zr {
				zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
				remove = true
			}
		}
	default:
		for i, zr := range zd.Value.(*ZoneData).RR[t] {
			if r == zr {
				zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
				remove = true
			}
		}
	}
	if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
		z.Wildcard--
		if z.Wildcard < 0 {
			z.Wildcard = 0
		}
	}
	// TODO(mg): what to do if the whole structure is empty? Set it to nil?
	return nil
}

// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
func (z *Zone) Find(s string) *ZoneData {
	zd := z.Radix.Find(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Predecessor searches the zone for a name shorter than s.
func (z *Zone) Predecessor(s string) *ZoneData {
	zd := z.Radix.Predecessor(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}
<commit_msg>gofmt<commit_after>package dns

// A structure for handling zone data

import (
	"github.com/miekg/radix"
	"strings"
	"sync"
)

// Zone represents a DNS zone. The structure is safe for concurrent access.
type Zone struct {
	Origin       string // Origin of the zone
	Wildcard     int    // Whenever we see a wildcard name, this is incremented
	*radix.Radix        // Zone data
	mutex        *sync.RWMutex
}

// ZoneData holds all the RRs having their owner name equal to Name.
type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map of the RR type to the RR
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
	NonAuth    bool                   // Always false, except for NSsets that differ from z.Origin
	mutex      *sync.RWMutex
}

// toRadixName reverses a domain name so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
	if d == "." {
		return "."
	}
	s := ""
	for _, l := range SplitLabels(d) {
		s = strings.ToLower(l) + "." + s
	}
	return "." + s
}

// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
	if origin == "" {
		origin = "."
	}
	if _, _, ok := IsDomainName(origin); !ok {
		return nil
	}
	z := new(Zone)
	z.mutex = new(sync.RWMutex)
	z.Origin = Fqdn(origin)
	z.Radix = radix.New()
	return z
}

// Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error {
	if !IsSubDomain(z.Origin, r.Header().Name) {
		return &Error{Err: "out of zone data", Name: r.Header().Name}
	}

	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd := z.Radix.Find(key)
	if zd == nil {
		defer z.mutex.Unlock()
		// Check if its a wildcard name
		if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
			z.Wildcard++
		}
		zd := new(ZoneData)
		zd.Name = r.Header().Name
		zd.RR = make(map[uint16][]RR)
		zd.Signatures = make(map[uint16][]*RR_RRSIG)
		zd.mutex = new(sync.RWMutex)
		switch t := r.Header().Rrtype; t {
		case TypeRRSIG:
			sigtype := r.(*RR_RRSIG).TypeCovered
			zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
		case TypeNS:
			// NS records with other names than z.Origin are non-auth
			if r.Header().Name != z.Origin {
				zd.NonAuth = true
			}
			fallthrough
		default:
			zd.RR[t] = append(zd.RR[t], r)
		}
		z.Radix.Insert(key, zd)
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	// Name already there
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
	case TypeNS:
		if r.Header().Name != z.Origin {
			zd.Value.(*ZoneData).NonAuth = true
		}
		fallthrough
	default:
		zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
	}
	return nil
}

// Remove removes the RR r from the zone. If there RR can not be found,
// this is a no-op.
func (z *Zone) Remove(r RR) error {
	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd := z.Radix.Find(key)
	if zd == nil {
		defer z.mutex.Unlock()
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	remove := false
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
			if r == zr {
				zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
				remove = true
			}
		}
	default:
		for i, zr := range zd.Value.(*ZoneData).RR[t] {
			if r == zr {
				zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
				remove = true
			}
		}
	}
	if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
		z.Wildcard--
		if z.Wildcard < 0 {
			z.Wildcard = 0
		}
	}
	// TODO(mg): what to do if the whole structure is empty? Set it to nil?
	return nil
}

// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
func (z *Zone) Find(s string) *ZoneData {
	zd := z.Radix.Find(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Predecessor searches the zone for a name shorter than s.
func (z *Zone) Predecessor(s string) *ZoneData {
	zd := z.Radix.Predecessor(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}
<|endoftext|>


<commit_before>package mdns

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/miekg/dns"
)

const (
	// defaultTTL is the default TTL value in returned DNS records in seconds.
	defaultTTL = 120
)

// Zone is the interface used to integrate with the server and
// to serve records dynamically
type Zone interface {
	// Records returns DNS records in response to a DNS question.
	Records(q dns.Question) []dns.RR
}

// MDNSService is used to export a named service by implementing a Zone
type MDNSService struct {
	Instance string   // Instance name (e.g. "hostService name")
	Service  string   // Service name (e.g. "_http._tcp.")
	Domain   string   // If blank, assumes "local"
	HostName string   // Host machine DNS name (e.g. "mymachine.net.")
	Port     int      // Service Port
	IPs      []net.IP // IP addresses for the service's host
	TXT      []string // Service TXT records

	serviceAddr  string // Fully qualified service address
	instanceAddr string // Fully qualified instance address
}

// validateFQDN returns an error if the passed string is not a fully qualified
// hdomain name (more specifically, a hostname).
func validateFQDN(s string) error {
	if len(s) == 0 {
		return fmt.Errorf("FQDN must not be blank")
	}
	if s[len(s)-1] != '.' {
		return fmt.Errorf("FQDN must end in period: %s", s)
	}
	// TODO(reddaly): Perform full validation.

	return nil
}

// NewMDNSService returns a new instance of MDNSService.
//
// If domain, hostName, or ips is set to the zero value, then a default value
// will be inferred from the operating system.
//
// TODO(reddaly): This interface may need to change to account for "unique
// record" conflict rules of the mDNS protocol.  Upon startup, the server should
// check to ensure that the instance name does not conflict with other instance
// names, and, if required, select a new name.  There may also be conflicting
// hostName A/AAAA records.
func NewMDNSService(instance, service, domain, hostName string, port int, ips []net.IP, txt []string) (*MDNSService, error) {
	// Sanity check inputs
	if instance == "" {
		return nil, fmt.Errorf("missing service instance name")
	}
	if service == "" {
		return nil, fmt.Errorf("missing service name")
	}
	if port == 0 {
		return nil, fmt.Errorf("missing service port")
	}

	// Set default domain
	if domain == "" {
		domain = "local."
	}
	if err := validateFQDN(domain); err != nil {
		return nil, fmt.Errorf("domain %q is not a fully-qualified domain name: %v", domain, err)
	}

	// Get host information if no host is specified.
	if hostName == "" {
		var err error
		hostName, err = os.Hostname()
		if err != nil {
			return nil, fmt.Errorf("could not determine host: %v", err)
		}
		hostName = fmt.Sprintf("%s.", hostName)
	}
	if err := validateFQDN(hostName); err != nil {
		return nil, fmt.Errorf("hostName %q is not a fully-qualified domain name: %v", hostName, err)
	}

	if len(ips) == 0 {
		var err error
		ips, err = net.LookupIP(hostName)
		if err != nil {
			// Try appending the host domain suffix and lookup again
			// (required for Linux-based hosts)
			tmpHostName := fmt.Sprintf("%s%s.", hostName, domain)

			ips, err = net.LookupIP(tmpHostName)

			if err != nil {
				return nil, fmt.Errorf("could not determine host IP addresses for %s", hostName)
			}
		}
	}
	for _, ip := range ips {
		if ip.To4() == nil && ip.To16() == nil {
			return nil, fmt.Errorf("invalid IP address in IPs list: %v", ip)
		}
	}

	return &MDNSService{
		Instance:     instance,
		Service:      service,
		Domain:       domain,
		HostName:     hostName,
		Port:         port,
		IPs:          ips,
		TXT:          txt,
		serviceAddr:  fmt.Sprintf("%s.%s.", trimDot(service), trimDot(domain)),
		instanceAddr: fmt.Sprintf("%s.%s.%s.", instance, trimDot(service), trimDot(domain)),
	}, nil
}

// trimDot is used to trim the dots from the start or end of a string
func trimDot(s string) string {
	return strings.Trim(s, ".")
}

// Records returns DNS records in response to a DNS question.
func (m *MDNSService) Records(q dns.Question) []dns.RR {
	switch q.Name {
	case m.serviceAddr:
		return m.serviceRecords(q)
	case m.instanceAddr:
		return m.instanceRecords(q)
	case m.HostName:
		if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA {
			return m.instanceRecords(q)
		}
		fallthrough
	default:
		return nil
	}
}

// serviceRecords is called when the query matches the service name
func (m *MDNSService) serviceRecords(q dns.Question) []dns.RR {
	switch q.Qtype {
	case dns.TypeANY:
		fallthrough
	case dns.TypePTR:
		// Build a PTR response for the service
		rr := &dns.PTR{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypePTR,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Ptr: m.instanceAddr,
		}
		servRec := []dns.RR{rr}

		// Get the instance records
		instRecs := m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeANY,
		})

		// Return the service record with the instance records
		return append(servRec, instRecs...)
	default:
		return nil
	}
}

// serviceRecords is called when the query matches the instance name
func (m *MDNSService) instanceRecords(q dns.Question) []dns.RR {
	switch q.Qtype {
	case dns.TypeANY:
		// Get the SRV, which includes A and AAAA
		recs := m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeSRV,
		})

		// Add the TXT record
		recs = append(recs, m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeTXT,
		})...)
		return recs

	case dns.TypeA:
		var rr []dns.RR
		for _, ip := range m.IPs {
			if ip4 := ip.To4(); ip4 != nil {
				rr = append(rr, &dns.A{
					Hdr: dns.RR_Header{
						Name:   m.HostName,
						Rrtype: dns.TypeA,
						Class:  dns.ClassINET,
						Ttl:    defaultTTL,
					},
					A: ip4,
				})
			}
		}
		return rr

	case dns.TypeAAAA:
		var rr []dns.RR
		for _, ip := range m.IPs {
			if ip.To4() != nil {
				// TODO(reddaly): IPv4 addresses could be encoded in IPv6 format and
				// putinto AAAA records, but the current logic puts ipv4-encodable
				// addresses into the A records exclusively.  Perhaps this should be
				// configurable?
				continue
			}

			if ip16 := ip.To16(); ip16 != nil {
				rr = append(rr, &dns.AAAA{
					Hdr: dns.RR_Header{
						Name:   m.HostName,
						Rrtype: dns.TypeAAAA,
						Class:  dns.ClassINET,
						Ttl:    defaultTTL,
					},
					AAAA: ip16,
				})
			}
		}
		return rr

	case dns.TypeSRV:
		// Create the SRV Record
		srv := &dns.SRV{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeSRV,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Priority: 10,
			Weight:   1,
			Port:     uint16(m.Port),
			Target:   m.HostName,
		}
		recs := []dns.RR{srv}

		// Add the A record
		recs = append(recs, m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeA,
		})...)

		// Add the AAAA record
		recs = append(recs, m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeAAAA,
		})...)
		return recs

	case dns.TypeTXT:
		txt := &dns.TXT{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeTXT,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Txt: m.TXT,
		}
		return []dns.RR{txt}
	}
	return nil
}
<commit_msg>Respond to _services._dns-sd._udp.<domain> dns question<commit_after>package mdns

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/miekg/dns"
)

const (
	// defaultTTL is the default TTL value in returned DNS records in seconds.
	defaultTTL = 120
)

// Zone is the interface used to integrate with the server and
// to serve records dynamically
type Zone interface {
	// Records returns DNS records in response to a DNS question.
	Records(q dns.Question) []dns.RR
}

// MDNSService is used to export a named service by implementing a Zone
type MDNSService struct {
	Instance string   // Instance name (e.g. "hostService name")
	Service  string   // Service name (e.g. "_http._tcp.")
	Domain   string   // If blank, assumes "local"
	HostName string   // Host machine DNS name (e.g. "mymachine.net.")
	Port     int      // Service Port
	IPs      []net.IP // IP addresses for the service's host
	TXT      []string // Service TXT records

	serviceAddr  string // Fully qualified service address
	instanceAddr string // Fully qualified instance address
	enumAddr     string // _services._dns-sd._udp.<domain>
}

// validateFQDN returns an error if the passed string is not a fully qualified
// hdomain name (more specifically, a hostname).
func validateFQDN(s string) error {
	if len(s) == 0 {
		return fmt.Errorf("FQDN must not be blank")
	}
	if s[len(s)-1] != '.' {
		return fmt.Errorf("FQDN must end in period: %s", s)
	}
	// TODO(reddaly): Perform full validation.

	return nil
}

// NewMDNSService returns a new instance of MDNSService.
//
// If domain, hostName, or ips is set to the zero value, then a default value
// will be inferred from the operating system.
//
// TODO(reddaly): This interface may need to change to account for "unique
// record" conflict rules of the mDNS protocol.  Upon startup, the server should
// check to ensure that the instance name does not conflict with other instance
// names, and, if required, select a new name.  There may also be conflicting
// hostName A/AAAA records.
func NewMDNSService(instance, service, domain, hostName string, port int, ips []net.IP, txt []string) (*MDNSService, error) {
	// Sanity check inputs
	if instance == "" {
		return nil, fmt.Errorf("missing service instance name")
	}
	if service == "" {
		return nil, fmt.Errorf("missing service name")
	}
	if port == 0 {
		return nil, fmt.Errorf("missing service port")
	}

	// Set default domain
	if domain == "" {
		domain = "local."
	}
	if err := validateFQDN(domain); err != nil {
		return nil, fmt.Errorf("domain %q is not a fully-qualified domain name: %v", domain, err)
	}

	// Get host information if no host is specified.
	if hostName == "" {
		var err error
		hostName, err = os.Hostname()
		if err != nil {
			return nil, fmt.Errorf("could not determine host: %v", err)
		}
		hostName = fmt.Sprintf("%s.", hostName)
	}
	if err := validateFQDN(hostName); err != nil {
		return nil, fmt.Errorf("hostName %q is not a fully-qualified domain name: %v", hostName, err)
	}

	if len(ips) == 0 {
		var err error
		ips, err = net.LookupIP(hostName)
		if err != nil {
			// Try appending the host domain suffix and lookup again
			// (required for Linux-based hosts)
			tmpHostName := fmt.Sprintf("%s%s.", hostName, domain)

			ips, err = net.LookupIP(tmpHostName)

			if err != nil {
				return nil, fmt.Errorf("could not determine host IP addresses for %s", hostName)
			}
		}
	}
	for _, ip := range ips {
		if ip.To4() == nil && ip.To16() == nil {
			return nil, fmt.Errorf("invalid IP address in IPs list: %v", ip)
		}
	}

	return &MDNSService{
		Instance:     instance,
		Service:      service,
		Domain:       domain,
		HostName:     hostName,
		Port:         port,
		IPs:          ips,
		TXT:          txt,
		serviceAddr:  fmt.Sprintf("%s.%s.", trimDot(service), trimDot(domain)),
		instanceAddr: fmt.Sprintf("%s.%s.%s.", instance, trimDot(service), trimDot(domain)),
		enumAddr:     fmt.Sprintf("_services._dns-sd._udp.%s.", trimDot(domain)),
	}, nil
}

// trimDot is used to trim the dots from the start or end of a string
func trimDot(s string) string {
	return strings.Trim(s, ".")
}

// Records returns DNS records in response to a DNS question.
func (m *MDNSService) Records(q dns.Question) []dns.RR {
	switch q.Name {
	case m.enumAddr:
		return m.serviceEnum(q)
	case m.serviceAddr:
		return m.serviceRecords(q)
	case m.instanceAddr:
		return m.instanceRecords(q)
	case m.HostName:
		if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA {
			return m.instanceRecords(q)
		}
		fallthrough
	default:
		return nil
	}
}

func (m *MDNSService) serviceEnum(q dns.Question) []dns.RR {
	switch q.Qtype {
	case dns.TypeANY:
		fallthrough
	case dns.TypePTR:
		rr := &dns.PTR{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypePTR,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Ptr: m.serviceAddr,
		}
		return []dns.RR{rr}
	default:
		return nil
	}
}

// serviceRecords is called when the query matches the service name
func (m *MDNSService) serviceRecords(q dns.Question) []dns.RR {
	switch q.Qtype {
	case dns.TypeANY:
		fallthrough
	case dns.TypePTR:
		// Build a PTR response for the service
		rr := &dns.PTR{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypePTR,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Ptr: m.instanceAddr,
		}
		servRec := []dns.RR{rr}

		// Get the instance records
		instRecs := m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeANY,
		})

		// Return the service record with the instance records
		return append(servRec, instRecs...)
	default:
		return nil
	}
}

// serviceRecords is called when the query matches the instance name
func (m *MDNSService) instanceRecords(q dns.Question) []dns.RR {
	switch q.Qtype {
	case dns.TypeANY:
		// Get the SRV, which includes A and AAAA
		recs := m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeSRV,
		})

		// Add the TXT record
		recs = append(recs, m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeTXT,
		})...)
		return recs

	case dns.TypeA:
		var rr []dns.RR
		for _, ip := range m.IPs {
			if ip4 := ip.To4(); ip4 != nil {
				rr = append(rr, &dns.A{
					Hdr: dns.RR_Header{
						Name:   m.HostName,
						Rrtype: dns.TypeA,
						Class:  dns.ClassINET,
						Ttl:    defaultTTL,
					},
					A: ip4,
				})
			}
		}
		return rr

	case dns.TypeAAAA:
		var rr []dns.RR
		for _, ip := range m.IPs {
			if ip.To4() != nil {
				// TODO(reddaly): IPv4 addresses could be encoded in IPv6 format and
				// putinto AAAA records, but the current logic puts ipv4-encodable
				// addresses into the A records exclusively.  Perhaps this should be
				// configurable?
				continue
			}

			if ip16 := ip.To16(); ip16 != nil {
				rr = append(rr, &dns.AAAA{
					Hdr: dns.RR_Header{
						Name:   m.HostName,
						Rrtype: dns.TypeAAAA,
						Class:  dns.ClassINET,
						Ttl:    defaultTTL,
					},
					AAAA: ip16,
				})
			}
		}
		return rr

	case dns.TypeSRV:
		// Create the SRV Record
		srv := &dns.SRV{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeSRV,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Priority: 10,
			Weight:   1,
			Port:     uint16(m.Port),
			Target:   m.HostName,
		}
		recs := []dns.RR{srv}

		// Add the A record
		recs = append(recs, m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeA,
		})...)

		// Add the AAAA record
		recs = append(recs, m.instanceRecords(dns.Question{
			Name:  m.instanceAddr,
			Qtype: dns.TypeAAAA,
		})...)
		return recs

	case dns.TypeTXT:
		txt := &dns.TXT{
			Hdr: dns.RR_Header{
				Name:   q.Name,
				Rrtype: dns.TypeTXT,
				Class:  dns.ClassINET,
				Ttl:    defaultTTL,
			},
			Txt: m.TXT,
		}
		return []dns.RR{txt}
	}
	return nil
}
<|endoftext|>


<commit_before>package deck

import(
	"math/rand"
	"encoding/json"
)

type CardSet []interface{}

func (p *CardSet) remove(i int) interface{} {
	if i < 0 {
		i += len(*p)
	}
	if i > p.Length() {
		return nil
	}
	out := (*p)[i]
	if i == 0 {
		*p = (*p)[1:]
	} else if i == len(*p) - 1 {
		*p = (*p)[0:i]
	} else {
		*p = append((*p)[:i], (*p)[i+1:]...)
	}
	return out
}

func (p *CardSet) Reset() *CardSet {
	p = NewCardSet()
	return p
}

func (p *CardSet) Pop() interface{} {
	return p.remove(-1)
}

func (p *CardSet) Push(card interface{}) {
	*p = append(*p, card)
}

func (p *CardSet) Shift() interface{} {
	return p.remove(0)
}

func (p *CardSet) Unshift(card interface{}) {
	*p = append(CardSet{ card }, (*p)[:]...)
}

func (p *CardSet) Remove(i int) interface{} {
	return p.remove(i)
}

func (p *CardSet) IndexOf(card interface{}) int {
	for i, c := range *p {
		if c == card {
			return i
		}
	}
	return -1
}

func (p *CardSet) All() []interface{} {
	return (*p)[:]
}

func (p *CardSet) Length() int {
	return len(*p)
}

func (p *CardSet) Swap (i, j int) {
	left := (*p)[i]
	right := (*p)[j]
	(*p)[j] = left
	(*p)[i] = right
}

func NewCardSet() *CardSet {
	return &CardSet{}
}

//

type Deck struct {
	Source     *CardSet
	Trash      *CardSet
	Out        *CardSet
}

func (p *Deck) Reset(all bool) *Deck {
	for p.Trash.Length() > 0 {
		p.Source.Push(p.Trash.Pop())
	}
	if all {
		for p.Out.Length() > 0 {
			p.Source.Push(p.Out.Pop())
		}
	}
	return p
}

func (p *Deck) Shuffle() *Deck {
	l := p.Source.Length()
	for i := l - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		p.Source.Swap(i, j)
	}
	return p
}

func (p *Deck) Draw() interface{} {
	out := p.Source.Shift()
	p.Out.Push(out)
	return out
}

func (p *Deck) discard(i int) {
	p.Trash.Unshift(p.Out.Remove(i))
}

func (p *Deck) Discard(card interface{}) bool {
	if i := p.Out.IndexOf(card); i != -1 {
		p.discard(i)
		card = nil
		return true
	}
	return false
}

func (p *Deck) DiscardAll(cards *CardSet) bool {
	// validate them first
	for _, card := range *cards {
		i := p.Out.IndexOf(card)
		if i >= 0 {
			return false
		}
	}
	for _, card := range *cards {
		i := p.Out.IndexOf(card)
		if i >= 0 {
			p.discard(i)
		}
	}
	return true
}

func (p *Deck) IndexOf(card interface{}) (int, int, int) {
	return p.Source.IndexOf(card), p.Out.IndexOf(card), p.Trash.IndexOf(card)
}

func (p *Deck) String() string {
	out, _ := json.Marshal(p)
	return string(out)
}

func (p *Deck) AddCard(card interface{}) *Deck {
	p.Source.Push(card)
	return p
}

func NewDeck() *Deck {
	return &Deck{
		Source: NewCardSet(),
		Trash: NewCardSet(),
		Out: NewCardSet(),
	}
}
<commit_msg>Added ability to to look at a card in a CardSet and Remaining card count in a deck<commit_after>package deck

import(
	"math/rand"
	"encoding/json"
)

type CardSet []interface{}

func (p *CardSet) remove(i int) interface{} {
	if i < 0 {
		i += len(*p)
	}
	if i > p.Length() {
		return nil
	}
	out := (*p)[i]
	if i == 0 {
		*p = (*p)[1:]
	} else if i == len(*p) - 1 {
		*p = (*p)[0:i]
	} else {
		*p = append((*p)[:i], (*p)[i+1:]...)
	}
	return out
}

func (p *CardSet) Peek(i int) interface{} {
	return (*p)[i]
}

func (p *CardSet) Reset() *CardSet {
	p = NewCardSet()
	return p
}

func (p *CardSet) Pop() interface{} {
	return p.remove(-1)
}

func (p *CardSet) Push(card interface{}) {
	*p = append(*p, card)
}

func (p *CardSet) Shift() interface{} {
	return p.remove(0)
}

func (p *CardSet) Unshift(card interface{}) {
	*p = append(CardSet{ card }, (*p)[:]...)
}

func (p *CardSet) Remove(i int) interface{} {
	return p.remove(i)
}

func (p *CardSet) IndexOf(card interface{}) int {
	for i, c := range *p {
		if c == card {
			return i
		}
	}
	return -1
}

func (p *CardSet) All() []interface{} {
	return (*p)[:]
}

func (p *CardSet) Length() int {
	return len(*p)
}

func (p *CardSet) Swap (i, j int) {
	left := (*p)[i]
	right := (*p)[j]
	(*p)[j] = left
	(*p)[i] = right
}

func NewCardSet() *CardSet {
	return &CardSet{}
}

//

type Deck struct {
	Source     *CardSet
	Trash      *CardSet
	Out        *CardSet
}

func (p *Deck) Reset(all bool) *Deck {
	for p.Trash.Length() > 0 {
		p.Source.Push(p.Trash.Pop())
	}
	if all {
		for p.Out.Length() > 0 {
			p.Source.Push(p.Out.Pop())
		}
	}
	return p
}

func (p *Deck) Shuffle() *Deck {
	l := p.Source.Length()
	for i := l - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		p.Source.Swap(i, j)
	}
	return p
}

func (p *Deck) Draw() interface{} {
	out := p.Source.Shift()
	p.Out.Push(out)
	return out
}

func (p *Deck) discard(i int) {
	p.Trash.Unshift(p.Out.Remove(i))
}

func (p *Deck) Discard(card interface{}) bool {
	if i := p.Out.IndexOf(card); i != -1 {
		p.discard(i)
		card = nil
		return true
	}
	return false
}

func (p *Deck) DiscardAll(cards *CardSet) bool {
	// validate them first
	for _, card := range *cards {
		i := p.Out.IndexOf(card)
		if i >= 0 {
			return false
		}
	}
	for _, card := range *cards {
		i := p.Out.IndexOf(card)
		if i >= 0 {
			p.discard(i)
		}
	}
	return true
}

func (p *Deck) Remaining() int {
	return p.Source.Length()
}

func (p *Deck) IndexOf(card interface{}) (int, int, int) {
	return p.Source.IndexOf(card), p.Out.IndexOf(card), p.Trash.IndexOf(card)
}

func (p *Deck) String() string {
	out, _ := json.Marshal(p)
	return string(out)
}

func (p *Deck) AddCard(card interface{}) *Deck {
	p.Source.Push(card)
	return p
}

func NewDeck() *Deck {
	return &Deck{
		Source: NewCardSet(),
		Trash: NewCardSet(),
		Out: NewCardSet(),
	}
}
<|endoftext|>


<commit_before>package the_platinum_searcher

import (
	"regexp"
	"strconv"
	"strings"
)

const (
	ColorReset = "\x1b[0m\x1b[K"

	SeparatorColon  = ":"
	SeparatorHyphen = "-"
)

type decorator interface {
	path(path string) string
	lineNumber(lineNum int) string
	columnNumber(columnNum int) string
	match(line string, matched bool) string
}

func newDecorator(pattern pattern, option Option) decorator {
	if option.OutputOption.EnableColor {
		return newColor(pattern, option)
	} else {
		return plain{}
	}
}

type color struct {
	from   string
	to     string
	regexp *regexp.Regexp

	colorLineNumber string
	colorPath       string
	colorMatch      string
}

func newColor(pattern pattern, option Option) color {
	color := color{
		colorLineNumber: ansiEscape(option.OutputOption.ColorCodeLineNumber),
		colorPath:       ansiEscape(option.OutputOption.ColorCodePath),
		colorMatch:      ansiEscape(option.OutputOption.ColorCodeMatch),
	}
	if pattern.regexp == nil {
		p := string(pattern.pattern)
		color.from = p
		color.to = color.colorMatch + p + ColorReset
	} else {
		color.to = color.colorMatch + "${1}" + ColorReset
		color.regexp = pattern.regexp
	}
	return color
}

func ansiEscape(code string) string {
	return "\x1b[" + code + "m"
}

func (c color) path(path string) string {
	return c.colorPath + path + ColorReset
}

func (c color) lineNumber(lineNum int) string {
	return c.colorLineNumber + strconv.Itoa(lineNum) + ColorReset
}

func (c color) columnNumber(columnNum int) string {
	return strconv.Itoa(columnNum)
}

func (c color) match(line string, matched bool) string {
	if !matched {
		return line
	} else if c.regexp == nil {
		return strings.Replace(line, c.from, c.to, -1)
	} else {
		return c.regexp.ReplaceAllString(line, c.to)
	}
}

type plain struct {
}

func (p plain) path(path string) string {
	return path
}

func (p plain) lineNumber(lineNum int) string {
	return strconv.Itoa(lineNum)
}

func (p plain) columnNumber(columnNum int) string {
	return strconv.Itoa(columnNum)
}

func (p plain) match(line string, matched bool) string {
	return line
}
<commit_msg>Invalid ANSI codes are now removed.<commit_after>package the_platinum_searcher

import (
	"regexp"
	"strconv"
	"strings"
)

const (
	ColorReset = "\x1b[0m\x1b[K"

	SeparatorColon  = ":"
	SeparatorHyphen = "-"
)

type decorator interface {
	path(path string) string
	lineNumber(lineNum int) string
	columnNumber(columnNum int) string
	match(line string, matched bool) string
}

func newDecorator(pattern pattern, option Option) decorator {
	if option.OutputOption.EnableColor {
		return newColor(pattern, option)
	} else {
		return plain{}
	}
}

type color struct {
	from   string
	to     string
	regexp *regexp.Regexp

	colorLineNumber string
	colorPath       string
	colorMatch      string
}

func newColor(pattern pattern, option Option) color {
	color := color{
		colorLineNumber: ansiEscape(option.OutputOption.ColorCodeLineNumber),
		colorPath:       ansiEscape(option.OutputOption.ColorCodePath),
		colorMatch:      ansiEscape(option.OutputOption.ColorCodeMatch),
	}
	if pattern.regexp == nil {
		p := string(pattern.pattern)
		color.from = p
		color.to = color.colorMatch + p + ColorReset
	} else {
		color.to = color.colorMatch + "${1}" + ColorReset
		color.regexp = pattern.regexp
	}
	return color
}

func ansiEscape(code string) string {
	re := regexp.MustCompile("[^0-9;]")
	sanitized := re.ReplaceAllString(code, "")
	if sanitized == "" {
		sanitized = "0" // all attributes off
	}
	return "\x1b[" + sanitized + "m"
}

func (c color) path(path string) string {
	return c.colorPath + path + ColorReset
}

func (c color) lineNumber(lineNum int) string {
	return c.colorLineNumber + strconv.Itoa(lineNum) + ColorReset
}

func (c color) columnNumber(columnNum int) string {
	return strconv.Itoa(columnNum)
}

func (c color) match(line string, matched bool) string {
	if !matched {
		return line
	} else if c.regexp == nil {
		return strings.Replace(line, c.from, c.to, -1)
	} else {
		return c.regexp.ReplaceAllString(line, c.to)
	}
}

type plain struct {
}

func (p plain) path(path string) string {
	return path
}

func (p plain) lineNumber(lineNum int) string {
	return strconv.Itoa(lineNum)
}

func (p plain) columnNumber(columnNum int) string {
	return strconv.Itoa(columnNum)
}

func (p plain) match(line string, matched bool) string {
	return line
}
<|endoftext|>


<commit_before>package main

import (
	"regexp"
	"strings"
)

var SPACES = regexp.MustCompile(`\s+`)

type Delimiter struct {
	re    *regexp.Regexp
	count int
}

func NewDelimiter(expr string, useRegexp bool, count int) (d *Delimiter, err error) {
	d = &Delimiter{}
	d.count = count
	if d.count < 1 {
		d.count = -1
	}
	switch {
	case expr == "":
		d.re = nil
		if d.count != -1 {
			d.count += 1
		}
	case useRegexp:
		d.re, err = regexp.Compile(expr)
		if err != nil {
			return nil, err
		}
	default:
		expr = regexp.QuoteMeta(expr)
		d.re, err = regexp.Compile(expr)
		if err != nil {
			return nil, err
		}
	}
	return d, nil
}

func (d *Delimiter) Split(s string) []string {
	if d.re == nil {
		return SPACES.Split(s, d.count)
	}

	matches := d.re.FindAllStringIndex(s, d.count)
	if len(matches) == 0 {
		return []string{strings.TrimSpace(s)}
	}

	a := make([]string, 0, len(matches)*2+1)
	beg, end := 0, 0
	useCount := d.count > 0
	for _, match := range matches {
		end = match[0]

		a = append(a, s[beg:end])
		beg, end = match[0], match[1]
		if useCount && len(a) >= d.count {
			break
		}

		a = append(a, s[beg:end])
		beg = match[1]
		if useCount && len(a) >= d.count {
			break
		}
	}
	a = append(a, s[beg:])
	for i := 0; i < len(a); i++ {
		a[i] = strings.TrimSpace(a[i])
	}
	return a
}
<commit_msg>Rereplace ALL_CAPS to CamelCase<commit_after>package main

import (
	"regexp"
	"strings"
)

var Spaces = regexp.MustCompile(`\s+`)

type Delimiter struct {
	re    *regexp.Regexp
	count int
}

func NewDelimiter(expr string, useRegexp bool, count int) (d *Delimiter, err error) {
	d = &Delimiter{}
	d.count = count
	if d.count < 1 {
		d.count = -1
	}
	switch {
	case expr == "":
		d.re = nil
		if d.count != -1 {
			d.count += 1
		}
	case useRegexp:
		d.re, err = regexp.Compile(expr)
		if err != nil {
			return nil, err
		}
	default:
		expr = regexp.QuoteMeta(expr)
		d.re, err = regexp.Compile(expr)
		if err != nil {
			return nil, err
		}
	}
	return d, nil
}

func (d *Delimiter) Split(s string) []string {
	if d.re == nil {
		return Spaces.Split(s, d.count)
	}

	matches := d.re.FindAllStringIndex(s, d.count)
	if len(matches) == 0 {
		return []string{strings.TrimSpace(s)}
	}

	a := make([]string, 0, len(matches)*2+1)
	beg, end := 0, 0
	useCount := d.count > 0
	for _, match := range matches {
		end = match[0]

		a = append(a, s[beg:end])
		beg, end = match[0], match[1]
		if useCount && len(a) >= d.count {
			break
		}

		a = append(a, s[beg:end])
		beg = match[1]
		if useCount && len(a) >= d.count {
			break
		}
	}
	a = append(a, s[beg:])
	for i := 0; i < len(a); i++ {
		a[i] = strings.TrimSpace(a[i])
	}
	return a
}
<|endoftext|>


<commit_before>package aws

import (
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSOpsWorksStack_importBasic(t *testing.T) {
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-west-2")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	name := acctest.RandString(10)

	resourceName := "aws_opsworks_stack.tf-acc"

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAwsOpsworksStackConfigVpcCreate(name),
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<commit_msg>provider/aws: Rename the Import aws_opsworks_stack import test<commit_after>package aws

import (
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSOpsworksStackImportBasic(t *testing.T) {
	name := acctest.RandString(10)

	resourceName := "aws_opsworks_stack.tf-acc"

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAwsOpsworksStackConfigVpcCreate(name),
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<|endoftext|>


<commit_before>package sparta

// THIS FILE IS AUTOMATICALLY GENERATED
// DO NOT EDIT
// CREATED: 2019-08-10 23:29:51.408982 +0000 UTC

// SpartaGitHash is the commit hash of this Sparta library
const SpartaGitHash = "679a7486c73aa16609705771ba4260f3726330f2"
<commit_msg>"Autogenerated build info"<commit_after>package sparta

// THIS FILE IS AUTOMATICALLY GENERATED
// DO NOT EDIT
// CREATED: 2019-08-11 20:07:53.985073 +0000 UTC

// SpartaGitHash is the commit hash of this Sparta library
const SpartaGitHash = "8f4e53643ca5ab3123aa5ea337d6baee623a3de0"
<|endoftext|>


<commit_before>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package diff implements diff functions to compare objects.
package diff

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

// A Diff represents the changes between two structures.
type Diff map[string]interface{}

// HasChange report whether the Diff contains some changes.
func (d Diff) HasChange() bool {
	return len(d) > 0
}

// PrettyJSON serializes the Diff into JSON in a indented (hence human readable)
// way.
func (d Diff) PrettyJSON() []byte {
	bs, err := json.MarshalIndent(d, "", "   ")
	if err != nil {
		return []byte{}
	}
	return bs
}

// JSON serializes the Diff into JSON in a compact format (no indentation).
func (d Diff) JSON() []byte {
	bs, err := json.Marshal(d)
	if err != nil {
		return []byte{}
	}
	return bs
}

type ChangeType string

// Possible values for a ChangeType.
const (
	AddType ChangeType = "ADD" // addition
	DelType ChangeType = "DEL" // deletion
	ModType ChangeType = "MOD" // modification
)

type Change struct {
	OldVal interface{} `json:"old_value,omitempty"`
	NewVal interface{} `json:"new_value,omitempty"`
	Type   ChangeType  `json:"type,omitempty"`
}

func Compute(x, y interface{}, recursive bool) (Diff, error) {
	vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
	tx, ty := vx.Type(), vy.Type()

	if !tx.AssignableTo(ty) {
		return nil, errors.New("input objects do not share the same type")
	}

	// since x and y share the same type, there is no need to check them both
	if vx.Kind() != reflect.Struct {
		return nil, errors.New("input values are not struct")
	}

	xNumFields := vx.NumField()

	delta := make(Diff)

	for i := 0; i < xNumFields; i++ {
		fx := vx.Field(i)
		typ := tx.Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		fy := vy.FieldByName(typ.Name)

		if d := handleValue(fx, fy); d != nil {
			delta[typ.Name] = d
		}
	}

	return delta, nil
}

func handleValue(fx, fy reflect.Value) interface{} {
	switch fx.Kind() {

	case reflect.Struct:
		return handleStruct(fx, fy)
	case reflect.Array, reflect.Slice:
		return handleSlice(fx, fy)
	case reflect.Map:
		return nil

	case reflect.Ptr:
		if fx.IsNil() {
			if fy.IsNil() {
				return nil
			}
			return Change{OldVal: nil, NewVal: fy.Elem().Interface()}
		} else if fy.IsNil() {
			return Change{OldVal: fx.Elem().Interface(), NewVal: nil, Type: ModType}
		}
		return handleValue(fx.Elem(), fy.Elem())

	case reflect.Interface, reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128:
		// TODO(gilliek): support complex numbers
		return nil

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		ix, iy := fx.Int(), fy.Int()
		if ix != iy {
			return Change{OldVal: ix, NewVal: iy, Type: ModType}
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		uix, uiy := fx.Uint(), fy.Uint()
		if uix != uiy {
			return Change{OldVal: uix, NewVal: uiy, Type: ModType}
		}
	case reflect.Float32, reflect.Float64:
		flx, fly := fx.Float(), fy.Float()
		if flx-fly < 0.000001 {
			return Change{OldVal: flx, NewVal: fly, Type: ModType}
		}
	case reflect.String:
		sx, sy := fx.String(), fy.String()
		if sx != sy {
			return Change{OldVal: sx, NewVal: sy, Type: ModType}
		}
	}

	return nil
}

func handleStruct(fx, fy reflect.Value) interface{} {
	if isFullyNonExportedStruct(fx) {
		if !isEqual(fx, fy) {
			return Change{OldVal: fx.Interface(), NewVal: fy.Interface()}
		}
		return nil
	}

	delta := make(Diff)
	numFields := fx.NumField()
	for i := 0; i < numFields; i++ {
		newFx := fx.Field(i)
		typ := fx.Type().Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		newFy := fy.FieldByName(typ.Name)

		if d := handleValue(newFx, newFy); d != nil {
			delta[typ.Name] = d
		}
	}
	if len(delta) > 0 {
		return delta
	}
	return nil
}

func handleSlice(fx, fy reflect.Value) interface{} {
	xLen, yLen := fx.Len(), fy.Len()

	changes := make(Diff)
	if xLen == 0 {
		if yLen == 0 {
			return nil
		}
		for i := 0; i < yLen; i++ {
			changes[strconv.Itoa(i)] = Change{NewVal: fy.Index(i).Interface(), Type: AddType}
		}
	} else if yLen == 0 {
		for i := 0; i < xLen; i++ {
			changes[strconv.Itoa(i)] = Change{OldVal: fx.Index(i).Interface(), Type: DelType}
		}
	} else {
		var maxLen int
		if xLen > yLen {
			maxLen = yLen
			for i := yLen; i < xLen; i++ {
				changes[strconv.Itoa(i)] = Change{OldVal: fx.Index(i).Interface(), Type: DelType}
			}
		} else if xLen < yLen {
			maxLen = xLen
			for i := xLen; i < yLen; i++ {
				changes[strconv.Itoa(i)] = Change{NewVal: fy.Index(i).Interface(), Type: AddType}
			}
		}
		for i := 0; i < maxLen; i++ {
			if d := handleValue(fx.Index(i), fy.Index(i)); d != nil {
				changes[strconv.Itoa(i)] = d
			}
		}
	}
	if len(changes) > 0 {
		return changes
	}
	return nil
}

func isExported(fieldName string) bool {
	if fieldName == "" {
		return false
	}
	firstLetter := string(fieldName[0])
	return firstLetter != strings.ToLower(firstLetter)
}

func isFullyNonExportedStruct(s reflect.Value) bool {
	if s.Kind() != reflect.Struct {
		return false
	}
	numFields := s.NumField()
	for i := 0; i < numFields; i++ {
		typ := s.Type().Field(i)
		if isExported(typ.Name) {
			return false
		}
	}
	return true
}

func isEqual(x, y reflect.Value) bool {
	return fmt.Sprint(x.Interface()) == fmt.Sprint(y.Interface())
}
<commit_msg>diff: fix invalid value for slices<commit_after>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package diff implements diff functions to compare objects.
package diff

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

// A Diff represents the changes between two structures.
type Diff map[string]interface{}

// HasChange report whether the Diff contains some changes.
func (d Diff) HasChange() bool {
	return len(d) > 0
}

// PrettyJSON serializes the Diff into JSON in a indented (hence human readable)
// way.
func (d Diff) PrettyJSON() []byte {
	bs, err := json.MarshalIndent(d, "", "   ")
	if err != nil {
		return []byte{}
	}
	return bs
}

// JSON serializes the Diff into JSON in a compact format (no indentation).
func (d Diff) JSON() []byte {
	bs, err := json.Marshal(d)
	if err != nil {
		return []byte{}
	}
	return bs
}

type ChangeType string

// Possible values for a ChangeType.
const (
	AddType ChangeType = "ADD" // addition
	DelType ChangeType = "DEL" // deletion
	ModType ChangeType = "MOD" // modification
)

type Change struct {
	OldVal interface{} `json:"old_value,omitempty"`
	NewVal interface{} `json:"new_value,omitempty"`
	Type   ChangeType  `json:"type,omitempty"`
}

func Compute(x, y interface{}, recursive bool) (Diff, error) {
	vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
	tx, ty := vx.Type(), vy.Type()

	if !tx.AssignableTo(ty) {
		return nil, errors.New("input objects do not share the same type")
	}

	// since x and y share the same type, there is no need to check them both
	if vx.Kind() != reflect.Struct {
		return nil, errors.New("input values are not struct")
	}

	xNumFields := vx.NumField()

	delta := make(Diff)

	for i := 0; i < xNumFields; i++ {
		fx := vx.Field(i)
		typ := tx.Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		fy := vy.FieldByName(typ.Name)

		if d := handleValue(fx, fy); d != nil {
			delta[typ.Name] = d
		}
	}

	return delta, nil
}

func handleValue(fx, fy reflect.Value) interface{} {
	switch fx.Kind() {

	case reflect.Struct:
		return handleStruct(fx, fy)
	case reflect.Array, reflect.Slice:
		return handleSlice(fx, fy)
	case reflect.Map:
		return nil

	case reflect.Ptr:
		if fx.IsNil() {
			if fy.IsNil() {
				return nil
			}
			return Change{OldVal: nil, NewVal: fy.Elem().Interface()}
		} else if fy.IsNil() {
			return Change{OldVal: fx.Elem().Interface(), NewVal: nil, Type: ModType}
		}
		return handleValue(fx.Elem(), fy.Elem())

	case reflect.Interface, reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128:
		// TODO(gilliek): support complex numbers
		return nil

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		ix, iy := fx.Int(), fy.Int()
		if ix != iy {
			return Change{OldVal: ix, NewVal: iy, Type: ModType}
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		uix, uiy := fx.Uint(), fy.Uint()
		if uix != uiy {
			return Change{OldVal: uix, NewVal: uiy, Type: ModType}
		}
	case reflect.Float32, reflect.Float64:
		flx, fly := fx.Float(), fy.Float()
		if flx-fly < 0.000001 {
			return Change{OldVal: flx, NewVal: fly, Type: ModType}
		}
	case reflect.String:
		sx, sy := fx.String(), fy.String()
		if sx != sy {
			return Change{OldVal: sx, NewVal: sy, Type: ModType}
		}
	}

	return nil
}

func handleStruct(fx, fy reflect.Value) interface{} {
	if isFullyNonExportedStruct(fx) {
		if !isEqual(fx, fy) {
			return Change{OldVal: fx.Interface(), NewVal: fy.Interface()}
		}
		return nil
	}

	delta := make(Diff)
	numFields := fx.NumField()
	for i := 0; i < numFields; i++ {
		newFx := fx.Field(i)
		typ := fx.Type().Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		newFy := fy.FieldByName(typ.Name)

		if d := handleValue(newFx, newFy); d != nil {
			delta[typ.Name] = d
		}
	}
	if len(delta) > 0 {
		return delta
	}
	return nil
}

func handleSlice(fx, fy reflect.Value) interface{} {
	xLen, yLen := fx.Len(), fy.Len()

	changes := make(map[string]Change)
	if xLen == 0 {
		if yLen == 0 {
			return nil
		}
		for i := 0; i < yLen; i++ {
			changes[strconv.Itoa(i)] = Change{NewVal: fy.Index(i).Interface(), Type: AddType}
		}
	} else if yLen == 0 {
		for i := 0; i < xLen; i++ {
			changes[strconv.Itoa(i)] = Change{OldVal: fx.Index(i).Interface(), Type: DelType}
		}
	} else {
		var maxLen int
		if xLen > yLen {
			maxLen = yLen
			for i := yLen; i < xLen; i++ {
				changes[strconv.Itoa(i)] = Change{OldVal: fx.Index(i).Interface(), Type: DelType}
			}
		} else if xLen < yLen {
			maxLen = xLen
			for i := xLen; i < yLen; i++ {
				changes[strconv.Itoa(i)] = Change{NewVal: fy.Index(i).Interface(), Type: AddType}
			}
		}
		for i := 0; i < maxLen; i++ {
			if d := handleValue(fx.Index(i), fy.Index(i)); d != nil {
				changes[strconv.Itoa(i)] = Change{NewVal: d, Type: ModType}
			}
		}
	}
	if len(changes) > 0 {
		return changes
	}
	return nil
}

func isExported(fieldName string) bool {
	if fieldName == "" {
		return false
	}
	firstLetter := string(fieldName[0])
	return firstLetter != strings.ToLower(firstLetter)
}

func isFullyNonExportedStruct(s reflect.Value) bool {
	if s.Kind() != reflect.Struct {
		return false
	}
	numFields := s.NumField()
	for i := 0; i < numFields; i++ {
		typ := s.Type().Field(i)
		if isExported(typ.Name) {
			return false
		}
	}
	return true
}

func isEqual(x, y reflect.Value) bool {
	return fmt.Sprint(x.Interface()) == fmt.Sprint(y.Interface())
}
<|endoftext|>


<commit_before>// Package bulk has functions to load and save image metadata to a CSV file as
// needed by the 'Bulk Add CSV' Shimmie2 extension. The CSV file is assumed to
// have the following format:
//
//  "/path/to/image.jpg","spaced tags","source","rating s/q/e","/path/thumbnail.jpg"
//
// The last record (thumbnail) is left empty as thumbnails can easily be
// generated by the server.
//
// The package assumes that all images and the CSV file are under a certain
// directory path that is used as input in many package functions.
package bulk

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Image holds the metadata of each image from the CSV file.
type Image struct {
	ID     int
	Name   string
	Tags   []string
	Source string
	Rating string
}

var supportedExt = []string{"gif", "jpeg", "jpg", "png", "swf"}

func isSupportedType(name string) bool {
	fname := strings.ToLower(name)
	for _, ext := range supportedExt {
		// The only possible returned error is ErrBadPattern, when pattern is
		// malformed. Patterns like *.jpg are never malformed so we ignore the
		// error.
		matches, _ := filepath.Match("*."+ext, fname)
		if matches {
			return true
		}
	}
	return false
}

// LoadImages expects a slice of directory entries (os.FileInfo) which is the
// result of a read directory like ioutil.ReadDir. It loops through the slice,
// ignoring any directory and keeps only the files with one of the following
// extensions: "gif", "jpeg", "jpg", "png", "swf"
//
// It returns a slice of images without metadata, using the filename as Name
// and the order the files were found as an increasing ID starting from 0.
//
// In case of a CSV file, the image metadata should be read using LoadCSV and
// then combined with the images (discovered by LoadImages) using Combine.
func LoadImages(files []os.FileInfo) []Image {
	images := []Image{}

	id := 0
	for _, f := range files {
		if !f.IsDir() {
			if isSupportedType(f.Name()) {
				img := Image{ID: id, Name: f.Name()}
				images = append(images, img)
				id++
			}
		}
	}
	return images
}

// LoadCSV loads the image metadata from a CSV file that is open for reading.
// The metadata are returned as slice of images and should be combined with the
// slice of images discovered by LoadImages by calling Combine.
func LoadCSV(file io.Reader) ([]Image, error) {
	images := []Image{}

	r := csv.NewReader(file)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if len(record) != 5 {
			return nil, fmt.Errorf("invalid csv file format")
		}
		// Image filepath (first column) should exist otherwise we cannot match
		// the metadata with the images found under the directory.
		if record[0] != "" {
			img := Image{
				Name:   filepath.Base(record[0]),
				Tags:   strings.Split(record[1], " "),
				Source: record[2],
				Rating: record[3],
			}
			images = append(images, img)
		}
	}
	return images, nil
}

// Combine takes the metadata of imagesWithInfo and copies them to images
// returning the combined result.
func Combine(images, imagesWithInfo []Image) []Image {
	for _, info := range imagesWithInfo {
		if info.Name == "" {
			continue
		}
		img := findByName(images, info.Name)
		if img != nil {
			img.Source = info.Source
			img.Rating = info.Rating
			img.Tags = info.Tags
		}
	}
	return images
}

type byName []Image

func (img byName) Len() int           { return len(img) }
func (img byName) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byName) Less(i, j int) bool { return img[i].Name < img[j].Name }

func findByName(image []Image, name string) *Image {
	sort.Sort(byName(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].Name >= name })
	if i < len(image) && image[i].Name == name {
		return &image[i]
	}
	return nil
}

type byID []Image

func (img byID) Len() int           { return len(img) }
func (img byID) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byID) Less(i, j int) bool { return img[i].ID < img[j].ID }

// FindByID takes a slice of images, sorts them by ID and then finds then one
// with ID id.
func FindByID(image []Image, id int) *Image {
	sort.Sort(byID(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].ID >= id })
	if i < len(image) && image[i].ID == id {
		return &image[i]
	}
	return nil
}

// CurrentPrefix reads from an open CSV file, reads the first line and uses the
// base directory of the provided dir path to find the path prefix used in the
// CSV file.
//
// As an example if the provided dir path is '/localpath/pics' and the first line
// has '/serverpath/pics/pic1' then the returned current prefix will be
// '/serverpath'.
func CurrentPrefix(workingDir string, file io.Reader) (string, error) {
	r := csv.NewReader(file)
	record, err := r.Read()
	if err == io.EOF {
		return "", nil
	}
	if err != nil {
		return "", err
	}

	serverDir := record[0]
	folder := filepath.Base(workingDir)
	sep := fmt.Sprintf("%c", filepath.Separator)
	if !strings.Contains(serverDir, folder) {
		return sep, nil
	}
	for {
		if filepath.Base(serverDir) == folder {
			break
		} else {
			serverDir, _ = filepath.Split(serverDir)
			serverDir = filepath.Dir(serverDir)
		}
	}
	return filepath.Dir(serverDir), nil
}

// Save will write the image metadata to an open for writing file. It will
// keep the base of the dir path and replace the prefix with the provided one.
func Save(file io.Writer, images []Image, dir, prefix string, useLinuxSep bool) error {
	w := csv.NewWriter(file)
	w.WriteAll(toRecords(images, dir, prefix, useLinuxSep))

	if err := w.Error(); err != nil {
		return fmt.Errorf("error writing csv: %v", err)
	}
	return nil
}

func toRecords(images []Image, dir, prefix string, useLinuxSep bool) [][]string {
	var records [][]string
	for _, img := range images {
		record := toRecord(img, dir, prefix, useLinuxSep)
		records = append(records, record)
	}
	return records
}

func toRecord(img Image, dir, prefix string, useLinuxSep bool) []string {
	var record []string
	p := filepath.Join(prefix, filepath.Base(dir), img.Name)
	if useLinuxSep {
		p = strings.Replace(p, "\\", "/", -1)
	}
	record = append(record, p)
	record = append(record, strings.Join(img.Tags, " "))
	record = append(record, img.Source)
	record = append(record, img.Rating)
	record = append(record, "")
	return record
}
<commit_msg>Rename folder to picFolder in CurrentPrefix<commit_after>// Package bulk has functions to load and save image metadata to a CSV file as
// needed by the 'Bulk Add CSV' Shimmie2 extension. The CSV file is assumed to
// have the following format:
//
//  "/path/to/image.jpg","spaced tags","source","rating s/q/e","/path/thumbnail.jpg"
//
// The last record (thumbnail) is left empty as thumbnails can easily be
// generated by the server.
//
// The package assumes that all images and the CSV file are under a certain
// directory path that is used as input in many package functions.
package bulk

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Image holds the metadata of each image from the CSV file.
type Image struct {
	ID     int
	Name   string
	Tags   []string
	Source string
	Rating string
}

var supportedExt = []string{"gif", "jpeg", "jpg", "png", "swf"}

func isSupportedType(name string) bool {
	fname := strings.ToLower(name)
	for _, ext := range supportedExt {
		// The only possible returned error is ErrBadPattern, when pattern is
		// malformed. Patterns like *.jpg are never malformed so we ignore the
		// error.
		matches, _ := filepath.Match("*."+ext, fname)
		if matches {
			return true
		}
	}
	return false
}

// LoadImages expects a slice of directory entries (os.FileInfo) which is the
// result of a read directory like ioutil.ReadDir. It loops through the slice,
// ignoring any directory and keeps only the files with one of the following
// extensions: "gif", "jpeg", "jpg", "png", "swf"
//
// It returns a slice of images without metadata, using the filename as Name
// and the order the files were found as an increasing ID starting from 0.
//
// In case of a CSV file, the image metadata should be read using LoadCSV and
// then combined with the images (discovered by LoadImages) using Combine.
func LoadImages(files []os.FileInfo) []Image {
	images := []Image{}

	id := 0
	for _, f := range files {
		if !f.IsDir() {
			if isSupportedType(f.Name()) {
				img := Image{ID: id, Name: f.Name()}
				images = append(images, img)
				id++
			}
		}
	}
	return images
}

// LoadCSV loads the image metadata from a CSV file that is open for reading.
// The metadata are returned as slice of images and should be combined with the
// slice of images discovered by LoadImages by calling Combine.
func LoadCSV(file io.Reader) ([]Image, error) {
	images := []Image{}

	r := csv.NewReader(file)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if len(record) != 5 {
			return nil, fmt.Errorf("invalid csv file format")
		}
		// Image filepath (first column) should exist otherwise we cannot match
		// the metadata with the images found under the directory.
		if record[0] != "" {
			img := Image{
				Name:   filepath.Base(record[0]),
				Tags:   strings.Split(record[1], " "),
				Source: record[2],
				Rating: record[3],
			}
			images = append(images, img)
		}
	}
	return images, nil
}

// Combine takes the metadata of imagesWithInfo and copies them to images
// returning the combined result.
func Combine(images, imagesWithInfo []Image) []Image {
	for _, info := range imagesWithInfo {
		if info.Name == "" {
			continue
		}
		img := findByName(images, info.Name)
		if img != nil {
			img.Source = info.Source
			img.Rating = info.Rating
			img.Tags = info.Tags
		}
	}
	return images
}

type byName []Image

func (img byName) Len() int           { return len(img) }
func (img byName) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byName) Less(i, j int) bool { return img[i].Name < img[j].Name }

func findByName(image []Image, name string) *Image {
	sort.Sort(byName(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].Name >= name })
	if i < len(image) && image[i].Name == name {
		return &image[i]
	}
	return nil
}

type byID []Image

func (img byID) Len() int           { return len(img) }
func (img byID) Swap(i, j int)      { img[i], img[j] = img[j], img[i] }
func (img byID) Less(i, j int) bool { return img[i].ID < img[j].ID }

// FindByID takes a slice of images, sorts them by ID and then finds then one
// with ID id.
func FindByID(image []Image, id int) *Image {
	sort.Sort(byID(image))
	i := sort.Search(len(image), func(i int) bool { return image[i].ID >= id })
	if i < len(image) && image[i].ID == id {
		return &image[i]
	}
	return nil
}

// CurrentPrefix reads from an open CSV file, reads the first line and uses the
// base directory of the provided dir path to find the path prefix used in the
// CSV file.
//
// As an example if the provided dir path is '/localpath/pics' and the first line
// has '/serverpath/pics/pic1' then the returned current prefix will be
// '/serverpath'.
func CurrentPrefix(workingDir string, file io.Reader) (string, error) {
	r := csv.NewReader(file)
	record, err := r.Read()
	if err == io.EOF {
		return "", nil
	}
	if err != nil {
		return "", err
	}

	serverDir := record[0]
	picFolder := filepath.Base(workingDir)
	sep := fmt.Sprintf("%c", filepath.Separator)
	if !strings.Contains(serverDir, picFolder) {
		return sep, nil
	}
	for {
		if filepath.Base(serverDir) == picFolder {
			break
		} else {
			serverDir, _ = filepath.Split(serverDir)
			serverDir = filepath.Dir(serverDir)
		}
	}
	return filepath.Dir(serverDir), nil
}

// Save will write the image metadata to an open for writing file. It will
// keep the base of the dir path and replace the prefix with the provided one.
func Save(file io.Writer, images []Image, dir, prefix string, useLinuxSep bool) error {
	w := csv.NewWriter(file)
	w.WriteAll(toRecords(images, dir, prefix, useLinuxSep))

	if err := w.Error(); err != nil {
		return fmt.Errorf("error writing csv: %v", err)
	}
	return nil
}

func toRecords(images []Image, dir, prefix string, useLinuxSep bool) [][]string {
	var records [][]string
	for _, img := range images {
		record := toRecord(img, dir, prefix, useLinuxSep)
		records = append(records, record)
	}
	return records
}

func toRecord(img Image, dir, prefix string, useLinuxSep bool) []string {
	var record []string
	p := filepath.Join(prefix, filepath.Base(dir), img.Name)
	if useLinuxSep {
		p = strings.Replace(p, "\\", "/", -1)
	}
	record = append(record, p)
	record = append(record, strings.Join(img.Tags, " "))
	record = append(record, img.Source)
	record = append(record, img.Rating)
	record = append(record, "")
	return record
}
<|endoftext|>


<commit_before>package registry

import (
	"github.com/hashicorp/consul/api"

	"fmt"
	"net"
	"net/url"
	"sort"
	"strconv"
	"strings"
)

type discovery struct {
	agent      *api.Agent
	health     *api.Health
	catalog    *api.Catalog
	datacenter string
}

func (d *discovery) Register(options ServiceOptions) error {
	var (
		host = options.Address
		port int
	)
	if strings.HasPrefix(host, "http") {
		url, err := url.Parse(host)
		if err != nil {
			return err
		}
		host = url.Host
	}
	if strings.Contains(host, ":") {
		h, p, err := net.SplitHostPort(host)
		if err != nil {
			return err
		}
		v, err := strconv.ParseUint(p, 10, 32)
		if err != nil {
			return err
		}
		host = h
		port = int(v)
	}
	return d.agent.ServiceRegister(&api.AgentServiceRegistration{
		ID:                options.ID,
		Name:              options.Name,
		Address:           host,
		Port:              port,
		Tags:              append(options.Tags, "DC="+d.datacenter),
		EnableTagOverride: true,
		Check: &api.AgentServiceCheck{
			Interval: options.Check.Interval,
			Timeout:  options.Check.Timeout,
			DeregisterCriticalServiceAfter: "10m",
			HTTP: options.Check.HTTP,
			TCP:  options.Check.TCP,
		},
	})
}

func (d *discovery) Deregister(ident string) error {
	return d.agent.ServiceDeregister(ident)
}

type sortServiceByID []Service

func (a sortServiceByID) Len() int           { return len(a) }
func (a sortServiceByID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a sortServiceByID) Less(i, j int) bool { return a[i].ID < a[j].ID }

// Lookup services by filter
//
// If needed to lookup all services around all DCs,
// set DC filter to "all". It takes all DCs from discovery
// and polls services every of them
func (d *discovery) Lookup(filter *Filter) ([]Service, error) {
	if filter.Datacenter != "all" {
		return d.lookup(filter)
	}

	dcl, err := d.catalog.Datacenters()
	if err != nil {
		return nil, fmt.Errorf("datacenters list: %s", err)
	}

	var services []Service
	for _, dc := range dcl {
		filter.Datacenter = dc
		s, err := d.lookup(filter)
		if err != nil {
			return nil, fmt.Errorf("datacenter %s lookup: %s", dc, err)
		}
		services = append(services, s...)
	}

	return services, nil
}

func (d *discovery) lookup(filter *Filter) ([]Service, error) {
	var (
		result []Service
		q      = &api.QueryOptions{Datacenter: filter.Datacenter}
	)
	list, _, err := d.catalog.Services(q)
	if err != nil {
		return nil, err
	}
	var names []string
	for name := range list {
		names = append(names, name)
		items, _, err := d.catalog.Service(name, "", q)
		if err != nil {
			return nil, err
		}
		for _, item := range items {
			var (
				srv = Service{
					ID:         item.ServiceID,
					Name:       item.ServiceName,
					Datacenter: dc(item.ServiceTags),
					Address:    item.ServiceAddress,
					Port:       item.ServicePort,
					Tags:       item.ServiceTags,
					Status:     SERVICE_STATUS_UNDEFINED,
				}
			)
			result = append(result, srv)
		}
	}
	sort.Sort(sortServiceByID(result))
	status := func(status string) int8 {
		switch status {
		case "passing":
			return SERVICE_STATUS_PASSING
		case "warning":
			return SERVICE_STATUS_WARNING
		case "critical":
			return SERVICE_STATUS_CRITICAL
		}
		return SERVICE_STATUS_UNDEFINED
	}
	for _, name := range names {
		healthChecks, _, err := d.health.Checks(name, nil)
		if err != nil {
			return nil, err
		}
		for _, check := range healthChecks {
			if i := sort.Search(len(result), func(i int) bool { return result[i].ID >= check.ServiceID }); i < len(result) && result[i].ID == check.ServiceID {
				result[i].Status = status(check.Status)
			}
		}
	}
	services := make([]Service, 0, len(result))
	for _, srv := range result {
		if srv.test(filter) {
			services = append(services, srv)
		}
	}
	return services, nil
}

func dc(tags []string) string {
	for _, tag := range tags {
		if strings.HasPrefix(tag, "DC=") {
			return strings.TrimPrefix(tag, "DC=")
		}
	}
	return ""
}
<commit_msg>Correct check status for multiple DCs mode<commit_after>package registry

import (
	"github.com/hashicorp/consul/api"

	"fmt"
	"net"
	"net/url"
	"sort"
	"strconv"
	"strings"
)

type discovery struct {
	agent      *api.Agent
	health     *api.Health
	catalog    *api.Catalog
	datacenter string
}

func (d *discovery) Register(options ServiceOptions) error {
	var (
		host = options.Address
		port int
	)
	if strings.HasPrefix(host, "http") {
		url, err := url.Parse(host)
		if err != nil {
			return err
		}
		host = url.Host
	}
	if strings.Contains(host, ":") {
		h, p, err := net.SplitHostPort(host)
		if err != nil {
			return err
		}
		v, err := strconv.ParseUint(p, 10, 32)
		if err != nil {
			return err
		}
		host = h
		port = int(v)
	}
	return d.agent.ServiceRegister(&api.AgentServiceRegistration{
		ID:                options.ID,
		Name:              options.Name,
		Address:           host,
		Port:              port,
		Tags:              append(options.Tags, "DC="+d.datacenter),
		EnableTagOverride: true,
		Check: &api.AgentServiceCheck{
			Interval: options.Check.Interval,
			Timeout:  options.Check.Timeout,
			DeregisterCriticalServiceAfter: "10m",
			HTTP: options.Check.HTTP,
			TCP:  options.Check.TCP,
		},
	})
}

func (d *discovery) Deregister(ident string) error {
	return d.agent.ServiceDeregister(ident)
}

type sortServiceByID []Service

func (a sortServiceByID) Len() int           { return len(a) }
func (a sortServiceByID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a sortServiceByID) Less(i, j int) bool { return a[i].ID < a[j].ID }

// Lookup services by filter
//
// If needed to lookup all services around all DCs,
// set DC filter to "all". It takes all DCs from discovery
// and polls services every of them
func (d *discovery) Lookup(filter *Filter) ([]Service, error) {
	if filter.Datacenter != "all" {
		return d.lookup(filter)
	}

	dcl, err := d.catalog.Datacenters()
	if err != nil {
		return nil, fmt.Errorf("datacenters list: %s", err)
	}

	var services []Service
	for _, dc := range dcl {
		filter.Datacenter = dc
		s, err := d.lookup(filter)
		if err != nil {
			return nil, fmt.Errorf("datacenter %s lookup: %s", dc, err)
		}
		services = append(services, s...)
	}

	return services, nil
}

func (d *discovery) lookup(filter *Filter) ([]Service, error) {
	var (
		result []Service
		q      = &api.QueryOptions{Datacenter: filter.Datacenter}
	)
	list, _, err := d.catalog.Services(q)
	if err != nil {
		return nil, err
	}
	var names []string
	for name := range list {
		names = append(names, name)
		items, _, err := d.catalog.Service(name, "", q)
		if err != nil {
			return nil, err
		}
		for _, item := range items {
			var (
				srv = Service{
					ID:         item.ServiceID,
					Name:       item.ServiceName,
					Datacenter: dc(item.ServiceTags),
					Address:    item.ServiceAddress,
					Port:       item.ServicePort,
					Tags:       item.ServiceTags,
					Status:     SERVICE_STATUS_UNDEFINED,
				}
			)
			result = append(result, srv)
		}
	}
	sort.Sort(sortServiceByID(result))
	status := func(status string) int8 {
		switch status {
		case "passing":
			return SERVICE_STATUS_PASSING
		case "warning":
			return SERVICE_STATUS_WARNING
		case "critical":
			return SERVICE_STATUS_CRITICAL
		}
		return SERVICE_STATUS_UNDEFINED
	}
	for _, name := range names {
		healthChecks, _, err := d.health.Checks(name, q)
		if err != nil {
			return nil, err
		}
		for _, check := range healthChecks {
			if i := sort.Search(len(result), func(i int) bool { return result[i].ID >= check.ServiceID }); i < len(result) && result[i].ID == check.ServiceID {
				result[i].Status = status(check.Status)
			}
		}
	}
	services := make([]Service, 0, len(result))
	for _, srv := range result {
		if srv.test(filter) {
			services = append(services, srv)
		}
	}
	return services, nil
}

func dc(tags []string) string {
	for _, tag := range tags {
		if strings.HasPrefix(tag, "DC=") {
			return strings.TrimPrefix(tag, "DC=")
		}
	}
	return ""
}
<|endoftext|>