<commit_before>package factory

import (
	"time"

	"github.com/golang/glog"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"

	osclient "github.com/openshift/origin/pkg/client"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	controller "github.com/openshift/origin/pkg/deploy/controller"
	imageapi "github.com/openshift/origin/pkg/image/api"
)

// DeploymentConfigControllerFactory can create a DeploymentConfigController which obtains
// DeploymentConfigs from a queue populated from a watch of all DeploymentConfigs.
type DeploymentConfigControllerFactory struct {
	Client     *osclient.Client
	KubeClient kclient.Interface
	Codec      runtime.Codec
	Stop       <-chan struct{}
}

func (factory *DeploymentConfigControllerFactory) Create() *controller.DeploymentConfigController {
	queue := cache.NewFIFO()
	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, queue).Run()

	return &controller.DeploymentConfigController{
		DeploymentInterface: &ClientDeploymentInterace{factory.KubeClient},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			config := queue.Pop().(*deployapi.DeploymentConfig)
			panicIfStopped(factory.Stop, "deployment config controller stopped")
			return config
		},
		Codec: factory.Codec,
		Stop:  factory.Stop,
	}
}

// DeploymentControllerFactory can create a DeploymentController which obtains Deployments
// from a queue populated from a watch of Deployments.
// Pods are obtained from a queue populated from a watch of all pods.
type DeploymentControllerFactory struct {
	// Client satisfies DeploymentInterface.
	Client *osclient.Client
	// KubeClient satisfies PodInterface.
	KubeClient *kclient.Client
	// Environment is a set of environment which should be injected into all deployment pod containers.
	Environment []kapi.EnvVar
	// UseLocalImages configures the ImagePullPolicy for containers deployment pods.
	UseLocalImages bool
	// RecreateStrategyImage specifies which Docker image which should implement the Recreate strategy.
	RecreateStrategyImage string
	// Codec is used to decode DeploymentConfigs.
	Codec runtime.Codec
	// Stop may be set to allow controllers created by this factory to be terminated.
	Stop <-chan struct{}

	// deploymentStore is maintained on the factory to support narrowing of the pod polling scope.
	deploymentStore cache.Store
}

func (factory *DeploymentControllerFactory) Create() *controller.DeploymentController {
	deploymentQueue := cache.NewFIFO()
	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, deploymentQueue).Run()

	factory.deploymentStore = cache.NewStore()
	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, factory.deploymentStore).Run()

	// Kubernetes does not currently synchronize Pod status in storage with a Pod's container
	// states. Because of this, we can't receive events related to container (and thus Pod)
	// state changes, such as Running -> Terminated. As a workaround, populate the FIFO with
	// a polling implementation which relies on client calls to list Pods - the Get/List
	// REST implementations will populate the synchronized container/pod status on-demand.
	//
	// TODO: Find a way to get watch events for Pod/container status updates. The polling
	// strategy is horribly inefficient and should be addressed upstream somehow.
	podQueue := cache.NewFIFO()
	cache.NewPoller(factory.pollPods, 10*time.Second, podQueue).Run()

	return &controller.DeploymentController{
		ContainerCreator:    factory,
		DeploymentInterface: &ClientDeploymentInterace{factory.KubeClient},
		PodInterface:        &DeploymentControllerPodInterface{factory.KubeClient},
		Environment:         factory.Environment,
		NextDeployment: func() *kapi.ReplicationController {
			deployment := deploymentQueue.Pop().(*kapi.ReplicationController)
			panicIfStopped(factory.Stop, "deployment controller stopped")
			return deployment
		},
		NextPod: func() *kapi.Pod {
			pod := podQueue.Pop().(*kapi.Pod)
			panicIfStopped(factory.Stop, "deployment controller stopped")
			return pod
		},
		DeploymentStore: factory.deploymentStore,
		UseLocalImages:  factory.UseLocalImages,
		Codec:           factory.Codec,
		Stop:            factory.Stop,
	}
}

// CreateContainer lets DeploymentControllerFactory satisfy the DeploymentContainerCreator interface
// and makes a container using the configuration of the factory.
func (factory *DeploymentControllerFactory) CreateContainer(strategy *deployapi.DeploymentStrategy) *kapi.Container {
	// Every strategy type should be handled here.
	switch strategy.Type {
	case deployapi.DeploymentStrategyTypeRecreate:
		// Use the factory-configured image.
		return &kapi.Container{
			Image: factory.RecreateStrategyImage,
		}
	case deployapi.DeploymentStrategyTypeCustom:
		// Use user-defined values from the strategy input.
		return &kapi.Container{
			Image: strategy.CustomParams.Image,
			Env:   strategy.CustomParams.Environment,
		}
	default:
		// TODO: This shouldn't be reachable. Improve error handling.
		glog.Errorf("Unsupported deployment strategy type %s", strategy.Type)
		return nil
	}
}

// pollPods lists all pods associated with pending or running deployments and returns
// a cache.Enumerator suitable for use with a cache.Poller.
func (factory *DeploymentControllerFactory) pollPods() (cache.Enumerator, error) {
	list := &kapi.PodList{}

	for _, obj := range factory.deploymentStore.List() {
		deployment := obj.(*kapi.ReplicationController)

		switch deployapi.DeploymentStatus(deployment.Annotations[deployapi.DeploymentStatusAnnotation]) {
		case deployapi.DeploymentStatusPending, deployapi.DeploymentStatusRunning:
			// Validate the correlating pod annotation
			podID, hasPodID := deployment.Annotations[deployapi.DeploymentPodAnnotation]
			if !hasPodID {
				glog.V(2).Infof("Unexpected state: Deployment %s has no pod annotation; skipping pod polling", deployment.Name)
				continue
			}

			pod, err := factory.KubeClient.Pods(deployment.Namespace).Get(podID)
			if err != nil {
				glog.V(2).Infof("Couldn't find pod %s for deployment %s: %#v", podID, deployment.Name, err)
				continue
			}

			list.Items = append(list.Items, *pod)
		}
	}

	return &podEnumerator{list}, nil
}

type DeploymentControllerPodInterface struct {
	KubeClient kclient.Interface
}

func (i DeploymentControllerPodInterface) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) {
	return i.KubeClient.Pods(namespace).Create(pod)
}

func (i DeploymentControllerPodInterface) DeletePod(namespace, id string) error {
	return i.KubeClient.Pods(namespace).Delete(id)
}

// podEnumerator allows a cache.Poller to enumerate items in an api.PodList
type podEnumerator struct {
	*kapi.PodList
}

// Len returns the number of items in the pod list.
func (pe *podEnumerator) Len() int {
	if pe.PodList == nil {
		return 0
	}
	return len(pe.Items)
}

// Get returns the item (and ID) with the particular index.
func (pe *podEnumerator) Get(index int) (string, interface{}) {
	return pe.Items[index].Name, &pe.Items[index]
}

// DeploymentConfigChangeControllerFactory can create a DeploymentConfigChangeController which obtains DeploymentConfigs
// from a queue populated from a watch of all DeploymentConfigs.
type DeploymentConfigChangeControllerFactory struct {
	Client     osclient.Interface
	KubeClient kclient.Interface
	Codec      runtime.Codec
	// Stop may be set to allow controllers created by this factory to be terminated.
	Stop <-chan struct{}
}

func (factory *DeploymentConfigChangeControllerFactory) Create() *controller.DeploymentConfigChangeController {
	queue := cache.NewFIFO()
	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, queue).Run()

	store := cache.NewStore()
	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, store).Run()

	return &controller.DeploymentConfigChangeController{
		ChangeStrategy: &ClientDeploymentConfigInterface{factory.Client},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			config := queue.Pop().(*deployapi.DeploymentConfig)
			panicIfStopped(factory.Stop, "deployment config change controller stopped")
			return config
		},
		DeploymentStore: store,
		Codec:           factory.Codec,
		Stop:            factory.Stop,
	}
}

// ImageChangeControllerFactory can create an ImageChangeController which obtains ImageRepositories
// from a queue populated from a watch of all ImageRepositories.
type ImageChangeControllerFactory struct {
	Client *osclient.Client
	// Stop may be set to allow controllers created by this factory to be terminated.
	Stop <-chan struct{}
}

func (factory *ImageChangeControllerFactory) Create() *controller.ImageChangeController {
	queue := cache.NewFIFO()
	cache.NewReflector(&imageRepositoryLW{factory.Client}, &imageapi.ImageRepository{}, queue).Run()

	store := cache.NewStore()
	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, store).Run()

	return &controller.ImageChangeController{
		DeploymentConfigInterface: &ClientDeploymentConfigInterface{factory.Client},
		DeploymentConfigStore:     store,
		NextImageRepository: func() *imageapi.ImageRepository {
			repo := queue.Pop().(*imageapi.ImageRepository)
			panicIfStopped(factory.Stop, "deployment config change controller stopped")
			return repo
		},
		Stop: factory.Stop,
	}
}

// panicIfStopped panics with the provided object if the channel is closed
func panicIfStopped(ch <-chan struct{}, message interface{}) {
	select {
	case <-ch:
		panic(message)
	default:
	}
}

// deploymentLW is a ListWatcher implementation for Deployments.
type deploymentLW struct {
	client kclient.Interface
	field  labels.Selector
}

// List lists all Deployments which match the given field selector.
func (lw *deploymentLW) List() (runtime.Object, error) {
	return lw.client.ReplicationControllers(kapi.NamespaceAll).List(labels.Everything())
}

// Watch watches all Deployments matching the given field selector.
func (lw *deploymentLW) Watch(resourceVersion string) (watch.Interface, error) {
	return lw.client.ReplicationControllers(kapi.NamespaceAll).Watch(labels.Everything(), lw.field, "0")
}

// deploymentConfigLW is a ListWatcher implementation for DeploymentConfigs.
type deploymentConfigLW struct {
	client osclient.Interface
}

// List lists all DeploymentConfigs.
func (lw *deploymentConfigLW) List() (runtime.Object, error) {
	return lw.client.DeploymentConfigs(kapi.NamespaceAll).List(labels.Everything(), labels.Everything())
}

// Watch watches all DeploymentConfigs.
func (lw *deploymentConfigLW) Watch(resourceVersion string) (watch.Interface, error) {
	return lw.client.DeploymentConfigs(kapi.NamespaceAll).Watch(labels.Everything(), labels.Everything(), "0")
}

// imageRepositoryLW is a ListWatcher for ImageRepositories.
type imageRepositoryLW struct {
	client osclient.Interface
}

// List lists all ImageRepositories.
func (lw *imageRepositoryLW) List() (runtime.Object, error) {
	return lw.client.ImageRepositories(kapi.NamespaceAll).List(labels.Everything(), labels.Everything())
}

// Watch watches all ImageRepositories.
func (lw *imageRepositoryLW) Watch(resourceVersion string) (watch.Interface, error) {
	return lw.client.ImageRepositories(kapi.NamespaceAll).Watch(labels.Everything(), labels.Everything(), "0")
}

// ClientDeploymentInterace is a dccDeploymentInterface and dcDeploymentInterface which delegates to the OpenShift client interfaces
type ClientDeploymentInterace struct {
	Client kclient.Interface
}

// GetDeployment returns deployment using OpenShift client.
func (c ClientDeploymentInterace) GetDeployment(namespace, name string) (*kapi.ReplicationController, error) {
	return c.Client.ReplicationControllers(namespace).Get(name)
}

// CreateDeployment creates deployment using OpenShift client.
func (c ClientDeploymentInterace) CreateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
	return c.Client.ReplicationControllers(namespace).Create(deployment)
}

// UpdateDeployment creates deployment using OpenShift client.
func (c ClientDeploymentInterace) UpdateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
	return c.Client.ReplicationControllers(namespace).Update(deployment)
}

// ClientDeploymentConfigInterface is a changeStrategy which delegates to the OpenShift client interfaces
type ClientDeploymentConfigInterface struct {
	Client osclient.Interface
}

// GenerateDeploymentConfig generates deploymentConfig using OpenShift client.
func (c ClientDeploymentConfigInterface) GenerateDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) {
	return c.Client.DeploymentConfigs(namespace).Generate(name)
}

// UpdateDeploymentConfig creates deploymentConfig using OpenShift client.
func (c ClientDeploymentConfigInterface) UpdateDeploymentConfig(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
	return c.Client.DeploymentConfigs(namespace).Update(config)
}
<commit_msg>Typo in deployment controller factory<commit_after>package factory

import (
	"time"

	"github.com/golang/glog"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"

	osclient "github.com/openshift/origin/pkg/client"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	controller "github.com/openshift/origin/pkg/deploy/controller"
	imageapi "github.com/openshift/origin/pkg/image/api"
)

