<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 controller

import (
	"context"
	"fmt"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes/typed/apps/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"

	crv1 "github.com/kinvolk/habitat-operator/pkg/habitat/apis/cr/v1"
)

type HabitatController struct {
	config Config
	logger log.Logger
}

type Config struct {
	HabitatClient    *rest.RESTClient
	KubernetesClient *v1beta1.AppsV1beta1Client
	Scheme           *runtime.Scheme
}

func New(config Config, logger log.Logger) HabitatController {
	hc := HabitatController{
		config: config,
		logger: logger,
	}

	return hc
}

// Run starts a Habitat resource controller
func (hc *HabitatController) Run(ctx context.Context) error {
	level.Info(hc.logger).Log("msg", "Watching Service Group objects")

	_, err := hc.watchCustomResources(ctx)
	if err != nil {
		level.Error(hc.logger).Log("msg", "Failed to register watch for ServiceGroup resource", "err", err)
		return err
	}

	// This channel is closed when the context is canceled or times out.
	<-ctx.Done()

	// Err() contains the error, if any.
	return ctx.Err()
}

func (hc *HabitatController) watchCustomResources(ctx context.Context) (cache.Controller, error) {
	source := cache.NewListWatchFromClient(
		hc.config.HabitatClient,
		crv1.ServiceGroupResourcePlural,
		apiv1.NamespaceAll,
		fields.Everything())

	_, k8sController := cache.NewInformer(
		source,

		// The object type.
		&crv1.ServiceGroup{},

		// resyncPeriod
		// Every resyncPeriod, all resources in the cache will retrigger events.
		// Set to 0 to disable the resync.
		1*time.Minute,

		// Your custom resource event handlers.
		cache.ResourceEventHandlerFuncs{
			AddFunc:    hc.onAdd,
			UpdateFunc: hc.onUpdate,
			DeleteFunc: hc.onDelete,
		})

	// The k8sController will start processing events from the API.
	go k8sController.Run(ctx.Done())

	return k8sController, nil
}

func (hc *HabitatController) onAdd(obj interface{}) {
	sg, ok := obj.(*crv1.ServiceGroup)
	if !ok {
		level.Error(hc.logger).Log("msg", "unknown event type")
		return
	}

	level.Debug(hc.logger).Log("function", "onAdd", "msg", sg.ObjectMeta.SelfLink)

	// Validate object.
	if err := validateCustomObject(*sg); err != nil {
		if vErr, ok := err.(validationError); ok {
			level.Error(hc.logger).Log("type", "validation error", "msg", err, "key", vErr.Key)
			return
		}

		level.Error(hc.logger).Log("msg", err)
		return
	}

	level.Debug(hc.logger).Log("msg", "validated object")

	// This value needs to be passed as a *int32, so we convert it, assign it to a
	// variable and afterwards pass a pointer to it.
	count := int32(sg.Spec.Count)

	// Create a deployment.
	deployment := &appsv1beta1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name: fmt.Sprintf("%s-deployment", sg.Name),
		},
		Spec: appsv1beta1.DeploymentSpec{
			Replicas: &count,
			Template: apiv1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"habitat": "true",
					},
				},
				Spec: apiv1.PodSpec{
					Containers: []apiv1.Container{
						{
							Name:  "habitat-service",
							Image: sg.Spec.Image,
						},
					},
				},
			},
		},
	}

	result, err := hc.config.KubernetesClient.Deployments(apiv1.NamespaceDefault).Create(deployment)
	if err != nil {
		level.Error(hc.logger).Log("msg", err)
		return
	}

	level.Info(hc.logger).Log("msg", "created deployment", "name", result.GetObjectMeta().GetName())
}

func (hc *HabitatController) onUpdate(oldObj, newObj interface{}) {
	oldServiceGroup := oldObj.(*crv1.ServiceGroup)
	newServiceGroup := newObj.(*crv1.ServiceGroup)
	level.Info(hc.logger).Log("function", "onUpdate", "msg", fmt.Sprintf("oldObj: %s, newObj: %s", oldServiceGroup.ObjectMeta.SelfLink, newServiceGroup.ObjectMeta.SelfLink))
}

func (hc *HabitatController) onDelete(obj interface{}) {
	sg, ok := obj.(*crv1.ServiceGroup)
	if !ok {
		level.Error(hc.logger).Log("msg", "unknown event type")
		return
	}

	level.Debug(hc.logger).Log("function", "onDelete", "msg", sg.ObjectMeta.SelfLink)

	deploymentsClient := hc.config.KubernetesClient.Deployments(sg.ObjectMeta.Namespace)
	deploymentName := fmt.Sprintf("%s-deployment", sg.Name)
	deletePolicy := metav1.DeletePropagationForeground
	deleteOptions := &metav1.DeleteOptions{
		PropagationPolicy: &deletePolicy,
	}

	err := deploymentsClient.Delete(deploymentName, deleteOptions)
	if err != nil {
		level.Error(hc.logger).Log("msg", err)
		return
	}

	level.Info(hc.logger).Log("msg", "deleted deployment", "name", deploymentName)
}
<commit_msg>Do not add redundant suffix to deployment<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 controller

import (
	"context"
	"fmt"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes/typed/apps/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"

	crv1 "github.com/kinvolk/habitat-operator/pkg/habitat/apis/cr/v1"
)

type HabitatController struct {
	config Config
	logger log.Logger
}

type Config struct {
	HabitatClient    *rest.RESTClient
	KubernetesClient *v1beta1.AppsV1beta1Client
	Scheme           *runtime.Scheme
}

func New(config Config, logger log.Logger) HabitatController {
	hc := HabitatController{
		config: config,
		logger: logger,
	}

	return hc
}

// Run starts a Habitat resource controller
func (hc *HabitatController) Run(ctx context.Context) error {
	level.Info(hc.logger).Log("msg", "Watching Service Group objects")

	_, err := hc.watchCustomResources(ctx)
	if err != nil {
		level.Error(hc.logger).Log("msg", "Failed to register watch for ServiceGroup resource", "err", err)
		return err
	}

	// This channel is closed when the context is canceled or times out.
	<-ctx.Done()

	// Err() contains the error, if any.
	return ctx.Err()
}

func (hc *HabitatController) watchCustomResources(ctx context.Context) (cache.Controller, error) {
	source := cache.NewListWatchFromClient(
		hc.config.HabitatClient,
		crv1.ServiceGroupResourcePlural,
		apiv1.NamespaceAll,
		fields.Everything())

	_, k8sController := cache.NewInformer(
		source,

		// The object type.
		&crv1.ServiceGroup{},

		// resyncPeriod
		// Every resyncPeriod, all resources in the cache will retrigger events.
		// Set to 0 to disable the resync.
		1*time.Minute,

		// Your custom resource event handlers.
		cache.ResourceEventHandlerFuncs{
			AddFunc:    hc.onAdd,
			UpdateFunc: hc.onUpdate,
			DeleteFunc: hc.onDelete,
		})

	// The k8sController will start processing events from the API.
	go k8sController.Run(ctx.Done())

	return k8sController, nil
}

func (hc *HabitatController) onAdd(obj interface{}) {
	sg, ok := obj.(*crv1.ServiceGroup)
	if !ok {
		level.Error(hc.logger).Log("msg", "unknown event type")
		return
	}

	level.Debug(hc.logger).Log("function", "onAdd", "msg", sg.ObjectMeta.SelfLink)

	// Validate object.
	if err := validateCustomObject(*sg); err != nil {
		if vErr, ok := err.(validationError); ok {
			level.Error(hc.logger).Log("type", "validation error", "msg", err, "key", vErr.Key)
			return
		}

		level.Error(hc.logger).Log("msg", err)
		return
	}

	level.Debug(hc.logger).Log("msg", "validated object")

	// This value needs to be passed as a *int32, so we convert it, assign it to a
	// variable and afterwards pass a pointer to it.
	count := int32(sg.Spec.Count)

	// Create a deployment.
	deployment := &appsv1beta1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name: fmt.Sprintf("%s-deployment", sg.Name),
		},
		Spec: appsv1beta1.DeploymentSpec{
			Replicas: &count,
			Template: apiv1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"habitat": "true",
					},
				},
				Spec: apiv1.PodSpec{
					Containers: []apiv1.Container{
						{
							Name:  "habitat-service",
							Image: sg.Spec.Image,
						},
					},
				},
			},
		},
	}

	result, err := hc.config.KubernetesClient.Deployments(apiv1.NamespaceDefault).Create(deployment)
	if err != nil {
		level.Error(hc.logger).Log("msg", err)
		return
	}

	level.Info(hc.logger).Log("msg", "created deployment", "name", result.GetObjectMeta().GetName())
}

func (hc *HabitatController) onUpdate(oldObj, newObj interface{}) {
	oldServiceGroup := oldObj.(*crv1.ServiceGroup)
	newServiceGroup := newObj.(*crv1.ServiceGroup)
	level.Info(hc.logger).Log("function", "onUpdate", "msg", fmt.Sprintf("oldObj: %s, newObj: %s", oldServiceGroup.ObjectMeta.SelfLink, newServiceGroup.ObjectMeta.SelfLink))
}