// DeploymentConfigControllerFactory can create a DeploymentConfigController which obtains
// DeploymentConfigs from a queue populated from a watch of all DeploymentConfigs.
type DeploymentConfigControllerFactory struct {
	Client     *osclient.Client
	KubeClient kclient.Interface
	Codec      runtime.Codec
	Stop       <-chan struct{}
}

func (factory *DeploymentConfigControllerFactory) Create() *controller.DeploymentConfigController {
	queue := cache.NewFIFO()
	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, queue).Run()

	return &controller.DeploymentConfigController{
		DeploymentInterface: &ClientDeploymentInterface{factory.KubeClient},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			config := queue.Pop().(*deployapi.DeploymentConfig)
			panicIfStopped(factory.Stop, "deployment config controller stopped")
			return config
		},
		Codec: factory.Codec,
		Stop:  factory.Stop,
	}
}

// DeploymentControllerFactory can create a DeploymentController which obtains Deployments
// from a queue populated from a watch of Deployments.
// Pods are obtained from a queue populated from a watch of all pods.
type DeploymentControllerFactory struct {
	// Client satisfies DeploymentInterface.
	Client *osclient.Client
	// KubeClient satisfies PodInterface.
	KubeClient *kclient.Client
	// Environment is a set of environment which should be injected into all deployment pod containers.
	Environment []kapi.EnvVar
	// UseLocalImages configures the ImagePullPolicy for containers deployment pods.
	UseLocalImages bool
	// RecreateStrategyImage specifies which Docker image which should implement the Recreate strategy.
	RecreateStrategyImage string
	// Codec is used to decode DeploymentConfigs.
	Codec runtime.Codec
	// Stop may be set to allow controllers created by this factory to be terminated.
	Stop <-chan struct{}

	// deploymentStore is maintained on the factory to support narrowing of the pod polling scope.
	deploymentStore cache.Store
}

func (factory *DeploymentControllerFactory) Create() *controller.DeploymentController {
	deploymentQueue := cache.NewFIFO()
	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, deploymentQueue).Run()

	factory.deploymentStore = cache.NewStore()
	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, factory.deploymentStore).Run()

	// Kubernetes does not currently synchronize Pod status in storage with a Pod's container
	// states. Because of this, we can't receive events related to container (and thus Pod)
	// state changes, such as Running -> Terminated. As a workaround, populate the FIFO with
	// a polling implementation which relies on client calls to list Pods - the Get/List
	// REST implementations will populate the synchronized container/pod status on-demand.
	//
	// TODO: Find a way to get watch events for Pod/container status updates. The polling
	// strategy is horribly inefficient and should be addressed upstream somehow.
	podQueue := cache.NewFIFO()
	cache.NewPoller(factory.pollPods, 10*time.Second, podQueue).Run()

	return &controller.DeploymentController{
		ContainerCreator:    factory,
		DeploymentInterface: &ClientDeploymentInterface{factory.KubeClient},
		PodInterface:        &DeploymentControllerPodInterface{factory.KubeClient},
		Environment:         factory.Environment,
		NextDeployment: func() *kapi.ReplicationController {
			deployment := deploymentQueue.Pop().(*kapi.ReplicationController)
			panicIfStopped(factory.Stop, "deployment controller stopped")
			return deployment
		},
		NextPod: func() *kapi.Pod {
			pod := podQueue.Pop().(*kapi.Pod)
			panicIfStopped(factory.Stop, "deployment controller stopped")
			return pod
		},
		DeploymentStore: factory.deploymentStore,
		UseLocalImages:  factory.UseLocalImages,
		Codec:           factory.Codec,
		Stop:            factory.Stop,
	}
}

// CreateContainer lets DeploymentControllerFactory satisfy the DeploymentContainerCreator interface
// and makes a container using the configuration of the factory.
func (factory *DeploymentControllerFactory) CreateContainer(strategy *deployapi.DeploymentStrategy) *kapi.Container {
	// Every strategy type should be handled here.
	switch strategy.Type {
	case deployapi.DeploymentStrategyTypeRecreate:
		// Use the factory-configured image.
		return &kapi.Container{
			Image: factory.RecreateStrategyImage,
		}
	case deployapi.DeploymentStrategyTypeCustom:
		// Use user-defined values from the strategy input.
		return &kapi.Container{
			Image: strategy.CustomParams.Image,
			Env:   strategy.CustomParams.Environment,
		}
	default:
		// TODO: This shouldn't be reachable. Improve error handling.
		glog.Errorf("Unsupported deployment strategy type %s", strategy.Type)
		return nil
	}
}

// pollPods lists all pods associated with pending or running deployments and returns
// a cache.Enumerator suitable for use with a cache.Poller.
func (factory *DeploymentControllerFactory) pollPods() (cache.Enumerator, error) {
	list := &kapi.PodList{}

	for _, obj := range factory.deploymentStore.List() {
		deployment := obj.(*kapi.ReplicationController)

		switch deployapi.DeploymentStatus(deployment.Annotations[deployapi.DeploymentStatusAnnotation]) {
		case deployapi.DeploymentStatusPending, deployapi.DeploymentStatusRunning:
			// Validate the correlating pod annotation
			podID, hasPodID := deployment.Annotations[deployapi.DeploymentPodAnnotation]
			if !hasPodID {
				glog.V(2).Infof("Unexpected state: Deployment %s has no pod annotation; skipping pod polling", deployment.Name)
				continue
			}

			pod, err := factory.KubeClient.Pods(deployment.Namespace).Get(podID)
			if err != nil {
				glog.V(2).Infof("Couldn't find pod %s for deployment %s: %#v", podID, deployment.Name, err)
				continue
			}

			list.Items = append(list.Items, *pod)
		}
	}

	return &podEnumerator{list}, nil
}

type DeploymentControllerPodInterface struct {
	KubeClient kclient.Interface
}

func (i DeploymentControllerPodInterface) CreatePod(namespace string, pod *kapi.Pod) (*kapi.Pod, error) {
	return i.KubeClient.Pods(namespace).Create(pod)
}

func (i DeploymentControllerPodInterface) DeletePod(namespace, id string) error {
	return i.KubeClient.Pods(namespace).Delete(id)
}

// podEnumerator allows a cache.Poller to enumerate items in an api.PodList
type podEnumerator struct {
	*kapi.PodList
}

// Len returns the number of items in the pod list.
func (pe *podEnumerator) Len() int {
	if pe.PodList == nil {
		return 0
	}
	return len(pe.Items)
}

// Get returns the item (and ID) with the particular index.
func (pe *podEnumerator) Get(index int) (string, interface{}) {
	return pe.Items[index].Name, &pe.Items[index]
}

// DeploymentConfigChangeControllerFactory can create a DeploymentConfigChangeController which obtains DeploymentConfigs
// from a queue populated from a watch of all DeploymentConfigs.
type DeploymentConfigChangeControllerFactory struct {
	Client     osclient.Interface
	KubeClient kclient.Interface
	Codec      runtime.Codec
	// Stop may be set to allow controllers created by this factory to be terminated.
	Stop <-chan struct{}
}

func (factory *DeploymentConfigChangeControllerFactory) Create() *controller.DeploymentConfigChangeController {
	queue := cache.NewFIFO()
	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, queue).Run()

	store := cache.NewStore()
	cache.NewReflector(&deploymentLW{client: factory.KubeClient, field: labels.Everything()}, &kapi.ReplicationController{}, store).Run()

	return &controller.DeploymentConfigChangeController{
		ChangeStrategy: &ClientDeploymentConfigInterface{factory.Client},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			config := queue.Pop().(*deployapi.DeploymentConfig)
			panicIfStopped(factory.Stop, "deployment config change controller stopped")
			return config
		},
		DeploymentStore: store,
		Codec:           factory.Codec,
		Stop:            factory.Stop,
	}
}

// ImageChangeControllerFactory can create an ImageChangeController which obtains ImageRepositories
// from a queue populated from a watch of all ImageRepositories.
type ImageChangeControllerFactory struct {
	Client *osclient.Client
	// Stop may be set to allow controllers created by this factory to be terminated.
	Stop <-chan struct{}
}

func (factory *ImageChangeControllerFactory) Create() *controller.ImageChangeController {
	queue := cache.NewFIFO()
	cache.NewReflector(&imageRepositoryLW{factory.Client}, &imageapi.ImageRepository{}, queue).Run()

	store := cache.NewStore()
	cache.NewReflector(&deploymentConfigLW{factory.Client}, &deployapi.DeploymentConfig{}, store).Run()

	return &controller.ImageChangeController{
		DeploymentConfigInterface: &ClientDeploymentConfigInterface{factory.Client},
		DeploymentConfigStore:     store,
		NextImageRepository: func() *imageapi.ImageRepository {
			repo := queue.Pop().(*imageapi.ImageRepository)
			panicIfStopped(factory.Stop, "deployment config change controller stopped")
			return repo
		},
		Stop: factory.Stop,
	}
}

// panicIfStopped panics with the provided object if the channel is closed
func panicIfStopped(ch <-chan struct{}, message interface{}) {
	select {
	case <-ch:
		panic(message)
	default:
	}
}

// deploymentLW is a ListWatcher implementation for Deployments.
type deploymentLW struct {
	client kclient.Interface
	field  labels.Selector
}

// List lists all Deployments which match the given field selector.
func (lw *deploymentLW) List() (runtime.Object, error) {
	return lw.client.ReplicationControllers(kapi.NamespaceAll).List(labels.Everything())
}

// Watch watches all Deployments matching the given field selector.
func (lw *deploymentLW) Watch(resourceVersion string) (watch.Interface, error) {
	return lw.client.ReplicationControllers(kapi.NamespaceAll).Watch(labels.Everything(), lw.field, "0")
}

// deploymentConfigLW is a ListWatcher implementation for DeploymentConfigs.
type deploymentConfigLW struct {
	client osclient.Interface
}

// List lists all DeploymentConfigs.
func (lw *deploymentConfigLW) List() (runtime.Object, error) {
	return lw.client.DeploymentConfigs(kapi.NamespaceAll).List(labels.Everything(), labels.Everything())
}

// Watch watches all DeploymentConfigs.
func (lw *deploymentConfigLW) Watch(resourceVersion string) (watch.Interface, error) {
	return lw.client.DeploymentConfigs(kapi.NamespaceAll).Watch(labels.Everything(), labels.Everything(), "0")
}

// imageRepositoryLW is a ListWatcher for ImageRepositories.
type imageRepositoryLW struct {
	client osclient.Interface
}

// List lists all ImageRepositories.
func (lw *imageRepositoryLW) List() (runtime.Object, error) {
	return lw.client.ImageRepositories(kapi.NamespaceAll).List(labels.Everything(), labels.Everything())
}

// Watch watches all ImageRepositories.
func (lw *imageRepositoryLW) Watch(resourceVersion string) (watch.Interface, error) {
	return lw.client.ImageRepositories(kapi.NamespaceAll).Watch(labels.Everything(), labels.Everything(), "0")
}

// ClientDeploymentInterface is a dccDeploymentInterface and dcDeploymentInterface which delegates to the OpenShift client interfaces
type ClientDeploymentInterface struct {
	Client kclient.Interface
}

// GetDeployment returns deployment using OpenShift client.
func (c ClientDeploymentInterface) GetDeployment(namespace, name string) (*kapi.ReplicationController, error) {
	return c.Client.ReplicationControllers(namespace).Get(name)
}

// CreateDeployment creates deployment using OpenShift client.
func (c ClientDeploymentInterface) CreateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
	return c.Client.ReplicationControllers(namespace).Create(deployment)
}

// UpdateDeployment creates deployment using OpenShift client.
func (c ClientDeploymentInterface) UpdateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
	return c.Client.ReplicationControllers(namespace).Update(deployment)
}

// ClientDeploymentConfigInterface is a changeStrategy which delegates to the OpenShift client interfaces
type ClientDeploymentConfigInterface struct {
	Client osclient.Interface
}

// GenerateDeploymentConfig generates deploymentConfig using OpenShift client.
func (c ClientDeploymentConfigInterface) GenerateDeploymentConfig(namespace, name string) (*deployapi.DeploymentConfig, error) {
	return c.Client.DeploymentConfigs(namespace).Generate(name)
}

// UpdateDeploymentConfig creates deploymentConfig using OpenShift client.
func (c ClientDeploymentConfigInterface) UpdateDeploymentConfig(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) {
	return c.Client.DeploymentConfigs(namespace).Update(config)
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

func (e *Evaluation) analyzePieces() {
	p := e.position
	var white, black [4]Score

	if Settings.Trace {
		defer func() {
			var his, her Score
			e.checkpoint(`+Pieces`,  Total{*his.add(white[0]).add(white[1]).add(white[2]).add(white[3]),
				*her.add(black[0]).add(black[1]).add(black[2]).add(black[3])})
			e.checkpoint(`-Knights`, Total{white[0], black[0]})
			e.checkpoint(`-Bishops`, Total{white[1], black[1]})
			e.checkpoint(`-Rooks`,   Total{white[2], black[2]})
			e.checkpoint(`-Queens`,  Total{white[3], black[3]})
		}()
	}

	// Mobility mask for white pieces excludes a) squares attacked by Black
	// pawns and b) squares occupied by white pawns and king.
	maskMobile := ^(e.attacks[BlackPawn] | p.outposts[Pawn] | p.outposts[King])
	if p.count[Knight] > 0 {
		white[0] = e.knights(White, maskMobile)
	}
	if p.count[Bishop] > 0 {
		white[1] = e.bishops(White, maskMobile)
	}
	if p.count[Rook] > 0 {
		white[2] = e.rooks(White, maskMobile)
	}
	if p.count[Queen] > 0 {
		white[3] = e.queens(White, maskMobile)
	}

	// Update mobility mask for black pieces.
	maskMobile = ^(e.attacks[Pawn] | p.outposts[BlackPawn] | p.outposts[BlackKing])
	if p.count[BlackKnight] > 0 {
		black[0] = e.knights(Black, maskMobile)
	}
	if p.count[BlackBishop] > 0 {
		black[1] = e.bishops(Black, maskMobile)
	}
	if p.count[BlackRook] > 0 {
		black[2] = e.rooks(Black, maskMobile)
	}
	if p.count[BlackQueen] > 0 {
		black[3] = e.queens(Black, maskMobile)
	}

	// Update attack bitmasks for both sides.
	e.attacks[White] |= e.attacks[Knight] | e.attacks[Bishop] | e.attacks[Rook] | e.attacks[Queen]
	e.attacks[Black] |= e.attacks[BlackKnight] | e.attacks[BlackBishop] | e.attacks[BlackRook] | e.attacks[BlackQueen]

	// Update cumulative score based on white vs. black delta.
	e.score.add(white[0]).add(white[1]).add(white[2]).add(white[3])
	e.score.subtract(black[0]).subtract(black[1]).subtract(black[2]).subtract(black[3])
}

func (e *Evaluation) knights(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	outposts := p.outposts[knight(color)]

	for outposts != 0 {
		square := outposts.pop()
		attacks := p.attacks(square)

		// Bonus for knight's mobility.
		score.add(mobilityKnight[(attacks & maskMobile).count()])

		// Penalty if knight is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Knight/2])
		}

		// Bonus if knight is behind friendly pawn.
		if RelRow(color, square) < 4 && p.outposts[pawn(color)].isSet(square + eight[color]) {
			score.add(behindPawn)
		}

		// Extra bonus if knight is in the center. Increase the extra
		// bonus if the knight is supported by a pawn and can't be
		// exchanged.
		flip := Flip(color, square)
		if extra := extraKnight[flip]; extra > 0 {
			if p.pawnAttacks(color).isSet(square) {
				if p.count[knight(color^1)] == 0 {
					extra *= 2 // No knights to exchange.
				}
				extra += extra / 2 // Supported by a pawn.
			}
			score.adjust(extra)
		}

		// Track if knight attacks squares around enemy's king.
		e.enemyKingThreat(knight(color), attacks)
	}
	return
}

func (e *Evaluation) bishops(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	outposts := p.outposts[bishop(color)]

	for outposts != 0 {
		square := outposts.pop()
		attacks := p.xrayAttacks(square)

		// Bonus for bishop's mobility
		score.add(mobilityBishop[(attacks & maskMobile).count()])

		// Penalty for light/dark square bishop and matching pawns.
		if count := (Same(square) & p.outposts[pawn(color)]).count(); count > 0 {
			score.subtract(bishopPawns)
		}

		// Penalty if bishop is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Bishop/2])
		}

		// Bonus if bishop is behind friendly pawn.
		if RelRow(color, square) < 4 && p.outposts[pawn(color)].isSet(square + eight[color]) {
			score.add(behindPawn)
		}

		// Middle game penalty for boxed bishop.
		if e.phase > 160 {
			if color == White {
				if (square == C1 && p.pieces[D2].isPawn() && p.pieces[D3] != 0) ||
				   (square == F1 && p.pieces[E2].isPawn() && p.pieces[E3] != 0) {
					score.midgame -= bishopBoxed.midgame
				}
			} else {
				if (square == C8 && p.pieces[D7].isPawn() && p.pieces[D6] != 0) ||
				   (square == F8 && p.pieces[E7].isPawn() && p.pieces[E6] != 0) {
					score.midgame -= bishopBoxed.midgame
				}
			}
		}

		// Extra bonus if bishop is in the center. Increase the extra
		// bonus if the bishop is supported by a pawn and can't be
		// exchanged.
		flip := Flip(color, square)
		if extra := extraBishop[flip]; extra > 0 {
			if p.pawnAttacks(color).isSet(square) {
				if p.count[bishop(color^1)] == 0 {
					extra *= 2 // No bishops to exchange.
				}
				extra += extra / 2 // Supported by a pawn.
			}
			score.adjust(extra)
		}

		// Track if bishop attacks squares around enemy's king.
		e.enemyKingThreat(bishop(color), attacks)
	}

	// Bonus for the pair of bishops.
	if bishops := p.count[bishop(color)]; bishops >= 2 {
		score.add(bishopPair)
	}
	return
}

func (e *Evaluation) rooks(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	hisPawns := p.outposts[pawn(color)]
	herPawns := p.outposts[pawn(color^1)]
	outposts := p.outposts[rook(color)]

	// Bonus if rook is on 7th rank and enemy's king trapped on 8th.
	if count := (outposts & mask7th[color]).count(); count > 0 && p.outposts[king(color^1)] & mask8th[color] != 0 {
		score.add(rookOn7th.times(count))
	}
	for outposts != 0 {
		square := outposts.pop()
		attacks := p.xrayAttacks(square)

		// Bonus for rook's mobility
		mobility := (attacks & maskMobile).count()
		score.add(mobilityRook[mobility])

		// Penalty if rook is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Rook/2])
		}

		// Bonus if rook is attacking enemy's pawns.
		if count := (attacks & p.outposts[pawn(color^1)]).count(); count > 0 {
			score.add(rookOnPawn.times(count))
		}

		// Bonuses if rook is on open or semi-open file.
		column := Col(square)
		isFileAjar := (hisPawns & maskFile[column] == 0)
		if isFileAjar {
			if herPawns & maskFile[column] == 0 {
				score.add(rookOnOpen)
			} else {
				score.add(rookOnSemiOpen)
			}
		}

		// Middle game penalty if a rook is boxed. Extra penalty if castle
		// rights have been lost.
		if mobility <= 3 || !isFileAjar {
			kingSquare := p.king[color]
			kingColumn := Col(kingSquare)

			// Queenside box: king on D/C/B vs. rook on A/B/C files. Double the
			// the penalty since no castle is possible.
			if column < kingColumn && rookBoxA[color].isSet(square) && kingBoxA[color].isSet(kingSquare) {
				score.midgame -= rookBoxed.midgame * 2
			}

			// Kingside box: king on E/F/G vs. rook on H/G/F files.
			if column > kingColumn && rookBoxH[color].isSet(square) && kingBoxH[color].isSet(kingSquare) {
				score.midgame -= rookBoxed.midgame
				if p.castles & castleKingside[color] == 0 {
					score.midgame -= rookBoxed.midgame
				}
			}
		}

		// Track if rook attacks squares around enemy's king.
		e.enemyKingThreat(rook(color), attacks)
	}
	return
}

func (e *Evaluation) queens(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	outposts := p.outposts[queen(color)]

	// Bonus if queen is on 7th rank and enemy's king trapped on 8th.
	if count := (outposts & mask7th[color]).count(); count > 0 && p.outposts[king(color^1)] & mask8th[color] != 0 {
		score.add(queenOn7th.times(count))
	}
	for outposts != 0 {
		square := outposts.pop()
		attacks := p.attacks(square)

		// Bonus for queen's mobility
		score.add(mobilityQueen[Min(15, (attacks & maskMobile).count())])

		// Penalty if queen is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Queen/2])
		}

		// Bonus if queen is out and attacking enemy's pawns.
		if count := (attacks & p.outposts[pawn(color^1)]).count(); count > 0 && RelRow(color, square) > 3 {
			score.add(queenOnPawn.times(count))
		}

		// Track if queen attacks squares around enemy's king.
		e.enemyKingThreat(queen(color), attacks)
	}
	return
}

func (e *Evaluation) enemyKingThreat(piece Piece, attacks Bitmask) {
	color := piece.color() ^ 1

	if attacks & e.king[color].fort != 0 {
		e.king[color].fortAttackers++
		e.king[color].threat += bonusKingThreat[piece.kind()/2]
		if bits := attacks & e.attacks[king(color)]; bits != 0 {
			e.king[color].homeAttacks += bits.count()
		}
	}

	// Update attack bitmask for the given piece.
	e.attacks[piece] |= attacks
}
<commit_msg>Tweak queen mobility calculation<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

func (e *Evaluation) analyzePieces() {
	p := e.position
	var white, black [4]Score

	if Settings.Trace {
		defer func() {
			var his, her Score
			e.checkpoint(`+Pieces`,  Total{*his.add(white[0]).add(white[1]).add(white[2]).add(white[3]),
				*her.add(black[0]).add(black[1]).add(black[2]).add(black[3])})
			e.checkpoint(`-Knights`, Total{white[0], black[0]})
			e.checkpoint(`-Bishops`, Total{white[1], black[1]})
			e.checkpoint(`-Rooks`,   Total{white[2], black[2]})
			e.checkpoint(`-Queens`,  Total{white[3], black[3]})
		}()
	}

	// Mobility mask for both sides excludes a) squares attacked by enemy's
	// pawns and b) squares occupied by own pawns and king.
	maskMobile := [2]Bitmask{
		^(e.attacks[BlackPawn] | p.outposts[Pawn] | p.outposts[King]),
		^(e.attacks[Pawn] | p.outposts[BlackPawn] | p.outposts[BlackKing]),
	}

	// Evaluate white pieces except queen.
	if p.count[Knight] > 0 {
		white[0] = e.knights(White, maskMobile[White])
	}
	if p.count[Bishop] > 0 {
		white[1] = e.bishops(White, maskMobile[White])
	}
	if p.count[Rook] > 0 {
		white[2] = e.rooks(White, maskMobile[White])
	}

	// Evaluate black pieces except queen.
	if p.count[BlackKnight] > 0 {
		black[0] = e.knights(Black, maskMobile[Black])
	}
	if p.count[BlackBishop] > 0 {
		black[1] = e.bishops(Black, maskMobile[Black])
	}
	if p.count[BlackRook] > 0 {
		black[2] = e.rooks(Black, maskMobile[Black])
	}

	// Now that we've built all attack bitmasks we can adjust mobility to
	// exclude attacks by enemy's knights, bishops, and rooks and evaluate
	// the queens.
	if p.count[Queen] > 0 {
		maskMobile[White] &= ^(e.attacks[BlackKnight] | e.attacks[BlackBishop] | e.attacks[BlackRook])
		white[3] = e.queens(White, maskMobile[White])
	}
	if p.count[BlackQueen] > 0 {
		maskMobile[Black] &= ^(e.attacks[Knight] | e.attacks[Bishop] | e.attacks[Rook])
		black[3] = e.queens(Black, maskMobile[Black])
	}

	// Update attack bitmasks for both sides.
	e.attacks[White] |= e.attacks[Knight] | e.attacks[Bishop] | e.attacks[Rook] | e.attacks[Queen]
	e.attacks[Black] |= e.attacks[BlackKnight] | e.attacks[BlackBishop] | e.attacks[BlackRook] | e.attacks[BlackQueen]

	// Update cumulative score based on white vs. black delta.
	e.score.add(white[0]).add(white[1]).add(white[2]).add(white[3])
	e.score.subtract(black[0]).subtract(black[1]).subtract(black[2]).subtract(black[3])
}