func (hc *HabitatController) onDelete(obj interface{}) {
	sg, ok := obj.(*crv1.ServiceGroup)
	if !ok {
		level.Error(hc.logger).Log("msg", "unknown event type")
		return
	}

	level.Debug(hc.logger).Log("function", "onDelete", "msg", sg.ObjectMeta.SelfLink)

	deploymentsClient := hc.config.KubernetesClient.Deployments(sg.ObjectMeta.Namespace)
	deploymentName := sg.Name
	deletePolicy := metav1.DeletePropagationForeground
	deleteOptions := &metav1.DeleteOptions{
		PropagationPolicy: &deletePolicy,
	}

	err := deploymentsClient.Delete(deploymentName, deleteOptions)
	if err != nil {
		level.Error(hc.logger).Log("msg", err)
		return
	}

	level.Info(hc.logger).Log("msg", "deleted deployment", "name", deploymentName)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package version

// ensure that git has a tag: "vX.Y" corresponding to major and minor
const (
	Major   = "3"
	Minor   = "25"
	Version = Major + "." + Minor
)
<commit_msg>[version] update for new timeouts<commit_after>// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package version

// ensure that git has a tag: "vX.Y" corresponding to major and minor
const (
	Major   = "3"
	Minor   = "26"
	Version = Major + "." + Minor
)
<|endoftext|>


<commit_before>package r2router

import (
	//"fmt"
	"net/http"
	"time"
)

// Before defines how a middleware should look like.
// Before middlewares are for handling request before routing.
// Before middlewares are executed in the order they were inserted.
// A middleware can choose to response to a request and not call next
// for continue with the next middleware/handler
type Before func(w http.ResponseWriter, req *http.Request, next func())

// After defines how a middleware should look like.
// After middlewares are for handling request after routing.
// After middlewares are executed in the order they were inserted.
// A middleware can choose to response to a request and not call next
// for continue with the next middleware/handler
type After func(w http.ResponseWriter, req *http.Request, params Params, next func())

// Seefor is a subtype of Router.
// It supports a simple middleware layers.
// Middlewares are always executed before handler,
// no matter where or when they are added.
// And middlewares are executed in the order they were inserted.
type Seefor struct {
	Router
	befores []Before
	afters  []After
	timer   *Timer
}

// NewSeeforRouter for creating a new instance of Seefor router
func NewSeeforRouter() *Seefor {
	c4 := &Seefor{}
	c4.afters = make([]After, 0)
	c4.befores = make([]Before, 0)
	c4.roots = make(map[string]*rootNode)
	c4.HandleMethodNotAllowed = true
	return c4
}

// Implementing http handler interface.
// This is a override of Router.ServeHTTP for handling middlewares
func (c4 *Seefor) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	before := time.Now()
	c4.handleBeforeMiddlewares(w, req, func() {
		if root, exist := c4.roots[req.Method]; exist {
			handler, params, route := root.match(req.URL.Path)
			if handler != nil {
				if c4.timer != nil {
					c4.timeit(route, before, handler, w, req, params)
				} else {
					c4.handleAfterMiddlewares(handler, w, req, params)
				}
				return
			}
		}
		c4.Router.handleMissing(w, req)
	})
}

func (c4 *Seefor) handleBeforeMiddlewares(w http.ResponseWriter, req *http.Request, nextHandler func()) {
	max := len(c4.befores)
	if max == 0 {
		nextHandler()
		return
	}
	var next func()
	counter := 0
	next = func() {
		if counter >= max {
			nextHandler()
			return
		}
		middleware := c4.befores[counter]
		counter += 1
		middleware(w, req, next)
	}
	next()
}

func (c4 *Seefor) timeit(route string, before time.Time, handler HandlerFunc, w http.ResponseWriter, req *http.Request, params Params) {
	after := time.Now()
	c4.handleAfterMiddlewares(handler, w, req, params)
	c4.timer.Get(route).Accumulate(before, after, time.Now())
}

func (c4 *Seefor) handleAfterMiddlewares(handler HandlerFunc, w http.ResponseWriter, req *http.Request, params Params) {
	var next func()

	max := len(c4.afters)
	if max == 0 {
		handler(w, req, params)
		return
	}

	counter := 0
	next = func() {
		if counter >= max {
			handler(w, req, params)
			return
		}
		middleware := c4.afters[counter]
		counter += 1
		middleware(w, req, params, next)
	}
	next()
}

// Before is for adding middleware for running before routing
func (c4 *Seefor) Before(middleware ...Before) {
	c4.befores = append(c4.befores, middleware...)
}

// After is for adding middleware for running after
func (c4 *Seefor) After(middleware ...After) {
	c4.afters = append(c4.afters, middleware...)
}

// Wrap for wrapping a handler to After middleware
// Be aware that it will not be able to stop execution propagation
// That is it will continue to execute the next middleware/handler
func Wrap(handler HandlerFunc) After {
	return func(w http.ResponseWriter, req *http.Request, params Params, next func()) {
		handler(w, req, params)
		next()
	}
}

// WrapHandler for wrapping a http.Handler to After middleware.
// Be aware that it will not be able to stop execution propagation
// That is it will continue to execute the next middleware/handler
func WrapHandler(handler http.Handler) After {
	return func(w http.ResponseWriter, req *http.Request, _ Params, next func()) {
		handler.ServeHTTP(w, req)
		next()
	}
}

// WrapBeforeHandler for wrapping a http.Handler to Before middleware.
// Be aware that it will not be able to stop execution propagation
// That is it will continue to execute the next middleware/handler
func WrapBeforeHandler(handler http.Handler) Before {
	return func(w http.ResponseWriter, req *http.Request, next func()) {
		handler.ServeHTTP(w, req)
		next()
	}
}

// UseTimer set timer for meaturing endpoint performance.
// If timer is nil then a new timer will be created.
// You can serve statistics internal using Timer as handler
func (c4 *Seefor) UseTimer(timer *Timer) *Timer {
	if timer == nil {
		timer = NewTimer()
	}
	c4.timer = timer

	return c4.timer
}
<commit_msg>Adding shortcut for map[string]interface<commit_after>package r2router

import (
	//"fmt"
	"net/http"
	"time"
)

// Shortcut for map[string]interface{}
// Helpful to build data for json response
type M map[string]interface{}

// Before defines how a middleware should look like.
// Before middlewares are for handling request before routing.
// Before middlewares are executed in the order they were inserted.
// A middleware can choose to response to a request and not call next
// for continue with the next middleware/handler
type Before func(w http.ResponseWriter, req *http.Request, next func())

// After defines how a middleware should look like.
// After middlewares are for handling request after routing.
// After middlewares are executed in the order they were inserted.
// A middleware can choose to response to a request and not call next
// for continue with the next middleware/handler
type After func(w http.ResponseWriter, req *http.Request, params Params, next func())

// Seefor is a subtype of Router.
// It supports a simple middleware layers.
// Middlewares are always executed before handler,
// no matter where or when they are added.
// And middlewares are executed in the order they were inserted.
type Seefor struct {
	Router
	befores []Before
	afters  []After
	timer   *Timer
}

// NewSeeforRouter for creating a new instance of Seefor router
func NewSeeforRouter() *Seefor {
	c4 := &Seefor{}
	c4.afters = make([]After, 0)
	c4.befores = make([]Before, 0)
	c4.roots = make(map[string]*rootNode)
	c4.HandleMethodNotAllowed = true
	return c4
}

// Implementing http handler interface.
// This is a override of Router.ServeHTTP for handling middlewares
func (c4 *Seefor) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	before := time.Now()
	c4.handleBeforeMiddlewares(w, req, func() {
		if root, exist := c4.roots[req.Method]; exist {
			handler, params, route := root.match(req.URL.Path)
			if handler != nil {
				if c4.timer != nil {
					c4.timeit(route, before, handler, w, req, params)
				} else {
					c4.handleAfterMiddlewares(handler, w, req, params)
				}
				return
			}
		}
		c4.Router.handleMissing(w, req)
	})
}

func (c4 *Seefor) handleBeforeMiddlewares(w http.ResponseWriter, req *http.Request, nextHandler func()) {
	max := len(c4.befores)
	if max == 0 {
		nextHandler()
		return
	}
	var next func()
	counter := 0
	next = func() {
		if counter >= max {
			nextHandler()
			return
		}
		middleware := c4.befores[counter]
		counter += 1
		middleware(w, req, next)
	}
	next()
}

func (c4 *Seefor) timeit(route string, before time.Time, handler HandlerFunc, w http.ResponseWriter, req *http.Request, params Params) {
	after := time.Now()
	c4.handleAfterMiddlewares(handler, w, req, params)
	c4.timer.Get(route).Accumulate(before, after, time.Now())
}

func (c4 *Seefor) handleAfterMiddlewares(handler HandlerFunc, w http.ResponseWriter, req *http.Request, params Params) {
	var next func()

	max := len(c4.afters)
	if max == 0 {
		handler(w, req, params)
		return
	}

	counter := 0
	next = func() {
		if counter >= max {
			handler(w, req, params)
			return
		}
		middleware := c4.afters[counter]
		counter += 1
		middleware(w, req, params, next)
	}
	next()
}

// Before is for adding middleware for running before routing
func (c4 *Seefor) Before(middleware ...Before) {
	c4.befores = append(c4.befores, middleware...)
}

// After is for adding middleware for running after
func (c4 *Seefor) After(middleware ...After) {
	c4.afters = append(c4.afters, middleware...)
}

// Wrap for wrapping a handler to After middleware
// Be aware that it will not be able to stop execution propagation
// That is it will continue to execute the next middleware/handler
func Wrap(handler HandlerFunc) After {
	return func(w http.ResponseWriter, req *http.Request, params Params, next func()) {
		handler(w, req, params)
		next()
	}
}

// WrapHandler for wrapping a http.Handler to After middleware.
// Be aware that it will not be able to stop execution propagation
// That is it will continue to execute the next middleware/handler
func WrapHandler(handler http.Handler) After {
	return func(w http.ResponseWriter, req *http.Request, _ Params, next func()) {
		handler.ServeHTTP(w, req)
		next()
	}
}

// WrapBeforeHandler for wrapping a http.Handler to Before middleware.
// Be aware that it will not be able to stop execution propagation
// That is it will continue to execute the next middleware/handler
func WrapBeforeHandler(handler http.Handler) Before {
	return func(w http.ResponseWriter, req *http.Request, next func()) {
		handler.ServeHTTP(w, req)
		next()
	}
}

// UseTimer set timer for meaturing endpoint performance.
// If timer is nil then a new timer will be created.
// You can serve statistics internal using Timer as handler
func (c4 *Seefor) UseTimer(timer *Timer) *Timer {
	if timer == nil {
		timer = NewTimer()
	}
	c4.timer = timer

	return c4.timer
}
<|endoftext|>


<commit_before>package sentry

import (
	"github.com/getsentry/raven-go"
	"github.com/gocraft/health"
)

// Sink emits errors to Sentry.
type Sink struct {
	Config *Config
	raven  *raven.Client
}

type cmdEventErr struct {
	Job   string
	Event string
	Err   *health.UnmutedError
	Kvs   map[string]string
}

// Config is used to configure Sentry sink.
type Config struct {
	// Application's Sentry URL.
	URL string
	// Only send errors if set.
	ErrorsOnly bool
}

// NewSink creates and returns new Sentry sink
// configured by given config.
func NewSink(config *Config) (*Sink, error) {
	const maxChanSize = 25

	epRaven, err := raven.NewClient(config.URL, nil)
	if err != nil {
		return nil, err
	}
	s := &Sink{
		Config: config,
		raven:  epRaven,
	}

	return s, nil
}

func (s *Sink) EmitEvent(job string, event string, kvs map[string]string) {
	// Ignore events if ErrorsOnly is set
	if s.Config.ErrorsOnly {
		return
	}
	packet := raven.NewPacket(job)
	packet.Level = raven.INFO
	kvs["event"] = event
	s.raven.Capture(packet, kvs)
}

func (s *Sink) EmitEventErr(job string, event string, inputErr error, kvs map[string]string) {
	switch inputErr := inputErr.(type) {
	case *health.UnmutedError:
		if !inputErr.Emitted {
			packet := raven.NewPacket(job, raven.NewException((inputErr), raven.NewStacktrace(2, 3, nil)))
			s.raven.Capture(packet, kvs)
		}
	case *health.MutedError:
		// Do nothing!
	default: // eg, case error:
		// This shouldn't happen, all errors passed in here should be wrapped.
	}
}

func (s *Sink) EmitTiming(job string, event string, nanos int64, kvs map[string]string) {
	// no-op
}

func (s *Sink) EmitComplete(job string, status health.CompletionStatus, nanos int64, kvs map[string]string) {
	// no-op
}

func (s *Sink) ShutdownServer() {
	s.raven.Close()
}
<commit_msg>Package name adjusted for clearance<commit_after>package health_sentry

import (
	"github.com/getsentry/raven-go"
	"github.com/gocraft/health"
)

// Sink emits errors to Sentry.
type Sink struct {
	Config *Config
	raven  *raven.Client
}

type cmdEventErr struct {
	Job   string
	Event string
	Err   *health.UnmutedError
	Kvs   map[string]string
}

// Config is used to configure Sentry sink.
type Config struct {
	// Application's Sentry URL.
	URL string
	// Only send errors if set.
	ErrorsOnly bool
}

// NewSink creates and returns new Sentry sink
// configured by given config.
func NewSink(config *Config) (*Sink, error) {
	const maxChanSize = 25

	epRaven, err := raven.NewClient(config.URL, nil)
	if err != nil {
		return nil, err
	}
	s := &Sink{
		Config: config,
		raven:  epRaven,
	}

	return s, nil
}

func (s *Sink) EmitEvent(job string, event string, kvs map[string]string) {
	// Ignore events if ErrorsOnly is set
	if s.Config.ErrorsOnly {
		return
	}
	packet := raven.NewPacket(job)
	packet.Level = raven.INFO
	kvs["event"] = event
	s.raven.Capture(packet, kvs)
}

func (s *Sink) EmitEventErr(job string, event string, inputErr error, kvs map[string]string) {
	switch inputErr := inputErr.(type) {
	case *health.UnmutedError:
		if !inputErr.Emitted {
			packet := raven.NewPacket(job, raven.NewException((inputErr), raven.NewStacktrace(2, 3, nil)))
			s.raven.Capture(packet, kvs)
		}
	case *health.MutedError:
		// Do nothing!
	default: // eg, case error:
		// This shouldn't happen, all errors passed in here should be wrapped.
	}
}

func (s *Sink) EmitTiming(job string, event string, nanos int64, kvs map[string]string) {
	// no-op
}

func (s *Sink) EmitComplete(job string, status health.CompletionStatus, nanos int64, kvs map[string]string) {
	// no-op
}

func (s *Sink) ShutdownServer() {
	s.raven.Close()
}
<|endoftext|>


<commit_before>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tabletserver

import (
	"sync"
	"time"

	log "github.com/golang/glog"

	"github.com/youtube/vitess/go/stats"
	"github.com/youtube/vitess/go/sync2"
	"github.com/youtube/vitess/go/vt/dbconfigs"
	"github.com/youtube/vitess/go/vt/dbconnpool"
	"github.com/youtube/vitess/go/vt/logutil"
	"github.com/youtube/vitess/go/vt/mysqlctl"
)

const (
	MAX_RESULT_NAME                = "_vtMaxResultSize"
	ROWCACHE_INVALIDATION_POSITION = "ROWCACHE_INVALIDATION_POSITION"

	// SPOT_CHECK_MULTIPLIER determines the precision of the
	// spot check ratio: 1e6 == 6 digits
	SPOT_CHECK_MULTIPLIER = 1e6
)

// QueryEngine implements the core functionality of tabletserver.
// It assumes that no requests will be sent to it before Open is
// called and succeeds.
// Shutdown is done in the following order:
//
// WaitForTxEmpty: There should be no more new calls to Begin
// once this function is called. This will return when there
// are no more pending transactions.
//
// Close: There should be no more pending queries when this
// function is called.
//
// Functions of QueryEngine do not return errors. They instead
// panic with NewTabletError as the error type.
// TODO(sougou): Switch to error return scheme.
type QueryEngine struct {
	schemaInfo *SchemaInfo
	dbconfig   *dbconfigs.DBConfig

	// Pools
	cachePool      *CachePool
	connPool       *dbconnpool.ConnectionPool
	streamConnPool *dbconnpool.ConnectionPool

	// Services
	txPool       *TxPool
	consolidator *Consolidator
	invalidator  *RowcacheInvalidator
	streamQList  *QueryList
	connKiller   *ConnectionKiller
	tasks        sync.WaitGroup

	// Vars
	queryTimeout     sync2.AtomicDuration
	spotCheckFreq    sync2.AtomicInt64
	strictMode       sync2.AtomicInt64
	maxResultSize    sync2.AtomicInt64
	streamBufferSize sync2.AtomicInt64
	strictTableAcl   bool

	// loggers
	accessCheckerLogger *logutil.ThrottledLogger
}

type compiledPlan struct {
	Query string
	*ExecPlan
	BindVars      map[string]interface{}
	TransactionID int64
}

var (
	// stats are globals to allow anybody to set them
	mysqlStats     *stats.Timings
	queryStats     *stats.Timings
	waitStats      *stats.Timings
	killStats      *stats.Counters
	infoErrors     *stats.Counters
	errorStats     *stats.Counters
	internalErrors *stats.Counters
	resultStats    *stats.Histogram
	spotCheckCount *stats.Int
	QPSRates       *stats.Rates

	resultBuckets = []int64{0, 1, 5, 10, 50, 100, 500, 1000, 5000, 10000}

	connPoolClosedErr = NewTabletError(FATAL, "connection pool is closed")
)

// CacheInvalidator provides the abstraction needed for an instant invalidation
// vs. delayed invalidation in the case of in-transaction dmls
type CacheInvalidator interface {
	Delete(key string)
}

// Helper method for conn pools to convert errors
func getOrPanic(pool *dbconnpool.ConnectionPool) dbconnpool.PoolConnection {
	conn, err := pool.Get()
	if err == nil {
		return conn
	}
	if err == dbconnpool.CONN_POOL_CLOSED_ERR {
		panic(connPoolClosedErr)
	}
	panic(NewTabletErrorSql(FATAL, err))
}