func (e *Evaluation) knights(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	outposts := p.outposts[knight(color)]

	for outposts != 0 {
		square := outposts.pop()
		attacks := p.attacks(square)

		// Bonus for knight's mobility.
		score.add(mobilityKnight[(attacks & maskMobile).count()])

		// Penalty if knight is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Knight/2])
		}

		// Bonus if knight is behind friendly pawn.
		if RelRow(color, square) < 4 && p.outposts[pawn(color)].isSet(square + eight[color]) {
			score.add(behindPawn)
		}

		// Extra bonus if knight is in the center. Increase the extra
		// bonus if the knight is supported by a pawn and can't be
		// exchanged.
		flip := Flip(color, square)
		if extra := extraKnight[flip]; extra > 0 {
			if p.pawnAttacks(color).isSet(square) {
				if p.count[knight(color^1)] == 0 {
					extra *= 2 // No knights to exchange.
				}
				extra += extra / 2 // Supported by a pawn.
			}
			score.adjust(extra)
		}

		// Track if knight attacks squares around enemy's king.
		e.enemyKingThreat(knight(color), attacks)
	}
	return
}

func (e *Evaluation) bishops(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	outposts := p.outposts[bishop(color)]

	for outposts != 0 {
		square := outposts.pop()
		attacks := p.xrayAttacks(square)

		// Bonus for bishop's mobility
		score.add(mobilityBishop[(attacks & maskMobile).count()])

		// Penalty for light/dark square bishop and matching pawns.
		if count := (Same(square) & p.outposts[pawn(color)]).count(); count > 0 {
			score.subtract(bishopPawns)
		}

		// Penalty if bishop is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Bishop/2])
		}

		// Bonus if bishop is behind friendly pawn.
		if RelRow(color, square) < 4 && p.outposts[pawn(color)].isSet(square + eight[color]) {
			score.add(behindPawn)
		}

		// Middle game penalty for boxed bishop.
		if e.phase > 160 {
			if color == White {
				if (square == C1 && p.pieces[D2].isPawn() && p.pieces[D3] != 0) ||
				   (square == F1 && p.pieces[E2].isPawn() && p.pieces[E3] != 0) {
					score.midgame -= bishopBoxed.midgame
				}
			} else {
				if (square == C8 && p.pieces[D7].isPawn() && p.pieces[D6] != 0) ||
				   (square == F8 && p.pieces[E7].isPawn() && p.pieces[E6] != 0) {
					score.midgame -= bishopBoxed.midgame
				}
			}
		}

		// Extra bonus if bishop is in the center. Increase the extra
		// bonus if the bishop is supported by a pawn and can't be
		// exchanged.
		flip := Flip(color, square)
		if extra := extraBishop[flip]; extra > 0 {
			if p.pawnAttacks(color).isSet(square) {
				if p.count[bishop(color^1)] == 0 {
					extra *= 2 // No bishops to exchange.
				}
				extra += extra / 2 // Supported by a pawn.
			}
			score.adjust(extra)
		}

		// Track if bishop attacks squares around enemy's king.
		e.enemyKingThreat(bishop(color), attacks)
	}

	// Bonus for the pair of bishops.
	if bishops := p.count[bishop(color)]; bishops >= 2 {
		score.add(bishopPair)
	}
	return
}

func (e *Evaluation) rooks(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	hisPawns := p.outposts[pawn(color)]
	herPawns := p.outposts[pawn(color^1)]
	outposts := p.outposts[rook(color)]

	// Bonus if rook is on 7th rank and enemy's king trapped on 8th.
	if count := (outposts & mask7th[color]).count(); count > 0 && p.outposts[king(color^1)] & mask8th[color] != 0 {
		score.add(rookOn7th.times(count))
	}
	for outposts != 0 {
		square := outposts.pop()
		attacks := p.xrayAttacks(square)

		// Bonus for rook's mobility
		mobility := (attacks & maskMobile).count()
		score.add(mobilityRook[mobility])

		// Penalty if rook is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Rook/2])
		}

		// Bonus if rook is attacking enemy's pawns.
		if count := (attacks & p.outposts[pawn(color^1)]).count(); count > 0 {
			score.add(rookOnPawn.times(count))
		}

		// Bonuses if rook is on open or semi-open file.
		column := Col(square)
		isFileAjar := (hisPawns & maskFile[column] == 0)
		if isFileAjar {
			if herPawns & maskFile[column] == 0 {
				score.add(rookOnOpen)
			} else {
				score.add(rookOnSemiOpen)
			}
		}

		// Middle game penalty if a rook is boxed. Extra penalty if castle
		// rights have been lost.
		if mobility <= 3 || !isFileAjar {
			kingSquare := p.king[color]
			kingColumn := Col(kingSquare)

			// Queenside box: king on D/C/B vs. rook on A/B/C files. Double the
			// the penalty since no castle is possible.
			if column < kingColumn && rookBoxA[color].isSet(square) && kingBoxA[color].isSet(kingSquare) {
				score.midgame -= rookBoxed.midgame * 2
			}

			// Kingside box: king on E/F/G vs. rook on H/G/F files.
			if column > kingColumn && rookBoxH[color].isSet(square) && kingBoxH[color].isSet(kingSquare) {
				score.midgame -= rookBoxed.midgame
				if p.castles & castleKingside[color] == 0 {
					score.midgame -= rookBoxed.midgame
				}
			}
		}

		// Track if rook attacks squares around enemy's king.
		e.enemyKingThreat(rook(color), attacks)
	}
	return
}

func (e *Evaluation) queens(color int, maskMobile Bitmask) (score Score) {
	p := e.position
	outposts := p.outposts[queen(color)]

	// Bonus if queen is on 7th rank and enemy's king trapped on 8th.
	if count := (outposts & mask7th[color]).count(); count > 0 && p.outposts[king(color^1)] & mask8th[color] != 0 {
		score.add(queenOn7th.times(count))
	}
	for outposts != 0 {
		square := outposts.pop()
		attacks := p.attacks(square)

		// Bonus for queen's mobility.
		score.add(mobilityQueen[Min(15, (attacks & maskMobile).count())])

		// Penalty if queen is attacked by enemy's pawn.
		if maskPawn[color^1][square] & p.outposts[pawn(color^1)] != 0 {
			score.subtract(penaltyPawnThreat[Queen/2])
		}

		// Bonus if queen is out and attacking enemy's pawns.
		if count := (attacks & p.outposts[pawn(color^1)]).count(); count > 0 && RelRow(color, square) > 3 {
			score.add(queenOnPawn.times(count))
		}

		// Track if queen attacks squares around enemy's king.
		e.enemyKingThreat(queen(color), attacks)
	}
	return
}

func (e *Evaluation) enemyKingThreat(piece Piece, attacks Bitmask) {
	color := piece.color() ^ 1

	if attacks & e.king[color].fort != 0 {
		e.king[color].fortAttackers++
		e.king[color].threat += bonusKingThreat[piece.kind()/2]
		if bits := attacks & e.attacks[king(color)]; bits != 0 {
			e.king[color].homeAttacks += bits.count()
		}
	}

	// Update attack bitmask for the given piece.
	e.attacks[piece] |= attacks
}
<|endoftext|>


<commit_before>package template

import "testing"

func TestTagOptsStringer(t *testing.T) {
	tests := []struct {
		tagopt TagOpts
		want   string
	}{
		{TagOpts{"fu": "bar"}, `fu="bar" `},
		{TagOpts{"fu": "bar", "bar": "fu"}, `fu="bar" bar="fu" `},
		{TagOpts{}, ``},
	}

	for _, s := range tests {
		if s.tagopt.String() != s.want {
			t.Errorf(`'%v' != '%v'`, s.tagopt.String(), s.want)
		}
	}
}

func TestNewTagOpts(t *testing.T) {
	tests := []struct {
		args []string
		want string
	}{
		{nil, ""},
		{[]string{}, ""},
		{[]string{"fu", "bar"}, `fu="bar" `},
		{[]string{"fu", "bar", "bar", "fu"}, `fu="bar" bar="fu" `},
	}

	for _, s := range tests {
		tagopt, err := newTagOpts(s.args...)
		if err != nil {
			t.Error(err)
		}

		if tagopt.String() != s.want {
			t.Errorf(`'%v' != '%v'`, tagopt.String(), s.want)
		}
	}

	errors := [][]string{
		[]string{"", "", ""},
	}

	for _, s := range errors {
		tagopt, err := newTagOpts(s...)
		if err == nil {
			t.Errorf("Expected an error with: %q. Got: '%v'", s, tagopt.String())
		}
	}
}

func TestGetTagOpt(t *testing.T) {
	t1 := make(TagOpts)
	t2, _ := newTagOpts("fu", "bar")
	t3, _ := newTagOpts("fu", "bar", "bar", "fu")

	tests := []struct {
		tagopts []TagOpts
		want    string
	}{
		{[]TagOpts{}, ""},
		{[]TagOpts{nil, t2}, ""},
		{[]TagOpts{t1, t2}, ""},
		{[]TagOpts{t2, t1}, t2.String()},
		{[]TagOpts{t2, t3, t1, t2, t1}, t2.String()},
	}

	for _, s := range tests {
		tagopt := getTagOpt(s.tagopts...)
		if tagopt.String() != s.want {
			t.Errorf(`'%v' != '%v'`, tagopt.String(), s.want)
		}
	}
}

func TestTag(t *testing.T) {
	tests := []struct {
		tag  string
		args []string
		want string
	}{
		{"div", nil, "<div>"},
		{"div", []string{"fu", "bar"}, `<div fu="bar">`},
		{"div", []string{"fu", "bar", "bar", "fu"}, `<div fu="bar" bar="fu">`},
	}

	for _, s := range tests {
		tagopt, err := newTagOpts(s.args...)
		if err != nil {
			t.Error(err)
		}

		if string(tag(s.tag, tagopt)) != s.want {
			t.Errorf(`'%v' != '%v'`, tagopt.String(), s.want)
		}
	}
}
<commit_msg>Better tests for tags<commit_after>package template

import (
	"errors"
	"fmt"
	"strings"
	"testing"
)

func verifyTagOpts(want string, opts ...TagOpts) error {
	if len(opts) == 0 || opts == nil {
		return nil
	}

	for _, opt := range opts {
		for k, v := range opt {
			if strings.Contains(want, fmt.Sprintf("%v=%q", k, v)) == false {
				return errors.New(fmt.Sprintf("%v=%q is not in '%v'", k, v, want))
			}
		}
	}

	return nil
}

func verifyTag(got, want string) error {
	if got == want {
		return nil
	}

	// Make sure all tags we got are there
	for _, s := range strings.Split(got, " ") {
		if !strings.Contains(want, s) {
			return errors.New(fmt.Sprintf("'%v' is not in '%v'", s, want))
		}
	}

	// Make sure it's not missing any expected tags
	for _, s := range strings.Split(want, " ") {
		if !strings.Contains(got, s) {
			return errors.New(fmt.Sprintf("'%v' is not in '%v'", s, got))
		}
	}
	return nil
}

func TestTagOptsStringer(t *testing.T) {
	tests := []struct {
		tagopt TagOpts
		want   string
	}{
		{TagOpts{"fu": "bar"}, `fu="bar" `},
		{TagOpts{"fu": "bar", "bar": "fu"}, `fu="bar" bar="fu" `},
		{TagOpts{}, ``},
	}

	for _, s := range tests {
		if err := verifyTagOpts(s.want, s.tagopt); err != nil {
			t.Error(err)
		}
	}
}

func TestNewTagOpts(t *testing.T) {
	tests := []struct {
		args []string
		want string
	}{
		{nil, ""},
		{[]string{}, ""},
		{[]string{"fu", "bar"}, `fu="bar" `},
		{[]string{"fu", "bar", "bar", "fu"}, `fu="bar" bar="fu" `},
	}

	for _, s := range tests {
		tagopt, err := newTagOpts(s.args...)
		if err != nil {
			t.Error(err)
		}

		if err := verifyTagOpts(tagopt.String(), tagopt); err != nil {
			t.Error(err)
		}

		if err := verifyTagOpts(s.want, tagopt); err != nil {
			t.Error(err)
		}
	}

	errors := [][]string{
		[]string{"", "", ""},
	}

	for _, s := range errors {
		tagopt, err := newTagOpts(s...)
		if err == nil {
			t.Errorf("Expected an error with: %q. Got: '%v'", s, tagopt.String())
		}
	}
}

func TestGetTagOpt(t *testing.T) {
	t1 := make(TagOpts)
	t2, _ := newTagOpts("fu", "bar")
	t3, _ := newTagOpts("fu", "bar", "bar", "fu")

	tests := []struct {
		tagopts []TagOpts
		want    string
	}{
		{[]TagOpts{}, ""},
		{[]TagOpts{nil, t2}, ""},
		{[]TagOpts{t1, t2}, ""},
		{[]TagOpts{t2, t1}, t2.String()},
		{[]TagOpts{t2, t3, t1, t2, t1}, t2.String()},
	}

	for _, s := range tests {
		tagopt := getTagOpt(s.tagopts...)

		if err := verifyTagOpts(tagopt.String(), tagopt); err != nil {
			t.Error(err)
		}

		if err := verifyTagOpts(s.want, tagopt); err != nil {
			t.Error(err)
		}
	}
}

func TestTag(t *testing.T) {
	tests := []struct {
		tag  string
		args []string
		want string
	}{
		{"div", nil, "<div>"},
		{"div", []string{"fu", "bar"}, `<div fu="bar">`},
		{"div", []string{"fu", "bar", "bar", "fu"}, `<div fu="bar" bar="fu">`},
	}

	for _, s := range tests {
		tagopt, err := newTagOpts(s.args...)
		if err != nil {
			t.Error(err)
		}

		got := string(tag(s.tag, tagopt))
		if err := verifyTagOpts(got, tagopt); err != nil {
			t.Error(err)
		}

		if err := verifyTag(got, s.want); err != nil {
			t.Error(err)
		}
	}
}
<|endoftext|>


<commit_before>package paypal_rest

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/fritzpay/paymentd/pkg/paymentd/nonce"

	"gopkg.in/inconshreveable/log15.v2"

	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment_method"
)

const (
	paypalPaymentPath = "/v1/payments/payment"
)