// NewQueryEngine creates a new QueryEngine.
// This is a singleton class.
// You must call this only once.
func NewQueryEngine(config Config) *QueryEngine {
	qe := &QueryEngine{}
	qe.schemaInfo = NewSchemaInfo(config.QueryCacheSize, time.Duration(config.SchemaReloadTime*1e9), time.Duration(config.IdleTimeout*1e9))

	mysqlStats = stats.NewTimings("Mysql")

	// Pools
	qe.cachePool = NewCachePool("Rowcache", config.RowCache, time.Duration(config.QueryTimeout*1e9), time.Duration(config.IdleTimeout*1e9))
	qe.connPool = dbconnpool.NewConnectionPool("ConnPool", config.PoolSize, time.Duration(config.IdleTimeout*1e9))
	qe.streamConnPool = dbconnpool.NewConnectionPool("StreamConnPool", config.StreamPoolSize, time.Duration(config.IdleTimeout*1e9))

	// Services
	qe.txPool = NewTxPool("TransactionPool", config.TransactionCap, time.Duration(config.TransactionTimeout*1e9), time.Duration(config.IdleTimeout*1e9))
	qe.connKiller = NewConnectionKiller(1, time.Duration(config.IdleTimeout*1e9))
	qe.consolidator = NewConsolidator()
	qe.invalidator = NewRowcacheInvalidator(qe)
	qe.streamQList = NewQueryList(qe.connKiller)

	// Vars
	qe.queryTimeout.Set(time.Duration(config.QueryTimeout * 1e9))
	qe.spotCheckFreq = sync2.AtomicInt64(config.SpotCheckRatio * SPOT_CHECK_MULTIPLIER)
	if config.StrictMode {
		qe.strictMode.Set(1)
	}
	qe.strictTableAcl = config.StrictTableAcl
	qe.maxResultSize = sync2.AtomicInt64(config.MaxResultSize)
	qe.streamBufferSize = sync2.AtomicInt64(config.StreamBufferSize)

	// loggers
	qe.accessCheckerLogger = logutil.NewThrottledLogger("accessChecker", 1*time.Second)

	// Stats
	stats.Publish("MaxResultSize", stats.IntFunc(qe.maxResultSize.Get))
	stats.Publish("StreamBufferSize", stats.IntFunc(qe.streamBufferSize.Get))
	stats.Publish("QueryTimeout", stats.DurationFunc(qe.queryTimeout.Get))
	queryStats = stats.NewTimings("Queries")
	QPSRates = stats.NewRates("QPS", queryStats, 15, 60*time.Second)
	waitStats = stats.NewTimings("Waits")
	killStats = stats.NewCounters("Kills")
	infoErrors = stats.NewCounters("InfoErrors")
	errorStats = stats.NewCounters("Errors")
	internalErrors = stats.NewCounters("InternalErrors")
	resultStats = stats.NewHistogram("Results", resultBuckets)
	stats.Publish("RowcacheSpotCheckRatio", stats.FloatFunc(func() float64 {
		return float64(qe.spotCheckFreq.Get()) / SPOT_CHECK_MULTIPLIER
	}))
	spotCheckCount = stats.NewInt("RowcacheSpotCheckCount")

	return qe
}

// Open must be called before sending requests to QueryEngine.
func (qe *QueryEngine) Open(dbconfig *dbconfigs.DBConfig, schemaOverrides []SchemaOverride, qrs *QueryRules, mysqld *mysqlctl.Mysqld) {
	qe.dbconfig = dbconfig
	connFactory := dbconnpool.DBConnectionCreator(&dbconfig.ConnectionParams, mysqlStats)

	strictMode := false
	if qe.strictMode.Get() != 0 {
		strictMode = true
	}
	if !strictMode && dbconfig.EnableRowcache {
		panic(NewTabletError(FATAL, "Rowcache cannot be enabled when queryserver-config-strict-mode is false"))
	}
	if dbconfig.EnableRowcache {
		qe.cachePool.Open()
		log.Infof("rowcache is enabled")
	} else {
		// Invalidator should not be enabled if rowcache is not enabled.
		dbconfig.EnableInvalidator = false
		log.Infof("rowcache is not enabled")
	}

	start := time.Now()
	// schemaInfo depends on cachePool. Every table that has a rowcache
	// points to the cachePool.
	qe.schemaInfo.Open(connFactory, schemaOverrides, qe.cachePool, qrs, strictMode)
	log.Infof("Time taken to load the schema: %v", time.Now().Sub(start))

	// Start the invalidator only after schema is loaded.
	// This will allow qe to find the table info
	// for the invalidation events that will start coming
	// immediately.
	if dbconfig.EnableInvalidator {
		qe.invalidator.Open(dbconfig.DbName, mysqld)
	}
	qe.connPool.Open(connFactory)
	qe.streamConnPool.Open(connFactory)
	qe.txPool.Open(connFactory)
	qe.connKiller.Open(connFactory)
}

// Launch launches the specified function inside a goroutine.
// If Close or WaitForTxEmpty is called while a goroutine is running,
// QueryEngine will not return until the existing functions have completed.
// This functionality allows us to launch tasks with the assurance that
// the QueryEngine will not be closed underneath us.
func (qe *QueryEngine) Launch(f func()) {
	qe.tasks.Add(1)
	go func() {
		defer func() {
			qe.tasks.Done()
			internalErrors.Add("Task", 1)
			if x := recover(); x != nil {
				log.Errorf("task error: %v", x)
			}
		}()
		f()
	}()
}

// WaitForTxEmpty must be called before calling Close.
// Before calling WaitForTxEmpty, you must ensure that there
// will be no more calls to Begin.
func (qe *QueryEngine) WaitForTxEmpty() {
	qe.txPool.WaitForEmpty()
	qe.tasks.Wait()
}

// Close must be called to shut down QueryEngine.
// You must ensure that no more queries will be sent
// before calling Close.
func (qe *QueryEngine) Close() {
	// Close in reverse order of Open.
	qe.connKiller.Close()
	qe.txPool.Close()
	qe.streamConnPool.Close()
	qe.connPool.Close()
	qe.invalidator.Close()
	qe.schemaInfo.Close()
	qe.cachePool.Close()
	qe.dbconfig = nil
}
<commit_msg>tabletserver: move taks.Wait to Close.<commit_after>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tabletserver

import (
	"sync"
	"time"

	log "github.com/golang/glog"

	"github.com/youtube/vitess/go/stats"
	"github.com/youtube/vitess/go/sync2"
	"github.com/youtube/vitess/go/vt/dbconfigs"
	"github.com/youtube/vitess/go/vt/dbconnpool"
	"github.com/youtube/vitess/go/vt/logutil"
	"github.com/youtube/vitess/go/vt/mysqlctl"
)

const (
	MAX_RESULT_NAME                = "_vtMaxResultSize"
	ROWCACHE_INVALIDATION_POSITION = "ROWCACHE_INVALIDATION_POSITION"

	// SPOT_CHECK_MULTIPLIER determines the precision of the
	// spot check ratio: 1e6 == 6 digits
	SPOT_CHECK_MULTIPLIER = 1e6
)

// QueryEngine implements the core functionality of tabletserver.
// It assumes that no requests will be sent to it before Open is
// called and succeeds.
// Shutdown is done in the following order:
//
// WaitForTxEmpty: There should be no more new calls to Begin
// once this function is called. This will return when there
// are no more pending transactions.
//
// Close: There should be no more pending queries when this
// function is called.
//
// Functions of QueryEngine do not return errors. They instead
// panic with NewTabletError as the error type.
// TODO(sougou): Switch to error return scheme.
type QueryEngine struct {
	schemaInfo *SchemaInfo
	dbconfig   *dbconfigs.DBConfig

	// Pools
	cachePool      *CachePool
	connPool       *dbconnpool.ConnectionPool
	streamConnPool *dbconnpool.ConnectionPool

	// Services
	txPool       *TxPool
	consolidator *Consolidator
	invalidator  *RowcacheInvalidator
	streamQList  *QueryList
	connKiller   *ConnectionKiller
	tasks        sync.WaitGroup

	// Vars
	queryTimeout     sync2.AtomicDuration
	spotCheckFreq    sync2.AtomicInt64
	strictMode       sync2.AtomicInt64
	maxResultSize    sync2.AtomicInt64
	streamBufferSize sync2.AtomicInt64
	strictTableAcl   bool

	// loggers
	accessCheckerLogger *logutil.ThrottledLogger
}

type compiledPlan struct {
	Query string
	*ExecPlan
	BindVars      map[string]interface{}
	TransactionID int64
}

var (
	// stats are globals to allow anybody to set them
	mysqlStats     *stats.Timings
	queryStats     *stats.Timings
	waitStats      *stats.Timings
	killStats      *stats.Counters
	infoErrors     *stats.Counters
	errorStats     *stats.Counters
	internalErrors *stats.Counters
	resultStats    *stats.Histogram
	spotCheckCount *stats.Int
	QPSRates       *stats.Rates

	resultBuckets = []int64{0, 1, 5, 10, 50, 100, 500, 1000, 5000, 10000}

	connPoolClosedErr = NewTabletError(FATAL, "connection pool is closed")
)

// CacheInvalidator provides the abstraction needed for an instant invalidation
// vs. delayed invalidation in the case of in-transaction dmls
type CacheInvalidator interface {
	Delete(key string)
}

// Helper method for conn pools to convert errors
func getOrPanic(pool *dbconnpool.ConnectionPool) dbconnpool.PoolConnection {
	conn, err := pool.Get()
	if err == nil {
		return conn
	}
	if err == dbconnpool.CONN_POOL_CLOSED_ERR {
		panic(connPoolClosedErr)
	}
	panic(NewTabletErrorSql(FATAL, err))
}