func (d *Driver) InitPayment(p *payment.Payment, method *payment_method.Method) (http.Handler, error) {
	log := d.log.New(log15.Ctx{
		"method":          "InitPayment",
		"projectID":       p.ProjectID(),
		"paymentID":       p.ID(),
		"paymentMethodID": method.ID,
	})

	var tx *sql.Tx
	var err error
	var commit bool
	defer func() {
		if tx != nil && !commit {
			err = tx.Rollback()
			if err != nil {
				log.Crit("error on rollback", log15.Ctx{"err": err})
			}
		}
	}()
	tx, err = d.ctx.PaymentDB().Begin()
	if err != nil {
		commit = true
		log.Crit("error on begin tx", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	currentTx, err := TransactionCurrentByPaymentIDTx(tx, p.PaymentID())
	if err != nil && err != ErrTransactionNotFound {
		log.Error("error retrieving transaction", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}
	if err == nil {
		if Debug {
			log.Debug("already initialized payment")
		}
		return d.StatusHandler(currentTx, p), nil
	}

	cfg, err := ConfigByPaymentMethodTx(tx, method)
	if err != nil {
		log.Error("error retrieving PayPal config", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	// create payment request
	non, err := nonce.New()
	if err != nil {
		log.Error("error generating nonce", log15.Ctx{"err": err})
		return nil, ErrInternal
	}
	req, err := d.createPaypalPaymentRequest(p, cfg, non)
	if err != nil {
		log.Error("error creating paypal payment request", log15.Ctx{"err": err})
		return nil, ErrInternal
	}
	if Debug {
		log.Debug("created paypal payment request", log15.Ctx{"request": req})
	}

	endpoint, err := url.Parse(cfg.Endpoint)
	if err != nil {
		log.Error("error on endpoint URL", log15.Ctx{"err": err})
		return nil, ErrInternal
	}
	endpoint.Path = paypalPaymentPath

	jsonBytes, err := json.Marshal(req)
	if err != nil {
		log.Error("error encoding request", log15.Ctx{"err": err})
		return nil, ErrInternal
	}

	paypalTx := &Transaction{
		ProjectID: p.ProjectID(),
		PaymentID: p.ID(),
		Timestamp: time.Now(),
		Type:      TransactionTypeCreatePayment,
	}
	paypalTx.SetIntent(cfg.Type)
	paypalTx.SetNonce(non.Nonce)
	paypalTx.Data = jsonBytes

	err = InsertTransactionTx(tx, paypalTx)
	if err != nil {
		log.Error("error saving transaction", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	commit = true
	err = tx.Commit()
	if err != nil {
		log.Crit("error on commit", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	go d.doInit(cfg, endpoint, p, string(jsonBytes))

	return d.InitPageHandler(p), nil
}

func (d *Driver) doInit(cfg *Config, reqURL *url.URL, p *payment.Payment, body string) {
	log := d.log.New(log15.Ctx{
		"method":      "doInit",
		"projectID":   p.ProjectID(),
		"paymentID":   p.ID(),
		"methodKey":   cfg.MethodKey,
		"requestBody": body,
	})
	if Debug {
		log.Debug("posting...")
	}

	req, err := http.NewRequest("POST", reqURL.String(), strings.NewReader(body))
	if err != nil {
		log.Error("error creating HTTP request", log15.Ctx{"err": err})
		return
	}
	req.Header.Set("Content-Type", "application/json")
	responseFunc := func(resp *http.Response, err error) error {
		if err != nil {
			log.Error("error on HTTP", log15.Ctx{"err": err})
			d.setPayPalError(p, nil)
			return err
		}
		respBody, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			log.Error("error reading response body", log15.Ctx{"err": err})
			d.setPayPalError(p, nil)
			return ErrHTTP
		}
		log = log.New(log15.Ctx{"responseBody": string(respBody)})
		if Debug {
			log.Debug("received response")
		}
		if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
			log.Error("error on HTTP request", log15.Ctx{"HTTPStatusCode": resp.StatusCode})
			d.setPayPalError(p, nil)
			return ErrHTTP
		}
		paypalP := &PaypalPayment{}
		err = json.Unmarshal(respBody, paypalP)
		if err != nil {
			log.Error("error decoding PayPal response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}

		paypalTx := &Transaction{
			ProjectID: p.ProjectID(),
			PaymentID: p.ID(),
			Timestamp: time.Now(),
			Type:      TransactionTypeCreatePaymentResponse,
		}
		if paypalP.Intent != "" {
			paypalTx.SetIntent(paypalP.Intent)
		}
		if paypalP.ID != "" {
			paypalTx.SetPaypalID(paypalP.ID)
		}
		if paypalP.State != "" {
			paypalTx.SetState(paypalP.State)
		}
		if paypalP.CreateTime != "" {
			t, err := time.Parse(time.RFC3339, paypalP.CreateTime)
			if err != nil {
				log.Warn("error parsing paypal create time", log15.Ctx{"err": err})
			} else {
				paypalTx.PaypalCreateTime = &t
			}
		}
		if paypalP.UpdateTime != "" {
			t, err := time.Parse(time.RFC3339, paypalP.UpdateTime)
			if err != nil {
				log.Warn("error parsing paypal update time", log15.Ctx{"err": err})
			} else {
				paypalTx.PaypalUpdateTime = &t
			}
		}
		paypalTx.Links, err = json.Marshal(paypalP.Links)
		if err != nil {
			log.Error("error on saving links on response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}
		paypalTx.Data, err = json.Marshal(paypalP)
		if err != nil {
			log.Error("error marshalling paypal payment response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}
		err = InsertTransactionDB(d.ctx.PaymentDB(), paypalTx)
		if err != nil {
			log.Error("error saving paypal response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}
		return nil
	}

	err = httpDo(d.ctx, d.oAuthTransportFunc(p, cfg), req, responseFunc)
	if err != nil {
		log.Error("error on create payment request", log15.Ctx{"err": err})
	}
}
<commit_msg>also log body on http status err<commit_after>package paypal_rest

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/fritzpay/paymentd/pkg/paymentd/nonce"

	"gopkg.in/inconshreveable/log15.v2"

	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment_method"
)

const (
	paypalPaymentPath = "/v1/payments/payment"
)

func (d *Driver) InitPayment(p *payment.Payment, method *payment_method.Method) (http.Handler, error) {
	log := d.log.New(log15.Ctx{
		"method":          "InitPayment",
		"projectID":       p.ProjectID(),
		"paymentID":       p.ID(),
		"paymentMethodID": method.ID,
	})

	var tx *sql.Tx
	var err error
	var commit bool
	defer func() {
		if tx != nil && !commit {
			err = tx.Rollback()
			if err != nil {
				log.Crit("error on rollback", log15.Ctx{"err": err})
			}
		}
	}()
	tx, err = d.ctx.PaymentDB().Begin()
	if err != nil {
		commit = true
		log.Crit("error on begin tx", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	currentTx, err := TransactionCurrentByPaymentIDTx(tx, p.PaymentID())
	if err != nil && err != ErrTransactionNotFound {
		log.Error("error retrieving transaction", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}
	if err == nil {
		if Debug {
			log.Debug("already initialized payment")
		}
		return d.StatusHandler(currentTx, p), nil
	}

	cfg, err := ConfigByPaymentMethodTx(tx, method)
	if err != nil {
		log.Error("error retrieving PayPal config", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	// create payment request
	non, err := nonce.New()
	if err != nil {
		log.Error("error generating nonce", log15.Ctx{"err": err})
		return nil, ErrInternal
	}
	req, err := d.createPaypalPaymentRequest(p, cfg, non)
	if err != nil {
		log.Error("error creating paypal payment request", log15.Ctx{"err": err})
		return nil, ErrInternal
	}
	if Debug {
		log.Debug("created paypal payment request", log15.Ctx{"request": req})
	}

	endpoint, err := url.Parse(cfg.Endpoint)
	if err != nil {
		log.Error("error on endpoint URL", log15.Ctx{"err": err})
		return nil, ErrInternal
	}
	endpoint.Path = paypalPaymentPath

	jsonBytes, err := json.Marshal(req)
	if err != nil {
		log.Error("error encoding request", log15.Ctx{"err": err})
		return nil, ErrInternal
	}

	paypalTx := &Transaction{
		ProjectID: p.ProjectID(),
		PaymentID: p.ID(),
		Timestamp: time.Now(),
		Type:      TransactionTypeCreatePayment,
	}
	paypalTx.SetIntent(cfg.Type)
	paypalTx.SetNonce(non.Nonce)
	paypalTx.Data = jsonBytes

	err = InsertTransactionTx(tx, paypalTx)
	if err != nil {
		log.Error("error saving transaction", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	commit = true
	err = tx.Commit()
	if err != nil {
		log.Crit("error on commit", log15.Ctx{"err": err})
		return nil, ErrDatabase
	}

	go d.doInit(cfg, endpoint, p, string(jsonBytes))

	return d.InitPageHandler(p), nil
}

func (d *Driver) doInit(cfg *Config, reqURL *url.URL, p *payment.Payment, body string) {
	log := d.log.New(log15.Ctx{
		"method":      "doInit",
		"projectID":   p.ProjectID(),
		"paymentID":   p.ID(),
		"methodKey":   cfg.MethodKey,
		"requestBody": body,
	})
	if Debug {
		log.Debug("posting...")
	}

	req, err := http.NewRequest("POST", reqURL.String(), strings.NewReader(body))
	if err != nil {
		log.Error("error creating HTTP request", log15.Ctx{"err": err})
		return
	}
	req.Header.Set("Content-Type", "application/json")
	responseFunc := func(resp *http.Response, err error) error {
		if err != nil {
			log.Error("error on HTTP", log15.Ctx{"err": err})
			d.setPayPalError(p, nil)
			return err
		}
		respBody, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			log.Error("error reading response body", log15.Ctx{"err": err})
			d.setPayPalError(p, nil)
			return ErrHTTP
		}
		log = log.New(log15.Ctx{"responseBody": string(respBody)})
		if Debug {
			log.Debug("received response")
		}
		if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
			log.Error("error on HTTP request", log15.Ctx{"HTTPStatusCode": resp.StatusCode})
			d.setPayPalError(p, respBody)
			return ErrHTTP
		}
		paypalP := &PaypalPayment{}
		err = json.Unmarshal(respBody, paypalP)
		if err != nil {
			log.Error("error decoding PayPal response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}

		paypalTx := &Transaction{
			ProjectID: p.ProjectID(),
			PaymentID: p.ID(),
			Timestamp: time.Now(),
			Type:      TransactionTypeCreatePaymentResponse,
		}
		if paypalP.Intent != "" {
			paypalTx.SetIntent(paypalP.Intent)
		}
		if paypalP.ID != "" {
			paypalTx.SetPaypalID(paypalP.ID)
		}
		if paypalP.State != "" {
			paypalTx.SetState(paypalP.State)
		}
		if paypalP.CreateTime != "" {
			t, err := time.Parse(time.RFC3339, paypalP.CreateTime)
			if err != nil {
				log.Warn("error parsing paypal create time", log15.Ctx{"err": err})
			} else {
				paypalTx.PaypalCreateTime = &t
			}
		}
		if paypalP.UpdateTime != "" {
			t, err := time.Parse(time.RFC3339, paypalP.UpdateTime)
			if err != nil {
				log.Warn("error parsing paypal update time", log15.Ctx{"err": err})
			} else {
				paypalTx.PaypalUpdateTime = &t
			}
		}
		paypalTx.Links, err = json.Marshal(paypalP.Links)
		if err != nil {
			log.Error("error on saving links on response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}
		paypalTx.Data, err = json.Marshal(paypalP)
		if err != nil {
			log.Error("error marshalling paypal payment response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}
		err = InsertTransactionDB(d.ctx.PaymentDB(), paypalTx)
		if err != nil {
			log.Error("error saving paypal response", log15.Ctx{"err": err})
			d.setPayPalError(p, respBody)
			return ErrProvider
		}
		return nil
	}

	err = httpDo(d.ctx, d.oAuthTransportFunc(p, cfg), req, responseFunc)
	if err != nil {
		log.Error("error on create payment request", log15.Ctx{"err": err})
	}
}
<|endoftext|>


<commit_before>package multildap

import (
	"errors"
	"testing"

	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/ldap"
	. "github.com/smartystreets/goconvey/convey"
)

func TestMultiLDAP(t *testing.T) {
	Convey("Multildap", t, func() {
		Convey("Ping()", func() {
			Convey("Should return error for absent config list", func() {
				setup()

				multi := New([]*ldap.ServerConfig{})
				_, err := multi.Ping()

				So(err, ShouldBeError)
				So(err, ShouldEqual, ErrNoLDAPServers)

				teardown()
			})
			Convey("Should return an unavailable status on dial error", func() {
				mock := setup()

				expectedErr := errors.New("Dial error")
				mock.dialErrReturn = expectedErr

				multi := New([]*ldap.ServerConfig{
					{Host: "10.0.0.1", Port: 361},
				})

				statuses, err := multi.Ping()

				So(err, ShouldBeNil)
				So(statuses[0].Host, ShouldEqual, "10.0.0.1")
				So(statuses[0].Port, ShouldEqual, 361)
				So(statuses[0].Available, ShouldBeFalse)
				So(statuses[0].Error, ShouldEqual, expectedErr)
				So(mock.closeCalledTimes, ShouldEqual, 0)

				teardown()
			})
			Convey("Should get the LDAP server statuses", func() {
				mock := setup()

				multi := New([]*ldap.ServerConfig{
					{Host: "10.0.0.1", Port: 361},
				})

				statuses, err := multi.Ping()

				So(err, ShouldBeNil)
				So(statuses[0].Host, ShouldEqual, "10.0.0.1")
				So(statuses[0].Port, ShouldEqual, 361)
				So(statuses[0].Available, ShouldBeTrue)
				So(statuses[0].Error, ShouldBeNil)
				So(mock.closeCalledTimes, ShouldEqual, 1)

				teardown()
			})
		})
		Convey("Login()", func() {
			Convey("Should return error for absent config list", func() {
				setup()

				multi := New([]*ldap.ServerConfig{})
				_, err := multi.Login(&models.LoginUserQuery{})

				So(err, ShouldBeError)
				So(err, ShouldEqual, ErrNoLDAPServers)

				teardown()
			})

			Convey("Should return a dial error", func() {
				mock := setup()

				expected := errors.New("Dial error")
				mock.dialErrReturn = expected

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})

				_, err := multi.Login(&models.LoginUserQuery{})

				So(err, ShouldBeError)
				So(err, ShouldEqual, expected)

				teardown()
			})

			Convey("Should call underlying LDAP methods", func() {
				mock := setup()

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Login(&models.LoginUserQuery{})

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(mock.loginCalledTimes, ShouldEqual, 2)
				So(mock.closeCalledTimes, ShouldEqual, 2)

				So(err, ShouldEqual, ErrInvalidCredentials)

				teardown()
			})

			Convey("Should get login result", func() {
				mock := setup()

				mock.loginReturn = &models.ExternalUserInfo{
					Login: "killa",
				}

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				result, err := multi.Login(&models.LoginUserQuery{})

				So(mock.dialCalledTimes, ShouldEqual, 1)
				So(mock.loginCalledTimes, ShouldEqual, 1)
				So(mock.closeCalledTimes, ShouldEqual, 1)

				So(result.Login, ShouldEqual, "killa")
				So(err, ShouldBeNil)

				teardown()
			})

			Convey("Should still call a second error for invalid not found error", func() {
				mock := setup()

				mock.loginErrReturn = ErrCouldNotFindUser

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Login(&models.LoginUserQuery{})

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(mock.loginCalledTimes, ShouldEqual, 2)
				So(mock.closeCalledTimes, ShouldEqual, 2)

				So(err, ShouldEqual, ErrInvalidCredentials)

				teardown()
			})

			Convey("Should still try to auth with the second server after receiving an invalid credentials error from the first", func() {
				mock := setup()

				mock.loginErrReturn = ErrInvalidCredentials

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Login(&models.LoginUserQuery{})

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(mock.loginCalledTimes, ShouldEqual, 2)
				So(mock.closeCalledTimes, ShouldEqual, 2)

				So(err, ShouldEqual, ErrInvalidCredentials)

				teardown()
			})

			Convey("Should still try to auth with the second server after receiving a dial error from the first", func() {
				mock := setup()

				expectedError := errors.New("Dial error")
				mock.dialErrReturn = expectedError

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Login(&models.LoginUserQuery{})

				So(mock.dialCalledTimes, ShouldEqual, 2)

				So(err, ShouldEqual, expectedError)

				teardown()
			})

			Convey("Should return unknown error", func() {
				mock := setup()

				expected := errors.New("Something unknown")
				mock.loginErrReturn = expected

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Login(&models.LoginUserQuery{})

				So(mock.dialCalledTimes, ShouldEqual, 1)
				So(mock.loginCalledTimes, ShouldEqual, 1)
				So(mock.closeCalledTimes, ShouldEqual, 1)

				So(err, ShouldEqual, expected)

				teardown()
			})
		})

		Convey("User()", func() {
			Convey("Should return error for absent config list", func() {
				setup()

				multi := New([]*ldap.ServerConfig{})
				_, _, err := multi.User("test")

				So(err, ShouldBeError)
				So(err, ShouldEqual, ErrNoLDAPServers)

				teardown()
			})

			Convey("Should return a dial error", func() {
				mock := setup()

				expected := errors.New("Dial error")
				mock.dialErrReturn = expected

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})

				_, _, err := multi.User("test")

				So(err, ShouldBeError)
				So(err, ShouldEqual, expected)

				teardown()
			})

			Convey("Should call underlying LDAP methods", func() {
				mock := setup()

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, _, err := multi.User("test")

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(mock.usersCalledTimes, ShouldEqual, 2)
				So(mock.closeCalledTimes, ShouldEqual, 2)

				So(err, ShouldEqual, ErrDidNotFindUser)

				teardown()
			})

			Convey("Should return some error", func() {
				mock := setup()

				expected := errors.New("Killa Gorilla")
				mock.usersErrReturn = expected

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, _, err := multi.User("test")

				So(mock.dialCalledTimes, ShouldEqual, 1)
				So(mock.usersCalledTimes, ShouldEqual, 1)
				So(mock.closeCalledTimes, ShouldEqual, 1)

				So(err, ShouldEqual, expected)

				teardown()
			})

			Convey("Should get only one user", func() {
				mock := setup()

				mock.usersFirstReturn = []*models.ExternalUserInfo{
					{
						Login: "one",
					},

					{
						Login: "two",
					},
				}

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				user, _, err := multi.User("test")

				So(mock.dialCalledTimes, ShouldEqual, 1)
				So(mock.usersCalledTimes, ShouldEqual, 1)
				So(mock.closeCalledTimes, ShouldEqual, 1)

				So(err, ShouldBeNil)
				So(user.Login, ShouldEqual, "one")

				teardown()
			})

			Convey("Should still try to auth with the second server after receiving a dial error from the first", func() {
				mock := setup()

				expectedError := errors.New("Dial error")
				mock.dialErrReturn = expectedError

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, _, err := multi.User("test")

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(err, ShouldEqual, expectedError)

				teardown()
			})
		})

		Convey("Users()", func() {
			Convey("Should still try to auth with the second server after receiving a dial error from the first", func() {
				mock := setup()

				expectedError := errors.New("Dial error")
				mock.dialErrReturn = expectedError

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Users([]string{"test"})

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(err, ShouldEqual, expectedError)

				teardown()
			})
			Convey("Should return error for absent config list", func() {
				setup()

				multi := New([]*ldap.ServerConfig{})
				_, err := multi.Users([]string{"test"})

				So(err, ShouldBeError)
				So(err, ShouldEqual, ErrNoLDAPServers)

				teardown()
			})

			Convey("Should return a dial error", func() {
				mock := setup()

				expected := errors.New("Dial error")
				mock.dialErrReturn = expected

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})

				_, err := multi.Users([]string{"test"})

				So(err, ShouldBeError)
				So(err, ShouldEqual, expected)

				teardown()
			})

			Convey("Should call underlying LDAP methods", func() {
				mock := setup()

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Users([]string{"test"})

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(mock.usersCalledTimes, ShouldEqual, 2)
				So(mock.closeCalledTimes, ShouldEqual, 2)

				So(err, ShouldBeNil)

				teardown()
			})

			Convey("Should return some error", func() {
				mock := setup()

				expected := errors.New("Killa Gorilla")
				mock.usersErrReturn = expected

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				_, err := multi.Users([]string{"test"})

				So(mock.dialCalledTimes, ShouldEqual, 1)
				So(mock.usersCalledTimes, ShouldEqual, 1)
				So(mock.closeCalledTimes, ShouldEqual, 1)

				So(err, ShouldEqual, expected)

				teardown()
			})

			Convey("Should get users", func() {
				mock := setup()

				mock.usersFirstReturn = []*models.ExternalUserInfo{
					{
						Login: "one",
					},

					{
						Login: "two",
					},
				}

				mock.usersRestReturn = []*models.ExternalUserInfo{
					{
						Login: "three",
					},
				}

				multi := New([]*ldap.ServerConfig{
					{}, {},
				})
				users, err := multi.Users([]string{"test"})

				So(mock.dialCalledTimes, ShouldEqual, 2)
				So(mock.usersCalledTimes, ShouldEqual, 2)
				So(mock.closeCalledTimes, ShouldEqual, 2)

				So(err, ShouldBeNil)
				So(users[0].Login, ShouldEqual, "one")
				So(users[1].Login, ShouldEqual, "two")
				So(users[2].Login, ShouldEqual, "three")

				teardown()
			})
		})
	})
}

// mockLDAP represents testing struct for ldap testing
type mockLDAP struct {
	dialCalledTimes  int
	loginCalledTimes int
	closeCalledTimes int
	usersCalledTimes int
	bindCalledTimes  int

	dialErrReturn error

	loginErrReturn error
	loginReturn    *models.ExternalUserInfo

	bindErrReturn error

	usersErrReturn   error
	usersFirstReturn []*models.ExternalUserInfo
	usersRestReturn  []*models.ExternalUserInfo
}

// Login test fn
func (mock *mockLDAP) Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error) {
	mock.loginCalledTimes++
	return mock.loginReturn, mock.loginErrReturn
}

// Users test fn
func (mock *mockLDAP) Users([]string) ([]*models.ExternalUserInfo, error) {
	mock.usersCalledTimes++

	if mock.usersCalledTimes == 1 {
		return mock.usersFirstReturn, mock.usersErrReturn
	}

	return mock.usersRestReturn, mock.usersErrReturn
}

// UserBind test fn
func (mock *mockLDAP) UserBind(string, string) error {
	return nil
}

// Dial test fn
func (mock *mockLDAP) Dial() error {
	mock.dialCalledTimes++
	return mock.dialErrReturn
}

// Close test fn
func (mock *mockLDAP) Close() {
	mock.closeCalledTimes++
}

func (mock *mockLDAP) Bind() error {
	mock.bindCalledTimes++
	return mock.bindErrReturn
}

func setup() *mockLDAP {
	mock := &mockLDAP{}

	newLDAP = func(config *ldap.ServerConfig) ldap.IServer {
		return mock
	}

	return mock
}

func teardown() {
	newLDAP = ldap.New
}
<commit_msg>Chore: Replace goconvey in multildap package (#40681)<commit_after>package multildap

import (
	"errors"
	"testing"

	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/ldap"

	"github.com/stretchr/testify/require"
)

func TestMultiLDAP(t *testing.T) {
	t.Run("Ping()", func(t *testing.T) {
		t.Run("Should return error for absent config list", func(t *testing.T) {
			setup()

			multi := New([]*ldap.ServerConfig{})
			_, err := multi.Ping()

			require.Error(t, err)
			require.Equal(t, ErrNoLDAPServers, err)

			teardown()
		})
		t.Run("Should return an unavailable status on dial error", func(t *testing.T) {
			mock := setup()

			expectedErr := errors.New("Dial error")
			mock.dialErrReturn = expectedErr

			multi := New([]*ldap.ServerConfig{
				{Host: "10.0.0.1", Port: 361},
			})

			statuses, err := multi.Ping()

			require.Nil(t, err)
			require.Equal(t, "10.0.0.1", statuses[0].Host)
			require.Equal(t, 361, statuses[0].Port)
			require.False(t, statuses[0].Available)
			require.Equal(t, expectedErr, statuses[0].Error)
			require.Equal(t, 0, mock.closeCalledTimes)

			teardown()
		})
		t.Run("Should get the LDAP server statuses", func(t *testing.T) {
			mock := setup()

			multi := New([]*ldap.ServerConfig{
				{Host: "10.0.0.1", Port: 361},
			})

			statuses, err := multi.Ping()

			require.Nil(t, err)
			require.Equal(t, "10.0.0.1", statuses[0].Host)
			require.Equal(t, 361, statuses[0].Port)
			require.True(t, statuses[0].Available)
			require.Nil(t, statuses[0].Error)
			require.Equal(t, 1, mock.closeCalledTimes)

			teardown()
		})
	})
	t.Run("Login()", func(t *testing.T) {
		t.Run("Should return error for absent config list", func(t *testing.T) {
			setup()

			multi := New([]*ldap.ServerConfig{})
			_, err := multi.Login(&models.LoginUserQuery{})

			require.Error(t, err)
			require.Equal(t, ErrNoLDAPServers, err)

			teardown()
		})

		t.Run("Should return a dial error", func(t *testing.T) {
			mock := setup()

			expected := errors.New("Dial error")
			mock.dialErrReturn = expected

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})

			_, err := multi.Login(&models.LoginUserQuery{})

			require.Error(t, err)
			require.Equal(t, expected, err)

			teardown()
		})

		t.Run("Should call underlying LDAP methods", func(t *testing.T) {
			mock := setup()

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Login(&models.LoginUserQuery{})

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, 2, mock.loginCalledTimes)
			require.Equal(t, 2, mock.closeCalledTimes)

			require.Equal(t, ErrInvalidCredentials, err)

			teardown()
		})

		t.Run("Should get login result", func(t *testing.T) {
			mock := setup()

			mock.loginReturn = &models.ExternalUserInfo{
				Login: "killa",
			}

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			result, err := multi.Login(&models.LoginUserQuery{})

			require.Equal(t, 1, mock.dialCalledTimes)
			require.Equal(t, 1, mock.loginCalledTimes)
			require.Equal(t, 1, mock.closeCalledTimes)

			require.Equal(t, "killa", result.Login)
			require.Nil(t, err)

			teardown()
		})

		t.Run("Should still call a second error for invalid not found error", func(t *testing.T) {
			mock := setup()

			mock.loginErrReturn = ErrCouldNotFindUser

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Login(&models.LoginUserQuery{})

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, 2, mock.loginCalledTimes)
			require.Equal(t, 2, mock.closeCalledTimes)

			require.Equal(t, ErrInvalidCredentials, err)

			teardown()
		})

		t.Run("Should still try to auth with the second server after receiving an invalid credentials error from the first", func(t *testing.T) {
			mock := setup()

			mock.loginErrReturn = ErrInvalidCredentials

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Login(&models.LoginUserQuery{})

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, 2, mock.loginCalledTimes)
			require.Equal(t, 2, mock.closeCalledTimes)

			require.Equal(t, ErrInvalidCredentials, err)

			teardown()
		})

		t.Run("Should still try to auth with the second server after receiving a dial error from the first", func(t *testing.T) {
			mock := setup()

			expectedError := errors.New("Dial error")
			mock.dialErrReturn = expectedError

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Login(&models.LoginUserQuery{})

			require.Equal(t, 2, mock.dialCalledTimes)

			require.Equal(t, expectedError, err)

			teardown()
		})

		t.Run("Should return unknown error", func(t *testing.T) {
			mock := setup()

			expected := errors.New("Something unknown")
			mock.loginErrReturn = expected

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Login(&models.LoginUserQuery{})

			require.Equal(t, 1, mock.dialCalledTimes)
			require.Equal(t, 1, mock.loginCalledTimes)
			require.Equal(t, 1, mock.closeCalledTimes)

			require.Equal(t, expected, err)

			teardown()
		})
	})

	t.Run("User()", func(t *testing.T) {
		t.Run("Should return error for absent config list", func(t *testing.T) {
			setup()

			multi := New([]*ldap.ServerConfig{})
			_, _, err := multi.User("test")

			require.Error(t, err)
			require.Equal(t, ErrNoLDAPServers, err)

			teardown()
		})

		t.Run("Should return a dial error", func(t *testing.T) {
			mock := setup()

			expected := errors.New("Dial error")
			mock.dialErrReturn = expected

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})

			_, _, err := multi.User("test")

			require.Error(t, err)
			require.Equal(t, expected, err)

			teardown()
		})

		t.Run("Should call underlying LDAP methods", func(t *testing.T) {
			mock := setup()

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, _, err := multi.User("test")

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, 2, mock.usersCalledTimes)
			require.Equal(t, 2, mock.closeCalledTimes)

			require.Equal(t, ErrDidNotFindUser, err)

			teardown()
		})

		t.Run("Should return some error", func(t *testing.T) {
			mock := setup()

			expected := errors.New("Killa Gorilla")
			mock.usersErrReturn = expected

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, _, err := multi.User("test")

			require.Equal(t, 1, mock.dialCalledTimes)
			require.Equal(t, 1, mock.usersCalledTimes)
			require.Equal(t, 1, mock.closeCalledTimes)

			require.Equal(t, expected, err)

			teardown()
		})

		t.Run("Should get only one user", func(t *testing.T) {
			mock := setup()

			mock.usersFirstReturn = []*models.ExternalUserInfo{
				{
					Login: "one",
				},

				{
					Login: "two",
				},
			}

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			user, _, err := multi.User("test")

			require.Equal(t, 1, mock.dialCalledTimes)
			require.Equal(t, 1, mock.usersCalledTimes)
			require.Equal(t, 1, mock.closeCalledTimes)

			require.Nil(t, err)
			require.Equal(t, "one", user.Login)

			teardown()
		})

		t.Run("Should still try to auth with the second server after receiving a dial error from the first", func(t *testing.T) {
			mock := setup()

			expectedError := errors.New("Dial error")
			mock.dialErrReturn = expectedError

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, _, err := multi.User("test")

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, expectedError, err)

			teardown()
		})
	})

	t.Run("Users()", func(t *testing.T) {
		t.Run("Should still try to auth with the second server after receiving a dial error from the first", func(t *testing.T) {
			mock := setup()

			expectedError := errors.New("Dial error")
			mock.dialErrReturn = expectedError

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Users([]string{"test"})

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, expectedError, err)

			teardown()
		})
		t.Run("Should return error for absent config list", func(t *testing.T) {
			setup()

			multi := New([]*ldap.ServerConfig{})
			_, err := multi.Users([]string{"test"})

			require.Error(t, err)
			require.Equal(t, ErrNoLDAPServers, err)

			teardown()
		})

		t.Run("Should return a dial error", func(t *testing.T) {
			mock := setup()

			expected := errors.New("Dial error")
			mock.dialErrReturn = expected

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})

			_, err := multi.Users([]string{"test"})

			require.Error(t, err)
			require.Equal(t, expected, err)

			teardown()
		})

		t.Run("Should call underlying LDAP methods", func(t *testing.T) {
			mock := setup()

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Users([]string{"test"})

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, 2, mock.usersCalledTimes)
			require.Equal(t, 2, mock.closeCalledTimes)

			require.Nil(t, err)

			teardown()
		})

		t.Run("Should return some error", func(t *testing.T) {
			mock := setup()

			expected := errors.New("Killa Gorilla")
			mock.usersErrReturn = expected

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			_, err := multi.Users([]string{"test"})

			require.Equal(t, 1, mock.dialCalledTimes)
			require.Equal(t, 1, mock.usersCalledTimes)
			require.Equal(t, 1, mock.closeCalledTimes)

			require.Equal(t, expected, err)

			teardown()
		})

		t.Run("Should get users", func(t *testing.T) {
			mock := setup()

			mock.usersFirstReturn = []*models.ExternalUserInfo{
				{
					Login: "one",
				},

				{
					Login: "two",
				},
			}

			mock.usersRestReturn = []*models.ExternalUserInfo{
				{
					Login: "three",
				},
			}

			multi := New([]*ldap.ServerConfig{
				{}, {},
			})
			users, err := multi.Users([]string{"test"})

			require.Equal(t, 2, mock.dialCalledTimes)
			require.Equal(t, 2, mock.usersCalledTimes)
			require.Equal(t, 2, mock.closeCalledTimes)

			require.Nil(t, err)
			require.Equal(t, "one", users[0].Login)
			require.Equal(t, "two", users[1].Login)
			require.Equal(t, "three", users[2].Login)

			teardown()
		})
	})
}

// mockLDAP represents testing struct for ldap testing
type mockLDAP struct {
	dialCalledTimes  int
	loginCalledTimes int
	closeCalledTimes int
	usersCalledTimes int
	bindCalledTimes  int

	dialErrReturn error

	loginErrReturn error
	loginReturn    *models.ExternalUserInfo

	bindErrReturn error

	usersErrReturn   error
	usersFirstReturn []*models.ExternalUserInfo
	usersRestReturn  []*models.ExternalUserInfo
}

// Login test fn
func (mock *mockLDAP) Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error) {
	mock.loginCalledTimes++
	return mock.loginReturn, mock.loginErrReturn
}

// Users test fn
func (mock *mockLDAP) Users([]string) ([]*models.ExternalUserInfo, error) {
	mock.usersCalledTimes++

	if mock.usersCalledTimes == 1 {
		return mock.usersFirstReturn, mock.usersErrReturn
	}

	return mock.usersRestReturn, mock.usersErrReturn
}

// UserBind test fn
func (mock *mockLDAP) UserBind(string, string) error {
	return nil
}

// Dial test fn
func (mock *mockLDAP) Dial() error {
	mock.dialCalledTimes++
	return mock.dialErrReturn
}

// Close test fn
func (mock *mockLDAP) Close() {
	mock.closeCalledTimes++
}

func (mock *mockLDAP) Bind() error {
	mock.bindCalledTimes++
	return mock.bindErrReturn
}

func setup() *mockLDAP {
	mock := &mockLDAP{}

	newLDAP = func(config *ldap.ServerConfig) ldap.IServer {
		return mock
	}

	return mock
}

func teardown() {
	newLDAP = ldap.New
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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 workerqueue

import (
	"testing"
	"time"

	"io/ioutil"
	"net/http"
	"net/http/httptest"

	"github.com/heptiolabs/healthcheck"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
)

func TestWorkerQueueRun(t *testing.T) {
	t.Parallel()

	received := make(chan string)
	defer close(received)

	syncHandler := func(name string) error {
		assert.Equal(t, "default/test", name)
		received <- name
		return nil
	}

	wq := NewWorkerQueue(syncHandler, logrus.WithField("source", "test"), "test")
	stop := make(chan struct{})
	defer close(stop)

	go wq.Run(1, stop)

	// no change, should be no value
	select {
	case <-received:
		assert.Fail(t, "should not have received value")
	case <-time.After(1 * time.Second):
	}

	wq.Enqueue(cache.ExplicitKey("default/test"))

	select {
	case <-received:
	case <-time.After(5 * time.Second):
		assert.Fail(t, "should have received value")
	}
}

func TestWorkerQueueHealthy(t *testing.T) {
	t.Parallel()

	done := make(chan struct{})
	handler := func(string) error {
		<-done
		return nil
	}
	wq := NewWorkerQueue(handler, logrus.WithField("source", "test"), "test")
	wq.Enqueue(cache.ExplicitKey("default/test"))

	stop := make(chan struct{})
	go wq.Run(1, stop)

	// Yield to the scheduler to ensure the worker queue goroutine can run.
	time.Sleep(10 * time.Millisecond)
	assert.Equal(t, 1, wq.RunCount())
	assert.Nil(t, wq.Healthy())

	close(done) // Ensure the handler no longer blocks.
	close(stop) // Stop the worker queue.

	// Yield to the scheduler again to ensure the worker queue goroutine can
	// finish.
	time.Sleep(10 * time.Millisecond)
	assert.Equal(t, 0, wq.RunCount())
	assert.EqualError(t, wq.Healthy(), "want 1 worker goroutine(s), got 0")
}

func TestWorkQueueHealthCheck(t *testing.T) {
	t.Parallel()

	health := healthcheck.NewHandler()
	handler := func(string) error {
		return nil
	}
	wq := NewWorkerQueue(handler, logrus.WithField("source", "test"), "test")
	health.AddLivenessCheck("test", wq.Healthy)

	server := httptest.NewServer(health)
	defer server.Close()

	stop := make(chan struct{})
	go wq.Run(1, stop)

	url := server.URL + "/live"

	f := func(t *testing.T, url string, status int) {
		resp, err := http.Get(url)
		assert.Nil(t, err)
		defer resp.Body.Close() // nolint: errcheck

		body, err := ioutil.ReadAll(resp.Body)
		assert.Nil(t, err)
		assert.Equal(t, status, resp.StatusCode)
		assert.Equal(t, []byte("{}\n"), body)
	}

	f(t, url, http.StatusOK)

	close(stop)
	// closing can take a short while
	err := wait.PollImmediate(time.Second, 5*time.Second, func() (bool, error) {
		rc := wq.RunCount()
		logrus.WithField("runcount", rc).Info("Checking run count")
		return rc == 0, nil
	})
	assert.Nil(t, err)

	// gate
	assert.Error(t, wq.Healthy())
	f(t, url, http.StatusServiceUnavailable)
}
<commit_msg>Solve rare flakiness on TestWorkerQueueHealthy<commit_after>// Copyright 2018 Google Inc. All Rights Reserved.
//
// 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 workerqueue

import (
	"testing"
	"time"

	"io/ioutil"
	"net/http"
	"net/http/httptest"

	"github.com/heptiolabs/healthcheck"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
)

func TestWorkerQueueRun(t *testing.T) {
	t.Parallel()

	received := make(chan string)
	defer close(received)

	syncHandler := func(name string) error {
		assert.Equal(t, "default/test", name)
		received <- name
		return nil
	}

	wq := NewWorkerQueue(syncHandler, logrus.WithField("source", "test"), "test")
	stop := make(chan struct{})
	defer close(stop)

	go wq.Run(1, stop)

	// no change, should be no value
	select {
	case <-received:
		assert.Fail(t, "should not have received value")
	case <-time.After(1 * time.Second):
	}

	wq.Enqueue(cache.ExplicitKey("default/test"))

	select {
	case <-received:
	case <-time.After(5 * time.Second):
		assert.Fail(t, "should have received value")
	}
}

func TestWorkerQueueHealthy(t *testing.T) {
	t.Parallel()

	done := make(chan struct{})
	handler := func(string) error {
		<-done
		return nil
	}
	wq := NewWorkerQueue(handler, logrus.WithField("source", "test"), "test")
	wq.Enqueue(cache.ExplicitKey("default/test"))

	stop := make(chan struct{})
	go wq.Run(1, stop)

	// Yield to the scheduler to ensure the worker queue goroutine can run.
	err := wait.Poll(100*time.Millisecond, 3*time.Second, func() (done bool, err error) {
		if (wq.RunCount() == 1) && wq.Healthy() == nil {
			return true, nil
		}

		return false, nil
	})
	assert.Nil(t, err)

	close(done) // Ensure the handler no longer blocks.
	close(stop) // Stop the worker queue.

	// Yield to the scheduler again to ensure the worker queue goroutine can
	// finish.
	err = wait.Poll(100*time.Millisecond, 3*time.Second, func() (done bool, err error) {
		if (wq.RunCount() == 0) && wq.Healthy() != nil {
			return true, nil
		}

		return false, nil
	})
	assert.Nil(t, err)
}

func TestWorkQueueHealthCheck(t *testing.T) {
	t.Parallel()

	health := healthcheck.NewHandler()
	handler := func(string) error {
		return nil
	}
	wq := NewWorkerQueue(handler, logrus.WithField("source", "test"), "test")
	health.AddLivenessCheck("test", wq.Healthy)

	server := httptest.NewServer(health)
	defer server.Close()

	stop := make(chan struct{})
	go wq.Run(1, stop)

	url := server.URL + "/live"

	f := func(t *testing.T, url string, status int) {
		resp, err := http.Get(url)
		assert.Nil(t, err)
		defer resp.Body.Close() // nolint: errcheck

		body, err := ioutil.ReadAll(resp.Body)
		assert.Nil(t, err)
		assert.Equal(t, status, resp.StatusCode)
		assert.Equal(t, []byte("{}\n"), body)
	}

	f(t, url, http.StatusOK)

	close(stop)
	// closing can take a short while
	err := wait.PollImmediate(time.Second, 5*time.Second, func() (bool, error) {
		rc := wq.RunCount()
		logrus.WithField("runcount", rc).Info("Checking run count")
		return rc == 0, nil
	})
	assert.Nil(t, err)

	// gate
	assert.Error(t, wq.Healthy())
	f(t, url, http.StatusServiceUnavailable)
}
<|endoftext|>


<commit_before>package turn

import (
	"bytes"
	"errors"
	"io"
)

// ChannelData represents the ChannelData Message.
//
// See RFC 5766 Section 11.4
type ChannelData struct {
	Data   []byte // can be subslice of Raw
	Length int    // ignored while encoding, len(Data) is used
	Number ChannelNumber
	Raw    []byte
}

// Equal returns true if b == c.
func (c *ChannelData) Equal(b *ChannelData) bool {
	if c == nil && b == nil {
		return true
	}
	if c == nil || b == nil {
		return false
	}
	if c.Number != b.Number {
		return false
	}
	if len(c.Data) != len(b.Data) {
		return false
	}
	return bytes.Equal(c.Data, b.Data)
}

// grow ensures that internal buffer will fit v more bytes and
// increases it capacity if necessary.
func (c *ChannelData) grow(v int) {
	// Not performing any optimizations here
	// (e.g. preallocate len(buf) * 2 to reduce allocations)
	// because they are already done by []byte implementation.
	n := len(c.Raw) + v
	for cap(c.Raw) < n {
		c.Raw = append(c.Raw, 0)
	}
	c.Raw = c.Raw[:n]
}

// Reset resets ChannelData, data and underlying buffer length.
func (c *ChannelData) Reset() {
	c.Raw = c.Raw[:0]
	c.Length = 0
	c.Data = c.Data[:0]
}

// Encode encodes ChannelData Message to Raw.
func (c *ChannelData) Encode() {
	c.Raw = c.Raw[:0]
	c.WriteHeader()
	c.Raw = append(c.Raw, c.Data...)
}

// WriteHeader writes channel number and length.
func (c *ChannelData) WriteHeader() {
	if len(c.Raw) < channelDataHeaderSize {
		// Making WriteHeader call valid even when m.Raw
		// is nil or len(m.Raw) is less than needed for header.
		c.grow(channelDataHeaderSize)
	}
	// early bounds check to guarantee safety of writes below
	_ = c.Raw[:channelDataHeaderSize]
	bin.PutUint16(c.Raw[:channelNumberSize], uint16(c.Number))
	bin.PutUint16(c.Raw[channelNumberSize:channelDataHeaderSize],
		uint16(len(c.Data)),
	)
}

// ErrBadChannelDataLength means that channel data length is not equal
// to actual data length.
var ErrBadChannelDataLength = errors.New("channelData length != len(Data)")

// Decode decodes The ChannelData Message from Raw.
func (c *ChannelData) Decode() error {
	// Decoding message header.
	buf := c.Raw
	if len(buf) < channelDataHeaderSize {
		return io.ErrUnexpectedEOF
	}
	num := bin.Uint16(buf[0:channelNumberSize])
	c.Number = ChannelNumber(num)
	l := bin.Uint16(buf[channelNumberSize:channelDataHeaderSize])
	c.Data = buf[channelDataHeaderSize:]
	c.Length = int(l)
	if int(l) != len(buf[channelDataHeaderSize:]) {
		return ErrBadChannelDataLength
	}
	if !c.Number.Valid() {
		return ErrInvalidChannelNumber
	}
	return nil
}

const (
	channelDataLengthSize = channelNumberSize
	channelDataHeaderSize = channelNumberSize + channelDataLengthSize
)

// IsChannelData returns true if buf looks like the ChannelData Message.
func IsChannelData(buf []byte) bool {
	if len(buf) < channelDataHeaderSize {
		return false
	}
	// Quick check for channel number.
	num := bin.Uint16(buf[0:channelNumberSize])
	if !ChannelNumber(num).Valid() {
		return false
	}
	// Check that length is valid.
	l := bin.Uint16(buf[channelNumberSize:channelDataHeaderSize])
	return int(l) == len(buf[channelDataHeaderSize:])
}
<commit_msg>chandata: make comments more consistent<commit_after>package turn

import (
	"bytes"
	"errors"
	"io"
)

// ChannelData represents The ChannelData Message.
//
// See RFC 5766 Section 11.4
type ChannelData struct {
	Data   []byte // can be subslice of Raw
	Length int    // ignored while encoding, len(Data) is used
	Number ChannelNumber
	Raw    []byte
}

// Equal returns true if b == c.
func (c *ChannelData) Equal(b *ChannelData) bool {
	if c == nil && b == nil {
		return true
	}
	if c == nil || b == nil {
		return false
	}
	if c.Number != b.Number {
		return false
	}
	if len(c.Data) != len(b.Data) {
		return false
	}
	return bytes.Equal(c.Data, b.Data)
}

// grow ensures that internal buffer will fit v more bytes and
// increases it capacity if necessary.
//
// Similar to stun.Message.grow method.
func (c *ChannelData) grow(v int) {
	n := len(c.Raw) + v
	for cap(c.Raw) < n {
		c.Raw = append(c.Raw, 0)
	}
	c.Raw = c.Raw[:n]
}

// Reset resets Length, Data and Raw length.
func (c *ChannelData) Reset() {
	c.Raw = c.Raw[:0]
	c.Length = 0
	c.Data = c.Data[:0]
}

// Encode encodes ChannelData Message to Raw.
func (c *ChannelData) Encode() {
	c.Raw = c.Raw[:0]
	c.WriteHeader()
	c.Raw = append(c.Raw, c.Data...)
}

// WriteHeader writes channel number and length.
func (c *ChannelData) WriteHeader() {
	if len(c.Raw) < channelDataHeaderSize {
		// Making WriteHeader call valid even when c.Raw
		// is nil or len(c.Raw) is less than needed for header.
		c.grow(channelDataHeaderSize)
	}
	// Early bounds check to guarantee safety of writes below.
	_ = c.Raw[:channelDataHeaderSize]
	bin.PutUint16(c.Raw[:channelNumberSize], uint16(c.Number))
	bin.PutUint16(c.Raw[channelNumberSize:channelDataHeaderSize],
		uint16(len(c.Data)),
	)
}

// ErrBadChannelDataLength means that channel data length is not equal
// to actual data length.
var ErrBadChannelDataLength = errors.New("channelData length != len(Data)")

// Decode decodes The ChannelData Message from Raw.
func (c *ChannelData) Decode() error {
	buf := c.Raw
	if len(buf) < channelDataHeaderSize {
		return io.ErrUnexpectedEOF
	}
	num := bin.Uint16(buf[0:channelNumberSize])
	c.Number = ChannelNumber(num)
	l := bin.Uint16(buf[channelNumberSize:channelDataHeaderSize])
	c.Data = buf[channelDataHeaderSize:]
	c.Length = int(l)
	if int(l) != len(buf[channelDataHeaderSize:]) {
		return ErrBadChannelDataLength
	}
	if !c.Number.Valid() {
		return ErrInvalidChannelNumber
	}
	return nil
}

const (
	channelDataLengthSize = channelNumberSize
	channelDataHeaderSize = channelNumberSize + channelDataLengthSize
)

// IsChannelData returns true if buf looks like the ChannelData Message.
func IsChannelData(buf []byte) bool {
	if len(buf) < channelDataHeaderSize {
		return false
	}
	// Quick check for channel number.
	num := bin.Uint16(buf[0:channelNumberSize])
	if !ChannelNumber(num).Valid() {
		return false
	}
	// Check that length is valid.
	l := bin.Uint16(buf[channelNumberSize:channelDataHeaderSize])
	return int(l) == len(buf[channelDataHeaderSize:])
}
<|endoftext|>