// NewQueryEngine creates a new QueryEngine.
// This is a singleton class.
// You must call this only once.
func NewQueryEngine(config Config) *QueryEngine {
	qe := &QueryEngine{}
	qe.schemaInfo = NewSchemaInfo(config.QueryCacheSize, time.Duration(config.SchemaReloadTime*1e9), time.Duration(config.IdleTimeout*1e9))

	mysqlStats = stats.NewTimings("Mysql")

	// Pools
	qe.cachePool = NewCachePool("Rowcache", config.RowCache, time.Duration(config.QueryTimeout*1e9), time.Duration(config.IdleTimeout*1e9))
	qe.connPool = dbconnpool.NewConnectionPool("ConnPool", config.PoolSize, time.Duration(config.IdleTimeout*1e9))
	qe.streamConnPool = dbconnpool.NewConnectionPool("StreamConnPool", config.StreamPoolSize, time.Duration(config.IdleTimeout*1e9))

	// Services
	qe.txPool = NewTxPool("TransactionPool", config.TransactionCap, time.Duration(config.TransactionTimeout*1e9), time.Duration(config.IdleTimeout*1e9))
	qe.connKiller = NewConnectionKiller(1, time.Duration(config.IdleTimeout*1e9))
	qe.consolidator = NewConsolidator()
	qe.invalidator = NewRowcacheInvalidator(qe)
	qe.streamQList = NewQueryList(qe.connKiller)

	// Vars
	qe.queryTimeout.Set(time.Duration(config.QueryTimeout * 1e9))
	qe.spotCheckFreq = sync2.AtomicInt64(config.SpotCheckRatio * SPOT_CHECK_MULTIPLIER)
	if config.StrictMode {
		qe.strictMode.Set(1)
	}
	qe.strictTableAcl = config.StrictTableAcl
	qe.maxResultSize = sync2.AtomicInt64(config.MaxResultSize)
	qe.streamBufferSize = sync2.AtomicInt64(config.StreamBufferSize)

	// loggers
	qe.accessCheckerLogger = logutil.NewThrottledLogger("accessChecker", 1*time.Second)

	// Stats
	stats.Publish("MaxResultSize", stats.IntFunc(qe.maxResultSize.Get))
	stats.Publish("StreamBufferSize", stats.IntFunc(qe.streamBufferSize.Get))
	stats.Publish("QueryTimeout", stats.DurationFunc(qe.queryTimeout.Get))
	queryStats = stats.NewTimings("Queries")
	QPSRates = stats.NewRates("QPS", queryStats, 15, 60*time.Second)
	waitStats = stats.NewTimings("Waits")
	killStats = stats.NewCounters("Kills")
	infoErrors = stats.NewCounters("InfoErrors")
	errorStats = stats.NewCounters("Errors")
	internalErrors = stats.NewCounters("InternalErrors")
	resultStats = stats.NewHistogram("Results", resultBuckets)
	stats.Publish("RowcacheSpotCheckRatio", stats.FloatFunc(func() float64 {
		return float64(qe.spotCheckFreq.Get()) / SPOT_CHECK_MULTIPLIER
	}))
	spotCheckCount = stats.NewInt("RowcacheSpotCheckCount")

	return qe
}

// Open must be called before sending requests to QueryEngine.
func (qe *QueryEngine) Open(dbconfig *dbconfigs.DBConfig, schemaOverrides []SchemaOverride, qrs *QueryRules, mysqld *mysqlctl.Mysqld) {
	qe.dbconfig = dbconfig
	connFactory := dbconnpool.DBConnectionCreator(&dbconfig.ConnectionParams, mysqlStats)

	strictMode := false
	if qe.strictMode.Get() != 0 {
		strictMode = true
	}
	if !strictMode && dbconfig.EnableRowcache {
		panic(NewTabletError(FATAL, "Rowcache cannot be enabled when queryserver-config-strict-mode is false"))
	}
	if dbconfig.EnableRowcache {
		qe.cachePool.Open()
		log.Infof("rowcache is enabled")
	} else {
		// Invalidator should not be enabled if rowcache is not enabled.
		dbconfig.EnableInvalidator = false
		log.Infof("rowcache is not enabled")
	}

	start := time.Now()
	// schemaInfo depends on cachePool. Every table that has a rowcache
	// points to the cachePool.
	qe.schemaInfo.Open(connFactory, schemaOverrides, qe.cachePool, qrs, strictMode)
	log.Infof("Time taken to load the schema: %v", time.Now().Sub(start))

	// Start the invalidator only after schema is loaded.
	// This will allow qe to find the table info
	// for the invalidation events that will start coming
	// immediately.
	if dbconfig.EnableInvalidator {
		qe.invalidator.Open(dbconfig.DbName, mysqld)
	}
	qe.connPool.Open(connFactory)
	qe.streamConnPool.Open(connFactory)
	qe.txPool.Open(connFactory)
	qe.connKiller.Open(connFactory)
}

// Launch launches the specified function inside a goroutine.
// If Close or WaitForTxEmpty is called while a goroutine is running,
// QueryEngine will not return until the existing functions have completed.
// This functionality allows us to launch tasks with the assurance that
// the QueryEngine will not be closed underneath us.
func (qe *QueryEngine) Launch(f func()) {
	qe.tasks.Add(1)
	go func() {
		defer func() {
			qe.tasks.Done()
			internalErrors.Add("Task", 1)
			if x := recover(); x != nil {
				log.Errorf("task error: %v", x)
			}
		}()
		f()
	}()
}

// WaitForTxEmpty must be called before calling Close.
// Before calling WaitForTxEmpty, you must ensure that there
// will be no more calls to Begin.
func (qe *QueryEngine) WaitForTxEmpty() {
	qe.txPool.WaitForEmpty()
}

// Close must be called to shut down QueryEngine.
// You must ensure that no more queries will be sent
// before calling Close.
func (qe *QueryEngine) Close() {
	qe.tasks.Wait()
	// Close in reverse order of Open.
	qe.connKiller.Close()
	qe.txPool.Close()
	qe.streamConnPool.Close()
	qe.connPool.Close()
	qe.invalidator.Close()
	qe.schemaInfo.Close()
	qe.cachePool.Close()
	qe.dbconfig = nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package task

import (
	"fmt"
	"time"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/pkg/util/workqueue"
)

var (
	keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc
)

// Queue manages a work queue through an independent worker that
// invokes the given sync function for every work item inserted.
type Queue struct {
	// queue is the work queue the worker polls
	queue workqueue.RateLimitingInterface
	// sync is called for each item in the queue
	sync func(interface{}) error
	// workerDone is closed when the worker exits
	workerDone chan struct{}

	fn func(interface{}) (string, error)
}

// Run ...
func (t *Queue) Run(period time.Duration, stopCh <-chan struct{}) {
	wait.Until(t.worker, period, stopCh)
}

// Enqueue enqueues ns/name of the given api object in the task queue.
func (t *Queue) Enqueue(obj interface{}) {
	glog.V(3).Infof("queuing item %v", obj)
	key, err := t.fn(obj)
	if err != nil {
		glog.Errorf("%v", err)
		return
	}
	t.queue.Add(key)
}

func (t *Queue) requeue(key interface{}) {
	t.queue.AddRateLimited(key)
}

func (t *Queue) defaultKeyFunc(obj interface{}) (string, error) {
	key, err := keyFunc(obj)
	if err != nil {
		return "", fmt.Errorf("could not get key for object %+v: %v", obj, err)
	}

	return key, nil
}

// worker processes work in the queue through sync.
func (t *Queue) worker() {
	for {
		k, quit := t.queue.Get()
		if quit {
			close(t.workerDone)
			return
		}
		key := k.(string)
		glog.V(3).Infof("syncing %v", key)
		if err := t.sync(key); err != nil {
			glog.Warningf("requeuing %v, err %v", key, err)
			t.requeue(key)
		} else {
			t.queue.Forget(key)
		}

		t.queue.Done(key)
	}
}

// Shutdown shuts down the work queue and waits for the worker to ACK
func (t *Queue) Shutdown() {
	t.queue.ShutDown()
	<-t.workerDone
}

// IsShuttingDown returns if the method Shutdown was invoked
func (t *Queue) IsShuttingDown() bool {
	return t.queue.ShuttingDown()
}

// NewTaskQueue creates a new task queue with the given sync function.
// The sync function is called for every element inserted into the queue.
func NewTaskQueue(syncFn func(interface{}) error) *Queue {
	return NewCustomTaskQueue(syncFn, nil)
}

// NewCustomTaskQueue ...
func NewCustomTaskQueue(syncFn func(interface{}) error, fn func(interface{}) (string, error)) *Queue {
	q := &Queue{
		queue:      workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
		sync:       syncFn,
		workerDone: make(chan struct{}),
		fn:         fn,
	}

	if fn == nil {
		q.fn = q.defaultKeyFunc
	}

	return q
}
<commit_msg>Remove requeue helper<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package task

import (
	"fmt"
	"time"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/pkg/util/workqueue"
)

var (
	keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc
)

// Queue manages a work queue through an independent worker that
// invokes the given sync function for every work item inserted.
type Queue struct {
	// queue is the work queue the worker polls
	queue workqueue.RateLimitingInterface
	// sync is called for each item in the queue
	sync func(interface{}) error
	// workerDone is closed when the worker exits
	workerDone chan struct{}

	fn func(interface{}) (string, error)
}

// Run ...
func (t *Queue) Run(period time.Duration, stopCh <-chan struct{}) {
	wait.Until(t.worker, period, stopCh)
}

// Enqueue enqueues ns/name of the given api object in the task queue.
func (t *Queue) Enqueue(obj interface{}) {
	glog.V(3).Infof("queuing item %v", obj)
	key, err := t.fn(obj)
	if err != nil {
		glog.Errorf("%v", err)
		return
	}
	t.queue.Add(key)
}

func (t *Queue) defaultKeyFunc(obj interface{}) (string, error) {
	key, err := keyFunc(obj)
	if err != nil {
		return "", fmt.Errorf("could not get key for object %+v: %v", obj, err)
	}

	return key, nil
}

// worker processes work in the queue through sync.
func (t *Queue) worker() {
	for {
		k, quit := t.queue.Get()
		if quit {
			close(t.workerDone)
			return
		}
		key := k.(string)
		glog.V(3).Infof("syncing %v", key)
		if err := t.sync(key); err != nil {
			glog.Warningf("requeuing %v, err %v", key, err)
			t.queue.AddRateLimited(key)
		} else {
			t.queue.Forget(key)
		}

		t.queue.Done(key)
	}
}

// Shutdown shuts down the work queue and waits for the worker to ACK
func (t *Queue) Shutdown() {
	t.queue.ShutDown()
	<-t.workerDone
}

// IsShuttingDown returns if the method Shutdown was invoked
func (t *Queue) IsShuttingDown() bool {
	return t.queue.ShuttingDown()
}

// NewTaskQueue creates a new task queue with the given sync function.
// The sync function is called for every element inserted into the queue.
func NewTaskQueue(syncFn func(interface{}) error) *Queue {
	return NewCustomTaskQueue(syncFn, nil)
}

// NewCustomTaskQueue ...
func NewCustomTaskQueue(syncFn func(interface{}) error, fn func(interface{}) (string, error)) *Queue {
	q := &Queue{
		queue:      workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
		sync:       syncFn,
		workerDone: make(chan struct{}),
		fn:         fn,
	}

	if fn == nil {
		q.fn = q.defaultKeyFunc
	}

	return q
}
<|endoftext|>


<commit_before>package session

import (
	"fmt"
	"io"
	"strconv"

	"github.com/Cristofori/kmud/combat"
	"github.com/Cristofori/kmud/events"
	"github.com/Cristofori/kmud/model"
	"github.com/Cristofori/kmud/types"
	"github.com/Cristofori/kmud/utils"
	// "log"
	// "os"
	"strings"
	"time"
)

type Session struct {
	conn   io.ReadWriter
	user   types.User
	player types.PC
	room   types.Room

	prompt string
	states map[string]string

	userInputChannel chan string
	inputModeChannel chan userInputMode
	prompterChannel  chan utils.Prompter
	panicChannel     chan interface{}
	eventChannel     chan events.Event

	silentMode bool

	replyId types.Id

	// logger *log.Logger
}

func NewSession(conn io.ReadWriter, user types.User, player types.PC) *Session {
	var session Session
	session.conn = conn
	session.user = user
	session.player = player
	session.room = model.GetRoom(player.GetRoomId())

	session.prompt = "%h/%H> "
	session.states = map[string]string{}

	session.userInputChannel = make(chan string)
	session.inputModeChannel = make(chan userInputMode)
	session.prompterChannel = make(chan utils.Prompter)
	session.panicChannel = make(chan interface{})
	session.eventChannel = events.Register(player)

	session.silentMode = false

	// file, err := os.OpenFile(player.GetName()+".log", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	// utils.PanicIfError(err)

	// session.logger = log.New(file, player.GetName()+" ", log.LstdFlags)

	model.Login(player)

	return &session
}

type userInputMode int

const (
	CleanUserInput userInputMode = iota
	RawUserInput   userInputMode = iota
)

func (session *Session) Exec() {
	defer events.Unregister(session.player)
	defer model.Logout(session.player)

	session.printLineColor(types.ColorWhite, "Welcome, "+session.player.GetName())
	session.printRoom()

	// Main routine in charge of actually reading input from the connection object,
	// also has built in throttling to limit how fast we are allowed to process
	// commands from the user.
	go func() {
		defer func() {
			if r := recover(); r != nil {
				session.panicChannel <- r
			}
		}()

		throttler := utils.NewThrottler(200 * time.Millisecond)

		for {
			mode := <-session.inputModeChannel
			prompter := <-session.prompterChannel
			input := ""

			switch mode {
			case CleanUserInput:
				input = utils.GetUserInputP(session.conn, prompter, session.user.GetColorMode())
			case RawUserInput:
				input = utils.GetRawUserInputP(session.conn, prompter, session.user.GetColorMode())
			default:
				panic("Unhandled case in switch statement (userInputMode)")
			}

			throttler.Sync()
			session.userInputChannel <- input
		}
	}()

	// Main loop
	for {
		input := session.getUserInputP(RawUserInput, session)
		if input == "" || input == "logout" || input == "quit" {
			return
		}

		if strings.HasPrefix(input, "/") {
			session.handleCommand(utils.Argify(input[1:]))
		} else {
			session.handleAction(utils.Argify(input))
		}
	}
}

func (session *Session) printLineColor(color types.Color, line string, a ...interface{}) {
	session.user.WriteLine(types.Colorize(color, fmt.Sprintf(line, a...)))
}

func (session *Session) printLine(line string, a ...interface{}) {
	session.printLineColor(types.ColorWhite, line, a...)
}

func (session *Session) printError(err string, a ...interface{}) {
	session.printLineColor(types.ColorRed, err, a...)
}

func (session *Session) printRoom() {
	playerList := model.PlayerCharactersIn(session.room.GetId(), session.player.GetId())
	npcList := model.NpcsIn(session.room.GetId())
	area := model.GetArea(session.room.GetAreaId())

	session.printLine(session.room.ToString(playerList, npcList,
		model.GetItems(session.room.GetItems()), area))
}

func (session *Session) clearLine() {
	utils.ClearLine(session.conn)
}

func (session *Session) asyncMessage(message string) {
	session.clearLine()
	session.printLine(message)
}

// Same behavior as menu.Exec(), except that it uses getUserInput
// which doesn't block the event loop while waiting for input
func (session *Session) execMenu(menu *utils.Menu) (string, types.Id) {
	choice := ""
	var data types.Id

	for {
		menu.Print(session.conn, session.user.GetColorMode())
		choice = session.getUserInputP(CleanUserInput, menu)
		if menu.HasAction(choice) || choice == "" {
			data = menu.GetData(choice)
			break
		}

		if choice != "?" {
			session.printError("Invalid selection")
		}
	}
	return choice, data
}

// getUserInput allows us to retrieve user input in a way that doesn't block the
// event loop by using channels and a separate Go routine to grab
// either the next user input or the next event.
func (session *Session) getUserInputP(inputMode userInputMode, prompter utils.Prompter) string {
	session.inputModeChannel <- inputMode
	session.prompterChannel <- prompter

	for {
		select {
		case input := <-session.userInputChannel:
			return input
		case event := <-session.eventChannel:
			if session.silentMode {
				continue
			}

			switch e := event.(type) {
			case events.TellEvent:
				session.replyId = e.From.GetId()
			case events.TickEvent:
				if !combat.InCombat(session.player) {
					oldHps := session.player.GetHitPoints()
					session.player.Heal(5)
					newHps := session.player.GetHitPoints()

					if oldHps != newHps {
						session.clearLine()
						session.user.Write(prompter.GetPrompt())
					}
				}
			}

			message := event.ToString(session.player)
			if message != "" {
				session.asyncMessage(message)
				session.user.Write(prompter.GetPrompt())
			}

		case quitMessage := <-session.panicChannel:
			panic(quitMessage)
		}
	}
}

func (session *Session) getUserInput(inputMode userInputMode, prompt string) string {
	return session.getUserInputP(inputMode, utils.SimplePrompter(prompt))
}

func (session *Session) getRawUserInput(prompt string) string {
	return session.getUserInput(RawUserInput, prompt)
}

func (session *Session) GetPrompt() string {
	prompt := session.prompt
	prompt = strings.Replace(prompt, "%h", strconv.Itoa(session.player.GetHitPoints()), -1)
	prompt = strings.Replace(prompt, "%H", strconv.Itoa(session.player.GetHealth()), -1)

	if len(session.states) > 0 {
		states := make([]string, len(session.states))

		i := 0
		for key, value := range session.states {
			states[i] = fmt.Sprintf("%s:%s", key, value)
			i++
		}

		prompt = fmt.Sprintf("%s %s", states, prompt)
	}

	return types.Colorize(types.ColorWhite, prompt)
}

func (session *Session) currentZone() types.Zone {
	return model.GetZone(session.room.GetZoneId())
}

func (self *Session) handleAction(action string, args []string) {
	if len(args) == 0 {
		direction := types.StringToDirection(action)

		if direction != types.DirectionNone {
			if self.room.HasExit(direction) {
				newRoom, err := model.MoveCharacter(self.player, direction)
				if err == nil {
					self.room = newRoom
					self.printRoom()
				} else {
					self.printError(err.Error())
				}

			} else {
				self.printError("You can't go that way")
			}

			return
		}
	}

	handler, found := actions[action]

	if found {
		if handler.alias != "" {
			handler = actions[handler.alias]
		}
		handler.exec(self, args)
	} else {
		self.printError("You can't do that")
	}
}

func (self *Session) handleCommand(command string, args []string) {
	if command[0] == '/' && self.user.IsAdmin() {
		quickRoom(self, command[1:])
		return
	}

	handler, found := commands[command]

	if found {
		if handler.alias != "" {
			handler = commands[handler.alias]
		}
		handler.exec(self, args)
	} else {
		self.printError("Unrecognized command: %s", command)
	}
}
<commit_msg>Prefer self for method object pointers<commit_after>package session

import (
	"fmt"
	"io"
	"strconv"

	"github.com/Cristofori/kmud/combat"
	"github.com/Cristofori/kmud/events"
	"github.com/Cristofori/kmud/model"
	"github.com/Cristofori/kmud/types"
	"github.com/Cristofori/kmud/utils"
	// "log"
	// "os"
	"strings"
	"time"
)

type Session struct {
	conn   io.ReadWriter
	user   types.User
	player types.PC
	room   types.Room

	prompt string
	states map[string]string

	userInputChannel chan string
	inputModeChannel chan userInputMode
	prompterChannel  chan utils.Prompter
	panicChannel     chan interface{}
	eventChannel     chan events.Event

	silentMode bool

	replyId types.Id

	// logger *log.Logger
}

func NewSession(conn io.ReadWriter, user types.User, player types.PC) *Session {
	var session Session
	session.conn = conn
	session.user = user
	session.player = player
	session.room = model.GetRoom(player.GetRoomId())

	session.prompt = "%h/%H> "
	session.states = map[string]string{}

	session.userInputChannel = make(chan string)
	session.inputModeChannel = make(chan userInputMode)
	session.prompterChannel = make(chan utils.Prompter)
	session.panicChannel = make(chan interface{})
	session.eventChannel = events.Register(player)

	session.silentMode = false

	// file, err := os.OpenFile(player.GetName()+".log", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	// utils.PanicIfError(err)

	// session.logger = log.New(file, player.GetName()+" ", log.LstdFlags)

	model.Login(player)

	return &session
}

type userInputMode int

const (
	CleanUserInput userInputMode = iota
	RawUserInput   userInputMode = iota
)

func (self *Session) Exec() {
	defer events.Unregister(self.player)
	defer model.Logout(self.player)

	self.printLineColor(types.ColorWhite, "Welcome, "+self.player.GetName())
	self.printRoom()

	// Main routine in charge of actually reading input from the connection object,
	// also has built in throttling to limit how fast we are allowed to process
	// commands from the user.
	go func() {
		defer func() {
			if r := recover(); r != nil {
				self.panicChannel <- r
			}
		}()

		throttler := utils.NewThrottler(200 * time.Millisecond)

		for {
			mode := <-self.inputModeChannel
			prompter := <-self.prompterChannel
			input := ""

			switch mode {
			case CleanUserInput:
				input = utils.GetUserInputP(self.conn, prompter, self.user.GetColorMode())
			case RawUserInput:
				input = utils.GetRawUserInputP(self.conn, prompter, self.user.GetColorMode())
			default:
				panic("Unhandled case in switch statement (userInputMode)")
			}

			throttler.Sync()
			self.userInputChannel <- input
		}
	}()

	// Main loop
	for {
		input := self.getUserInputP(RawUserInput, self)
		if input == "" || input == "logout" || input == "quit" {
			return
		}

		if strings.HasPrefix(input, "/") {
			self.handleCommand(utils.Argify(input[1:]))
		} else {
			self.handleAction(utils.Argify(input))
		}
	}
}

func (self *Session) printLineColor(color types.Color, line string, a ...interface{}) {
	self.user.WriteLine(types.Colorize(color, fmt.Sprintf(line, a...)))
}

func (self *Session) printLine(line string, a ...interface{}) {
	self.printLineColor(types.ColorWhite, line, a...)
}

func (self *Session) printError(err string, a ...interface{}) {
	self.printLineColor(types.ColorRed, err, a...)
}

func (self *Session) printRoom() {
	playerList := model.PlayerCharactersIn(self.room.GetId(), self.player.GetId())
	npcList := model.NpcsIn(self.room.GetId())
	area := model.GetArea(self.room.GetAreaId())

	self.printLine(self.room.ToString(playerList, npcList,
		model.GetItems(self.room.GetItems()), area))
}

func (self *Session) clearLine() {
	utils.ClearLine(self.conn)
}

func (self *Session) asyncMessage(message string) {
	self.clearLine()
	self.printLine(message)
}

// Same behavior as menu.Exec(), except that it uses getUserInput
// which doesn't block the event loop while waiting for input
func (self *Session) execMenu(menu *utils.Menu) (string, types.Id) {
	choice := ""
	var data types.Id

	for {
		menu.Print(self.conn, self.user.GetColorMode())
		choice = self.getUserInputP(CleanUserInput, menu)
		if menu.HasAction(choice) || choice == "" {
			data = menu.GetData(choice)
			break
		}

		if choice != "?" {
			self.printError("Invalid selection")
		}
	}
	return choice, data
}

// getUserInput allows us to retrieve user input in a way that doesn't block the
// event loop by using channels and a separate Go routine to grab
// either the next user input or the next event.
func (self *Session) getUserInputP(inputMode userInputMode, prompter utils.Prompter) string {
	self.inputModeChannel <- inputMode
	self.prompterChannel <- prompter

	for {
		select {
		case input := <-self.userInputChannel:
			return input
		case event := <-self.eventChannel:
			if self.silentMode {
				continue
			}

			switch e := event.(type) {
			case events.TellEvent:
				self.replyId = e.From.GetId()
			case events.TickEvent:
				if !combat.InCombat(self.player) {
					oldHps := self.player.GetHitPoints()
					self.player.Heal(5)
					newHps := self.player.GetHitPoints()

					if oldHps != newHps {
						self.clearLine()
						self.user.Write(prompter.GetPrompt())
					}
				}
			}

			message := event.ToString(self.player)
			if message != "" {
				self.asyncMessage(message)
				self.user.Write(prompter.GetPrompt())
			}

		case quitMessage := <-self.panicChannel:
			panic(quitMessage)
		}
	}
}

func (self *Session) getUserInput(inputMode userInputMode, prompt string) string {
	return self.getUserInputP(inputMode, utils.SimplePrompter(prompt))
}

func (self *Session) getRawUserInput(prompt string) string {
	return self.getUserInput(RawUserInput, prompt)
}

func (self *Session) GetPrompt() string {
	prompt := self.prompt
	prompt = strings.Replace(prompt, "%h", strconv.Itoa(self.player.GetHitPoints()), -1)
	prompt = strings.Replace(prompt, "%H", strconv.Itoa(self.player.GetHealth()), -1)

	if len(self.states) > 0 {
		states := make([]string, len(self.states))

		i := 0
		for key, value := range self.states {
			states[i] = fmt.Sprintf("%s:%s", key, value)
			i++
		}

		prompt = fmt.Sprintf("%s %s", states, prompt)
	}

	return types.Colorize(types.ColorWhite, prompt)
}

func (self *Session) currentZone() types.Zone {
	return model.GetZone(self.room.GetZoneId())
}

func (self *Session) handleAction(action string, args []string) {
	if len(args) == 0 {
		direction := types.StringToDirection(action)

		if direction != types.DirectionNone {
			if self.room.HasExit(direction) {
				newRoom, err := model.MoveCharacter(self.player, direction)
				if err == nil {
					self.room = newRoom
					self.printRoom()
				} else {
					self.printError(err.Error())
				}

			} else {
				self.printError("You can't go that way")
			}

			return
		}
	}

	handler, found := actions[action]

	if found {
		if handler.alias != "" {
			handler = actions[handler.alias]
		}
		handler.exec(self, args)
	} else {
		self.printError("You can't do that")
	}
}

func (self *Session) handleCommand(command string, args []string) {
	if command[0] == '/' && self.user.IsAdmin() {
		quickRoom(self, command[1:])
		return
	}

	handler, found := commands[command]

	if found {
		if handler.alias != "" {
			handler = commands[handler.alias]
		}
		handler.exec(self, args)
	} else {
		self.printError("Unrecognized command: %s", command)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
)

import (
	. "github.com/modcloth/amqp-tools"
	"github.com/streadway/amqp"
)

const (
	NOT_COOL_ZEUS = 86
	CONSUME_CAT   = `

    Ack      /\-/\
            /a a  \              _
           =\ Y  =/-~~~~~~-,____/ )
             '^--'          _____/
               \           /
               ||  |---'\  \
              (_(__|   ((__|

`
)

var (
	uriFlag     = flag.String("U", "amqp://guest:guest@localhost:5672", "AMQP Connection URI")
	rmqLogsFlag = flag.Bool("rabbitmq.logs", false, "Consume from amq.rabbitmq.logs and amq.rabbitmq.trace")
	showCatFlag = flag.Bool("mrow", false, "")
	versionFlag = flag.Bool("version", false, "Print version and exit")
	revFlag     = flag.Bool("rev", false, "Print git revision and exit")
	quit        = make(chan bool)

	queueBindings QueueBindings
	debugger      Debugger
)

func init() {
	flag.Var(&queueBindings, "q", "Queue bindings specified as \"/\"-delimited strings of the form \"exchange/queue-name/routing-key\"")
	flag.Var(&debugger, "debug", "Show debug output")
}

func main() {
	flag.Parse()

	if *showCatFlag {
		fmt.Println(CONSUME_CAT)
		os.Exit(0)
	}

	if *versionFlag {
		progName := path.Base(os.Args[0])
		if VersionString == "" {
			VersionString = "<unknown>"
		}
		fmt.Printf("%s %s\n", progName, VersionString)
		os.Exit(0)
	}

	if *revFlag {
		if RevString == "" {
			RevString = "<unknown>"
		}
		fmt.Println(RevString)
		os.Exit(0)
	}

	deliveries := make(chan interface{})

	if len(queueBindings) > 0 {
		for _, binding := range queueBindings {
			debugger.Print(fmt.Sprintf("Binding to %s", binding))
		}

		go ConsumeForBindings(*uriFlag, queueBindings, deliveries, debugger)

		go func() {
			for delivery := range deliveries {
				switch delivery.(type) {
				case nil:
					quit <- true
				default:
					HandleDelivery(delivery.(amqp.Delivery), debugger)
				}
			}
		}()
	} else {
		fmt.Println("ERROR: define at least one exchange/queue/binding argument.")
		flag.Usage()
		os.Exit(NOT_COOL_ZEUS)
	}
	<-quit
}
<commit_msg>Adding a debug statement when finished<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
)

import (
	. "github.com/modcloth/amqp-tools"
	"github.com/streadway/amqp"
)

const (
	NOT_COOL_ZEUS = 86
	CONSUME_CAT   = `

    Ack      /\-/\
            /a a  \              _
           =\ Y  =/-~~~~~~-,____/ )
             '^--'          _____/
               \           /
               ||  |---'\  \
              (_(__|   ((__|

`
)

var (
	uriFlag     = flag.String("U", "amqp://guest:guest@localhost:5672", "AMQP Connection URI")
	rmqLogsFlag = flag.Bool("rabbitmq.logs", false, "Consume from amq.rabbitmq.logs and amq.rabbitmq.trace")
	showCatFlag = flag.Bool("mrow", false, "")
	versionFlag = flag.Bool("version", false, "Print version and exit")
	revFlag     = flag.Bool("rev", false, "Print git revision and exit")
	quit        = make(chan bool)

	queueBindings QueueBindings
	debugger      Debugger
)

func init() {
	flag.Var(&queueBindings, "q", "Queue bindings specified as \"/\"-delimited strings of the form \"exchange/queue-name/routing-key\"")
	flag.Var(&debugger, "debug", "Show debug output")
}

func main() {
	flag.Parse()

	if *showCatFlag {
		fmt.Println(CONSUME_CAT)
		os.Exit(0)
	}

	if *versionFlag {
		progName := path.Base(os.Args[0])
		if VersionString == "" {
			VersionString = "<unknown>"
		}
		fmt.Printf("%s %s\n", progName, VersionString)
		os.Exit(0)
	}

	if *revFlag {
		if RevString == "" {
			RevString = "<unknown>"
		}
		fmt.Println(RevString)
		os.Exit(0)
	}

	deliveries := make(chan interface{})

	if len(queueBindings) > 0 {
		for _, binding := range queueBindings {
			debugger.Print(fmt.Sprintf("Binding to %s", binding))
		}

		go ConsumeForBindings(*uriFlag, queueBindings, deliveries, debugger)

		go func() {
			for delivery := range deliveries {
				switch delivery.(type) {
				case nil:
					debugger.Print("Done consuming. Thanks for playing!")
					quit <- true
				default:
					HandleDelivery(delivery.(amqp.Delivery), debugger)
				}
			}
		}()
	} else {
		fmt.Println("ERROR: define at least one exchange/queue/binding argument.")
		flag.Usage()
		os.Exit(NOT_COOL_ZEUS)
	}
	<-quit
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"testing"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestParsingVersion(c *C) {
	Version, err := ParseVersion("1.5.9")
	c.Assert(err, Equals, nil)
	c.Assert(Version.Major, Equals, 1)
	c.Assert(Version.Minor, Equals, 5)
	c.Assert(Version.Patch, Equals, 9)
}

func (s *MySuite) TestParsingErrorForIncorrectNumberOfDotCharacters(c *C) {
	_, err := ParseVersion("1.5.9.9")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")

	_, err = ParseVersion("0.")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")
}

func (s *MySuite) TestParsingErrorForNonIntegerVersion(c *C) {
	_, err := ParseVersion("a.9.0")
	c.Assert(err, ErrorMatches, `Error parsing major Version a to integer. strconv.ParseInt: parsing "a": invalid syntax`)

	_, err = ParseVersion("0.ffhj.78")
	c.Assert(err, ErrorMatches, `Error parsing minor Version ffhj to integer. strconv.ParseInt: parsing "ffhj": invalid syntax`)

	_, err = ParseVersion("8.9.opl")
	c.Assert(err, ErrorMatches, `Error parsing patch Version opl to integer. strconv.ParseInt: parsing "opl": invalid syntax`)
}

func (s *MySuite) TestVersionComparisonGreaterLesser(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("4.7.2")
	version2, _ := ParseVersion("4.7.2")
	c.Assert(version1.IsEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonGreaterThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(higherVersion.IsGreaterThanEqualTo(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsGreaterThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonLesserThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("5.8.2")
	lowerVersion, _ = ParseVersion("2.9.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsLesserThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionIsBetweenTwoVersions(c *C) {
	higherVersion, _ := ParseVersion("0.0.9")
	lowerVersion, _ := ParseVersion("0.0.7")
	middleVersion, _ := ParseVersion("0.0.8")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	middleVersion, _ = ParseVersion("0.6.9")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("4.0.1")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.0.2")
	lowerVersion, _ = ParseVersion("0.0.1")
	middleVersion, _ = ParseVersion("0.0.2")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)
}

func (s *MySuite) TestGetLatestVersion(c *C) {
	highestVersion := &Version{2, 2, 2}
	versions := []*Version{&Version{0, 0, 1}, &Version{1, 2, 2}, highestVersion, &Version{0, 0, 2}, &Version{0, 2, 2}, &Version{0, 0, 3}, &Version{0, 2, 1}, &Version{0, 1, 2}}
	latestVersion := GetLatestVersion(versions)

	c.Assert(latestVersion, DeepEquals, highestVersion)
}

func (s *MySuite) TestCheckVersionCompatibilitySuccess(c *C) {
	versionSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{0, 6, 7}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.0.1"}
	gaugeVersion = &Version{1, 5, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 5, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

}

func (s *MySuite) TestCheckVersionCompatibilityFailure(c *C) {
	versionsSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{1, 9, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "1.3.1"}
	gaugeVersion = &Version{1, 3, 0}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 0, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

}

func (s *MySuite) TestFullVersionWithBuildMetadata(c *C) {
	c.Assert(FullVersion(), Equals, CurrentGaugeVersion.String())
	BuildMetadata = "nightly-2016-02-21"
	c.Assert(FullVersion(), Equals, fmt.Sprintf("%s.%s", CurrentGaugeVersion.String(), BuildMetadata))
}
<commit_msg>Fixing error string in test as per go source tip<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"testing"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestParsingVersion(c *C) {
	Version, err := ParseVersion("1.5.9")
	c.Assert(err, Equals, nil)
	c.Assert(Version.Major, Equals, 1)
	c.Assert(Version.Minor, Equals, 5)
	c.Assert(Version.Patch, Equals, 9)
}

func (s *MySuite) TestParsingErrorForIncorrectNumberOfDotCharacters(c *C) {
	_, err := ParseVersion("1.5.9.9")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")

	_, err = ParseVersion("0.")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")
}

func (s *MySuite) TestParsingErrorForNonIntegerVersion(c *C) {
	_, err := ParseVersion("a.9.0")
	c.Assert(err, ErrorMatches, `Error parsing major Version a to integer. strconv.*: parsing "a": invalid syntax`)

	_, err = ParseVersion("0.ffhj.78")
	c.Assert(err, ErrorMatches, `Error parsing minor Version ffhj to integer. strconv.*: parsing "ffhj": invalid syntax`)

	_, err = ParseVersion("8.9.opl")
	c.Assert(err, ErrorMatches, `Error parsing patch Version opl to integer. strconv.*: parsing "opl": invalid syntax`)
}

func (s *MySuite) TestVersionComparisonGreaterLesser(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("4.7.2")
	version2, _ := ParseVersion("4.7.2")
	c.Assert(version1.IsEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonGreaterThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(higherVersion.IsGreaterThanEqualTo(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsGreaterThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonLesserThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("5.8.2")
	lowerVersion, _ = ParseVersion("2.9.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsLesserThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionIsBetweenTwoVersions(c *C) {
	higherVersion, _ := ParseVersion("0.0.9")
	lowerVersion, _ := ParseVersion("0.0.7")
	middleVersion, _ := ParseVersion("0.0.8")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	middleVersion, _ = ParseVersion("0.6.9")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("4.0.1")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.0.2")
	lowerVersion, _ = ParseVersion("0.0.1")
	middleVersion, _ = ParseVersion("0.0.2")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)
}

func (s *MySuite) TestGetLatestVersion(c *C) {
	highestVersion := &Version{2, 2, 2}
	versions := []*Version{&Version{0, 0, 1}, &Version{1, 2, 2}, highestVersion, &Version{0, 0, 2}, &Version{0, 2, 2}, &Version{0, 0, 3}, &Version{0, 2, 1}, &Version{0, 1, 2}}
	latestVersion := GetLatestVersion(versions)

	c.Assert(latestVersion, DeepEquals, highestVersion)
}

func (s *MySuite) TestCheckVersionCompatibilitySuccess(c *C) {
	versionSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{0, 6, 7}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.0.1"}
	gaugeVersion = &Version{1, 5, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 5, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

}

func (s *MySuite) TestCheckVersionCompatibilityFailure(c *C) {
	versionsSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{1, 9, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "1.3.1"}
	gaugeVersion = &Version{1, 3, 0}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 0, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

}

func (s *MySuite) TestFullVersionWithBuildMetadata(c *C) {
	c.Assert(FullVersion(), Equals, CurrentGaugeVersion.String())
	BuildMetadata = "nightly-2016-02-21"
	c.Assert(FullVersion(), Equals, fmt.Sprintf("%s.%s", CurrentGaugeVersion.String(), BuildMetadata))
}
<|endoftext|>